gaoluyang
9 天以前 d39e4f7d839167c16425086dfa2f8747bccfdff9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
@@ -96,6 +96,13 @@
    private void createTransactionList(Long produceId, List<MesWmProductProduceLineDO> lines) {
        for (MesWmProductProduceLineDO line : lines) {
            if (MesWmQualityStatusEnum.FAIL.getStatus().equals(line.getQualityStatus())) {
                // 不合格产出保留记录,交由不合格品处理,不生成合格库存流水。
                continue;
            }
            if (!MesWmQualityStatusEnum.PASS.getStatus().equals(line.getQualityStatus())) {
                throw exception(WM_PRODUCT_PRODUCE_QUALITY_PENDING);
            }
            // 1. 校验每行明细数量之和等于行数量
            List<MesWmProductProduceDetailDO> details = productProduceDetailService.getProductProduceDetailListByLineId(
                    line.getId());
@@ -121,6 +128,7 @@
    @Transactional(rollbackFor = Exception.class)
    public MesWmProductProduceDO generateProductProduce(MesProFeedbackDO feedback, boolean checkFlag,
                                                         MesProFeedbackApproveReqVO approveReqVO) {
        if (!checkFlag) throw exception(WM_PRODUCT_PRODUCE_QUALITY_PENDING);
        // 0. 查询关联的工单(用于获取 clientId 等信息)
        MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(feedback.getWorkOrderId());
@@ -135,6 +143,7 @@
        // 2. 获取或生成批次(审批请求中的批次属性优先)
        MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO()
                .setItemId(feedback.getItemId())
                .setReelNo(workOrder != null ? workOrder.getReelNo() : null)
                .setProduceDate(LocalDate.now().atStartOfDay()) // 截断到当天零点,确保同一天的报工生成相同批次号
                .setExpireDate(feedback.getExpireDate())
                .setWorkOrderId(feedback.getWorkOrderId())
@@ -160,68 +169,23 @@
        Long batchId = batch != null ? batch.getId() : null;
        String batchCode = batch != null ? batch.getCode() : null;
        WarehouseTarget outputTarget = resolveOutputTarget(feedback.getWorkOrderId(), feedback.getProcessId(), feedback.getItemId());
        // 3. 根据是否需要检验分支处理
        if (checkFlag) {
            // 3.1 需要检验:创建一条行(质量状态=待检验),不生成明细
            MesWmProductProduceLineDO line = buildProduceLine(produce, feedback, batchId, batchCode,
                    feedback.getFeedbackQuantity(), MesWmQualityStatusEnum.PENDING.getStatus());
            productProduceLineService.createProductProduceLine(line);
            // 注意:checkFlag=true 时不生成明细行,等 IPQC 检验完成后由 splitPendingAndFinishProduce 按质量结果拆行+生成明细
        } else {
            // 3.2 无需检验:按合格品/不合格品各生成一行 + 明细
            BigDecimal qualifiedQty = ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO);
            BigDecimal unqualifiedQty = ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO);
            // 3.2.0 兜底:合格/不良数量都未填写时(报工只填报工数量),默认整单为合格品
            if (qualifiedQty.compareTo(BigDecimal.ZERO) <= 0 && unqualifiedQty.compareTo(BigDecimal.ZERO) <= 0) {
                qualifiedQty = feedback.getFeedbackQuantity();
            }
            // 3.2.1 不合格品行 + 明细
            if (unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
                MesWmProductProduceLineDO unqualifiedLine = buildProduceLine(produce, feedback, batchId, batchCode,
                        unqualifiedQty, MesWmQualityStatusEnum.FAIL.getStatus());
                productProduceLineService.createProductProduceLine(unqualifiedLine);
                MesWmProductProduceDetailDO unqualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode,
                        unqualifiedLine.getId(), unqualifiedQty,
                        outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId());
                productProduceDetailService.createProductProduceDetail(unqualifiedDetail);
            }
            // 3.2.2 合格品行 + 明细
            if (qualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
                MesWmProductProduceLineDO qualifiedLine = buildProduceLine(produce, feedback, batchId, batchCode,
                        qualifiedQty, MesWmQualityStatusEnum.PASS.getStatus());
                productProduceLineService.createProductProduceLine(qualifiedLine);
                MesWmProductProduceDetailDO qualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode,
                        qualifiedLine.getId(), qualifiedQty,
                        outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId());
                productProduceDetailService.createProductProduceDetail(qualifiedDetail);
            }
        }
        // 新报工产出统一待检,历史不检验即入库分支已移除。
        MesWmProductProduceLineDO line = buildProduceLine(produce, feedback, batchId, batchCode,
                feedback.getFeedbackQuantity(), MesWmQualityStatusEnum.PENDING.getStatus(),
                workOrder != null ? workOrder.getReelNo() : null);
        productProduceLineService.createProductProduceLine(line);
        return produce;
    }
    private MesWmProductProduceLineDO buildProduceLine(MesWmProductProduceDO produce, MesProFeedbackDO feedback,
                                                       Long batchId, String batchCode,
                                                       BigDecimal quantity, Integer qualityStatus) {
                                                       BigDecimal quantity, Integer qualityStatus, String reelNo) {
        return MesWmProductProduceLineDO.builder()
                .produceId(produce.getId()).feedbackId(feedback.getId())
                .itemId(feedback.getItemId()).quantity(quantity)
                .batchId(batchId).batchCode(batchCode)
                .batchId(batchId).batchCode(batchCode).reelNo(reelNo)
                .expireDate(feedback.getExpireDate()).lotNumber(feedback.getLotNumber())
                .qualityStatus(qualityStatus)
                .build();
    }
    private MesWmProductProduceDetailDO buildProduceDetail(MesWmProductProduceDO produce, MesProFeedbackDO feedback,
                                                           Long batchId, String batchCode,
                                                           Long lineId, BigDecimal quantity,
                                                           Long warehouseId, Long locationId, Long areaId) {
        return MesWmProductProduceDetailDO.builder()
                .produceId(produce.getId()).lineId(lineId)
                .itemId(feedback.getItemId()).quantity(quantity)
                .batchId(batchId).batchCode(batchCode)
                .warehouseId(warehouseId).locationId(locationId).areaId(areaId)
                .build();
    }
@@ -256,6 +220,14 @@
        }
        // 2. 通过 sourceLineId 直接定位待检验行
        MesWmProductProduceLineDO pendingLine = productProduceLineService.validateProductProduceLineExists(sourceLineId);
        if (!ObjUtil.equal(pendingLine.getProduceId(), produce.getId())
                || !ObjUtil.equal(pendingLine.getFeedbackId(), feedbackId)
                || !ObjUtil.equal(pendingLine.getQualityStatus(), MesWmQualityStatusEnum.PENDING.getStatus())
                || qualifiedQty == null || unqualifiedQty == null || pendingLine.getQuantity() == null
                || qualifiedQty.signum() < 0 || unqualifiedQty.signum() < 0
                || qualifiedQty.add(unqualifiedQty).compareTo(pendingLine.getQuantity()) != 0) {
            throw exception(WM_PRODUCT_PRODUCE_INSPECTION_INVALID);
        }
        WarehouseTarget outputTarget = resolveOutputTarget(produce.getWorkOrderId(), produce.getProcessId(), pendingLine.getItemId());
        // 3A. 情况一:存在不合格品数量,需要拆分行
@@ -265,14 +237,14 @@
                    .produceId(produce.getId()).feedbackId(feedbackId)
                    .itemId(pendingLine.getItemId()).quantity(unqualifiedQty)
                    .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
                    .expireDate(pendingLine.getExpireDate()).lotNumber(pendingLine.getLotNumber())
                    .expireDate(pendingLine.getExpireDate()).lotNumber(pendingLine.getLotNumber()).reelNo(pendingLine.getReelNo())
                    .qualityStatus(MesWmQualityStatusEnum.FAIL.getStatus())
                    .build();
            productProduceLineService.createProductProduceLine(unqualifiedLine);
            productProduceDetailService.createProductProduceDetail(MesWmProductProduceDetailDO.builder()
                    .produceId(produce.getId()).lineId(unqualifiedLine.getId())
                    .itemId(pendingLine.getItemId()).quantity(unqualifiedQty)
                    .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
                    .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()).reelNo(pendingLine.getReelNo())
                    .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
                    .build());
            // 3A.2 合格品:复用原待检行,更新数量和状态 + 明细
@@ -283,7 +255,7 @@
                productProduceDetailService.createProductProduceDetail(MesWmProductProduceDetailDO.builder()
                        .produceId(produce.getId()).lineId(pendingLine.getId())
                        .itemId(pendingLine.getItemId()).quantity(qualifiedQty)
                        .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
                        .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()).reelNo(pendingLine.getReelNo())
                        .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
                        .build());
            }
@@ -294,7 +266,7 @@
            productProduceDetailService.createProductProduceDetail(MesWmProductProduceDetailDO.builder()
                    .produceId(produce.getId()).lineId(pendingLine.getId())
                    .itemId(pendingLine.getItemId()).quantity(pendingLine.getQuantity())
                    .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
                    .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()).reelNo(pendingLine.getReelNo())
                    .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
                    .build());
        }