From b3fe06c151dc8455649bf0a46e481ceb83e3ea2a Mon Sep 17 00:00:00 2001
From: 吴羡 <16976746+Xian-TT3@user.noreply.gitee.com>
Date: 星期六, 07 十一月 2026 13:37:54 +0800
Subject: [PATCH] feat: 完成好水川 MOM 需求1/2/4/5/8(装箱、BOM成本、销售合同、劳动合同、留样)

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/packages/MesWmPackageLineServiceImpl.java |  117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 110 insertions(+), 7 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/packages/MesWmPackageLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/packages/MesWmPackageLineServiceImpl.java
index 295fc61..a670662 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/packages/MesWmPackageLineServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/packages/MesWmPackageLineServiceImpl.java
@@ -1,22 +1,38 @@
 package cn.iocoder.yudao.module.mes.service.wm.packages;
 
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.packages.vo.line.MesWmPackageLinePageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.packages.vo.line.MesWmPackageLineSaveReqVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.packages.MesWmPackageLineDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.packages.MesWmPackageLineMapper;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
 import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
+import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
 import jakarta.annotation.Resource;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PACKAGE_LINE_NOT_EXISTS;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
 
 /**
  * MES 瑁呯鏄庣粏 Service 瀹炵幇绫�
@@ -27,6 +43,9 @@
 @Validated
 public class MesWmPackageLineServiceImpl implements MesWmPackageLineService {
 
+    private static final String ASSEMBLY_PROCESS_CODE = "PRO-ASSEMBLY";
+    private static final String ASSEMBLY_PROCESS_NAME = "缁勮";
+
     @Resource
     private MesWmPackageLineMapper packageLineMapper;
 
@@ -36,15 +55,26 @@
     @Resource
     private MesMdmItemService mdmItemService;
     @Resource
+    private MesMdItemService mdItemService;
+    @Resource
+    private MesMdItemTypeService itemTypeService;
+    @Resource
     private MesProWorkOrderService workOrderService;
+    @Resource
+    private MesProWorkOrderProcessService workOrderProcessService;
+    @Resource
+    private MesProProcessService processService;
+    @Resource
+    private MesProFeedbackMapper feedbackMapper;
 
     @Override
     public Long createPackageLine(MesWmPackageLineSaveReqVO createReqVO) {
         // 鏍¢獙鏁版嵁
-        validateLineSaveData(createReqVO);
+        int boxCount = validateLineSaveData(createReqVO);
 
         // 鎻掑叆
         MesWmPackageLineDO line = BeanUtils.toBean(createReqVO, MesWmPackageLineDO.class);
+        line.setBoxCount(boxCount);
         packageLineMapper.insert(line);
         return line.getId();
     }
@@ -55,10 +85,11 @@
         MesWmPackageLineDO line = validatePackageLineExists(updateReqVO.getId());
         // 鏍¢獙鏁版嵁
         updateReqVO.setPackageId(line.getPackageId());
-        validateLineSaveData(updateReqVO);
+        int boxCount = validateLineSaveData(updateReqVO);
 
         // 鏇存柊
         MesWmPackageLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmPackageLineDO.class);
+        updateObj.setBoxCount(boxCount);
         packageLineMapper.updateById(updateObj);
     }
 
@@ -95,15 +126,87 @@
     // ========== 鏍¢獙鏂规硶 ==========
 
     /**
-     * 鏍¢獙淇濆瓨鏃剁殑鍏宠仈鏁版嵁
+     * 鏍¢獙淇濆瓨鏃剁殑鍏宠仈鏁版嵁锛屽苟杩斿洖鎸� 鏁伴噺 / 姣忕鏁伴噺 鍚戜笂鍙栨暣璁$畻鐨勭鏁�
      */
-    private void validateLineSaveData(MesWmPackageLineSaveReqVO reqVO) {
+    private int validateLineSaveData(MesWmPackageLineSaveReqVO reqVO) {
         // 鏍¢獙瑁呯鍗曠姸鎬佷负鑽夌
         packageService.validatePackageStatusDraft(reqVO.getPackageId());
         // 鏍¢獙浜у搧鐗╂枡瀛樺湪
         mdmItemService.validateItemExistsAndEnable(reqVO.getItemId());
-        // 鏍¢獙宸ュ崟宸茬‘璁�
-        workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
+
+        // 鏍¢獙鐗╂枡妗f锛氬繀椤诲睘浜庢垚鍝佸垎绫伙紝涓斿凡缁存姢姣忕鏁伴噺
+        MesMdItemDO item = mdItemService.getItem(reqVO.getItemId());
+        if (item == null) {
+            throw exception(WM_PACKAGE_LINE_BOX_QUANTITY_NOT_CONFIGURED, "ID=" + reqVO.getItemId());
+        }
+        String itemName = StrUtil.isNotBlank(item.getName()) ? item.getName() : item.getCode();
+        MesMdItemTypeDO itemType = item.getItemTypeId() != null
+                ? itemTypeService.getItemTypeMap(Collections.singleton(item.getItemTypeId()))
+                        .get(item.getItemTypeId())
+                : null;
+        if (itemType == null || !Boolean.TRUE.equals(itemType.getFinishedFlag())) {
+            throw exception(WM_PACKAGE_LINE_ITEM_NOT_FINISHED, itemName);
+        }
+        if (item.getBoxQuantity() == null || item.getBoxQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+            throw exception(WM_PACKAGE_LINE_BOX_QUANTITY_NOT_CONFIGURED, itemName);
+        }
+
+        // 鏍¢獙宸ュ崟宸茬‘璁わ紝涓旇绠辩墿鏂欏繀椤讳负宸ュ崟鐨勪骇鍝�
+        MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
+        if (ObjUtil.notEqual(reqVO.getItemId(), workOrder.getProductId())) {
+            MesMdItemDO product = workOrder.getProductId() != null
+                    ? mdItemService.getItem(workOrder.getProductId()) : null;
+            String productName = product != null
+                    ? (StrUtil.isNotBlank(product.getName()) ? product.getName() : product.getCode())
+                    : "ID=" + workOrder.getProductId();
+            throw exception(WM_PACKAGE_LINE_WORK_ORDER_PRODUCT_MISMATCH,
+                    itemName, workOrder.getCode(), productName);
+        }
+
+        // 鏍¢獙鍚屼竴宸ュ崟绱瑁呯鏁伴噺涓嶈秴杩囧伐鍗曟暟閲忥紙鑽夌涓庡凡瀹屾垚鐨勮绠卞崟閮借鍏ワ級
+        BigDecimal plannedQuantity = packageLineMapper.selectListByWorkOrderId(workOrder.getId()).stream()
+                .filter(line -> ObjUtil.notEqual(line.getId(), reqVO.getId()))
+                .map(MesWmPackageLineDO::getQuantity)
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal totalQuantity = plannedQuantity.add(reqVO.getQuantity());
+        if (workOrder.getQuantity() != null && totalQuantity.compareTo(workOrder.getQuantity()) > 0) {
+            throw exception(WM_PACKAGE_LINE_WORK_ORDER_QUANTITY_EXCEED,
+                    workOrder.getCode(), workOrder.getQuantity(), plannedQuantity, reqVO.getQuantity());
+        }
+
+        // 鏍¢獙缁勮宸ュ簭宸插畬鎴愭姤宸ワ細缁勮鍚堟牸鏁伴噺蹇呴』瑕嗙洊绱瑁呯鏁伴噺
+        validateAssemblyCompleted(workOrder, plannedQuantity, reqVO.getQuantity(), totalQuantity);
+
+        // 绠辨暟 = 鏁伴噺 / 姣忕鏁伴噺锛屽悜涓婂彇鏁�
+        return reqVO.getQuantity().divide(item.getBoxQuantity(), 0, RoundingMode.CEILING).intValue();
+    }
+
+    /**
+     * 鏍¢獙宸ュ崟鐨勩�愮粍瑁呫�戝伐搴忓凡瀹屾垚鎶ュ伐锛屼笖鍚堟牸鏁伴噺涓嶅皯浜庣疮璁¤绠辨暟閲�
+     */
+    private void validateAssemblyCompleted(MesProWorkOrderDO workOrder, BigDecimal packedQuantity,
+                                           BigDecimal currentQuantity, BigDecimal totalQuantity) {
+        List<Long> processIds = workOrderProcessService.getWorkOrderProcessListByWorkOrderId(workOrder.getId())
+                .stream()
+                .map(MesProWorkOrderProcessDO::getProcessId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .toList();
+        List<Long> assemblyProcessIds = processService.getProcessMap(processIds).values().stream()
+                .filter(process -> ASSEMBLY_PROCESS_CODE.equalsIgnoreCase(process.getCode())
+                        || ASSEMBLY_PROCESS_NAME.equals(process.getName()))
+                .map(MesProProcessDO::getId)
+                .toList();
+        if (assemblyProcessIds.isEmpty()) {
+            throw exception(WM_PACKAGE_LINE_ASSEMBLY_PROCESS_NOT_FOUND, workOrder.getCode());
+        }
+        BigDecimal assembledQuantity = feedbackMapper.selectFinishedQualifiedQuantitySum(
+                workOrder.getId(), assemblyProcessIds);
+        if (assembledQuantity.compareTo(totalQuantity) < 0) {
+            throw exception(WM_PACKAGE_LINE_ASSEMBLY_NOT_COMPLETED,
+                    workOrder.getCode(), assembledQuantity, totalQuantity, packedQuantity, currentQuantity);
+        }
     }
 
     private MesWmPackageLineDO validatePackageLineExists(Long id) {

--
Gitblit v1.9.3