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 | 169 +++++++++++++++++++++++++++++++++++++++++--------------
1 files changed, 125 insertions(+), 44 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..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,9 +6,20 @@
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;
+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.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;
+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;
@@ -16,20 +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;
@@ -58,6 +72,16 @@
private MesWmProductSalesMapper productSalesMapper;
@Resource
private MesWmProductSalesLineMapper productSalesLineMapper;
+ @Resource
+ private MesWmProductSalesDetailMapper productSalesDetailMapper;
+ @Resource
+ private MesProBagCodeService bagCodeService;
+ @Resource
+ private MesProPalletService palletService;
+ @Resource
+ private MesProBatchService proBatchService;
+ @Resource
+ private MesWmBatchService mesWmBatchService;
@Resource
private MesWmProductSalesLineService productSalesLineService;
@@ -78,7 +102,7 @@
private MesQcOqcService oqcService;
@Resource
- private ErpSaleOrderApi saleOrderApi;
+ private MesTraceIntegrityService integrityService;
@Override
public Long createProductSales(MesWmProductSalesSaveReqVO createReqVO) {
@@ -200,6 +224,85 @@
@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);
+ }
+ // 鏈粦瀹氱敓浜ф壒娆$殑涓存椂鎵樼洏涓嶅厑璁稿弬涓庨攢鍞嚭搴�
+ 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)
+ : 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 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());
+ 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.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) {
+ 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(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(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("鎵爜鎴愬姛");
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
public void stockProductSales(Long id) {
// 鏍¢獙瀛樺湪
MesWmProductSalesDO sales = validateProductSalesExists(id);
@@ -241,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
@@ -281,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);
}
/**
@@ -311,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