| | |
| | | import com.ruoyi.production.bean.vo.ProductionOrderVo; |
| | | import com.ruoyi.production.bean.vo.ProductionPlanVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderWorkOrderDetailVo; |
| | | import com.ruoyi.production.bean.vo.ProcessRouteStatusVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderProcessTaskVo; |
| | | import com.ruoyi.production.enums.ProductOrderStatusEnum; |
| | | import com.ruoyi.production.mapper.*; |
| | | import com.ruoyi.production.pojo.*; |
| | |
| | | |
| | | 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; |
| | |
| | | // 分页查询生产订单 |
| | | Page<ProductionOrderVo> result = (Page<ProductionOrderVo>) baseMapper.pageProductionOrder(page, dto); |
| | | fillProductImages(result.getRecords()); |
| | | fillProcessRouteStatus(result.getRecords()); |
| | | return result; |
| | | } |
| | | |
| | |
| | | // 查询生产订单列表 |
| | | List<ProductionOrderVo> records = baseMapper.listProductionOrder(dto); |
| | | fillProductImages(records); |
| | | fillProcessRouteStatus(records); |
| | | return records; |
| | | } |
| | | |
| | |
| | | // 下单入口统一补齐来源单据、计划和工艺信息,避免前端分别传多套字段。 |
| | | validateAndFillOrder(productionOrder, oldOrder); |
| | | if (productionOrder.getNpsNo() == null || productionOrder.getNpsNo().trim().isEmpty()) { |
| | | productionOrder.setNpsNo(generateNextOrderNo(productionOrder.getCreateTime() != null ? productionOrder.getCreateTime() : LocalDateTime.now())); |
| | | productionOrder.setNpsNo(generateNextOrderNo()); |
| | | } |
| | | if (productionOrder.getCompleteQuantity() == null) { |
| | | productionOrder.setCompleteQuantity(BigDecimal.ZERO); |
| | |
| | | .orderByDesc(ProductionOrder::getId); |
| | | } |
| | | |
| | | private String generateNextOrderNo(LocalDateTime createTime) { |
| | | private String generateNextOrderNo() { |
| | | // 生成下一个生产订单号 |
| | | LocalDate localDate = createTime.toLocalDate(); |
| | | String datePrefix = localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String prefix = "SC" + datePrefix; |
| | | ProductionOrder latestOrder = this.getOne(Wrappers.<ProductionOrder>lambdaQuery() |
| | | .likeRight(ProductionOrder::getNpsNo, prefix) |
| | |
| | | } |
| | | } |
| | | |
| | | private void fillProcessRouteStatus(List<ProductionOrderVo> records) { |
| | | if (records == null || records.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> orderIds = records.stream() |
| | | .map(ProductionOrderVo::getId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (orderIds.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | List<ProductionOrderProcessTaskVo> tasks = productionOperationTaskMapper.listProcessStatusByOrderIds(orderIds); |
| | | Map<Long, List<ProcessRouteStatusVo>> statusMap = new LinkedHashMap<>(); |
| | | if (tasks != null) { |
| | | for (ProductionOrderProcessTaskVo task : tasks) { |
| | | if (task == null || task.getProductionOrderId() == null) { |
| | | continue; |
| | | } |
| | | ProcessRouteStatusVo status = new ProcessRouteStatusVo(); |
| | | status.setName(task.getOperationName() != null && !task.getOperationName().isBlank() |
| | | ? task.getOperationName() |
| | | : "未知工序"); |
| | | BigDecimal percentage = task.getCompletionStatus() == null |
| | | ? BigDecimal.ZERO |
| | | : task.getCompletionStatus(); |
| | | if (percentage.compareTo(new BigDecimal("100")) > 0) { |
| | | percentage = new BigDecimal("100"); |
| | | } |
| | | status.setPercentage(percentage); |
| | | statusMap.computeIfAbsent(task.getProductionOrderId(), key -> new ArrayList<>()).add(status); |
| | | } |
| | | } |
| | | |
| | | for (ProductionOrderVo record : records) { |
| | | record.setProcessRouteStatus(statusMap.getOrDefault(record.getId(), Collections.emptyList())); |
| | | } |
| | | } |
| | | |
| | | private StorageBlobVO toStorageBlobVO(StorageBlob blob) { |
| | | // 将存储文件对象转换为VO |
| | | StorageBlobVO vo = BeanUtil.copyProperties(blob, StorageBlobVO.class); |
| | |
| | | } |
| | | } |
| | | |
| | | // 过滤出非根节点(实际领料项) |
| | | // 排除投入品与产出品相同且比例为1的情况(自身加工,不需要领料) |
| | | // 收集订单成品规格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 -> { |
| | | 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); |
| | | }) |
| | | .filter(s -> s.getId() == null || !parentNodeIds.contains(s.getId())) |
| | | .filter(s -> s.getProductModelId() == null || !finishedProductModelIds.contains(s.getProductModelId())) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 遍历处理数据并组装结果 |
| | |
| | | if (!productModelIds.isEmpty()) { |
| | | List<StockInventory> stockList = stockInventoryMapper.selectList( |
| | | Wrappers.<StockInventory>lambdaQuery() |
| | | .in(StockInventory::getProductModelId, productModelIds)); |
| | | .in(StockInventory::getProductModelId, productModelIds) |
| | | .gt(StockInventory::getQualitity, BigDecimal.ZERO)); |
| | | for (StockInventory stockItem : stockList) { |
| | | if (stockItem == null || stockItem.getProductModelId() == null) { |
| | | continue; |
| | |
| | | } |
| | | } |
| | | |
| | | List<ProductionOrderPickVo> pickList = new ArrayList<>(); |
| | | Map<String, ProductionOrderPickVo> mergedPickMap = new LinkedHashMap<>(); |
| | | for (ProductionBomStructureVo structure : childStructureList) { |
| | | if (structure == null || structure.getProductModelId() == null) { |
| | | continue; |
| | | } |
| | | Long productModelId = structure.getProductModelId(); |
| | | ProductionOrderPickVo vo = new ProductionOrderPickVo(); |
| | | 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(defaultDecimal(structure.getDemandedQuantity())); |
| | | vo.setDemandedQuantity(BigDecimal.ZERO); |
| | | vo.setUnit(structure.getUnit()); |
| | | List<String> batchNoList = stockBatchNoMap.get(productModelId) == null |
| | | ? Collections.emptyList() |
| | |
| | | vo.setBatchNoList(batchNoList); |
| | | vo.setStockQuantity(stockQuantityMap.getOrDefault(productModelId, BigDecimal.ZERO)); |
| | | vo.setBom(true); |
| | | pickList.add(vo); |
| | | mergedPickMap.put(mergeKey, vo); |
| | | } |
| | | return pickList; |
| | | vo.setDemandedQuantity(defaultDecimal(vo.getDemandedQuantity()).add(defaultDecimal(structure.getDemandedQuantity()))); |
| | | } |
| | | return new ArrayList<>(mergedPickMap.values()); |
| | | } |
| | | |
| | | @Override |
| | | public int updateOrder(ProductionOrderDto productionOrderDto) { |
| | | // 更新生产订单主数据 |
| | | productionOrderDto.setStatus(ProductOrderStatusEnum.END.getCode()); |
| | | productionOrderDto.setStatus(5); |
| | | return baseMapper.updateById(productionOrderDto); |
| | | } |
| | | } |