From 1b3fd46c7ccbf636208bcf098c5588441bb71f78 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 25 八月 2026 17:53:53 +0800
Subject: [PATCH] feat(mes): 添加销售出库扫码拣货功能

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java |   78 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 78 insertions(+), 0 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 9338217..c2a2cc6 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
@@ -9,6 +9,15 @@
 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;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesScanReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesScanRespVO;
+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.materialstock.MesWmMaterialStockDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper;
+import cn.iocoder.yudao.module.mes.service.pro.bagcode.MesProBagCodeService;
+import cn.iocoder.yudao.module.mes.service.pro.pallet.MesProPalletService;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO;
@@ -24,6 +33,7 @@
 import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
 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.wm.stockreserve.MesWmStockReserveService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
@@ -58,6 +68,14 @@
     private MesWmProductSalesMapper productSalesMapper;
     @Resource
     private MesWmProductSalesLineMapper productSalesLineMapper;
+    @Resource
+    private MesWmProductSalesDetailMapper productSalesDetailMapper;
+    @Resource
+    private MesProBagCodeService bagCodeService;
+    @Resource
+    private MesProPalletService palletService;
+    @Resource
+    private MesProBatchService wmBatchService;
 
     @Resource
     private MesWmProductSalesLineService productSalesLineService;
@@ -200,6 +218,66 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
+    public MesWmProductSalesScanRespVO scanProductSales(MesWmProductSalesScanReqVO reqVO) {
+        MesWmProductSalesDO sales = validateProductSalesExists(reqVO.getSalesId());
+        if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.APPROVING.getStatus(), sales.getStatus())) {
+            throw exception(WM_PRODUCT_SALES_CANNOT_PICK);
+        }
+        MesProPalletDO pallet = palletService.getPalletByCode(reqVO.getScanContent());
+        MesProBagCodeDO bag = pallet == null ? bagCodeService.getBagCodeByCode(reqVO.getScanContent()) : null;
+        if (pallet == null && bag == null) {
+            throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
+        }
+        Long codeId = pallet != null ? pallet.getId() : bag.getId();
+        MesWmProductSalesDetailDO existed = pallet != null
+                ? productSalesDetailMapper.selectBySalesIdAndPalletCodeId(reqVO.getSalesId(), codeId)
+                : productSalesDetailMapper.selectBySalesIdAndBagCodeId(reqVO.getSalesId(), codeId);
+        if (existed != null) {
+            return new MesWmProductSalesScanRespVO().setScanType(pallet != null ? "PALLET" : "BAG")
+                    .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);
+        if (batch == null) {
+            throw exception(WM_TRANSACTION_BATCH_NOT_EXISTS);
+        }
+        List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(reqVO.getSalesId());
+        MesWmProductSalesLineDO line = lines.stream().filter(item -> ObjUtil.equal(item.getItemId(), batch.getItemId())).findFirst().orElse(null);
+        if (line == null) {
+            throw exception(WM_PRODUCT_SALES_DETAIL_ITEM_MISMATCH);
+        }
+        BigDecimal quantity = reqVO.getQuantity();
+        if (quantity == null) {
+            quantity = line.getQuantity();
+        }
+        List<MesWmMaterialStockDO> stocks = materialStockService.getMaterialStockListByItemAndBatch(batch.getItemId(), batchId);
+        BigDecimal outboundQuantity = quantity;
+        MesWmMaterialStockDO stock = stocks.stream().filter(item -> item.getQuantity().compareTo(outboundQuantity) >= 0).findFirst().orElse(null);
+        if (stock == null) {
+            throw exception(WM_MATERIAL_STOCK_INSUFFICIENT);
+        }
+        BigDecimal scanned = productSalesDetailService.getProductSalesDetailListByLineId(line.getId()).stream()
+                .map(MesWmProductSalesDetailDO::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
+        if (scanned.add(quantity).compareTo(line.getQuantity()) > 0) {
+            throw exception(WM_PRODUCT_SALES_LINE_QUANTITY_INVALID);
+        }
+        MesWmProductSalesDetailDO detail = new MesWmProductSalesDetailDO().setLineId(line.getId())
+                .setSalesId(reqVO.getSalesId()).setItemId(batch.getItemId()).setQuantity(quantity)
+                .setMaterialStockId(stock.getId()).setBatchId(batchId).setBatchCode(batch.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())
+                .setAvailableQuantity(stock.getQuantity()).setScanQuantity(quantity).setOutboundQuantity(quantity)
+                .setAlreadyScanned(false).setDetailId(detail.getId()).setWarehouseId(stock.getWarehouseId())
+                .setLocationId(stock.getLocationId()).setAreaId(stock.getAreaId()).setMessage("鎵爜鎴愬姛");
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
     public void stockProductSales(Long id) {
         // 鏍¢獙瀛樺湪
         MesWmProductSalesDO sales = validateProductSalesExists(id);

--
Gitblit v1.9.3