From 4d0918398760814cb46b43572c9954c52f1de2f2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 4 Apr 2026 23:29:23 +0800 Subject: [PATCH] ✨ feat(service): 增强物料消耗记录生成逻辑 --- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java | 22 +++++++++++++++------- yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImplTest.java | 31 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java index 63daf67..1fc98f0 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java +++ b/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; import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemConsumeStatusEnum; import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService; +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService; import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; @@ -31,7 +32,9 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_FEEDBACK_ROUTE_PROCESS_INVALID; /** * MES 物料消耗记录 Service 实现类 @@ -51,6 +54,8 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService { @Resource private MesProRouteProductBomService routeProductBomService; @Resource + private MesProRouteService routeService; + @Resource private MesWmTransactionService wmTransactionService; @Resource private MesWmWarehouseService warehouseService; @@ -63,34 +68,37 @@ public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService { @Override public MesWmItemConsumeDO generateItemConsume(MesProFeedbackDO feedback) { - // 1. 查询当前工序的 BOM 物料配置 + // 1.1 避免 routeId 等为 null 时 getRouteProductBomList 因 eqIfPresent 放宽条件导致误查 + if (feedback.getRouteId() == null || feedback.getProcessId() == null || feedback.getItemId() == null) { + throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID); + } + routeService.validateRouteExists(feedback.getRouteId()); + // 1.2 查询当前工序的 BOM 物料配置 List boms = routeProductBomService.getRouteProductBomList( feedback.getRouteId(), feedback.getProcessId(), feedback.getItemId()); if (CollUtil.isEmpty(boms)) { return null; } - - // 2. 获取虚拟线边库信息 + // 1.3 获取虚拟线边库信息 MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode( MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION); MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA); - // 3. 生成消耗单头 + // 2.1 生成消耗单头 MesWmItemConsumeDO consume = MesWmItemConsumeDO.builder() .workOrderId(feedback.getWorkOrderId()).taskId(feedback.getTaskId()) .workstationId(feedback.getWorkstationId()).processId(feedback.getProcessId()) .feedbackId(feedback.getId()).consumeDate(LocalDateTime.now()) .status(MesWmItemConsumeStatusEnum.PREPARE.getStatus()).build(); itemConsumeMapper.insert(consume); - - // 4. 批量生成消耗行(消耗数量 = BOM 用料比例 × 报工数量) + // 2.2 批量生成消耗行(消耗数量 = BOM 用料比例 × 报工数量) List lines = convertList(boms, bom -> MesWmItemConsumeLineDO.builder() .consumeId(consume.getId()).itemId(bom.getItemId()) .quantity(bom.getQuantity().multiply(feedback.getFeedbackQuantity())).build()); itemConsumeLineService.createItemConsumeLineBatch(lines); - // 5. 按线边库 FIFO 生成消耗明细 + // 3. 按线边库 FIFO 生成消耗明细 List allDetails = new ArrayList<>(); for (MesWmItemConsumeLineDO line : lines) { generateDetailForLine(line, consume.getId(), diff --git a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImplTest.java b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImplTest.java index 7569111..c5e2e09 100644 --- a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImplTest.java +++ b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImplTest.java @@ -1,8 +1,10 @@ package cn.iocoder.yudao.module.mes.service.wm.itemconsume; +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockListReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDetailDO; @@ -16,6 +18,7 @@ import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemConsumeStatusEnum; import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService; +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService; import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; @@ -35,9 +38,13 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicLong; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_FEEDBACK_ROUTE_PROCESS_INVALID; +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_ROUTE_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.*; @@ -62,6 +69,8 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest { @MockitoBean private MesProRouteProductBomService routeProductBomService; @MockitoBean + private MesProRouteService routeService; + @MockitoBean private MesWmTransactionService wmTransactionService; @MockitoBean private MesWmWarehouseService warehouseService; @@ -103,6 +112,11 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest { when(areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA)) .thenReturn(virtualArea); + lenient().when(routeService.validateRouteExists(anyLong())).thenAnswer(invocation -> { + Long routeId = invocation.getArgument(0); + return new MesProRouteDO().setId(routeId); + }); + // mock createItemConsumeLineBatch:模拟 insertBatch 后填充 ID AtomicLong lineIdSeq = new AtomicLong(1000L); doAnswer(invocation -> { @@ -125,6 +139,23 @@ public class MesWmItemConsumeServiceImplTest extends BaseDbUnitTest { // ========== generateItemConsume ========== @Test + public void testGenerateItemConsume_routeContextIncomplete() { + MesProFeedbackDO feedback = buildFeedback(); + feedback.setRouteId(null); + assertServiceException(() -> itemConsumeService.generateItemConsume(feedback), PRO_FEEDBACK_ROUTE_PROCESS_INVALID); + verify(routeProductBomService, never()).getRouteProductBomList(any(), any(), any()); + } + + @Test + public void testGenerateItemConsume_routeNotExists() { + MesProFeedbackDO feedback = buildFeedback(); + doThrow(ServiceExceptionUtil.exception(PRO_ROUTE_NOT_EXISTS)).when(routeService) + .validateRouteExists(feedback.getRouteId()); + assertServiceException(() -> itemConsumeService.generateItemConsume(feedback), PRO_ROUTE_NOT_EXISTS); + verify(routeProductBomService, never()).getRouteProductBomList(any(), any(), any()); + } + + @Test public void testGenerateItemConsume_noBom() { // 准备 MesProFeedbackDO feedback = buildFeedback(); -- libgit2 0.21.4