| | |
| | | package cn.iocoder.yudao.module.mes.service.pro.feedback; |
| | | |
| | | import cn.hutool.core.collection.ListUtil; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| | | import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemBatchConfigApi; |
| | | import cn.iocoder.yudao.module.mes.api.mps.MesProMpsApi; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackApproveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackQuickCreateReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackQuickCreateRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSubmitReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProTaskStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService; |
| | | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderParamRecordService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.itemconsume.MesWmItemConsumeService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceLineService; |
| | |
| | | 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; |
| | |
| | | private MesWmProductProduceService productProduceService; |
| | | @MockitoBean |
| | | private MesWmProductProduceLineService produceLineService; |
| | | @MockitoBean |
| | | private MesProWorkOrderParamRecordService paramRecordService; |
| | | @MockitoBean |
| | | private MesProWorkOrderBomService workOrderBomService; |
| | | @MockitoBean |
| | | private MesProWorkOrderProcessService workOrderProcessService; |
| | | @MockitoBean |
| | | private MesProProcessService processService; |
| | | @MockitoBean |
| | | private MesProMpsApi mpsApi; |
| | | @MockitoBean |
| | | private MesMdAutoCodeRecordService autoCodeRecordService; |
| | | @MockitoBean |
| | | private MesMdmItemService mdmItemService; |
| | | @MockitoBean |
| | | private MdmItemBatchConfigApi itemBatchConfigApi; |
| | | @MockitoBean |
| | | private BpmProcessInstanceApi processInstanceApi; |
| | | |
| | | @Test |
| | | public void testUpdateProFeedbackWhenIpqcFinish_success_withUnqualified() { |
| | |
| | | verify(productProduceService, never()).generateProductProduce(any(), anyBoolean(), any()); |
| | | } |
| | | |
| | | // ==================== 顺序报工(上一道工序完成后才能报下一道)测试 ==================== |
| | | |
| | | /** |
| | | * 构造"上道工序(prev)+ 当前工序(cur)"的工单工序链(故意乱序传入,验证按 sort 排序后取前驱) |
| | | */ |
| | | private List<MesProWorkOrderProcessDO> buildTwoProcessChain(Long workOrderId, Long prevProcessId, |
| | | Long curProcessId) { |
| | | return ListUtil.of( |
| | | MesProWorkOrderProcessDO.builder().workOrderId(workOrderId).processId(curProcessId).sort(2).build(), |
| | | MesProWorkOrderProcessDO.builder().workOrderId(workOrderId).processId(prevProcessId).sort(1).build()); |
| | | } |
| | | |
| | | @Test |
| | | public void testQuickCreateFeedback_blockedWhenPreviousProcessNotFinished() { |
| | | // 准备:当前任务在第 2 道工序,上道工序任务只做了 60/100 |
| | | Long workOrderId = randomLongId(); |
| | | Long routeId = randomLongId(); |
| | | Long prevProcessId = randomLongId(); |
| | | Long curProcessId = randomLongId(); |
| | | Long curTaskId = randomLongId(); |
| | | |
| | | MesProTaskDO curTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setId(curTaskId); |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setRouteId(routeId); |
| | | o.setProcessId(curProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(100)); |
| | | o.setProducedQuantity(BigDecimal.ZERO); |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | when(taskService.validateTaskNotFinished(curTaskId)).thenReturn(curTask); |
| | | when(workOrderProcessService.getWorkOrderProcessListByWorkOrderId(workOrderId)) |
| | | .thenReturn(buildTwoProcessChain(workOrderId, prevProcessId, curProcessId)); |
| | | MesProTaskDO prevTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setProcessId(prevProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(100)); |
| | | o.setProducedQuantity(BigDecimal.valueOf(60)); |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | when(taskService.getTaskListByWorkOrderIds(Collections.singletonList(workOrderId))) |
| | | .thenReturn(List.of(prevTask)); |
| | | when(processService.getProcess(prevProcessId)).thenReturn( |
| | | randomPojo(MesProProcessDO.class, o -> o.setName("CNC加工"))); |
| | | |
| | | // 调用:应被拦截 |
| | | MesProFeedbackQuickCreateReqVO req = new MesProFeedbackQuickCreateReqVO(); |
| | | req.setTaskId(curTaskId); |
| | | req.setFeedbackQuantity(BigDecimal.TEN); |
| | | ServiceException ex = assertThrows(ServiceException.class, () -> feedbackService.quickCreateFeedback(req)); |
| | | assertTrue(ex.getMessage().contains("尚未报工完成")); |
| | | assertTrue(ex.getMessage().contains("CNC加工")); |
| | | // 未真正生成报工单 |
| | | verify(autoCodeRecordService, never()).generateAutoCode(anyString()); |
| | | } |
| | | |
| | | @Test |
| | | public void testQuickCreateFeedback_blockedWhenPreviousMultiTaskAnyNotFinished() { |
| | | // 准备:上道工序有 2 个任务,一个做满、一个未做满,仍应拦截 |
| | | Long workOrderId = randomLongId(); |
| | | Long routeId = randomLongId(); |
| | | Long prevProcessId = randomLongId(); |
| | | Long curProcessId = randomLongId(); |
| | | Long curTaskId = randomLongId(); |
| | | |
| | | MesProTaskDO curTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setId(curTaskId); |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setRouteId(routeId); |
| | | o.setProcessId(curProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(100)); |
| | | o.setProducedQuantity(BigDecimal.ZERO); |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | when(taskService.validateTaskNotFinished(curTaskId)).thenReturn(curTask); |
| | | when(workOrderProcessService.getWorkOrderProcessListByWorkOrderId(workOrderId)) |
| | | .thenReturn(buildTwoProcessChain(workOrderId, prevProcessId, curProcessId)); |
| | | |
| | | MesProTaskDO finishedTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setProcessId(prevProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(50)); |
| | | o.setProducedQuantity(BigDecimal.valueOf(50)); // 已做满 |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | MesProTaskDO partialTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setProcessId(prevProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(50)); |
| | | o.setProducedQuantity(BigDecimal.valueOf(20)); // 未做满 |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | when(taskService.getTaskListByWorkOrderIds(Collections.singletonList(workOrderId))) |
| | | .thenReturn(ListUtil.of(finishedTask, partialTask)); |
| | | |
| | | MesProFeedbackQuickCreateReqVO req = new MesProFeedbackQuickCreateReqVO(); |
| | | req.setTaskId(curTaskId); |
| | | req.setFeedbackQuantity(BigDecimal.TEN); |
| | | ServiceException ex = assertThrows(ServiceException.class, () -> feedbackService.quickCreateFeedback(req)); |
| | | assertTrue(ex.getMessage().contains("尚未报工完成")); |
| | | } |
| | | |
| | | @Test |
| | | public void testQuickCreateFeedback_allowedWhenPreviousProcessFinished() { |
| | | // 准备:上道工序任务已做满(100/100),当前为第 2 道工序,应放行 |
| | | Long workOrderId = randomLongId(); |
| | | Long routeId = randomLongId(); |
| | | Long prevProcessId = randomLongId(); |
| | | Long curProcessId = randomLongId(); |
| | | Long curTaskId = randomLongId(); |
| | | |
| | | MesProTaskDO curTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setId(curTaskId); |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setRouteId(routeId); |
| | | o.setProcessId(curProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(100)); |
| | | o.setProducedQuantity(BigDecimal.ZERO); |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | when(taskService.validateTaskNotFinished(curTaskId)).thenReturn(curTask); |
| | | when(workOrderProcessService.getWorkOrderProcessListByWorkOrderId(workOrderId)) |
| | | .thenReturn(buildTwoProcessChain(workOrderId, prevProcessId, curProcessId)); |
| | | MesProTaskDO prevTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setProcessId(prevProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(100)); |
| | | o.setProducedQuantity(BigDecimal.valueOf(100)); // 已做满 |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | when(taskService.getTaskListByWorkOrderIds(Collections.singletonList(workOrderId))) |
| | | .thenReturn(List.of(prevTask)); |
| | | |
| | | // 走通后续逻辑的桩 |
| | | when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, curProcessId)) |
| | | .thenReturn(MesProRouteProcessDO.builder().routeId(routeId).processId(curProcessId) |
| | | .keyFlag(false).checkFlag(false).build()); |
| | | when(autoCodeRecordService.generateAutoCode(anyString())).thenReturn("FB-ORDER-1"); |
| | | |
| | | // 调用:应成功生成报工单 |
| | | MesProFeedbackQuickCreateReqVO req = new MesProFeedbackQuickCreateReqVO(); |
| | | req.setTaskId(curTaskId); |
| | | req.setFeedbackQuantity(BigDecimal.TEN); |
| | | MesProFeedbackQuickCreateRespVO resp = feedbackService.quickCreateFeedback(req); |
| | | assertNotNull(resp); |
| | | assertNotNull(resp.getFeedbackId()); |
| | | verify(autoCodeRecordService).generateAutoCode(anyString()); |
| | | } |
| | | |
| | | @Test |
| | | public void testQuickCreateFeedback_allowedWhenPreviousProcessAllCancelled() { |
| | | // 准备:上道工序任务全部取消,视为无前置要求,当前第 2 道工序应放行 |
| | | Long workOrderId = randomLongId(); |
| | | Long routeId = randomLongId(); |
| | | Long prevProcessId = randomLongId(); |
| | | Long curProcessId = randomLongId(); |
| | | Long curTaskId = randomLongId(); |
| | | |
| | | MesProTaskDO curTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setId(curTaskId); |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setRouteId(routeId); |
| | | o.setProcessId(curProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(100)); |
| | | o.setProducedQuantity(BigDecimal.ZERO); |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | when(taskService.validateTaskNotFinished(curTaskId)).thenReturn(curTask); |
| | | when(workOrderProcessService.getWorkOrderProcessListByWorkOrderId(workOrderId)) |
| | | .thenReturn(buildTwoProcessChain(workOrderId, prevProcessId, curProcessId)); |
| | | MesProTaskDO cancelledTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setProcessId(prevProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(100)); |
| | | o.setProducedQuantity(BigDecimal.ZERO); |
| | | o.setStatus(MesProTaskStatusEnum.CANCELED.getStatus()); |
| | | }); |
| | | when(taskService.getTaskListByWorkOrderIds(Collections.singletonList(workOrderId))) |
| | | .thenReturn(List.of(cancelledTask)); |
| | | when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, curProcessId)) |
| | | .thenReturn(MesProRouteProcessDO.builder().routeId(routeId).processId(curProcessId) |
| | | .keyFlag(false).checkFlag(false).build()); |
| | | when(autoCodeRecordService.generateAutoCode(anyString())).thenReturn("FB-ORDER-2"); |
| | | |
| | | MesProFeedbackQuickCreateReqVO req = new MesProFeedbackQuickCreateReqVO(); |
| | | req.setTaskId(curTaskId); |
| | | req.setFeedbackQuantity(BigDecimal.TEN); |
| | | MesProFeedbackQuickCreateRespVO resp = feedbackService.quickCreateFeedback(req); |
| | | assertNotNull(resp); |
| | | assertNotNull(resp.getFeedbackId()); |
| | | } |
| | | |
| | | @Test |
| | | public void testQuickCreateFeedback_allowedWhenCurrentIsFirstProcess() { |
| | | // 准备:当前任务就是第 1 道工序(链中只有一道工序),无前置,应放行 |
| | | Long workOrderId = randomLongId(); |
| | | Long routeId = randomLongId(); |
| | | Long curProcessId = randomLongId(); |
| | | Long curTaskId = randomLongId(); |
| | | |
| | | MesProTaskDO curTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setId(curTaskId); |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setRouteId(routeId); |
| | | o.setProcessId(curProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(100)); |
| | | o.setProducedQuantity(BigDecimal.ZERO); |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | when(taskService.validateTaskNotFinished(curTaskId)).thenReturn(curTask); |
| | | when(workOrderProcessService.getWorkOrderProcessListByWorkOrderId(workOrderId)) |
| | | .thenReturn(ListUtil.of(MesProWorkOrderProcessDO.builder() |
| | | .workOrderId(workOrderId).processId(curProcessId).sort(1).build())); |
| | | when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, curProcessId)) |
| | | .thenReturn(MesProRouteProcessDO.builder().routeId(routeId).processId(curProcessId) |
| | | .keyFlag(false).checkFlag(false).build()); |
| | | when(autoCodeRecordService.generateAutoCode(anyString())).thenReturn("FB-ORDER-3"); |
| | | |
| | | MesProFeedbackQuickCreateReqVO req = new MesProFeedbackQuickCreateReqVO(); |
| | | req.setTaskId(curTaskId); |
| | | req.setFeedbackQuantity(BigDecimal.TEN); |
| | | MesProFeedbackQuickCreateRespVO resp = feedbackService.quickCreateFeedback(req); |
| | | assertNotNull(resp); |
| | | assertNotNull(resp.getFeedbackId()); |
| | | } |
| | | |
| | | @Test |
| | | public void testSubmitFeedback_blockedWhenPreviousProcessNotFinished() { |
| | | // 准备:已存在草稿报工单(第 2 道工序),上道工序未做满,提交应被拦截 |
| | | Long workOrderId = randomLongId(); |
| | | Long routeId = randomLongId(); |
| | | Long prevProcessId = randomLongId(); |
| | | Long curProcessId = randomLongId(); |
| | | Long curTaskId = randomLongId(); |
| | | |
| | | MesProFeedbackDO feedback = randomPojo(MesProFeedbackDO.class, o -> { |
| | | o.setStatus(MesProFeedbackStatusEnum.PREPARE.getStatus()); |
| | | o.setTaskId(curTaskId); |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setRouteId(routeId); |
| | | o.setProcessId(curProcessId); |
| | | }); |
| | | feedbackMapper.insert(feedback); |
| | | |
| | | MesProTaskDO curTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setId(curTaskId); |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setRouteId(routeId); |
| | | o.setProcessId(curProcessId); |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | when(taskService.getTask(curTaskId)).thenReturn(curTask); |
| | | when(workOrderProcessService.getWorkOrderProcessListByWorkOrderId(workOrderId)) |
| | | .thenReturn(buildTwoProcessChain(workOrderId, prevProcessId, curProcessId)); |
| | | MesProTaskDO prevTask = randomPojo(MesProTaskDO.class, o -> { |
| | | o.setWorkOrderId(workOrderId); |
| | | o.setProcessId(prevProcessId); |
| | | o.setQuantity(BigDecimal.valueOf(100)); |
| | | o.setProducedQuantity(BigDecimal.valueOf(30)); |
| | | o.setStatus(MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | }); |
| | | when(taskService.getTaskListByWorkOrderIds(Collections.singletonList(workOrderId))) |
| | | .thenReturn(List.of(prevTask)); |
| | | |
| | | MesProFeedbackSubmitReqVO req = new MesProFeedbackSubmitReqVO(); |
| | | req.setId(feedback.getId()); |
| | | ServiceException ex = assertThrows(ServiceException.class, () -> feedbackService.submitFeedback(req)); |
| | | assertTrue(ex.getMessage().contains("尚未报工完成")); |
| | | } |
| | | |
| | | } |