liyong
5 天以前 0f792c9b3b88dc4b1b7b306f89e257d931d2d0bd
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
@@ -12,9 +12,15 @@
import cn.iocoder.yudao.module.mes.dal.mysql.wm.arrivalnotice.MesWmArrivalNoticeMapper;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmArrivalNoticeStatusEnum;
import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderRespDTO;
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 jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -40,11 +46,19 @@
    @Resource
    private MesWmArrivalNoticeLineService arrivalNoticeLineService;
    @Resource
    private cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private ErpPurchaseOrderApi purchaseOrderApi;
    @Resource
    private MesMdmItemService mdmItemService;
    @Resource
    @Lazy
    private MesQcIqcService iqcService;
    @Override
    public Long createArrivalNotice(MesWmArrivalNoticeSaveReqVO createReqVO) {
@@ -73,16 +87,14 @@
    private void validateArrivalNoticeSaveData(MesWmArrivalNoticeSaveReqVO reqVO) {
        // 校验编码唯一
        validateCodeUnique(reqVO.getId(), reqVO.getCode());
        // 校验供应商存在且启用
        vendorService.validateVendorExistsAndEnable(reqVO.getVendorId());
        // 校验采购订单存在,并自动填充采购订单号
        // 校验供应商存在(ERP 采购供应商)
        supplierApi.validateSupplier(reqVO.getVendorId());
        // 校验采购订单存在
        if (reqVO.getPurchaseOrderId() != null) {
            ErpPurchaseOrderRespDTO purchaseOrder = purchaseOrderApi.getPurchaseOrder(reqVO.getPurchaseOrderId()).getCheckedData();
            if (purchaseOrder == null) {
                throw exception(WM_PURCHASE_ORDER_NOT_EXISTS);
            }
            // 自动填充采购订单号
            reqVO.setPurchaseOrderCode(purchaseOrder.getNo());
        }
    }
@@ -112,7 +124,7 @@
    @Transactional(rollbackFor = Exception.class)
    public void submitArrivalNotice(Long id) {
        // 1.1 校验存在 + 草稿状态
        validateArrivalNoticeExistsAndDraft(id);
        MesWmArrivalNoticeDO notice = validateArrivalNoticeExistsAndDraft(id);
        // 1.2 检查是否有行项目
        List<MesWmArrivalNoticeLineDO> lines = arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(id);
        if (CollUtil.isEmpty(lines)) {
@@ -123,9 +135,26 @@
        boolean needCheck = CollectionUtils.anyMatch(lines,
                line -> Boolean.TRUE.equals(line.getIqcCheckFlag()));
        if (needCheck) {
            // 需要检验,进入待质检
            // 2.1 需要检验,进入待质检
            arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO()
                    .setId(id).setStatus(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus()));
            // 2.2 为需要质检的行自动创建 IQC 检验单
            for (MesWmArrivalNoticeLineDO line : lines) {
                if (Boolean.TRUE.equals(line.getIqcCheckFlag()) && line.getIqcId() == null) {
                    try {
                        Long iqcId = iqcService.createIqcFromArrivalNotice(
                                id, line, notice.getCode(), notice.getVendorId());
                        // 更新行上的 iqcId
                        arrivalNoticeLineService.updateArrivalNoticeLineIqcId(line.getId(), iqcId);
                        log.info("[submitArrivalNotice][到货通知单({}) 行({}) 自动创建 IQC 检验单({})]",
                                id, line.getId(), iqcId);
                    } catch (Exception e) {
                        log.warn("[submitArrivalNotice][到货通知单({}) 行({}) 创建 IQC 失败: {}]",
                                id, line.getId(), e.getMessage());
                        // 继续处理其他行,不中断流程
                    }
                }
            }
            return;
        }
        // 不需要检验,直接进入待入库
@@ -135,7 +164,7 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateArrivalNoticeWhenIqcFinish(Long id, Long lineId, Long iqcId, BigDecimal qualifiedQuantity) {
    public void updateArrivalNoticeWhenIqcFinish(Long id, Long lineId, Long iqcId, Integer checkResult, BigDecimal qualifiedQuantity) {
        // 1.1 校验到货通知单存在
        MesWmArrivalNoticeDO notice = validateArrivalNoticeExists(id);
        // 1.2 校验状态为待质检
@@ -143,18 +172,28 @@
            throw exception(WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_QC);
        }
        // 2. 更新到货通知单行:绑定 iqcId + 合格数量
        arrivalNoticeLineService.updateArrivalNoticeLineWhenIqcFinish(lineId, iqcId, qualifiedQuantity);
        // 2. 更新到货通知单行:绑定 iqcId + 质检结果 + 合格数量
        arrivalNoticeLineService.updateArrivalNoticeLineWhenIqcFinish(lineId, iqcId, checkResult, qualifiedQuantity);
        // 3.1 判断是否所有需检行都已完成
        // 3.1 判断是否所有需检行都已完成质检
        List<MesWmArrivalNoticeLineDO> lines = arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(id);
        boolean hasUnchecked = CollectionUtils.anyMatch(lines,
                line -> Boolean.TRUE.equals(line.getIqcCheckFlag()) && line.getIqcId() == null);
                line -> Boolean.TRUE.equals(line.getIqcCheckFlag()) && line.getIqcCheckResult() == null);
        if (hasUnchecked) {
            log.info("[updateArrivalNoticeWhenIqcFinish][到货通知单({}) 还有未完成检验的行,暂不更新状态]", id);
            return;
        }
        // 3.2 所有行检验完成,更新主表状态 PENDING_QC → PENDING_RECEIPT
        // 3.2 判断是否所有需检行都可以入库(合格或特采)
        boolean hasNonInbound = CollectionUtils.anyMatch(lines,
                line -> Boolean.TRUE.equals(line.getIqcCheckFlag())
                        && !MesQcCheckResultEnum.canInbound(line.getIqcCheckResult()));
        if (hasNonInbound) {
            log.info("[updateArrivalNoticeWhenIqcFinish][到货通知单({}) 存在不可入库的行(退货/报废),状态保持待质检]", id);
            return;
        }
        // 3.3 所有行检验完成且可入库,更新主表状态 PENDING_QC → PENDING_RECEIPT
        arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO()
                .setId(id).setStatus(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus()));
    }
@@ -256,4 +295,88 @@
        return arrivalNoticeMapper.selectCountByVendorId(vendorId);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long generateArrivalNoticeFromPurchaseOrder(Long purchaseOrderId) {
        // 1. 校验是否已生成过到货通知单(防止重复生成)
        MesWmArrivalNoticeDO existNotice = arrivalNoticeMapper.selectByPurchaseOrderId(purchaseOrderId);
        if (existNotice != null) {
            throw exception(WM_ARRIVAL_NOTICE_ALREADY_EXISTS);
        }
        // 2. 获取采购订单
        ErpPurchaseOrderRespDTO purchaseOrder = purchaseOrderApi.getPurchaseOrder(purchaseOrderId).getCheckedData();
        if (purchaseOrder == null) {
            throw exception(WM_PURCHASE_ORDER_NOT_EXISTS);
        }
        // 3. 获取采购订单明细
        List<ErpPurchaseOrderItemRespDTO> items = purchaseOrderApi.getPurchaseOrderItemList(purchaseOrderId).getCheckedData();
        if (CollUtil.isEmpty(items)) {
            throw exception(WM_PURCHASE_ORDER_NO_ITEM);
        }
        // 4. 生成到货通知单编码
        String code = generateArrivalNoticeCode();
        // 5. 创建到货通知单主表,直接设为待入库状态
        MesWmArrivalNoticeDO notice = new MesWmArrivalNoticeDO();
        notice.setCode(code);
        notice.setName(purchaseOrder.getNo() + "到货通知");
        notice.setPurchaseOrderId(purchaseOrderId);
        notice.setPurchaseOrderCode(purchaseOrder.getNo());
        notice.setVendorId(purchaseOrder.getSupplierId());
        notice.setArrivalDate(java.time.LocalDateTime.now()); // 到货时间为当前时间
        notice.setStatus(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus());
        arrivalNoticeMapper.insert(notice);
        // 6. 遍历明细创建到货通知单行
        boolean hasNeedQc = false;
        for (ErpPurchaseOrderItemRespDTO item : items) {
            MesWmArrivalNoticeLineDO line = new MesWmArrivalNoticeLineDO();
            line.setNoticeId(notice.getId());
            line.setPurchaseOrderItemId(item.getId());
            line.setItemId(item.getProductId()); // 从采购订单明细带入物料编号
            BigDecimal arrivalQuantity = item.getCount() != null ? item.getCount().subtract(item.getInCount() != null ? item.getInCount() : BigDecimal.ZERO) : BigDecimal.ZERO;
            line.setArrivalQuantity(arrivalQuantity);
            // 从采购订单明细带入质检标识
            boolean needQc = Boolean.TRUE.equals(item.getQcCheckFlag());
            line.setIqcCheckFlag(needQc);
            // 如果不需要质检,合格数量等于到货数量
            if (!needQc) {
                line.setQualifiedQuantity(arrivalQuantity);
            } else {
                hasNeedQc = true;
            }
            arrivalNoticeLineService.createArrivalNoticeLineFromPurchaseOrderItem(line);
        }
        // 7. 如果有需要质检的行,更新状态为待质检并自动创建 IQC 单
        if (hasNeedQc) {
            arrivalNoticeMapper.updateById(new MesWmArrivalNoticeDO()
                    .setId(notice.getId()).setStatus(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus()));
            // 为需要质检的行自动创建 IQC 检验单
            List<MesWmArrivalNoticeLineDO> lines = arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(notice.getId());
            for (MesWmArrivalNoticeLineDO line : lines) {
                if (Boolean.TRUE.equals(line.getIqcCheckFlag())) {
                    Long iqcId = iqcService.createIqcFromArrivalNotice(
                            notice.getId(), line, notice.getCode(), notice.getVendorId());
                    arrivalNoticeLineService.updateArrivalNoticeLineIqcId(line.getId(), iqcId);
                    log.info("[generateArrivalNoticeFromPurchaseOrder][到货通知单({}) 行({}) 自动创建 IQC 检验单({})]",
                            notice.getId(), line.getId(), iqcId);
                }
            }
        }
        return notice.getId();
    }
    /**
     * 生成到货通知单编码
     */
    private String generateArrivalNoticeCode() {
        // TODO: 使用编码规则服务生成编码
        return "AN" + System.currentTimeMillis();
    }
}