From dbccfbab8e7448b2fd8ff68f520d20e849e766d9 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 24 Mar 2026 01:00:15 +0800 Subject: [PATCH] ✅ test(iqc): 添加 IQC 服务单元测试,验证状态更新逻辑 --- yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImplTest.java | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImplTest.java | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImplTest.java | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-mes/src/test/resources/sql/clean.sql | 3 +++ yudao-module-mes/src/test/resources/sql/create_tables.sql | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 603 insertions(+), 0 deletions(-) create mode 100644 yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImplTest.java create mode 100644 yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImplTest.java create mode 100644 yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImplTest.java diff --git a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImplTest.java b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImplTest.java new file mode 100644 index 0000000..36656e6 --- /dev/null +++ b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImplTest.java @@ -0,0 +1,176 @@ +package cn.iocoder.yudao.module.mes.service.qc.iqc; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO; +import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcMapper; +import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; +import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum; +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; +import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService; +import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService; +import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateDetailService; +import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService; +import cn.iocoder.yudao.module.mes.service.wm.outsourcereceipt.MesWmOutsourceReceiptService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import jakarta.annotation.Resource; + +import java.math.BigDecimal; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link MesQcIqcServiceImpl} 的单元测试 + * + * @author 芋道源码 + */ +@Import(MesQcIqcServiceImpl.class) +public class MesQcIqcServiceImplTest extends BaseDbUnitTest { + + @Resource + private MesQcIqcServiceImpl iqcService; + + @Resource + private MesQcIqcMapper iqcMapper; + + @MockitoBean + private MesWmArrivalNoticeService arrivalNoticeService; + @MockitoBean + private MesWmOutsourceReceiptService outsourceReceiptService; + @MockitoBean + private MesQcIqcLineService iqcLineService; + @MockitoBean + private MesQcDefectRecordService defectRecordService; + @MockitoBean + private MesMdVendorService vendorService; + @MockitoBean + private MesMdItemService itemService; + @MockitoBean + private MesQcTemplateDetailService templateDetailService; + @MockitoBean + private AdminUserApi adminUserApi; + + @Test + public void testFinishIqc_writeBack_arrivalNotice() { + // mock 数据:插入一条草稿状态的 IQC 单,来源为到货通知单 + Long sourceDocId = randomLongId(); + Long sourceLineId = randomLongId(); + MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> { + o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); + o.setCheckResult(1); // 非空,满足 finishIqc 的校验 + o.setSourceDocType(MesBizTypeConstants.WM_ARRIVAL_NOTICE); + o.setSourceDocId(sourceDocId); + o.setSourceLineId(sourceLineId); + o.setQualifiedQuantity(BigDecimal.valueOf(100)); + o.setUnqualifiedQuantity(BigDecimal.ZERO); + }); + iqcMapper.insert(iqc); + + // 调用 + iqcService.finishIqc(iqc.getId()); + + // 断言:验证状态更新为已完成 + MesQcIqcDO updatedIqc = iqcMapper.selectById(iqc.getId()); + assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedIqc.getStatus()); + // 断言:验证回写到货通知单(参数使用 any 避免 BigDecimal 精度问题) + verify(arrivalNoticeService).approveArrivalNoticeWhenIqcComplete( + eq(sourceDocId), eq(sourceLineId), eq(iqc.getId()), any(BigDecimal.class)); + // 断言:不应该调用外协入库单的回写 + verify(outsourceReceiptService, never()).approveOutsourceReceiptWhenIqcComplete( + anyLong(), anyLong(), anyLong(), any(), any()); + } + + @Test + public void testFinishIqc_writeBack_outsourceReceipt() { + // mock 数据:插入一条草稿状态的 IQC 单,来源为外协入库单 + Long sourceDocId = randomLongId(); + Long sourceLineId = randomLongId(); + MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> { + o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); + o.setCheckResult(1); + o.setSourceDocType(MesBizTypeConstants.WM_OUTSOURCE_RECPT); + o.setSourceDocId(sourceDocId); + o.setSourceLineId(sourceLineId); + o.setQualifiedQuantity(BigDecimal.valueOf(80)); + o.setUnqualifiedQuantity(BigDecimal.valueOf(20)); + }); + iqcMapper.insert(iqc); + + // 调用 + iqcService.finishIqc(iqc.getId()); + + // 断言:验证状态更新为已完成 + MesQcIqcDO updatedIqc = iqcMapper.selectById(iqc.getId()); + assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedIqc.getStatus()); + // 断言:验证回写外协入库单(参数使用 any 避免 BigDecimal 精度问题) + verify(outsourceReceiptService).approveOutsourceReceiptWhenIqcComplete( + eq(sourceDocId), eq(sourceLineId), eq(iqc.getId()), any(BigDecimal.class), any(BigDecimal.class)); + // 断言:不应该调用到货通知单的回写 + verify(arrivalNoticeService, never()).approveArrivalNoticeWhenIqcComplete( + anyLong(), anyLong(), anyLong(), any()); + } + + @Test + public void testFinishIqc_writeBack_noSourceDoc() { + // mock 数据:sourceDocType 为空,不需要回写 + MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> { + o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); + o.setCheckResult(1); + o.setSourceDocType(null); + o.setSourceDocId(null); + o.setSourceLineId(null); + }); + iqcMapper.insert(iqc); + + // 调用 + iqcService.finishIqc(iqc.getId()); + + // 断言:验证状态更新为已完成 + MesQcIqcDO updatedIqc = iqcMapper.selectById(iqc.getId()); + assertEquals(MesQcStatusEnum.FINISHED.getStatus(), updatedIqc.getStatus()); + // 断言:不应该调用任何回写 + verify(arrivalNoticeService, never()).approveArrivalNoticeWhenIqcComplete( + anyLong(), anyLong(), anyLong(), any()); + verify(outsourceReceiptService, never()).approveOutsourceReceiptWhenIqcComplete( + anyLong(), anyLong(), anyLong(), any(), any()); + } + + @Test + public void testFinishIqc_writeBack_sourceDocTypeDirtyData() { + // mock 数据:sourceDocType 非空但 sourceLineId 为空,应该抛异常 + MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> { + o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); + o.setCheckResult(1); + o.setSourceDocType(MesBizTypeConstants.WM_ARRIVAL_NOTICE); + o.setSourceDocId(randomLongId()); + o.setSourceLineId(null); // 脏数据 + }); + iqcMapper.insert(iqc); + + // 调用,并断言异常 + assertThrows(IllegalArgumentException.class, () -> iqcService.finishIqc(iqc.getId())); + } + + @Test + public void testFinishIqc_writeBack_unknownSourceDocType() { + // mock 数据:sourceDocType 无法识别 + MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> { + o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); + o.setCheckResult(1); + o.setSourceDocType(99999); // 无法识别的类型 + o.setSourceDocId(randomLongId()); + o.setSourceLineId(randomLongId()); + }); + iqcMapper.insert(iqc); + + // 调用,并断言异常 + assertThrows(IllegalArgumentException.class, () -> iqcService.finishIqc(iqc.getId())); + } + +} diff --git a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImplTest.java b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImplTest.java new file mode 100644 index 0000000..b43b57f --- /dev/null +++ b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImplTest.java @@ -0,0 +1,149 @@ +package cn.iocoder.yudao.module.mes.service.wm.arrivalnotice; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO; +import cn.iocoder.yudao.module.mes.dal.mysql.wm.arrivalnotice.MesWmArrivalNoticeMapper; +import cn.iocoder.yudao.module.mes.enums.wm.MesWmArrivalNoticeStatusEnum; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import jakarta.annotation.Resource; + +import java.math.BigDecimal; +import java.util.Arrays; + +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.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +/** + * {@link MesWmArrivalNoticeServiceImpl#approveArrivalNoticeWhenIqcComplete} 的单元测试 + * + * @author 芋道源码 + */ +@Import(MesWmArrivalNoticeServiceImpl.class) +public class MesWmArrivalNoticeServiceImplTest extends BaseDbUnitTest { + + @Resource + private MesWmArrivalNoticeServiceImpl arrivalNoticeService; + + @Resource + private MesWmArrivalNoticeMapper arrivalNoticeMapper; + + @MockitoBean + private MesWmArrivalNoticeLineService arrivalNoticeLineService; + + @Test + public void testApproveArrivalNoticeWhenIqcComplete_success_allLinesChecked() { + // mock 数据:插入一条待质检的到货通知单 + MesWmArrivalNoticeDO notice = randomPojo(MesWmArrivalNoticeDO.class, o -> { + o.setStatus(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus()); + }); + arrivalNoticeMapper.insert(notice); + // 准备参数 + Long noticeId = notice.getId(); + Long lineId = randomLongId(); + Long iqcId = randomLongId(); + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100); + + // mock 行服务方法:所有需检行都已完成(iqcId 不为空) + MesWmArrivalNoticeLineDO line1 = randomPojo(MesWmArrivalNoticeLineDO.class, o -> { + o.setNoticeId(noticeId); + o.setIqcCheckFlag(true); + o.setIqcId(iqcId); // 已检完 + }); + MesWmArrivalNoticeLineDO line2 = randomPojo(MesWmArrivalNoticeLineDO.class, o -> { + o.setNoticeId(noticeId); + o.setIqcCheckFlag(false); // 不需要检验 + o.setIqcId(null); + }); + when(arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(eq(noticeId))) + .thenReturn(Arrays.asList(line1, line2)); + + // 调用 + arrivalNoticeService.approveArrivalNoticeWhenIqcComplete(noticeId, lineId, iqcId, qualifiedQuantity); + + // 断言:行回写被调用 + verify(arrivalNoticeLineService).updateByIqcComplete(eq(lineId), eq(iqcId), eq(qualifiedQuantity)); + // 断言:主表状态更新为待入库 + MesWmArrivalNoticeDO updatedNotice = arrivalNoticeMapper.selectById(noticeId); + assertEquals(MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus(), updatedNotice.getStatus()); + } + + @Test + public void testApproveArrivalNoticeWhenIqcComplete_success_hasUncheckedLines() { + // mock 数据:插入一条待质检的到货通知单 + MesWmArrivalNoticeDO notice = randomPojo(MesWmArrivalNoticeDO.class, o -> { + o.setStatus(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus()); + }); + arrivalNoticeMapper.insert(notice); + // 准备参数 + Long noticeId = notice.getId(); + Long lineId = randomLongId(); + Long iqcId = randomLongId(); + BigDecimal qualifiedQuantity = BigDecimal.valueOf(50); + + // mock 行服务方法:还有未完成的需检行 + MesWmArrivalNoticeLineDO line1 = randomPojo(MesWmArrivalNoticeLineDO.class, o -> { + o.setNoticeId(noticeId); + o.setIqcCheckFlag(true); + o.setIqcId(iqcId); // 已检完 + }); + MesWmArrivalNoticeLineDO line2 = randomPojo(MesWmArrivalNoticeLineDO.class, o -> { + o.setNoticeId(noticeId); + o.setIqcCheckFlag(true); + o.setIqcId(null); // 还未检验 + }); + when(arrivalNoticeLineService.getArrivalNoticeLineListByNoticeId(eq(noticeId))) + .thenReturn(Arrays.asList(line1, line2)); + + // 调用 + arrivalNoticeService.approveArrivalNoticeWhenIqcComplete(noticeId, lineId, iqcId, qualifiedQuantity); + + // 断言:行回写被调用 + verify(arrivalNoticeLineService).updateByIqcComplete(eq(lineId), eq(iqcId), eq(qualifiedQuantity)); + // 断言:主表状态不变,仍然是待质检 + MesWmArrivalNoticeDO updatedNotice = arrivalNoticeMapper.selectById(noticeId); + assertEquals(MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus(), updatedNotice.getStatus()); + } + + @Test + public void testApproveArrivalNoticeWhenIqcComplete_noticeNotExists() { + // 准备参数 + Long noticeId = randomLongId(); + Long lineId = randomLongId(); + Long iqcId = randomLongId(); + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100); + + // 调用,并断言异常 + assertServiceException( + () -> arrivalNoticeService.approveArrivalNoticeWhenIqcComplete(noticeId, lineId, iqcId, qualifiedQuantity), + WM_ARRIVAL_NOTICE_NOT_EXISTS); + } + + @Test + public void testApproveArrivalNoticeWhenIqcComplete_statusNotPendingQc() { + // mock 数据:插入一条草稿状态的到货通知单(非待质检) + MesWmArrivalNoticeDO notice = randomPojo(MesWmArrivalNoticeDO.class, o -> { + o.setStatus(MesWmArrivalNoticeStatusEnum.PREPARE.getStatus()); + }); + arrivalNoticeMapper.insert(notice); + // 准备参数 + Long noticeId = notice.getId(); + Long lineId = randomLongId(); + Long iqcId = randomLongId(); + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100); + + // 调用,并断言异常 + assertServiceException( + () -> arrivalNoticeService.approveArrivalNoticeWhenIqcComplete(noticeId, lineId, iqcId, qualifiedQuantity), + WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_QC); + } + +} diff --git a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImplTest.java b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImplTest.java new file mode 100644 index 0000000..7275e72 --- /dev/null +++ b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImplTest.java @@ -0,0 +1,192 @@ +package cn.iocoder.yudao.module.mes.service.wm.outsourcereceipt; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptLineDO; +import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourcereceipt.MesWmOutsourceReceiptMapper; +import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutsourceReceiptStatusEnum; +import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; +import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService; +import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import jakarta.annotation.Resource; + +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.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +/** + * {@link MesWmOutsourceReceiptServiceImpl#approveOutsourceReceiptWhenIqcComplete} 的单元测试 + * + * @author 芋道源码 + */ +@Import(MesWmOutsourceReceiptServiceImpl.class) +public class MesWmOutsourceReceiptServiceImplTest extends BaseDbUnitTest { + + @Resource + private MesWmOutsourceReceiptServiceImpl outsourceReceiptService; + + @Resource + private MesWmOutsourceReceiptMapper outsourceReceiptMapper; + + @MockitoBean + private MesWmOutsourceReceiptLineService outsourceReceiptLineService; + @MockitoBean + private MesWmOutsourceReceiptDetailService outsourceReceiptDetailService; + @MockitoBean + private MesWmTransactionService wmTransactionService; + @MockitoBean + private MesMdVendorService vendorService; + @MockitoBean + private MesMdItemService itemService; + + @Test + public void testApproveOutsourceReceiptWhenIqcComplete_success_bothQualifiedAndUnqualified() { + // mock 数据:插入一条待检验状态的外协入库单 + MesWmOutsourceReceiptDO receipt = randomPojo(MesWmOutsourceReceiptDO.class, o -> { + o.setStatus(MesWmOutsourceReceiptStatusEnum.CONFIRMED.getStatus()); + }); + outsourceReceiptMapper.insert(receipt); + // 准备参数 + Long receiptId = receipt.getId(); + Long lineId = randomLongId(); + Long iqcId = randomLongId(); + BigDecimal qualifiedQuantity = BigDecimal.valueOf(80); + BigDecimal unqualifiedQuantity = BigDecimal.valueOf(20); + + // mock 行查询 + MesWmOutsourceReceiptLineDO line = randomPojo(MesWmOutsourceReceiptLineDO.class, o -> { + o.setId(lineId); + o.setReceiptId(receiptId); + o.setQuantity(BigDecimal.valueOf(100)); + }); + when(outsourceReceiptLineService.getOutsourceReceiptLine(eq(lineId))).thenReturn(line); + + // 调用 + outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete( + receiptId, lineId, iqcId, qualifiedQuantity, unqualifiedQuantity); + + // 断言:合格品行更新 + verify(outsourceReceiptLineService).updateOutsourceReceiptLineDO(argThat(updateLine -> + lineId.equals(updateLine.getId()) + && MesWmQualityStatusEnum.PASS.getStatus().equals(updateLine.getQualityStatus()) + && iqcId.equals(updateLine.getIqcId()) + && qualifiedQuantity.equals(updateLine.getQuantity()))); + // 断言:不合格品新增行 + verify(outsourceReceiptLineService).createOutsourceReceiptLineDO(argThat(newLine -> + newLine.getId() == null + && MesWmQualityStatusEnum.FAIL.getStatus().equals(newLine.getQualityStatus()) + && iqcId.equals(newLine.getIqcId()) + && unqualifiedQuantity.equals(newLine.getQuantity()))); + // 断言:主表状态更新为待上架 + MesWmOutsourceReceiptDO updatedReceipt = outsourceReceiptMapper.selectById(receiptId); + assertEquals(MesWmOutsourceReceiptStatusEnum.APPROVING.getStatus(), updatedReceipt.getStatus()); + } + + @Test + public void testApproveOutsourceReceiptWhenIqcComplete_success_onlyQualified() { + // mock 数据:插入一条待检验状态的外协入库单 + MesWmOutsourceReceiptDO receipt = randomPojo(MesWmOutsourceReceiptDO.class, o -> { + o.setStatus(MesWmOutsourceReceiptStatusEnum.CONFIRMED.getStatus()); + }); + outsourceReceiptMapper.insert(receipt); + // 准备参数 + Long receiptId = receipt.getId(); + Long lineId = randomLongId(); + Long iqcId = randomLongId(); + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100); + BigDecimal unqualifiedQuantity = BigDecimal.ZERO; // 没有不合格品 + + // mock 行查询 + MesWmOutsourceReceiptLineDO line = randomPojo(MesWmOutsourceReceiptLineDO.class, o -> { + o.setId(lineId); + o.setReceiptId(receiptId); + }); + when(outsourceReceiptLineService.getOutsourceReceiptLine(eq(lineId))).thenReturn(line); + + // 调用 + outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete( + receiptId, lineId, iqcId, qualifiedQuantity, unqualifiedQuantity); + + // 断言:合格品行更新 + verify(outsourceReceiptLineService).updateOutsourceReceiptLineDO(argThat(updateLine -> + lineId.equals(updateLine.getId()))); + // 断言:不合格品不应该新增行(数量为 0) + verify(outsourceReceiptLineService, never()).createOutsourceReceiptLineDO(any()); + // 断言:主表状态更新为待上架 + MesWmOutsourceReceiptDO updatedReceipt = outsourceReceiptMapper.selectById(receiptId); + assertEquals(MesWmOutsourceReceiptStatusEnum.APPROVING.getStatus(), updatedReceipt.getStatus()); + } + + @Test + public void testApproveOutsourceReceiptWhenIqcComplete_receiptNotExists() { + // 准备参数 + Long receiptId = randomLongId(); + Long lineId = randomLongId(); + Long iqcId = randomLongId(); + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100); + BigDecimal unqualifiedQuantity = BigDecimal.ZERO; + + // 调用,并断言异常 + assertServiceException( + () -> outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete( + receiptId, lineId, iqcId, qualifiedQuantity, unqualifiedQuantity), + WM_OUTSOURCE_RECEIPT_NOT_EXISTS); + } + + @Test + public void testApproveOutsourceReceiptWhenIqcComplete_statusNotConfirmed() { + // mock 数据:插入一条草稿状态的外协入库单(非待检验状态) + MesWmOutsourceReceiptDO receipt = randomPojo(MesWmOutsourceReceiptDO.class, o -> { + o.setStatus(MesWmOutsourceReceiptStatusEnum.PREPARE.getStatus()); + }); + outsourceReceiptMapper.insert(receipt); + // 准备参数 + Long receiptId = receipt.getId(); + Long lineId = randomLongId(); + Long iqcId = randomLongId(); + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100); + BigDecimal unqualifiedQuantity = BigDecimal.ZERO; + + // 调用,并断言异常 + assertServiceException( + () -> outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete( + receiptId, lineId, iqcId, qualifiedQuantity, unqualifiedQuantity), + WM_OUTSOURCE_RECEIPT_STATUS_ERROR); + } + + @Test + public void testApproveOutsourceReceiptWhenIqcComplete_lineNotExists() { + // mock 数据:插入一条待检验状态的外协入库单 + MesWmOutsourceReceiptDO receipt = randomPojo(MesWmOutsourceReceiptDO.class, o -> { + o.setStatus(MesWmOutsourceReceiptStatusEnum.CONFIRMED.getStatus()); + }); + outsourceReceiptMapper.insert(receipt); + // 准备参数 + Long receiptId = receipt.getId(); + Long lineId = randomLongId(); + Long iqcId = randomLongId(); + BigDecimal qualifiedQuantity = BigDecimal.valueOf(100); + BigDecimal unqualifiedQuantity = BigDecimal.ZERO; + + // mock 行查询返回 null + when(outsourceReceiptLineService.getOutsourceReceiptLine(eq(lineId))).thenReturn(null); + + // 调用,并断言异常 + assertServiceException( + () -> outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete( + receiptId, lineId, iqcId, qualifiedQuantity, unqualifiedQuantity), + WM_OUTSOURCE_RECEIPT_LINE_NOT_EXISTS); + } + +} diff --git a/yudao-module-mes/src/test/resources/sql/clean.sql b/yudao-module-mes/src/test/resources/sql/clean.sql index 175f73c..5ac2653 100644 --- a/yudao-module-mes/src/test/resources/sql/clean.sql +++ b/yudao-module-mes/src/test/resources/sql/clean.sql @@ -1,3 +1,6 @@ DELETE FROM "mes_md_auto_code_rule"; DELETE FROM "mes_md_auto_code_part"; DELETE FROM "mes_md_auto_code_record"; +DELETE FROM "mes_wm_arrival_notice"; +DELETE FROM "mes_wm_outsource_receipt"; +DELETE FROM "mes_qc_iqc"; diff --git a/yudao-module-mes/src/test/resources/sql/create_tables.sql b/yudao-module-mes/src/test/resources/sql/create_tables.sql index 6dd7bd2..3e38115 100644 --- a/yudao-module-mes/src/test/resources/sql/create_tables.sql +++ b/yudao-module-mes/src/test/resources/sql/create_tables.sql @@ -63,3 +63,86 @@ CREATE TABLE IF NOT EXISTS "mes_md_auto_code_record" ( "tenant_id" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("id") ); + +-- ---------------------------- +-- MES 到货通知单 +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "mes_wm_arrival_notice" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(64) DEFAULT NULL, + "name" varchar(255) DEFAULT NULL, + "purchase_order_code" varchar(64) DEFAULT NULL, + "vendor_id" bigint DEFAULT NULL, + "arrival_date" timestamp DEFAULT NULL, + "contact_name" varchar(64) DEFAULT NULL, + "contact_telephone" varchar(64) DEFAULT NULL, + "status" tinyint DEFAULT NULL, + "remark" varchar(500) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT 0, + PRIMARY KEY ("id") +); + +-- ---------------------------- +-- MES 外协入库单 +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "mes_wm_outsource_receipt" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(64) DEFAULT NULL, + "name" varchar(255) DEFAULT NULL, + "work_order_id" bigint DEFAULT NULL, + "vendor_id" bigint DEFAULT NULL, + "receipt_date" timestamp DEFAULT NULL, + "status" tinyint DEFAULT NULL, + "remark" varchar(500) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT 0, + PRIMARY KEY ("id") +); + +-- ---------------------------- +-- MES 来料检验单(IQC) +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "mes_qc_iqc" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(64) DEFAULT NULL, + "name" varchar(255) DEFAULT NULL, + "template_id" bigint DEFAULT NULL, + "source_doc_type" int DEFAULT NULL, + "source_doc_id" bigint DEFAULT NULL, + "source_line_id" bigint DEFAULT NULL, + "vendor_id" bigint DEFAULT NULL, + "vendor_batch" varchar(64) DEFAULT NULL, + "item_id" bigint DEFAULT NULL, + "received_quantity" decimal(24,6) DEFAULT NULL, + "check_quantity" decimal(24,6) DEFAULT NULL, + "qualified_quantity" decimal(24,6) DEFAULT NULL, + "unqualified_quantity" decimal(24,6) DEFAULT NULL, + "critical_rate" decimal(10,2) DEFAULT NULL, + "major_rate" decimal(10,2) DEFAULT NULL, + "minor_rate" decimal(10,2) DEFAULT NULL, + "critical_quantity" int DEFAULT NULL, + "major_quantity" int DEFAULT NULL, + "minor_quantity" int DEFAULT NULL, + "check_result" tinyint DEFAULT NULL, + "receive_date" timestamp DEFAULT NULL, + "inspect_date" timestamp DEFAULT NULL, + "inspector_user_id" bigint DEFAULT NULL, + "status" tinyint DEFAULT NULL, + "remark" varchar(500) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT 0, + PRIMARY KEY ("id") +); -- libgit2 0.21.4