From bd86068dd507b7ee076456ab71aa03394730076f Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期五, 15 五月 2026 17:25:46 +0800
Subject: [PATCH] fix:1.工单产出数量根据bom需求数量赋值

---
 src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java |  205 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 204 insertions(+), 1 deletions(-)

diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
index 35cdb27..2bded77 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -1,18 +1,29 @@
 package com.ruoyi.production.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.production.bean.dto.ProductionBomStructureDto;
 import com.ruoyi.production.bean.vo.ProductionBomStructureVo;
 import com.ruoyi.production.mapper.ProductionBomStructureMapper;
+import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
+import com.ruoyi.production.mapper.ProductionOrderBomMapper;
+import com.ruoyi.production.mapper.ProductionOrderMapper;
+import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
 import com.ruoyi.production.pojo.ProductionBomStructure;
+import com.ruoyi.production.pojo.ProductionOperationTask;
+import com.ruoyi.production.pojo.ProductionOrder;
+import com.ruoyi.production.pojo.ProductionOrderBom;
+import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
 import com.ruoyi.production.service.ProductionBomStructureService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -26,7 +37,11 @@
 @RequiredArgsConstructor()
 public class ProductionBomStructureServiceImpl extends ServiceImpl<ProductionBomStructureMapper, ProductionBomStructure> implements ProductionBomStructureService {
 
-    private  final ProductionBomStructureMapper productionBomStructureMapper;
+    private final ProductionBomStructureMapper productionBomStructureMapper;
+    private final ProductionOrderBomMapper productionOrderBomMapper;
+    private final ProductionOrderMapper productionOrderMapper;
+    private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
+    private final ProductionOperationTaskMapper productionOperationTaskMapper;
 
     /**
      * 鏍规嵁BOM鏌ヨ骞剁粍瑁呯粨鏋勬爲銆�
@@ -136,9 +151,197 @@
         if (!updateList.isEmpty()) {
             this.updateBatchById(updateList);
         }
+        syncDemandedQuantityAndTaskPlanQuantity(orderBomId, dto.getProductionOrderId());
         return true;
     }
 
+    private void syncDemandedQuantityAndTaskPlanQuantity(Long orderBomId, Long productionOrderId) {
+        if (orderBomId == null) {
+            return;
+        }
+        ProductionOrderBom orderBom = productionOrderBomMapper.selectById(orderBomId);
+        if (orderBom == null) {
+            return;
+        }
+        Long currentProductionOrderId = productionOrderId != null ? productionOrderId : orderBom.getProductionOrderId();
+        if (currentProductionOrderId == null) {
+            return;
+        }
+        ProductionOrder productionOrder = productionOrderMapper.selectById(currentProductionOrderId);
+        if (productionOrder == null) {
+            return;
+        }
+
+        BigDecimal orderQuantity = defaultDecimal(productionOrder.getQuantity());
+        List<ProductionBomStructure> structureList = this.list(
+                Wrappers.<ProductionBomStructure>lambdaQuery()
+                        .eq(ProductionBomStructure::getProductionOrderBomId, orderBomId)
+                        .orderByAsc(ProductionBomStructure::getId));
+        syncStructureDemandedQuantity(structureList, orderQuantity);
+        syncTaskPlanQuantity(
+                currentProductionOrderId,
+                structureList,
+                orderQuantity,
+                orderBom.getProductModelId() != null ? orderBom.getProductModelId() : productionOrder.getProductModelId());
+    }
+
+    private void syncStructureDemandedQuantity(List<ProductionBomStructure> structureList, BigDecimal orderQuantity) {
+        if (structureList == null || structureList.isEmpty()) {
+            return;
+        }
+        List<ProductionBomStructure> updateList = new ArrayList<>();
+        for (ProductionBomStructure structure : structureList) {
+            if (structure == null || structure.getId() == null) {
+                continue;
+            }
+            BigDecimal demandedQuantity = defaultDecimal(structure.getUnitQuantity()).multiply(orderQuantity);
+            if (compareDecimal(structure.getDemandedQuantity(), demandedQuantity) == 0) {
+                continue;
+            }
+            ProductionBomStructure update = new ProductionBomStructure();
+            update.setId(structure.getId());
+            update.setDemandedQuantity(demandedQuantity);
+            updateList.add(update);
+            structure.setDemandedQuantity(demandedQuantity);
+        }
+        if (!updateList.isEmpty()) {
+            this.updateBatchById(updateList);
+        }
+    }
+
+    private void syncTaskPlanQuantity(Long productionOrderId,
+                                      List<ProductionBomStructure> structureList,
+                                      BigDecimal orderQuantity,
+                                      Long rootProductModelId) {
+        List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList(
+                Wrappers.<ProductionOperationTask>lambdaQuery()
+                        .eq(ProductionOperationTask::getProductionOrderId, productionOrderId)
+                        .orderByAsc(ProductionOperationTask::getId));
+        if (taskList == null || taskList.isEmpty()) {
+            return;
+        }
+        Set<Long> routingOperationIds = taskList.stream()
+                .map(ProductionOperationTask::getProductionOrderRoutingOperationId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        if (routingOperationIds.isEmpty()) {
+            return;
+        }
+        Map<Long, ProductionOrderRoutingOperation> routingOperationMap = productionOrderRoutingOperationMapper
+                .selectBatchIds(routingOperationIds)
+                .stream()
+                .filter(item -> item != null && item.getId() != null)
+                .collect(Collectors.toMap(ProductionOrderRoutingOperation::getId, item -> item, (left, right) -> left));
+        // Keep task plan quantities aligned with the same order BOM snapshot demand used during snapshot creation.
+        Map<String, BigDecimal> demandedQuantityMap = buildOperationDemandedQuantityMap(structureList, rootProductModelId);
+        for (ProductionOperationTask task : taskList) {
+            if (task == null || task.getId() == null || task.getProductionOrderRoutingOperationId() == null) {
+                continue;
+            }
+            ProductionOrderRoutingOperation routingOperation = routingOperationMap.get(task.getProductionOrderRoutingOperationId());
+            if (routingOperation == null || routingOperation.getTechnologyRoutingOperationId() == null) {
+                continue;
+            }
+            BigDecimal planQuantity = resolveTaskPlanQuantity(
+                    routingOperation,
+                    demandedQuantityMap,
+                    orderQuantity,
+                    rootProductModelId);
+            if (compareDecimal(task.getPlanQuantity(), planQuantity) == 0) {
+                continue;
+            }
+            ProductionOperationTask update = new ProductionOperationTask();
+            update.setId(task.getId());
+            update.setPlanQuantity(planQuantity);
+            productionOperationTaskMapper.updateById(update);
+        }
+    }
+    private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> structureList,
+                                                                      Long rootProductModelId) {
+        if (structureList == null || structureList.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        Map<Long, ProductionBomStructure> structureById = structureList.stream()
+                .filter(item -> item != null && item.getId() != null)
+                .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left));
+        Map<String, BigDecimal> demandedQuantityMap = new HashMap<>();
+        Set<String> mergedOutputNodeKeySet = new HashSet<>();
+        for (ProductionBomStructure bomStructure : structureList) {
+            if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
+                continue;
+            }
+            // Resolve the output node first, then read the output node demand for the task plan quantity.
+            ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById);
+            Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId);
+            if (outputProductModelId == null) {
+                continue;
+            }
+            String mergedOutputNodeKey = buildOperationOutputNodeKey(
+                    bomStructure.getTechnologyOperationId(),
+                    outputNode == null ? null : outputNode.getId(),
+                    outputProductModelId);
+            if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) {
+                continue;
+            }
+            // Multiple input rows can point to the same output node, so only count that output demand once.
+            String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId);
+            demandedQuantityMap.merge(key, defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity()), BigDecimal::add);
+        }
+        return demandedQuantityMap;
+    }
+    private BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation,
+                                               Map<String, BigDecimal> demandedQuantityMap,
+                                               BigDecimal orderQuantity,
+                                               Long rootProductModelId) {
+        if (routingOperation == null || demandedQuantityMap == null || demandedQuantityMap.isEmpty()) {
+            return orderQuantity;
+        }
+        Long outputProductModelId = routingOperation.getProductModelId() != null
+                ? routingOperation.getProductModelId()
+                : rootProductModelId;
+        String key = buildOperationDemandedQuantityKey(
+                routingOperation.getTechnologyOperationId(),
+                outputProductModelId);
+        BigDecimal planQuantity = demandedQuantityMap.get(key);
+        return planQuantity != null ? planQuantity : orderQuantity;
+    }
+
+    private String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) {
+        return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
+    }
+
+    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 null;
+        }
+        // The root node is the first output node; other rows use their direct parent as the current operation output.
+        if (bomStructure.getParentId() == null) {
+            return bomStructure;
+        }
+        ProductionBomStructure parent = structureById.get(bomStructure.getParentId());
+        return parent != null ? parent : bomStructure;
+    }
+    private Long resolveOutputProductModelId(ProductionBomStructure outputNode,
+                                             Long rootProductModelId) {
+        if (outputNode == null) {
+            return rootProductModelId;
+        }
+        return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId;
+    }
+
+    private BigDecimal defaultDecimal(BigDecimal value) {
+        return value == null ? BigDecimal.ZERO : value;
+    }
+
+    private int compareDecimal(BigDecimal left, BigDecimal right) {
+        return defaultDecimal(left).compareTo(defaultDecimal(right));
+    }
+
     /**
      * 灏嗘爲褰㈢粨鏋勬媿骞虫垚鍒楄〃锛屼究浜庣粺涓�淇濆瓨銆�
      */

--
Gitblit v1.9.3