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,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(); |