From c9e2d5efbb2ef8c67e71fd763a3935ac6a6a5870 Mon Sep 17 00:00:00 2001
From: cl <1442464845@qq.com>
Date: 星期五, 08 五月 2026 02:27:28 +0800
Subject: [PATCH] 出库补发

---
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java              |  427 ++++++++++++++++++++++++--------------
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CloudWmsNotifyLogServiceImpl.java |  129 +++++++++++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java           |    9 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CheckOrderServiceImpl.java        |    8 
 rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaCheckOrderServiceImpl.java         |   13 +
 rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaOutStockServiceImpl.java           |   10 
 rsf-server/src/main/java/com/vincent/rsf/server/common/utils/QuantityUtils.java                        |   23 ++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/TaskService.java                       |    4 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/CloudWmsNotifyLogService.java          |   12 +
 9 files changed, 470 insertions(+), 165 deletions(-)

diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaCheckOrderServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaCheckOrderServiceImpl.java
index ff3e3af..c858023 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaCheckOrderServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaCheckOrderServiceImpl.java
@@ -27,6 +27,7 @@
 import com.vincent.rsf.server.system.service.UserService;
 import com.vincent.rsf.server.system.service.impl.FieldsItemServiceImpl;
 import com.vincent.rsf.server.system.service.impl.UserServiceImpl;
+import com.vincent.rsf.server.common.utils.QuantityUtils;
 import com.vincent.rsf.server.system.utils.SerialRuleUtils;
 import com.vincent.rsf.server.system.utils.SystemAuthUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -75,7 +76,6 @@
 
     @Autowired
     private CusBarcodeSyncViewQueryService cusBarcodeSyncViewQueryService;
-
 
     @Override
     public R getCheckList(String code) {
@@ -178,6 +178,7 @@
                 if (!taskItemService.save(taskItem)) {
                     throw new CoolException("鏂板鐩樼偣鏄庣粏淇濆瓨澶辫触锛侊紒");
                 }
+                taskService.enqueueCloudWmsOutNotifyLogEarly(task, taskItem, QuantityUtils.toScaledBigDecimal(diffItem.getCheckQty()));
 
                 diffItem.setTaskItemId(taskItem.getId()).setExceStatus(CheckDiffExceStatus.CHECK_DIFF_EXCE_STATUS_HOLD.val);
 
@@ -200,6 +201,13 @@
                         .set(TaskItem::getAnfme, diffItem.getCheckQty())
                         .eq(TaskItem::getId, diffItem.getTaskItemId()))) {
                     throw new CoolException("浠诲姟鏁伴噺淇敼澶辫触锛侊紒");
+                }
+                TaskItem tiUpd = taskItemService.getById(diffItem.getTaskItemId());
+                if (tiUpd != null) {
+                    Task t = taskService.getById(tiUpd.getTaskId());
+                    if (t != null) {
+                        taskService.enqueueCloudWmsOutNotifyLogEarly(t, tiUpd, QuantityUtils.toScaledBigDecimal(diffItem.getCheckQty()));
+                    }
                 }
             }
         }
@@ -496,6 +504,9 @@
         if (!taskItemService.saveBatch(taskItems)) {
             throw new CoolException("鏂板鐩樼偣鐗╂枡澶辫触锛侊紒");
         }
+        for (TaskItem ti : taskItems) {
+            taskService.enqueueCloudWmsOutNotifyLogEarly(task, ti, QuantityUtils.toScaledBigDecimal(ti.getAnfme()));
+        }
 
         return R.ok();
     }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaOutStockServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaOutStockServiceImpl.java
index 535bd96..3f175aa 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaOutStockServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaOutStockServiceImpl.java
@@ -31,6 +31,7 @@
 import com.vincent.rsf.server.system.service.impl.FieldsServiceImpl;
 import com.vincent.rsf.server.system.utils.SerialRuleUtils;
 import com.vincent.rsf.server.common.constant.Constants;
+import com.vincent.rsf.server.common.utils.QuantityUtils;
 import com.vincent.rsf.server.system.utils.SystemAuthUtils;
 import lombok.Synchronized;
 import org.apache.commons.lang3.StringUtils;
@@ -77,7 +78,6 @@
     private LocItemWorkingService locItemWorkingService;
     @Autowired
     private ConfigServiceImpl configService;
-
     /**
      * 蹇�熸嫞璐ф煡璇細鍚屼竴绠辩爜鍙兘鏈夊鏉′换鍔★紝浠� RCS 鍑哄簱鍥炶皟鍚庡彉涓� 199 鐨勬墠灞曠ず锛涜绠辩爜涓嬩粛涓嶆槸 199 鐨� PDA 涓嶆樉绀恒��
      * 杩斿洖锛歰rders 鎸夊嚭搴撳崟鍒嗘ā鍧椼�乴ist/taskItems 璇ョ鐮佷笅 199 浠诲姟鏄庣粏銆�
@@ -156,10 +156,11 @@
                     for (TaskItem ti : items) {
                         if (ti.getQty() == null || ti.getQty().compareTo(0.0) <= 0) {
                             ti.setQty(ti.getAnfme() != null ? ti.getAnfme() : 0.0);
-                            ti.setUpdateBy(loginUserId);
-                            ti.setUpdateTime(new Date());
-                            taskItemService.updateById(ti);
                         }
+                        taskService.enqueueCloudWmsOutNotifyLogEarly(task, ti, QuantityUtils.toScaledBigDecimal(ti.getQty()));
+                        ti.setUpdateBy(loginUserId);
+                        ti.setUpdateTime(new Date());
+                        taskItemService.updateById(ti);
                     }
                 }
                 long not200 = taskService.count(new LambdaQueryWrapper<Task>()
@@ -432,6 +433,7 @@
             }
             batchPickSumByOrderItem.merge(orderItem.getId(), pickInc, (a, b) -> (a != null ? a : 0.0) + (b != null ? b : 0.0));
             Double picQty = Math.round(((item.getQty() != null ? item.getQty() : 0.0) + pickInc) * 1000000.0) / 1000000.0;
+            taskService.enqueueCloudWmsOutNotifyLogEarly(task, item, QuantityUtils.toScaledBigDecimal(picQty));
             item.setQty(picQty).setOrderId(order.getId()).setOrderItemId(orderItem.getId());
             if (!taskItemService.updateById(item)) {
                 throw new CoolException("鎷h揣鏁伴噺鏇存柊澶辫触锛侊紒");
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/common/utils/QuantityUtils.java b/rsf-server/src/main/java/com/vincent/rsf/server/common/utils/QuantityUtils.java
index c116bcd..74e448e 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/common/utils/QuantityUtils.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/common/utils/QuantityUtils.java
@@ -92,4 +92,27 @@
         }
         return toBigDecimal(v).setScale(SCALE, ROUNDING).doubleValue();
     }
+
+    public static Double roundToScale(BigDecimal v) {
+        if (v == null) {
+            return 0.0;
+        }
+        return v.setScale(SCALE, ROUNDING).doubleValue();
+    }
+
+    /** Double 鍏ュ弬缁熶竴涓烘爣搴﹀悗鐨� BigDecimal锛岄伩鍏嶄簩杩涘埗娴偣灏惧樊杩涘叆棰勫瓨/姹囨�� */
+    public static BigDecimal toScaledBigDecimal(Double v) {
+        if (v == null) {
+            return null;
+        }
+        return toBigDecimal(v).setScale(SCALE, ROUNDING);
+    }
+
+    /** 浜戜粨 JSON 鏁伴噺瀛楁锛氭棤绉戝璁℃暟娉曪紝鍘绘帀鏈熬澶氫綑 0 */
+    public static String toPlainQtyString(BigDecimal v) {
+        if (v == null) {
+            return null;
+        }
+        return v.setScale(SCALE, ROUNDING).stripTrailingZeros().toPlainString();
+    }
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/CloudWmsNotifyLogService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/CloudWmsNotifyLogService.java
index 060f56e..94b0dc5 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/CloudWmsNotifyLogService.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/CloudWmsNotifyLogService.java
@@ -6,7 +6,10 @@
 import com.vincent.rsf.server.manager.entity.CloudWmsNotifyLog;
 
 import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 /** 浜戜粨涓婃姤寰呭姙 */
 public interface CloudWmsNotifyLogService extends IService<CloudWmsNotifyLog> {
@@ -46,4 +49,13 @@
 
     /** manual 妯″紡锛氭寜鍗曞彿鏀捐鏆傜紦鐨勫叆鍑哄簱寰呭姙锛坰end_hold=1鈫�0锛� */
     R manualFlushToNotifyByOrderCode(String orderCode, boolean inbound);
+
+    /** 鍑哄簱姹囨�伙細浠ュ悇 task_item 鏈�鏂颁竴鏉″叆鍑哄簱寰呭姙 request_body.qty 涓哄噯锛堟棤鍒� map 涓嶅惈璇� id锛� */
+    Map<Long, BigDecimal> mapOutQtyFromInOutNotifyLogByTaskItemIds(Collection<Long> taskItemIds);
+
+    /** 瀹岀粨涓婃姤鍘婚噸锛歜iz_ref 宸插惈璇� taskItemId 鐨勫叆鍑哄簱寰呭姙浠讳竴琛屽瓨鍦ㄥ垯涓嶉噸澶嶆彃鍏� */
+    boolean hasInOutNotifyRowForTaskItem(Long taskItemId, boolean inbound);
+
+    /** 涓庡畬缁撳悓缁撴瀯鐨勫崟琛屽緟鍔烇紱pending/fail 鍒欐洿鏂� request_body */
+    void saveOrUpdateInOutNotifyEarly(InOutResultReportParam param, Long taskId, Long taskItemId, boolean sendHold, Integer tenantId);
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/TaskService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/TaskService.java
index f085325..84fbbb7 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/TaskService.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/TaskService.java
@@ -5,6 +5,7 @@
 import com.vincent.rsf.server.manager.controller.params.GenerateTaskParams;
 import com.vincent.rsf.server.manager.entity.*;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 
@@ -50,4 +51,7 @@
      * 鍚岀鐮佷笅澶氭潯 200 鎷f枡鍑哄簱涓�娆℃�у鐞嗭細鎸夌浉鍚岀墿鏂欏悎璁℃墸鍑忓簱浣嶃�佹洿鏂板嚭搴撳崟/搴撳瓨鏄庣粏銆佺敓鎴愪竴寮犳嫞鏂欏叆搴撳崟锛堟湁浣欓噺鏃讹級銆佹洿鏂板簱浣嶇姸鎬�
      */
     void processPickOutBarcodeAll200(List<Task> all200Tasks);
+
+    /** 鍑哄簱 9.1锛氭敼浠诲姟鏄庣粏鍓嶅啓鍏�/鏇存柊 notify_log锛坬ty 浠ユ湰鏂规硶鍏ュ弬涓哄噯锛屾眹鎬绘椂璇� request_body锛� */
+    void enqueueCloudWmsOutNotifyLogEarly(Task task, TaskItem taskItem, BigDecimal outQty);
 }
\ No newline at end of file
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CheckOrderServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CheckOrderServiceImpl.java
index 6d0b1da..4d87dec 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CheckOrderServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CheckOrderServiceImpl.java
@@ -8,6 +8,7 @@
 import com.vincent.rsf.framework.common.R;
 import com.vincent.rsf.framework.exception.CoolException;
 import com.vincent.rsf.server.common.utils.ExcelUtil;
+import com.vincent.rsf.server.common.utils.QuantityUtils;
 import com.vincent.rsf.server.manager.controller.params.*;
 import com.vincent.rsf.server.manager.entity.*;
 import com.vincent.rsf.server.manager.entity.excel.CheckOrderTemplate;
@@ -350,6 +351,13 @@
                 if (!taskItemService.saveBatch(items)) {
                     throw new CoolException("浠诲姟鏄庣粏淇濆瓨澶辫触锛侊紒");
                 }
+                List<TaskItem> persisted = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
+                for (TaskItem ti : persisted) {
+                    if (ti.getAnfme() == null) {
+                        throw new CoolException("浠诲姟鏄庣粏鏁伴噺寮傚父");
+                    }
+                    taskService.enqueueCloudWmsOutNotifyLogEarly(task, ti, QuantityUtils.toScaledBigDecimal(ti.getAnfme()));
+                }
 
                 loc.setUseStatus(LocStsType.LOC_STS_TYPE_R.type);
                 if (!locService.updateById(loc)) {
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CloudWmsNotifyLogServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CloudWmsNotifyLogServiceImpl.java
index a8d03ee..7f4e5a7 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CloudWmsNotifyLogServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CloudWmsNotifyLogServiceImpl.java
@@ -5,6 +5,7 @@
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.vincent.rsf.server.api.controller.erp.params.InOutResultReportParam;
@@ -22,12 +23,17 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.Collection;
 import java.util.Date;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Service
@@ -325,4 +331,127 @@
         }
         return Collections.singletonList(objectMapper.readValue(body, InOutResultReportParam.class));
     }
+
+    @Override
+    public Map<Long, BigDecimal> mapOutQtyFromInOutNotifyLogByTaskItemIds(Collection<Long> taskItemIds) {
+        if (taskItemIds == null || taskItemIds.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        List<Long> ids = taskItemIds.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
+        Map<Long, BigDecimal> out = new HashMap<>();
+        String reportType = getReportTypeInOutResult();
+        int outboundFlag = 0;
+        for (Long tid : ids) {
+            String needle = "taskItemId=" + tid + ",";
+            CloudWmsNotifyLog row = getOne(new LambdaQueryWrapper<CloudWmsNotifyLog>()
+                    .eq(CloudWmsNotifyLog::getReportType, reportType)
+                    .eq(CloudWmsNotifyLog::getInboundFlag, outboundFlag)
+                    .like(CloudWmsNotifyLog::getBizRef, needle)
+                    .orderByDesc(CloudWmsNotifyLog::getId)
+                    .last("LIMIT 1"));
+            if (row == null || StringUtils.isBlank(row.getRequestBody())) {
+                continue;
+            }
+            BigDecimal q = parseQtyFromInOutRequestBody(row.getRequestBody());
+            if (q != null) {
+                out.put(tid, q);
+            }
+        }
+        return out;
+    }
+
+    private BigDecimal parseQtyFromInOutRequestBody(String body) {
+        if (StringUtils.isBlank(body)) {
+            return null;
+        }
+        try {
+            JsonNode root = objectMapper.readTree(body);
+            if (root.has("lines") && root.get("lines").isArray() && root.get("lines").size() > 0) {
+                root = root.get("lines").get(0);
+            }
+            JsonNode qn = root.get("qty");
+            if (qn == null || qn.isNull()) {
+                return null;
+            }
+            String s = qn.isTextual() ? qn.asText() : qn.asText();
+            if (StringUtils.isBlank(s)) {
+                return null;
+            }
+            return new BigDecimal(s.trim()).setScale(6, java.math.RoundingMode.HALF_UP);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean hasInOutNotifyRowForTaskItem(Long taskItemId, boolean inbound) {
+        if (taskItemId == null) {
+            return false;
+        }
+        String needle = "taskItemId=" + taskItemId + ",";
+        int flag = inbound ? 1 : 0;
+        return count(new LambdaQueryWrapper<CloudWmsNotifyLog>()
+                .eq(CloudWmsNotifyLog::getReportType, getReportTypeInOutResult())
+                .eq(CloudWmsNotifyLog::getInboundFlag, flag)
+                .like(CloudWmsNotifyLog::getBizRef, needle)) > 0;
+    }
+
+    @Override
+    public void saveOrUpdateInOutNotifyEarly(InOutResultReportParam param, Long taskId, Long taskItemId, boolean sendHold, Integer tenantId) {
+        if (param == null || taskId == null || taskItemId == null) {
+            return;
+        }
+        boolean inbound = Boolean.TRUE.equals(param.getInbound());
+        int inboundFlag = inbound ? 1 : 0;
+        String needle = "taskItemId=" + taskItemId + ",";
+        String orderNo = param.getOrderNo();
+        Date now = new Date();
+        String body;
+        try {
+            body = objectMapper.writeValueAsString(param);
+        } catch (JsonProcessingException e) {
+            log.warn("浜戜粨9.1鏃〦nqueue搴忓垪鍖栧け璐� taskItemId={}", taskItemId, e);
+            return;
+        }
+        CloudWmsNotifyLog existing = getOne(new LambdaQueryWrapper<CloudWmsNotifyLog>()
+                .eq(CloudWmsNotifyLog::getReportType, getReportTypeInOutResult())
+                .eq(CloudWmsNotifyLog::getInboundFlag, inboundFlag)
+                .like(CloudWmsNotifyLog::getBizRef, needle)
+                .orderByDesc(CloudWmsNotifyLog::getId)
+                .last("LIMIT 1"));
+        if (existing != null) {
+            Integer st = existing.getNotifyStatus();
+            if (st != null && st == getNotifyStatusSuccess()) {
+                return;
+            }
+            existing.setRequestBody(body)
+                    .setSourceOrderNo(orderNo)
+                    .setWareHouseCode(param.getWareHouseId())
+                    .setSendHold(sendHold ? 1 : 0)
+                    .setUpdateTime(now);
+            if (tenantId != null) {
+                existing.setTenantId(tenantId);
+            }
+            updateById(existing);
+            return;
+        }
+        String bizRef = "taskId=" + taskId + ",taskItemId=" + taskItemId + ",orderNo=" + StringUtils.defaultString(orderNo);
+        CloudWmsNotifyLog row = new CloudWmsNotifyLog()
+                .setReportType(getReportTypeInOutResult())
+                .setRequestBody(body)
+                .setRetryCount(0)
+                .setBizRef(bizRef)
+                .setCreateTime(now)
+                .setUpdateTime(now)
+                .setSourceOrderNo(orderNo)
+                .setInboundFlag(inboundFlag)
+                .setWareHouseCode(param.getWareHouseId())
+                .setSendHold(sendHold ? 1 : 0)
+                .setSending(0);
+        if (tenantId != null) {
+            row.setTenantId(tenantId);
+        }
+        fillFromConfig(row);
+        save(row);
+    }
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
index 3d5eada..aaefbfc 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
@@ -8,6 +8,7 @@
 import com.vincent.rsf.server.api.service.WcsService;
 import com.vincent.rsf.server.api.utils.LocUtils;
 import com.vincent.rsf.server.common.constant.Constants;
+import com.vincent.rsf.server.common.utils.QuantityUtils;
 import com.vincent.rsf.server.manager.controller.params.CheckLocQueryParams;
 import com.vincent.rsf.server.manager.controller.params.LocToTaskParams;
 import com.vincent.rsf.server.manager.entity.*;
@@ -60,7 +61,6 @@
     private MatnrService matnrService;
     @Autowired
     private AsnOrderItemService asnOrderItemService;
-
     /** 鍏ュ簱/鍑哄簱淇濆瓨鍓嶏細鑻ヨ鏍兼垨鍨嬪彿涓虹┖鍒欎粠鐗╂枡甯﹀嚭 */
     private void fillSpecModelFromMatnr(LocItem item) {
         if (item == null || item.getMatnrId() == null) {
@@ -425,6 +425,13 @@
             if (!taskItemService.saveBatch(taskItems)) {
                 throw new CoolException("浠诲姟鏄庣粏鐢熸垚澶辫触锛侊紒");
             }
+            List<TaskItem> persistedItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
+            for (TaskItem ti : persistedItems) {
+                if (ti.getAnfme() == null) {
+                    throw new CoolException("浠诲姟鏄庣粏鏁伴噺寮傚父");
+                }
+                taskService.enqueueCloudWmsOutNotifyLogEarly(task, ti, QuantityUtils.toScaledBigDecimal(ti.getAnfme()));
+            }
         });
     }
 
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
index 0b2102a..48b828f 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -1477,6 +1477,7 @@
                         log.debug("[鎷f枡鍏ュ簱] 璁$畻鍚� pickedQty={}, minQty(鍓╀綑)={}, 灏嗘洿鏂� taskItem.anfme={}, taskItem.qty={}",
                                 pickedQty, minQty, minQty, pickedQty);
                         if (QuantityUtils.isNonNegative(minQty)) {
+                            enqueueCloudWmsOutNotifyLogEarly(task, taskItem, QuantityUtils.toScaledBigDecimal(pickedQty));
                             taskItem.setAnfme(minQty);
                             taskItem.setQty(pickedQty);
                             if (!taskItemService.updateById(taskItem)) {
@@ -1612,6 +1613,8 @@
         if (allItems.isEmpty()) {
             throw new CoolException("浠诲姟鏄庣粏涓虹┖");
         }
+        List<Long> allItemIds = allItems.stream().map(TaskItem::getId).filter(Objects::nonNull).collect(Collectors.toList());
+        Map<Long, BigDecimal> preQtyByItem = cloudWmsNotifyLogService.mapOutQtyFromInOutNotifyLogByTaskItemIds(allItemIds);
         Long loginUserId = SystemAuthUtils.getLoginUserId();
         if (loginUserId == null) {
             loginUserId = 1L;
@@ -1628,7 +1631,15 @@
         Map<String, Double> remainderByKey = new LinkedHashMap<>();
         for (Map.Entry<String, List<TaskItem>> e : byKey.entrySet()) {
             List<TaskItem> group = e.getValue();
-            double totalQty = group.stream().mapToDouble(ti -> ti.getQty() != null && ti.getQty() > 0 ? ti.getQty() : (ti.getAnfme() != null ? ti.getAnfme() : 0)).sum();
+            BigDecimal totalBd = BigDecimal.ZERO;
+            for (TaskItem ti : group) {
+                BigDecimal q = preQtyByItem.get(ti.getId());
+                if (q == null) {
+                    throw new CoolException("浜戜粨寰呭姙鏃犲嚭搴撴暟閲忥紝taskItemId=" + ti.getId());
+                }
+                totalBd = totalBd.add(q);
+            }
+            double totalQty = totalBd.setScale(6, RoundingMode.HALF_UP).doubleValue();
             TaskItem rep = group.get(0);
             TaskItem forDeduct = new TaskItem();
             forDeduct.setMatnrId(rep.getMatnrId()).setBatch(rep.getBatch()).setFieldsIndex(rep.getFieldsIndex()).setQty(totalQty);
@@ -1970,7 +1981,18 @@
         if (StringUtils.isBlank(ruleCode)) {
             throw new CoolException("褰撳墠涓氬姟锛�" + SerialRuleCode.SYS_STOCK_CODE + "锛岀紪鐮佽鍒欎笉瀛樺湪锛侊紒");
         }
-        Double sum = taskItems.stream().mapToDouble(TaskItem::getAnfme).sum();
+        List<Long> itemIds = taskItems.stream().map(TaskItem::getId).filter(Objects::nonNull).collect(Collectors.toList());
+        Map<Long, BigDecimal> preMap = cloudWmsNotifyLogService.mapOutQtyFromInOutNotifyLogByTaskItemIds(itemIds);
+        BigDecimal sumBd = BigDecimal.ZERO;
+        for (TaskItem ti : taskItems) {
+            BigDecimal q = preMap.get(ti.getId());
+            if (q == null) {
+                throw new CoolException("浜戜粨寰呭姙鏃犲嚭搴撴暟閲忥紝taskItemId=" + ti.getId());
+            }
+            sumBd = sumBd.add(q);
+        }
+        sumBd = sumBd.setScale(6, RoundingMode.HALF_UP);
+        Double sum = sumBd.doubleValue();
         stock.setCode(ruleCode)
                 .setUpdateBy(loginUserId)
                 .setUpdateTime(new Date())
@@ -1983,22 +2005,26 @@
                 WkOrder wkOrder = asnOrderService.getById(orderId);
                 if (wkOrder != null) {
                     Double curQty = wkOrder.getQty() != null ? wkOrder.getQty() : 0.0;
-                    Double newQty = QuantityUtils.roundToScale(curQty + sum);
-                    wkOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val).setQty(newQty);
+                    BigDecimal newQtyBd = BigDecimal.valueOf(curQty).add(sumBd).setScale(6, RoundingMode.HALF_UP);
+                    wkOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val).setQty(newQtyBd.doubleValue());
                     if (!asnOrderService.updateById(wkOrder)) {
                         throw new CoolException("鍑哄簱鍗曞畬鎴愭暟閲忔洿鏂板け璐ワ紒锛�");
                     }
                 }
-                // 鎸夊嚭搴撳崟鏄庣粏姹囨�绘湰娆′换鍔℃暟閲忥紝鏇存柊鍚勬槑缁� workQty
-                Map<Long, Double> sumByOrderItemId = taskItems.stream()
-                        .filter(ti -> ti.getOrderItemId() != null)
-                        .collect(Collectors.groupingBy(TaskItem::getOrderItemId,
-                                Collectors.summingDouble(ti -> ti.getAnfme() != null ? ti.getAnfme() : 0.0)));
-                for (Map.Entry<Long, Double> e : sumByOrderItemId.entrySet()) {
+                Map<Long, BigDecimal> sumByOrderItemId = new HashMap<>();
+                for (TaskItem ti : taskItems) {
+                    if (ti.getOrderItemId() == null) {
+                        continue;
+                    }
+                    BigDecimal q = preMap.get(ti.getId());
+                    sumByOrderItemId.merge(ti.getOrderItemId(), q, BigDecimal::add);
+                }
+                for (Map.Entry<Long, BigDecimal> e : sumByOrderItemId.entrySet()) {
                     WkOrderItem oi = outStockItemService.getById(e.getKey());
                     if (oi != null) {
                         Double wq = oi.getWorkQty() != null ? oi.getWorkQty() : 0.0;
-                        oi.setWorkQty(QuantityUtils.roundToScale(wq + e.getValue()));
+                        BigDecimal nwq = BigDecimal.valueOf(wq).add(e.getValue()).setScale(6, RoundingMode.HALF_UP);
+                        oi.setWorkQty(nwq.doubleValue());
                         if (!outStockItemService.updateById(oi)) {
                             throw new CoolException("鍑哄簱鍗曟槑缁嗘墽琛屾暟閲忔洿鏂板け璐ワ紒锛�");
                         }
@@ -2008,7 +2034,7 @@
         } else if (!Objects.isNull(orderItem) && StringUtils.isNotBlank(orderItem.getId() + "")) {
             WkOrder wkOrder = asnOrderService.getById(orderItem.getOrderId());
             double curHeaderQty = wkOrder.getQty() != null ? wkOrder.getQty() : 0.0;
-            Double qty = Math.round((curHeaderQty + sum) * 1000000) / 1000000.0;
+            Double qty = QuantityUtils.roundToScale(QuantityUtils.add(curHeaderQty, sum));
             wkOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val)
                     .setQty(qty);
             if (!asnOrderService.updateById(wkOrder)) {
@@ -2032,6 +2058,7 @@
             /**閫氳繃浠诲姟鏄庣粏涓殑taskId鏌ヨ,鑾峰彇TASK鐨勭洰鏍囧簱浣嶄俊鎭�*/
             StockItem stockItem = new StockItem();
             BeanUtils.copyProperties(item, stockItem);
+            stockItem.setAnfme(QuantityUtils.roundToScale(preMap.get(item.getId())));
             stockItem.setSourceItemId(item.getOrderItemId())
                     .setUpdateBy(loginUserId)
                     .setUpdateTime(new Date())
@@ -2854,6 +2881,32 @@
         }
     }
 
+    @Override
+    public void enqueueCloudWmsOutNotifyLogEarly(Task task, TaskItem taskItem, BigDecimal outQty) {
+        if (task == null || taskItem == null || outQty == null) {
+            return;
+        }
+        try {
+            Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, task.getOrgLoc()));
+            if (loc == null) {
+                return;
+            }
+            CloudInOutReportContext ctx = loadCloudInOutReportContext(task, loc, Collections.singletonList(taskItem), null, false);
+            if (ctx == null) {
+                return;
+            }
+            InOutResultReportParam param = buildInOutParamForItem(task, taskItem, false, outQty, ctx);
+            if (param == null) {
+                return;
+            }
+            String mode = resolveCloudWmsInoutReportMode();
+            boolean sendHold = CloudWmsInoutReportMode.MANUAL.equals(mode) || CloudWmsInoutReportMode.WAIT_ORDER.equals(mode);
+            cloudWmsNotifyLogService.saveOrUpdateInOutNotifyEarly(param, task.getId(), taskItem.getId(), sendHold, taskItem.getTenantId());
+        } catch (Exception e) {
+            log.warn("浜戜粨9.1鏃〦nqueue澶辫触 taskId={} taskItemId={}锛歿}", task.getId(), taskItem.getId(), e.getMessage());
+        }
+    }
+
     /**
      * 9.1 鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙
      * @param isInbound true 鍏ュ簱瀹屾垚锛宖alse 鍑哄簱瀹屾垚
@@ -2861,159 +2914,56 @@
      */
     private void reportInOutResultToCloud(Task task, Loc loc, List<TaskItem> taskItems, Set<Long> pkinItemIds, boolean isInbound) {
         try {
-            String locId = isInbound ? task.getTargLoc() : task.getOrgLoc();
-            String wareHouseId = null;
-            if (loc.getWarehouseId() != null) {
-                Warehouse wh = warehouseService.getById(loc.getWarehouseId());
-                if (wh != null) {
-                    wareHouseId = wh.getCode();
-                }
-            }
-            if (wareHouseId == null) {
-                log.warn("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙璺宠繃锛氫粨搴撶紪鐮佷负绌猴紝taskId={}", task.getId());
+            CloudInOutReportContext ctx = loadCloudInOutReportContext(task, loc, taskItems, pkinItemIds, isInbound);
+            if (ctx == null) {
                 return;
             }
-            Map<Long, String> sourceToOrderNo = new HashMap<>();
-            if (isInbound && pkinItemIds != null && !pkinItemIds.isEmpty()) {
-                List<WaitPakinItem> pakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().in(WaitPakinItem::getId, pkinItemIds));
-                for (WaitPakinItem p : pakinItems) {
-                    if (p.getAsnCode() != null) {
-                        sourceToOrderNo.put(p.getId(), p.getAsnCode());
-                    }
-                }
-            }
-            // 鍏ュ簱缁勬墭鍦烘櫙鍏佽鎸� WaitPakinItem.asnCode 鍏滃簳锛涘嚭搴撳厑璁告寜鍗� orderId+lineId锛堜簯浠� 8.3锛夊厹搴曪紝閬垮厤浠� plat_order_code 涓虹┖鏃惰璺宠繃
-            boolean hasOutboundCloudLine = !isInbound && taskItems.stream().anyMatch(ti ->
-                    ti != null && ti.getOrderId() != null && StringUtils.isNotBlank(ti.getPlatItemId()));
-            boolean hasCloudSource = taskItems.stream().anyMatch(this::hasCloudOrderRef)
-                    || (isInbound && !sourceToOrderNo.isEmpty())
-                    || hasOutboundCloudLine;
-            if (!hasCloudSource) {
-                log.info("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙璺宠繃锛氭棤浜戜粨鏉ユ簮鍗曟嵁锛宼askId={}", task.getId());
-                return;
-            }
-            Set<Long> orderIdSet = taskItems.stream()
-                    .filter(Objects::nonNull)
-                    .map(TaskItem::getOrderId)
-                    .filter(Objects::nonNull)
-                    .collect(Collectors.toSet());
-            Map<Long, WkOrder> orderById = new HashMap<>();
-            if (!orderIdSet.isEmpty()) {
-                for (WkOrder o : asnOrderService.listByIds(orderIdSet)) {
-                    if (o != null && o.getId() != null) {
-                        orderById.put(o.getId(), o);
-                    }
-                }
-            }
-            Set<String> orderCodeSet = new HashSet<>();
-            for (TaskItem ti : taskItems) {
-                if (ti == null) {
-                    continue;
-                }
-                String on = isInbound && ti.getSource() != null
-                        ? sourceToOrderNo.get(ti.getSource())
-                        : (ti.getPlatOrderCode() != null ? ti.getPlatOrderCode() : ti.getPlatWorkCode());
-                if (on == null && isInbound) {
-                    on = ti.getPlatOrderCode() != null ? ti.getPlatOrderCode() : ti.getPlatWorkCode();
-                }
-                if (on == null && !isInbound && ti.getOrderId() != null) {
-                    WkOrder o = orderById.get(ti.getOrderId());
-                    if (o != null) {
-                        on = StringUtils.isNotBlank(o.getPoCode()) ? o.getPoCode() : o.getCode();
-                    }
-                }
-                if (StringUtils.isNotBlank(on)) {
-                    orderCodeSet.add(on);
-                }
-            }
-            Map<String, WkOrder> orderByCode = new HashMap<>();
-            if (!orderCodeSet.isEmpty()) {
-                for (WkOrder o : asnOrderService.list(new LambdaQueryWrapper<WkOrder>().in(WkOrder::getCode, orderCodeSet))) {
-                    if (o != null && StringUtils.isNotBlank(o.getCode())) {
-                        orderByCode.put(o.getCode(), o);
-                    }
-                }
-                for (WkOrder o : asnOrderService.list(new LambdaQueryWrapper<WkOrder>().in(WkOrder::getPoCode, orderCodeSet))) {
-                    if (o != null && StringUtils.isNotBlank(o.getPoCode())) {
-                        orderByCode.putIfAbsent(o.getPoCode(), o);
-                    }
-                }
-            }
+            List<Long> reportItemIds = taskItems.stream().map(TaskItem::getId).filter(Objects::nonNull).collect(Collectors.toList());
+            Map<Long, BigDecimal> outQtyByItem = isInbound ? Collections.emptyMap()
+                    : cloudWmsNotifyLogService.mapOutQtyFromInOutNotifyLogByTaskItemIds(reportItemIds);
             ObjectMapper om = new ObjectMapper();
             Date now = new Date();
-            Map<String, List<InOutResultReportParam>> byOrder = new LinkedHashMap<>();
-            for (TaskItem item : taskItems) {
-                if (item == null) {
-                    continue;
-                }
-                boolean hasTaskItemCloudRef = hasCloudOrderRef(item);
-                boolean hasInboundAsnFallbackRef = isInbound
-                        && item.getSource() != null
-                        && StringUtils.isNotBlank(sourceToOrderNo.get(item.getSource()));
-                boolean hasOutboundCloudLineRef = !isInbound && item.getOrderId() != null && StringUtils.isNotBlank(item.getPlatItemId());
-                if (!hasTaskItemCloudRef && !hasInboundAsnFallbackRef && !hasOutboundCloudLineRef) {
-                    continue;
-                }
-                WkOrder asnOrder = null;
-                if (item.getOrderId() != null) {
-                    asnOrder = orderById.get(item.getOrderId());
-                }
-                String orderNo = isInbound ? sourceToOrderNo.get(item.getSource()) : (item.getPlatOrderCode() != null ? item.getPlatOrderCode() : item.getPlatWorkCode());
-                if (orderNo == null && isInbound) {
-                    orderNo = item.getPlatOrderCode() != null ? item.getPlatOrderCode() : item.getPlatWorkCode();
-                }
-                if (orderNo == null && !isInbound && asnOrder != null) {
-                    orderNo = StringUtils.isNotBlank(asnOrder.getPoCode()) ? asnOrder.getPoCode() : asnOrder.getCode();
-                }
-                if (orderNo == null || item.getMatnrCode() == null) {
-                    continue;
-                }
-                if (asnOrder == null) {
-                    asnOrder = orderByCode.get(orderNo);
-                }
-                InOutResultReportParam param = new InOutResultReportParam()
-                        .setOrderNo(orderNo)
-                        .setPlanNo(item.getPlatWorkCode())
-                        .setWkType(item.getWkType())
-                        .setUnitNo(item.getUnit())
-                        .setLineId(item.getPlatItemId())
-                        .setWareHouseId(wareHouseId)
-                        .setDocWarehouseNo(asnOrder != null ? asnOrder.getDocTaskWarehouseNo() : null)
-                        .setOrgNo(asnOrder != null ? asnOrder.getDocOrgNo() : null)
-                        .setInWarehouseNo(isInbound && asnOrder != null ? asnOrder.getDocInWarehouseNo() : null)
-                        .setOutWarehouseNo(!isInbound && asnOrder != null ? asnOrder.getDocOutWarehouseNo() : null)
-                        .setLocId(locId)
-                        .setMatNr(item.getMatnrCode())
-                        .setQty(item.getAnfme() != null ? String.valueOf(item.getAnfme()) : "0")
-                        .setBatch(item.getBatch())
-                        .setInbound(isInbound)
-                        .setBarcode(task.getBarcode());
-                byOrder.computeIfAbsent(orderNo, k -> new ArrayList<>()).add(param);
-            }
             String mode = resolveCloudWmsInoutReportMode();
             boolean sendHold = CloudWmsInoutReportMode.MANUAL.equals(mode) || CloudWmsInoutReportMode.WAIT_ORDER.equals(mode);
-            for (Map.Entry<String, List<InOutResultReportParam>> e : byOrder.entrySet()) {
-                String orderNo = e.getKey();
-                for (InOutResultReportParam param : e.getValue()) {
-                    try {
-                        String requestBody = om.writeValueAsString(param);
-                        CloudWmsNotifyLog notifyLog = new CloudWmsNotifyLog()
-                                .setReportType(cloudWmsNotifyLogService.getReportTypeInOutResult())
-                                .setRequestBody(requestBody)
-                                .setRetryCount(0)
-                                .setBizRef("taskId=" + task.getId() + ",orderNo=" + orderNo)
-                                .setCreateTime(now)
-                                .setUpdateTime(now)
-                                .setSourceOrderNo(orderNo)
-                                .setInboundFlag(isInbound ? 1 : 0)
-                                .setWareHouseCode(wareHouseId)
-                                .setSendHold(sendHold ? 1 : 0)
-                                .setSending(0);
-                        cloudWmsNotifyLogService.fillFromConfig(notifyLog);
-                        cloudWmsNotifyLogService.save(notifyLog);
-                    } catch (JsonProcessingException ex) {
-                        log.warn("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙钀藉簱澶辫触锛堜笉褰卞搷涓绘祦绋嬶級锛宼askId={}锛宱rderNo={}锛歿}", task.getId(), orderNo, ex.getMessage());
-                    }
+            for (TaskItem item : taskItems) {
+                if (item == null || item.getId() == null) {
+                    continue;
+                }
+                BigDecimal outboundQty = isInbound ? null : outQtyByItem.get(item.getId());
+                if (!isInbound && outboundQty == null) {
+                    log.warn("浜戜粨9.1璺宠繃锛氬緟鍔炴棤鍑哄簱鏁伴噺锛宼askId={}锛宼askItemId={}", task.getId(), item.getId());
+                    continue;
+                }
+                InOutResultReportParam param = buildInOutParamForItem(task, item, isInbound, outboundQty, ctx);
+                if (param == null) {
+                    continue;
+                }
+                if (!isInbound && cloudWmsNotifyLogService.hasInOutNotifyRowForTaskItem(item.getId(), false)) {
+                    continue;
+                }
+                if (isInbound && cloudWmsNotifyLogService.hasInOutNotifyRowForTaskItem(item.getId(), true)) {
+                    continue;
+                }
+                String orderNo = param.getOrderNo();
+                try {
+                    String requestBody = om.writeValueAsString(param);
+                    String bizRef = "taskId=" + task.getId() + ",taskItemId=" + item.getId() + ",orderNo=" + orderNo;
+                    CloudWmsNotifyLog notifyLog = new CloudWmsNotifyLog()
+                            .setReportType(cloudWmsNotifyLogService.getReportTypeInOutResult())
+                            .setRequestBody(requestBody)
+                            .setRetryCount(0)
+                            .setBizRef(bizRef)
+                            .setCreateTime(now)
+                            .setUpdateTime(now)
+                            .setSourceOrderNo(orderNo)
+                            .setInboundFlag(isInbound ? 1 : 0)
+                            .setWareHouseCode(ctx.wareHouseId)
+                            .setSendHold(sendHold ? 1 : 0)
+                            .setSending(0);
+                    cloudWmsNotifyLogService.fillFromConfig(notifyLog);
+                    cloudWmsNotifyLogService.save(notifyLog);
+                } catch (JsonProcessingException ex) {
+                    log.warn("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙钀藉簱澶辫触锛堜笉褰卞搷涓绘祦绋嬶級锛宼askId={}锛宱rderNo={}锛歿}", task.getId(), orderNo, ex.getMessage());
                 }
             }
         } catch (Exception e) {
@@ -3021,6 +2971,165 @@
         }
     }
 
+    private CloudInOutReportContext loadCloudInOutReportContext(Task task, Loc loc, List<TaskItem> taskItems, Set<Long> pkinItemIds, boolean isInbound) {
+        if (task == null || loc == null || taskItems == null || taskItems.isEmpty()) {
+            return null;
+        }
+        String locId = isInbound ? task.getTargLoc() : task.getOrgLoc();
+        String wareHouseId = null;
+        if (loc.getWarehouseId() != null) {
+            Warehouse wh = warehouseService.getById(loc.getWarehouseId());
+            if (wh != null) {
+                wareHouseId = wh.getCode();
+            }
+        }
+        if (wareHouseId == null) {
+            log.warn("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙璺宠繃锛氫粨搴撶紪鐮佷负绌猴紝taskId={}", task.getId());
+            return null;
+        }
+        Map<Long, String> sourceToOrderNo = new HashMap<>();
+        if (isInbound && pkinItemIds != null && !pkinItemIds.isEmpty()) {
+            List<WaitPakinItem> pakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().in(WaitPakinItem::getId, pkinItemIds));
+            for (WaitPakinItem p : pakinItems) {
+                if (p.getAsnCode() != null) {
+                    sourceToOrderNo.put(p.getId(), p.getAsnCode());
+                }
+            }
+        }
+        boolean hasOutboundCloudLine = !isInbound && taskItems.stream().anyMatch(ti ->
+                ti != null && ti.getOrderId() != null && StringUtils.isNotBlank(ti.getPlatItemId()));
+        boolean hasCloudSource = taskItems.stream().anyMatch(this::hasCloudOrderRef)
+                || (isInbound && !sourceToOrderNo.isEmpty())
+                || hasOutboundCloudLine;
+        if (!hasCloudSource) {
+            log.info("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙璺宠繃锛氭棤浜戜粨鏉ユ簮鍗曟嵁锛宼askId={}", task.getId());
+            return null;
+        }
+        Set<Long> orderIdSet = taskItems.stream()
+                .filter(Objects::nonNull)
+                .map(TaskItem::getOrderId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        Map<Long, WkOrder> orderById = new HashMap<>();
+        if (!orderIdSet.isEmpty()) {
+            for (WkOrder o : asnOrderService.listByIds(orderIdSet)) {
+                if (o != null && o.getId() != null) {
+                    orderById.put(o.getId(), o);
+                }
+            }
+        }
+        Set<String> orderCodeSet = new HashSet<>();
+        for (TaskItem ti : taskItems) {
+            if (ti == null) {
+                continue;
+            }
+            String on = isInbound && ti.getSource() != null
+                    ? sourceToOrderNo.get(ti.getSource())
+                    : (ti.getPlatOrderCode() != null ? ti.getPlatOrderCode() : ti.getPlatWorkCode());
+            if (on == null && isInbound) {
+                on = ti.getPlatOrderCode() != null ? ti.getPlatOrderCode() : ti.getPlatWorkCode();
+            }
+            if (on == null && !isInbound && ti.getOrderId() != null) {
+                WkOrder o = orderById.get(ti.getOrderId());
+                if (o != null) {
+                    on = StringUtils.isNotBlank(o.getPoCode()) ? o.getPoCode() : o.getCode();
+                }
+            }
+            if (StringUtils.isNotBlank(on)) {
+                orderCodeSet.add(on);
+            }
+        }
+        Map<String, WkOrder> orderByCode = new HashMap<>();
+        if (!orderCodeSet.isEmpty()) {
+            for (WkOrder o : asnOrderService.list(new LambdaQueryWrapper<WkOrder>().in(WkOrder::getCode, orderCodeSet))) {
+                if (o != null && StringUtils.isNotBlank(o.getCode())) {
+                    orderByCode.put(o.getCode(), o);
+                }
+            }
+            for (WkOrder o : asnOrderService.list(new LambdaQueryWrapper<WkOrder>().in(WkOrder::getPoCode, orderCodeSet))) {
+                if (o != null && StringUtils.isNotBlank(o.getPoCode())) {
+                    orderByCode.putIfAbsent(o.getPoCode(), o);
+                }
+            }
+        }
+        return new CloudInOutReportContext(wareHouseId, locId, sourceToOrderNo, orderById, orderByCode);
+    }
+
+    private InOutResultReportParam buildInOutParamForItem(Task task, TaskItem item, boolean isInbound,
+            BigDecimal outboundQtyResolved, CloudInOutReportContext ctx) {
+        if (task == null || item == null || ctx == null) {
+            return null;
+        }
+        boolean hasTaskItemCloudRef = hasCloudOrderRef(item);
+        boolean hasInboundAsnFallbackRef = isInbound
+                && item.getSource() != null
+                && StringUtils.isNotBlank(ctx.sourceToOrderNo.get(item.getSource()));
+        boolean hasOutboundCloudLineRef = !isInbound && item.getOrderId() != null && StringUtils.isNotBlank(item.getPlatItemId());
+        if (!hasTaskItemCloudRef && !hasInboundAsnFallbackRef && !hasOutboundCloudLineRef) {
+            return null;
+        }
+        WkOrder asnOrder = null;
+        if (item.getOrderId() != null) {
+            asnOrder = ctx.orderById.get(item.getOrderId());
+        }
+        String orderNo = isInbound ? ctx.sourceToOrderNo.get(item.getSource()) : (item.getPlatOrderCode() != null ? item.getPlatOrderCode() : item.getPlatWorkCode());
+        if (orderNo == null && isInbound) {
+            orderNo = item.getPlatOrderCode() != null ? item.getPlatOrderCode() : item.getPlatWorkCode();
+        }
+        if (orderNo == null && !isInbound && asnOrder != null) {
+            orderNo = StringUtils.isNotBlank(asnOrder.getPoCode()) ? asnOrder.getPoCode() : asnOrder.getCode();
+        }
+        if (orderNo == null || item.getMatnrCode() == null) {
+            return null;
+        }
+        if (!isInbound && outboundQtyResolved == null) {
+            return null;
+        }
+        if (asnOrder == null) {
+            asnOrder = ctx.orderByCode.get(orderNo);
+        }
+        String qtyStr;
+        if (isInbound) {
+            qtyStr = item.getAnfme() != null ? QuantityUtils.toPlainQtyString(QuantityUtils.toScaledBigDecimal(item.getAnfme())) : "0";
+        } else {
+            qtyStr = QuantityUtils.toPlainQtyString(outboundQtyResolved);
+        }
+        return new InOutResultReportParam()
+                .setOrderNo(orderNo)
+                .setPlanNo(item.getPlatWorkCode())
+                .setWkType(item.getWkType())
+                .setUnitNo(item.getUnit())
+                .setLineId(item.getPlatItemId())
+                .setWareHouseId(ctx.wareHouseId)
+                .setDocWarehouseNo(asnOrder != null ? asnOrder.getDocTaskWarehouseNo() : null)
+                .setOrgNo(asnOrder != null ? asnOrder.getDocOrgNo() : null)
+                .setInWarehouseNo(isInbound && asnOrder != null ? asnOrder.getDocInWarehouseNo() : null)
+                .setOutWarehouseNo(!isInbound && asnOrder != null ? asnOrder.getDocOutWarehouseNo() : null)
+                .setLocId(ctx.locId)
+                .setMatNr(item.getMatnrCode())
+                .setQty(qtyStr)
+                .setBatch(item.getBatch())
+                .setInbound(isInbound)
+                .setBarcode(task.getBarcode());
+    }
+
+    private static final class CloudInOutReportContext {
+        final String wareHouseId;
+        final String locId;
+        final Map<Long, String> sourceToOrderNo;
+        final Map<Long, WkOrder> orderById;
+        final Map<String, WkOrder> orderByCode;
+
+        private CloudInOutReportContext(String wareHouseId, String locId, Map<Long, String> sourceToOrderNo,
+                Map<Long, WkOrder> orderById, Map<String, WkOrder> orderByCode) {
+            this.wareHouseId = wareHouseId;
+            this.locId = locId;
+            this.sourceToOrderNo = sourceToOrderNo;
+            this.orderById = orderById;
+            this.orderByCode = orderByCode;
+        }
+    }
+
     private boolean hasCloudOrderRef(TaskItem item) {
         if (item == null) {
             return false;

--
Gitblit v1.9.1