Commit 9c94c1f58940f0e3aeb90aa4c449ff88dc584e20

Authored by YunaiV
1 parent 0d27932d

feat(mes): 修复 IQC 模块 TODO @AI(第二轮)

1. MesQcIqcSaveReqVO: 新增 @AssertTrue 校验 qualifiedQuantity + unqualifiedQuantity == receivedQuantity
2. MesQcIqcLineDO/ServiceImpl/RespVO/Controller: toolId(Long) 改为 tool(String),从 MesQcIndicatorDO 冗余读取,移除 MesTmToolService 依赖
3. MesWmArrivalNoticeService/Impl: 新增 validateArrivalNoticeAndLineExists 方法,校验通知单+行存在且匹配
4. MesQcIqcServiceImpl: 抽取 validateSourceDoc 私有方法校验来源单据;completeIqc 增加 checkResult 必填校验并移除重复数量校验;writeBackSourceDoc 使用 ObjectUtil.hasNull 简化判断
5. ErrorCodeConstants: 新增 QC_IQC_SOURCE_DOC_PARAMS_MISSING、QC_IQC_CHECK_RESULT_EMPTY
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcLineController.java
@@ -9,11 +9,9 @@ import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.line.MesQcIqcLineR @@ -9,11 +9,9 @@ import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.line.MesQcIqcLineR
9 import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcLineDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcLineDO;
11 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO; 11 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO;
12 -import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolDO;  
13 import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService; 12 import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
14 import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcLineService; 13 import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcLineService;
15 import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService; 14 import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
16 -import cn.iocoder.yudao.module.mes.service.tm.tool.MesTmToolService;  
17 import io.swagger.v3.oas.annotations.Operation; 15 import io.swagger.v3.oas.annotations.Operation;
18 import io.swagger.v3.oas.annotations.Parameter; 16 import io.swagger.v3.oas.annotations.Parameter;
19 import io.swagger.v3.oas.annotations.tags.Tag; 17 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -46,8 +44,6 @@ public class MesQcIqcLineController { @@ -46,8 +44,6 @@ public class MesQcIqcLineController {
46 private MesQcIndicatorService indicatorService; 44 private MesQcIndicatorService indicatorService;
47 @Resource 45 @Resource
48 private MesMdUnitMeasureService unitMeasureService; 46 private MesMdUnitMeasureService unitMeasureService;
49 - @Resource  
50 - private MesTmToolService toolService;  
51 47
52 @GetMapping("/get") 48 @GetMapping("/get")
53 @Operation(summary = "获得来料检验单行") 49 @Operation(summary = "获得来料检验单行")
@@ -85,13 +81,6 @@ public class MesQcIqcLineController { @@ -85,13 +81,6 @@ public class MesQcIqcLineController {
85 .setIndicatorType(indicator.getType())); 81 .setIndicatorType(indicator.getType()));
86 findAndThen(unitMeasureMap, vo.getUnitMeasureId(), 82 findAndThen(unitMeasureMap, vo.getUnitMeasureId(),
87 unit -> vo.setUnitMeasureName(unit.getName())); 83 unit -> vo.setUnitMeasureName(unit.getName()));
88 - // 逐个查询工具名称(工具数量通常较少)  
89 - if (vo.getToolId() != null) {  
90 - MesTmToolDO tool = toolService.getTool(vo.getToolId());  
91 - if (tool != null) {  
92 - vo.setToolName(tool.getName());  
93 - }  
94 - }  
95 }); 84 });
96 } 85 }
97 86
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcSaveReqVO.java
1 package cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo; 1 package cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.AssertTrue;
4 import jakarta.validation.constraints.DecimalMin; 6 import jakarta.validation.constraints.DecimalMin;
5 import jakarta.validation.constraints.NotEmpty; 7 import jakarta.validation.constraints.NotEmpty;
6 import jakarta.validation.constraints.NotNull; 8 import jakarta.validation.constraints.NotNull;
@@ -82,4 +84,13 @@ public class MesQcIqcSaveReqVO { @@ -82,4 +84,13 @@ public class MesQcIqcSaveReqVO {
82 @Schema(description = "备注", example = "备注") 84 @Schema(description = "备注", example = "备注")
83 private String remark; 85 private String remark;
84 86
  87 + @AssertTrue(message = "合格品数量与不合格品数量之和必须等于本次接收数量")
  88 + @Schema(hidden = true)
  89 + public boolean isQuantityValid() {
  90 + if (ObjUtil.hasNull(qualifiedQuantity, unqualifiedQuantity, receivedQuantity)) {
  91 + return true; // @NotNull 会处理 null
  92 + }
  93 + return qualifiedQuantity.add(unqualifiedQuantity).compareTo(receivedQuantity) == 0;
  94 + }
  95 +
85 } 96 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/line/MesQcIqcLineRespVO.java
@@ -30,11 +30,8 @@ public class MesQcIqcLineRespVO { @@ -30,11 +30,8 @@ public class MesQcIqcLineRespVO {
30 @Schema(description = "检测指标类型", example = "QUANTITATIVE") 30 @Schema(description = "检测指标类型", example = "QUANTITATIVE")
31 private String indicatorType; 31 private String indicatorType;
32 32
33 - @Schema(description = "检测工具 ID", example = "5")  
34 - private Long toolId;  
35 -  
36 - @Schema(description = "检测工具名称", example = "卡尺")  
37 - private String toolName; 33 + @Schema(description = "检测工具", example = "卡尺")
  34 + private String tool;
38 35
39 @Schema(description = "检测方法", example = "目测") 36 @Schema(description = "检测方法", example = "目测")
40 private String checkMethod; 37 private String checkMethod;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/iqc/MesQcIqcLineDO.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc;
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; 4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
5 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO; 5 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO;
6 -import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolDO;  
7 import com.baomidou.mybatisplus.annotation.KeySequence; 6 import com.baomidou.mybatisplus.annotation.KeySequence;
8 import com.baomidou.mybatisplus.annotation.TableId; 7 import com.baomidou.mybatisplus.annotation.TableId;
9 import com.baomidou.mybatisplus.annotation.TableName; 8 import com.baomidou.mybatisplus.annotation.TableName;
@@ -44,11 +43,11 @@ public class MesQcIqcLineDO extends BaseDO { @@ -44,11 +43,11 @@ public class MesQcIqcLineDO extends BaseDO {
44 */ 43 */
45 private Long indicatorId; 44 private Long indicatorId;
46 /** 45 /**
47 - * 检测工具 ID 46 + * 检测工具
48 * 47 *
49 - * 关联 {@link MesTmToolDO#getId()} 48 + * 冗余 {@link MesQcIndicatorDO#getTool()}
50 */ 49 */
51 - private Long toolId; 50 + private String tool;
52 /** 51 /**
53 * 检测方法 52 * 检测方法
54 */ 53 */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/template/MesQcTemplateIndicatorMapper.java
@@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Mapper; @@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Mapper;
9 9
10 import java.util.List; 10 import java.util.List;
11 11
  12 +// TODO @AI:MesQcTemplateIndicatorService 独立一个;然后非 MesQcTemplateIndicatorService 调用 mapper 的地方,都替换下;
12 /** 13 /**
13 * MES 质检方案-检测指标项 Mapper 14 * MES 质检方案-检测指标项 Mapper
14 * 15 *
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -250,6 +250,8 @@ public interface ErrorCodeConstants { @@ -250,6 +250,8 @@ public interface ErrorCodeConstants {
250 ErrorCode QC_IQC_NOT_PREPARE = new ErrorCode(1_040_603_002, "只有草稿状态的检验单才可操作"); 250 ErrorCode QC_IQC_NOT_PREPARE = new ErrorCode(1_040_603_002, "只有草稿状态的检验单才可操作");
251 ErrorCode QC_IQC_QUANTITY_MISMATCH = new ErrorCode(1_040_603_004, "合格品与不合格品数量之和须等于检测数量"); 251 ErrorCode QC_IQC_QUANTITY_MISMATCH = new ErrorCode(1_040_603_004, "合格品与不合格品数量之和须等于检测数量");
252 ErrorCode QC_IQC_NO_TEMPLATE = new ErrorCode(1_040_603_005, "当前产品未配置 IQC 检测模板"); 252 ErrorCode QC_IQC_NO_TEMPLATE = new ErrorCode(1_040_603_005, "当前产品未配置 IQC 检测模板");
  253 + ErrorCode QC_IQC_SOURCE_DOC_PARAMS_MISSING = new ErrorCode(1_040_603_006, "来源单据类型非空时,来源单据 ID 和来源单据行 ID 不能为空");
  254 + ErrorCode QC_IQC_CHECK_RESULT_EMPTY = new ErrorCode(1_040_603_007, "完成检验单前,检测结果必须填写");
253 // ========== MES 质量管理-来料检验行(1-040-603-100) ========== 255 // ========== MES 质量管理-来料检验行(1-040-603-100) ==========
254 ErrorCode QC_IQC_LINE_NOT_EXISTS = new ErrorCode(1_040_603_100, "来料检验行不存在"); 256 ErrorCode QC_IQC_LINE_NOT_EXISTS = new ErrorCode(1_040_603_100, "来料检验行不存在");
255 257
@@ -344,4 +346,10 @@ public interface ErrorCodeConstants { @@ -344,4 +346,10 @@ public interface ErrorCodeConstants {
344 ErrorCode WM_MATERIAL_REQUEST_STATUS_INVALID = new ErrorCode(1_040_706_001, "领料申请单状态不正确,无法执行该操作"); 346 ErrorCode WM_MATERIAL_REQUEST_STATUS_INVALID = new ErrorCode(1_040_706_001, "领料申请单状态不正确,无法执行该操作");
345 ErrorCode WM_MATERIAL_REQUEST_LINE_NOT_EXISTS = new ErrorCode(1_040_706_100, "领料申请单行不存在"); 347 ErrorCode WM_MATERIAL_REQUEST_LINE_NOT_EXISTS = new ErrorCode(1_040_706_100, "领料申请单行不存在");
346 348
  349 + // ========== MES 仓库管理-外协出库单(1-040-707-000) ==========
  350 + ErrorCode WM_OUTSOURCE_ISSUE_NOT_EXISTS = new ErrorCode(1_040_707_000, "外协出库单不存在");
  351 + ErrorCode WM_OUTSOURCE_ISSUE_STATUS_INVALID = new ErrorCode(1_040_707_001, "外协出库单状态不正确,无法执行该操作");
  352 + ErrorCode WM_OUTSOURCE_ISSUE_QUANTITY_MISMATCH = new ErrorCode(1_040_707_002, "出库单行数量与明细数量不一致");
  353 + ErrorCode WM_OUTSOURCE_ISSUE_LINE_NOT_EXISTS = new ErrorCode(1_040_707_100, "外协出库单行不存在");
  354 +
347 } 355 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcLineServiceImpl.java
@@ -6,20 +6,24 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -6,20 +6,24 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.line.MesQcIqcLinePageReqVO; 6 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.line.MesQcIqcLinePageReqVO;
7 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO; 7 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
8 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcLineDO; 8 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcLineDO;
  9 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO;
9 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateIndicatorDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateIndicatorDO;
10 import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcLineMapper; 11 import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcLineMapper;
11 import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateIndicatorMapper; 12 import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateIndicatorMapper;
12 import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum; 13 import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
  14 +import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
13 import jakarta.annotation.Resource; 15 import jakarta.annotation.Resource;
14 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
15 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
16 18
17 import java.util.ArrayList; 19 import java.util.ArrayList;
18 import java.util.List; 20 import java.util.List;
  21 +import java.util.Map;
19 import java.util.Objects; 22 import java.util.Objects;
20 23
21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
22 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
23 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; 27 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
24 28
25 /** 29 /**
@@ -36,6 +40,9 @@ public class MesQcIqcLineServiceImpl implements MesQcIqcLineService { @@ -36,6 +40,9 @@ public class MesQcIqcLineServiceImpl implements MesQcIqcLineService {
36 @Resource 40 @Resource
37 private MesQcTemplateIndicatorMapper templateIndicatorMapper; 41 private MesQcTemplateIndicatorMapper templateIndicatorMapper;
38 42
  43 + @Resource
  44 + private MesQcIndicatorService indicatorService;
  45 +
39 @Override 46 @Override
40 public MesQcIqcLineDO validateIqcLineExists(Long id) { 47 public MesQcIqcLineDO validateIqcLineExists(Long id) {
41 MesQcIqcLineDO line = iqcLineMapper.selectById(id); 48 MesQcIqcLineDO line = iqcLineMapper.selectById(id);
@@ -57,14 +64,22 @@ public class MesQcIqcLineServiceImpl implements MesQcIqcLineService { @@ -57,14 +64,22 @@ public class MesQcIqcLineServiceImpl implements MesQcIqcLineService {
57 64
58 @Override 65 @Override
59 public void createLinesFromTemplate(Long iqcId, Long templateId) { 66 public void createLinesFromTemplate(Long iqcId, Long templateId) {
60 - List<MesQcTemplateIndicatorDO> indicators = templateIndicatorMapper.selectListByTemplateId(templateId);  
61 - List<MesQcIqcLineDO> lines = convertList(indicators, indicator -> new MesQcIqcLineDO()  
62 - .setIqcId(iqcId).setIndicatorId(indicator.getIndicatorId())  
63 - .setToolId(null) // TODO @芋艿:模板指标暂无 toolId,后续可扩展  
64 - .setCheckMethod(indicator.getCheckMethod())  
65 - .setStandardValue(indicator.getStandardValue()).setUnitMeasureId(indicator.getUnitMeasureId())  
66 - .setMaxThreshold(indicator.getThresholdMax()).setMinThreshold(indicator.getThresholdMin())  
67 - .setCriticalQuantity(0).setMajorQuantity(0).setMinorQuantity(0)); 67 + List<MesQcTemplateIndicatorDO> templateIndicators = templateIndicatorMapper.selectListByTemplateId(templateId);
  68 + if (CollUtil.isEmpty(templateIndicators)) {
  69 + return;
  70 + }
  71 + Map<Long, MesQcIndicatorDO> indicatorMap = indicatorService.getIndicatorMap(
  72 + convertSet(templateIndicators, MesQcTemplateIndicatorDO::getIndicatorId));
  73 + List<MesQcIqcLineDO> lines = convertList(templateIndicators, templateIndicator -> {
  74 + MesQcIndicatorDO indicator = indicatorMap.get(templateIndicator.getIndicatorId());
  75 + return new MesQcIqcLineDO()
  76 + .setIqcId(iqcId).setIndicatorId(templateIndicator.getIndicatorId())
  77 + .setTool(indicator != null ? indicator.getTool() : null)
  78 + .setCheckMethod(templateIndicator.getCheckMethod())
  79 + .setStandardValue(templateIndicator.getStandardValue()).setUnitMeasureId(templateIndicator.getUnitMeasureId())
  80 + .setMaxThreshold(templateIndicator.getThresholdMax()).setMinThreshold(templateIndicator.getThresholdMin())
  81 + .setCriticalQuantity(0).setMajorQuantity(0).setMinorQuantity(0);
  82 + });
68 iqcLineMapper.insertBatch(lines); 83 iqcLineMapper.insertBatch(lines);
69 } 84 }
70 85
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java
@@ -22,16 +22,18 @@ public interface MesQcIqcService { @@ -22,16 +22,18 @@ public interface MesQcIqcService {
22 * 创建来料检验单 22 * 创建来料检验单
23 * 23 *
24 * @param createReqVO 创建信息 24 * @param createReqVO 创建信息
  25 + * @param inspectorUserId 检测人员用户 ID
25 * @return 编号 26 * @return 编号
26 */ 27 */
27 - Long createIqc(@Valid MesQcIqcSaveReqVO createReqVO); 28 + Long createIqc(@Valid MesQcIqcSaveReqVO createReqVO, Long inspectorUserId);
28 29
29 /** 30 /**
30 * 更新来料检验单 31 * 更新来料检验单
31 * 32 *
32 * @param updateReqVO 更新信息 33 * @param updateReqVO 更新信息
  34 + * @param inspectorUserId 检测人员用户 ID
33 */ 35 */
34 - void updateIqc(@Valid MesQcIqcSaveReqVO updateReqVO); 36 + void updateIqc(@Valid MesQcIqcSaveReqVO updateReqVO, Long inspectorUserId);
35 37
36 /** 38 /**
37 * 完成来料检验单 39 * 完成来料检验单
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
@@ -4,11 +4,11 @@ import cn.hutool.core.collection.CollUtil; @@ -4,11 +4,11 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.hutool.core.util.ObjectUtil;
7 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO; 8 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO;
8 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO; 9 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO;
9 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO; 11 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
11 -import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;  
12 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO; 12 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
13 import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcMapper; 13 import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcMapper;
14 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; 14 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
@@ -16,7 +16,7 @@ import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum; @@ -16,7 +16,7 @@ import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
16 import cn.iocoder.yudao.module.mes.enums.qc.MesQcIqcStatusEnum; 16 import cn.iocoder.yudao.module.mes.enums.qc.MesQcIqcStatusEnum;
17 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; 17 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
18 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService; 18 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
19 -import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService; 19 +import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateDetailService;
20 import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService; 20 import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService;
21 import jakarta.annotation.Resource; 21 import jakarta.annotation.Resource;
22 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
@@ -44,7 +44,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -44,7 +44,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
44 @Resource 44 @Resource
45 private MesQcIqcMapper iqcMapper; 45 private MesQcIqcMapper iqcMapper;
46 @Resource 46 @Resource
47 - private MesQcTemplateService templateService; 47 + private MesQcTemplateDetailService templateDetailService;
48 48
49 @Resource 49 @Resource
50 private MesQcIqcLineService iqcLineService; 50 private MesQcIqcLineService iqcLineService;
@@ -55,46 +55,42 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -55,46 +55,42 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
55 55
56 @Override 56 @Override
57 @Transactional(rollbackFor = Exception.class) 57 @Transactional(rollbackFor = Exception.class)
58 - public Long createIqc(MesQcIqcSaveReqVO createReqVO) { 58 + public Long createIqc(MesQcIqcSaveReqVO createReqVO, Long inspectorUserId) {
59 // 1.1 校验编号唯一 59 // 1.1 校验编号唯一
60 validateIqcCodeUnique(null, createReqVO.getCode()); 60 validateIqcCodeUnique(null, createReqVO.getCode());
61 - // 1.2 查找物料关联的 IQC 检测模板  
62 - Long templateId = createReqVO.getTemplateId();  
63 - MesQcTemplateDO template = templateService.validateTemplateExists(templateId);  
64 - if (!CollUtil.contains(template.getTypes(), MesQcTypeEnum.IQC.getType())) {  
65 - throw exception(QC_IQC_NO_TEMPLATE);  
66 - } 61 + // 1.2 校验来源单据参数,并验证数据存在
  62 + validateSourceDoc(createReqVO.getSourceDocType(), createReqVO.getSourceDocId(), createReqVO.getSourceLineId());
  63 + // 1.3 通过 itemId + IQC 类型自动查找模板
  64 + MesQcTemplateItemDO templateItem = templateDetailService.getRequiredTemplateByItemIdAndType(
  65 + createReqVO.getItemId(), MesQcTypeEnum.IQC.getType());
  66 + Long templateId = templateItem.getTemplateId();
67 67
68 - // 3.1 从模板的产品关联中获取检测参数(min_check_quantity、max_unqualified_quantity)  
69 - MesQcTemplateItemDO templateItem = templateService.getTemplateItemByTemplateIdAndItemId(  
70 - templateId, createReqVO.getItemId());  
71 - if (templateItem != null) {  
72 - if (createReqVO.getMinCheckQuantity() == null) {  
73 - createReqVO.setMinCheckQuantity(templateItem.getQuantityCheck());  
74 - }  
75 - if (createReqVO.getMaxUnqualifiedQuantity() == null) {  
76 - createReqVO.setMaxUnqualifiedQuantity(templateItem.getQuantityUnqualified());  
77 - }  
78 - }  
79 - // 3.2 插入主表 68 + // 2. 插入主表
80 MesQcIqcDO iqc = BeanUtils.toBean(createReqVO, MesQcIqcDO.class); 69 MesQcIqcDO iqc = BeanUtils.toBean(createReqVO, MesQcIqcDO.class);
  70 + iqc.setTemplateId(templateId);
  71 + iqc.setCheckQuantity(createReqVO.getQualifiedQuantity().add(createReqVO.getUnqualifiedQuantity()));
  72 + iqc.setInspectorUserId(inspectorUserId);
81 iqc.setStatus(MesQcIqcStatusEnum.PREPARE.getType()); 73 iqc.setStatus(MesQcIqcStatusEnum.PREPARE.getType());
82 iqcMapper.insert(iqc); 74 iqcMapper.insert(iqc);
83 75
84 - // 4. 从模板指标自动生成检验行 76 + // 3. 从模板指标自动生成检验行
85 iqcLineService.createLinesFromTemplate(iqc.getId(), templateId); 77 iqcLineService.createLinesFromTemplate(iqc.getId(), templateId);
86 return iqc.getId(); 78 return iqc.getId();
87 } 79 }
88 80
89 @Override 81 @Override
90 - public void updateIqc(MesQcIqcSaveReqVO updateReqVO) { 82 + public void updateIqc(MesQcIqcSaveReqVO updateReqVO, Long inspectorUserId) {
91 // 1.1 校验存在 + 草稿状态 83 // 1.1 校验存在 + 草稿状态
92 validateIqcStatusPrepare(updateReqVO.getId()); 84 validateIqcStatusPrepare(updateReqVO.getId());
93 // 1.2 校验编号唯一 85 // 1.2 校验编号唯一
94 validateIqcCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); 86 validateIqcCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
95 87
96 - // 2. 更新 88 + // 2. 更新主表
97 MesQcIqcDO updateObj = BeanUtils.toBean(updateReqVO, MesQcIqcDO.class); 89 MesQcIqcDO updateObj = BeanUtils.toBean(updateReqVO, MesQcIqcDO.class);
  90 + updateObj.setSourceDocType(null).setSourceDocId(null).setSourceLineId(null); // 不允许修改来源单据
  91 + updateObj.setTemplateId(null); // 不允许修改模板
  92 + updateObj.setCheckQuantity(updateReqVO.getQualifiedQuantity().add(updateReqVO.getUnqualifiedQuantity()));
  93 + updateObj.setInspectorUserId(inspectorUserId);
98 iqcMapper.updateById(updateObj); 94 iqcMapper.updateById(updateObj);
99 } 95 }
100 96
@@ -102,14 +98,11 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -102,14 +98,11 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
102 public void completeIqc(Long id) { 98 public void completeIqc(Long id) {
103 // 1.1 校验存在 + 草稿状态 99 // 1.1 校验存在 + 草稿状态
104 MesQcIqcDO iqc = validateIqcStatusPrepare(id); 100 MesQcIqcDO iqc = validateIqcStatusPrepare(id);
105 - // 1.2 校验合格品 + 不合格品 = 检测数量  
106 - if (iqc.getCheckQuantity() != null && iqc.getCheckQuantity().compareTo(BigDecimal.ZERO) > 0) {  
107 - BigDecimal total = ObjUtil.defaultIfNull(iqc.getQualifiedQuantity(), BigDecimal.ZERO)  
108 - .add(ObjUtil.defaultIfNull(iqc.getUnqualifiedQuantity(), BigDecimal.ZERO));  
109 - if (total.compareTo(iqc.getCheckQuantity()) != 0) {  
110 - throw exception(QC_IQC_QUANTITY_MISMATCH);  
111 - } 101 + // DONE @AI:已修复,增加 checkResult 必填校验
  102 + if (iqc.getCheckResult() == null) {
  103 + throw exception(QC_IQC_CHECK_RESULT_EMPTY);
112 } 104 }
  105 + // DONE @AI:已修复,数量校验已在 MesQcIqcSaveReqVO 的 @AssertTrue 中保证,此处移除
113 106
114 // 2. 更新状态为已完成 107 // 2. 更新状态为已完成
115 MesQcIqcDO updateObj = new MesQcIqcDO() 108 MesQcIqcDO updateObj = new MesQcIqcDO()
@@ -130,7 +123,8 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -130,7 +123,8 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
130 if (iqc.getSourceDocType() == null) { 123 if (iqc.getSourceDocType() == null) {
131 return; 124 return;
132 } 125 }
133 - if (iqc.getSourceLineId() == null || iqc.getSourceDocId() == null) { 126 + // DONE @AI:已修复,使用 ObjectUtil.hasNull() 简化判断
  127 + if (ObjectUtil.hasNull(iqc.getSourceLineId(), iqc.getSourceDocId())) {
134 throw new IllegalArgumentException( 128 throw new IllegalArgumentException(
135 "IQC 单[" + iqc.getId() + "] sourceDocType 非空但 sourceLineId 或 sourceDocId 为空"); 129 "IQC 单[" + iqc.getId() + "] sourceDocType 非空但 sourceLineId 或 sourceDocId 为空");
136 } 130 }
@@ -152,9 +146,9 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -152,9 +146,9 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
152 // 2.1 删除主表 146 // 2.1 删除主表
153 iqcMapper.deleteById(id); 147 iqcMapper.deleteById(id);
154 // 2.2 级联删除行 148 // 2.2 级联删除行
155 - iqcLineService.deleteByIqcId(id); 149 + iqcLineService.deleteListByIqcId(id);
156 // 2.3 级联删除缺陷记录 150 // 2.3 级联删除缺陷记录
157 - defectRecordService.deleteByQcTypeAndQcId(MesQcTypeEnum.IQC.getType(), id); 151 + defectRecordService.deleteListByQcTypeAndQcId(MesQcTypeEnum.IQC.getType(), id);
158 } 152 }
159 153
160 @Override 154 @Override
@@ -190,6 +184,26 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -190,6 +184,26 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
190 } 184 }
191 } 185 }
192 186
  187 + /**
  188 + * 校验来源单据参数,并验证数据存在
  189 + *
  190 + * @param sourceDocType 来源单据类型
  191 + * @param sourceDocId 来源单据 ID
  192 + * @param sourceLineId 来源单据行 ID
  193 + */
  194 + private void validateSourceDoc(Integer sourceDocType, Long sourceDocId, Long sourceLineId) {
  195 + if (sourceDocType == null) {
  196 + return;
  197 + }
  198 + if (ObjUtil.hasNull(sourceDocId, sourceLineId)) {
  199 + throw exception(QC_IQC_SOURCE_DOC_PARAMS_MISSING);
  200 + }
  201 + // 根据来源单据类型,校验数据存在且匹配
  202 + if (Objects.equals(sourceDocType, MesBizTypeConstants.WM_ARRIVAL_NOTICE)) {
  203 + arrivalNoticeService.validateArrivalNoticeAndLineExists(sourceDocId, sourceLineId);
  204 + }
  205 + }
  206 +
193 @Override 207 @Override
194 public MesQcIqcDO getIqc(Long id) { 208 public MesQcIqcDO getIqc(Long id) {
195 return iqcMapper.selectById(id); 209 return iqcMapper.selectById(id);
@@ -246,7 +260,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { @@ -246,7 +260,7 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
246 if (CollUtil.isEmpty(ids)) { 260 if (CollUtil.isEmpty(ids)) {
247 return Collections.emptyMap(); 261 return Collections.emptyMap();
248 } 262 }
249 - List<MesQcIqcDO> list = iqcMapper.selectBatchIds(ids); 263 + List<MesQcIqcDO> list = iqcMapper.selectByIds(ids);
250 return convertMap(list, MesQcIqcDO::getId); 264 return convertMap(list, MesQcIqcDO::getId);
251 } 265 }
252 266
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeService.java
@@ -93,6 +93,14 @@ public interface MesWmArrivalNoticeService { @@ -93,6 +93,14 @@ public interface MesWmArrivalNoticeService {
93 } 93 }
94 94
95 /** 95 /**
  96 + * 校验到货通知单和行存在,且行属于该通知单
  97 + *
  98 + * @param noticeId 到货通知单 ID
  99 + * @param lineId 到货通知单行 ID
  100 + */
  101 + void validateArrivalNoticeAndLineExists(Long noticeId, Long lineId);
  102 +
  103 + /**
96 * 按状态获得到货通知单列表 104 * 按状态获得到货通知单列表
97 * 105 *
98 * @param status 状态 106 * @param status 状态
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
@@ -21,6 +21,7 @@ import java.math.BigDecimal; @@ -21,6 +21,7 @@ import java.math.BigDecimal;
21 import java.util.Collection; 21 import java.util.Collection;
22 import java.util.Collections; 22 import java.util.Collections;
23 import java.util.List; 23 import java.util.List;
  24 +import java.util.Objects;
24 25
25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; 27 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -166,6 +167,24 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService @@ -166,6 +167,24 @@ public class MesWmArrivalNoticeServiceImpl implements MesWmArrivalNoticeService
166 return arrivalNoticeMapper.selectListByStatus(status); 167 return arrivalNoticeMapper.selectListByStatus(status);
167 } 168 }
168 169
  170 + @Override
  171 + public void validateArrivalNoticeAndLineExists(Long noticeId, Long lineId) {
  172 + // 1. 校验通知单存在
  173 + MesWmArrivalNoticeDO notice = arrivalNoticeMapper.selectById(noticeId);
  174 + if (notice == null) {
  175 + throw exception(WM_ARRIVAL_NOTICE_NOT_EXISTS);
  176 + }
  177 + // 2.1 校验行存在
  178 + MesWmArrivalNoticeLineDO line = arrivalNoticeLineService.getArrivalNoticeLine(lineId);
  179 + if (line == null) {
  180 + throw exception(WM_ARRIVAL_NOTICE_LINE_NOT_EXISTS);
  181 + }
  182 + // 2.2 校验行属于该通知单
  183 + if (ObjUtil.notEqual(line.getNoticeId(), noticeId)) {
  184 + throw exception(WM_ARRIVAL_NOTICE_LINE_NOT_EXISTS);
  185 + }
  186 + }
  187 +
169 private MesWmArrivalNoticeDO validateArrivalNoticeExists(Long id) { 188 private MesWmArrivalNoticeDO validateArrivalNoticeExists(Long id) {
170 MesWmArrivalNoticeDO notice = arrivalNoticeMapper.selectById(id); 189 MesWmArrivalNoticeDO notice = arrivalNoticeMapper.selectById(id);
171 if (notice == null) { 190 if (notice == null) {