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.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.basic.mapper.ProductMapper; import com.ruoyi.basic.mapper.ProductModelMapper; import com.ruoyi.basic.pojo.Product; import com.ruoyi.basic.pojo.ProductModel; import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum; import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.procurementrecord.utils.StockUtils; import com.ruoyi.production.bean.dto.ProductStructureDto; import com.ruoyi.production.bean.dto.ProductionProductMainDto; import com.ruoyi.production.enums.ProductOrderStatusEnum; import com.ruoyi.production.mapper.*; import com.ruoyi.production.pojo.*; import com.ruoyi.production.service.ProductionProductMainService; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; import com.ruoyi.quality.mapper.*; import com.ruoyi.quality.pojo.*; import com.ruoyi.technology.mapper.TechnologyOperationMapper; import com.ruoyi.technology.mapper.TechnologyRoutingOperationMapper; import com.ruoyi.technology.pojo.TechnologyOperation; import com.ruoyi.technology.pojo.TechnologyRoutingOperation; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Service @AllArgsConstructor @Transactional(rollbackFor = Exception.class) public class ProductionProductMainServiceImpl extends ServiceImpl implements ProductionProductMainService { private final ProductionProductMainMapper productionProductMainMapper; private final SysUserMapper userMapper; private final ProductionProductOutputMapper productionProductOutputMapper; private final ProductModelMapper productModelMapper; private final QualityInspectMapper qualityInspectMapper; private final QualityUnqualifiedMapper qualityUnqualifiedMapper; private final ProductMapper productMapper; private final QualityTestStandardParamMapper qualityTestStandardParamMapper; private final QualityTestStandardMapper qualityTestStandardMapper; private final QualityInspectParamMapper qualityInspectParamMapper; private final ProductionProductInputMapper productionProductInputMapper; private final ProductionAccountMapper productionAccountMapper; private final ProductionOperationTaskMapper productionOperationTaskMapper; private final ProductionOrderMapper productionOrderMapper; private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper; private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper; private final TechnologyOperationMapper technologyOperationMapper; private final StockUtils stockUtils; @Override public IPage listPageProductionProductMainDto(Page page, ProductionProductMainDto productionProductMainDto) { return productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto); } @Override public IPage pageProductionProductMain(Page page, ProductionProductMainDto productionProductMainDto) { return listPageProductionProductMainDto(page, productionProductMainDto); } @Override public ProductionProductMainDto getProductionProductMainInfo(Long id) { return productionProductMainMapper.listPageProductionProductMainDto(new Page<>(1, 1), new ProductionProductMainDto() {{ setId(id); }}).getRecords().stream().findFirst().orElse(null); } @Override public Boolean addProductMain(ProductionProductMainDto dto) { if (dto.getProductionOperationTaskId() == null) { throw new ServiceException("请传入生产工单ID"); } return addProductMainByProductionTask(dto); } @Override public Boolean saveProductionProductMain(ProductionProductMainDto productionProductMainDto) { return addProductMain(productionProductMainDto); } @Override public Boolean removeProductMain(Long id) { ProductionProductMain currentMain = productionProductMainMapper.selectById(id); if (currentMain == null) { return true; } return removeProductMainByProductionTask(currentMain); } private Boolean addProductMainByProductionTask(ProductionProductMainDto dto) { // 报工以订单工序快照为准,避免工艺主数据变更后影响历史工单执行。 SysUser user = userMapper.selectUserById(dto.getUserId()); ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(dto.getProductionOperationTaskId()); if (productionOperationTask == null) { throw new ServiceException("生产工单不存在"); } ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getTechnologyRoutingOperationId()); if (routingOperation == null) { throw new ServiceException("订单工艺路线工序不存在"); } ProductionOrder productionOrder = productionOrderMapper.selectById(productionOperationTask.getProductionOrderId()); if (productionOrder == null) { throw new ServiceException("生产订单不存在"); } TechnologyRoutingOperation technologyRoutingOperation = technologyRoutingOperationMapper.selectById(routingOperation.getTechnologyRoutingOperationId()); TechnologyOperation technologyOperation = technologyRoutingOperation == null ? null : technologyOperationMapper.selectById(technologyRoutingOperation.getTechnologyOperationId()); ProductModel productModel = productModelMapper.selectById( routingOperation.getProductModelId() != null ? routingOperation.getProductModelId() : productionOrder.getProductModelId()); if (productModel == null) { throw new ServiceException("产品规格不存在"); } ProductionProductMain productionProductMain = new ProductionProductMain(); productionProductMain.setProductNo(generateProductNo()); productionProductMain.setUserId(dto.getUserId()); productionProductMain.setUserName(dto.getUserName()); productionProductMain.setProductionOperationTaskId(productionOperationTask.getId()); productionProductMain.setWorkOrderId(productionOperationTask.getId()); productionProductMain.setStatus(0); productionProductMainMapper.insert(productionProductMain); List productStructureDtos = new ArrayList<>(); ProductStructureDto productStructureDto = new ProductStructureDto(); productStructureDto.setProductModelId(productModel.getId()); productStructureDto.setUnitQuantity(BigDecimal.ONE); productStructureDtos.add(productStructureDto); for (ProductStructureDto item : productStructureDtos) { // 当前实现按工序成品直接作为投入,后续若接入领料记录可在这里替换来源。 ProductionProductInput productionProductInput = new ProductionProductInput(); productionProductInput.setProductionProductMainId(productionProductMain.getId()); productionProductInput.setProductMainId(productionProductMain.getId()); productionProductInput.setProductModelId(item.getProductModelId()); productionProductInput.setInputQuantity(item.getUnitQuantity().multiply(defaultDecimal(dto.getQuantity()))); productionProductInput.setQuantity(productionProductInput.getInputQuantity()); productionProductInputMapper.insert(productionProductInput); stockUtils.substractStock(item.getProductModelId(), productionProductInput.getInputQuantity(), StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode(), productionProductMain.getId()); } ProductionProductOutput productionProductOutput = new ProductionProductOutput(); productionProductOutput.setProductionProductMainId(productionProductMain.getId()); productionProductOutput.setProductMainId(productionProductMain.getId()); productionProductOutput.setProductModelId(productModel.getId()); productionProductOutput.setQuantity(defaultDecimal(dto.getQuantity())); productionProductOutput.setScrapQty(defaultDecimal(dto.getScrapQty())); productionProductOutputMapper.insert(productionProductOutput); BigDecimal productQty = productionProductOutput.getQuantity().subtract(productionProductOutput.getScrapQty()); List routingOperationList = productionOrderRoutingOperationMapper.selectList( Wrappers.lambdaQuery() .eq(ProductionOrderRoutingOperation::getOrderRoutingId, routingOperation.getOrderRoutingId()) .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId())); boolean isLastOperation = routingOperation.getDragSort() != null && routingOperation.getDragSort().equals(routingOperationList.size()); if (productQty.compareTo(BigDecimal.ZERO) > 0) { if (Boolean.TRUE.equals(routingOperation.getIsQuality())) { // 质检工序先生成检验单,非质检工序直接入合格品库存。 int inspectType = isLastOperation ? 2 : 1; String process = isLastOperation ? null : technologyOperation == null ? null : technologyOperation.getName(); Product product = productMapper.selectById(productModel.getProductId()); QualityInspect qualityInspect = new QualityInspect(); qualityInspect.setProductId(product.getId()); qualityInspect.setProductName(product.getProductName()); qualityInspect.setModel(productModel.getModel()); qualityInspect.setUnit(productModel.getUnit()); qualityInspect.setQuantity(productQty); qualityInspect.setProcess(process); qualityInspect.setInspectState(0); qualityInspect.setInspectType(inspectType); qualityInspect.setProductMainId(productionProductMain.getId()); qualityInspect.setProductModelId(productModel.getId()); qualityInspectMapper.insert(qualityInspect); List qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(product.getId(), inspectType, process); if (qualityTestStandard.size() > 0) { qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId()); qualityInspectMapper.updateById(qualityInspect); qualityTestStandardParamMapper.selectList(Wrappers.lambdaQuery() .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.get(0).getId())) .forEach(qualityTestStandardParam -> { QualityInspectParam param = new QualityInspectParam(); BeanUtils.copyProperties(qualityTestStandardParam, param); param.setId(null); param.setInspectId(qualityInspect.getId()); qualityInspectParamMapper.insert(param); }); } } else { stockUtils.addStock(productModel.getId(), productQty, StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), productionProductMain.getId()); } productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).add(productQty)); if (ObjectUtils.isNull(productionOperationTask.getActualStartTime())) { productionOperationTask.setActualStartTime(LocalDate.now()); } // 报工驱动工单状态流转:有产出即进行中,达到计划量即完工。 productionOperationTask.setStatus(3); if (productionOperationTask.getPlanQuantity() != null && productionOperationTask.getCompleteQuantity().compareTo(productionOperationTask.getPlanQuantity()) >= 0) { productionOperationTask.setActualEndTime(LocalDate.now()); productionOperationTask.setStatus(4); } productionOperationTaskMapper.updateById(productionOperationTask); if (ObjectUtils.isNull(productionOrder.getStartTime())) { productionOrder.setStartTime(LocalDateTime.now()); } // 订单状态由最后一道工序的合格产出推动,避免中间工序提前完工。 productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode()); if (isLastOperation) { productionOrder.setCompleteQuantity(defaultDecimal(productionOrder.getCompleteQuantity()).add(productQty)); if (productionOrder.getQuantity() != null && productionOrder.getCompleteQuantity().compareTo(productionOrder.getQuantity()) >= 0) { productionOrder.setEndTime(LocalDateTime.now()); productionOrder.setStatus(ProductOrderStatusEnum.FINISHED.getCode()); } } productionOrderMapper.updateById(productionOrder); BigDecimal workHours = BigDecimal.ZERO; if (technologyOperation != null && technologyOperation.getSalaryQuota() != null) { workHours = Integer.valueOf(1).equals(technologyOperation.getType()) ? technologyOperation.getSalaryQuota().multiply(productQty) : technologyOperation.getSalaryQuota(); } ProductionAccount productionAccount = new ProductionAccount(); productionAccount.setProductionProductMainId(productionProductMain.getId()); // productionAccount.setSalesLedgerId(productionOrder.getSalesLedgerId()); // productionAccount.setSalesLedgerProductId(productionOrder.getSalesLedgerProductId() == null ? null : productionOrder.getSalesLedgerProductId().longValue()); productionAccount.setSchedulingUserId(user == null ? null : user.getUserId()); productionAccount.setSchedulingUserName(user == null ? dto.getUserName() : user.getNickName()); productionAccount.setFinishedNum(productQty); productionAccount.setWorkHours(workHours); productionAccount.setTechnologyOperationName(technologyOperation == null ? null : technologyOperation.getName()); productionAccount.setSchedulingDate(LocalDateTime.now()); productionAccountMapper.insert(productionAccount); } if (defaultDecimal(dto.getScrapQty()).compareTo(BigDecimal.ZERO) > 0) { stockUtils.addUnStock(productModel.getId(), dto.getScrapQty(), StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), productionProductMain.getId()); } return true; } private Boolean removeProductMainByProductionTask(ProductionProductMain productionProductMain) { // 删除报工需要同步回滚质检、库存、工时核算和订单/工单进度。 List qualityInspects = qualityInspectMapper.selectList( Wrappers.lambdaQuery().eq(QualityInspect::getProductMainId, productionProductMain.getId())); if (qualityInspects.size() > 0) { List qualityUnqualifieds = qualityUnqualifiedMapper.selectList( Wrappers.lambdaQuery() .in(QualityUnqualified::getInspectId, qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList()))); if (qualityUnqualifieds.size() > 0 && qualityUnqualifieds.get(0).getInspectState() == 1) { throw new ServiceException("该条报工已经不合格处理了,不允许删除"); } } ProductionProductOutput productionProductOutput = productionProductOutputMapper.selectList( Wrappers.lambdaQuery() .eq(ProductionProductOutput::getProductionProductMainId, productionProductMain.getId())) .stream().findFirst().orElse(null); productionAccountMapper.delete(new LambdaQueryWrapper() .eq(ProductionAccount::getProductionProductMainId, productionProductMain.getId())); ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(productionProductMain.getProductionOperationTaskId()); if (productionOperationTask != null && productionProductOutput != null) { BigDecimal validQuantity = defaultDecimal(productionProductOutput.getQuantity()).subtract(defaultDecimal(productionProductOutput.getScrapQty())); productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).subtract(validQuantity)); productionOperationTask.setActualEndTime(null); if (defaultDecimal(productionOperationTask.getCompleteQuantity()).compareTo(BigDecimal.ZERO) <= 0) { productionOperationTask.setCompleteQuantity(BigDecimal.ZERO); productionOperationTask.setActualStartTime(null); productionOperationTask.setStatus(2); } else { productionOperationTask.setStatus(3); } productionOperationTaskMapper.updateById(productionOperationTask); ProductionOrder productionOrder = productionOrderMapper.selectById(productionOperationTask.getProductionOrderId()); ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getTechnologyRoutingOperationId()); if (productionOrder != null && routingOperation != null) { // 只有最后一道工序的报工才会影响生产订单完工数量。 List routingOperationList = productionOrderRoutingOperationMapper.selectList( Wrappers.lambdaQuery() .eq(ProductionOrderRoutingOperation::getOrderRoutingId, routingOperation.getOrderRoutingId()) .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId())); boolean isLastOperation = routingOperation.getDragSort() != null && routingOperation.getDragSort().equals(routingOperationList.size()); if (isLastOperation) { BigDecimal newCompleteQty = defaultDecimal(productionOrder.getCompleteQuantity()).subtract(validQuantity); productionOrder.setCompleteQuantity(newCompleteQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newCompleteQty); productionOrder.setEndTime(null); } if (defaultDecimal(productionOrder.getCompleteQuantity()).compareTo(BigDecimal.ZERO) <= 0) { productionOrder.setStartTime(null); productionOrder.setStatus(ProductOrderStatusEnum.WAIT.getCode()); } else { productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode()); } productionOrderMapper.updateById(productionOrder); } } qualityInspectMapper.selectList(new LambdaQueryWrapper() .eq(QualityInspect::getProductMainId, productionProductMain.getId())).forEach(q -> { qualityInspectParamMapper.delete(new LambdaQueryWrapper() .eq(QualityInspectParam::getInspectId, q.getId())); qualityInspectMapper.deleteById(q.getId()); stockUtils.deleteStockInRecord(q.getId(), StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode()); }); productionProductOutputMapper.delete(new LambdaQueryWrapper() .eq(ProductionProductOutput::getProductionProductMainId, productionProductMain.getId())); productionProductInputMapper.delete(new LambdaQueryWrapper() .eq(ProductionProductInput::getProductionProductMainId, productionProductMain.getId())); stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode()); stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode()); stockUtils.deleteStockOutRecord(productionProductMain.getId(), StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode()); productionProductMainMapper.deleteById(productionProductMain.getId()); return true; } private String generateProductNo() { String datePrefix = "BG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd")); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.select("MAX(product_no) as maxNo").likeRight("product_no", datePrefix); List> resultList = productionProductMainMapper.selectMaps(queryWrapper); int sequenceNumber = 1; if (resultList != null && !resultList.isEmpty()) { Map result = resultList.get(0); if (result != null && result.get("maxNo") != null) { String lastNo = result.get("maxNo").toString(); if (lastNo.startsWith(datePrefix)) { try { sequenceNumber = Integer.parseInt(lastNo.substring(datePrefix.length())) + 1; } catch (NumberFormatException e) { sequenceNumber = 1; } } } } return String.format("%s%03d", datePrefix, sequenceNumber); } private BigDecimal defaultDecimal(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } @Override public ArrayList listMain(List idList) { return productionProductMainMapper.listMain(idList); } }