liding
6 天以前 bd86068dd507b7ee076456ab71aa03394730076f
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -220,7 +220,6 @@
        if (taskList == null || taskList.isEmpty()) {
            return;
        }
        Set<Long> routingOperationIds = taskList.stream()
                .map(ProductionOperationTask::getProductionOrderRoutingOperationId)
                .filter(Objects::nonNull)
@@ -228,14 +227,13 @@
        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));
        Map<String, BigDecimal> demandedQuantityMap = buildOperationDemandedQuantityMap(structureList, rootProductModelId, orderQuantity);
        // 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;
@@ -244,7 +242,11 @@
            if (routingOperation == null || routingOperation.getTechnologyRoutingOperationId() == null) {
                continue;
            }
            BigDecimal planQuantity = resolveTaskPlanQuantity(routingOperation, demandedQuantityMap, orderQuantity);
            BigDecimal planQuantity = resolveTaskPlanQuantity(
                    routingOperation,
                    demandedQuantityMap,
                    orderQuantity,
                    rootProductModelId);
            if (compareDecimal(task.getPlanQuantity(), planQuantity) == 0) {
                continue;
            }
@@ -254,10 +256,8 @@
            productionOperationTaskMapper.updateById(update);
        }
    }
    private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> structureList,
                                                                      Long rootProductModelId,
                                                                      BigDecimal orderQuantity) {
                                                                      Long rootProductModelId) {
        if (structureList == null || structureList.isEmpty()) {
            return Collections.emptyMap();
        }
@@ -265,26 +265,43 @@
                .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 || bomStructure.getUnitQuantity() == null) {
            if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
                continue;
            }
            Long outputProductModelId = resolveOutputProductModelId(bomStructure, structureById, rootProductModelId);
            // 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, bomStructure.getUnitQuantity().multiply(orderQuantity), BigDecimal::add);
            demandedQuantityMap.merge(key, defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity()), BigDecimal::add);
        }
        return demandedQuantityMap;
    }
    private BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation,
                                               Map<String, BigDecimal> demandedQuantityMap,
                                               BigDecimal orderQuantity) {
                                               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(),
                routingOperation.getProductModelId());
                outputProductModelId);
        BigDecimal planQuantity = demandedQuantityMap.get(key);
        return planQuantity != null ? planQuantity : orderQuantity;
    }
@@ -293,21 +310,28 @@
        return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
    }
    private Long resolveOutputProductModelId(ProductionBomStructure bomStructure,
                                             Map<Long, ProductionBomStructure> structureById,
                                             Long rootProductModelId) {
    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;
        }
        Long parentId = bomStructure.getParentId();
        if (parentId == null) {
            return rootProductModelId != null ? rootProductModelId : bomStructure.getProductModelId();
        }
        ProductionBomStructure parent = structureById.get(parentId);
        if (parent != null && parent.getProductModelId() != null) {
            return parent.getProductModelId();
        }
        return rootProductModelId != null ? rootProductModelId : bomStructure.getProductModelId();
        return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId;
    }
    private BigDecimal defaultDecimal(BigDecimal value) {