gaoluyang
5 天以前 6121117080cffd6528b7f233c98e4b1574d8c823
fix(mes): 部分来料不合格时合格数量仍自动进入采购入库

- IQC 完成时按行取可入库数量(需检行取合格数量、无需检行取到货数量),
存在合格数量即推进到货单到待入库并自动生成采购入库单,不再因不合格行阻塞整单
- 生成入库单时需检行只入质检合格数量,不再回退到到货数量
- 整单无可入库合格数量时给出精确提示,避免生成空入库单

Co-Authored-By: Claude Code <noreply@anthropic.com>
已修改3个文件
42 ■■■■■ 文件已修改
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -593,6 +593,7 @@
    ErrorCode WM_ARRIVAL_NOTICE_NO_IQC_LINE = new ErrorCode(1_040_704_009, "到货通知单没有需要检验的明细行,无需生成来料检验单");
    ErrorCode WM_ARRIVAL_NOTICE_IQC_ALREADY_CREATED = new ErrorCode(1_040_704_010, "到货通知单的需检行已全部生成来料检验单");
    ErrorCode WM_ARRIVAL_NOTICE_IQC_CREATE_FAILED = new ErrorCode(1_040_704_011, "生成来料检验单失败");
    ErrorCode WM_ARRIVAL_NOTICE_NO_QUALIFIED_LINE = new ErrorCode(1_040_704_012, "到货通知单没有可入库的合格数量");
    ErrorCode WM_ARRIVAL_NOTICE_LINE_NOT_EXISTS = new ErrorCode(1_040_704_100, "到货通知单行不存在");
    ErrorCode WM_ARRIVAL_NOTICE_LINE_NOT_MATCH = new ErrorCode(1_040_704_101, "到货通知单行不属于指定的到货通知单");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
@@ -22,7 +22,6 @@
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderItemRespDTO;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcCheckResultEnum;
import cn.iocoder.yudao.module.mes.service.wm.itemreceipt.MesWmItemReceiptService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@@ -260,16 +259,18 @@
            return;
        }
        // 3.2 判断是否所有需检行都可以入库(合格或特采)
        boolean hasNonInbound = CollectionUtils.anyMatch(lines,
                line -> Boolean.TRUE.equals(line.getIqcCheckFlag())
                        && !MesQcCheckResultEnum.canInbound(line.getIqcCheckResult()));
        if (hasNonInbound) {
            log.info("[updateArrivalNoticeWhenIqcFinish][到货通知单({}) 存在不可入库的行(退货/报废),状态保持待质检]", id);
        // 3.2 判断是否存在可入库的合格数量:需检行取质检合格数量,无需检行取到货数量。
        //     部分不合格时,合格部分照常入库;不合格部分由 NCR 单独处置(退货/让步接收),不再阻塞合格品入库。
        boolean hasReceivable = CollectionUtils.anyMatch(lines, line -> {
            BigDecimal receivable = resolveReceivableQuantity(line);
            return receivable != null && receivable.signum() > 0;
        });
        if (!hasReceivable) {
            log.info("[updateArrivalNoticeWhenIqcFinish][到货通知单({}) 各行的合格数量均为 0,状态保持待质检,等待不合格品处置]", id);
            return;
        }
        // 3.3 所有行检验完成且可入库,更新主表状态 PENDING_QC → PENDING_RECEIPT
        // 3.3 检验完成且存在可入库数量,更新主表状态 PENDING_QC → PENDING_RECEIPT
        arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO()
                .setId(id).setStatus(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus()));
        // 3.4 质检合格后直接生成采购入库单(待入库),库管只需核对数量并确认入库
@@ -277,6 +278,16 @@
    }
    /**
     * 计算到货通知行本次可入库数量:需检行取质检合格数量,无需检行取到货数量。
     */
    private BigDecimal resolveReceivableQuantity(MesWmArrivalNoticeLineDO line) {
        if (Boolean.TRUE.equals(line.getIqcCheckFlag())) {
            return line.getQualifiedQuantity();
        }
        return line.getArrivalQuantity();
    }
    /**
     * 到货通知单进入「待入库」后自动生成采购入库单。
     * 库管无需再回到到货通知点「执行入库」,直接到【采购入库】核对数量并确认入库即可。
     */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java
@@ -652,11 +652,12 @@
        receipt.setStatus(MesWmItemReceiptStatusEnum.PREPARE.getStatus());
        itemReceiptMapper.insert(receipt);
        // 6. 遍历行创建入库单行
        // 6. 遍历行创建入库单行:需检行只入质检合格数量,无需检行入到货数量
        int createdLineCount = 0;
        for (MesWmArrivalNoticeLineDO noticeLine : lines) {
            BigDecimal receivable = noticeLine.getQualifiedQuantity() != null
            BigDecimal receivable = Boolean.TRUE.equals(noticeLine.getIqcCheckFlag())
                    ? noticeLine.getQualifiedQuantity() : noticeLine.getArrivalQuantity();
            // 整行已退供应商时不生成零数量入库行;其余行仍正常收货。
            // 整行不合格(合格数量为 0)或无需检行未填写到货数量时,不生成零数量入库行
            if (receivable == null || receivable.signum() <= 0) {
                continue;
            }
@@ -665,11 +666,16 @@
            receiptLine.setArrivalNoticeLineId(noticeLine.getId());
            receiptLine.setPurchaseOrderItemId(noticeLine.getPurchaseOrderItemId());
            receiptLine.setItemId(noticeLine.getItemId());
            // 使用合格数量(如果有 IQC 则是合格数量,否则是到货数量)
            receiptLine.setReceivedQuantity(receivable);
            // 按采购订单行单价快照金额,供来票/对账使用
            fillReceiptLinePrice(receiptLine);
            itemReceiptLineService.createItemReceiptLineFromArrivalNoticeLine(receiptLine);
            createdLineCount++;
        }
        if (createdLineCount == 0) {
            throw new ServiceException(WM_ARRIVAL_NOTICE_NO_QUALIFIED_LINE.getCode(),
                    "到货通知单[" + notice.getCode() + "]没有可入库的合格数量:所有需检行的质检合格数量均为 0 或为空。"
                            + "请先完成不合格品的处置(退货或让步接收),处置完成后再生成入库单。");
        }
        return receipt.getId();