Commit dbccfbab8e7448b2fd8ff68f520d20e849e766d9

Authored by YunaiV
1 parent 2e113305

✅ test(iqc): 添加 IQC 服务单元测试,验证状态更新逻辑

新增 IQC 服务的单元测试,确保在完成 IQC 时正确更新状态并回写相关单据。测试覆盖了不同来源的 IQC 单,包括到货通知单和外协入库单,以及处理无来源单据和脏数据的情况。
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.qc.iqc;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
  5 +import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcMapper;
  6 +import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
  7 +import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
  8 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  9 +import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
  10 +import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
  11 +import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateDetailService;
  12 +import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService;
  13 +import cn.iocoder.yudao.module.mes.service.wm.outsourcereceipt.MesWmOutsourceReceiptService;
  14 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  15 +import org.junit.jupiter.api.Test;
  16 +import org.springframework.context.annotation.Import;
  17 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  18 +
  19 +import jakarta.annotation.Resource;
  20 +
  21 +import java.math.BigDecimal;
  22 +
  23 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  25 +import static org.junit.jupiter.api.Assertions.*;
  26 +import static org.mockito.Mockito.*;
  27 +
  28 +/**
  29 + * {@link MesQcIqcServiceImpl} 的单元测试
  30 + *
  31 + * @author 芋道源码
  32 + */
  33 +@Import(MesQcIqcServiceImpl.class)
  34 +public class MesQcIqcServiceImplTest extends BaseDbUnitTest {
  35 +
  36 + @Resource
  37 + private MesQcIqcServiceImpl iqcService;
  38 +
  39 + @Resource
  40 + private MesQcIqcMapper iqcMapper;
  41 +
  42 + @MockitoBean
  43 + private MesWmArrivalNoticeService arrivalNoticeService;
  44 + @MockitoBean
  45 + private MesWmOutsourceReceiptService outsourceReceiptService;
  46 + @MockitoBean
  47 + private MesQcIqcLineService iqcLineService;
  48 + @MockitoBean
  49 + private MesQcDefectRecordService defectRecordService;
  50 + @MockitoBean
  51 + private MesMdVendorService vendorService;
  52 + @MockitoBean
  53 + private MesMdItemService itemService;
  54 + @MockitoBean
  55 + private MesQcTemplateDetailService templateDetailService;
  56 + @MockitoBean
  57 + private AdminUserApi adminUserApi;
  58 +
  59 + @Test
  60 + public void testFinishIqc_writeBack_arrivalNotice() {
  61 + // mock 数据:插入一条草稿状态的 IQC 单,来源为到货通知单
  62 + Long sourceDocId = randomLongId();
  63 + Long sourceLineId = randomLongId();
  64 + MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> {
  65 + o.setStatus(MesQcStatusEnum.DRAFT.getStatus());
  66 + o.setCheckResult(1); // 非空,满足 finishIqc 的校验
  67 + o.setSourceDocType(MesBizTypeConstants.WM_ARRIVAL_NOTICE);
  68 + o.setSourceDocId(sourceDocId);
  69 + o.setSourceLineId(sourceLineId);
  70 + o.setQualifiedQuantity(BigDecimal.valueOf(100));
  71 + o.setUnqualifiedQuantity(BigDecimal.ZERO);
  72 + });
  73 + iqcMapper.insert(iqc);
  74 +
  75 + // 调用
  76 + iqcService.finishIqc(iqc.getId());
  77 +
  78 + // 断言:验证状态更新为已完成
  79 + MesQcIqcDO updatedIqc = iqcMapper.selectById(iqc.getId());
  80 + assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedIqc.getStatus());
  81 + // 断言:验证回写到货通知单(参数使用 any 避免 BigDecimal 精度问题)
  82 + verify(arrivalNoticeService).approveArrivalNoticeWhenIqcComplete(
  83 + eq(sourceDocId), eq(sourceLineId), eq(iqc.getId()), any(BigDecimal.class));
  84 + // 断言:不应该调用外协入库单的回写
  85 + verify(outsourceReceiptService, never()).approveOutsourceReceiptWhenIqcComplete(
  86 + anyLong(), anyLong(), anyLong(), any(), any());
  87 + }
  88 +
  89 + @Test
  90 + public void testFinishIqc_writeBack_outsourceReceipt() {
  91 + // mock 数据:插入一条草稿状态的 IQC 单,来源为外协入库单
  92 + Long sourceDocId = randomLongId();
  93 + Long sourceLineId = randomLongId();
  94 + MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> {
  95 + o.setStatus(MesQcStatusEnum.DRAFT.getStatus());
  96 + o.setCheckResult(1);
  97 + o.setSourceDocType(MesBizTypeConstants.WM_OUTSOURCE_RECPT);
  98 + o.setSourceDocId(sourceDocId);
  99 + o.setSourceLineId(sourceLineId);
  100 + o.setQualifiedQuantity(BigDecimal.valueOf(80));
  101 + o.setUnqualifiedQuantity(BigDecimal.valueOf(20));
  102 + });
  103 + iqcMapper.insert(iqc);
  104 +
  105 + // 调用
  106 + iqcService.finishIqc(iqc.getId());
  107 +
  108 + // 断言:验证状态更新为已完成
  109 + MesQcIqcDO updatedIqc = iqcMapper.selectById(iqc.getId());
  110 + assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedIqc.getStatus());
  111 + // 断言:验证回写外协入库单(参数使用 any 避免 BigDecimal 精度问题)
  112 + verify(outsourceReceiptService).approveOutsourceReceiptWhenIqcComplete(
  113 + eq(sourceDocId), eq(sourceLineId), eq(iqc.getId()), any(BigDecimal.class), any(BigDecimal.class));
  114 + // 断言:不应该调用到货通知单的回写
  115 + verify(arrivalNoticeService, never()).approveArrivalNoticeWhenIqcComplete(
  116 + anyLong(), anyLong(), anyLong(), any());
  117 + }
  118 +
  119 + @Test
  120 + public void testFinishIqc_writeBack_noSourceDoc() {
  121 + // mock 数据:sourceDocType 为空,不需要回写
  122 + MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> {
  123 + o.setStatus(MesQcStatusEnum.DRAFT.getStatus());
  124 + o.setCheckResult(1);
  125 + o.setSourceDocType(null);
  126 + o.setSourceDocId(null);
  127 + o.setSourceLineId(null);
  128 + });
  129 + iqcMapper.insert(iqc);
  130 +
  131 + // 调用
  132 + iqcService.finishIqc(iqc.getId());
  133 +
  134 + // 断言:验证状态更新为已完成
  135 + MesQcIqcDO updatedIqc = iqcMapper.selectById(iqc.getId());
  136 + assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedIqc.getStatus());
  137 + // 断言:不应该调用任何回写
  138 + verify(arrivalNoticeService, never()).approveArrivalNoticeWhenIqcComplete(
  139 + anyLong(), anyLong(), anyLong(), any());
  140 + verify(outsourceReceiptService, never()).approveOutsourceReceiptWhenIqcComplete(
  141 + anyLong(), anyLong(), anyLong(), any(), any());
  142 + }
  143 +
  144 + @Test
  145 + public void testFinishIqc_writeBack_sourceDocTypeDirtyData() {
  146 + // mock 数据:sourceDocType 非空但 sourceLineId 为空,应该抛异常
  147 + MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> {
  148 + o.setStatus(MesQcStatusEnum.DRAFT.getStatus());
  149 + o.setCheckResult(1);
  150 + o.setSourceDocType(MesBizTypeConstants.WM_ARRIVAL_NOTICE);
  151 + o.setSourceDocId(randomLongId());
  152 + o.setSourceLineId(null); // 脏数据
  153 + });
  154 + iqcMapper.insert(iqc);
  155 +
  156 + // 调用,并断言异常
  157 + assertThrows(IllegalArgumentException.class, () -> iqcService.finishIqc(iqc.getId()));
  158 + }
  159 +
  160 + @Test
  161 + public void testFinishIqc_writeBack_unknownSourceDocType() {
  162 + // mock 数据:sourceDocType 无法识别
  163 + MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> {
  164 + o.setStatus(MesQcStatusEnum.DRAFT.getStatus());
  165 + o.setCheckResult(1);
  166 + o.setSourceDocType(99999); // 无法识别的类型
  167 + o.setSourceDocId(randomLongId());
  168 + o.setSourceLineId(randomLongId());
  169 + });
  170 + iqcMapper.insert(iqc);
  171 +
  172 + // 调用,并断言异常
  173 + assertThrows(IllegalArgumentException.class, () -> iqcService.finishIqc(iqc.getId()));
  174 + }
  175 +
  176 +}
... ...
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.arrivalnotice;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
  6 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.arrivalnotice.MesWmArrivalNoticeMapper;
  7 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmArrivalNoticeStatusEnum;
  8 +import org.junit.jupiter.api.Test;
  9 +import org.springframework.context.annotation.Import;
  10 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  11 +
  12 +import jakarta.annotation.Resource;
  13 +
  14 +import java.math.BigDecimal;
  15 +import java.util.Arrays;
  16 +
  17 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  18 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  19 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  20 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  21 +import static org.junit.jupiter.api.Assertions.*;
  22 +import static org.mockito.ArgumentMatchers.eq;
  23 +import static org.mockito.Mockito.*;
  24 +
  25 +/**
  26 + * {@link MesWmArrivalNoticeServiceImpl#approveArrivalNoticeWhenIqcComplete} 的单元测试
  27 + *
  28 + * @author 芋道源码
  29 + */
  30 +@Import(MesWmArrivalNoticeServiceImpl.class)
  31 +public class MesWmArrivalNoticeServiceImplTest extends BaseDbUnitTest {
  32 +
  33 + @Resource
  34 + private MesWmArrivalNoticeServiceImpl arrivalNoticeService;
  35 +
  36 + @Resource
  37 + private MesWmArrivalNoticeMapper arrivalNoticeMapper;
  38 +
  39 + @MockitoBean
  40 + private MesWmArrivalNoticeLineService arrivalNoticeLineService;
  41 +
  42 + @Test
  43 + public void testApproveArrivalNoticeWhenIqcComplete_success_allLinesChecked() {
  44 + // mock 数据:插入一条待质检的到货通知单
  45 + MesWmArrivalNoticeDO notice = randomPojo(MesWmArrivalNoticeDO.class, o -> {
  46 + o.setStatus(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus());
  47 + });
  48 + arrivalNoticeMapper.insert(notice);
  49 + // 准备参数
  50 + Long noticeId = notice.getId();
  51 + Long lineId = randomLongId();
  52 + Long iqcId = randomLongId();
  53 + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100);
  54 +
  55 + // mock 行服务方法:所有需检行都已完成(iqcId 不为空)
  56 + MesWmArrivalNoticeLineDO line1 = randomPojo(MesWmArrivalNoticeLineDO.class, o -> {
  57 + o.setNoticeId(noticeId);
  58 + o.setIqcCheckFlag(true);
  59 + o.setIqcId(iqcId); // 已检完
  60 + });
  61 + MesWmArrivalNoticeLineDO line2 = randomPojo(MesWmArrivalNoticeLineDO.class, o -> {
  62 + o.setNoticeId(noticeId);
  63 + o.setIqcCheckFlag(false); // 不需要检验
  64 + o.setIqcId(null);
  65 + });
  66 + when(arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(eq(noticeId)))
  67 + .thenReturn(Arrays.asList(line1, line2));
  68 +
  69 + // 调用
  70 + arrivalNoticeService.approveArrivalNoticeWhenIqcComplete(noticeId, lineId, iqcId, qualifiedQuantity);
  71 +
  72 + // 断言:行回写被调用
  73 + verify(arrivalNoticeLineService).updateByIqcComplete(eq(lineId), eq(iqcId), eq(qualifiedQuantity));
  74 + // 断言:主表状态更新为待入库
  75 + MesWmArrivalNoticeDO updatedNotice = arrivalNoticeMapper.selectById(noticeId);
  76 + assertEquals(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus(), updatedNotice.getStatus());
  77 + }
  78 +
  79 + @Test
  80 + public void testApproveArrivalNoticeWhenIqcComplete_success_hasUncheckedLines() {
  81 + // mock 数据:插入一条待质检的到货通知单
  82 + MesWmArrivalNoticeDO notice = randomPojo(MesWmArrivalNoticeDO.class, o -> {
  83 + o.setStatus(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus());
  84 + });
  85 + arrivalNoticeMapper.insert(notice);
  86 + // 准备参数
  87 + Long noticeId = notice.getId();
  88 + Long lineId = randomLongId();
  89 + Long iqcId = randomLongId();
  90 + BigDecimal qualifiedQuantity = BigDecimal.valueOf(50);
  91 +
  92 + // mock 行服务方法:还有未完成的需检行
  93 + MesWmArrivalNoticeLineDO line1 = randomPojo(MesWmArrivalNoticeLineDO.class, o -> {
  94 + o.setNoticeId(noticeId);
  95 + o.setIqcCheckFlag(true);
  96 + o.setIqcId(iqcId); // 已检完
  97 + });
  98 + MesWmArrivalNoticeLineDO line2 = randomPojo(MesWmArrivalNoticeLineDO.class, o -> {
  99 + o.setNoticeId(noticeId);
  100 + o.setIqcCheckFlag(true);
  101 + o.setIqcId(null); // 还未检验
  102 + });
  103 + when(arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(eq(noticeId)))
  104 + .thenReturn(Arrays.asList(line1, line2));
  105 +
  106 + // 调用
  107 + arrivalNoticeService.approveArrivalNoticeWhenIqcComplete(noticeId, lineId, iqcId, qualifiedQuantity);
  108 +
  109 + // 断言:行回写被调用
  110 + verify(arrivalNoticeLineService).updateByIqcComplete(eq(lineId), eq(iqcId), eq(qualifiedQuantity));
  111 + // 断言:主表状态不变,仍然是待质检
  112 + MesWmArrivalNoticeDO updatedNotice = arrivalNoticeMapper.selectById(noticeId);
  113 + assertEquals(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus(), updatedNotice.getStatus());
  114 + }
  115 +
  116 + @Test
  117 + public void testApproveArrivalNoticeWhenIqcComplete_noticeNotExists() {
  118 + // 准备参数
  119 + Long noticeId = randomLongId();
  120 + Long lineId = randomLongId();
  121 + Long iqcId = randomLongId();
  122 + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100);
  123 +
  124 + // 调用,并断言异常
  125 + assertServiceException(
  126 + () -> arrivalNoticeService.approveArrivalNoticeWhenIqcComplete(noticeId, lineId, iqcId, qualifiedQuantity),
  127 + WM_ARRIVAL_NOTICE_NOT_EXISTS);
  128 + }
  129 +
  130 + @Test
  131 + public void testApproveArrivalNoticeWhenIqcComplete_statusNotPendingQc() {
  132 + // mock 数据:插入一条草稿状态的到货通知单(非待质检)
  133 + MesWmArrivalNoticeDO notice = randomPojo(MesWmArrivalNoticeDO.class, o -> {
  134 + o.setStatus(MesWmArrivalNoticeStatusEnum.PREPARE.getStatus());
  135 + });
  136 + arrivalNoticeMapper.insert(notice);
  137 + // 准备参数
  138 + Long noticeId = notice.getId();
  139 + Long lineId = randomLongId();
  140 + Long iqcId = randomLongId();
  141 + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100);
  142 +
  143 + // 调用,并断言异常
  144 + assertServiceException(
  145 + () -> arrivalNoticeService.approveArrivalNoticeWhenIqcComplete(noticeId, lineId, iqcId, qualifiedQuantity),
  146 + WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_QC);
  147 + }
  148 +
  149 +}
... ...
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.outsourcereceipt;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptLineDO;
  6 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourcereceipt.MesWmOutsourceReceiptMapper;
  7 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutsourceReceiptStatusEnum;
  8 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
  9 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  10 +import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
  11 +import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
  12 +import org.junit.jupiter.api.Test;
  13 +import org.springframework.context.annotation.Import;
  14 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  15 +
  16 +import jakarta.annotation.Resource;
  17 +
  18 +import java.math.BigDecimal;
  19 +
  20 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  21 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  22 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  23 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  24 +import static org.junit.jupiter.api.Assertions.*;
  25 +import static org.mockito.ArgumentMatchers.*;
  26 +import static org.mockito.Mockito.*;
  27 +
  28 +/**
  29 + * {@link MesWmOutsourceReceiptServiceImpl#approveOutsourceReceiptWhenIqcComplete} 的单元测试
  30 + *
  31 + * @author 芋道源码
  32 + */
  33 +@Import(MesWmOutsourceReceiptServiceImpl.class)
  34 +public class MesWmOutsourceReceiptServiceImplTest extends BaseDbUnitTest {
  35 +
  36 + @Resource
  37 + private MesWmOutsourceReceiptServiceImpl outsourceReceiptService;
  38 +
  39 + @Resource
  40 + private MesWmOutsourceReceiptMapper outsourceReceiptMapper;
  41 +
  42 + @MockitoBean
  43 + private MesWmOutsourceReceiptLineService outsourceReceiptLineService;
  44 + @MockitoBean
  45 + private MesWmOutsourceReceiptDetailService outsourceReceiptDetailService;
  46 + @MockitoBean
  47 + private MesWmTransactionService wmTransactionService;
  48 + @MockitoBean
  49 + private MesMdVendorService vendorService;
  50 + @MockitoBean
  51 + private MesMdItemService itemService;
  52 +
  53 + @Test
  54 + public void testApproveOutsourceReceiptWhenIqcComplete_success_bothQualifiedAndUnqualified() {
  55 + // mock 数据:插入一条待检验状态的外协入库单
  56 + MesWmOutsourceReceiptDO receipt = randomPojo(MesWmOutsourceReceiptDO.class, o -> {
  57 + o.setStatus(MesWmOutsourceReceiptStatusEnum.CONFIRMED.getStatus());
  58 + });
  59 + outsourceReceiptMapper.insert(receipt);
  60 + // 准备参数
  61 + Long receiptId = receipt.getId();
  62 + Long lineId = randomLongId();
  63 + Long iqcId = randomLongId();
  64 + BigDecimal qualifiedQuantity = BigDecimal.valueOf(80);
  65 + BigDecimal unqualifiedQuantity = BigDecimal.valueOf(20);
  66 +
  67 + // mock 行查询
  68 + MesWmOutsourceReceiptLineDO line = randomPojo(MesWmOutsourceReceiptLineDO.class, o -> {
  69 + o.setId(lineId);
  70 + o.setReceiptId(receiptId);
  71 + o.setQuantity(BigDecimal.valueOf(100));
  72 + });
  73 + when(outsourceReceiptLineService.getOutsourceReceiptLine(eq(lineId))).thenReturn(line);
  74 +
  75 + // 调用
  76 + outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete(
  77 + receiptId, lineId, iqcId, qualifiedQuantity, unqualifiedQuantity);
  78 +
  79 + // 断言:合格品行更新
  80 + verify(outsourceReceiptLineService).updateOutsourceReceiptLineDO(argThat(updateLine ->
  81 + lineId.equals(updateLine.getId())
  82 + && MesWmQualityStatusEnum.PASS.getStatus().equals(updateLine.getQualityStatus())
  83 + && iqcId.equals(updateLine.getIqcId())
  84 + && qualifiedQuantity.equals(updateLine.getQuantity())));
  85 + // 断言:不合格品新增行
  86 + verify(outsourceReceiptLineService).createOutsourceReceiptLineDO(argThat(newLine ->
  87 + newLine.getId() == null
  88 + && MesWmQualityStatusEnum.FAIL.getStatus().equals(newLine.getQualityStatus())
  89 + && iqcId.equals(newLine.getIqcId())
  90 + && unqualifiedQuantity.equals(newLine.getQuantity())));
  91 + // 断言:主表状态更新为待上架
  92 + MesWmOutsourceReceiptDO updatedReceipt = outsourceReceiptMapper.selectById(receiptId);
  93 + assertEquals(MesWmOutsourceReceiptStatusEnum.APPROVING.getStatus(), updatedReceipt.getStatus());
  94 + }
  95 +
  96 + @Test
  97 + public void testApproveOutsourceReceiptWhenIqcComplete_success_onlyQualified() {
  98 + // mock 数据:插入一条待检验状态的外协入库单
  99 + MesWmOutsourceReceiptDO receipt = randomPojo(MesWmOutsourceReceiptDO.class, o -> {
  100 + o.setStatus(MesWmOutsourceReceiptStatusEnum.CONFIRMED.getStatus());
  101 + });
  102 + outsourceReceiptMapper.insert(receipt);
  103 + // 准备参数
  104 + Long receiptId = receipt.getId();
  105 + Long lineId = randomLongId();
  106 + Long iqcId = randomLongId();
  107 + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100);
  108 + BigDecimal unqualifiedQuantity = BigDecimal.ZERO; // 没有不合格品
  109 +
  110 + // mock 行查询
  111 + MesWmOutsourceReceiptLineDO line = randomPojo(MesWmOutsourceReceiptLineDO.class, o -> {
  112 + o.setId(lineId);
  113 + o.setReceiptId(receiptId);
  114 + });
  115 + when(outsourceReceiptLineService.getOutsourceReceiptLine(eq(lineId))).thenReturn(line);
  116 +
  117 + // 调用
  118 + outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete(
  119 + receiptId, lineId, iqcId, qualifiedQuantity, unqualifiedQuantity);
  120 +
  121 + // 断言:合格品行更新
  122 + verify(outsourceReceiptLineService).updateOutsourceReceiptLineDO(argThat(updateLine ->
  123 + lineId.equals(updateLine.getId())));
  124 + // 断言:不合格品不应该新增行(数量为 0)
  125 + verify(outsourceReceiptLineService, never()).createOutsourceReceiptLineDO(any());
  126 + // 断言:主表状态更新为待上架
  127 + MesWmOutsourceReceiptDO updatedReceipt = outsourceReceiptMapper.selectById(receiptId);
  128 + assertEquals(MesWmOutsourceReceiptStatusEnum.APPROVING.getStatus(), updatedReceipt.getStatus());
  129 + }
  130 +
  131 + @Test
  132 + public void testApproveOutsourceReceiptWhenIqcComplete_receiptNotExists() {
  133 + // 准备参数
  134 + Long receiptId = randomLongId();
  135 + Long lineId = randomLongId();
  136 + Long iqcId = randomLongId();
  137 + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100);
  138 + BigDecimal unqualifiedQuantity = BigDecimal.ZERO;
  139 +
  140 + // 调用,并断言异常
  141 + assertServiceException(
  142 + () -> outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete(
  143 + receiptId, lineId, iqcId, qualifiedQuantity, unqualifiedQuantity),
  144 + WM_OUTSOURCE_RECEIPT_NOT_EXISTS);
  145 + }
  146 +
  147 + @Test
  148 + public void testApproveOutsourceReceiptWhenIqcComplete_statusNotConfirmed() {
  149 + // mock 数据:插入一条草稿状态的外协入库单(非待检验状态)
  150 + MesWmOutsourceReceiptDO receipt = randomPojo(MesWmOutsourceReceiptDO.class, o -> {
  151 + o.setStatus(MesWmOutsourceReceiptStatusEnum.PREPARE.getStatus());
  152 + });
  153 + outsourceReceiptMapper.insert(receipt);
  154 + // 准备参数
  155 + Long receiptId = receipt.getId();
  156 + Long lineId = randomLongId();
  157 + Long iqcId = randomLongId();
  158 + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100);
  159 + BigDecimal unqualifiedQuantity = BigDecimal.ZERO;
  160 +
  161 + // 调用,并断言异常
  162 + assertServiceException(
  163 + () -> outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete(
  164 + receiptId, lineId, iqcId, qualifiedQuantity, unqualifiedQuantity),
  165 + WM_OUTSOURCE_RECEIPT_STATUS_ERROR);
  166 + }
  167 +
  168 + @Test
  169 + public void testApproveOutsourceReceiptWhenIqcComplete_lineNotExists() {
  170 + // mock 数据:插入一条待检验状态的外协入库单
  171 + MesWmOutsourceReceiptDO receipt = randomPojo(MesWmOutsourceReceiptDO.class, o -> {
  172 + o.setStatus(MesWmOutsourceReceiptStatusEnum.CONFIRMED.getStatus());
  173 + });
  174 + outsourceReceiptMapper.insert(receipt);
  175 + // 准备参数
  176 + Long receiptId = receipt.getId();
  177 + Long lineId = randomLongId();
  178 + Long iqcId = randomLongId();
  179 + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100);
  180 + BigDecimal unqualifiedQuantity = BigDecimal.ZERO;
  181 +
  182 + // mock 行查询返回 null
  183 + when(outsourceReceiptLineService.getOutsourceReceiptLine(eq(lineId))).thenReturn(null);
  184 +
  185 + // 调用,并断言异常
  186 + assertServiceException(
  187 + () -> outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete(
  188 + receiptId, lineId, iqcId, qualifiedQuantity, unqualifiedQuantity),
  189 + WM_OUTSOURCE_RECEIPT_LINE_NOT_EXISTS);
  190 + }
  191 +
  192 +}
... ...
yudao-module-mes/src/test/resources/sql/clean.sql
1 1 DELETE FROM "mes_md_auto_code_rule";
2 2 DELETE FROM "mes_md_auto_code_part";
3 3 DELETE FROM "mes_md_auto_code_record";
  4 +DELETE FROM "mes_wm_arrival_notice";
  5 +DELETE FROM "mes_wm_outsource_receipt";
  6 +DELETE FROM "mes_qc_iqc";
... ...
yudao-module-mes/src/test/resources/sql/create_tables.sql
... ... @@ -63,3 +63,86 @@ CREATE TABLE IF NOT EXISTS "mes_md_auto_code_record" (
63 63 "tenant_id" bigint NOT NULL DEFAULT 0,
64 64 PRIMARY KEY ("id")
65 65 );
  66 +
  67 +-- ----------------------------
  68 +-- MES 到货通知单
  69 +-- ----------------------------
  70 +CREATE TABLE IF NOT EXISTS "mes_wm_arrival_notice" (
  71 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  72 + "code" varchar(64) DEFAULT NULL,
  73 + "name" varchar(255) DEFAULT NULL,
  74 + "purchase_order_code" varchar(64) DEFAULT NULL,
  75 + "vendor_id" bigint DEFAULT NULL,
  76 + "arrival_date" timestamp DEFAULT NULL,
  77 + "contact_name" varchar(64) DEFAULT NULL,
  78 + "contact_telephone" varchar(64) DEFAULT NULL,
  79 + "status" tinyint DEFAULT NULL,
  80 + "remark" varchar(500) DEFAULT NULL,
  81 + "creator" varchar(64) DEFAULT '',
  82 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  83 + "updater" varchar(64) DEFAULT '',
  84 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  85 + "deleted" bit NOT NULL DEFAULT FALSE,
  86 + "tenant_id" bigint NOT NULL DEFAULT 0,
  87 + PRIMARY KEY ("id")
  88 +);
  89 +
  90 +-- ----------------------------
  91 +-- MES 外协入库单
  92 +-- ----------------------------
  93 +CREATE TABLE IF NOT EXISTS "mes_wm_outsource_receipt" (
  94 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  95 + "code" varchar(64) DEFAULT NULL,
  96 + "name" varchar(255) DEFAULT NULL,
  97 + "work_order_id" bigint DEFAULT NULL,
  98 + "vendor_id" bigint DEFAULT NULL,
  99 + "receipt_date" timestamp DEFAULT NULL,
  100 + "status" tinyint DEFAULT NULL,
  101 + "remark" varchar(500) DEFAULT NULL,
  102 + "creator" varchar(64) DEFAULT '',
  103 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  104 + "updater" varchar(64) DEFAULT '',
  105 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  106 + "deleted" bit NOT NULL DEFAULT FALSE,
  107 + "tenant_id" bigint NOT NULL DEFAULT 0,
  108 + PRIMARY KEY ("id")
  109 +);
  110 +
  111 +-- ----------------------------
  112 +-- MES 来料检验单(IQC)
  113 +-- ----------------------------
  114 +CREATE TABLE IF NOT EXISTS "mes_qc_iqc" (
  115 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  116 + "code" varchar(64) DEFAULT NULL,
  117 + "name" varchar(255) DEFAULT NULL,
  118 + "template_id" bigint DEFAULT NULL,
  119 + "source_doc_type" int DEFAULT NULL,
  120 + "source_doc_id" bigint DEFAULT NULL,
  121 + "source_line_id" bigint DEFAULT NULL,
  122 + "vendor_id" bigint DEFAULT NULL,
  123 + "vendor_batch" varchar(64) DEFAULT NULL,
  124 + "item_id" bigint DEFAULT NULL,
  125 + "received_quantity" decimal(24,6) DEFAULT NULL,
  126 + "check_quantity" decimal(24,6) DEFAULT NULL,
  127 + "qualified_quantity" decimal(24,6) DEFAULT NULL,
  128 + "unqualified_quantity" decimal(24,6) DEFAULT NULL,
  129 + "critical_rate" decimal(10,2) DEFAULT NULL,
  130 + "major_rate" decimal(10,2) DEFAULT NULL,
  131 + "minor_rate" decimal(10,2) DEFAULT NULL,
  132 + "critical_quantity" int DEFAULT NULL,
  133 + "major_quantity" int DEFAULT NULL,
  134 + "minor_quantity" int DEFAULT NULL,
  135 + "check_result" tinyint DEFAULT NULL,
  136 + "receive_date" timestamp DEFAULT NULL,
  137 + "inspect_date" timestamp DEFAULT NULL,
  138 + "inspector_user_id" bigint DEFAULT NULL,
  139 + "status" tinyint DEFAULT NULL,
  140 + "remark" varchar(500) DEFAULT NULL,
  141 + "creator" varchar(64) DEFAULT '',
  142 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  143 + "updater" varchar(64) DEFAULT '',
  144 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  145 + "deleted" bit NOT NULL DEFAULT FALSE,
  146 + "tenant_id" bigint NOT NULL DEFAULT 0,
  147 + PRIMARY KEY ("id")
  148 +);
... ...