yuan
2 天以前 6f0df10fdd0e55e5ed3e31b1720efd25e2bf02fb
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -16,17 +16,18 @@
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.ProductionOrderPickService;
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.stock.pojo.StockInRecord;
import com.ruoyi.stock.pojo.StockInventory;
import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.service.StockInventoryService;
@@ -42,15 +43,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
@Service
@@ -72,10 +65,10 @@
    private final ProductionAccountMapper productionAccountMapper;
    private final ProductionOperationTaskMapper productionOperationTaskMapper;
    private final ProductionOrderMapper productionOrderMapper;
    private final ProductionOrderBomMapper productionOrderBomMapper;
    private final ProductionBomStructureMapper productionBomStructureMapper;
    private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
    private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper;
    private final ProductionOrderPickMapper productionOrderPickMapper;
    private final ProductionOrderPickService productionOrderPickService;
    private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper;
    private final TechnologyOperationMapper technologyOperationMapper;
    private final StockUtils stockUtils;
@@ -262,6 +255,11 @@
        if (productionOrder == null) {
            throw new ServiceException("生产订单不存在");
        }
        // 报工前置条件:当前工序必须已经领料(任一物料净领料数量大于0)。
        // 该校验在事务内执行,绕过前端直接调用接口同样受限。
        if (!productionOrderPickService.hasEffectivePick(taskId)) {
            throw new ServiceException("当前工序未领料,无法报工");
        }
        TechnologyRoutingOperation technologyRoutingOperation = technologyRoutingOperationMapper.selectById(routingOperation.getTechnologyRoutingOperationId());
        TechnologyOperation technologyOperation = technologyRoutingOperation == null ? null
                : technologyOperationMapper.selectById(technologyRoutingOperation.getTechnologyOperationId());
@@ -281,24 +279,9 @@
        productionProductMainMapper.insert(productionProductMain);
        syncOperationParamInputValue(dto, routingOperation.getId(), productionProductMain.getId());
        List<ProductStructureDto> productStructureDtos = resolveInputStructures(
                productionOrder.getId(), routingOperation, productModel.getId());
       // 如果没有bom子节点了,那么投入就是他本身
        if (productStructureDtos.isEmpty()) {
            ProductStructureDto fallbackInput = new ProductStructureDto();
            fallbackInput.setProductModelId(productModel.getId());
            fallbackInput.setUnitQuantity(BigDecimal.ONE);
            productStructureDtos.add(fallbackInput);
        }
        for (ProductStructureDto item : productStructureDtos) {
            // 当前实现按工序成品直接作为投入,后续若接入领料记录可在这里替换来源。
            ProductionProductInput productionProductInput = new ProductionProductInput();
            productionProductInput.setProductionProductMainId(productionProductMain.getId());
            productionProductInput.setProductModelId(item.getProductModelId());
            productionProductInput.setInputQuantity(item.getUnitQuantity().multiply(defaultDecimal(dto.getQuantity())));
            productionProductInput.setQuantity(productionProductInput.getInputQuantity());
            productionProductInputMapper.insert(productionProductInput);
        }
        // 报工投入来源于当前工序的实际领料记录,不再按 BOM 单位用量 × 报工数量推算。
        // 每条有效领料只会被首次报工归集一次,重复报工不会重复计入同一批物料。
        insertInputsFromTaskPicks(taskId, productionProductMain.getId());
        ProductionProductOutput productionProductOutput = new ProductionProductOutput();
        productionProductOutput.setProductionProductMainId(productionProductMain.getId());
@@ -360,6 +343,12 @@
                        StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(),
                        productModel.getId(),
                        "0");
                //StockInRecord stockInRecord = stockInRecordService.getOne(Wrappers.<StockInRecord>lambdaQuery()
                //        .eq(StockInRecord::getRecordId, productionProductMain.getId())
                //        .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode())
                //);
                //stockInRecordService.batchApprove(Collections.singletonList(stockInRecord.getId()),1);
            }
            productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).add(productQty));
@@ -401,7 +390,7 @@
            productionAccount.setSchedulingUserId(user == null ? null : user.getUserId());
            productionAccount.setSchedulingUserName(user == null ? dto.getUserName() : user.getNickName());
            productionAccount.setFinishedNum(productQty);
            productionAccount.setWorkHours(workHours);
            productionAccount.setWorkHours(technologyOperation != null ? technologyOperation.getSalaryQuota() : null);
            productionAccount.setTechnologyOperationName(technologyOperation == null ? null : technologyOperation.getName());
            productionAccount.setSchedulingDate(LocalDateTime.now());
            productionAccountMapper.insert(productionAccount);
@@ -504,62 +493,54 @@
        return target;
    }
    private List<ProductStructureDto> resolveInputStructures(Long productionOrderId,
                                                             ProductionOrderRoutingOperation routingOperation,
                                                             Long outputProductModelId) {
        if (productionOrderId == null || routingOperation == null || routingOperation.getTechnologyOperationId() == null) {
            return new ArrayList<>();
    /**
     * 用当前工序尚未被报工归集的实际领料生成报工投入。
     * 投入数量取净领料数量(领料+补料-退料),并记录来源领料ID,
     * 保证同一笔领料不会在多次报工中被重复计入。
     */
    private void insertInputsFromTaskPicks(Long productionOperationTaskId, Long productionProductMainId) {
        if (productionOperationTaskId == null || productionProductMainId == null) {
            return;
        }
        ProductionOrderBom orderBom = productionOrderBomMapper.selectOne(
                Wrappers.<ProductionOrderBom>lambdaQuery()
                        .eq(ProductionOrderBom::getProductionOrderId, productionOrderId)
                        .orderByDesc(ProductionOrderBom::getId)
                        .last("limit 1"));
        if (orderBom == null || orderBom.getId() == null) {
            return new ArrayList<>();
        List<ProductionOrderPick> taskPickList = productionOrderPickMapper.selectList(
                Wrappers.<ProductionOrderPick>lambdaQuery()
                        .eq(ProductionOrderPick::getProductionOperationTaskId, productionOperationTaskId)
                        .orderByAsc(ProductionOrderPick::getId));
        if (taskPickList.isEmpty()) {
            return;
        }
        List<Long> pickIdList = taskPickList.stream()
                .map(ProductionOrderPick::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        Set<Long> consumedPickIdSet = pickIdList.isEmpty()
                ? Collections.emptySet()
                : productionProductInputMapper.selectList(
                        Wrappers.<ProductionProductInput>lambdaQuery()
                                .in(ProductionProductInput::getProductionOrderPickId, pickIdList))
                .stream()
                .map(ProductionProductInput::getProductionOrderPickId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        List<ProductionBomStructure> bomNodeList = productionBomStructureMapper.selectList(
                Wrappers.<ProductionBomStructure>lambdaQuery()
                        .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId())
                        .orderByAsc(ProductionBomStructure::getId));
        if (bomNodeList.isEmpty()) {
            return new ArrayList<>();
        }
        Map<Long, ProductionBomStructure> nodeMap = bomNodeList.stream()
                .filter(item -> item != null && item.getId() != null)
                .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left));
        Long currentOutputModelId = routingOperation.getProductModelId() != null
                ? routingOperation.getProductModelId()
                : outputProductModelId;
        Map<Long, BigDecimal> unitQtyByProductModel = new LinkedHashMap<>();
        for (ProductionBomStructure node : bomNodeList) {
            if (node == null || node.getParentId() == null || node.getProductModelId() == null) {
        for (ProductionOrderPick pick : taskPickList) {
            if (pick == null || pick.getId() == null || consumedPickIdSet.contains(pick.getId())) {
                continue;
            }
            if (!Objects.equals(node.getTechnologyOperationId(), routingOperation.getTechnologyOperationId())) {
            BigDecimal effectiveQuantity = defaultDecimal(pick.getQuantity())
                    .add(defaultDecimal(pick.getFeedingQty()))
                    .subtract(defaultDecimal(pick.getReturnQty()));
            if (effectiveQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                continue;
            }
            ProductionBomStructure parent = nodeMap.get(node.getParentId());
            if (parent == null || !Objects.equals(parent.getProductModelId(), currentOutputModelId)) {
                continue;
            }
            unitQtyByProductModel.merge(node.getProductModelId(), defaultDecimal(node.getUnitQuantity()), BigDecimal::add);
            ProductionProductInput productionProductInput = new ProductionProductInput();
            productionProductInput.setProductionProductMainId(productionProductMainId);
            productionProductInput.setProductModelId(pick.getProductModelId());
            productionProductInput.setProductionOrderPickId(pick.getId());
            productionProductInput.setInputQuantity(effectiveQuantity);
            productionProductInput.setQuantity(effectiveQuantity);
            productionProductInputMapper.insert(productionProductInput);
        }
        List<ProductStructureDto> result = new ArrayList<>();
        for (Map.Entry<Long, BigDecimal> entry : unitQtyByProductModel.entrySet()) {
            if (entry.getValue().compareTo(BigDecimal.ZERO) <= 0) {
                continue;
            }
            ProductStructureDto item = new ProductStructureDto();
            item.setProductModelId(entry.getKey());
            item.setUnitQuantity(entry.getValue());
            result.add(item);
        }
        return result;
    }
    private Boolean removeProductMainByProductionTask(ProductionProductMain productionProductMain) {