package com.ruoyi.quality.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.procurementrecord.utils.StockUtils; import com.ruoyi.production.mapper.ProductionOperationTaskMapper; import com.ruoyi.production.mapper.ProductionProductMainMapper; import com.ruoyi.production.service.ProductionAccountService; import com.ruoyi.production.pojo.ProductionOperationTask; import com.ruoyi.production.pojo.ProductionProductMain; import com.ruoyi.quality.mapper.QualityUnqualifiedMapper; import com.ruoyi.quality.pojo.QualityInspect; import com.ruoyi.quality.pojo.QualityUnqualified; import com.ruoyi.stock.pojo.StockInRecord; import com.ruoyi.stock.service.StockInRecordService; import com.ruoyi.quality.service.IQualityInspectService; import com.ruoyi.quality.service.IQualityUnqualifiedService; import com.ruoyi.stock.service.StockUninventoryService; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import jakarta.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; @AllArgsConstructor @Service public class QualityUnqualifiedServiceImpl extends ServiceImpl implements IQualityUnqualifiedService { private final StockUtils stockUtils; private final QualityUnqualifiedMapper qualityUnqualifiedMapper; private final IQualityInspectService qualityInspectService; private final ProductionProductMainMapper productionProductMainMapper; private final ProductionOperationTaskMapper productionOperationTaskMapper; private final StockUninventoryService stockUninventoryService; private final StockInRecordService stockInRecordService; private final ProductionAccountService productionAccountService; @Override public IPage qualityUnqualifiedListPage(Page page, QualityUnqualified qualityUnqualified) { return qualityUnqualifiedMapper.qualityUnqualifiedListPage(page, qualityUnqualified); } @Override public void qualityUnqualifiedExport(HttpServletResponse response, QualityUnqualified qualityUnqualified) { List qualityUnqualifieds = qualityUnqualifiedMapper.qualityUnqualifiedExport(qualityUnqualified); ExcelUtil util = new ExcelUtil<>(QualityUnqualified.class); util.exportExcel(response, qualityUnqualifieds, "不合格管理导出"); } @Override @Transactional(rollbackFor = Exception.class) public int deal(QualityUnqualified qualityUnqualified) { QualityUnqualified unqualified = qualityUnqualifiedMapper.selectById(qualityUnqualified.getId()); QualityInspect qualityInspect = qualityInspectService.getById(unqualified.getInspectId()); String batchNo = qualityInspect == null ? null : resolveProductionBatchNo(qualityInspect.getProductMainId(), qualityInspect.getId(), qualityInspect.getProductModelId()); if (ObjectUtils.isNotNull(qualityInspect) && qualityInspect.getInspectType() != 0) { ProductionProductMain sourceMain = null; if (ObjectUtils.isNotNull(qualityInspect.getProductMainId())) { sourceMain = productionProductMainMapper.selectById(qualityInspect.getProductMainId()); } switch (qualityUnqualified.getDealResult()) { case "报工": handleProductionReportResult(qualityUnqualified, unqualified, sourceMain); break; case "报废": handleProductionScrapResult(qualityInspect, qualityUnqualified, unqualified, sourceMain, batchNo); break; case "让步放行": stockUtils.addStockWithBatchNo(qualityInspect.getProductModelId(), unqualified.getQuantity(), StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId(), batchNo); break; default: break; } } else { Long modelId = qualityUnqualifiedMapper.getModelId(qualityUnqualified.getProductName(), qualityUnqualified.getModel()); switch (qualityUnqualified.getDealResult()) { case "报废": stockUtils.addUnStock(modelId, unqualified.getQuantity(), StockInQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), unqualified.getId()); break; case "让步放行": stockUtils.addStock(modelId, unqualified.getQuantity(), StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId()); break; default: break; } } qualityUnqualified.setInspectState(1); return qualityUnqualifiedMapper.updateById(qualityUnqualified); } @Override public QualityUnqualified getUnqualified(Integer id) { return qualityUnqualifiedMapper.getUnqualified(id); } private void handleProductionReportResult(QualityUnqualified qualityUnqualified, QualityUnqualified sourceUnqualified, ProductionProductMain sourceMain) { Integer dealType = qualityUnqualified.getDealType(); if (dealType == null) { throw new ServiceException("报工时必须选择不合格类型"); } if (sourceMain == null || sourceMain.getProductionOperationTaskId() == null) { throw new ServiceException("历史报工未绑定生产工单,无法执行报工处理"); } if (Integer.valueOf(0).equals(dealType)) { applyLightRework(sourceMain, qualityUnqualified); return; } if (Integer.valueOf(1).equals(dealType)) { markSourceMainAsUnpaid(sourceMain, 2); createReworkProductionByNewModel(sourceMain, sourceUnqualified.getQuantity()); qualityUnqualified.setRegenerateNewOrder(1); productionAccountService.rebuildByProductionProductMainId(sourceMain.getId()); return; } throw new ServiceException("报工处理仅支持轻微返工或严重返工"); } private void handleProductionScrapResult(QualityInspect qualityInspect, QualityUnqualified qualityUnqualified, QualityUnqualified sourceUnqualified, ProductionProductMain sourceMain, String batchNo) { if (sourceMain == null || sourceMain.getProductionOperationTaskId() == null) { throw new ServiceException("历史报工未绑定生产工单,无法执行报废处理"); } markSourceMainAsUnpaid(sourceMain, 3); createReworkProductionByNewModel(sourceMain, sourceUnqualified.getQuantity()); qualityUnqualified.setRegenerateNewOrder(1); productionAccountService.rebuildByProductionProductMainId(sourceMain.getId()); // 原有报废库存处理逻辑先按新规则注释,不再直接按不合格单执行报废出库。 // stockUtils.addUnStockWithBatchNo(qualityInspect.getProductModelId(), sourceUnqualified.getQuantity(), // StockInQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), sourceUnqualified.getId(), batchNo); } private void applyLightRework(ProductionProductMain sourceMain, QualityUnqualified qualityUnqualified) { if (qualityUnqualified.getRepairWorkHour() == null || qualityUnqualified.getRepairWorkHour().compareTo(BigDecimal.ZERO) <= 0) { throw new ServiceException("轻微返工必须填写大于0的修理工时"); } ProductionProductMain update = new ProductionProductMain(); update.setId(sourceMain.getId()); update.setReportType(1); update.setRepairWorkHour(defaultDecimal(sourceMain.getRepairWorkHour()).add(defaultDecimal(qualityUnqualified.getRepairWorkHour()))); productionProductMainMapper.updateById(update); qualityUnqualified.setRegenerateNewOrder(0); productionAccountService.rebuildByProductionProductMainId(sourceMain.getId()); } private void markSourceMainAsUnpaid(ProductionProductMain sourceMain, int reportType) { ProductionProductMain update = new ProductionProductMain(); update.setId(sourceMain.getId()); update.setReportType(reportType); productionProductMainMapper.updateById(update); } private void createReworkProductionByNewModel(ProductionProductMain sourceMain, BigDecimal quantity) { ProductionOperationTask sourceTask = productionOperationTaskMapper.selectById(sourceMain.getProductionOperationTaskId()); if (sourceTask == null) { throw new ServiceException("关联的生产工单不存在"); } ProductionOperationTask newTask = new ProductionOperationTask(); BeanUtils.copyProperties(sourceTask, newTask); newTask.setId(null); newTask.setWorkOrderNo(generateNextTaskNo("FG")); newTask.setPlanQuantity(defaultDecimal(quantity)); newTask.setCompleteQuantity(BigDecimal.ZERO); newTask.setPlanStartTime(null); newTask.setPlanEndTime(null); newTask.setActualStartTime(null); newTask.setActualEndTime(null); newTask.setStatus(1); newTask.setCreateTime(null); newTask.setUpdateTime(null); productionOperationTaskMapper.insert(newTask); } private String generateNextTaskNo(String prefix) { String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); String taskPrefix = prefix + datePrefix; ProductionOperationTask latestTask = productionOperationTaskMapper.selectOne( Wrappers.lambdaQuery() .likeRight(ProductionOperationTask::getWorkOrderNo, taskPrefix) .orderByDesc(ProductionOperationTask::getWorkOrderNo) .last("limit 1")); int sequence = 1; if (latestTask != null && latestTask.getWorkOrderNo() != null && latestTask.getWorkOrderNo().startsWith(taskPrefix)) { try { sequence = Integer.parseInt(latestTask.getWorkOrderNo().substring(taskPrefix.length())) + 1; } catch (NumberFormatException ignored) { sequence = 1; } } return taskPrefix + String.format("%03d", sequence); } private BigDecimal defaultDecimal(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } private String resolveProductionBatchNo(Long productionProductMainId, Long qualityInspectId, Long productModelId) { if (productModelId == null) { return null; } if (productionProductMainId != null) { StockInRecord productionRecord = stockInRecordService.getOne( Wrappers.lambdaQuery() .eq(StockInRecord::getRecordId, productionProductMainId) .eq(StockInRecord::getProductModelId, productModelId) .in(StockInRecord::getRecordType, Arrays.asList( StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode())) .isNotNull(StockInRecord::getBatchNo) .orderByDesc(StockInRecord::getId) .last("limit 1"), false); if (productionRecord != null) { return productionRecord.getBatchNo(); } } if (qualityInspectId == null) { return null; } StockInRecord inspectRecord = stockInRecordService.getOne( Wrappers.lambdaQuery() .eq(StockInRecord::getRecordId, qualityInspectId) .eq(StockInRecord::getProductModelId, productModelId) .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode()) .isNotNull(StockInRecord::getBatchNo) .orderByDesc(StockInRecord::getId) .last("limit 1"), false); return inspectRecord == null ? null : inspectRecord.getBatchNo(); } }