From 1ba0dbfde5634c8bf2ec20891d6b226b996f6b59 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 02 九月 2026 14:24:38 +0800
Subject: [PATCH] refactor(mes): 优化MES生产扫码入库功能支持多类型扫码

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java |  103 ++++++++++++++++++++++++++-------------------------
 1 files changed, 53 insertions(+), 50 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
index c2a2cc6..628720f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
@@ -6,6 +6,7 @@
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO;
@@ -14,6 +15,7 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper;
 import cn.iocoder.yudao.module.mes.service.pro.bagcode.MesProBagCodeService;
@@ -25,21 +27,23 @@
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesMapper;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesLineMapper;
 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeQcStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmSalesNoticeStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveBizTypeEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
 import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
+import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
 import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeService;
 import cn.iocoder.yudao.module.mes.service.pro.batch.MesProBatchService;
+import cn.iocoder.yudao.module.mes.service.trace.integrity.MesTraceIntegrityService;
+import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
 import cn.iocoder.yudao.module.mes.service.wm.stockreserve.MesWmStockReserveService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
 import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService;
-import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi;
-import cn.iocoder.yudao.module.erp.api.sale.enums.ErpSaleOrderOutStatusEnum;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Lazy;
@@ -75,7 +79,9 @@
     @Resource
     private MesProPalletService palletService;
     @Resource
-    private MesProBatchService wmBatchService;
+    private MesProBatchService proBatchService;
+    @Resource
+    private MesWmBatchService mesWmBatchService;
 
     @Resource
     private MesWmProductSalesLineService productSalesLineService;
@@ -96,7 +102,7 @@
     private MesQcOqcService oqcService;
 
     @Resource
-    private ErpSaleOrderApi saleOrderApi;
+    private MesTraceIntegrityService integrityService;
 
     @Override
     public Long createProductSales(MesWmProductSalesSaveReqVO createReqVO) {
@@ -228,6 +234,19 @@
         if (pallet == null && bag == null) {
             throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
         }
+        // 鏈粦瀹氱敓浜ф壒娆$殑涓存椂鎵樼洏涓嶅厑璁稿弬涓庨攢鍞嚭搴�
+        if (pallet != null && pallet.getBatchId() == null) {
+            throw exception(PRO_PALLET_NOT_BIND_BATCH);
+        }
+        // 绉嶅瓙璐ㄦ鎸夎鐮佺矑搴︽祦杞悗锛屽嚭搴撳彧鍏佽宸茶川妫�鍚堟牸鐨勮鐮�/鎵樼洏
+        if (pallet != null) {
+            List<MesProBagCodeDO> palletBags = bagCodeService.getBagCodeListByPalletId(pallet.getId());
+            if (palletBags.stream().anyMatch(b -> !MesProBagCodeQcStatusEnum.QUALIFIED.getStatus().equals(b.getQcStatus()))) {
+                throw exception(WM_PRODUCT_SALES_SCAN_PALLET_NOT_ALL_QUALIFIED);
+            }
+        } else if (!MesProBagCodeQcStatusEnum.QUALIFIED.getStatus().equals(bag.getQcStatus())) {
+            throw exception(WM_PRODUCT_SALES_SCAN_BAG_NOT_QUALIFIED);
+        }
         Long codeId = pallet != null ? pallet.getId() : bag.getId();
         MesWmProductSalesDetailDO existed = pallet != null
                 ? productSalesDetailMapper.selectBySalesIdAndPalletCodeId(reqVO.getSalesId(), codeId)
@@ -237,9 +256,14 @@
                     .setCodeId(codeId).setScanContent(reqVO.getScanContent()).setAlreadyScanned(true)
                     .setDetailId(existed.getId()).setOutboundQuantity(existed.getQuantity()).setMessage("璇ョ爜宸叉壂鎻�");
         }
-        Long batchId = pallet != null ? pallet.getBatchId() : bag.getBatchId();
-        MesProBatchDO batch = wmBatchService.getBatch(batchId);
+        Long proBatchId = pallet != null ? pallet.getBatchId() : bag.getBatchId();
+        MesProBatchDO batch = proBatchService.getBatch(proBatchId);
         if (batch == null) {
+            throw exception(WM_TRANSACTION_BATCH_NOT_EXISTS);
+        }
+        // 鐢熶骇鎵规妗ユ帴浠撳簱鎵规锛氭壂鐮佸叆搴撳啓鍏ョ殑鏄粨搴撴壒娆★紙mes_wm_batch锛夛紝姝ゅ蹇呴』鐢ㄤ粨搴撴壒娆� id 鏌ュ簱瀛�
+        MesWmBatchDO wmBatch = mesWmBatchService.getOrCreateByProBatch(batch);
+        if (wmBatch == null) {
             throw exception(WM_TRANSACTION_BATCH_NOT_EXISTS);
         }
         List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(reqVO.getSalesId());
@@ -251,7 +275,8 @@
         if (quantity == null) {
             quantity = line.getQuantity();
         }
-        List<MesWmMaterialStockDO> stocks = materialStockService.getMaterialStockListByItemAndBatch(batch.getItemId(), batchId);
+        // 鍙粠鍚堟牸搴撹褰曚腑閫夊簱瀛橈紙鍚屾壒娆℃殏瀛樺簱璁板綍涓嶅彲鍑哄簱锛�
+        List<MesWmMaterialStockDO> stocks = materialStockService.getQualifiedMaterialStockListByItemAndBatch(batch.getItemId(), wmBatch.getId());
         BigDecimal outboundQuantity = quantity;
         MesWmMaterialStockDO stock = stocks.stream().filter(item -> item.getQuantity().compareTo(outboundQuantity) >= 0).findFirst().orElse(null);
         if (stock == null) {
@@ -264,13 +289,13 @@
         }
         MesWmProductSalesDetailDO detail = new MesWmProductSalesDetailDO().setLineId(line.getId())
                 .setSalesId(reqVO.getSalesId()).setItemId(batch.getItemId()).setQuantity(quantity)
-                .setMaterialStockId(stock.getId()).setBatchId(batchId).setBatchCode(batch.getCode())
+                .setMaterialStockId(stock.getId()).setBatchId(wmBatch.getId()).setBatchCode(wmBatch.getCode())
                 .setWarehouseId(stock.getWarehouseId()).setLocationId(stock.getLocationId()).setAreaId(stock.getAreaId())
                 .setBagCodeId(pallet == null ? codeId : null).setPalletCodeId(pallet != null ? codeId : null);
         productSalesDetailMapper.insert(detail);
         return new MesWmProductSalesScanRespVO().setScanType(pallet != null ? "PALLET" : "BAG")
                 .setCodeId(codeId).setScanContent(reqVO.getScanContent()).setItemId(batch.getItemId())
-                .setBatchId(batchId).setBatchCode(batch.getCode()).setMaterialStockId(stock.getId())
+                .setBatchId(wmBatch.getId()).setBatchCode(wmBatch.getCode()).setMaterialStockId(stock.getId())
                 .setAvailableQuantity(stock.getQuantity()).setScanQuantity(quantity).setOutboundQuantity(quantity)
                 .setAlreadyScanned(false).setDetailId(detail.getId()).setWarehouseId(stock.getWarehouseId())
                 .setLocationId(stock.getLocationId()).setAreaId(stock.getAreaId()).setMessage("鎵爜鎴愬姛");
@@ -319,12 +344,6 @@
         // 鎵ц鎷h揣锛堝緟鎷h揣 鈫� 寰呭~鍐欒繍鍗曪級
         productSalesMapper.updateById(new MesWmProductSalesDO()
                 .setId(id).setStatus(MesWmProductSalesStatusEnum.SHIPPING.getStatus()));
-
-        // 鍥炲啓 ERP 閿�鍞鍗曞嚭搴撶姸鎬佷负"閮ㄥ垎鍑哄簱"锛堣〃绀哄凡鎷h揣/鍗犵敤锛�
-        if (sales.getSaleOrderId() != null) {
-            saleOrderApi.updateOrderOutStatusTo(sales.getSaleOrderId(),
-                    ErpSaleOrderOutStatusEnum.PART_OUT.getStatus());
-        }
     }
 
     @Override
@@ -359,17 +378,31 @@
         // 3. 閬嶅巻鎵�鏈夋槑缁嗭紝鍒涘缓搴撳瓨浜嬪姟锛堟墸鍑忓簱瀛� + 璁板綍娴佹按锛�
         createTransactionList(sales);
 
-        // 4. 鏇存柊鍑哄簱鍗曠姸鎬�
+        // 4. 鍥哄寲缁忛攢鍟嗕笌瀹為檯鍑哄簱鎿嶄綔淇℃伅
+        CrmCustomerRespDTO customer = customerApi.getCustomer(sales.getClientId());
         productSalesMapper.updateById(new MesWmProductSalesDO()
-                .setId(id).setStatus(MesWmProductSalesStatusEnum.FINISHED.getStatus()));
+                .setId(id)
+                .setClientName(customer != null ? customer.getName() : null)
+                .setOutboundTime(java.time.LocalDateTime.now())
+                .setOutboundUserId(SecurityFrameworkUtils.getLoginUserId())
+                .setStatus(MesWmProductSalesStatusEnum.FINISHED.getStatus()));
 
         // 5. 鏇存柊鍏宠仈鐨勫彂璐ч�氱煡鍗曞嚭搴撴暟閲忓拰鐘舵��
         if (sales.getNoticeId() != null) {
             updateSalesNoticeOutCount(sales);
         }
 
-        // 6. 鍥炲啓 ERP 閿�鍞鍗曠殑鍑哄簱鏁伴噺鍜岀姸鎬�
-        updateErpSaleOrderOutCount(sales);
+        // 6. 鍥炲啓琚嬬爜/鎵樼洏鍑哄簱鍘诲悜锛堜緵绐滆揣绋芥煡瀹氫綅缁忛攢鍟嗗湴鍖猴級锛屽苟鐢熸垚鍑哄簱瀹屾暣鎬ф憳瑕�
+        List<MesWmProductSalesDetailDO> outDetails = productSalesDetailService.getProductSalesDetailListBySalesId(id);
+        for (MesWmProductSalesDetailDO detail : outDetails) {
+            if (detail.getBagCodeId() != null) {
+                bagCodeService.markOutbound(detail.getBagCodeId(), id);
+            }
+            if (detail.getPalletCodeId() != null) {
+                palletService.markOutbound(detail.getPalletCodeId(), id);
+            }
+        }
+        integrityService.recordSalesIntegrity(id);
     }
 
     /**
@@ -389,43 +422,13 @@
         }
     }
 
-    /**
-     * 鍥炲啓 ERP 閿�鍞鍗曠殑鍑哄簱鏁伴噺鍜岀姸鎬�
-     */
-    private void updateErpSaleOrderOutCount(MesWmProductSalesDO sales) {
-        // 鑾峰彇鍑哄簱鍗曡
-        List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(sales.getId());
-        if (CollUtil.isEmpty(lines)) {
-            return;
-        }
-        // 鐢ㄤ簬瀛樺偍閿�鍞鍗� ID锛岄伩鍏嶉噸澶嶆洿鏂�
-        Long saleOrderId = null;
-        // 閬嶅巻琛岋紝鎸� saleOrderItemId 鏇存柊鍑哄簱鏁伴噺
-        for (MesWmProductSalesLineDO line : lines) {
-            if (line.getSaleOrderItemId() != null && line.getQuantity() != null) {
-                saleOrderApi.updateOrderItemOutCount(line.getSaleOrderItemId(), line.getQuantity());
-                // 鑾峰彇閿�鍞鍗� ID锛堜粠绗竴涓湁鏁堢殑鏄庣粏椤硅幏鍙栵級
-                if (saleOrderId == null) {
-                    cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO itemDTO =
-                            saleOrderApi.getSaleOrderItem(line.getSaleOrderItemId()).getCheckedData();
-                    if (itemDTO != null && itemDTO.getOrderId() != null) {
-                        saleOrderId = itemDTO.getOrderId();
-                    }
-                }
-            }
-        }
-        // 鏇存柊閿�鍞鍗曠殑鍑哄簱鐘舵��
-        if (saleOrderId != null) {
-            saleOrderApi.updateOrderOutStatus(saleOrderId);
-        }
-    }
-
     private void createTransactionList(MesWmProductSalesDO sales) {
         List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListBySalesId(sales.getId());
         wmTransactionService.createTransactionList(convertList(details, detail -> new MesWmTransactionSaveReqDTO()
                 .setType(MesWmTransactionTypeEnum.OUT.getType()).setItemId(detail.getItemId())
                 .setQuantity(detail.getQuantity().negate()) // 鍑哄簱鏁伴噺涓鸿礋鏁�
                 .setBatchId(detail.getBatchId()).setBatchCode(detail.getBatchCode())
+                .setBagCodeId(detail.getBagCodeId()) // 琚嬬爜鍨嬫槑缁嗗嚭搴撴椂缃鐮佸凡鍑哄簱锛涙墭鐩樺瀷鏄庣粏涓嶉�愯澶勭悊
                 .setWarehouseId(detail.getWarehouseId()).setLocationId(detail.getLocationId()).setAreaId(detail.getAreaId())
                 .setBizType(MesBizTypeConstants.WM_PRODUCT_SALES).setBizId(sales.getId())
                 .setBizCode(sales.getCode()).setBizLineId(detail.getLineId())

--
Gitblit v1.9.3