| | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.qc.ipqc.MesQcIpqcMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; |
| | | import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; |
| | | 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.workstation.MesMdWorkstationService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.ncr.MesQcNcrService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceLineService; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import jakarta.annotation.Resource; |
| | | import org.junit.jupiter.api.Test; |
| | |
| | | import org.springframework.test.context.bean.override.mockito.MockitoBean; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | 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.any; |
| | | import static org.mockito.ArgumentMatchers.eq; |
| | | import static org.mockito.ArgumentMatchers.isNull; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | /** |
| | | * {@link MesQcIpqcServiceImpl} 的单元测试 |
| | | * |
| | | * @author 芋道源码 |
| | | * @author 超级管理员 |
| | | */ |
| | | @Import(MesQcIpqcServiceImpl.class) |
| | | public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { |
| | |
| | | @MockitoBean |
| | | private MesQcTemplateItemService templateItemService; |
| | | @MockitoBean |
| | | private MesQcTemplateService templateService; |
| | | @MockitoBean |
| | | private MesQcIpqcLineService ipqcLineService; |
| | | @MockitoBean |
| | | private MesProWorkOrderService workOrderService; |
| | | @MockitoBean |
| | | private MesMdWorkstationService workstationService; |
| | | @MockitoBean |
| | | private MesMdItemService itemService; |
| | | @MockitoBean |
| | | private MesQcDefectRecordService defectRecordService; |
| | | @MockitoBean |
| | |
| | | @MockitoBean |
| | | private MesProFeedbackService feedbackService; |
| | | @MockitoBean |
| | | private MesProRouteProductService routeProductService; |
| | | @MockitoBean |
| | | private MesProRouteProcessService routeProcessService; |
| | | @MockitoBean |
| | | private MesProTaskService taskService; |
| | | @MockitoBean |
| | | private MesQcIndicatorResultService indicatorResultService; |
| | | @MockitoBean |
| | | private MesWmProductProduceLineService productProduceLineService; |
| | | @MockitoBean |
| | | private MesQcNcrService ncrService; |
| | | @MockitoBean |
| | | private MdmItemApi mdmItemApi; |
| | | |
| | | // ==================== finishIpqc 回写来源单据 ==================== |
| | | |
| | | @Test |
| | | public void testFinishIpqc_writeBack_feedback() { |
| | |
| | | Long sourceLineId = randomLongId(); |
| | | BigDecimal qualifiedQty = BigDecimal.valueOf(80); |
| | | BigDecimal unqualifiedQty = BigDecimal.valueOf(20); |
| | | BigDecimal laborScrapQty = BigDecimal.valueOf(5); |
| | | BigDecimal materialScrapQty = BigDecimal.valueOf(10); |
| | | BigDecimal otherScrapQty = BigDecimal.valueOf(5); |
| | | |
| | | MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { |
| | | o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); |
| | |
| | | o.setSourceLineId(sourceLineId); |
| | | o.setQualifiedQuantity(qualifiedQty); |
| | | o.setUnqualifiedQuantity(unqualifiedQty); |
| | | o.setLaborScrapQuantity(laborScrapQty); |
| | | o.setMaterialScrapQuantity(materialScrapQty); |
| | | o.setOtherScrapQuantity(otherScrapQty); |
| | | o.setLaborScrapQuantity(BigDecimal.valueOf(5)); |
| | | o.setMaterialScrapQuantity(BigDecimal.valueOf(10)); |
| | | o.setOtherScrapQuantity(BigDecimal.valueOf(5)); |
| | | }); |
| | | ipqcMapper.insert(ipqc); |
| | | |
| | |
| | | any(BigDecimal.class), |
| | | any(BigDecimal.class), |
| | | any(BigDecimal.class)); |
| | | // 断言 3:不合格数量 > 0,自动创建 NCR |
| | | verify(ncrService).createNcr(any()); |
| | | } |
| | | |
| | | @Test |
| | |
| | | |
| | | @Test |
| | | public void testFinishIpqc_writeBack_noSourceDoc() { |
| | | // 准备数据:sourceDocType 为空,不需要回写 |
| | | // 准备数据:无来源单据(手工创建的 IPQC),直接完成,不回写 |
| | | MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { |
| | | o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); |
| | | o.setCheckResult(1); |
| | | o.setSourceDocType(null); |
| | | o.setSourceDocId(null); |
| | | o.setSourceLineId(null); |
| | | o.setUnqualifiedQuantity(BigDecimal.ZERO); |
| | | }); |
| | | ipqcMapper.insert(ipqc); |
| | | |
| | |
| | | // 断言 2:不应该调用 feedbackService |
| | | verify(feedbackService, never()).updateProFeedbackWhenIpqcFinish( |
| | | anyLong(), anyLong(), any(), any(), any(), any(), any()); |
| | | } |
| | | |
| | | @Test |
| | | public void testFinishIpqc_writeBack_sourceDocTypeDirtyData() { |
| | | // 准备数据:sourceDocType 非空但 sourceDocId 为空,应该抛异常 |
| | | MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { |
| | | o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); |
| | | o.setCheckResult(1); |
| | | o.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); |
| | | o.setSourceDocId(null); // 脏数据 |
| | | o.setSourceLineId(null); |
| | | }); |
| | | ipqcMapper.insert(ipqc); |
| | | |
| | | // 调用,并断言异常 |
| | | assertThrows(IllegalArgumentException.class, () -> ipqcService.finishIpqc(ipqc.getId())); |
| | | } |
| | | |
| | | @Test |
| | | public void testFinishIpqc_writeBack_unknownSourceDocType() { |
| | | // 准备数据:sourceDocType 无法识别 |
| | | MesQcIpqcDO ipqc = randomPojo(MesQcIpqcDO.class, o -> { |
| | | o.setStatus(MesQcStatusEnum.DRAFT.getStatus()); |
| | | o.setCheckResult(1); |
| | | o.setSourceDocType(99999); // 无法识别的类型 |
| | | o.setSourceDocId(randomLongId()); |
| | | o.setSourceLineId(null); |
| | | }); |
| | | ipqcMapper.insert(ipqc); |
| | | |
| | | // 调用,并断言异常 |
| | | assertThrows(IllegalArgumentException.class, () -> ipqcService.finishIpqc(ipqc.getId())); |
| | | } |
| | | |
| | | @Test |
| | |
| | | assertThrows(Exception.class, () -> ipqcService.finishIpqc(ipqc.getId())); |
| | | } |
| | | |
| | | // ==================== processId 推导行为测试 ==================== |
| | | // ==================== createIpqc 物料 / 批号 / 模板匹配 ==================== |
| | | |
| | | /** |
| | | * 工位有工序 + 该工序在产品工艺路线中 → processId 应该被设置为工位工序 |
| | | * 手工创建:物料来自 MDM,模板按 MDM 物料 ID 匹配;批号、物料均落库 |
| | | */ |
| | | @Test |
| | | public void testCreateIpqc_processId_workstationHasProcess() { |
| | | public void testCreateIpqc_success() { |
| | | // 准备参数 |
| | | Long workstationId = randomLongId(); |
| | | Long workOrderId = randomLongId(); |
| | | Long productId = randomLongId(); |
| | | Long processId = randomLongId(); |
| | | Long routeId = randomLongId(); |
| | | Long itemId = randomLongId(); |
| | | Long templateId = randomLongId(); |
| | | MesQcIpqcSaveReqVO reqVO = buildReqVO("IPQC-TEST-001"); |
| | | reqVO.setItemId(itemId); |
| | | reqVO.setBatchCode("BC20250101"); |
| | | |
| | | MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); |
| | | reqVO.setCode("IPQC-TEST-001"); |
| | | reqVO.setWorkstationId(workstationId); |
| | | reqVO.setWorkOrderId(workOrderId); |
| | | reqVO.setInspectorUserId(randomLongId()); |
| | | |
| | | // mock 工位返回有工序 |
| | | MesMdWorkstationDO workstation = new MesMdWorkstationDO(); |
| | | workstation.setId(workstationId); |
| | | workstation.setProcessId(processId); |
| | | when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); |
| | | // mock 工单 |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | // mock 模板匹配 |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | // mock 工艺路线 |
| | | MesProRouteProductDO routeProduct = new MesProRouteProductDO(); |
| | | routeProduct.setRouteId(routeId); |
| | | when(routeProductService.getRouteProductByItemId(productId)).thenReturn(routeProduct); |
| | | // mock 工序在路线中 |
| | | when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId)) |
| | | .thenReturn(new MesProRouteProcessDO()); |
| | | // mock:按 MDM 物料 ID 匹配到质检方案 |
| | | mockTemplate(templateId); |
| | | |
| | | // 调用 |
| | | Long ipqcId = ipqcService.createIpqc(reqVO); |
| | | |
| | | // 断言:processId 应该是工位工序 |
| | | // 断言 1:批号、物料、方案均落库 |
| | | MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); |
| | | assertEquals(processId, ipqc.getProcessId()); |
| | | assertNotNull(ipqc); |
| | | assertEquals("BC20250101", ipqc.getBatchCode()); |
| | | assertEquals(itemId, ipqc.getItemId()); |
| | | assertEquals(templateId, ipqc.getTemplateId()); |
| | | assertEquals(MesQcStatusEnum.DRAFT.getStatus(), ipqc.getStatus()); |
| | | assertNull(ipqc.getSourceDocCode()); |
| | | |
| | | // 断言 2:模板按 MDM 物料 ID 匹配,不再依赖工单/任务 |
| | | verify(templateItemService).getTemplateItemList( |
| | | eq(itemId), eq(MesQcTypeEnum.IPQC.getType()), isNull(), isNull()); |
| | | // 断言 3:物料存在性校验走 MDM |
| | | verify(mdmItemApi).validateItemExists(itemId); |
| | | } |
| | | |
| | | /** |
| | | * 工位没有工序(processId=null)→ processId 应该留 null(不回退到任务工序) |
| | | * 物料未匹配到任何质检方案 → 创建失败 |
| | | */ |
| | | @Test |
| | | public void testCreateIpqc_processId_workstationNoProcess_shouldBeNull() { |
| | | public void testCreateIpqc_noTemplate() { |
| | | // 准备参数 |
| | | Long workstationId = randomLongId(); |
| | | Long workOrderId = randomLongId(); |
| | | Long productId = randomLongId(); |
| | | Long taskId = randomLongId(); |
| | | Long templateId = randomLongId(); |
| | | MesQcIpqcSaveReqVO reqVO = buildReqVO("IPQC-TEST-002"); |
| | | reqVO.setItemId(randomLongId()); |
| | | |
| | | MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); |
| | | reqVO.setCode("IPQC-TEST-002"); |
| | | reqVO.setWorkstationId(workstationId); |
| | | reqVO.setWorkOrderId(workOrderId); |
| | | reqVO.setTaskId(taskId); // 故意传入 taskId,验证不会回退 |
| | | reqVO.setInspectorUserId(randomLongId()); |
| | | // mock:无匹配方案 |
| | | when(templateItemService.getTemplateItemList(any(), any(), any(), any())) |
| | | .thenReturn(Collections.emptyList()); |
| | | |
| | | // mock 工位没有工序 |
| | | MesMdWorkstationDO workstation = new MesMdWorkstationDO(); |
| | | workstation.setId(workstationId); |
| | | workstation.setProcessId(null); // 无工序 |
| | | when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); |
| | | // mock 工单 |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | // mock 任务 |
| | | MesProTaskDO task = new MesProTaskDO(); |
| | | task.setId(taskId); |
| | | task.setWorkOrderId(workOrderId); |
| | | task.setWorkstationId(workstationId); |
| | | task.setItemId(productId); |
| | | when(taskService.validateTaskNotFinished(taskId)).thenReturn(task); |
| | | // mock 模板匹配 |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | |
| | | // 调用 |
| | | Long ipqcId = ipqcService.createIpqc(reqVO); |
| | | |
| | | // 断言:processId 应该是 null,即使传入了 taskId 也不应该回退 |
| | | MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); |
| | | assertNull(ipqc.getProcessId(), |
| | | "工位无工序时 processId 应为 null,不应回退到任务工序"); |
| | | // 断言:不应该读取任务的 processId |
| | | verify(routeProductService, never()).getRouteProductByItemId(any()); |
| | | // 调用,断言异常 |
| | | ServiceException ex = assertThrows(ServiceException.class, () -> ipqcService.createIpqc(reqVO)); |
| | | assertEquals(QC_NO_TEMPLATE.getCode(), ex.getCode()); |
| | | } |
| | | |
| | | /** |
| | | * 工位有工序但该工序不在产品工艺路线中 → processId 应该留 null |
| | | */ |
| | | @Test |
| | | public void testCreateIpqc_processId_processNotInRoute() { |
| | | // 准备参数 |
| | | Long workstationId = randomLongId(); |
| | | Long workOrderId = randomLongId(); |
| | | Long productId = randomLongId(); |
| | | Long processId = randomLongId(); |
| | | Long routeId = randomLongId(); |
| | | Long templateId = randomLongId(); |
| | | |
| | | MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); |
| | | reqVO.setCode("IPQC-TEST-003"); |
| | | reqVO.setWorkstationId(workstationId); |
| | | reqVO.setWorkOrderId(workOrderId); |
| | | reqVO.setInspectorUserId(randomLongId()); |
| | | |
| | | // mock 工位有工序 |
| | | MesMdWorkstationDO workstation = new MesMdWorkstationDO(); |
| | | workstation.setId(workstationId); |
| | | workstation.setProcessId(processId); |
| | | when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); |
| | | // mock 工单 |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | // mock 模板匹配 |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | // mock 工艺路线存在 |
| | | MesProRouteProductDO routeProduct = new MesProRouteProductDO(); |
| | | routeProduct.setRouteId(routeId); |
| | | when(routeProductService.getRouteProductByItemId(productId)).thenReturn(routeProduct); |
| | | // mock 工序不在路线中 |
| | | when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId)) |
| | | .thenReturn(null); |
| | | |
| | | // 调用 |
| | | Long ipqcId = ipqcService.createIpqc(reqVO); |
| | | |
| | | // 断言:processId 应该是 null |
| | | MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); |
| | | assertNull(ipqc.getProcessId()); |
| | | } |
| | | |
| | | // ==================== sourceLineId 校验测试 ==================== |
| | | // ==================== createIpqc sourceLineId 校验 ==================== |
| | | |
| | | /** |
| | | * sourceDocType=PRO_FEEDBACK 时 sourceLineId 为空 → 创建失败 |
| | |
| | | @Test |
| | | public void testCreateIpqc_sourceLineId_required() { |
| | | // 准备参数 |
| | | Long workstationId = randomLongId(); |
| | | Long workOrderId = randomLongId(); |
| | | Long productId = randomLongId(); |
| | | Long feedbackId = randomLongId(); |
| | | Long templateId = randomLongId(); |
| | | |
| | | MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); |
| | | reqVO.setCode("IPQC-SL-001"); |
| | | reqVO.setWorkstationId(workstationId); |
| | | reqVO.setWorkOrderId(workOrderId); |
| | | reqVO.setInspectorUserId(randomLongId()); |
| | | MesQcIpqcSaveReqVO reqVO = buildReqVO("IPQC-SL-001"); |
| | | reqVO.setItemId(randomLongId()); |
| | | reqVO.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); |
| | | reqVO.setSourceDocId(feedbackId); |
| | | reqVO.setSourceLineId(null); // 不传 sourceLineId |
| | | |
| | | // mock |
| | | MesMdWorkstationDO workstation = new MesMdWorkstationDO(); |
| | | workstation.setId(workstationId); |
| | | workstation.setProcessId(null); |
| | | when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | mockTemplate(randomLongId()); |
| | | when(feedbackService.validateFeedbackExists(feedbackId)).thenReturn(new MesProFeedbackDO()); |
| | | |
| | | // 调用,断言异常 |
| | |
| | | @Test |
| | | public void testCreateIpqc_sourceLineId_notBelong() { |
| | | // 准备参数 |
| | | Long workstationId = randomLongId(); |
| | | Long workOrderId = randomLongId(); |
| | | Long productId = randomLongId(); |
| | | Long feedbackId = randomLongId(); |
| | | Long sourceLineId = randomLongId(); |
| | | Long templateId = randomLongId(); |
| | | |
| | | MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); |
| | | reqVO.setCode("IPQC-SL-002"); |
| | | reqVO.setWorkstationId(workstationId); |
| | | reqVO.setWorkOrderId(workOrderId); |
| | | reqVO.setInspectorUserId(randomLongId()); |
| | | MesQcIpqcSaveReqVO reqVO = buildReqVO("IPQC-SL-002"); |
| | | reqVO.setItemId(randomLongId()); |
| | | reqVO.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); |
| | | reqVO.setSourceDocId(feedbackId); |
| | | reqVO.setSourceLineId(sourceLineId); |
| | | |
| | | // mock |
| | | MesMdWorkstationDO workstation = new MesMdWorkstationDO(); |
| | | workstation.setId(workstationId); |
| | | workstation.setProcessId(null); |
| | | when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | mockTemplate(randomLongId()); |
| | | when(feedbackService.validateFeedbackExists(feedbackId)).thenReturn(new MesProFeedbackDO()); |
| | | // mock:产出行存在但 feedbackId 不匹配 |
| | | MesWmProductProduceLineDO line = new MesWmProductProduceLineDO(); |
| | |
| | | @Test |
| | | public void testCreateIpqc_sourceLineId_notPending() { |
| | | // 准备参数 |
| | | Long workstationId = randomLongId(); |
| | | Long workOrderId = randomLongId(); |
| | | Long productId = randomLongId(); |
| | | Long feedbackId = randomLongId(); |
| | | Long sourceLineId = randomLongId(); |
| | | Long templateId = randomLongId(); |
| | | |
| | | MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); |
| | | reqVO.setCode("IPQC-SL-003"); |
| | | reqVO.setWorkstationId(workstationId); |
| | | reqVO.setWorkOrderId(workOrderId); |
| | | reqVO.setInspectorUserId(randomLongId()); |
| | | MesQcIpqcSaveReqVO reqVO = buildReqVO("IPQC-SL-003"); |
| | | reqVO.setItemId(randomLongId()); |
| | | reqVO.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); |
| | | reqVO.setSourceDocId(feedbackId); |
| | | reqVO.setSourceLineId(sourceLineId); |
| | | |
| | | // mock |
| | | MesMdWorkstationDO workstation = new MesMdWorkstationDO(); |
| | | workstation.setId(workstationId); |
| | | workstation.setProcessId(null); |
| | | when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | mockTemplate(randomLongId()); |
| | | when(feedbackService.validateFeedbackExists(feedbackId)).thenReturn(new MesProFeedbackDO()); |
| | | // mock:产出行存在且归属正确,但不是 PENDING 状态 |
| | | MesWmProductProduceLineDO line = new MesWmProductProduceLineDO(); |
| | |
| | | } |
| | | |
| | | /** |
| | | * sourceLineId 校验全部通过 → 创建成功 |
| | | * sourceLineId 校验全部通过 → 创建成功,来源单据编号回写 |
| | | */ |
| | | @Test |
| | | public void testCreateIpqc_sourceLineId_success() { |
| | | // 准备参数 |
| | | Long workstationId = randomLongId(); |
| | | Long workOrderId = randomLongId(); |
| | | Long productId = randomLongId(); |
| | | Long itemId = randomLongId(); |
| | | Long feedbackId = randomLongId(); |
| | | Long sourceLineId = randomLongId(); |
| | | Long templateId = randomLongId(); |
| | | |
| | | MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); |
| | | reqVO.setCode("IPQC-SL-004"); |
| | | reqVO.setWorkstationId(workstationId); |
| | | reqVO.setWorkOrderId(workOrderId); |
| | | reqVO.setInspectorUserId(randomLongId()); |
| | | MesQcIpqcSaveReqVO reqVO = buildReqVO("IPQC-SL-004"); |
| | | reqVO.setItemId(itemId); |
| | | reqVO.setBatchCode("BC20250101"); |
| | | reqVO.setSourceDocType(MesBizTypeConstants.PRO_FEEDBACK); |
| | | reqVO.setSourceDocId(feedbackId); |
| | | reqVO.setSourceLineId(sourceLineId); |
| | | |
| | | // mock |
| | | MesMdWorkstationDO workstation = new MesMdWorkstationDO(); |
| | | workstation.setId(workstationId); |
| | | workstation.setProcessId(null); |
| | | when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | mockTemplate(templateId); |
| | | MesProFeedbackDO feedback = new MesProFeedbackDO(); |
| | | feedback.setId(feedbackId); |
| | | feedback.setCode("FB-001"); |
| | |
| | | // 调用 |
| | | Long ipqcId = ipqcService.createIpqc(reqVO); |
| | | |
| | | // 断言:创建成功,sourceDocCode 回写 |
| | | // 断言:创建成功,来源单据字段落库 |
| | | MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); |
| | | assertNotNull(ipqc); |
| | | assertEquals(feedbackId, ipqc.getSourceDocId()); |
| | | assertEquals(sourceLineId, ipqc.getSourceLineId()); |
| | | assertEquals("FB-001", ipqc.getSourceDocCode()); |
| | | assertEquals("BC20250101", ipqc.getBatchCode()); |
| | | assertEquals(itemId, ipqc.getItemId()); |
| | | } |
| | | |
| | | /** |
| | | * 未知来源单据类型 → 创建失败 |
| | | */ |
| | | @Test |
| | | public void testCreateIpqc_sourceDocTypeUnknown() { |
| | | // 准备参数 |
| | | MesQcIpqcSaveReqVO reqVO = buildReqVO("IPQC-SL-005"); |
| | | reqVO.setItemId(randomLongId()); |
| | | reqVO.setSourceDocType(99999); // 无法识别的类型 |
| | | reqVO.setSourceDocId(randomLongId()); |
| | | |
| | | // mock |
| | | mockTemplate(randomLongId()); |
| | | |
| | | // 调用,断言异常 |
| | | ServiceException ex = assertThrows(ServiceException.class, () -> ipqcService.createIpqc(reqVO)); |
| | | assertEquals(QC_IPQC_SOURCE_DOC_TYPE_UNKNOWN.getCode(), ex.getCode()); |
| | | } |
| | | |
| | | // ==================== 辅助方法 ==================== |
| | | |
| | | /** |
| | | * 构造一个满足必填项的过程检验单请求(不含来源单据) |
| | | */ |
| | | private MesQcIpqcSaveReqVO buildReqVO(String code) { |
| | | MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); |
| | | reqVO.setCode(code); |
| | | reqVO.setName(code + "-名称"); |
| | | reqVO.setType(1); |
| | | reqVO.setBatchCode("BC-" + code); |
| | | reqVO.setCheckQuantity(BigDecimal.valueOf(100)); |
| | | reqVO.setQualifiedQuantity(BigDecimal.valueOf(95)); |
| | | reqVO.setUnqualifiedQuantity(BigDecimal.valueOf(5)); |
| | | reqVO.setLaborScrapQuantity(BigDecimal.ZERO); |
| | | reqVO.setMaterialScrapQuantity(BigDecimal.ZERO); |
| | | reqVO.setOtherScrapQuantity(BigDecimal.ZERO); |
| | | reqVO.setInspectorUserId(randomLongId()); |
| | | return reqVO; |
| | | } |
| | | |
| | | /** |
| | | * mock:按 MDM 物料 ID + IPQC 类型匹配到指定质检方案 |
| | | */ |
| | | private void mockTemplate(Long templateId) { |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | List<MesQcTemplateItemDO> templateItems = Collections.singletonList(templateItem); |
| | | when(templateItemService.getTemplateItemList(any(), eq(MesQcTypeEnum.IPQC.getType()), isNull(), isNull())) |
| | | .thenReturn(templateItems); |
| | | } |
| | | |
| | | } |