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 16 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemConsumeStatusEnum;
17 17 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
18 18 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
  19 +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService;
19 20 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
20 21 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
21 22 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
... ... @@ -31,7 +32,9 @@ import java.time.LocalDateTime;
31 32 import java.util.ArrayList;
32 33 import java.util.List;
33 34  
  35 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
34 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 40 * MES 物料消耗记录 Service 实现类
... ... @@ -51,6 +54,8 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService {
51 54 @Resource
52 55 private MesProRouteProductBomService routeProductBomService;
53 56 @Resource
  57 + private MesProRouteService routeService;
  58 + @Resource
54 59 private MesWmTransactionService wmTransactionService;
55 60 @Resource
56 61 private MesWmWarehouseService warehouseService;
... ... @@ -63,34 +68,37 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService {
63 68  
64 69 @Override
65 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 77 List<MesProRouteProductBomDO> boms = routeProductBomService.getRouteProductBomList(
68 78 feedback.getRouteId(), feedback.getProcessId(), feedback.getItemId());
69 79 if (CollUtil.isEmpty(boms)) {
70 80 return null;
71 81 }
72   -
73   - // 2. 获取虚拟线边库信息
  82 + // 1.3 获取虚拟线边库信息
74 83 MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
75 84 MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(
76 85 MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
77 86 MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
78 87  
79   - // 3. 生成消耗单头
  88 + // 2.1 生成消耗单头
80 89 MesWmItemConsumeDO consume = MesWmItemConsumeDO.builder()
81 90 .workOrderId(feedback.getWorkOrderId()).taskId(feedback.getTaskId())
82 91 .workstationId(feedback.getWorkstationId()).processId(feedback.getProcessId())
83 92 .feedbackId(feedback.getId()).consumeDate(LocalDateTime.now())
84 93 .status(MesWmItemConsumeStatusEnum.PREPARE.getStatus()).build();
85 94 itemConsumeMapper.insert(consume);
86   -
87   - // 4. 批量生成消耗行(消耗数量 = BOM 用料比例 × 报工数量)
  95 + // 2.2 批量生成消耗行(消耗数量 = BOM 用料比例 × 报工数量)
88 96 List<MesWmItemConsumeLineDO> lines = convertList(boms, bom -> MesWmItemConsumeLineDO.builder()
89 97 .consumeId(consume.getId()).itemId(bom.getItemId())
90 98 .quantity(bom.getQuantity().multiply(feedback.getFeedbackQuantity())).build());
91 99 itemConsumeLineService.createItemConsumeLineBatch(lines);
92 100  
93   - // 5. 按线边库 FIFO 生成消耗明细
  101 + // 3. 按线边库 FIFO 生成消耗明细
94 102 List<MesWmItemConsumeDetailDO> allDetails = new ArrayList<>();
95 103 for (MesWmItemConsumeLineDO line : lines) {
96 104 generateDetailForLine(line, consume.getId(),
... ...
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImplTest.java
1 1 package cn.iocoder.yudao.module.mes.service.wm.itemconsume;
2 2  
  3 +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
3 4 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
4 5 import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockListReqVO;
5 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 8 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
7 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
8 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 18 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemConsumeStatusEnum;
17 19 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
18 20 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
  21 +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService;
19 22 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
20 23 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
21 24 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
... ... @@ -35,9 +38,13 @@ import java.util.Collections;
35 38 import java.util.List;
36 39 import java.util.concurrent.atomic.AtomicLong;
37 40  
  41 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
38 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 45 import static org.junit.jupiter.api.Assertions.*;
40 46 import static org.mockito.ArgumentMatchers.any;
  47 +import static org.mockito.ArgumentMatchers.anyLong;
41 48 import static org.mockito.ArgumentMatchers.argThat;
42 49 import static org.mockito.Mockito.*;
43 50  
... ... @@ -62,6 +69,8 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest {
62 69 @MockitoBean
63 70 private MesProRouteProductBomService routeProductBomService;
64 71 @MockitoBean
  72 + private MesProRouteService routeService;
  73 + @MockitoBean
65 74 private MesWmTransactionService wmTransactionService;
66 75 @MockitoBean
67 76 private MesWmWarehouseService warehouseService;
... ... @@ -103,6 +112,11 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest {
103 112 when(areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA))
104 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 120 // mock createItemConsumeLineBatch:模拟 insertBatch 后填充 ID
107 121 AtomicLong lineIdSeq = new AtomicLong(1000L);
108 122 doAnswer(invocation -> {
... ... @@ -125,6 +139,23 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest {
125 139 // ========== generateItemConsume ==========
126 140  
127 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 159 public void testGenerateItemConsume_noBom() {
129 160 // 准备
130 161 MesProFeedbackDO feedback = buildFeedback();
... ...