Commit 4d0918398760814cb46b43572c9954c52f1de2f2
1 parent
b690a596
✨ feat(service): 增强物料消耗记录生成逻辑
新增对工序路由存在性验证,避免在生成物料消耗记录时出现无效路由导致的错误。更新了相关的异常处理逻辑,并调整了注释以提高代码可读性。
Showing
2 changed files
with
46 additions
and
7 deletions
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(); | ... | ... |