| | |
| | | |
| | | 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; |
| | |
| | | // 下单入口统一补齐来源单据、计划和工艺信息,避免前端分别传多套字段。 |
| | | 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); |
| | |
| | | productionOrderBomMapper.insert(orderBom); |
| | | |
| | | Map<Long, Long> idMap = new HashMap<>(); |
| | | BigDecimal lastProcessDemandedQuantity = orderQuantity; |
| | | // 构建 sourceId → sourceNode 映射 和 parentId → children 映射 |
| | | Map<Long, TechnologyBomStructure> sourceById = new HashMap<>(); |
| | | Map<Long, List<TechnologyBomStructure>> childrenMap = new LinkedHashMap<>(); |
| | | for (TechnologyBomStructure source : structureList) { |
| | | // 子节点 parentId 需要映射成新快照节点 id,才能保留原始 BOM 层级。 |
| | | if (source == null || source.getId() == null) { |
| | | continue; |
| | | } |
| | | sourceById.put(source.getId(), source); |
| | | Long parentId = source.getParentId(); |
| | | childrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(source); |
| | | } |
| | | |
| | | // BFS 从根节点开始,父节点先于子节点处理 |
| | | Deque<TechnologyBomStructure> queue = new ArrayDeque<>(); |
| | | List<TechnologyBomStructure> rootNodes = childrenMap.get(null); |
| | | if (rootNodes != null) { |
| | | queue.addAll(rootNodes); |
| | | } |
| | | // parentId=0 的也视为根节点 |
| | | List<TechnologyBomStructure> zeroParentNodes = childrenMap.get(0L); |
| | | if (zeroParentNodes != null) { |
| | | for (TechnologyBomStructure node : zeroParentNodes) { |
| | | if (!queue.contains(node)) { |
| | | queue.add(node); |
| | | } |
| | | } |
| | | } |
| | | |
| | | while (!queue.isEmpty()) { |
| | | TechnologyBomStructure source = queue.poll(); |
| | | if (source == null || source.getId() == null) { |
| | | continue; |
| | | } |
| | | // 计算需求量 = 父节点需求量 × 自身 unitQuantity |
| | | BigDecimal parentDemandedQuantity = orderQuantity; |
| | | Long sourceParentId = source.getParentId(); |
| | | if (sourceParentId != null && sourceParentId != 0L) { |
| | | Long parentTargetId = idMap.get(sourceParentId); |
| | | if (parentTargetId != null) { |
| | | // 父节点已经插入,用父节点的 targetId 从已插入列表找 |
| | | // 这里我们直接用存下来的父节点需求量 |
| | | TechnologyBomStructure parentSource = sourceById.get(sourceParentId); |
| | | if (parentSource != null && parentSource.getDemandedQuantity() != null) { |
| | | parentDemandedQuantity = parentSource.getDemandedQuantity(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | ProductionBomStructure target = new ProductionBomStructure(); |
| | | target.setProductionOrderId(productionOrder.getId()); |
| | | target.setProductionOrderBomId(orderBom.getId()); |
| | |
| | | target.setProductModelId(source.getProductModelId()); |
| | | target.setTechnologyOperationId(source.getOperationId()); |
| | | target.setUnitQuantity(source.getUnitQuantity()); |
| | | target.setDemandedQuantity(lastProcessDemandedQuantity.multiply(source.getUnitQuantity())); |
| | | target.setDemandedQuantity(parentDemandedQuantity.multiply(defaultDecimal(source.getUnitQuantity()))); |
| | | target.setUnit(source.getUnit()); |
| | | productionBomStructureMapper.insert(target); |
| | | idMap.put(source.getId(), target.getId()); |
| | | lastProcessDemandedQuantity = target.getDemandedQuantity(); |
| | | // 将当前节点的 demandedQuantity 回写到 source 对象上,供子节点计算使用 |
| | | source.setDemandedQuantity(target.getDemandedQuantity()); |
| | | |
| | | // 将子节点加入队列 |
| | | List<TechnologyBomStructure> children = childrenMap.get(source.getId()); |
| | | if (children != null) { |
| | | for (TechnologyBomStructure child : children) { |
| | | queue.add(child); |
| | | } |
| | | } |
| | | } |
| | | 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) |
| | |
| | | } |
| | | |
| | | // 过滤出非根节点(实际领料项) |
| | | // 排除投入品与产出品相同且比例为1的情况(自身加工,不需要领料) |
| | | List<ProductionBomStructureVo> childStructureList = bomStructureList.stream() |
| | | .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0) |
| | | .filter(s -> { |
| | | ProductionBomStructureVo parent = structureByIdMap.get(s.getParentId()); |
| | | if (parent == null) { |
| | | return true; |
| | | } |
| | | // 投入品与产出品相同且比例为1时,不需要领料 |
| | | boolean sameProduct = Objects.equals(s.getProductModelId(), parent.getProductModelId()); |
| | | boolean unitRatio = BigDecimal.ONE.compareTo(defaultDecimal(s.getUnitQuantity())) == 0; |
| | | return !(sameProduct && unitRatio); |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 遍历处理数据并组装结果 |
| | |
| | | } |
| | | } |
| | | |
| | | Map<String, ProductionOrderPickVo> mergedPickMap = new LinkedHashMap<>(); |
| | | List<ProductionOrderPickVo> pickList = new ArrayList<>(); |
| | | for (ProductionBomStructureVo structure : childStructureList) { |
| | | if (structure == null || structure.getProductModelId() == null) { |
| | | continue; |
| | | } |
| | | Long productModelId = structure.getProductModelId(); |
| | | String mergeKey = String.valueOf(structure.getTechnologyOperationId()) + "#" + productModelId; |
| | | ProductionOrderPickVo vo = mergedPickMap.get(mergeKey); |
| | | if (vo == null) { |
| | | vo = new ProductionOrderPickVo(); |
| | | vo.setProductModelId(productModelId); |
| | | vo.setOperationName(structure.getOperationName()); |
| | | vo.setTechnologyOperationId(structure.getTechnologyOperationId()); |
| | | vo.setProductName(structure.getProductName()); |
| | | vo.setModel(structure.getModel()); |
| | | vo.setDemandedQuantity(BigDecimal.ZERO); |
| | | vo.setUnit(structure.getUnit()); |
| | | List<String> batchNoList = stockBatchNoMap.get(productModelId) == null |
| | | ? Collections.emptyList() |
| | | : new ArrayList<>(stockBatchNoMap.get(productModelId)); |
| | | vo.setBatchNoList(batchNoList); |
| | | vo.setStockQuantity(stockQuantityMap.getOrDefault(productModelId, BigDecimal.ZERO)); |
| | | vo.setBom(true); |
| | | mergedPickMap.put(mergeKey, vo); |
| | | } |
| | | vo.setDemandedQuantity(defaultDecimal(vo.getDemandedQuantity()).add(defaultDecimal(structure.getDemandedQuantity()))); |
| | | ProductionOrderPickVo vo = new ProductionOrderPickVo(); |
| | | vo.setProductModelId(productModelId); |
| | | vo.setOperationName(structure.getOperationName()); |
| | | vo.setTechnologyOperationId(structure.getTechnologyOperationId()); |
| | | vo.setProductName(structure.getProductName()); |
| | | vo.setModel(structure.getModel()); |
| | | vo.setDemandedQuantity(defaultDecimal(structure.getDemandedQuantity())); |
| | | vo.setUnit(structure.getUnit()); |
| | | List<String> batchNoList = stockBatchNoMap.get(productModelId) == null |
| | | ? Collections.emptyList() |
| | | : new ArrayList<>(stockBatchNoMap.get(productModelId)); |
| | | vo.setBatchNoList(batchNoList); |
| | | vo.setStockQuantity(stockQuantityMap.getOrDefault(productModelId, BigDecimal.ZERO)); |
| | | vo.setBom(true); |
| | | pickList.add(vo); |
| | | } |
| | | return new ArrayList<>(mergedPickMap.values()); |
| | | return pickList; |
| | | } |
| | | |
| | | @Override |
| | | public int updateOrder(ProductionOrderDto productionOrderDto) { |
| | | // 更新生产订单主数据 |
| | | productionOrderDto.setStatus(5); |
| | | productionOrderDto.setStatus(ProductOrderStatusEnum.END.getCode()); |
| | | return baseMapper.updateById(productionOrderDto); |
| | | } |
| | | } |