Commit 4d0918398760814cb46b43572c9954c52f1de2f2

Authored by YunaiV
1 parent b690a596

✨ feat(service): 增强物料消耗记录生成逻辑

新增对工序路由存在性验证,避免在生成物料消耗记录时出现无效路由导致的错误。更新了相关的异常处理逻辑,并调整了注释以提高代码可读性。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java
@@ -16,6 +16,7 @@ import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; @@ -16,6 +16,7 @@ import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
16 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemConsumeStatusEnum; 16 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemConsumeStatusEnum;
17 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; 17 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
18 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService; 18 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
  19 +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService;
19 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; 20 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
20 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; 21 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
21 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; 22 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
@@ -31,7 +32,9 @@ import java.time.LocalDateTime; @@ -31,7 +32,9 @@ import java.time.LocalDateTime;
31 import java.util.ArrayList; 32 import java.util.ArrayList;
32 import java.util.List; 33 import java.util.List;
33 34
  35 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
34 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  37 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_FEEDBACK_ROUTE_PROCESS_INVALID;
35 38
36 /** 39 /**
37 * MES 物料消耗记录 Service 实现类 40 * MES 物料消耗记录 Service 实现类
@@ -51,6 +54,8 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService { @@ -51,6 +54,8 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService {
51 @Resource 54 @Resource
52 private MesProRouteProductBomService routeProductBomService; 55 private MesProRouteProductBomService routeProductBomService;
53 @Resource 56 @Resource
  57 + private MesProRouteService routeService;
  58 + @Resource
54 private MesWmTransactionService wmTransactionService; 59 private MesWmTransactionService wmTransactionService;
55 @Resource 60 @Resource
56 private MesWmWarehouseService warehouseService; 61 private MesWmWarehouseService warehouseService;
@@ -63,34 +68,37 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService { @@ -63,34 +68,37 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService {
63 68
64 @Override 69 @Override
65 public MesWmItemConsumeDO generateItemConsume(MesProFeedbackDO feedback) { 70 public MesWmItemConsumeDO generateItemConsume(MesProFeedbackDO feedback) {
66 - // 1. 查询当前工序的 BOM 物料配置 71 + // 1.1 避免 routeId 等为 null 时 getRouteProductBomList 因 eqIfPresent 放宽条件导致误查
  72 + if (feedback.getRouteId() == null || feedback.getProcessId() == null || feedback.getItemId() == null) {
  73 + throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
  74 + }
  75 + routeService.validateRouteExists(feedback.getRouteId());
  76 + // 1.2 查询当前工序的 BOM 物料配置
67 List<MesProRouteProductBomDO> boms = routeProductBomService.getRouteProductBomList( 77 List<MesProRouteProductBomDO> boms = routeProductBomService.getRouteProductBomList(
68 feedback.getRouteId(), feedback.getProcessId(), feedback.getItemId()); 78 feedback.getRouteId(), feedback.getProcessId(), feedback.getItemId());
69 if (CollUtil.isEmpty(boms)) { 79 if (CollUtil.isEmpty(boms)) {
70 return null; 80 return null;
71 } 81 }
72 -  
73 - // 2. 获取虚拟线边库信息 82 + // 1.3 获取虚拟线边库信息
74 MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); 83 MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
75 MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode( 84 MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(
76 MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION); 85 MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
77 MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA); 86 MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
78 87
79 - // 3. 生成消耗单头 88 + // 2.1 生成消耗单头
80 MesWmItemConsumeDO consume = MesWmItemConsumeDO.builder() 89 MesWmItemConsumeDO consume = MesWmItemConsumeDO.builder()
81 .workOrderId(feedback.getWorkOrderId()).taskId(feedback.getTaskId()) 90 .workOrderId(feedback.getWorkOrderId()).taskId(feedback.getTaskId())
82 .workstationId(feedback.getWorkstationId()).processId(feedback.getProcessId()) 91 .workstationId(feedback.getWorkstationId()).processId(feedback.getProcessId())
83 .feedbackId(feedback.getId()).consumeDate(LocalDateTime.now()) 92 .feedbackId(feedback.getId()).consumeDate(LocalDateTime.now())
84 .status(MesWmItemConsumeStatusEnum.PREPARE.getStatus()).build(); 93 .status(MesWmItemConsumeStatusEnum.PREPARE.getStatus()).build();
85 itemConsumeMapper.insert(consume); 94 itemConsumeMapper.insert(consume);
86 -  
87 - // 4. 批量生成消耗行(消耗数量 = BOM 用料比例 × 报工数量) 95 + // 2.2 批量生成消耗行(消耗数量 = BOM 用料比例 × 报工数量)
88 List<MesWmItemConsumeLineDO> lines = convertList(boms, bom -> MesWmItemConsumeLineDO.builder() 96 List<MesWmItemConsumeLineDO> lines = convertList(boms, bom -> MesWmItemConsumeLineDO.builder()
89 .consumeId(consume.getId()).itemId(bom.getItemId()) 97 .consumeId(consume.getId()).itemId(bom.getItemId())
90 .quantity(bom.getQuantity().multiply(feedback.getFeedbackQuantity())).build()); 98 .quantity(bom.getQuantity().multiply(feedback.getFeedbackQuantity())).build());
91 itemConsumeLineService.createItemConsumeLineBatch(lines); 99 itemConsumeLineService.createItemConsumeLineBatch(lines);
92 100
93 - // 5. 按线边库 FIFO 生成消耗明细 101 + // 3. 按线边库 FIFO 生成消耗明细
94 List<MesWmItemConsumeDetailDO> allDetails = new ArrayList<>(); 102 List<MesWmItemConsumeDetailDO> allDetails = new ArrayList<>();
95 for (MesWmItemConsumeLineDO line : lines) { 103 for (MesWmItemConsumeLineDO line : lines) {
96 generateDetailForLine(line, consume.getId(), 104 generateDetailForLine(line, consume.getId(),
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImplTest.java
1 package cn.iocoder.yudao.module.mes.service.wm.itemconsume; 1 package cn.iocoder.yudao.module.mes.service.wm.itemconsume;
2 2
  3 +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
3 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 4 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
4 import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockListReqVO; 5 import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockListReqVO;
5 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; 6 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO;
6 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO; 8 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
7 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
8 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDetailDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDetailDO;
@@ -16,6 +18,7 @@ import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; @@ -16,6 +18,7 @@ import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
16 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemConsumeStatusEnum; 18 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemConsumeStatusEnum;
17 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; 19 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
18 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService; 20 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
  21 +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService;
19 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; 22 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
20 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; 23 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
21 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; 24 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
@@ -35,9 +38,13 @@ import java.util.Collections; @@ -35,9 +38,13 @@ import java.util.Collections;
35 import java.util.List; 38 import java.util.List;
36 import java.util.concurrent.atomic.AtomicLong; 39 import java.util.concurrent.atomic.AtomicLong;
37 40
  41 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
38 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; 42 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  43 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_FEEDBACK_ROUTE_PROCESS_INVALID;
  44 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_ROUTE_NOT_EXISTS;
39 import static org.junit.jupiter.api.Assertions.*; 45 import static org.junit.jupiter.api.Assertions.*;
40 import static org.mockito.ArgumentMatchers.any; 46 import static org.mockito.ArgumentMatchers.any;
  47 +import static org.mockito.ArgumentMatchers.anyLong;
41 import static org.mockito.ArgumentMatchers.argThat; 48 import static org.mockito.ArgumentMatchers.argThat;
42 import static org.mockito.Mockito.*; 49 import static org.mockito.Mockito.*;
43 50
@@ -62,6 +69,8 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest { @@ -62,6 +69,8 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest {
62 @MockitoBean 69 @MockitoBean
63 private MesProRouteProductBomService routeProductBomService; 70 private MesProRouteProductBomService routeProductBomService;
64 @MockitoBean 71 @MockitoBean
  72 + private MesProRouteService routeService;
  73 + @MockitoBean
65 private MesWmTransactionService wmTransactionService; 74 private MesWmTransactionService wmTransactionService;
66 @MockitoBean 75 @MockitoBean
67 private MesWmWarehouseService warehouseService; 76 private MesWmWarehouseService warehouseService;
@@ -103,6 +112,11 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest { @@ -103,6 +112,11 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest {
103 when(areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA)) 112 when(areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA))
104 .thenReturn(virtualArea); 113 .thenReturn(virtualArea);
105 114
  115 + lenient().when(routeService.validateRouteExists(anyLong())).thenAnswer(invocation -> {
  116 + Long routeId = invocation.getArgument(0);
  117 + return new MesProRouteDO().setId(routeId);
  118 + });
  119 +
106 // mock createItemConsumeLineBatch:模拟 insertBatch 后填充 ID 120 // mock createItemConsumeLineBatch:模拟 insertBatch 后填充 ID
107 AtomicLong lineIdSeq = new AtomicLong(1000L); 121 AtomicLong lineIdSeq = new AtomicLong(1000L);
108 doAnswer(invocation -> { 122 doAnswer(invocation -> {
@@ -125,6 +139,23 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest { @@ -125,6 +139,23 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest {
125 // ========== generateItemConsume ========== 139 // ========== generateItemConsume ==========
126 140
127 @Test 141 @Test
  142 + public void testGenerateItemConsume_routeContextIncomplete() {
  143 + MesProFeedbackDO feedback = buildFeedback();
  144 + feedback.setRouteId(null);
  145 + assertServiceException(() -> itemConsumeService.generateItemConsume(feedback), PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
  146 + verify(routeProductBomService, never()).getRouteProductBomList(any(), any(), any());
  147 + }
  148 +
  149 + @Test
  150 + public void testGenerateItemConsume_routeNotExists() {
  151 + MesProFeedbackDO feedback = buildFeedback();
  152 + doThrow(ServiceExceptionUtil.exception(PRO_ROUTE_NOT_EXISTS)).when(routeService)
  153 + .validateRouteExists(feedback.getRouteId());
  154 + assertServiceException(() -> itemConsumeService.generateItemConsume(feedback), PRO_ROUTE_NOT_EXISTS);
  155 + verify(routeProductBomService, never()).getRouteProductBomList(any(), any(), any());
  156 + }
  157 +
  158 + @Test
128 public void testGenerateItemConsume_noBom() { 159 public void testGenerateItemConsume_noBom() {
129 // 准备 160 // 准备
130 MesProFeedbackDO feedback = buildFeedback(); 161 MesProFeedbackDO feedback = buildFeedback();