From 7bd10d34e8a8dc2437ec8551eb511e37634d7306 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 02 十一月 2026 16:51:52 +0800
Subject: [PATCH] feat(mes): 生产计划审批改走协同办公,修复工单领料与产成品入库校验

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.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/productreceipt/MesWmProductReceiptServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
index 100627f..036131c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
@@ -2,6 +2,7 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -19,6 +20,8 @@
 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductReceiptStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
@@ -32,6 +35,9 @@
 
 import java.math.BigDecimal;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -54,6 +60,8 @@
     private MesWmTransactionService wmTransactionService;
     @Resource
     private MesProWorkOrderService workOrderService;
+    @Resource
+    private MesMdItemService itemService;
     @Resource
     private MesWmWarehouseService warehouseService;
     @Resource
@@ -139,11 +147,81 @@
             throw exception(WM_PRODUCT_RECPT_STATUS_ERROR);
         }
 
+        // 鏍¢獙涓婃灦鏄庣粏宸茶鐩栨瘡涓�琛屾敹璐ф暟閲忥細鐘舵�佷竴鏃︽帹杩涘埌銆屽緟鎵ц鍏ュ簱銆嶏紝鏄庣粏灏变笉鑳藉啀缂栬緫锛�
+        // 鑻ユ鏃舵槑缁嗕负绌烘垨鏁伴噺涓嶈冻锛屽崟鎹皢姘歌繙鏃犳硶鎵ц鍏ュ簱锛堜細琚崱姝诲湪寰呮墽琛屽叆搴擄級
+        validateProductReceiptStockDetail(receipt);
+
         // 鎵ц涓婃灦锛堝緟涓婃灦 鈫� 寰呭叆搴擄級
         productReceiptMapper.updateById(new MesWmProductReceiptDO()
                 .setId(id).setStatus(MesWmProductReceiptStatusEnum.APPROVED.getStatus()));
     }
 
+    /**
+     * 鏍¢獙鎵ц涓婃灦鍓嶇殑涓婃灦鏄庣粏锛氭瘡涓�琛岀殑鏀惰揣鏁伴噺閮藉繀椤昏璇ヨ鐨勪笂鏋舵槑缁嗗悎璁¤鐩�
+     *
+     * 鎻愮ず鎸夈�屽叿浣撹 + 鍙屾柟鏁伴噺 + 涓嬩竴姝ュ姩浣溿�嶇粍缁囷紝閬垮厤鍙姤涓�鍙ョ缁熺殑"鏁伴噺涓嶅尮閰�"
+     */
+    private void validateProductReceiptStockDetail(MesWmProductReceiptDO receipt) {
+        List<MesWmProductReceiptLineDO> lines = productReceiptLineService.getProductReceiptLineListByRecptId(receipt.getId());
+        if (CollUtil.isEmpty(lines)) {
+            throw new ServiceException(WM_PRODUCT_RECPT_NO_LINE.getCode(),
+                    String.format("鏀惰揣鍗昜%s]娌℃湁鏀惰揣琛岋紝鏃犳硶鎵ц涓婃灦锛氳鍏堝湪鍗曟嵁涓坊鍔犳敹璐ц锛堢墿鏂欎笌鏀惰揣鏁伴噺锛夊悗鍐嶆墽琛屼笂鏋躲��",
+                            receipt.getCode()));
+        }
+        Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(lines.stream()
+                .map(MesWmProductReceiptLineDO::getItemId).filter(Objects::nonNull).collect(Collectors.toSet()));
+        for (int i = 0; i < lines.size(); i++) {
+            MesWmProductReceiptLineDO line = lines.get(i);
+            String lineLabel = String.format("绗� %d 琛孾%s]", i + 1, formatItemLabel(line.getItemId(), itemMap));
+            if (line.getBatchCode() != null) {
+                lineLabel += String.format("锛堟壒娆%s]锛�", line.getBatchCode());
+            }
+            BigDecimal lineQuantity = line.getQuantity() == null ? BigDecimal.ZERO : line.getQuantity();
+            List<MesWmProductReceiptDetailDO> details =
+                    productReceiptDetailService.getProductReceiptDetailListByLineId(line.getId());
+            // 鏄庣粏涓虹┖鏃剁洿鎺ユ彁绀�"鍘荤偣涓婃灦"锛屾瘮鎻愮ず"鍚堣 0 涓� 20 涓嶄竴鑷�"鏇存槑纭�
+            if (CollUtil.isEmpty(details)) {
+                throw new ServiceException(WM_PRODUCT_RECPT_NO_DETAIL.getCode(),
+                        String.format("鏀惰揣鍗昜%s]%s杩樻病鏈変笂鏋舵槑缁嗭紙鏀惰揣鏁伴噺[%s]锛夛細璇峰厛鍦ㄨ琛岀偣銆屼笂鏋躲�嶏紝濉啓鍏ュ簱浠撳簱涓庝笂鏋舵暟閲忥紝"
+                                + "鍚堣濉弧 %s 鍚庡啀鎵ц涓婃灦銆�", receipt.getCode(), lineLabel,
+                                formatQuantity(lineQuantity), formatQuantity(lineQuantity)));
+            }
+            BigDecimal detailQuantity = CollectionUtils.getSumValue(details,
+                    MesWmProductReceiptDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO);
+            if (detailQuantity.compareTo(lineQuantity) != 0) {
+                BigDecimal diff = lineQuantity.subtract(detailQuantity);
+                String action = diff.signum() > 0
+                        ? String.format("杩樺樊[%s]锛氳鍦ㄨ琛岀偣銆屼笂鏋躲�嶈ˉ鐧荤己灏戠殑涓婃灦鏄庣粏", formatQuantity(diff))
+                        : String.format("澶氬嚭[%s]锛氳鍒犻櫎鎴栬皟鏁磋琛屽浣欑殑涓婃灦鏄庣粏", formatQuantity(diff.negate()));
+                throw new ServiceException(WM_PRODUCT_RECPT_DETAIL_QUANTITY_MISMATCH.getCode(),
+                        String.format("鏀惰揣鍗昜%s]%s涓婃灦鏄庣粏鍚堣[%s]涓庢敹璐ф暟閲廩%s]涓嶄竴鑷达紝%s锛岃皟鏁翠竴鑷村悗鍐嶆墽琛屼笂鏋躲��",
+                                receipt.getCode(), lineLabel, formatQuantity(detailQuantity),
+                                formatQuantity(lineQuantity), action));
+            }
+        }
+    }
+
+    /**
+     * 鏁伴噺鏄剧ず锛氬幓鎺夋暟鎹簱 scale 甯﹀嚭鐨勫浣� 0锛�20.000000 鈫� 20锛�
+     */
+    private String formatQuantity(BigDecimal quantity) {
+        if (quantity == null) {
+            return "0";
+        }
+        return quantity.stripTrailingZeros().toPlainString();
+    }
+
+    /**
+     * 鐗╂枡鏄剧ず鍚嶏細浼樺厛銆屽悕绉�/缂栫爜銆嶏紝鏌ヤ笉鍒版椂鍥為��涓� ID锛岄伩鍏嶆妸瑁� ID 鐩存帴鎶涚粰鐢ㄦ埛
+     */
+    private String formatItemLabel(Long itemId, Map<Long, MesMdItemDO> itemMap) {
+        MesMdItemDO item = itemId == null ? null : itemMap.get(itemId);
+        if (item == null) {
+            return itemId == null ? "鐗╂枡[鏈�夋嫨]" : String.format("鐗╂枡[ID=%s]", itemId);
+        }
+        return String.format("%s/%s", item.getName(), item.getCode());
+    }
+
     @Override
     public Boolean checkProductReceiptQuantity(Long id) {
         List<MesWmProductReceiptLineDO> lines = productReceiptLineService.getProductReceiptLineListByRecptId(id);

--
Gitblit v1.9.3