yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
@@ -7,6 +7,7 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcImportExcelVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO;
@@ -16,6 +17,7 @@
import cn.iocoder.yudao.module.mes.service.qc.imports.MesQcImportHelper;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcMapper;
@@ -27,6 +29,7 @@
import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService;
import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService;
import cn.iocoder.yudao.module.mes.service.wm.outsourcereceipt.MesWmOutsourceReceiptService;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrSaveReqVO;
@@ -65,6 +68,8 @@
    @Resource
    private MesQcTemplateItemService templateItemService;
    @Resource
    private MesQcTemplateService templateService;
    @Resource
    private MesQcIqcLineService iqcLineService;
    @Resource
    private MesQcDefectRecordService defectRecordService;
@@ -101,22 +106,24 @@
        // 1.2 校验来源单据参数,并验证数据存在
        String sourceDocCode = validateAndGetSourceDocCode(
                createReqVO.getSourceDocType(), createReqVO.getSourceDocId(), createReqVO.getSourceLineId());
        // 1.3 根据产品 + IQC 类型查找可用质检方案(支持前端显式指定)
        List<MesQcTemplateItemDO> templateItems = templateItemService.getTemplateItemList(
                createReqVO.getItemId(), MesQcTypeEnum.IQC.getType(), null, null);
        if (CollUtil.isEmpty(templateItems)) {
            throw exception(QC_NO_TEMPLATE);
        }
        MesQcTemplateItemDO templateItem = templateItems.get(0);
        // 1.3 确定质检方案:质检方案与产品物料不绑定,显式指定时只校验方案本身可用;未指定时走默认方案解析
        Long templateId;
        if (createReqVO.getTemplateId() != null) {
            templateItem = templateItems.stream()
                    .filter(item -> Objects.equals(item.getTemplateId(), createReqVO.getTemplateId()))
                    .findFirst().orElse(null);
            if (templateItem == null) {
                throw exception(QC_NO_TEMPLATE); // 指定了方案但不在匹配列表中,拒绝
            MesQcTemplateDO template = templateService.getTemplate(createReqVO.getTemplateId());
            if (template == null) {
                throw new ServiceException(QC_TEMPLATE_NOT_EXISTS.getCode(), StrUtil.format(
                        "所选质检方案不存在(ID={}),可能已被删除,请刷新后重新选择质检方案", createReqVO.getTemplateId()));
            }
            if (!CommonStatusEnum.isEnable(template.getStatus())) {
                throw exception(QC_IQC_TEMPLATE_IS_DISABLE, template.getName());
            }
            if (!CollUtil.contains(template.getTypes(), MesQcTypeEnum.IQC.getType())) {
                throw exception(QC_IQC_TEMPLATE_TYPE_NOT_MATCH, template.getName());
            }
            templateId = template.getId();
        } else {
            templateId = resolveDefaultIqcTemplateId(createReqVO.getItemId());
        }
        Long templateId = templateItem.getTemplateId();
        // 2. 插入主表
        MesQcIqcDO iqc = BeanUtils.toBean(createReqVO, MesQcIqcDO.class)
@@ -377,17 +384,8 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createIqcFromArrivalNotice(Long noticeId, MesWmArrivalNoticeLineDO line, String noticeCode, Long vendorId) {
        // 1. 校验物料是否配置了 IQC 检验模板
        MesQcTemplateItemDO templateItem;
        try {
            templateItem = templateItemService.getRequiredTemplateByItemIdAndType(
                    line.getItemId(), MesQcTypeEnum.IQC.getType());
        } catch (Exception e) {
            // 获取物料信息,提供更明确的错误提示
            String itemInfo = getItemInfo(line.getItemId());
            throw exception(QC_NO_TEMPLATE_FOR_ITEM, itemInfo);
        }
        Long templateId = templateItem.getTemplateId();
        // 1. 确定质检方案:质检方案与产品物料不绑定,物料未绑定方案时取任一启用的来料检验方案
        Long templateId = resolveDefaultIqcTemplateId(line.getItemId());
        // 2. 生成检验单编码
        String code = "IQC" + System.currentTimeMillis();
@@ -418,21 +416,28 @@
    }
    /**
     * 获取物料信息(编码+名称)
     * 解析来料检验的默认质检方案:质检方案与产品物料不绑定。
     *
     * 1. 物料已绑定来料检验方案时,优先沿用(兼容存量配置);
     * 2. 物料未绑定时,取任一启用且检测种类包含来料检验的方案(按编号取最早创建的一个)。
     *
     * @param itemId 产品物料编号
     * @return 质检方案编号
     */
    private String getItemInfo(Long itemId) {
        if (itemId == null) {
            return "未知物料";
    private Long resolveDefaultIqcTemplateId(Long itemId) {
        List<MesQcTemplateItemDO> boundList = templateItemService.getTemplateItemList(
                itemId, MesQcTypeEnum.IQC.getType(), null, null);
        if (CollUtil.isNotEmpty(boundList)) {
            return boundList.get(0).getTemplateId();
        }
        try {
            cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO item = mdmItemApi.getItem(itemId).getCheckedData();
            if (item != null) {
                return item.getCode() + "-" + item.getName();
            }
        } catch (Exception e) {
            // 忽略异常
        List<MesQcTemplateDO> enabledTemplates = templateService.getEnabledTemplateListByType(MesQcTypeEnum.IQC.getType());
        if (CollUtil.isEmpty(enabledTemplates)) {
            throw exception(QC_IQC_NO_ENABLED_TEMPLATE);
        }
        return "物料ID:" + itemId;
        return enabledTemplates.stream()
                .map(MesQcTemplateDO::getId)
                .min(Comparator.naturalOrder())
                .orElseThrow(() -> exception(QC_IQC_NO_ENABLED_TEMPLATE));
    }
    @Override