| | |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | /** |
| | | * 批次号按前缀日期升序排序(格式如 20260703-ed01-001,日期早的在前) |
| | | */ |
| | | private void sortBatchNoList(List<String> batchNoList) { |
| | | if (batchNoList == null || batchNoList.size() < 2) { |
| | | return; |
| | | } |
| | | batchNoList.sort(Comparator |
| | | .comparingLong(this::batchDateKey) |
| | | .thenComparing(batchNo -> batchNo == null ? "" : batchNo)); |
| | | } |
| | | |
| | | /** |
| | | * 提取批次号开头的数字作为排序键(yyyyMMdd),无数字时放到最后 |
| | | */ |
| | | private long batchDateKey(String batchNo) { |
| | | if (batchNo == null) { |
| | | return Long.MAX_VALUE; |
| | | } |
| | | String trimmed = batchNo.trim(); |
| | | int end = 0; |
| | | while (end < trimmed.length() && Character.isDigit(trimmed.charAt(end))) { |
| | | end++; |
| | | } |
| | | if (end == 0) { |
| | | return Long.MAX_VALUE; |
| | | } |
| | | try { |
| | | return Long.parseLong(trimmed.substring(0, end)); |
| | | } catch (NumberFormatException e) { |
| | | return Long.MAX_VALUE; |
| | | } |
| | | } |
| | | |
| | | private void validateAndFillOrder(ProductionOrder productionOrder, ProductionOrder oldOrder) { |
| | | // 校验订单参数并补齐默认值 |
| | | if (productionOrder == null) { |
| | |
| | | } |
| | | } |
| | | |
| | | // 过滤出非根节点(实际领料项) |
| | | // 收集订单成品规格ID:包含生产订单成品规格与BOM根节点规格,用于排除“自己”这一物料 |
| | | Set<Long> finishedProductModelIds = new HashSet<>(); |
| | | if (productionOrder != null && productionOrder.getProductModelId() != null) { |
| | | finishedProductModelIds.add(productionOrder.getProductModelId()); |
| | | } |
| | | bomStructureList.stream() |
| | | .filter(s -> s != null && (s.getParentId() == null || s.getParentId() == 0)) |
| | | .map(ProductionBomStructureVo::getProductModelId) |
| | | .filter(Objects::nonNull) |
| | | .forEach(finishedProductModelIds::add); |
| | | |
| | | // 收集所有被引用为父级的节点ID:BOM从最后一道工序往里配置前一道工序, |
| | | // 中间节点是上一道工序的产出(自制),只有每条分支最里层的叶子节点才是真正需要领用的物料。 |
| | | Set<Long> parentNodeIds = bomStructureList.stream() |
| | | .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0) |
| | | .map(ProductionBomStructureVo::getParentId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | |
| | | // 过滤出叶子节点(实际领料项):非根节点、非中间节点,并排除与订单成品同规格的物料(自制成品无需领料) |
| | | List<ProductionBomStructureVo> childStructureList = bomStructureList.stream() |
| | | .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0) |
| | | .filter(s -> s.getId() == null || !parentNodeIds.contains(s.getId())) |
| | | .filter(s -> s.getProductModelId() == null || !finishedProductModelIds.contains(s.getProductModelId())) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 遍历处理数据并组装结果 |
| | |
| | | List<String> batchNoList = stockBatchNoMap.get(productModelId) == null |
| | | ? Collections.emptyList() |
| | | : new ArrayList<>(stockBatchNoMap.get(productModelId)); |
| | | sortBatchNoList(batchNoList); |
| | | vo.setBatchNoList(batchNoList); |
| | | vo.setStockQuantity(stockQuantityMap.getOrDefault(productModelId, BigDecimal.ZERO)); |
| | | vo.setBom(true); |