From 620bb4712a31791231c4381581f0f60088f079fe Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 27 五月 2026 14:03:45 +0800
Subject: [PATCH] Merge branch 'refs/heads/dev_New_pro' into dev_宁夏_英泽防锈

---
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java |  160 +++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 112 insertions(+), 48 deletions(-)

diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
index ad20fe5..9543cad 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -44,6 +44,7 @@
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -59,6 +60,7 @@
     private final ProductionOperationTaskMapper productionOperationTaskMapper;
     private final ProductionOrderBomMapper productionOrderBomMapper;
     private final ProductionBomStructureMapper productionBomStructureMapper;
+    private final ProductionOrderMapper productionOrderMapper;
     private final ProductionProductMainMapper productionProductMainMapper;
     private final ProductionProductOutputMapper productionProductOutputMapper;
     private final ProductionOrderPickMapper productionOrderPickMapper;
@@ -114,7 +116,7 @@
         // 涓嬪崟鍏ュ彛缁熶竴琛ラ綈鏉ユ簮鍗曟嵁銆佽鍒掑拰宸ヨ壓淇℃伅锛岄伩鍏嶅墠绔垎鍒紶澶氬瀛楁銆�
         validateAndFillOrder(productionOrder, oldOrder);
         if (productionOrder.getNpsNo() == null || productionOrder.getNpsNo().trim().isEmpty()) {
-            productionOrder.setNpsNo(generateNextOrderNo());
+            productionOrder.setNpsNo(generateNextOrderNo(productionOrder.getCreateTime() != null ? productionOrder.getCreateTime() : LocalDateTime.now()));
         }
         if (productionOrder.getCompleteQuantity() == null) {
             productionOrder.setCompleteQuantity(BigDecimal.ZERO);
@@ -245,7 +247,18 @@
                         .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId())
                         .orderByDesc(TechnologyRoutingOperation::getDragSort)
                         .orderByDesc(TechnologyRoutingOperation::getId));
-        Map<String, BigDecimal> operationDemandedQuantityMap = buildOperationDemandedQuantityMap(technologyRouting, productionOrder);
+        // Build task plan quantities from order BOM snapshot demand instead of recomputing from technology BOM units.
+        Long rootProductModelId = orderBom != null && orderBom.getProductModelId() != null
+                ? orderBom.getProductModelId()
+                : productionOrder.getProductModelId();
+        List<ProductionBomStructure> orderBomStructureList = orderBom == null || orderBom.getId() == null
+                ? Collections.emptyList()
+                : productionBomStructureMapper.selectList(
+                Wrappers.<ProductionBomStructure>lambdaQuery()
+                        .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId())
+                        .orderByAsc(ProductionBomStructure::getId));
+        Map<String, BigDecimal> operationDemandedQuantityMap =
+                buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId);
         Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds(
         // 閬嶅巻澶勭悊鏁版嵁骞剁粍瑁呯粨鏋�
                         routingOperations.stream()
@@ -279,7 +292,11 @@
                 ProductionOperationTask task = new ProductionOperationTask();
                 task.setProductionOrderRoutingOperationId(targetOperation.getId());
                 task.setProductionOrderId(productionOrder.getId());
-                task.setPlanQuantity(resolveTaskPlanQuantity(sourceOperation, operationDemandedQuantityMap, productionOrder));
+                task.setPlanQuantity(resolveTaskPlanQuantity(
+                        sourceOperation,
+                        operationDemandedQuantityMap,
+                        productionOrder,
+                        rootProductModelId));
                 task.setCompleteQuantity(BigDecimal.ZERO);
                 task.setWorkOrderNo(generateNextTaskNo());
                 task.setStatus(2);
@@ -314,47 +331,52 @@
         return syncedParamCount;
     }
 
-    private Map<String, BigDecimal> buildOperationDemandedQuantityMap(TechnologyRouting technologyRouting,
-                                                                      ProductionOrder productionOrder) {
-        if (technologyRouting == null || technologyRouting.getBomId() == null) {
+    private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> bomStructures,
+                                                                      Long rootProductModelId) {
+        if (bomStructures == null || bomStructures.isEmpty()) {
             return Collections.emptyMap();
         }
-        BigDecimal orderQuantity = defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
-        List<TechnologyBomStructure> bomStructures = technologyBomStructureMapper.selectList(
-                Wrappers.<TechnologyBomStructure>lambdaQuery()
-                        .eq(TechnologyBomStructure::getBomId, technologyRouting.getBomId())
-                        .isNotNull(TechnologyBomStructure::getOperationId)
-                        .orderByAsc(TechnologyBomStructure::getId));
-        if (bomStructures.isEmpty()) {
-            return Collections.emptyMap();
-        }
-
-        Map<Long, TechnologyBomStructure> structureById = bomStructures.stream()
+        Map<Long, ProductionBomStructure> structureById = bomStructures.stream()
                 .filter(item -> item != null && item.getId() != null)
-                .collect(Collectors.toMap(TechnologyBomStructure::getId, item -> item, (left, right) -> left));
+                .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left));
         Map<String, BigDecimal> demandedQuantityMap = new HashMap<>();
-        for (TechnologyBomStructure bomStructure : bomStructures) {
-            if (bomStructure == null || bomStructure.getOperationId() == null) {
+        Set<String> mergedOutputNodeKeySet = new HashSet<>();
+        for (ProductionBomStructure bomStructure : bomStructures) {
+            if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
                 continue;
             }
-            BigDecimal unitQuantity = bomStructure.getUnitQuantity();
-            if (unitQuantity == null) {
+            // The BOM row points to the producing operation; task quantity should come from that operation's output node.
+            ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById);
+            Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId);
+            if (outputProductModelId == null) {
                 continue;
             }
-            Long outputProductModelId = resolveOutputProductModelId(bomStructure, structureById, technologyRouting.getProductModelId());
-            String key = buildOperationDemandedQuantityKey(bomStructure.getOperationId(), outputProductModelId);
-            demandedQuantityMap.merge(key, unitQuantity.multiply(orderQuantity), BigDecimal::add);
+            String mergedOutputNodeKey = buildOperationOutputNodeKey(
+                    bomStructure.getTechnologyOperationId(),
+                    outputNode == null ? null : outputNode.getId(),
+                    outputProductModelId);
+            if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) {
+                continue;
+            }
+            // demandedQuantity is already the order-level required output quantity for the current output node.
+            BigDecimal demandedQuantity = defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity());
+            String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId);
+            demandedQuantityMap.merge(key, demandedQuantity, BigDecimal::add);
         }
         return demandedQuantityMap;
     }
 
     private BigDecimal resolveTaskPlanQuantity(TechnologyRoutingOperation sourceOperation,
                                                Map<String, BigDecimal> operationDemandedQuantityMap,
-                                               ProductionOrder productionOrder) {
+                                               ProductionOrder productionOrder,
+                                               Long rootProductModelId) {
         if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) {
             return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
         }
-        String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), sourceOperation.getProductModelId());
+        Long outputProductModelId = sourceOperation.getProductModelId() != null
+                ? sourceOperation.getProductModelId()
+                : rootProductModelId;
+        String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), outputProductModelId);
         BigDecimal planQuantity = operationDemandedQuantityMap.get(key);
         return planQuantity != null ? planQuantity : defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
     }
@@ -363,21 +385,29 @@
         return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
     }
 
-    private Long resolveOutputProductModelId(TechnologyBomStructure bomStructure,
-                                             Map<Long, TechnologyBomStructure> structureById,
-                                             Long routingProductModelId) {
+    private String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) {
+        return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId);
+    }
+
+    private ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure,
+                                                              Map<Long, ProductionBomStructure> structureById) {
         if (bomStructure == null) {
-            return routingProductModelId;
+            return null;
         }
-        Long parentId = bomStructure.getParentId();
-        if (parentId == null) {
-            return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId();
+        // The root node is the first output node; child rows use their direct parent as the current operation output.
+        if (bomStructure.getParentId() == null) {
+            return bomStructure;
         }
-        TechnologyBomStructure parent = structureById.get(parentId);
-        if (parent != null && parent.getProductModelId() != null) {
-            return parent.getProductModelId();
+        ProductionBomStructure parent = structureById.get(bomStructure.getParentId());
+        return parent != null ? parent : bomStructure;
+    }
+
+    private Long resolveOutputProductModelId(ProductionBomStructure outputNode,
+                                             Long rootProductModelId) {
+        if (outputNode == null) {
+            return rootProductModelId;
         }
-        return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId();
+        return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId;
     }
 
     private ProductionOrderBom syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) {
@@ -409,6 +439,7 @@
         productionOrderBomMapper.insert(orderBom);
 
         Map<Long, Long> idMap = new HashMap<>();
+        BigDecimal lastProcessDemandedQuantity = orderQuantity;
         for (TechnologyBomStructure source : structureList) {
             // 瀛愯妭鐐� parentId 闇�瑕佹槧灏勬垚鏂板揩鐓ц妭鐐� id锛屾墠鑳戒繚鐣欏師濮� BOM 灞傜骇銆�
             ProductionBomStructure target = new ProductionBomStructure();
@@ -418,10 +449,11 @@
             target.setProductModelId(source.getProductModelId());
             target.setTechnologyOperationId(source.getOperationId());
             target.setUnitQuantity(source.getUnitQuantity());
-            target.setDemandedQuantity(source.getUnitQuantity().multiply(orderQuantity));
+            target.setDemandedQuantity(lastProcessDemandedQuantity.multiply(source.getUnitQuantity()));
             target.setUnit(source.getUnit());
             productionBomStructureMapper.insert(target);
             idMap.put(source.getId(), target.getId());
+            lastProcessDemandedQuantity = target.getDemandedQuantity();
         }
         return orderBom;
     }
@@ -477,9 +509,10 @@
                 .orderByDesc(ProductionOrder::getId);
     }
 
-    private String generateNextOrderNo() {
+    private String generateNextOrderNo(LocalDateTime createTime) {
         // 鐢熸垚涓嬩竴涓敓浜ц鍗曞彿
-        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+        LocalDate localDate = createTime.toLocalDate();
+        String datePrefix = localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
         String prefix = "SC" + datePrefix;
         ProductionOrder latestOrder = this.getOne(Wrappers.<ProductionOrder>lambdaQuery()
                 .likeRight(ProductionOrder::getNpsNo, prefix)
@@ -855,12 +888,10 @@
                 if (reportOutput == null) {
                     continue;
                 }
-                Long reportMainId = reportOutput.getProductionProductMainId() != null
-                        ? reportOutput.getProductionProductMainId()
-                        : reportOutput.getProductMainId();
-                if (reportMainId == null) {
+                if (reportOutput.getProductionProductMainId() == null) {
                     continue;
                 }
+                Long reportMainId = reportOutput.getProductionProductMainId();
                 reportOutputMap.computeIfAbsent(reportMainId, k -> new ArrayList<>()).add(reportOutput);
             }
 
@@ -1001,13 +1032,46 @@
             return Collections.emptyList();
         }
 
-        List<ProductionBomStructureVo> bomStructureList = productionBomStructureMapper.pickByBomId(orderBom.getId());
+        // 鏌ヨ瀹屾暣鐨凚OM缁撴瀯锛堝寘鎷牴鑺傜偣锛夛紝鐢ㄤ簬璁$畻灞傜骇闇�姹傛暟閲�
+        List<ProductionBomStructureVo> bomStructureList = productionBomStructureMapper.listByBomId(orderBom.getId());
         if (bomStructureList == null || bomStructureList.isEmpty()) {
             return Collections.emptyList();
         }
 
+        // 鏌ヨ鐢熶骇璁㈠崟鑾峰彇璁㈠崟鏁伴噺
+        ProductionOrder productionOrder = productionOrderMapper.selectById(productionOrderId);
+        BigDecimal orderQuantity = productionOrder != null ? defaultDecimal(productionOrder.getQuantity()) : BigDecimal.ZERO;
+
+        // 鏋勫缓鏍戝舰缁撴瀯骞惰绠楀眰绾ч渶姹傛暟閲�
+        Map<Long, ProductionBomStructureVo> structureByIdMap = bomStructureList.stream()
+                .filter(s -> s != null && s.getId() != null)
+                .collect(Collectors.toMap(ProductionBomStructureVo::getId, s -> s));
+
+        // 鎸夊眰绾ц绠楅渶姹傛暟閲忥細瀛愮骇闇�姹傛暟閲� = 鐖剁骇闇�姹傛暟閲� 脳 瀛愮骇鍗曚綅浜у嚭鎵�闇�鏁伴噺
+        for (ProductionBomStructureVo structure : bomStructureList) {
+            if (structure == null) continue;
+
+            if (structure.getParentId() == null || structure.getParentId() == 0) {
+                // 鏍硅妭鐐癸細闇�姹傛暟閲� = 璁㈠崟鏁伴噺
+                structure.setDemandedQuantity(orderQuantity);
+            } else {
+                // 瀛愯妭鐐癸細闇�姹傛暟閲� = 鐖剁骇闇�姹傛暟閲� 脳 瀛愮骇鍗曚綅浜у嚭鎵�闇�鏁伴噺
+                ProductionBomStructureVo parent = structureByIdMap.get(structure.getParentId());
+                if (parent != null) {
+                    BigDecimal parentDemandedQty = defaultDecimal(parent.getDemandedQuantity());
+                    BigDecimal unitQuantity = defaultDecimal(structure.getUnitQuantity());
+                    structure.setDemandedQuantity(parentDemandedQty.multiply(unitQuantity));
+                }
+            }
+        }
+
+        // 杩囨护鍑洪潪鏍硅妭鐐癸紙瀹為檯棰嗘枡椤癸級
+        List<ProductionBomStructureVo> childStructureList = bomStructureList.stream()
+                .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0)
+                .collect(Collectors.toList());
+
         // 閬嶅巻澶勭悊鏁版嵁骞剁粍瑁呯粨鏋�
-        List<Long> productModelIds = bomStructureList.stream()
+        List<Long> productModelIds = childStructureList.stream()
                 .map(ProductionBomStructureVo::getProductModelId)
                 .filter(Objects::nonNull)
                 .distinct()
@@ -1032,7 +1096,7 @@
         }
 
         Map<String, ProductionOrderPickVo> mergedPickMap = new LinkedHashMap<>();
-        for (ProductionBomStructureVo structure : bomStructureList) {
+        for (ProductionBomStructureVo structure : childStructureList) {
             if (structure == null || structure.getProductModelId() == null) {
                 continue;
             }

--
Gitblit v1.9.3