Commit 053a100f5facee21ea30cb57763fe29f14e2f083

Authored by YunaiV
1 parent 7cca10fc

fix(mes): 修复采购入库上架明细批次丢失

yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -467,6 +467,7 @@ public interface ErrorCodeConstants {
467 467 ErrorCode WM_ITEM_RECEIPT_LINE_ARRIVAL_NOTICE_LINE_NOT_ALLOWED = new ErrorCode(1_040_705_102,
468 468 "入库单未关联到货通知单,不能选择到货通知单行");
469 469 ErrorCode WM_ITEM_RECEIPT_DETAIL_NOT_EXISTS = new ErrorCode(1_040_705_200, "采购入库明细不存在");
  470 + ErrorCode WM_ITEM_RECEIPT_DETAIL_LINE_MISMATCH = new ErrorCode(1_040_705_201, "入库单行与上架明细的单据或物料不一致");
470 471 ErrorCode WM_ITEM_RECEIPT_DETAIL_QUANTITY_EXCEED = new ErrorCode(1_040_705_202, "上架明细总数量不能超过行入库数量");
471 472  
472 473 // ========== MES 仓库管理-领料申请单(1-040-706-000) ==========
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptDetailServiceImpl.java
... ... @@ -8,6 +8,8 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceip
8 8 import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptDetailMapper;
9 9 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
10 10 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
  11 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  12 +import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
11 13 import jakarta.annotation.Resource;
12 14 import org.springframework.context.annotation.Lazy;
13 15 import org.springframework.stereotype.Service;
... ... @@ -15,6 +17,7 @@ import org.springframework.validation.annotation.Validated;
15 17  
16 18 import java.math.BigDecimal;
17 19 import java.util.List;
  20 +import java.util.Objects;
18 21  
19 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
20 23 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
... ... @@ -27,6 +30,11 @@ import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
27 30 public class MesWmItemReceiptDetailServiceImpl implements MesWmItemReceiptDetailService {
28 31  
29 32 @Resource
  33 + private MesMdItemService itemService;
  34 + @Resource
  35 + private MesWmBatchService batchService;
  36 +
  37 + @Resource
30 38 private MesWmItemReceiptDetailMapper itemReceiptDetailMapper;
31 39  
32 40 @Resource
... ... @@ -62,6 +70,8 @@ public class MesWmItemReceiptDetailServiceImpl implements MesWmItemReceiptDetail
62 70 MesWmItemReceiptDetailDO detail = validateItemReceiptDetailExists(updateReqVO.getId());
63 71 // 1.2 校验数据
64 72 updateReqVO.setReceiptId(detail.getReceiptId());
  73 + updateReqVO.setLineId(detail.getLineId());
  74 + updateReqVO.setItemId(detail.getItemId());
65 75 validateItemReceiptDetailSaveData(updateReqVO);
66 76  
67 77 // 2. 更新
... ... @@ -75,6 +85,23 @@ public class MesWmItemReceiptDetailServiceImpl implements MesWmItemReceiptDetail
75 85 private void validateItemReceiptDetailSaveData(MesWmItemReceiptDetailSaveReqVO reqVO) {
76 86 // 校验父单据存在且为可编辑状态
77 87 itemReceiptService.validateItemReceiptEditable(reqVO.getReceiptId());
  88 + // 上架明细沿用入库单行的物料和批次,避免客户端漏传或传错批次。
  89 + MesWmItemReceiptLineDO line = itemReceiptLineService.getItemReceiptLine(reqVO.getLineId());
  90 + if (line == null) {
  91 + throw exception(WM_ITEM_RECEIPT_LINE_NOT_EXISTS);
  92 + }
  93 + if (!Objects.equals(line.getReceiptId(), reqVO.getReceiptId())
  94 + || !Objects.equals(line.getItemId(), reqVO.getItemId())) {
  95 + throw exception(WM_ITEM_RECEIPT_DETAIL_LINE_MISMATCH);
  96 + }
  97 + reqVO.setBatchId(line.getBatchId());
  98 + if (Boolean.TRUE.equals(itemService.validateItemExists(reqVO.getItemId()).getBatchFlag())
  99 + && reqVO.getBatchId() == null) {
  100 + throw exception(MD_ITEM_BATCH_REQUIRED);
  101 + }
  102 + if (reqVO.getBatchId() != null) {
  103 + batchService.validateBatchExists(reqVO.getBatchId(), reqVO.getItemId());
  104 + }
78 105 // 校验库区关系
79 106 warehouseAreaService.validateWarehouseAreaExists(reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId());
80 107 // 校验库位物料/批次混放规则
... ...
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptDetailServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.itemreceipt;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.detail.MesWmItemReceiptDetailSaveReqVO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptLineDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDetailDO;
  8 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptDetailMapper;
  9 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  10 +import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
  11 +import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
  12 +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
  13 +import org.junit.jupiter.api.Test;
  14 +import org.mockito.InjectMocks;
  15 +import org.mockito.Mock;
  16 +
  17 +import java.math.BigDecimal;
  18 +import java.util.Objects;
  19 +
  20 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  21 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  22 +import static org.mockito.ArgumentMatchers.*;
  23 +import static org.mockito.Mockito.*;
  24 +
  25 +class MesWmItemReceiptDetailServiceImplTest extends BaseMockitoUnitTest {
  26 + @InjectMocks private MesWmItemReceiptDetailServiceImpl service;
  27 + @Mock private MesWmItemReceiptDetailMapper itemReceiptDetailMapper;
  28 + @Mock private MesWmItemReceiptService itemReceiptService;
  29 + @Mock private MesWmItemReceiptLineService itemReceiptLineService;
  30 + @Mock private MesWmWarehouseAreaService warehouseAreaService;
  31 + @Mock private MesWmMaterialStockService materialStockService;
  32 + @Mock private MesMdItemService itemService;
  33 + @Mock private MesWmBatchService batchService;
  34 +
  35 + private MesWmItemReceiptDetailSaveReqVO prepare(Long batchId, boolean batchFlag) {
  36 + when(itemReceiptLineService.getItemReceiptLine(11L)).thenReturn(
  37 + new MesWmItemReceiptLineDO().setId(11L).setReceiptId(1L).setItemId(2L).setBatchId(batchId));
  38 + when(itemService.validateItemExists(2L)).thenReturn(new MesMdItemDO().setId(2L).setBatchFlag(batchFlag));
  39 + return new MesWmItemReceiptDetailSaveReqVO().setReceiptId(1L).setLineId(11L)
  40 + .setItemId(2L).setAreaId(3L).setQuantity(BigDecimal.ONE);
  41 + }
  42 +
  43 + @Test
  44 + void createInheritsLineBatchBeforeMixingValidation() {
  45 + MesWmItemReceiptDetailSaveReqVO request = prepare(22L, true).setBatchId(999L);
  46 + service.createItemReceiptDetail(request);
  47 + verify(batchService).validateBatchExists(22L, 2L);
  48 + verify(materialStockService).checkAreaMixingRule(3L, 2L, 22L);
  49 + verify(itemReceiptDetailMapper).insert(argThat((MesWmItemReceiptDetailDO row) -> Objects.equals(row.getBatchId(), 22L)));
  50 + }
  51 +
  52 + @Test
  53 + void missingLineBatchIsRejectedBeforeInsert() {
  54 + MesWmItemReceiptDetailSaveReqVO request = prepare(null, true);
  55 + assertServiceException(() -> service.createItemReceiptDetail(request), MD_ITEM_BATCH_REQUIRED);
  56 + verify(itemReceiptDetailMapper, never()).insert(any(MesWmItemReceiptDetailDO.class));
  57 + }
  58 +
  59 + @Test
  60 + void unrelatedReceiptLineIsRejected() {
  61 + MesWmItemReceiptDetailSaveReqVO request = prepare(22L, true).setReceiptId(99L);
  62 + reset(itemService);
  63 + assertServiceException(() -> service.createItemReceiptDetail(request), WM_ITEM_RECEIPT_DETAIL_LINE_MISMATCH);
  64 + verifyNoInteractions(materialStockService);
  65 + }
  66 +
  67 + @Test
  68 + void nonBatchItemCanRemainWithoutBatch() {
  69 + service.createItemReceiptDetail(prepare(null, false));
  70 + verifyNoInteractions(batchService);
  71 + verify(itemReceiptDetailMapper).insert(argThat((MesWmItemReceiptDetailDO row) -> row.getBatchId() == null));
  72 + }
  73 +}
... ...