| | |
| | | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | // 下单入口统一补齐来源单据、计划和工艺信息,避免前端分别传多套字段。 |
| | | 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); |
| | |
| | | ProductionOrder update = new ProductionOrder(); |
| | | update.setId(productionOrder.getId()); |
| | | update.setTechnologyRoutingId(targetRoutingId); |
| | | // 持久化或输出处理结果 |
| | | if (!this.updateById(update)) { |
| | | throw new ServiceException("绑定工艺路线失败"); |
| | | } |
| | |
| | | clearProductionSnapshot(productionOrderId); |
| | | ProductionOrderBom orderBom = syncProductionOrderBomSnapshot(productionOrder, technologyRouting); |
| | | |
| | | //生产订单工艺路线表 |
| | | ProductionOrderRouting orderRouting = new ProductionOrderRouting(); |
| | | orderRouting.setProductionOrderId(productionOrder.getId()); |
| | | orderRouting.setTechnologyRoutingId(technologyRouting.getId()); |
| | |
| | | orderRouting.setDescription(technologyRouting.getDescription()); |
| | | orderRouting.setBomId(technologyRouting.getBomId()); |
| | | orderRouting.setOrderBomId(orderBom == null ? null : orderBom.getId()); |
| | | // 持久化或输出处理结果 |
| | | productionOrderRoutingMapper.insert(orderRouting); |
| | | |
| | | int syncedParamCount = 0; |
| | |
| | | .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. |
| | | 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() |
| | |
| | | targetOperation.setIsQuality(sourceOperation.getIsQuality()); |
| | | targetOperation.setOperationName(operationNameMap.get(sourceOperation.getTechnologyOperationId())); |
| | | targetOperation.setTechnologyOperationId(sourceOperation.getTechnologyOperationId()); |
| | | targetOperation.setType(sourceOperation.getType()); |
| | | productionOrderRoutingOperationMapper.insert(targetOperation); |
| | | |
| | | boolean isLastOperation = lastDragSort != null && Objects.equals(sourceOperation.getDragSort(), lastDragSort); |
| | |
| | | ProductionOperationTask task = new ProductionOperationTask(); |
| | | task.setProductionOrderRoutingOperationId(targetOperation.getId()); |
| | | task.setProductionOrderId(productionOrder.getId()); |
| | | task.setPlanQuantity(defaultDecimal(productionOrder.getQuantity())); |
| | | task.setPlanQuantity(resolveTaskPlanQuantity( |
| | | sourceOperation, |
| | | operationDemandedQuantityMap, |
| | | productionOrder, |
| | | rootProductModelId)); |
| | | task.setCompleteQuantity(BigDecimal.ZERO); |
| | | task.setWorkOrderNo(generateNextTaskNo()); |
| | | task.setStatus(2); |
| | |
| | | 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) { |
| | | // 同步订单BOM快照结构 |
| | | if (technologyRouting.getBomId() == null) { |
| | |
| | | productionOrderBomMapper.insert(orderBom); |
| | | |
| | | Map<Long, Long> idMap = new HashMap<>(); |
| | | BigDecimal lastProcessDemandedQuantity = orderQuantity; |
| | | for (TechnologyBomStructure source : structureList) { |
| | | // 子节点 parentId 需要映射成新快照节点 id,才能保留原始 BOM 层级。 |
| | | ProductionBomStructure target = new ProductionBomStructure(); |
| | |
| | | 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; |
| | | } |
| | |
| | | .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) |
| | |
| | | : workOrderPage.getRecords().stream() |
| | | .filter(Objects::nonNull) |
| | | .sorted(Comparator.comparing(ProductionOperationTaskVo::getId, Comparator.nullsLast(Comparator.naturalOrder()))) |
| | | .collect(Collectors.toList()); |
| | | if (workOrderList == null || workOrderList.isEmpty()) { |
| | | .toList(); |
| | | if (workOrderList.isEmpty()) { |
| | | detailVo.setWorkOrderList(Collections.emptyList()); |
| | | return detailVo; |
| | | } |
| | |
| | | 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); |
| | | } |
| | | |
| | |
| | | |
| | | ProductionOrderWorkOrderDetailVo.ReportDetail reportDetail = new ProductionOrderWorkOrderDetailVo.ReportDetail(); |
| | | reportDetail.setReportMain(reportMain); |
| | | reportDetail.setWorkHour(reportMain.getWorkHour()); |
| | | reportDetail.setReportOutputList(reportOutputMap.getOrDefault(reportMainId, Collections.emptyList())); |
| | | reportDetail.setReportParamList(reportParamMap.getOrDefault(reportMainId, Collections.emptyList())); |
| | | reportDetailList.add(reportDetail); |
| | |
| | | inspectDetail.setReportId(reportMainId); |
| | | inspectDetail.setReportNo(reportMain.getProductNo()); |
| | | inspectDetail.setReportMain(reportMain); |
| | | inspectDetail.setWorkHour(reportMain.getWorkHour()); |
| | | inspectDetail.setInspect(inspect); |
| | | inspectDetail.setInspectParamList(inspectParamMap.getOrDefault(inspect.getId(), Collections.emptyList())); |
| | | inspectDetail.setInspectFileList(inspectFileMap.getOrDefault(inspect.getId(), Collections.emptyList())); |
| | |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | List<ProductionBomStructureVo> bomStructureList = productionBomStructureMapper.pickByBomId(orderBom.getId()); |
| | | // 查询完整的BOM结构(包括根节点),用于计算层级需求数量 |
| | | 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() |
| | |
| | | } |
| | | |
| | | Map<String, ProductionOrderPickVo> mergedPickMap = new LinkedHashMap<>(); |
| | | for (ProductionBomStructureVo structure : bomStructureList) { |
| | | for (ProductionBomStructureVo structure : childStructureList) { |
| | | if (structure == null || structure.getProductModelId() == null) { |
| | | continue; |
| | | } |