云
2026-10-25 d54fbc9d4f8981fa0f7f613b33a28a8cd4f764ea
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
@@ -9,11 +9,13 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductProduceStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
@@ -23,6 +25,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
import cn.iocoder.yudao.module.mes.service.wm.productreceipt.MesWmProductReceiptService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
@@ -52,6 +55,8 @@
    @Resource
    private MesWmProductProduceMapper productProduceMapper;
    @Resource
    private MesProFeedbackMapper feedbackMapper;
    @Resource
    private MesWmProductProduceLineService productProduceLineService;
@@ -61,6 +66,8 @@
    private MesProWorkOrderService workOrderService;
    @Resource
    private MesProWorkOrderProcessService workOrderProcessService;
    @Resource
    private MesProRouteProductService routeProductService;
    @Resource
    private MesWmProductReceiptService productReceiptService;
    @Resource
@@ -86,7 +93,7 @@
        }
        // 2. 创建库存事务
        createTransactionList(id, lines);
        createTransactionList(produce, lines);
        // 3. 更新入库单状态
        productProduceMapper.updateById(new MesWmProductProduceDO()
@@ -100,7 +107,11 @@
        }
    }
    private void createTransactionList(Long produceId, List<MesWmProductProduceLineDO> lines) {
    private void createTransactionList(MesWmProductProduceDO produce, List<MesWmProductProduceLineDO> lines) {
        MesProFeedbackDO feedback = produce.getFeedbackId() != null
                ? feedbackMapper.selectById(produce.getFeedbackId()) : null;
        String bizCode = feedback != null && cn.hutool.core.util.StrUtil.isNotBlank(feedback.getCode())
                ? feedback.getCode() : "PRODUCE-" + produce.getId();
        for (MesWmProductProduceLineDO line : lines) {
            // 1. 校验每行明细数量之和等于行数量
            List<MesWmProductProduceDetailDO> details = productProduceDetailService.getProductProduceDetailListByLineId(
@@ -117,8 +128,8 @@
                        .setQuantity(detail.getQuantity())
                        .setBatchId(detail.getBatchId()).setBatchCode(detail.getBatchCode())
                        .setWarehouseId(detail.getWarehouseId()).setLocationId(detail.getLocationId()).setAreaId(detail.getAreaId())
                        .setBizType(MesBizTypeConstants.WM_PRODUCT_PRODUCE).setBizId(produceId)
                        .setBizCode("").setBizLineId(line.getId()));
                        .setBizType(MesBizTypeConstants.WM_PRODUCT_PRODUCE).setBizId(produce.getId())
                        .setBizCode(bizCode).setBizLineId(line.getId()));
            }
        }
    }
@@ -127,6 +138,11 @@
    @Transactional(rollbackFor = Exception.class)
    public MesWmProductProduceDO generateProductProduce(MesProFeedbackDO feedback, boolean checkFlag,
                                                         MesProFeedbackApproveReqVO approveReqVO) {
        // 一个报工只能生成一张生产产出单;审批接口重试时直接返回既有单据
        MesWmProductProduceDO existingProduce = productProduceMapper.selectByFeedbackId(feedback.getId());
        if (existingProduce != null) {
            return existingProduce;
        }
        // 0. 查询关联的工单(用于获取 clientId 等信息)
        MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(feedback.getWorkOrderId());
@@ -166,10 +182,8 @@
        Long batchId = batch != null ? batch.getId() : null;
        String batchCode = batch != null ? batch.getCode() : null;
        // 1.5 获取虚拟线边库
        MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
        MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
        MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
        // 1.5 优先使用工艺路线产品绑定的报工仓库;历史数据未配置时回退虚拟线边库。
        WarehouseTarget outputTarget = resolveOutputTarget(feedback.getItemId());
        // 3. 根据是否需要检验分支处理
        if (checkFlag) {
@@ -189,7 +203,7 @@
                productProduceLineService.createProductProduceLine(unqualifiedLine);
                MesWmProductProduceDetailDO unqualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode,
                        unqualifiedLine.getId(), unqualifiedQty,
                        virtualWarehouse.getId(), virtualLocation.getId(), virtualArea.getId());
                        outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId());
                productProduceDetailService.createProductProduceDetail(unqualifiedDetail);
            }
            // 3.2.2 合格品行 + 明细
@@ -199,7 +213,7 @@
                productProduceLineService.createProductProduceLine(qualifiedLine);
                MesWmProductProduceDetailDO qualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode,
                        qualifiedLine.getId(), qualifiedQty,
                        virtualWarehouse.getId(), virtualLocation.getId(), virtualArea.getId());
                        outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId());
                productProduceDetailService.createProductProduceDetail(qualifiedDetail);
            }
        }
@@ -259,13 +273,9 @@
        if (produce == null) {
            throw exception(WM_PRODUCT_PRODUCE_NOT_EXISTS);
        }
        // 1.2 获取虚拟线边库
        MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
        MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
        MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
        // 2. 通过 sourceLineId 直接定位待检验行
        MesWmProductProduceLineDO pendingLine = productProduceLineService.validateProductProduceLineExists(sourceLineId);
        WarehouseTarget outputTarget = resolveOutputTarget(pendingLine.getItemId());
        // 3A. 情况一:存在不合格品数量,需要拆分行
        if (unqualifiedQty != null && unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
@@ -282,7 +292,7 @@
                    .produceId(produce.getId()).lineId(unqualifiedLine.getId())
                    .itemId(pendingLine.getItemId()).quantity(unqualifiedQty)
                    .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
                    .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId())
                    .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
                    .build());
            // 3A.2 合格品:复用原待检行,更新数量和状态 + 明细
            pendingLine.setQuantity(ObjectUtil.defaultIfNull(qualifiedQty, BigDecimal.ZERO));
@@ -293,7 +303,7 @@
                        .produceId(produce.getId()).lineId(pendingLine.getId())
                        .itemId(pendingLine.getItemId()).quantity(qualifiedQty)
                        .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
                        .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId())
                        .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
                        .build());
            }
        } else {
@@ -304,7 +314,7 @@
                    .produceId(produce.getId()).lineId(pendingLine.getId())
                    .itemId(pendingLine.getItemId()).quantity(pendingLine.getQuantity())
                    .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
                    .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId())
                    .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
                    .build());
        }
@@ -312,4 +322,23 @@
        finishProductProduce(produce.getId());
    }
    private WarehouseTarget resolveOutputTarget(Long itemId) {
        MesProRouteProductDO routeProduct = itemId != null
                ? routeProductService.getRouteProductByItemId(itemId) : null;
        if (routeProduct != null && routeProduct.getWarehouseId() != null
                && routeProduct.getLocationId() != null && routeProduct.getAreaId() != null) {
            areaService.validateWarehouseAreaExists(routeProduct.getWarehouseId(),
                    routeProduct.getLocationId(), routeProduct.getAreaId());
            return new WarehouseTarget(routeProduct.getWarehouseId(), routeProduct.getLocationId(), routeProduct.getAreaId());
        }
        MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
        MesWmWarehouseLocationDO location = locationService.getWarehouseLocationByCode(
                MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
        MesWmWarehouseAreaDO area = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
        return new WarehouseTarget(warehouse.getId(), location.getId(), area.getId());
    }
    private record WarehouseTarget(Long warehouseId, Long locationId, Long areaId) {
    }
}