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,11 +9,9 @@ import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.line.MesQcRqcLineR
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.rqc.MesQcRqcLineDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcLineDO;
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.rqc.MesQcRqcLineService; 13 import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcLineService;
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 MesQcRqcLineController { @@ -46,8 +44,6 @@ public class MesQcRqcLineController {
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 = "获得退货检验单行")
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,8 +20,8 @@ public class MesQcRqcPageReqVO extends PageParam {
20 @Schema(description = "检验单编号", example = "RQC2025") 20 @Schema(description = "检验单编号", example = "RQC2025")
21 private String code; 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 @Schema(description = "产品物料 ID", example = "20") 26 @Schema(description = "产品物料 ID", example = "20")
27 private Long itemId; 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,7 +17,8 @@ public enum MesMdAutoCodeRuleCodeEnum {
17 BATCH_CODE("WM_BATCH_CODE", "批次编码"), 17 BATCH_CODE("WM_BATCH_CODE", "批次编码"),
18 TASK_CODE("PRO_TASK_CODE", "生产任务编码"), 18 TASK_CODE("PRO_TASK_CODE", "生产任务编码"),
19 QC_IQC_CODE("QC_IQC_CODE", "来料检验单编码"), 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,8 +134,8 @@ public class MesQcRqcServiceImpl implements MesQcRqcService {
134 * 134 *
135 * <p>根据 sourceDocType 分发: 135 * <p>根据 sourceDocType 分发:
136 * <ul> 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 * </ul> 139 * </ul>
140 * 140 *
141 * @param rqc 退货检验单 141 * @param rqc 退货检验单
@@ -150,11 +150,15 @@ public class MesQcRqcServiceImpl implements MesQcRqcService { @@ -150,11 +150,15 @@ public class MesQcRqcServiceImpl implements MesQcRqcService {
150 } 150 }
151 151
152 if (Objects.equals(rqc.getSourceDocType(), MesBizTypeConstants.WM_RETURN_ISSUE)) { 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 } else if (Objects.equals(rqc.getSourceDocType(), MesBizTypeConstants.WM_RETURN_SALES)) { 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 } else { 162 } else {
159 throw new IllegalArgumentException( 163 throw new IllegalArgumentException(
160 "RQC 单[" + rqc.getId() + "] sourceDocType=" + rqc.getSourceDocType() + " 无法识别,无法回写来源单据"); 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 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.line.MesWm
6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO; 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO;
7 import jakarta.validation.Valid; 7 import jakarta.validation.Valid;
8 8
  9 +import java.math.BigDecimal;
9 import java.util.List; 10 import java.util.List;
10 11
11 /** 12 /**
@@ -83,11 +84,23 @@ public interface MesWmReturnIssueLineService { @@ -83,11 +84,23 @@ public interface MesWmReturnIssueLineService {
83 void updateReturnIssueQualityStatusByIssueId(Long issueId, Integer issueType); 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 package cn.iocoder.yudao.module.mes.service.wm.returnissue; 1 package cn.iocoder.yudao.module.mes.service.wm.returnissue;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 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,8 +9,8 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.line.MesWm
8 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueDO;
9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO;
10 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnissue.MesWmReturnIssueLineMapper; 11 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnissue.MesWmReturnIssueLineMapper;
11 -import cn.iocoder.yudao.module.mes.enums.qc.MesQcCheckResultEnum;  
12 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; 12 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
  13 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnIssueStatusEnum;
13 import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnIssueTypeEnum; 14 import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnIssueTypeEnum;
14 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; 15 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
15 import jakarta.annotation.Resource; 16 import jakarta.annotation.Resource;
@@ -17,6 +18,7 @@ import org.springframework.context.annotation.Lazy; @@ -17,6 +18,7 @@ import org.springframework.context.annotation.Lazy;
17 import org.springframework.stereotype.Service; 18 import org.springframework.stereotype.Service;
18 import org.springframework.validation.annotation.Validated; 19 import org.springframework.validation.annotation.Validated;
19 20
  21 +import java.math.BigDecimal;
20 import java.util.List; 22 import java.util.List;
21 import java.util.Objects; 23 import java.util.Objects;
22 24
@@ -137,10 +139,51 @@ public class MesWmReturnIssueLineServiceImpl implements MesWmReturnIssueLineServ @@ -137,10 +139,51 @@ public class MesWmReturnIssueLineServiceImpl implements MesWmReturnIssueLineServ
137 } 139 }
138 140
139 @Override 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,4 +85,12 @@ public interface MesWmReturnIssueService {
85 */ 85 */
86 void cancelReturnIssue(Long id); 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,4 +271,10 @@ public class MesWmReturnIssueServiceImpl implements MesWmReturnIssueService {
271 return issue; 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 +6,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWm
6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO; 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO;
7 import jakarta.validation.Valid; 7 import jakarta.validation.Valid;
8 8
  9 +import java.math.BigDecimal;
9 import java.util.List; 10 import java.util.List;
10 11
11 /** 12 /**
@@ -85,11 +86,23 @@ public interface MesWmReturnSalesLineService { @@ -85,11 +86,23 @@ public interface MesWmReturnSalesLineService {
85 void updateQualityStatusByReturnId(Long returnId, Integer qualityStatus); 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 package cn.iocoder.yudao.module.mes.service.wm.returnsales; 1 package cn.iocoder.yudao.module.mes.service.wm.returnsales;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWmReturnSalesLinePageReqVO; 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,14 +8,15 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWm
7 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; 8 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
8 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO;
9 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales.MesWmReturnSalesLineMapper; 10 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales.MesWmReturnSalesLineMapper;
10 -import cn.iocoder.yudao.module.mes.enums.qc.MesQcCheckResultEnum;  
11 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; 11 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
  12 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnSalesStatusEnum;
12 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; 13 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
13 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
14 import org.springframework.context.annotation.Lazy; 15 import org.springframework.context.annotation.Lazy;
15 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
16 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
17 18
  19 +import java.math.BigDecimal;
18 import java.util.List; 20 import java.util.List;
19 import java.util.Objects; 21 import java.util.Objects;
20 22
@@ -129,11 +131,48 @@ public class MesWmReturnSalesLineServiceImpl implements MesWmReturnSalesLineServ @@ -129,11 +131,48 @@ public class MesWmReturnSalesLineServiceImpl implements MesWmReturnSalesLineServ
129 } 131 }
130 132
131 @Override 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,6 +88,14 @@ public interface MesWmReturnSalesService {
88 void cancelReturnSales(Long id); 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 * @param id 编号 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,4 +239,10 @@ public class MesWmReturnSalesServiceImpl implements MesWmReturnSalesService {
239 return returnSales; 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,15 +16,15 @@ import org.junit.jupiter.api.Test;
16 import org.springframework.context.annotation.Import; 16 import org.springframework.context.annotation.Import;
17 import org.springframework.test.context.bean.override.mockito.MockitoBean; 17 import org.springframework.test.context.bean.override.mockito.MockitoBean;
18 18
  19 +import java.math.BigDecimal;
  20 +
19 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; 21 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
20 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; 22 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
21 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 23 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
22 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.QC_RQC_NOT_EXISTS; 24 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.QC_RQC_NOT_EXISTS;
23 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.QC_RQC_NOT_PREPARE; 25 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.QC_RQC_NOT_PREPARE;
24 import static org.junit.jupiter.api.Assertions.assertEquals; 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 import static org.mockito.Mockito.never; 28 import static org.mockito.Mockito.never;
29 import static org.mockito.Mockito.verify; 29 import static org.mockito.Mockito.verify;
30 30
@@ -61,18 +61,22 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest { @@ -61,18 +61,22 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest {
61 public void testFinishRqc_successWithReturnIssue() { 61 public void testFinishRqc_successWithReturnIssue() {
62 // 准备参数 62 // 准备参数
63 Long sourceLineId = randomLongId(); 63 Long sourceLineId = randomLongId();
  64 + Long sourceDocId = randomLongId();
64 Integer checkResult = 1; 65 Integer checkResult = 1;
  66 + BigDecimal qualifiedQty = new BigDecimal("80");
  67 + BigDecimal unqualifiedQty = new BigDecimal("20");
65 68
66 // mock 数据 69 // mock 数据
67 MesQcRqcDO rqc = randomPojo(MesQcRqcDO.class, o -> { 70 MesQcRqcDO rqc = randomPojo(MesQcRqcDO.class, o -> {
68 o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); 71 o.setStatus(MesQcStatusEnum.DRAFT.getStatus());
69 o.setSourceDocType(MesBizTypeConstants.WM_RETURN_ISSUE); 72 o.setSourceDocType(MesBizTypeConstants.WM_RETURN_ISSUE);
  73 + o.setSourceDocId(sourceDocId);
70 o.setSourceLineId(sourceLineId); 74 o.setSourceLineId(sourceLineId);
71 o.setCheckResult(checkResult); 75 o.setCheckResult(checkResult);
72 // 设置数量通过 finishRqc 的数量校验:合格 + 不合格 = 检测数量 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 rqcMapper.insert(rqc); 81 rqcMapper.insert(rqc);
78 82
@@ -83,28 +87,36 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest { @@ -83,28 +87,36 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest {
83 MesQcRqcDO updatedRqc = rqcMapper.selectById(rqc.getId()); 87 MesQcRqcDO updatedRqc = rqcMapper.selectById(rqc.getId());
84 assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedRqc.getStatus()); 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 @Test 100 @Test
93 public void testFinishRqc_successWithReturnSales() { 101 public void testFinishRqc_successWithReturnSales() {
94 // 准备参数 102 // 准备参数
95 Long sourceLineId = randomLongId(); 103 Long sourceLineId = randomLongId();
  104 + Long sourceDocId = randomLongId();
96 Integer checkResult = 1; 105 Integer checkResult = 1;
  106 + BigDecimal qualifiedQty = new BigDecimal("90");
  107 + BigDecimal unqualifiedQty = new BigDecimal("10");
97 108
98 // mock 数据 109 // mock 数据
99 MesQcRqcDO rqc = randomPojo(MesQcRqcDO.class, o -> { 110 MesQcRqcDO rqc = randomPojo(MesQcRqcDO.class, o -> {
100 o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); 111 o.setStatus(MesQcStatusEnum.DRAFT.getStatus());
101 o.setSourceDocType(MesBizTypeConstants.WM_RETURN_SALES); 112 o.setSourceDocType(MesBizTypeConstants.WM_RETURN_SALES);
  113 + o.setSourceDocId(sourceDocId);
102 o.setSourceLineId(sourceLineId); 114 o.setSourceLineId(sourceLineId);
103 o.setCheckResult(checkResult); 115 o.setCheckResult(checkResult);
104 // 设置数量通过 finishRqc 的数量校验:合格 + 不合格 = 检测数量 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 rqcMapper.insert(rqc); 121 rqcMapper.insert(rqc);
110 122
@@ -115,10 +127,14 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest { @@ -115,10 +127,14 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest {
115 MesQcRqcDO updatedRqc = rqcMapper.selectById(rqc.getId()); 127 MesQcRqcDO updatedRqc = rqcMapper.selectById(rqc.getId());
116 assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedRqc.getStatus()); 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 @Test 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,8 +12,11 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean;
12 12
13 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; 13 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
14 14
  15 +import java.math.BigDecimal;
  16 +
15 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 17 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
16 import static org.junit.jupiter.api.Assertions.assertEquals; 18 import static org.junit.jupiter.api.Assertions.assertEquals;
  19 +import static org.junit.jupiter.api.Assertions.assertNotNull;
17 20
18 /** 21 /**
19 * {@link MesWmReturnIssueLineServiceImpl} 的单元测试类 22 * {@link MesWmReturnIssueLineServiceImpl} 的单元测试类
@@ -36,37 +39,75 @@ public class MesWmReturnIssueLineServiceImplTest extends BaseDbUnitTest { @@ -36,37 +39,75 @@ public class MesWmReturnIssueLineServiceImplTest extends BaseDbUnitTest {
36 private MesMdItemService itemService; 39 private MesMdItemService itemService;
37 40
38 @Test 41 @Test
39 - public void testUpdateReturnIssueLineWhenRqcFinish_pass() {  
40 - // 准备参数 42 + public void testUpdateReturnIssueLineWhenRqcFinish_allPass() {
  43 + // 准备参数:全部合格
41 Integer checkResult = MesQcCheckResultEnum.PASS.getType(); 44 Integer checkResult = MesQcCheckResultEnum.PASS.getType();
  45 + BigDecimal qualifiedQty = new BigDecimal("100");
  46 + BigDecimal unqualifiedQty = BigDecimal.ZERO;
42 47
43 // mock 数据 48 // mock 数据
44 - MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class); 49 + MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class, o -> o.setQuantity(qualifiedQty));
45 returnIssueLineMapper.insert(lineDO); 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 MesWmReturnIssueLineDO updateLine = returnIssueLineMapper.selectById(lineDO.getId()); 57 MesWmReturnIssueLineDO updateLine = returnIssueLineMapper.selectById(lineDO.getId());
52 assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), updateLine.getQualityStatus()); 58 assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), updateLine.getQualityStatus());
53 } 59 }
54 60
55 @Test 61 @Test
56 - public void testUpdateReturnIssueLineWhenRqcFinish_fail() {  
57 - // 准备参数 62 + public void testUpdateReturnIssueLineWhenRqcFinish_allFail() {
  63 + // 准备参数:全部不合格
58 Integer checkResult = MesQcCheckResultEnum.FAIL.getType(); 64 Integer checkResult = MesQcCheckResultEnum.FAIL.getType();
  65 + BigDecimal qualifiedQty = BigDecimal.ZERO;
  66 + BigDecimal unqualifiedQty = new BigDecimal("100");
59 67
60 // mock 数据 68 // mock 数据
61 - MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class); 69 + MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class, o -> o.setQuantity(unqualifiedQty));
62 returnIssueLineMapper.insert(lineDO); 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 MesWmReturnIssueLineDO updateLine = returnIssueLineMapper.selectById(lineDO.getId()); 77 MesWmReturnIssueLineDO updateLine = returnIssueLineMapper.selectById(lineDO.getId());
69 assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), updateLine.getQualityStatus()); 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,8 +12,11 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean;
12 12
13 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; 13 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
14 14
  15 +import java.math.BigDecimal;
  16 +
15 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 17 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
16 import static org.junit.jupiter.api.Assertions.assertEquals; 18 import static org.junit.jupiter.api.Assertions.assertEquals;
  19 +import static org.junit.jupiter.api.Assertions.assertNotNull;
17 20
18 /** 21 /**
19 * {@link MesWmReturnSalesLineServiceImpl} 的单元测试类 22 * {@link MesWmReturnSalesLineServiceImpl} 的单元测试类
@@ -36,37 +39,75 @@ public class MesWmReturnSalesLineServiceImplTest extends BaseDbUnitTest { @@ -36,37 +39,75 @@ public class MesWmReturnSalesLineServiceImplTest extends BaseDbUnitTest {
36 private MesMdItemService itemService; 39 private MesMdItemService itemService;
37 40
38 @Test 41 @Test
39 - public void testUpdateReturnSalesLineWhenRqcFinish_pass() {  
40 - // 准备参数 42 + public void testUpdateReturnSalesLineWhenRqcFinish_allPass() {
  43 + // 准备参数:全部合格
41 Integer checkResult = MesQcCheckResultEnum.PASS.getType(); 44 Integer checkResult = MesQcCheckResultEnum.PASS.getType();
  45 + BigDecimal qualifiedQty = new BigDecimal("100");
  46 + BigDecimal unqualifiedQty = BigDecimal.ZERO;
42 47
43 // mock 数据 48 // mock 数据
44 - MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class); 49 + MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class, o -> o.setQuantity(qualifiedQty));
45 returnSalesLineMapper.insert(lineDO); 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 MesWmReturnSalesLineDO updateLine = returnSalesLineMapper.selectById(lineDO.getId()); 57 MesWmReturnSalesLineDO updateLine = returnSalesLineMapper.selectById(lineDO.getId());
52 assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), updateLine.getQualityStatus()); 58 assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), updateLine.getQualityStatus());
53 } 59 }
54 60
55 @Test 61 @Test
56 - public void testUpdateReturnSalesLineWhenRqcFinish_fail() {  
57 - // 准备参数 62 + public void testUpdateReturnSalesLineWhenRqcFinish_allFail() {
  63 + // 准备参数:全部不合格
58 Integer checkResult = MesQcCheckResultEnum.FAIL.getType(); 64 Integer checkResult = MesQcCheckResultEnum.FAIL.getType();
  65 + BigDecimal qualifiedQty = BigDecimal.ZERO;
  66 + BigDecimal unqualifiedQty = new BigDecimal("100");
59 67
60 // mock 数据 68 // mock 数据
61 - MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class); 69 + MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class, o -> o.setQuantity(unqualifiedQty));
62 returnSalesLineMapper.insert(lineDO); 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 MesWmReturnSalesLineDO updateLine = returnSalesLineMapper.selectById(lineDO.getId()); 77 MesWmReturnSalesLineDO updateLine = returnSalesLineMapper.selectById(lineDO.getId());
69 assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), updateLine.getQualityStatus()); 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 }