gongchunyi
6 小时以前 10570a9fa5e87231210a449eddc732c7d0599349
fix: 报工计算修改
已修改5个文件
188 ■■■■ 文件已修改
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java 99 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/util/TaskPlanQuantityUtil.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -822,14 +822,22 @@
        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) {
        if (bomStructure.getParentId() == null || hasChildren(bomStructure, structureById)) {
            return bomStructure;
        }
        ProductionBomStructure parent = structureById.get(bomStructure.getParentId());
        return parent != null ? parent : bomStructure;
    }
    private boolean hasChildren(ProductionBomStructure bomStructure,
                                Map<Long, ProductionBomStructure> structureById) {
        if (bomStructure == null || bomStructure.getId() == null || structureById == null) {
            return false;
        }
        return structureById.values().stream()
                .anyMatch(child -> child != null && Objects.equals(child.getParentId(), bomStructure.getId()));
    }
    private Long resolveOutputProductModelId(ProductionBomStructure outputNode,
                                             Long rootProductModelId) {
        if (outputNode == null) {
src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java
@@ -39,7 +39,6 @@
    private final ProductionOrderRoutingMapper productionOrderRoutingMapper;
    private final ProductionOrderBomMapper productionOrderBomMapper;
    private final ProductionBomStructureMapper productionBomStructureMapper;
    private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper;
    @Override
    public R addRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation) {
@@ -159,14 +158,11 @@
                Map<String, BigDecimal> operationDemandedQuantityMap =
                        TaskPlanQuantityUtil.buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId);
                // 获取工艺路线工序(用于计算计划数量)
                TechnologyRoutingOperation sourceOperation = technologyRoutingOperationMapper.selectById(
                        updatedOperation.getTechnologyRoutingOperationId());
                // 将原来的私有方法替换为调用工具类
                // 按订单工艺路线快照的产出规格读取对应 BOM 需求量。
                BigDecimal planQuantity = TaskPlanQuantityUtil.resolveTaskPlanQuantity(
                        sourceOperation,
                        updatedOperation,
                        operationDemandedQuantityMap,
                        productionOrder,
                        productionOrder.getQuantity(),
                        rootProductModelId);
                task.setPlanQuantity(planQuantity);
                task.setCompleteQuantity(BigDecimal.ZERO);
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -34,6 +34,7 @@
import com.ruoyi.quality.pojo.QualityInspectFile;
import com.ruoyi.quality.pojo.QualityInspectParam;
import com.ruoyi.production.service.ProductionOrderService;
import com.ruoyi.production.util.TaskPlanQuantityUtil;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.stock.mapper.StockInventoryMapper;
@@ -250,7 +251,7 @@
                        .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId())
                        .orderByDesc(TechnologyRoutingOperation::getDragSort)
                        .orderByDesc(TechnologyRoutingOperation::getId));
        // Build task plan quantities from order BOM snapshot demand instead of recomputing from technology BOM units.
        // 工单计划数量直接使用订单 BOM 快照中的需求量,避免与 BOM 页面展示口径不一致。
        Long rootProductModelId = orderBom != null && orderBom.getProductModelId() != null
                ? orderBom.getProductModelId()
                : productionOrder.getProductModelId();
@@ -261,7 +262,9 @@
                        .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId())
                        .orderByAsc(ProductionBomStructure::getId));
        Map<String, BigDecimal> operationDemandedQuantityMap =
                buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId);
                TaskPlanQuantityUtil.buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId);
        Map<Long, Set<Long>> operationOutputProductModelMap =
                TaskPlanQuantityUtil.buildOperationOutputProductModelMap(orderBomStructureList, rootProductModelId);
        Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds(
                        // 遍历处理数据并组装结果
                        routingOperations.stream()
@@ -281,7 +284,12 @@
            targetOperation.setProductionOrderId(productionOrder.getId());
            targetOperation.setTechnologyRoutingOperationId(sourceOperation.getId());
            targetOperation.setOrderRoutingId(orderRouting.getId());
            targetOperation.setProductModelId(sourceOperation.getProductModelId());
            Long outputProductModelId = TaskPlanQuantityUtil.resolveOperationOutputProductModelId(
                    sourceOperation.getTechnologyOperationId(),
                    sourceOperation.getProductModelId(),
                    operationOutputProductModelMap,
                    rootProductModelId);
            targetOperation.setProductModelId(outputProductModelId);
            targetOperation.setDragSort(sourceOperation.getDragSort());
            targetOperation.setIsProduction(sourceOperation.getIsProduction());
            targetOperation.setIsQuality(sourceOperation.getIsQuality());
@@ -295,10 +303,10 @@
                ProductionOperationTask task = new ProductionOperationTask();
                task.setProductionOrderRoutingOperationId(targetOperation.getId());
                task.setProductionOrderId(productionOrder.getId());
                task.setPlanQuantity(resolveTaskPlanQuantity(
                        sourceOperation,
                task.setPlanQuantity(TaskPlanQuantityUtil.resolveTaskPlanQuantity(
                        targetOperation,
                        operationDemandedQuantityMap,
                        productionOrder,
                        productionOrder.getQuantity(),
                        rootProductModelId));
                task.setCompleteQuantity(BigDecimal.ZERO);
                task.setWorkOrderNo(generateNextTaskNo());
@@ -332,85 +340,6 @@
            }
        }
        return syncedParamCount;
    }
    private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> bomStructures,
                                                                      Long rootProductModelId) {
        if (bomStructures == null || bomStructures.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<Long, ProductionBomStructure> structureById = bomStructures.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 : bomStructures) {
            if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
                continue;
            }
            // 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;
            }
            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,
                                               Long rootProductModelId) {
        if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) {
            return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
        }
        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());
    }
    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; child 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 ProductionOrderBom syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) {
src/main/java/com/ruoyi/production/util/TaskPlanQuantityUtil.java
@@ -88,6 +88,49 @@
        return demandedQuantityMap;
    }
    public Map<Long, Set<Long>> buildOperationOutputProductModelMap(List<ProductionBomStructure> bomStructures,
                                                                     Long rootProductModelId) {
        if (bomStructures == null || bomStructures.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<Long, ProductionBomStructure> structureById = new HashMap<>();
        for (ProductionBomStructure item : bomStructures) {
            if (item != null && item.getId() != null) {
                structureById.put(item.getId(), item);
            }
        }
        Map<Long, Set<Long>> outputProductModelMap = new HashMap<>();
        for (ProductionBomStructure bomStructure : bomStructures) {
            if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
                continue;
            }
            ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById);
            Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId);
            if (outputProductModelId != null) {
                outputProductModelMap.computeIfAbsent(
                        bomStructure.getTechnologyOperationId(), key -> new LinkedHashSet<>())
                        .add(outputProductModelId);
            }
        }
        return outputProductModelMap;
    }
    public Long resolveOperationOutputProductModelId(Long operationId,
                                                      Long preferredProductModelId,
                                                      Map<Long, Set<Long>> outputProductModelMap,
                                                      Long rootProductModelId) {
        Set<Long> candidates = outputProductModelMap == null ? null : outputProductModelMap.get(operationId);
        if (candidates != null && !candidates.isEmpty()) {
            if (preferredProductModelId != null && candidates.contains(preferredProductModelId)) {
                return preferredProductModelId;
            }
            if (candidates.size() == 1) {
                return candidates.iterator().next();
            }
        }
        return preferredProductModelId != null ? preferredProductModelId : rootProductModelId;
    }
    /**
     * 构建工序需求量key
     */
@@ -110,13 +153,22 @@
        if (bomStructure == null) {
            return null;
        }
        if (bomStructure.getParentId() == null) {
        if (bomStructure.getParentId() == null || hasChildren(bomStructure, structureById)) {
            return bomStructure;
        }
        ProductionBomStructure parent = structureById.get(bomStructure.getParentId());
        return parent != null ? parent : bomStructure;
    }
    private boolean hasChildren(ProductionBomStructure bomStructure,
                                Map<Long, ProductionBomStructure> structureById) {
        if (bomStructure == null || bomStructure.getId() == null || structureById == null) {
            return false;
        }
        return structureById.values().stream()
                .anyMatch(child -> child != null && Objects.equals(child.getParentId(), bomStructure.getId()));
    }
    /**
     * 解析输出产品规格ID
     */
src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java
@@ -177,6 +177,10 @@
        if (bomStructure == null) {
            return routingProductModelId;
        }
        if (hasChildren(bomStructure, structureById)) {
            return bomStructure.getProductModelId() != null
                    ? bomStructure.getProductModelId() : routingProductModelId;
        }
        Long parentId = bomStructure.getParentId();
        if (parentId == null) {
            return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId();
@@ -188,6 +192,15 @@
        return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId();
    }
    private boolean hasChildren(TechnologyBomStructure bomStructure,
                                Map<Long, TechnologyBomStructure> structureById) {
        if (bomStructure == null || bomStructure.getId() == null || structureById == null) {
            return false;
        }
        return structureById.values().stream()
                .anyMatch(child -> child != null && java.util.Objects.equals(child.getParentId(), bomStructure.getId()));
    }
    private void syncRoutingOperationParams(Long technologyRoutingOperationId, Long technologyOperationId) {
        List<TechnologyOperationParam> operationParamList = technologyOperationParamMapper.selectList(
                Wrappers.<TechnologyOperationParam>lambdaQuery()