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.qc.defectrecord.MesQcDefectRecordService; import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService; 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.mes.service.qc.indicatorresult.MesQcIndicatorResultService; 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 MesMdItemService itemService; @MockitoBean private MesQcTemplateItemService templateItemService; @MockitoBean private AdminUserApi adminUserApi; @MockitoBean private MesQcIndicatorResultService indicatorResultService; @Test public void testFinishIqc_writeBack_arrivalNotice() { // 准备数据:插入一条草稿状态的 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).updateArrivalNoticeWhenIqcFinish( // eq(sourceDocId), eq(sourceLineId), eq(iqc.getId()), any(BigDecimal.class)); // 断言:不应该调用外协入库单的回写 verify(outsourceReceiptService, never()).updateOutsourceReceiptWhenIqcFinish( anyLong(), anyLong(), anyLong(), any(), any()); } @Test public void testFinishIqc_writeBack_outsourceReceipt() { // 准备数据:插入一条草稿状态的 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).updateOutsourceReceiptWhenIqcFinish( eq(sourceDocId), eq(sourceLineId), eq(iqc.getId()), any(BigDecimal.class), any(BigDecimal.class)); // 断言:不应该调用到货通知单的回写 // verify(arrivalNoticeService, never()).updateArrivalNoticeWhenIqcFinish( // anyLong(), anyLong(), anyLong(), any()); } @Test public void testFinishIqc_writeBack_noSourceDoc() { // 准备数据: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()).updateArrivalNoticeWhenIqcFinish( // anyLong(), anyLong(), anyLong(), any()); verify(outsourceReceiptService, never()).updateOutsourceReceiptWhenIqcFinish( anyLong(), anyLong(), anyLong(), any(), any()); } @Test public void testFinishIqc_writeBack_sourceDocTypeDirtyData() { // 准备数据: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() { // 准备数据: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())); } @Test public void testFinishIqc_checkResultEmpty() { // 准备数据:checkResult 为空,应该报错 MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> { o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); o.setCheckResult(null); // 检测结论为空 o.setSourceDocType(null); }); iqcMapper.insert(iqc); // 调用,应该抛异常(检测结论必填) assertThrows(Exception.class, () -> iqcService.finishIqc(iqc.getId())); } @Test public void testFinishIqc_notDraftStatus() { // 准备数据:已完成状态,不能再次完成 MesQcIqcDO iqc = randomPojo(MesQcIqcDO.class, o -> { o.setStatus(MesQcStatusEnum.FINISHED.getStatus()); o.setCheckResult(1); }); iqcMapper.insert(iqc); // 调用,应该抛异常(不是草稿状态) assertThrows(Exception.class, () -> iqcService.finishIqc(iqc.getId())); } }