From d2feaae7d48c3526ba74cab619f95d9c77a3ce5b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 25 Mar 2026 23:37:30 +0800 Subject: [PATCH] ✨ feat(mes): 更新退货检验单逻辑,支持合格/不合格数量拆分 --- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/MesQcRqcLineController.java | 4 ---- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcPageReqVO.java | 4 ++-- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java | 3 ++- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImpl.java | 16 ++++++++++------ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineService.java | 21 +++++++++++++++++---- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueService.java | 8 ++++++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java | 6 ++++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineService.java | 21 +++++++++++++++++---- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java | 51 +++++++++++++++++++++++++++++++++++++++++++++------ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesService.java | 8 ++++++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesServiceImpl.java | 6 ++++++ yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImplTest.java | 46 +++++++++++++++++++++++++++++++--------------- yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImplTest.java | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------- yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImplTest.java | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 15 files changed, 304 insertions(+), 69 deletions(-) diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/MesQcRqcLineController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/MesQcRqcLineController.java index ca965d1..2139ba5 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/MesQcRqcLineController.java +++ b/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 import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcLineDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO; -import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolDO; import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService; import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcLineService; import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService; -import cn.iocoder.yudao.module.mes.service.tm.tool.MesTmToolService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -46,8 +44,6 @@ public class MesQcRqcLineController { private MesQcIndicatorService indicatorService; @Resource private MesMdUnitMeasureService unitMeasureService; - @Resource - private MesTmToolService toolService; @GetMapping("/get") @Operation(summary = "获得退货检验单行") diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcPageReqVO.java index c8368e2..5966a24 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcPageReqVO.java +++ b/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 { @Schema(description = "检验单编号", example = "RQC2025") private String code; - @Schema(description = "来源单据类型", example = "RTISSUE") - private String sourceDocType; + @Schema(description = "来源单据类型", example = "1") + private Integer sourceDocType; @Schema(description = "产品物料 ID", example = "20") private Long itemId; diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java index 49aa520..7dd1047 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java @@ -17,7 +17,8 @@ public enum MesMdAutoCodeRuleCodeEnum { BATCH_CODE("WM_BATCH_CODE", "批次编码"), TASK_CODE("PRO_TASK_CODE", "生产任务编码"), QC_IQC_CODE("QC_IQC_CODE", "来料检验单编码"), - QC_IPQC_CODE("QC_IPQC_CODE", "过程检验单编码"); + QC_IPQC_CODE("QC_IPQC_CODE", "过程检验单编码"), + QC_RQC_CODE("QC_RQC_CODE", "退货检验单编码"); /** * 规则代码 diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImpl.java index cfed9b2..cd6e513 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImpl.java +++ b/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 { * *

根据 sourceDocType 分发: *

* * @param rqc 退货检验单 @@ -150,11 +150,15 @@ public class MesQcRqcServiceImpl implements MesQcRqcService { } if (Objects.equals(rqc.getSourceDocType(), MesBizTypeConstants.WM_RETURN_ISSUE)) { - // 回写生产退料单行的 qualityStatus - returnIssueLineService.updateReturnIssueLineWhenRqcFinish(rqc.getSourceLineId(), rqc.getCheckResult()); + // 回写生产退料单行:拆分行 + 更新质量状态 + 联动主单状态 + returnIssueLineService.updateReturnIssueLineWhenRqcFinish( + rqc.getSourceLineId(), rqc.getSourceDocId(), rqc.getCheckResult(), + rqc.getQualifiedQuantity(), rqc.getUnqualifiedQuantity()); } else if (Objects.equals(rqc.getSourceDocType(), MesBizTypeConstants.WM_RETURN_SALES)) { - // 回写销售退货单行的 qualityStatus - returnSalesLineService.updateReturnSalesLineWhenRqcFinish(rqc.getSourceLineId(), rqc.getCheckResult()); + // 回写销售退货单行:拆分行 + 更新质量状态 + 联动主单状态 + returnSalesLineService.updateReturnSalesLineWhenRqcFinish( + rqc.getSourceLineId(), rqc.getSourceDocId(), rqc.getCheckResult(), + rqc.getQualifiedQuantity(), rqc.getUnqualifiedQuantity()); } else { throw new IllegalArgumentException( "RQC 单[" + rqc.getId() + "] sourceDocType=" + rqc.getSourceDocType() + " 无法识别,无法回写来源单据"); diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineService.java index fb70546..6a8786f 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineService.java +++ b/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 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO; import jakarta.validation.Valid; +import java.math.BigDecimal; import java.util.List; /** @@ -83,11 +84,23 @@ public interface MesWmReturnIssueLineService { void updateReturnIssueQualityStatusByIssueId(Long issueId, Integer issueType); /** - * RQC 检验完成后,更新生产退料单行的质量状态 + * RQC 检验完成后,根据合格品/不合格品数量更新生产退料单行 * - * @param id 行 ID - * @param checkResult 检验结果 + *

拆分逻辑: + *

+ * 同时检查退料单下所有行是否都已检验完毕,若全检完则将退料单状态设为待上架。 + * + * @param sourceLineId 来源行 ID + * @param sourceDocId 来源退料单 ID + * @param checkResult 检验结果 + * @param qualifiedQuantity 合格品数量 + * @param unqualifiedQuantity 不合格品数量 */ - void updateReturnIssueLineWhenRqcFinish(Long id, Integer checkResult); + void updateReturnIssueLineWhenRqcFinish(Long sourceLineId, Long sourceDocId, Integer checkResult, + BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity); } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java index 335c93b..f4dff35 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.mes.service.wm.returnissue; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; 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 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO; import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnissue.MesWmReturnIssueLineMapper; -import cn.iocoder.yudao.module.mes.enums.qc.MesQcCheckResultEnum; import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; +import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnIssueStatusEnum; import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnIssueTypeEnum; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import jakarta.annotation.Resource; @@ -17,6 +18,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.math.BigDecimal; import java.util.List; import java.util.Objects; @@ -137,10 +139,51 @@ public class MesWmReturnIssueLineServiceImpl implements MesWmReturnIssueLineServ } @Override - public void updateReturnIssueLineWhenRqcFinish(Long id, Integer checkResult) { - Integer qualityStatus = Objects.equals(checkResult, MesQcCheckResultEnum.PASS.getType()) - ? MesWmQualityStatusEnum.PASS.getStatus() : MesWmQualityStatusEnum.FAIL.getStatus(); - issueLineMapper.updateById(new MesWmReturnIssueLineDO().setId(id).setQualityStatus(qualityStatus)); + public void updateReturnIssueLineWhenRqcFinish(Long sourceLineId, Long sourceDocId, Integer checkResult, + BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity) { + MesWmReturnIssueLineDO sourceLine = validateReturnIssueLineExists(sourceLineId); + boolean hasUnqualified = unqualifiedQuantity != null && unqualifiedQuantity.compareTo(BigDecimal.ZERO) > 0; + boolean hasQualified = qualifiedQuantity != null && qualifiedQuantity.compareTo(BigDecimal.ZERO) > 0; + + // 1. 根据合格/不合格品数量,更新退料单行的质量状态 + if (!hasUnqualified) { + // 1.A 情况一:全部合格 + issueLineMapper.updateById(new MesWmReturnIssueLineDO() + .setId(sourceLineId).setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus())); + } else if (!hasQualified) { + // 1.B 情况二:全部不合格 + issueLineMapper.updateById(new MesWmReturnIssueLineDO() + .setId(sourceLineId).setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus())); + } else { + // 1.C 情况三:部分合格部分不合格 → 拆分行 + // 1.C.1 新增一行不合格品 + MesWmReturnIssueLineDO unqualifiedLine = new MesWmReturnIssueLineDO() + .setIssueId(sourceLine.getIssueId()) + .setMaterialStockId(sourceLine.getMaterialStockId()) + .setItemId(sourceLine.getItemId()) + .setQuantity(unqualifiedQuantity) + .setBatchId(sourceLine.getBatchId()) + .setIpqcId(sourceLine.getIpqcId()) + .setQcFlag(sourceLine.getQcFlag()) + .setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus()) + .setRemark(sourceLine.getRemark()); + issueLineMapper.insert(unqualifiedLine); + // 1.C.2 更新原行为合格品(数量调整为合格数量) + issueLineMapper.updateById(new MesWmReturnIssueLineDO() + .setId(sourceLineId) + .setQuantity(qualifiedQuantity) + .setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus())); + } + + // 2. 检查退料单下是否还有待检验的行,若全检完则将退料单状态设为"待上架" + if (sourceDocId != null) { + List allLines = issueLineMapper.selectListByIssueId(sourceDocId); + boolean allInspected = !CollUtil.contains(allLines, + line -> Objects.equals(line.getQualityStatus(), MesWmQualityStatusEnum.PENDING.getStatus())); + if (allInspected) { + issueService.updateReturnIssueStatus(sourceDocId, MesWmReturnIssueStatusEnum.APPROVING.getStatus()); + } + } } } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueService.java index 68118a2..5f16cb5 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueService.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueService.java @@ -85,4 +85,12 @@ public interface MesWmReturnIssueService { */ void cancelReturnIssue(Long id); + /** + * 更新生产退料单状态(供外部模块联动调用,如 RQC 检验完成后) + * + * @param id 编号 + * @param status 目标状态 + */ + void updateReturnIssueStatus(Long id, Integer status); + } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java index 92e634c..cf47a6f 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java +++ b/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 { return issue; } + @Override + public void updateReturnIssueStatus(Long id, Integer status) { + validateReturnIssueExists(id); + issueMapper.updateById(new MesWmReturnIssueDO().setId(id).setStatus(status)); + } + } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineService.java index 376e454..496d4a1 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineService.java +++ b/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 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO; import jakarta.validation.Valid; +import java.math.BigDecimal; import java.util.List; /** @@ -85,11 +86,23 @@ public interface MesWmReturnSalesLineService { void updateQualityStatusByReturnId(Long returnId, Integer qualityStatus); /** - * RQC 检验完成后,更新销售退货单行的质量状态 + * RQC 检验完成后,根据合格品/不合格品数量更新销售退货单行 * - * @param id 行 ID - * @param checkResult 检验结果 + *

拆分逻辑: + *

+ * 同时检查退货单下所有行是否都已检验完毕,若全检完则将退货单状态设为待上架。 + * + * @param sourceLineId 来源行 ID + * @param sourceDocId 来源退货单 ID + * @param checkResult 检验结果 + * @param qualifiedQuantity 合格品数量 + * @param unqualifiedQuantity 不合格品数量 */ - void updateReturnSalesLineWhenRqcFinish(Long id, Integer checkResult); + void updateReturnSalesLineWhenRqcFinish(Long sourceLineId, Long sourceDocId, Integer checkResult, + BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity); } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java index a6478b5..a3fb263 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.mes.service.wm.returnsales; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWmReturnSalesLinePageReqVO; @@ -7,14 +8,15 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWm import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO; import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales.MesWmReturnSalesLineMapper; -import cn.iocoder.yudao.module.mes.enums.qc.MesQcCheckResultEnum; import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; +import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnSalesStatusEnum; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.math.BigDecimal; import java.util.List; import java.util.Objects; @@ -129,11 +131,48 @@ public class MesWmReturnSalesLineServiceImpl implements MesWmReturnSalesLineServ } @Override - public void updateReturnSalesLineWhenRqcFinish(Long id, Integer checkResult) { - Integer qualityStatus = Objects.equals(checkResult, MesQcCheckResultEnum.PASS.getType()) - ? MesWmQualityStatusEnum.PASS.getStatus() - : MesWmQualityStatusEnum.FAIL.getStatus(); - returnSalesLineMapper.updateById(new MesWmReturnSalesLineDO().setId(id).setQualityStatus(qualityStatus)); + public void updateReturnSalesLineWhenRqcFinish(Long sourceLineId, Long sourceDocId, Integer checkResult, + BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity) { + MesWmReturnSalesLineDO sourceLine = validateReturnSalesLineExists(sourceLineId); + boolean hasUnqualified = unqualifiedQuantity != null && unqualifiedQuantity.compareTo(BigDecimal.ZERO) > 0; + boolean hasQualified = qualifiedQuantity != null && qualifiedQuantity.compareTo(BigDecimal.ZERO) > 0; + + // 1. 根据合格/不合格品数量,更新退货单行的质量状态 + if (!hasUnqualified) { + // 1.A 情况一:全部合格 + returnSalesLineMapper.updateById(new MesWmReturnSalesLineDO() + .setId(sourceLineId).setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus())); + } else if (!hasQualified) { + // 1.B 情况二:全部不合格 + returnSalesLineMapper.updateById(new MesWmReturnSalesLineDO() + .setId(sourceLineId).setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus())); + } else { + // 1.C 情况三:部分合格部分不合格 → 拆分行 + // 1.C.1 新增一行不合格品 + MesWmReturnSalesLineDO unqualifiedLine = new MesWmReturnSalesLineDO() + .setReturnId(sourceLine.getReturnId()) + .setItemId(sourceLine.getItemId()) + .setQuantity(unqualifiedQuantity) + .setBatchId(sourceLine.getBatchId()) + .setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus()) + .setRemark(sourceLine.getRemark()); + returnSalesLineMapper.insert(unqualifiedLine); + // 1.C.2 更新原行为合格品 + returnSalesLineMapper.updateById(new MesWmReturnSalesLineDO() + .setId(sourceLineId) + .setQuantity(qualifiedQuantity) + .setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus())); + } + + // 2. 检查退货单下是否还有待检验的行,若全检完则将退货单状态设为"待上架" + if (sourceDocId != null) { + List allLines = returnSalesLineMapper.selectListByReturnId(sourceDocId); + boolean allInspected = !CollUtil.contains(allLines, + line -> Objects.equals(line.getQualityStatus(), MesWmQualityStatusEnum.PENDING.getStatus())); + if (allInspected) { + returnSalesService.updateReturnSalesStatus(sourceDocId, MesWmReturnSalesStatusEnum.APPROVED.getStatus()); + } + } } } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesService.java index 7a9f15c..ccba7c3 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesService.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesService.java @@ -88,6 +88,14 @@ public interface MesWmReturnSalesService { void cancelReturnSales(Long id); /** + * 更新销售退货单状态(供外部模块联动调用,如 RQC 检验完成后) + * + * @param id 编号 + * @param status 目标状态 + */ + void updateReturnSalesStatus(Long id, Integer status); + + /** * 校验销售退货单的数量:每行明细数量之和是否等于行退货数量 * * @param id 编号 diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesServiceImpl.java index 8b941c9..675ed98 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesServiceImpl.java +++ b/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 { return returnSales; } + @Override + public void updateReturnSalesStatus(Long id, Integer status) { + validateReturnSalesExists(id); + returnSalesMapper.updateById(new MesWmReturnSalesDO().setId(id).setStatus(status)); + } + } diff --git a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImplTest.java b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImplTest.java index ac174e0..4fc1da9 100644 --- a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImplTest.java +++ b/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; import org.springframework.context.annotation.Import; import org.springframework.test.context.bean.override.mockito.MockitoBean; +import java.math.BigDecimal; + import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.QC_RQC_NOT_EXISTS; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.QC_RQC_NOT_PREPARE; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -61,18 +61,22 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest { public void testFinishRqc_successWithReturnIssue() { // 准备参数 Long sourceLineId = randomLongId(); + Long sourceDocId = randomLongId(); Integer checkResult = 1; + BigDecimal qualifiedQty = new BigDecimal("80"); + BigDecimal unqualifiedQty = new BigDecimal("20"); // mock 数据 MesQcRqcDO rqc = randomPojo(MesQcRqcDO.class, o -> { o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); o.setSourceDocType(MesBizTypeConstants.WM_RETURN_ISSUE); + o.setSourceDocId(sourceDocId); o.setSourceLineId(sourceLineId); o.setCheckResult(checkResult); // 设置数量通过 finishRqc 的数量校验:合格 + 不合格 = 检测数量 - o.setCheckQuantity(new java.math.BigDecimal("100")); - o.setQualifiedQuantity(new java.math.BigDecimal("80")); - o.setUnqualifiedQuantity(new java.math.BigDecimal("20")); + o.setCheckQuantity(new BigDecimal("100")); + o.setQualifiedQuantity(qualifiedQty); + o.setUnqualifiedQuantity(unqualifiedQty); }); rqcMapper.insert(rqc); @@ -83,28 +87,36 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest { MesQcRqcDO updatedRqc = rqcMapper.selectById(rqc.getId()); assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedRqc.getStatus()); - // 断言:验证回写调用生产退料 - verify(returnIssueLineService).updateReturnIssueLineWhenRqcFinish(eq(sourceLineId), eq(checkResult)); + // 断言:验证回写调用生产退料(含拆分参数) + verify(returnIssueLineService).updateReturnIssueLineWhenRqcFinish( + eq(sourceLineId), eq(sourceDocId), eq(checkResult), + argThat(v -> v.compareTo(qualifiedQty) == 0), + argThat(v -> v.compareTo(unqualifiedQty) == 0)); // 断言:未调用销售退货 - verify(returnSalesLineService, never()).updateReturnSalesLineWhenRqcFinish(anyLong(), anyInt()); + verify(returnSalesLineService, never()).updateReturnSalesLineWhenRqcFinish( + anyLong(), anyLong(), anyInt(), any(BigDecimal.class), any(BigDecimal.class)); } @Test public void testFinishRqc_successWithReturnSales() { // 准备参数 Long sourceLineId = randomLongId(); + Long sourceDocId = randomLongId(); Integer checkResult = 1; + BigDecimal qualifiedQty = new BigDecimal("90"); + BigDecimal unqualifiedQty = new BigDecimal("10"); // mock 数据 MesQcRqcDO rqc = randomPojo(MesQcRqcDO.class, o -> { o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); o.setSourceDocType(MesBizTypeConstants.WM_RETURN_SALES); + o.setSourceDocId(sourceDocId); o.setSourceLineId(sourceLineId); o.setCheckResult(checkResult); // 设置数量通过 finishRqc 的数量校验:合格 + 不合格 = 检测数量 - o.setCheckQuantity(new java.math.BigDecimal("100")); - o.setQualifiedQuantity(new java.math.BigDecimal("90")); - o.setUnqualifiedQuantity(new java.math.BigDecimal("10")); + o.setCheckQuantity(new BigDecimal("100")); + o.setQualifiedQuantity(qualifiedQty); + o.setUnqualifiedQuantity(unqualifiedQty); }); rqcMapper.insert(rqc); @@ -115,10 +127,14 @@ public class MesQcRqcServiceImplTest extends BaseDbUnitTest { MesQcRqcDO updatedRqc = rqcMapper.selectById(rqc.getId()); assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedRqc.getStatus()); - // 断言:验证回写调用销售退货 - verify(returnSalesLineService).updateReturnSalesLineWhenRqcFinish(eq(sourceLineId), eq(checkResult)); + // 断言:验证回写调用销售退货(含拆分参数) + verify(returnSalesLineService).updateReturnSalesLineWhenRqcFinish( + eq(sourceLineId), eq(sourceDocId), eq(checkResult), + argThat(v -> v.compareTo(qualifiedQty) == 0), + argThat(v -> v.compareTo(unqualifiedQty) == 0)); // 断言:未调用生产退料 - verify(returnIssueLineService, never()).updateReturnIssueLineWhenRqcFinish(anyLong(), anyInt()); + verify(returnIssueLineService, never()).updateReturnIssueLineWhenRqcFinish( + anyLong(), anyLong(), anyInt(), any(BigDecimal.class), any(BigDecimal.class)); } @Test diff --git a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImplTest.java b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImplTest.java index 8243184..4934dca 100644 --- a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImplTest.java +++ b/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; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; +import java.math.BigDecimal; + import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * {@link MesWmReturnIssueLineServiceImpl} 的单元测试类 @@ -36,37 +39,75 @@ public class MesWmReturnIssueLineServiceImplTest extends BaseDbUnitTest { private MesMdItemService itemService; @Test - public void testUpdateReturnIssueLineWhenRqcFinish_pass() { - // 准备参数 + public void testUpdateReturnIssueLineWhenRqcFinish_allPass() { + // 准备参数:全部合格 Integer checkResult = MesQcCheckResultEnum.PASS.getType(); + BigDecimal qualifiedQty = new BigDecimal("100"); + BigDecimal unqualifiedQty = BigDecimal.ZERO; // mock 数据 - MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class); + MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class, o -> o.setQuantity(qualifiedQty)); returnIssueLineMapper.insert(lineDO); - // 调用 - returnIssueLineService.updateReturnIssueLineWhenRqcFinish(lineDO.getId(), checkResult); + // 调用(sourceDocId 传 null 不联动主表状态) + returnIssueLineService.updateReturnIssueLineWhenRqcFinish( + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty); - // 断言:验证 mapper 更新参数正确 + // 断言:验证质量状态更新为合格 MesWmReturnIssueLineDO updateLine = returnIssueLineMapper.selectById(lineDO.getId()); assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), updateLine.getQualityStatus()); } @Test - public void testUpdateReturnIssueLineWhenRqcFinish_fail() { - // 准备参数 + public void testUpdateReturnIssueLineWhenRqcFinish_allFail() { + // 准备参数:全部不合格 Integer checkResult = MesQcCheckResultEnum.FAIL.getType(); + BigDecimal qualifiedQty = BigDecimal.ZERO; + BigDecimal unqualifiedQty = new BigDecimal("100"); // mock 数据 - MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class); + MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class, o -> o.setQuantity(unqualifiedQty)); returnIssueLineMapper.insert(lineDO); // 调用 - returnIssueLineService.updateReturnIssueLineWhenRqcFinish(lineDO.getId(), checkResult); + returnIssueLineService.updateReturnIssueLineWhenRqcFinish( + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty); - // 断言:验证 mapper 更新参数正确 + // 断言:验证质量状态更新为不合格 MesWmReturnIssueLineDO updateLine = returnIssueLineMapper.selectById(lineDO.getId()); assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), updateLine.getQualityStatus()); } + @Test + public void testUpdateReturnIssueLineWhenRqcFinish_partialSplit() { + // 准备参数:部分合格部分不合格,触发行拆分 + Integer checkResult = MesQcCheckResultEnum.PASS.getType(); + BigDecimal qualifiedQty = new BigDecimal("80"); + BigDecimal unqualifiedQty = new BigDecimal("20"); + + // mock 数据 + MesWmReturnIssueLineDO lineDO = randomPojo(MesWmReturnIssueLineDO.class, o -> + o.setQuantity(new BigDecimal("100"))); + returnIssueLineMapper.insert(lineDO); + + // 调用 + returnIssueLineService.updateReturnIssueLineWhenRqcFinish( + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty); + + // 断言:原行变为合格,数量调整为合格数量 + MesWmReturnIssueLineDO originalLine = returnIssueLineMapper.selectById(lineDO.getId()); + assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), originalLine.getQualityStatus()); + assertEquals(0, qualifiedQty.compareTo(originalLine.getQuantity())); + + // 断言:新增了一行不合格品 + var allLines = returnIssueLineMapper.selectListByIssueId(lineDO.getIssueId()); + assertEquals(2, allLines.size()); + MesWmReturnIssueLineDO newLine = allLines.stream() + .filter(l -> !l.getId().equals(lineDO.getId())) + .findFirst().orElse(null); + assertNotNull(newLine); + assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), newLine.getQualityStatus()); + assertEquals(0, unqualifiedQty.compareTo(newLine.getQuantity())); + } + } diff --git a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImplTest.java b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImplTest.java index b8f9ff3..3ef35fe 100644 --- a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImplTest.java +++ b/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; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; +import java.math.BigDecimal; + import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * {@link MesWmReturnSalesLineServiceImpl} 的单元测试类 @@ -36,37 +39,75 @@ public class MesWmReturnSalesLineServiceImplTest extends BaseDbUnitTest { private MesMdItemService itemService; @Test - public void testUpdateReturnSalesLineWhenRqcFinish_pass() { - // 准备参数 + public void testUpdateReturnSalesLineWhenRqcFinish_allPass() { + // 准备参数:全部合格 Integer checkResult = MesQcCheckResultEnum.PASS.getType(); + BigDecimal qualifiedQty = new BigDecimal("100"); + BigDecimal unqualifiedQty = BigDecimal.ZERO; // mock 数据 - MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class); + MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class, o -> o.setQuantity(qualifiedQty)); returnSalesLineMapper.insert(lineDO); - // 调用 - returnSalesLineService.updateReturnSalesLineWhenRqcFinish(lineDO.getId(), checkResult); + // 调用(sourceDocId 传 null 不联动主表状态) + returnSalesLineService.updateReturnSalesLineWhenRqcFinish( + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty); - // 断言:验证 mapper 更新参数正确 + // 断言:验证质量状态更新为合格 MesWmReturnSalesLineDO updateLine = returnSalesLineMapper.selectById(lineDO.getId()); assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), updateLine.getQualityStatus()); } @Test - public void testUpdateReturnSalesLineWhenRqcFinish_fail() { - // 准备参数 + public void testUpdateReturnSalesLineWhenRqcFinish_allFail() { + // 准备参数:全部不合格 Integer checkResult = MesQcCheckResultEnum.FAIL.getType(); + BigDecimal qualifiedQty = BigDecimal.ZERO; + BigDecimal unqualifiedQty = new BigDecimal("100"); // mock 数据 - MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class); + MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class, o -> o.setQuantity(unqualifiedQty)); returnSalesLineMapper.insert(lineDO); // 调用 - returnSalesLineService.updateReturnSalesLineWhenRqcFinish(lineDO.getId(), checkResult); + returnSalesLineService.updateReturnSalesLineWhenRqcFinish( + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty); - // 断言:验证 mapper 更新参数正确 + // 断言:验证质量状态更新为不合格 MesWmReturnSalesLineDO updateLine = returnSalesLineMapper.selectById(lineDO.getId()); assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), updateLine.getQualityStatus()); } + @Test + public void testUpdateReturnSalesLineWhenRqcFinish_partialSplit() { + // 准备参数:部分合格部分不合格,触发行拆分 + Integer checkResult = MesQcCheckResultEnum.PASS.getType(); + BigDecimal qualifiedQty = new BigDecimal("70"); + BigDecimal unqualifiedQty = new BigDecimal("30"); + + // mock 数据 + MesWmReturnSalesLineDO lineDO = randomPojo(MesWmReturnSalesLineDO.class, o -> + o.setQuantity(new BigDecimal("100"))); + returnSalesLineMapper.insert(lineDO); + + // 调用 + returnSalesLineService.updateReturnSalesLineWhenRqcFinish( + lineDO.getId(), null, checkResult, qualifiedQty, unqualifiedQty); + + // 断言:原行变为合格,数量调整为合格数量 + MesWmReturnSalesLineDO originalLine = returnSalesLineMapper.selectById(lineDO.getId()); + assertEquals(MesWmQualityStatusEnum.PASS.getStatus(), originalLine.getQualityStatus()); + assertEquals(0, qualifiedQty.compareTo(originalLine.getQuantity())); + + // 断言:新增了一行不合格品 + var allLines = returnSalesLineMapper.selectListByReturnId(lineDO.getReturnId()); + assertEquals(2, allLines.size()); + MesWmReturnSalesLineDO newLine = allLines.stream() + .filter(l -> !l.getId().equals(lineDO.getId())) + .findFirst().orElse(null); + assertNotNull(newLine); + assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), newLine.getQualityStatus()); + assertEquals(0, unqualifiedQty.compareTo(newLine.getQuantity())); + } + } -- libgit2 0.21.4