Commit d2feaae7d48c3526ba74cab619f95d9c77a3ce5b

Authored by YunaiV
1 parent 40a6786a

✨ feat(mes): 更新退货检验单逻辑,支持合格/不合格数量拆分

优化退货检验单的处理逻辑,新增对合格品和不合格品数量的支持。实现了根据检验结果更新退货单行的质量状态,并在部分合格的情况下进行行拆分。同时,更新了相关的接口和数据结构,确保数据一致性和完整性。
Showing 15 changed files with 304 additions and 69 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/MesQcRqcLineController.java
... ... @@ -9,11 +9,9 @@ import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.line.MesQcRqcLineR
9 9 import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
10 10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcLineDO;
11 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 12 import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
14 13 import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcLineService;
15 14 import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
16   -import cn.iocoder.yudao.module.mes.service.tm.tool.MesTmToolService;
17 15 import io.swagger.v3.oas.annotations.Operation;
18 16 import io.swagger.v3.oas.annotations.Parameter;
19 17 import io.swagger.v3.oas.annotations.tags.Tag;
... ... @@ -46,8 +44,6 @@ public class MesQcRqcLineController {
46 44 private MesQcIndicatorService indicatorService;
47 45 @Resource
48 46 private MesMdUnitMeasureService unitMeasureService;
49   - @Resource
50   - private MesTmToolService toolService;
51 47  
52 48 @GetMapping("/get")
53 49 @Operation(summary = "获得退货检验单行")
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcPageReqVO.java
... ... @@ -20,8 +20,8 @@ public class MesQcRqcPageReqVO extends PageParam {
20 20 @Schema(description = "检验单编号", example = "RQC2025")
21 21 private String code;
22 22  
23   - @Schema(description = "来源单据类型", example = "RTISSUE")
24   - private String sourceDocType;
  23 + @Schema(description = "来源单据类型", example = "1")
  24 + private Integer sourceDocType;
25 25  
26 26 @Schema(description = "产品物料 ID", example = "20")
27 27 private Long itemId;
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
... ... @@ -17,7 +17,8 @@ public enum MesMdAutoCodeRuleCodeEnum {
17 17 BATCH_CODE("WM_BATCH_CODE", "批次编码"),
18 18 TASK_CODE("PRO_TASK_CODE", "生产任务编码"),
19 19 QC_IQC_CODE("QC_IQC_CODE", "来料检验单编码"),
20   - QC_IPQC_CODE("QC_IPQC_CODE", "过程检验单编码");
  20 + QC_IPQC_CODE("QC_IPQC_CODE", "过程检验单编码"),
  21 + QC_RQC_CODE("QC_RQC_CODE", "退货检验单编码");
21 22  
22 23 /**
23 24 * 规则代码
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImpl.java
... ... @@ -134,8 +134,8 @@ public class MesQcRqcServiceImpl implements MesQcRqcService {
134 134 *
135 135 * <p>根据 sourceDocType 分发:
136 136 * <ul>
137   - * <li>WM_RETURN_ISSUE(生产退料单)→ 回写行的 qualityStatus</li>
138   - * <li>WM_RETURN_SALES(销售退货单)→ 回写行的 qualityStatus</li>
  137 + * <li>WM_RETURN_ISSUE(生产退料单)→ 根据合格/不合格数量拆分行 + 更新 qualityStatus + 联动主单状态</li>
  138 + * <li>WM_RETURN_SALES(销售退货单)→ 同上</li>
139 139 * </ul>
140 140 *
141 141 * @param rqc 退货检验单
... ... @@ -150,11 +150,15 @@ public class MesQcRqcServiceImpl implements MesQcRqcService {
150 150 }
151 151  
152 152 if (Objects.equals(rqc.getSourceDocType(), MesBizTypeConstants.WM_RETURN_ISSUE)) {
153   - // 回写生产退料单行的 qualityStatus
154   - returnIssueLineService.updateReturnIssueLineWhenRqcFinish(rqc.getSourceLineId(), rqc.getCheckResult());
  153 + // 回写生产退料单行:拆分行 + 更新质量状态 + 联动主单状态
  154 + returnIssueLineService.updateReturnIssueLineWhenRqcFinish(
  155 + rqc.getSourceLineId(), rqc.getSourceDocId(), rqc.getCheckResult(),
  156 + rqc.getQualifiedQuantity(), rqc.getUnqualifiedQuantity());
155 157 } else if (Objects.equals(rqc.getSourceDocType(), MesBizTypeConstants.WM_RETURN_SALES)) {
156   - // 回写销售退货单行的 qualityStatus
157   - returnSalesLineService.updateReturnSalesLineWhenRqcFinish(rqc.getSourceLineId(), rqc.getCheckResult());
  158 + // 回写销售退货单行:拆分行 + 更新质量状态 + 联动主单状态
  159 + returnSalesLineService.updateReturnSalesLineWhenRqcFinish(
  160 + rqc.getSourceLineId(), rqc.getSourceDocId(), rqc.getCheckResult(),
  161 + rqc.getQualifiedQuantity(), rqc.getUnqualifiedQuantity());
158 162 } else {
159 163 throw new IllegalArgumentException(
160 164 "RQC 单[" + rqc.getId() + "] sourceDocType=" + rqc.getSourceDocType() + " 无法识别,无法回写来源单据");
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineService.java
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.line.MesWm
6 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO;
7 7 import jakarta.validation.Valid;
8 8  
  9 +import java.math.BigDecimal;
9 10 import java.util.List;
10 11  
11 12 /**
... ... @@ -83,11 +84,23 @@ public interface MesWmReturnIssueLineService {
83 84 void updateReturnIssueQualityStatusByIssueId(Long issueId, Integer issueType);
84 85  
85 86 /**
86   - * RQC 检验完成后,更新生产退料单行的质量状态
  87 + * RQC 检验完成后,根据合格品/不合格品数量更新生产退料单行
87 88 *
88   - * @param id 行 ID
89   - * @param checkResult 检验结果
  89 + * <p>拆分逻辑:
  90 + * <ul>
  91 + * <li>全部合格:直接更新行的 qualityStatus 为合格</li>
  92 + * <li>全部不合格:直接更新行的 qualityStatus 为不合格</li>
  93 + * <li>部分合格部分不合格:拆分原行(原行改为合格+数量=合格数,新增一行不合格+数量=不合格数)</li>
  94 + * </ul>
  95 + * 同时检查退料单下所有行是否都已检验完毕,若全检完则将退料单状态设为待上架。
  96 + *
  97 + * @param sourceLineId 来源行 ID
  98 + * @param sourceDocId 来源退料单 ID
  99 + * @param checkResult 检验结果
  100 + * @param qualifiedQuantity 合格品数量
  101 + * @param unqualifiedQuantity 不合格品数量
90 102 */
91   - void updateReturnIssueLineWhenRqcFinish(Long id, Integer checkResult);
  103 + void updateReturnIssueLineWhenRqcFinish(Long sourceLineId, Long sourceDocId, Integer checkResult,
  104 + BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity);
92 105  
93 106 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.wm.returnissue;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.ObjUtil;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
... ... @@ -8,8 +9,8 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.line.MesWm
8 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueDO;
9 10 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO;
10 11 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnissue.MesWmReturnIssueLineMapper;
11   -import cn.iocoder.yudao.module.mes.enums.qc.MesQcCheckResultEnum;
12 12 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
  13 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnIssueStatusEnum;
13 14 import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnIssueTypeEnum;
14 15 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
15 16 import jakarta.annotation.Resource;
... ... @@ -17,6 +18,7 @@ import org.springframework.context.annotation.Lazy;
17 18 import org.springframework.stereotype.Service;
18 19 import org.springframework.validation.annotation.Validated;
19 20  
  21 +import java.math.BigDecimal;
20 22 import java.util.List;
21 23 import java.util.Objects;
22 24  
... ... @@ -137,10 +139,51 @@ public class MesWmReturnIssueLineServiceImpl implements MesWmReturnIssueLineServ
137 139 }
138 140  
139 141 @Override
140   - public void updateReturnIssueLineWhenRqcFinish(Long id, Integer checkResult) {
141   - Integer qualityStatus = Objects.equals(checkResult, MesQcCheckResultEnum.PASS.getType())
142   - ? MesWmQualityStatusEnum.PASS.getStatus() : MesWmQualityStatusEnum.FAIL.getStatus();
143   - issueLineMapper.updateById(new MesWmReturnIssueLineDO().setId(id).setQualityStatus(qualityStatus));
  142 + public void updateReturnIssueLineWhenRqcFinish(Long sourceLineId, Long sourceDocId, Integer checkResult,
  143 + BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity) {
  144 + MesWmReturnIssueLineDO sourceLine = validateReturnIssueLineExists(sourceLineId);
  145 + boolean hasUnqualified = unqualifiedQuantity != null && unqualifiedQuantity.compareTo(BigDecimal.ZERO) > 0;
  146 + boolean hasQualified = qualifiedQuantity != null && qualifiedQuantity.compareTo(BigDecimal.ZERO) > 0;
  147 +
  148 + // 1. 根据合格/不合格品数量,更新退料单行的质量状态
  149 + if (!hasUnqualified) {
  150 + // 1.A 情况一:全部合格
  151 + issueLineMapper.updateById(new MesWmReturnIssueLineDO()
  152 + .setId(sourceLineId).setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus()));
  153 + } else if (!hasQualified) {
  154 + // 1.B 情况二:全部不合格
  155 + issueLineMapper.updateById(new MesWmReturnIssueLineDO()
  156 + .setId(sourceLineId).setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus()));
  157 + } else {
  158 + // 1.C 情况三:部分合格部分不合格 → 拆分行
  159 + // 1.C.1 新增一行不合格品
  160 + MesWmReturnIssueLineDO unqualifiedLine = new MesWmReturnIssueLineDO()
  161 + .setIssueId(sourceLine.getIssueId())
  162 + .setMaterialStockId(sourceLine.getMaterialStockId())
  163 + .setItemId(sourceLine.getItemId())
  164 + .setQuantity(unqualifiedQuantity)
  165 + .setBatchId(sourceLine.getBatchId())
  166 + .setIpqcId(sourceLine.getIpqcId())
  167 + .setQcFlag(sourceLine.getQcFlag())
  168 + .setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus())
  169 + .setRemark(sourceLine.getRemark());
  170 + issueLineMapper.insert(unqualifiedLine);
  171 + // 1.C.2 更新原行为合格品(数量调整为合格数量)
  172 + issueLineMapper.updateById(new MesWmReturnIssueLineDO()
  173 + .setId(sourceLineId)
  174 + .setQuantity(qualifiedQuantity)
  175 + .setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus()));
  176 + }
  177 +
  178 + // 2. 检查退料单下是否还有待检验的行,若全检完则将退料单状态设为"待上架"
  179 + if (sourceDocId != null) {
  180 + List<MesWmReturnIssueLineDO> allLines = issueLineMapper.selectListByIssueId(sourceDocId);
  181 + boolean allInspected = !CollUtil.contains(allLines,
  182 + line -> Objects.equals(line.getQualityStatus(), MesWmQualityStatusEnum.PENDING.getStatus()));
  183 + if (allInspected) {
  184 + issueService.updateReturnIssueStatus(sourceDocId, MesWmReturnIssueStatusEnum.APPROVING.getStatus());
  185 + }
  186 + }
144 187 }
145 188  
146 189 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueService.java
... ... @@ -85,4 +85,12 @@ public interface MesWmReturnIssueService {
85 85 */
86 86 void cancelReturnIssue(Long id);
87 87  
  88 + /**
  89 + * 更新生产退料单状态(供外部模块联动调用,如 RQC 检验完成后)
  90 + *
  91 + * @param id 编号
  92 + * @param status 目标状态
  93 + */
  94 + void updateReturnIssueStatus(Long id, Integer status);
  95 +
88 96 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java
... ... @@ -271,4 +271,10 @@ public class MesWmReturnIssueServiceImpl implements MesWmReturnIssueService {
271 271 return issue;
272 272 }
273 273  
  274 + @Override
  275 + public void updateReturnIssueStatus(Long id, Integer status) {
  276 + validateReturnIssueExists(id);
  277 + issueMapper.updateById(new MesWmReturnIssueDO().setId(id).setStatus(status));
  278 + }
  279 +
274 280 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineService.java
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWm
6 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO;
7 7 import jakarta.validation.Valid;
8 8  
  9 +import java.math.BigDecimal;
9 10 import java.util.List;
10 11  
11 12 /**
... ... @@ -85,11 +86,23 @@ public interface MesWmReturnSalesLineService {
85 86 void updateQualityStatusByReturnId(Long returnId, Integer qualityStatus);
86 87  
87 88 /**
88   - * RQC 检验完成后,更新销售退货单行的质量状态
  89 + * RQC 检验完成后,根据合格品/不合格品数量更新销售退货单行
89 90 *
90   - * @param id 行 ID
91   - * @param checkResult 检验结果
  91 + * <p>拆分逻辑:
  92 + * <ul>
  93 + * <li>全部合格:直接更新行的 qualityStatus 为合格</li>
  94 + * <li>全部不合格:直接更新行的 qualityStatus 为不合格</li>
  95 + * <li>部分合格部分不合格:拆分原行(原行改为合格+数量=合格数,新增一行不合格+数量=不合格数)</li>
  96 + * </ul>
  97 + * 同时检查退货单下所有行是否都已检验完毕,若全检完则将退货单状态设为待上架。
  98 + *
  99 + * @param sourceLineId 来源行 ID
  100 + * @param sourceDocId 来源退货单 ID
  101 + * @param checkResult 检验结果
  102 + * @param qualifiedQuantity 合格品数量
  103 + * @param unqualifiedQuantity 不合格品数量
92 104 */
93   - void updateReturnSalesLineWhenRqcFinish(Long id, Integer checkResult);
  105 + void updateReturnSalesLineWhenRqcFinish(Long sourceLineId, Long sourceDocId, Integer checkResult,
  106 + BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity);
94 107  
95 108 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.wm.returnsales;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 6 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWmReturnSalesLinePageReqVO;
... ... @@ -7,14 +8,15 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWm
7 8 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
8 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO;
9 10 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales.MesWmReturnSalesLineMapper;
10   -import cn.iocoder.yudao.module.mes.enums.qc.MesQcCheckResultEnum;
11 11 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
  12 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnSalesStatusEnum;
12 13 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
13 14 import jakarta.annotation.Resource;
14 15 import org.springframework.context.annotation.Lazy;
15 16 import org.springframework.stereotype.Service;
16 17 import org.springframework.validation.annotation.Validated;
17 18  
  19 +import java.math.BigDecimal;
18 20 import java.util.List;
19 21 import java.util.Objects;
20 22  
... ... @@ -129,11 +131,48 @@ public class MesWmReturnSalesLineServiceImpl implements MesWmReturnSalesLineServ
129 131 }
130 132  
131 133 @Override
132   - public void updateReturnSalesLineWhenRqcFinish(Long id, Integer checkResult) {
133   - Integer qualityStatus = Objects.equals(checkResult, MesQcCheckResultEnum.PASS.getType())
134   - ? MesWmQualityStatusEnum.PASS.getStatus()
135   - : MesWmQualityStatusEnum.FAIL.getStatus();
136   - returnSalesLineMapper.updateById(new MesWmReturnSalesLineDO().setId(id).setQualityStatus(qualityStatus));
  134 + public void updateReturnSalesLineWhenRqcFinish(Long sourceLineId, Long sourceDocId, Integer checkResult,
  135 + BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity) {
  136 + MesWmReturnSalesLineDO sourceLine = validateReturnSalesLineExists(sourceLineId);
  137 + boolean hasUnqualified = unqualifiedQuantity != null && unqualifiedQuantity.compareTo(BigDecimal.ZERO) > 0;
  138 + boolean hasQualified = qualifiedQuantity != null && qualifiedQuantity.compareTo(BigDecimal.ZERO) > 0;
  139 +
  140 + // 1. 根据合格/不合格品数量,更新退货单行的质量状态
  141 + if (!hasUnqualified) {
  142 + // 1.A 情况一:全部合格
  143 + returnSalesLineMapper.updateById(new MesWmReturnSalesLineDO()
  144 + .setId(sourceLineId).setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus()));
  145 + } else if (!hasQualified) {
  146 + // 1.B 情况二:全部不合格
  147 + returnSalesLineMapper.updateById(new MesWmReturnSalesLineDO()
  148 + .setId(sourceLineId).setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus()));
  149 + } else {
  150 + // 1.C 情况三:部分合格部分不合格 → 拆分行
  151 + // 1.C.1 新增一行不合格品
  152 + MesWmReturnSalesLineDO unqualifiedLine = new MesWmReturnSalesLineDO()
  153 + .setReturnId(sourceLine.getReturnId())
  154 + .setItemId(sourceLine.getItemId())
  155 + .setQuantity(unqualifiedQuantity)
  156 + .setBatchId(sourceLine.getBatchId())
  157 + .setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus())
  158 + .setRemark(sourceLine.getRemark());
  159 + returnSalesLineMapper.insert(unqualifiedLine);
  160 + // 1.C.2 更新原行为合格品
  161 + returnSalesLineMapper.updateById(new MesWmReturnSalesLineDO()
  162 + .setId(sourceLineId)
  163 + .setQuantity(qualifiedQuantity)
  164 + .setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus()));
  165 + }
  166 +
  167 + // 2. 检查退货单下是否还有待检验的行,若全检完则将退货单状态设为"待上架"
  168 + if (sourceDocId != null) {
  169 + List<MesWmReturnSalesLineDO> allLines = returnSalesLineMapper.selectListByReturnId(sourceDocId);
  170 + boolean allInspected = !CollUtil.contains(allLines,
  171 + line -> Objects.equals(line.getQualityStatus(), MesWmQualityStatusEnum.PENDING.getStatus()));
  172 + if (allInspected) {
  173 + returnSalesService.updateReturnSalesStatus(sourceDocId, MesWmReturnSalesStatusEnum.APPROVED.getStatus());
  174 + }
  175 + }
137 176 }
138 177  
139 178 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesService.java
... ... @@ -88,6 +88,14 @@ public interface MesWmReturnSalesService {
88 88 void cancelReturnSales(Long id);
89 89  
90 90 /**
  91 + * 更新销售退货单状态(供外部模块联动调用,如 RQC 检验完成后)
  92 + *
  93 + * @param id 编号
  94 + * @param status 目标状态
  95 + */
  96 + void updateReturnSalesStatus(Long id, Integer status);
  97 +
  98 + /**
91 99 * 校验销售退货单的数量:每行明细数量之和是否等于行退货数量
92 100 *
93 101 * @param id 编号
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesServiceImpl.java
... ... @@ -239,4 +239,10 @@ public class MesWmReturnSalesServiceImpl implements MesWmReturnSalesService {
239 239 return returnSales;
240 240 }
241 241  
  242 + @Override
  243 + public void updateReturnSalesStatus(Long id, Integer status) {
  244 + validateReturnSalesExists(id);
  245 + returnSalesMapper.updateById(new MesWmReturnSalesDO().setId(id).setStatus(status));
  246 + }
  247 +
242 248 }
... ...
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImplTest.java
... ... @@ -16,15 +16,15 @@ import org.junit.jupiter.api.Test;
16 16 import org.springframework.context.annotation.Import;
17 17 import org.springframework.test.context.bean.override.mockito.MockitoBean;
18 18  
  19 +import java.math.BigDecimal;
  20 +
19 21 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
20 22 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
21 23 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
22 24 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.QC_RQC_NOT_EXISTS;
23 25 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.QC_RQC_NOT_PREPARE;
24 26 import static org.junit.jupiter.api.Assertions.assertEquals;
25   -import static org.mockito.ArgumentMatchers.anyInt;
26   -import static org.mockito.ArgumentMatchers.anyLong;
27   -import static org.mockito.ArgumentMatchers.eq;
  27 +import static org.mockito.ArgumentMatchers.*;
28 28 import static org.mockito.Mockito.never;
29 29 import static org.mockito.Mockito.verify;
30 30  
... ... @@ -61,18 +61,22 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest {
61 61 public void testFinishRqc_successWithReturnIssue() {
62 62 // 准备参数
63 63 Long sourceLineId = randomLongId();
  64 + Long sourceDocId = randomLongId();
64 65 Integer checkResult = 1;
  66 + BigDecimal qualifiedQty = new BigDecimal("80");
  67 + BigDecimal unqualifiedQty = new BigDecimal("20");
65 68  
66 69 // mock 数据
67 70 MesQcRqcDO rqc = randomPojo(MesQcRqcDO.class, o -> {
68 71 o.setStatus(MesQcStatusEnum.DRAFT.getStatus());
69 72 o.setSourceDocType(MesBizTypeConstants.WM_RETURN_ISSUE);
  73 + o.setSourceDocId(sourceDocId);
70 74 o.setSourceLineId(sourceLineId);
71 75 o.setCheckResult(checkResult);
72 76 // 设置数量通过 finishRqc 的数量校验:合格 + 不合格 = 检测数量
73   - o.setCheckQuantity(new java.math.BigDecimal("100"));
74   - o.setQualifiedQuantity(new java.math.BigDecimal("80"));
75   - o.setUnqualifiedQuantity(new java.math.BigDecimal("20"));
  77 + o.setCheckQuantity(new BigDecimal("100"));
  78 + o.setQualifiedQuantity(qualifiedQty);
  79 + o.setUnqualifiedQuantity(unqualifiedQty);
76 80 });
77 81 rqcMapper.insert(rqc);
78 82  
... ... @@ -83,28 +87,36 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest {
83 87 MesQcRqcDO updatedRqc = rqcMapper.selectById(rqc.getId());
84 88 assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedRqc.getStatus());
85 89  
86   - // 断言:验证回写调用生产退料
87   - verify(returnIssueLineService).updateReturnIssueLineWhenRqcFinish(eq(sourceLineId), eq(checkResult));
  90 + // 断言:验证回写调用生产退料(含拆分参数)
  91 + verify(returnIssueLineService).updateReturnIssueLineWhenRqcFinish(
  92 + eq(sourceLineId), eq(sourceDocId), eq(checkResult),
  93 + argThat(v -> v.compareTo(qualifiedQty) == 0),
  94 + argThat(v -> v.compareTo(unqualifiedQty) == 0));
88 95 // 断言:未调用销售退货
89   - verify(returnSalesLineService, never()).updateReturnSalesLineWhenRqcFinish(anyLong(), anyInt());
  96 + verify(returnSalesLineService, never()).updateReturnSalesLineWhenRqcFinish(
  97 + anyLong(), anyLong(), anyInt(), any(BigDecimal.class), any(BigDecimal.class));
90 98 }
91 99  
92 100 @Test
93 101 public void testFinishRqc_successWithReturnSales() {
94 102 // 准备参数
95 103 Long sourceLineId = randomLongId();
  104 + Long sourceDocId = randomLongId();
96 105 Integer checkResult = 1;
  106 + BigDecimal qualifiedQty = new BigDecimal("90");
  107 + BigDecimal unqualifiedQty = new BigDecimal("10");
97 108  
98 109 // mock 数据
99 110 MesQcRqcDO rqc = randomPojo(MesQcRqcDO.class, o -> {
100 111 o.setStatus(MesQcStatusEnum.DRAFT.getStatus());
101 112 o.setSourceDocType(MesBizTypeConstants.WM_RETURN_SALES);
  113 + o.setSourceDocId(sourceDocId);
102 114 o.setSourceLineId(sourceLineId);
103 115 o.setCheckResult(checkResult);
104 116 // 设置数量通过 finishRqc 的数量校验:合格 + 不合格 = 检测数量
105   - o.setCheckQuantity(new java.math.BigDecimal("100"));
106   - o.setQualifiedQuantity(new java.math.BigDecimal("90"));
107   - o.setUnqualifiedQuantity(new java.math.BigDecimal("10"));
  117 + o.setCheckQuantity(new BigDecimal("100"));
  118 + o.setQualifiedQuantity(qualifiedQty);
  119 + o.setUnqualifiedQuantity(unqualifiedQty);
108 120 });
109 121 rqcMapper.insert(rqc);
110 122  
... ... @@ -115,10 +127,14 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest {
115 127 MesQcRqcDO updatedRqc = rqcMapper.selectById(rqc.getId());
116 128 assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedRqc.getStatus());
117 129  
118   - // 断言:验证回写调用销售退货
119   - verify(returnSalesLineService).updateReturnSalesLineWhenRqcFinish(eq(sourceLineId), eq(checkResult));
  130 + // 断言:验证回写调用销售退货(含拆分参数)
  131 + verify(returnSalesLineService).updateReturnSalesLineWhenRqcFinish(
  132 + eq(sourceLineId), eq(sourceDocId), eq(checkResult),
  133 + argThat(v -> v.compareTo(qualifiedQty) == 0),
  134 + argThat(v -> v.compareTo(unqualifiedQty) == 0));
120 135 // 断言:未调用生产退料
121   - verify(returnIssueLineService, never()).updateReturnIssueLineWhenRqcFinish(anyLong(), anyInt());
  136 + verify(returnIssueLineService, never()).updateReturnIssueLineWhenRqcFinish(
  137 + anyLong(), anyLong(), anyInt(), any(BigDecimal.class), any(BigDecimal.class));
122 138 }
123 139  
124 140 @Test
... ...
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImplTest.java
... ... @@ -12,8 +12,11 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean;
12 12  
13 13 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
14 14  
  15 +import java.math.BigDecimal;
  16 +
15 17 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
16 18 import static org.junit.jupiter.api.Assertions.assertEquals;
  19 +import static org.junit.jupiter.api.Assertions.assertNotNull;
17 20  
18 21 /**
19 22 * {@link MesWmReturnIssueLineServiceImpl} 的单元测试类
... ... @@ -36,37 +39,75 @@ public class MesWmReturnIssueLineServiceImplTest extends BaseDbUnitTest {
36 39 private MesMdItemService itemService;
37 40  
38 41 @Test
39   - public void testUpdateReturnIssueLineWhenRqcFinish_pass() {
40   - // 准备参数
  42 + public void testUpdateReturnIssueLineWhenRqcFinish_allPass() {
  43 + // 准备参数:全部合格
41 44 Integer checkResult = MesQcCheckResultEnum.PASS.getType();
  45 + BigDecimal qualifiedQty = new BigDecimal("100");
  46 + BigDecimal unqualifiedQty = BigDecimal.ZERO;
42 47  
43 48 // mock 数据
44   - MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class);
  49 + MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class, o -> o.setQuantity(qualifiedQty));
45 50 returnIssueLineMapper.insert(lineDO);
46 51  
47   - // 调用
48   - returnIssueLineService.updateReturnIssueLineWhenRqcFinish(lineDO.getId(), checkResult);
  52 + // 调用(sourceDocId 传 null 不联动主表状态)
  53 + returnIssueLineService.updateReturnIssueLineWhenRqcFinish(
  54 + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty);
49 55  
50   - // 断言:验证 mapper 更新参数正确
  56 + // 断言:验证质量状态更新为合格
51 57 MesWmReturnIssueLineDO updateLine = returnIssueLineMapper.selectById(lineDO.getId());
52 58 assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), updateLine.getQualityStatus());
53 59 }
54 60  
55 61 @Test
56   - public void testUpdateReturnIssueLineWhenRqcFinish_fail() {
57   - // 准备参数
  62 + public void testUpdateReturnIssueLineWhenRqcFinish_allFail() {
  63 + // 准备参数:全部不合格
58 64 Integer checkResult = MesQcCheckResultEnum.FAIL.getType();
  65 + BigDecimal qualifiedQty = BigDecimal.ZERO;
  66 + BigDecimal unqualifiedQty = new BigDecimal("100");
59 67  
60 68 // mock 数据
61   - MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class);
  69 + MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class, o -> o.setQuantity(unqualifiedQty));
62 70 returnIssueLineMapper.insert(lineDO);
63 71  
64 72 // 调用
65   - returnIssueLineService.updateReturnIssueLineWhenRqcFinish(lineDO.getId(), checkResult);
  73 + returnIssueLineService.updateReturnIssueLineWhenRqcFinish(
  74 + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty);
66 75  
67   - // 断言:验证 mapper 更新参数正确
  76 + // 断言:验证质量状态更新为不合格
68 77 MesWmReturnIssueLineDO updateLine = returnIssueLineMapper.selectById(lineDO.getId());
69 78 assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), updateLine.getQualityStatus());
70 79 }
71 80  
  81 + @Test
  82 + public void testUpdateReturnIssueLineWhenRqcFinish_partialSplit() {
  83 + // 准备参数:部分合格部分不合格,触发行拆分
  84 + Integer checkResult = MesQcCheckResultEnum.PASS.getType();
  85 + BigDecimal qualifiedQty = new BigDecimal("80");
  86 + BigDecimal unqualifiedQty = new BigDecimal("20");
  87 +
  88 + // mock 数据
  89 + MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class, o ->
  90 + o.setQuantity(new BigDecimal("100")));
  91 + returnIssueLineMapper.insert(lineDO);
  92 +
  93 + // 调用
  94 + returnIssueLineService.updateReturnIssueLineWhenRqcFinish(
  95 + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty);
  96 +
  97 + // 断言:原行变为合格,数量调整为合格数量
  98 + MesWmReturnIssueLineDO originalLine = returnIssueLineMapper.selectById(lineDO.getId());
  99 + assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), originalLine.getQualityStatus());
  100 + assertEquals(0, qualifiedQty.compareTo(originalLine.getQuantity()));
  101 +
  102 + // 断言:新增了一行不合格品
  103 + var allLines = returnIssueLineMapper.selectListByIssueId(lineDO.getIssueId());
  104 + assertEquals(2, allLines.size());
  105 + MesWmReturnIssueLineDO newLine = allLines.stream()
  106 + .filter(l -> !l.getId().equals(lineDO.getId()))
  107 + .findFirst().orElse(null);
  108 + assertNotNull(newLine);
  109 + assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), newLine.getQualityStatus());
  110 + assertEquals(0, unqualifiedQty.compareTo(newLine.getQuantity()));
  111 + }
  112 +
72 113 }
... ...
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImplTest.java
... ... @@ -12,8 +12,11 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean;
12 12  
13 13 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
14 14  
  15 +import java.math.BigDecimal;
  16 +
15 17 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
16 18 import static org.junit.jupiter.api.Assertions.assertEquals;
  19 +import static org.junit.jupiter.api.Assertions.assertNotNull;
17 20  
18 21 /**
19 22 * {@link MesWmReturnSalesLineServiceImpl} 的单元测试类
... ... @@ -36,37 +39,75 @@ public class MesWmReturnSalesLineServiceImplTest extends BaseDbUnitTest {
36 39 private MesMdItemService itemService;
37 40  
38 41 @Test
39   - public void testUpdateReturnSalesLineWhenRqcFinish_pass() {
40   - // 准备参数
  42 + public void testUpdateReturnSalesLineWhenRqcFinish_allPass() {
  43 + // 准备参数:全部合格
41 44 Integer checkResult = MesQcCheckResultEnum.PASS.getType();
  45 + BigDecimal qualifiedQty = new BigDecimal("100");
  46 + BigDecimal unqualifiedQty = BigDecimal.ZERO;
42 47  
43 48 // mock 数据
44   - MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class);
  49 + MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class, o -> o.setQuantity(qualifiedQty));
45 50 returnSalesLineMapper.insert(lineDO);
46 51  
47   - // 调用
48   - returnSalesLineService.updateReturnSalesLineWhenRqcFinish(lineDO.getId(), checkResult);
  52 + // 调用(sourceDocId 传 null 不联动主表状态)
  53 + returnSalesLineService.updateReturnSalesLineWhenRqcFinish(
  54 + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty);
49 55  
50   - // 断言:验证 mapper 更新参数正确
  56 + // 断言:验证质量状态更新为合格
51 57 MesWmReturnSalesLineDO updateLine = returnSalesLineMapper.selectById(lineDO.getId());
52 58 assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), updateLine.getQualityStatus());
53 59 }
54 60  
55 61 @Test
56   - public void testUpdateReturnSalesLineWhenRqcFinish_fail() {
57   - // 准备参数
  62 + public void testUpdateReturnSalesLineWhenRqcFinish_allFail() {
  63 + // 准备参数:全部不合格
58 64 Integer checkResult = MesQcCheckResultEnum.FAIL.getType();
  65 + BigDecimal qualifiedQty = BigDecimal.ZERO;
  66 + BigDecimal unqualifiedQty = new BigDecimal("100");
59 67  
60 68 // mock 数据
61   - MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class);
  69 + MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class, o -> o.setQuantity(unqualifiedQty));
62 70 returnSalesLineMapper.insert(lineDO);
63 71  
64 72 // 调用
65   - returnSalesLineService.updateReturnSalesLineWhenRqcFinish(lineDO.getId(), checkResult);
  73 + returnSalesLineService.updateReturnSalesLineWhenRqcFinish(
  74 + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty);
66 75  
67   - // 断言:验证 mapper 更新参数正确
  76 + // 断言:验证质量状态更新为不合格
68 77 MesWmReturnSalesLineDO updateLine = returnSalesLineMapper.selectById(lineDO.getId());
69 78 assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), updateLine.getQualityStatus());
70 79 }
71 80  
  81 + @Test
  82 + public void testUpdateReturnSalesLineWhenRqcFinish_partialSplit() {
  83 + // 准备参数:部分合格部分不合格,触发行拆分
  84 + Integer checkResult = MesQcCheckResultEnum.PASS.getType();
  85 + BigDecimal qualifiedQty = new BigDecimal("70");
  86 + BigDecimal unqualifiedQty = new BigDecimal("30");
  87 +
  88 + // mock 数据
  89 + MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class, o ->
  90 + o.setQuantity(new BigDecimal("100")));
  91 + returnSalesLineMapper.insert(lineDO);
  92 +
  93 + // 调用
  94 + returnSalesLineService.updateReturnSalesLineWhenRqcFinish(
  95 + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty);
  96 +
  97 + // 断言:原行变为合格,数量调整为合格数量
  98 + MesWmReturnSalesLineDO originalLine = returnSalesLineMapper.selectById(lineDO.getId());
  99 + assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), originalLine.getQualityStatus());
  100 + assertEquals(0, qualifiedQty.compareTo(originalLine.getQuantity()));
  101 +
  102 + // 断言:新增了一行不合格品
  103 + var allLines = returnSalesLineMapper.selectListByReturnId(lineDO.getReturnId());
  104 + assertEquals(2, allLines.size());
  105 + MesWmReturnSalesLineDO newLine = allLines.stream()
  106 + .filter(l -> !l.getId().equals(lineDO.getId()))
  107 + .findFirst().orElse(null);
  108 + assertNotNull(newLine);
  109 + assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), newLine.getQualityStatus());
  110 + assertEquals(0, unqualifiedQty.compareTo(newLine.getQuantity()));
  111 + }
  112 +
72 113 }
... ...