宁夏万通需求:生产领料取消三个字段必填;来料检验质检方案与产品物料解绑

1. 生产领料:取消生产任务 taskId 的 @NotNull 校验,创建/修改/保存校验三处 task 关联改为未选择时跳过(生产订单、工作站本就非必填)
2. 来料检验:创建时显式指定质检方案则校验方案本身(存在、已启用、检测种类含来料检验),不再要求方案与产品物料绑定;未指定时仍按物料绑定方案兜底
3. 质检方案选择接口:未指定物料/工单/任务时按检测种类返回全部启用方案;新增错误码 QC_IQC_TEMPLATE_IS_DISABLE、QC_IQC_TEMPLATE_TYPE_NOT_MATCH
已修改7个文件
89 ■■■■■ 文件已修改
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/template/MesQcTemplateItemController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/MesWmProductIssueSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/template/MesQcTemplateItemController.java
@@ -28,6 +28,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
@@ -91,13 +92,22 @@
    }
    @GetMapping("/list-by-item-and-type")
    @Operation(summary = "根据物料或工单/任务和检测种类获取可用质检方案")
    @Operation(summary = "根据物料或工单/任务和检测种类获取可用质检方案(未指定物料/工单/任务时返回该检测种类下全部启用方案)")
    @PreAuthorize("@ss.hasPermission('mes:qc-template:query')")
    public CommonResult<List<MesQcTemplateItemOptionRespVO>> getTemplateItemOptionList(
            @RequestParam(value = "qcType", required = false) Integer qcType,
            @RequestParam(value = "itemId", required = false) Long itemId,
            @RequestParam(value = "workOrderId", required = false) Long workOrderId,
            @RequestParam(value = "taskId", required = false) Long taskId) {
        // 未指定物料/工单/任务时,按检测种类返回全部启用的质检方案(不限制产品物料关联)
        if (itemId == null && workOrderId == null && taskId == null && qcType != null) {
            List<MesQcTemplateItemOptionRespVO> options = templateService.getEnabledTemplateListByType(qcType)
                    .stream().map(template -> new MesQcTemplateItemOptionRespVO()
                            .setTemplateId(template.getId()).setTemplateCode(template.getCode())
                            .setTemplateName(template.getName()))
                    .collect(Collectors.toList());
            return success(options);
        }
        List<MesQcTemplateItemDO> list = templateItemService.getTemplateItemList(
                itemId, qcType, workOrderId, taskId);
        if (CollUtil.isEmpty(list)) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/MesWmProductIssueSaveReqVO.java
@@ -22,8 +22,7 @@
    @NotBlank(message = "领料单名称不能为空")
    private String name;
    @Schema(description = "生产任务 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "生产任务 ID 不能为空")
    @Schema(description = "生产任务 ID", example = "1")
    private Long taskId;
    @Schema(description = "生产工单 ID", example = "1")
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -393,6 +393,8 @@
    ErrorCode QC_NO_TEMPLATE_FOR_ITEM = new ErrorCode(1_040_603_008, "物料【{}】未配置 IQC 检测模板,请先在质量管理中配置");
    ErrorCode QC_IQC_SOURCE_DOC_PARAMS_MISSING = new ErrorCode(1_040_603_006, "来源单据类型非空时,来源单据 ID 和来源单据行 ID 不能为空");
    ErrorCode QC_IQC_CHECK_RESULT_EMPTY = new ErrorCode(1_040_603_007, "完成检验单前,检测结果必须填写");
    ErrorCode QC_IQC_TEMPLATE_IS_DISABLE = new ErrorCode(1_040_603_009, "所选质检方案【{}】已禁用,请重新选择已启用的质检方案");
    ErrorCode QC_IQC_TEMPLATE_TYPE_NOT_MATCH = new ErrorCode(1_040_603_010, "所选质检方案【{}】的检测种类不包含来料检验,请重新选择检测种类包含来料检验的质检方案");
    // ========== MES 质量管理-来料检验行(1-040-603-100) ==========
    ErrorCode QC_IQC_LINE_NOT_EXISTS = new ErrorCode(1_040_603_100, "来料检验行不存在");
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,25 @@
        // 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 = templateItemService.getRequiredTemplateByItemIdAndType(
                    createReqVO.getItemId(), MesQcTypeEnum.IQC.getType()).getTemplateId();
        }
        Long templateId = templateItem.getTemplateId();
        // 2. 插入主表
        MesQcIqcDO iqc = BeanUtils.toBean(createReqVO, MesQcIqcDO.class)
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateService.java
@@ -90,4 +90,12 @@
     */
    MesQcTemplateDO validateTemplateExists(Long id);
    /**
     * 获得指定检测种类的启用质检方案列表(未指定物料等上下文时的下拉选择)
     *
     * @param qcType 检测种类
     * @return 质检方案列表
     */
    List<MesQcTemplateDO> getEnabledTemplateListByType(Integer qcType);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/template/MesQcTemplateServiceImpl.java
@@ -2,6 +2,7 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.MesQcTemplatePageReqVO;
@@ -17,6 +18,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -119,5 +121,13 @@
        return templateMapper.selectByIds(ids);
    }
    @Override
    public List<MesQcTemplateDO> getEnabledTemplateListByType(Integer qcType) {
        return templateMapper.selectList().stream()
                .filter(template -> CommonStatusEnum.isEnable(template.getStatus())
                        && CollUtil.contains(template.getTypes(), qcType))
                .collect(Collectors.toList());
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java
@@ -91,11 +91,11 @@
        validateProductIssueSaveData(createReqVO);
        // 2. 从 task 自动带入 workOrderId 和 workstationId
        MesProTaskDO task = taskService.validateTaskExists(createReqVO.getTaskId());
        if (createReqVO.getWorkOrderId() == null) {
        MesProTaskDO task = createReqVO.getTaskId() != null ? taskService.validateTaskExists(createReqVO.getTaskId()) : null;
        if (task != null && createReqVO.getWorkOrderId() == null) {
            createReqVO.setWorkOrderId(task.getWorkOrderId());
        }
        if (createReqVO.getWorkstationId() == null) {
        if (task != null && createReqVO.getWorkstationId() == null) {
            createReqVO.setWorkstationId(task.getWorkstationId());
        }
@@ -115,11 +115,11 @@
        validateProductIssueSaveData(updateReqVO);
        // 2. 从 task 自动带入 workOrderId 和 workstationId
        MesProTaskDO task = taskService.validateTaskExists(updateReqVO.getTaskId());
        if (updateReqVO.getWorkOrderId() == null) {
        MesProTaskDO task = updateReqVO.getTaskId() != null ? taskService.validateTaskExists(updateReqVO.getTaskId()) : null;
        if (task != null && updateReqVO.getWorkOrderId() == null) {
            updateReqVO.setWorkOrderId(task.getWorkOrderId());
        }
        if (updateReqVO.getWorkstationId() == null) {
        if (task != null && updateReqVO.getWorkstationId() == null) {
            updateReqVO.setWorkstationId(task.getWorkstationId());
        }
@@ -398,10 +398,10 @@
     */
    private void validateProductIssueSaveData(MesWmProductIssueSaveReqVO reqVO) {
        validateCodeUnique(reqVO.getId(), reqVO.getCode());
        // 校验 task 存在
        MesProTaskDO task = taskService.validateTaskExists(reqVO.getTaskId());
        // 校验 task 关联的工单已确认
        Long workOrderId = reqVO.getWorkOrderId() != null ? reqVO.getWorkOrderId() : task.getWorkOrderId();
        // 校验 task 存在(生产任务非必填,未选时跳过)
        MesProTaskDO task = reqVO.getTaskId() != null ? taskService.validateTaskExists(reqVO.getTaskId()) : null;
        // 校验关联的工单已确认
        Long workOrderId = reqVO.getWorkOrderId() != null ? reqVO.getWorkOrderId() : (task != null ? task.getWorkOrderId() : null);
        if (workOrderId != null) {
            workOrderService.validateWorkOrderConfirmed(workOrderId);
        }