From 400881f9103218313c552b254c8312b54a24f563 Mon Sep 17 00:00:00 2001
From: cl <1442464845@qq.com>
Date: 星期五, 08 五月 2026 12:36:59 +0800
Subject: [PATCH] 转历史单优化

---
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java |  463 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 390 insertions(+), 73 deletions(-)

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 581c448..c679e2b 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
@@ -27,6 +27,7 @@
 import com.vincent.rsf.framework.common.R;
 import com.vincent.rsf.framework.exception.CoolException;
 import com.vincent.rsf.server.api.utils.LocUtils;
+import com.vincent.rsf.server.manager.constant.CloudWmsInoutReportMode;
 import com.vincent.rsf.server.manager.controller.params.GenerateTaskParams;
 import com.vincent.rsf.server.manager.entity.CloudWmsNotifyLog;
 import com.vincent.rsf.server.manager.entity.*;
@@ -55,6 +56,8 @@
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.support.TransactionSynchronization;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
 import org.springframework.web.client.HttpStatusCodeException;
 import org.springframework.web.client.RestTemplate;
 
@@ -211,6 +214,7 @@
                     throw new CoolException("缁勬墭鏄庣粏淇墽琛屾暟閲忎慨鏀瑰け璐ワ紒锛�");
                 }
             });
+            syncAsnOrderItemBarcodeByPakin(waitPakinItems, pakin.getBarcode(), loginUserId);
         });
 
         if (!waitPakinService.update(new LambdaUpdateWrapper<WaitPakin>()
@@ -301,6 +305,7 @@
                     throw new CoolException("缁勬墭鏄庣粏淇墽琛屾暟閲忎慨鏀瑰け璐ワ紒锛�");
                 }
             });
+            syncAsnOrderItemBarcodeByPakin(waitPakinItems, pakin.getBarcode(), loginUserId);
         });
 
         if (!waitPakinService.update(new LambdaUpdateWrapper<WaitPakin>()
@@ -453,6 +458,7 @@
                     throw new CoolException("缁勬墭鏄庣粏淇墽琛屾暟閲忎慨鏀瑰け璐ワ紒锛�");
                 }
             });
+            syncAsnOrderItemBarcodeByPakin(waitPakinItems, pakin.getBarcode(), loginUserId);
         });
 
         if (!waitPakinService.update(new LambdaUpdateWrapper<WaitPakin>()
@@ -463,6 +469,27 @@
             throw new CoolException("缁勬嫋鐘舵�佷慨鏀瑰け璐ワ紒锛�");
         }
         return R.ok("浠诲姟鐢熸垚瀹屾瘯锛�");
+    }
+
+    /** 缁勬墭鍚庡皢閫氱煡鍗曟槑缁嗘潯鐮佸洖鍐欎负鏂欑鐮� */
+    private void syncAsnOrderItemBarcodeByPakin(List<WaitPakinItem> waitPakinItems, String pakinBarcode, Long loginUserId) {
+        if (waitPakinItems == null || waitPakinItems.isEmpty() || StringUtils.isBlank(pakinBarcode)) {
+            return;
+        }
+        Set<Long> asnItemIds = waitPakinItems.stream()
+                .map(WaitPakinItem::getAsnItemId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        if (asnItemIds.isEmpty()) {
+            return;
+        }
+        if (!asnOrderItemService.update(new LambdaUpdateWrapper<WkOrderItem>()
+                .in(WkOrderItem::getId, asnItemIds)
+                .set(WkOrderItem::getBarcode, pakinBarcode)
+                .set(WkOrderItem::getUpdateBy, loginUserId)
+                .set(WkOrderItem::getUpdateTime, new Date()))) {
+            throw new CoolException("閫氱煡鍗曟槑缁嗘潯鐮佸洖鍐欏け璐ワ紒锛�");
+        }
     }
 
     /**
@@ -597,15 +624,44 @@
         if (StringUtils.isNotBlank(task.getTaskCode())) {
             rcsBusTaskNoticeService.notifyTaskStatus(task.getTaskCode(), task.getTaskStatus());
         }
+        final Long taskIdForFinish = task.getId();
+        if (TransactionSynchronizationManager.isSynchronizationActive()) {
+            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
+                @Override
+                public void afterCommit() {
+                    taskService.runStockFinishAfterManualComplete(taskIdForFinish);
+                }
+            });
+        }
         return task;
     }
 
+    @Override
+    public void runStockFinishAfterManualComplete(Long taskId) {
+        if (taskId == null) {
+            return;
+        }
+        try {
+            Task t = taskService.getById(taskId);
+            if (t == null) {
+                return;
+            }
+            if (Objects.equals(t.getTaskStatus(), TaskStsType.COMPLETE_IN.id)) {
+                taskService.complateInTask(Collections.singletonList(t));
+            } else if (Objects.equals(t.getTaskStatus(), TaskStsType.COMPLETE_OUT.id)) {
+                taskService.completeTask(Collections.singletonList(t));
+            }
+        } catch (Exception e) {
+            log.warn("鎵嬪姩瀹岀粨鍚庣珛鍗冲簱瀛樻敹灏惧け璐ワ紝灏嗙敱瀹氭椂浠诲姟閲嶈瘯锛宼askId={}锛歿}", taskId, e.getMessage());
+        }
+    }
+
     /**
-     * 鍏ㄧ増鍑哄簱瀹岀粨锛氭墸闄ゅ簱浣嶆暟閲忥紝灏嗗簱浣嶇姸鎬佽涓虹┖
+     * 鍏ㄦ澘鍑哄簱瀹岀粨锛氭墸闄ゅ簱浣嶆暟閲忥紝灏嗗簱浣嶇姸鎬佽涓虹┖
      *
      * @param id 浠诲姟ID
      * @param loginUserId 鐧诲綍鐢ㄦ埛ID
-     * @param notifyRcsFromAdmin 绠$悊鍚庡彴鍏ㄧ増鍑哄簱瀹岀粨鎺ュ彛涓� true 鏃堕�氱煡 RCS锛涘畾鏃�/PDA 绛変负 false
+     * @param notifyRcsFromAdmin 绠$悊鍚庡彴鍏ㄦ澘鍑哄簱瀹岀粨鎺ュ彛涓� true 鏃堕�氱煡 RCS锛涘畾鏃�/PDA 绛変负 false
      * @return 浠诲姟瀵硅薄
      */
     @Override
@@ -619,9 +675,9 @@
             throw new CoolException("浠诲姟涓嶅瓨鍦紒锛�");
         }
 
-        // 妫�鏌ヤ换鍔$被鍨嬫槸鍚︿负鍏ㄧ増鍑哄簱
+        // 妫�鏌ヤ换鍔$被鍨嬫槸鍚︿负鍏ㄦ澘鍑哄簱
         if (!task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) {
-            throw new CoolException("褰撳墠浠诲姟涓嶆槸鍏ㄧ増鍑哄簱浠诲姟锛屾棤娉曟墽琛屾鎿嶄綔锛侊紒");
+            throw new CoolException("褰撳墠浠诲姟涓嶆槸鍏ㄦ澘鍑哄簱浠诲姟锛屾棤娉曟墽琛屾鎿嶄綔锛侊紒");
         }
 
         // 妫�鏌ヤ换鍔$姸鎬侊細蹇呴』鏄�199锛圵AVE_SEED锛夌姸鎬佹墠鑳芥墜鍔ㄥ畬缁�
@@ -938,8 +994,11 @@
         if (!taskService.updateById(task)) {
             throw new CoolException("浠诲姟鐘舵�佷慨鏀瑰け璐ワ紒锛�");
         }
-        // 9.1 鍏�/鍑哄簱缁撴灉涓婃姤锛氭嫞鏂欏啀鍏ュ簱/鐩樼偣鍐嶅叆搴撳畬鎴愬悗閫氱煡浜戜粨锛堜笌瀹氭椂浠诲姟闂幆涓�鑷达級
-        reportInOutResultToCloud(task, loc, taskItems, null, true);
+        // 鐩樼偣鍐嶅叆搴撲笂鎶ヤ簯浠撳叆搴撳畬鎴愶紱鎷f枡鍐嶅叆搴撹烦杩囷紙鍏ュ簱渚� qty 鍙� anfme 涓烘墭鐩樹綑閲忥紱浜戜粨鍑哄簱瀹炲彂鍦ㄥ嚭搴撳畬鎴愪晶锛�
+        // reportInOutResultToCloud(task, loc, taskItems, null, true);
+        if (!TaskType.TASK_TYPE_PICK_IN.type.equals(task.getTaskType())) {
+            reportInOutResultToCloud(task, loc, taskItems, null, true);
+        }
     }
 
     /**
@@ -1169,6 +1228,14 @@
                         .set(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_F.type))) {
                     throw new CoolException("婧愬簱浣嶇姸鎬佷慨鏀瑰け璐ワ紒锛�");
                 }
+            } else if (task.getTaskType().equals(TaskType.TASK_TYPE_EMPITY_OUT.type)) {
+                // 绌烘澘鍑哄簱锛氬缓鍗曟椂 D鈫扲锛屽彇娑堟仮澶嶄负绌烘澘 D
+                if (!locService.update(new LambdaUpdateWrapper<Loc>()
+                        .eq(Loc::getCode, task.getOrgLoc())
+                        .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_R.type)
+                        .set(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_D.type))) {
+                    throw new CoolException("绌烘澘鍑哄簱婧愬簱浣嶆仮澶嶇┖鏉垮け璐ワ紒锛�");
+                }
             }
 
             if (!Objects.isNull(task.getWarehType()) && task.getWarehType().equals(WarehType.WAREHOUSE_TYPE_AGV.val)) {
@@ -1212,7 +1279,7 @@
                             throw new CoolException("搴撲綅淇℃伅淇敼澶辫触锛侊紒");
                         }
                         //鍑哄簱
-                        if (item.getWkType().equals(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type))) {
+                        if (Objects.equals(item.getWkType(), OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type)) {
                             //搴撳瓨鍑哄簱
                         } else if (task.getResource().equals(TaskResouceType.TASK_RESOUCE_WAVE_TYPE.val)) {
                             WaveItem waveItem = waveItemService.getById(item.getSource());
@@ -1234,7 +1301,7 @@
                                 throw new CoolException("娉㈡鏇存柊澶辫触锛侊紒");
                             }
 
-                        } else if (item.getWkType().equals(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_OTHER.type))) {
+                        } else if (Objects.equals(item.getWkType(), OrderWorkType.ORDER_WORK_TYPE_OTHER.type)) {
                             //鍏跺畠鍑哄簱
 
                         } else {
@@ -1413,6 +1480,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)) {
@@ -1548,6 +1616,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;
@@ -1564,7 +1634,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);
@@ -1630,10 +1708,16 @@
                 if (group == null || group.isEmpty()) continue;
                 TaskItem rep = group.get(0);
                 TaskItem ti = new TaskItem();
-                ti.setTaskId(pickInTask.getId());
-                ti.setMatnrId(rep.getMatnrId()).setMaktx(rep.getMaktx()).setMatnrCode(rep.getMatnrCode());
-                ti.setBatch(rep.getBatch()).setFieldsIndex(rep.getFieldsIndex()).setUnit(rep.getUnit()).setSpec(rep.getSpec()).setModel(rep.getModel());
-                ti.setAnfme(rem).setQty(0.0);
+                BeanUtils.copyProperties(rep, ti);
+                ti.setId(null)
+                        .setTaskId(pickInTask.getId())
+                        .setAnfme(rem)
+                        .setQty(0.0)
+                        .setWorkQty(0.0)
+                        .setCreateBy(loginUserId)
+                        .setUpdateBy(loginUserId)
+                        .setCreateTime(new Date())
+                        .setUpdateTime(new Date());
                 taskItemService.save(ti);
                 LocItemWorking w = new LocItemWorking();
                 w.setTaskId(pickInTask.getId());
@@ -1759,7 +1843,7 @@
             try {
                 // 鏍规嵁浠诲姟绫诲瀷鏇存柊搴撲綅鏄庣粏
                 if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) {
-                    // 鍏ㄧ増鍑哄簱锛氫笉鍒犻櫎搴撲綅鏄庣粏锛岀瓑寰匬DA蹇�熸嫞璐х‘璁ゆ椂鍐嶅垹闄�
+                    // 鍏ㄦ澘鍑哄簱锛氫笉鍒犻櫎搴撲綅鏄庣粏锛岀瓑寰匬DA蹇�熸嫞璐х‘璁ゆ椂鍐嶅垹闄�
                     // subtractLocItem(loc); // 宸茬Щ闄わ紝鏀逛负鍦╟ompleteFullOutStock涓垹闄�
                 } else if (!TaskType.TASK_TYPE_PICK_AGAIN_OUT.type.equals(task.getTaskType())) {
                     // 閮ㄥ垎鍑哄簱锛堝鐩樼偣鍑哄簱锛夛細鏍规嵁TaskItem鏁伴噺鎵e噺搴撲綅鏄庣粏锛涙嫞鏂欏嚭搴撳湪鐢熸垚鎷f枡鍏ュ簱鍗曟椂鎵e噺
@@ -1772,12 +1856,12 @@
         }
 
         //娣诲姞鍑哄叆搴撹褰曚俊鎭�
-        Map<Short, List<TaskItem>> listMap = taskItems.stream().collect(Collectors.groupingBy(TaskItem::getWkType));
+        Map<String, List<TaskItem>> listMap = taskItems.stream().collect(Collectors.groupingBy(TaskItem::getWkType));
         /***鑾峰彇搴撳瓨鍑哄簱鍊硷紝濡傛灉涓虹┖琛ㄧず姝e父鍗曟嵁鍑哄簱锛岄潪绌鸿〃鏄庢槸搴撳瓨鍑哄簱
          * 1. 搴撳瓨鍑哄簱娌℃湁鍗曟嵁淇℃伅锛屽崟鎹俊鎭粯璁や负绌�
          * 2. 鍗曟嵁搴撳瓨闇�閫氳繃娉㈡鏌ヨ鍘熷鍗曟嵁淇℃伅锛屽皢鍗曟嵁淇℃伅濉叆stock涓�
          * */
-        List<TaskItem> list = listMap.get(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type));
+        List<TaskItem> list = listMap.get(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type);
         if (Objects.isNull(list) || list.isEmpty()) {
             Map<Long, List<TaskItem>> maps = taskItems.stream().collect(Collectors.groupingBy(TaskItem::getSource));
             maps.keySet().forEach(key -> {
@@ -1828,7 +1912,7 @@
             // 鎷f枡鍑哄簱/鐩樼偣鍑哄簱锛氬湪鏈敓鎴愭嫞鏂欏叆搴撳崟涔嬪墠淇濇寔 R.棰勭害鍑哄簱锛屽惁鍒欎笅鍙戜换鍔℃椂鏌ヤ笉鍒拌搴撲綅锛堝彧鏌� F+R锛夊鑷粹�滃簱瀛樹笉瓒斥��
             // 绛� PDA 纭骞剁敓鎴愭嫞鏂欏叆搴撲换鍔℃椂锛屽啀鍦� pickOrCheckTask 涓皢鐩爣搴撲綅鏀逛负 S.棰勭害鍏ュ簱
         } else if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) {
-            // 鍏ㄧ増鍑哄簱锛氫笉鏇存柊搴撲綅鐘舵�佷负O锛岀瓑寰匬DA蹇�熸嫞璐х‘璁ゆ椂鍐嶆洿鏂�
+            // 鍏ㄦ澘鍑哄簱锛氫笉鏇存柊搴撲綅鐘舵�佷负O锛岀瓑寰匬DA蹇�熸嫞璐х‘璁ゆ椂鍐嶆洿鏂�
             // 搴撲綅鐘舵�佷繚鎸佸師鏍凤紙R.鍑哄簱棰勭害鐘舵�侊級
         } else {
             /**淇敼涓哄簱浣嶇姸鎬佷负O.绌哄簱*/
@@ -1906,7 +1990,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())
@@ -1919,22 +2014,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("鍑哄簱鍗曟槑缁嗘墽琛屾暟閲忔洿鏂板け璐ワ紒锛�");
                         }
@@ -1943,19 +2042,20 @@
             }
         } else if (!Objects.isNull(orderItem) && StringUtils.isNotBlank(orderItem.getId() + "")) {
             WkOrder wkOrder = asnOrderService.getById(orderItem.getOrderId());
-            Double qty = Math.round((wkOrder.getQty() + sum) * 1000000) / 1000000.0;
+            double curHeaderQty = wkOrder.getQty() != null ? wkOrder.getQty() : 0.0;
+            Double qty = QuantityUtils.roundToScale(QuantityUtils.add(curHeaderQty, sum));
             wkOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val)
                     .setQty(qty);
             if (!asnOrderService.updateById(wkOrder)) {
                 throw new CoolException("鍑哄簱鍗曟洿鏂板け璐ワ紒锛�");
             }
-            stock.setWkType(Short.parseShort(wkOrder.getWkType()))
+            stock.setWkType(wkOrder.getWkType())
                     .setType(OrderType.ORDER_OUT.type);
         } else if (!Objects.isNull(diffItem)) {
-            stock.setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_CHECK.type))
+            stock.setWkType(OrderWorkType.ORDER_WORK_TYPE_STOCK_CHECK.type)
                     .setType(OrderType.ORDER_CHECK.type);
         } else {
-            stock.setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type))
+            stock.setWkType(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type)
                     .setMemo("搴撳瓨鍑哄簱锛屾棤鍗曟嵁淇℃伅锛侊紒")
                     .setType(OrderType.ORDER_OUT.type);
         }
@@ -1967,6 +2067,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())
@@ -2081,6 +2182,15 @@
                     log.error("========== RCS浠诲姟涓嬪彂澶辫触 ==========");
                     log.error("绔欑偣涓嶅瓨鍦紒锛佷换鍔$紪鐮侊細{}锛岀洰鏍囩珯鐐癸細{}", task.getTaskCode(), task.getTargSite());
                     continue;
+                }
+                // 鍑哄簱涓嬪彂鍓嶆牎楠岀珯鐐圭姸鎬佷笌鍑哄簱鑳藉姏
+                if (task.getTaskType() >= TaskType.TASK_TYPE_OUT.type) {
+                    if (!Integer.valueOf(1).equals(station.getStatus()) || !Integer.valueOf(1).equals(station.getOutAble())) {
+                        log.error("========== RCS浠诲姟涓嬪彂澶辫触 ==========");
+                        log.error("绔欑偣涓嶅彲鍑哄簱涓嬪彂锛侊紒浠诲姟缂栫爜锛歿}锛岀洰鏍囩珯鐐癸細{}锛岀珯鐐圭姸鎬�(status)锛歿}锛岃兘鍑�(outAble)锛歿}",
+                                task.getTaskCode(), task.getTargSite(), station.getStatus(), station.getOutAble());
+                        continue;
+                    }
                 }
             }
 
@@ -2386,7 +2496,7 @@
     /**
      * @author Ryan
      * @date 2025/5/20
-     * @description: 鎵e噺搴撳瓨鏄庣粏锛堝叏鐗堝嚭搴擄細鍒犻櫎鎵�鏈夊簱浣嶆槑缁嗭級
+     * @description: 鎵e噺搴撳瓨鏄庣粏锛堝叏鏉垮嚭搴擄細鍒犻櫎鎵�鏈夊簱浣嶆槑缁嗭級
      * @version 1.0
      */
     @Transactional(rollbackFor = Exception.class)
@@ -2549,6 +2659,10 @@
             WkOrder o = asnOrderService.getById(e.getKey());
             if (o != null) {
                 Double newQty = QuantityUtils.roundToScale(QuantityUtils.add(o.getQty() != null ? o.getQty() : 0.0, e.getValue()));
+                Double planQty = QuantityUtils.roundToScale(o.getAnfme() != null ? o.getAnfme() : 0.0);
+                if (QuantityUtils.compare(newQty, planQty) > 0) {
+                    newQty = planQty;
+                }
                 o.setQty(newQty);
                 if (!asnOrderService.updateById(o)) {
                     throw new CoolException("鍏ュ簱鍗曞畬鎴愭暟閲忔洿鏂板け璐ワ紒锛�");
@@ -2559,6 +2673,10 @@
             WkOrderItem oi = asnOrderItemService.getById(e.getKey());
             if (oi != null) {
                 Double newQty = QuantityUtils.roundToScale(QuantityUtils.add(oi.getQty() != null ? oi.getQty() : 0.0, e.getValue()));
+                Double planQty = QuantityUtils.roundToScale(oi.getAnfme() != null ? oi.getAnfme() : 0.0);
+                if (QuantityUtils.compare(newQty, planQty) > 0) {
+                    newQty = planQty;
+                }
                 oi.setQty(newQty);
                 if (!asnOrderItemService.updateById(oi)) {
                     throw new CoolException("鍏ュ簱鍗曟槑缁嗗畬鎴愭暟閲忔洿鏂板け璐ワ紒锛�");
@@ -2691,7 +2809,7 @@
      * @return
      */
     @Transactional(rollbackFor = Exception.class)
-    public synchronized void saveStockItems(List<TaskItem> items, Task task, Long id, String code, Short wkType, String type, Long loginUserId) {
+    public synchronized void saveStockItems(List<TaskItem> items, Task task, Long id, String code, String wkType, String type, Long loginUserId) {
         Stock stock = new Stock();
         String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_STOCK_CODE, null);
         if (StringUtils.isBlank(ruleCode)) {
@@ -2772,6 +2890,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 鍑哄簱瀹屾垚
@@ -2779,66 +2923,239 @@
      */
     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());
-                    }
-                }
-            }
+            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();
+            String mode = resolveCloudWmsInoutReportMode();
+            boolean sendHold = CloudWmsInoutReportMode.MANUAL.equals(mode) || CloudWmsInoutReportMode.WAIT_ORDER.equals(mode);
             for (TaskItem item : taskItems) {
-                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 || item.getMatnrCode() == null) {
+                if (item == null || item.getId() == null) {
                     continue;
                 }
-                InOutResultReportParam param = new InOutResultReportParam()
-                        .setOrderNo(orderNo)
-                        .setPlanNo(item.getPlatWorkCode())
-                        .setLineId(item.getPlatItemId())
-                        .setWareHouseId(wareHouseId)
-                        .setLocId(locId)
-                        .setMatNr(item.getMatnrCode())
-                        .setQty(item.getAnfme() != null ? String.valueOf(item.getAnfme()) : "0")
-                        .setBatch(item.getBatch())
-                        .setInbound(isInbound)
-                        .setBarcode(task.getBarcode());
+                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)
-                            .setNotifyStatus(cloudWmsNotifyLogService.getNotifyStatusPending())
                             .setRetryCount(0)
-                            .setBizRef("taskId=" + task.getId() + ",orderNo=" + orderNo)
+                            .setBizRef(bizRef)
                             .setCreateTime(now)
-                            .setUpdateTime(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 e) {
-                    log.warn("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙钀藉簱澶辫触锛堜笉褰卞搷涓绘祦绋嬶級锛宼askId={}锛宱rderNo={}锛歿}", task.getId(), orderNo, e.getMessage());
+                } catch (JsonProcessingException ex) {
+                    log.warn("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙钀藉簱澶辫触锛堜笉褰卞搷涓绘祦绋嬶級锛宼askId={}锛宱rderNo={}锛歿}", task.getId(), orderNo, ex.getMessage());
                 }
             }
         } catch (Exception e) {
             log.warn("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙澶辫触锛宼askId={}锛宨sInbound={}锛歿}", task.getId(), isInbound, e.getMessage());
         }
     }
+
+    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;
+        }
+        return StringUtils.isNotBlank(item.getPlatOrderCode())
+                || StringUtils.isNotBlank(item.getPlatWorkCode());
+    }
+
+    /** sys_config CLOUD_WMS_INOUT_REPORT_MODE锛歩mmediate / wait_order / manual / single */
+    private String resolveCloudWmsInoutReportMode() {
+        try {
+            Config cfg = configService.getCachedOrLoad(GlobalConfigCode.CLOUD_WMS_INOUT_REPORT_MODE);
+            if (cfg != null && StringUtils.isNotBlank(cfg.getVal())) {
+                return cfg.getVal().trim().toLowerCase();
+            }
+        } catch (Exception ignored) {
+        }
+        return CloudWmsInoutReportMode.IMMEDIATE;
+    }
 }

--
Gitblit v1.9.1