| | |
| | | package com.ruoyi.production.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.ArrayUtils; |
| | | import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
| | | import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.production.dto.ProductBomDto; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.production.dto.ProductOrderDto; |
| | | import com.ruoyi.production.dto.ProductStructureDto; |
| | | import com.ruoyi.production.dto.ProductWorkOrderDto; |
| | | import com.ruoyi.production.mapper.*; |
| | | import com.ruoyi.production.pojo.*; |
| | | import com.ruoyi.production.service.ProcessRouteService; |
| | | import com.ruoyi.production.service.ProductOrderService; |
| | | import com.ruoyi.quality.mapper.QualityInspectMapper; |
| | | import com.ruoyi.quality.pojo.QualityInspect; |
| | | import com.ruoyi.sales.service.impl.CommonFileServiceImpl; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.service.impl.StockInventoryServiceImpl; |
| | | import org.jetbrains.annotations.NotNull; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, ProductOrder> implements ProductOrderService { |
| | |
| | | private ProductProcessRouteMapper productProcessRouteMapper; |
| | | |
| | | @Autowired |
| | | private ProductStructureRecordMapper productStructureRecordMapper; |
| | | |
| | | @Autowired |
| | | private ProcessRouteItemMapper processRouteItemMapper; |
| | | |
| | | @Autowired |
| | |
| | | @Autowired |
| | | private ProductWorkOrderMapper productWorkOrderMapper; |
| | | |
| | | @Autowired |
| | | private ProductionProductMainMapper productionProductMainMapper; |
| | | |
| | | @Autowired |
| | | private ProductionProductOutputMapper productionProductOutputMapper; |
| | | |
| | | @Autowired |
| | | private ProductionProductInputMapper productionProductInputMapper; |
| | | |
| | | @Autowired |
| | | private QualityInspectMapper qualityInspectMapper; |
| | | |
| | | @Autowired |
| | | private SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper; |
| | | |
| | | @Autowired |
| | | private StockUtils stockUtils; |
| | | |
| | | @Autowired |
| | | private StockInventoryServiceImpl stockInventoryService; |
| | | |
| | | @Autowired |
| | | private CommonFileServiceImpl commonFileService; |
| | | |
| | | @Override |
| | | public IPage<ProductOrderDto> pageProductOrder(Page page, ProductOrderDto productOrder) { |
| | | return productOrderMapper.pageProductOrder(page, productOrder); |
| | | IPage<ProductOrderDto> productOrderDtoIPage = productOrderMapper.pageProductOrder(page, productOrder); |
| | | productOrderDtoIPage.getRecords().forEach(productOrderDto -> { |
| | | // 获取产品合格库存 |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productOrderDto.getProductModelId()); |
| | | stockInventoryDto.setProductType(1); |
| | | Page page1 = new Page<>(1,1); |
| | | IPage<StockInventoryDto> stockInventoryDtoIPage = stockInventoryService.pagestockInventory(page1,stockInventoryDto); |
| | | if(stockInventoryDtoIPage.getTotal() > 0){ |
| | | productOrderDto.setInventoryQuantity(stockInventoryDtoIPage.getRecords().get(0).getQualitity()); |
| | | } |
| | | // 附件 |
| | | productOrderDto.setSalesLedgerFiles(commonFileService.getFileListByBusinessId(productOrderDto.getId() |
| | | , FileNameType.PRODUCT_ORDER.getValue())); |
| | | // 获取对应工单记录 |
| | | ProductWorkOrderDto productWorkOrder = new ProductWorkOrderDto(); |
| | | productWorkOrder.setProductOrderId(productOrderDto.getId()); |
| | | List<ProductWorkOrderDto> productWorkOrders = productWorkOrderMapper.getProductWorkOrderList(productWorkOrder); |
| | | if(CollectionUtils.isNotEmpty(productWorkOrders)){ |
| | | productOrderDto.setProductWorkOrders(calculateColor(productWorkOrders)); |
| | | } |
| | | |
| | | }); |
| | | return productOrderDtoIPage; |
| | | } |
| | | |
| | | /** |
| | | * 自动计算颜色(核心方法) |
| | | * @param productWorkOrders |
| | | */ |
| | | public List<ProductWorkOrderDto> calculateColor(List<ProductWorkOrderDto> productWorkOrders) { |
| | | List<ProductWorkOrderDto> result = new java.util.ArrayList<>(); |
| | | for (int i = 0; i < productWorkOrders.size(); i++) { |
| | | ProductWorkOrderDto current = productWorkOrders.get(i); |
| | | // 判断是否存在下一道工单 + 下一道工单是否有完成数量 |
| | | boolean nextOrderHasComplete = false; // 黄色 |
| | | boolean nextOrderHasScrap = false; // 红色 |
| | | // 如果不是最后一条,就取下一条判断 |
| | | for (int j = i + 1; j < productWorkOrders.size(); j++) { |
| | | if (i < productWorkOrders.size() - 1) { |
| | | ProductWorkOrderDto next = productWorkOrders.get(j); |
| | | BigDecimal nextComplete = next.getCompleteQuantity() == null ? BigDecimal.ZERO : next.getCompleteQuantity(); |
| | | BigDecimal nextScrap = next.getPlanQuantity() == null ? BigDecimal.ZERO : next.getPlanQuantity(); |
| | | // 下一道工单完成数量 > 0 |
| | | nextOrderHasComplete = nextComplete.compareTo(BigDecimal.ZERO) > 0; |
| | | |
| | | nextOrderHasScrap = nextComplete.compareTo(nextScrap) == 0; |
| | | break; |
| | | } |
| | | } |
| | | // =============== 核心:计算颜色并赋值 =============== |
| | | BigDecimal planQty = current.getPlanQuantity() == null ? BigDecimal.ZERO : current.getPlanQuantity(); |
| | | BigDecimal completeQty = current.getCompleteQuantity() == null ? BigDecimal.ZERO : current.getCompleteQuantity(); |
| | | |
| | | // 1. 当前工序完成数量等于0 且 下一道工序已完成 → 红色(4) |
| | | if (completeQty.compareTo(BigDecimal.ZERO) == 0 |
| | | && nextOrderHasScrap) { |
| | | current.setColor(4); |
| | | } |
| | | // 2. 需求数量 > 完成数量(未完成) 且 下一道工序完成数量 > 0 → 黄色(2) |
| | | else if (planQty.compareTo(completeQty) > 0 |
| | | && nextOrderHasComplete) { |
| | | current.setColor(2); |
| | | } |
| | | // 3. 完成数量=0 → 灰色(1) |
| | | else if (completeQty.compareTo(BigDecimal.ZERO) == 0) { |
| | | current.setColor(1); |
| | | } |
| | | // 4. 完成数量=需求数量 → 绿色(3) |
| | | else if (completeQty.compareTo(planQty) == 0) { |
| | | current.setColor(3); |
| | | } |
| | | // 其他情况默认灰色(可选) |
| | | else { |
| | | current.setColor(1); |
| | | } |
| | | result.add(current); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | |
| | | productProcessRoute.setProductOrderId(productOrder.getId()); |
| | | productProcessRoute.setBomId(processRoute.getBomId()); |
| | | productProcessRouteMapper.insert(productProcessRoute); |
| | | // 绑定生产bom清单 |
| | | if(CollectionUtils.isNotEmpty(productOrder.getProductStructureRecords())){ |
| | | List<ProductStructureRecord> productStructureDtos = productOrder.getProductStructureRecords(); |
| | | productStructureDtos.forEach(item ->{ |
| | | item.setProductOrderId(productOrder.getId()); |
| | | productStructureRecordMapper.insert(item); |
| | | }); |
| | | } |
| | | //新增生产订单下的工艺路线子表 |
| | | List<ProcessRouteItem> processRouteItems = processRouteItemMapper.selectList(new QueryWrapper<ProcessRouteItem>().lambda().eq(ProcessRouteItem::getRouteId, processRoute.getId())); |
| | | List<ProductProcessRouteItem> processRouteItems = productOrder.getProcessRouteItems(); |
| | | // 生成当前日期的前缀:年月日 |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | for (ProcessRouteItem processRouteItem : processRouteItems) { |
| | | ProductProcessRouteItem productProcessRouteItem = new ProductProcessRouteItem(); |
| | | productProcessRouteItem.setProductModelId(processRouteItem.getProductModelId()); |
| | | productProcessRouteItem.setProcessId(processRouteItem.getProcessId()); |
| | | for (ProductProcessRouteItem productProcessRouteItem : processRouteItems) { |
| | | productProcessRouteItem.setProductModelId(productOrder.getProductModelId()); |
| | | productProcessRouteItem.setProductOrderId(productOrder.getId()); |
| | | productProcessRouteItem.setProductRouteId(productProcessRoute.getId()); |
| | | productProcessRouteItem.setDragSort(processRouteItem.getDragSort()); |
| | | int insert = productProcessRouteItemMapper.insert(productProcessRouteItem); |
| | | if (insert > 0) { |
| | | // 查询今日已存在的最大工单号 |
| | | QueryWrapper<ProductWorkOrder> queryWrapper = new QueryWrapper<>(); |
| | | queryWrapper.likeRight("work_order_no", datePrefix) |
| | | .orderByDesc("work_order_no") |
| | | .last("LIMIT 1"); |
| | | ProductWorkOrder lastWorkOrder = productWorkOrderMapper.selectOne(queryWrapper); |
| | | int sequenceNumber = 1; // 默认序号 |
| | | if (lastWorkOrder != null && lastWorkOrder.getWorkOrderNo() != null) { |
| | | String lastNo = lastWorkOrder.getWorkOrderNo().toString(); |
| | | if (lastNo.startsWith(datePrefix)) { |
| | | String seqStr = lastNo.substring(datePrefix.length()); |
| | | try { |
| | | sequenceNumber = Integer.parseInt(seqStr) + 1; |
| | | } catch (NumberFormatException e) { |
| | | sequenceNumber = 1; |
| | | } |
| | | } |
| | | } |
| | | // 生成完整的工单号 |
| | | String workOrderNoStr ="GD"+ String.format("%s%03d", datePrefix, sequenceNumber); |
| | | ProductWorkOrder lastWorkOrder = productWorkOrderMapper.selectMax(datePrefix); |
| | | String workOrderNoStr = getWorkOrderNoStr(lastWorkOrder, datePrefix); // 工单号 |
| | | ProductWorkOrder productWorkOrder = new ProductWorkOrder(); |
| | | productWorkOrder.setProductProcessRouteItemId(productProcessRouteItem.getId()); |
| | | productWorkOrder.setProductOrderId(productOrder.getId()); |
| | | ProductOrder order = productOrderMapper.selectById(productOrder.getId()); |
| | | productWorkOrder.setPlanQuantity(order.getQuantity()); |
| | | productWorkOrder.setPlanQuantity(BigDecimal.valueOf(productProcessRouteItem.getPlanNum())); |
| | | productWorkOrder.setWorkOrderNo(workOrderNoStr); |
| | | productWorkOrder.setPlanStartTime(productProcessRouteItem.getPlanStartTime()); |
| | | productWorkOrder.setPlanEndTime(productProcessRouteItem.getPlanEndTime()); |
| | | productWorkOrder.setStatus(1); |
| | | productWorkOrder.setUserPower(productProcessRouteItem.getUserPower()); |
| | | productWorkOrderMapper.insert(productWorkOrder); |
| | | } |
| | | } |
| | | return productOrderMapper.updateById(productOrder); |
| | | } |
| | | |
| | | @NotNull |
| | | private static String getWorkOrderNoStr(ProductWorkOrder lastWorkOrder, String datePrefix) { |
| | | int sequenceNumber = 1; // 默认序号 |
| | | if (lastWorkOrder != null && lastWorkOrder.getWorkOrderNo() != null) { |
| | | String lastNo = lastWorkOrder.getWorkOrderNo(); |
| | | if (lastNo.startsWith(datePrefix)) { |
| | | String seqStr = lastNo.substring(datePrefix.length()); |
| | | try { |
| | | sequenceNumber = Integer.parseInt(seqStr) + 1; |
| | | } catch (NumberFormatException e) { |
| | | sequenceNumber = 1; |
| | | } |
| | | } |
| | | } |
| | | // 生成完整的工单号 |
| | | String workOrderNoStr = "GD" + String.format("%s%03d", datePrefix, sequenceNumber); |
| | | return workOrderNoStr; |
| | | } |
| | | |
| | | @Override |
| | |
| | | public List<ProductStructureDto> listProcessBom(Long orderId) { |
| | | return productOrderMapper.listProcessBom(orderId); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean addProductOrder(ProductOrder productOrder) throws IOException { |
| | | String string = generateNextOrderNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))); |
| | | productOrder.setNpsNo(string); |
| | | productOrder.setCompleteQuantity(BigDecimal.ZERO); |
| | | productOrder.setStatus("待生产"); |
| | | this.save(productOrder); |
| | | if (ObjectUtils.isNotEmpty(productOrder.getRouteId())) { |
| | | this.bindingRoute(productOrder); |
| | | } |
| | | if(CollectionUtils.isNotEmpty(productOrder.getTempFileIds())){ |
| | | commonFileService.migrateTempFilesToFormal(productOrder.getId(), productOrder.getTempFileIds()); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean delete(Long[] idList) { |
| | | //批量查询productOrder |
| | | List<ProductOrder> productOrders = productOrderMapper.selectList( |
| | | new LambdaQueryWrapper<ProductOrder>() |
| | | .in(ProductOrder::getId, Arrays.asList(idList)) |
| | | ); |
| | | if (!org.springframework.util.CollectionUtils.isEmpty(productOrders)) { |
| | | List<Long> ids = productOrders.stream().map(ProductOrder::getId).collect(Collectors.toList()); |
| | | |
| | | // 批量查询processRouteItems |
| | | List<ProductProcessRouteItem> allRouteItems = productProcessRouteItemMapper.selectList( |
| | | new LambdaQueryWrapper<ProductProcessRouteItem>() |
| | | .in(ProductProcessRouteItem::getProductOrderId, ids) |
| | | ); |
| | | |
| | | if (!com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(allRouteItems)) { |
| | | // 获取要删除的工序项ID |
| | | List<Long> routeItemIds = allRouteItems.stream() |
| | | .map(ProductProcessRouteItem::getId) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 查询关联的工单ID |
| | | List<ProductWorkOrder> workOrders = productWorkOrderMapper.selectList( |
| | | new LambdaQueryWrapper<ProductWorkOrder>() |
| | | .in(ProductWorkOrder::getProductProcessRouteItemId, routeItemIds) |
| | | ); |
| | | if (!com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(workOrders)) { |
| | | List<Long> workOrderIds = workOrders.stream() |
| | | .map(ProductWorkOrder::getId) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 查询关联的生产主表ID |
| | | List<ProductionProductMain> productMains = productionProductMainMapper.selectList( |
| | | new LambdaQueryWrapper<ProductionProductMain>() |
| | | .in(ProductionProductMain::getWorkOrderId, workOrderIds) |
| | | ); |
| | | List<Long> productMainIds = productMains.stream() |
| | | .map(ProductionProductMain::getId) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 删除产出表、投入表数据 |
| | | if (!com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(productMainIds)) { |
| | | productionProductOutputMapper.deleteByProductMainIds(productMainIds); |
| | | productionProductInputMapper.deleteByProductMainIds(ids); |
| | | List<QualityInspect> qualityInspects = qualityInspectMapper.selectList( |
| | | new LambdaQueryWrapper<QualityInspect>() |
| | | .in(QualityInspect::getProductMainId, productMainIds) |
| | | ); |
| | | //删除出库记录 |
| | | for (Long productMainId : productMainIds) { |
| | | //删除生产出库记录 |
| | | stockUtils.deleteStockOutRecord(productMainId, StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode()); |
| | | //删除报废的入库记录 |
| | | stockUtils.deleteStockInRecord(productMainId, StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode()); |
| | | } |
| | | qualityInspects.forEach(qualityInspect -> { |
| | | //inspectState=1 已提交 不能删除 |
| | | if (qualityInspect.getInspectState() == 1) { |
| | | throw new RuntimeException("已提交的检验单不能删除"); |
| | | } |
| | | }); |
| | | qualityInspectMapper.deleteByProductMainIds(productMainIds); |
| | | salesLedgerProductionAccountingMapper.delete(new LambdaQueryWrapper<SalesLedgerProductionAccounting>() |
| | | .in(SalesLedgerProductionAccounting::getProductMainId, productMainIds)); |
| | | } |
| | | |
| | | // 删除生产主表数据 |
| | | productionProductMainMapper.deleteByWorkOrderIds(workOrderIds); |
| | | |
| | | // 删除工单数据 |
| | | productWorkOrderMapper.delete(new LambdaQueryWrapper<ProductWorkOrder>() |
| | | .in(ProductWorkOrder::getProductProcessRouteItemId, routeItemIds)); |
| | | } |
| | | } |
| | | // 批量删除processRouteItem |
| | | productProcessRouteItemMapper.delete(new LambdaQueryWrapper<ProductProcessRouteItem>() |
| | | .in(ProductProcessRouteItem::getProductOrderId, ids)); |
| | | |
| | | // 批量删除productProcessRoute |
| | | productProcessRouteMapper.delete(new LambdaQueryWrapper<ProductProcessRoute>() |
| | | .in(ProductProcessRoute::getProductOrderId, ids)); |
| | | |
| | | // 批量删除productOrder |
| | | productOrderMapper.delete(new LambdaQueryWrapper<ProductOrder>() |
| | | .in(ProductOrder::getId, ids)); |
| | | // 删除附件 |
| | | commonFileService.deleteByBusinessIds(ids, FileNameType.PRODUCT_ORDER.getValue()); |
| | | // 删除生产订单-物料清单 |
| | | productStructureRecordMapper.delete(new LambdaQueryWrapper<ProductStructureRecord>() |
| | | .in(ProductStructureRecord::getProductOrderId, ids)); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public Object startOrPause(ProductOrderDto item) { |
| | | ProductOrder productOrder = productOrderMapper.selectById(item.getId()); |
| | | if(productOrder == null){ |
| | | throw new ServiceException("生产订单不存在"); |
| | | } |
| | | int i; |
| | | if(item.getOperation().equals(1)){ |
| | | i = 2; |
| | | } else { |
| | | i = 1; |
| | | } |
| | | productOrder.setStatus(item.getOperation() == 1 ? "生产中" : "待生产"); |
| | | productOrderMapper.updateById(productOrder); |
| | | List<ProductWorkOrder> productWorkOrders = productWorkOrderMapper.selectList(new LambdaQueryWrapper<ProductWorkOrder>() |
| | | .eq(ProductWorkOrder::getProductOrderId, productOrder.getId())); |
| | | if(CollectionUtils.isNotEmpty(productWorkOrders)){ |
| | | |
| | | productWorkOrders.forEach(productWorkOrder -> { |
| | | productWorkOrder.setStatus(i); |
| | | productWorkOrderMapper.updateById(productWorkOrder); |
| | | }); |
| | | } |
| | | return "操作成功"; |
| | | } |
| | | |
| | | //获取当前生产订单号 |
| | | public String getMaxOrderNoByDate(String datePrefix) { |
| | | QueryWrapper<ProductOrder> queryWrapper = new QueryWrapper<>(); |
| | | // 匹配以 SC + 日期开头的订单号 |
| | | queryWrapper.likeRight("nps_no", "SC" + datePrefix); |
| | | // 按订单号倒序排列 |
| | | queryWrapper.orderByDesc("nps_no"); |
| | | queryWrapper.last("LIMIT 1"); |
| | | |
| | | ProductOrder latestOrder = this.getOne(queryWrapper); |
| | | return latestOrder != null ? latestOrder.getNpsNo() : null; |
| | | } |
| | | |
| | | public String generateNextOrderNo(String datePrefix) { |
| | | String maxOrderNo = getMaxOrderNoByDate(datePrefix); |
| | | int sequence = 1; // 默认起始序号 |
| | | if (maxOrderNo != null && !maxOrderNo.isEmpty()) { |
| | | // 提取流水号部分(假设格式为 SC + 日期 + 流水号) |
| | | String sequenceStr = maxOrderNo.substring(("SC" + datePrefix).length()); |
| | | try { |
| | | sequence = Integer.parseInt(sequenceStr) + 1; |
| | | } catch (NumberFormatException e) { |
| | | // 异常情况下重置为1 |
| | | sequence = 1; |
| | | } |
| | | } |
| | | // 生成新订单号 |
| | | return "SC" + datePrefix + String.format("%04d", sequence); |
| | | } |
| | | |
| | | |
| | | } |