云
2026-10-28 dd637406e18a7afbf08d323c6ece913fe545b7ad
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package cn.iocoder.yudao.module.mes.service.wm.returnsales;
 
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi;
import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.detail.MesWmReturnSalesDetailSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWmReturnSalesLineSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales.MesWmReturnSalesDetailMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales.MesWmReturnSalesLineMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnSalesStatusEnum;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
 
import java.math.BigDecimal;
import java.util.List;
 
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
 
@ExtendWith(MockitoExtension.class)
class MesWmReturnSalesReelTest {
    @InjectMocks private MesWmReturnSalesDetailServiceImpl details;
    @InjectMocks private MesWmReturnSalesLineServiceImpl lines;
    @Mock private MesWmReturnSalesLineService lineService;
    @Mock private MesWmReturnSalesService returnService;
    @Mock private MesWmReturnSalesDetailMapper detailMapper;
    @Mock private MesWmReturnSalesLineMapper lineMapper;
    @Mock private MesWmProductSalesDetailMapper productSalesDetailMapper;
    @Mock private MesWmWarehouseAreaService areaService;
    @Mock private MesWmMaterialStockService stockService;
    @Mock private MesWmBatchService batchService;
    @Mock private MesMdmItemService itemService;
    @Mock private ErpSaleOrderApi saleOrderApi;
 
    @Test
    void resolvesBatchBeforeCheckingMixingAndKeepsSource() {
        var req = new MesWmReturnSalesDetailSaveReqVO();
        req.setLineId(1L); req.setReturnId(2L); req.setItemId(3L);
        req.setBatchCode("B1"); req.setQuantity(BigDecimal.ONE); req.setAreaId(4L);
        when(lineService.validateReturnSalesLineExists(1L)).thenReturn(MesWmReturnSalesLineDO.builder()
                .returnId(2L).itemId(3L).batchId(5L).quantity(BigDecimal.TEN).build());
        when(returnService.validateReturnSalesExists(2L)).thenReturn(MesWmReturnSalesDO.builder()
                .status(MesWmReturnSalesStatusEnum.APPROVED.getStatus()).build());
        when(batchService.getBatchByCode("B1")).thenReturn(MesWmBatchDO.builder().id(5L).itemId(3L).code("B1").build());
        when(detailMapper.selectListByLineId(1L)).thenReturn(List.of());
        details.createReturnSalesDetail(req);
        verify(stockService).checkAreaMixingRule(4L, 3L, 5L);
        assertEquals(5L, req.getBatchId());
    }
 
    @Test
    void rejectsDifferentBatchForSameReturnLine() {
        var req = new MesWmReturnSalesDetailSaveReqVO();
        req.setLineId(1L); req.setReturnId(2L); req.setItemId(3L); req.setBatchCode("B2");
        when(lineService.validateReturnSalesLineExists(1L)).thenReturn(MesWmReturnSalesLineDO.builder()
                .returnId(2L).itemId(3L).batchId(5L).build());
        when(returnService.validateReturnSalesExists(2L)).thenReturn(MesWmReturnSalesDO.builder()
                .status(MesWmReturnSalesStatusEnum.APPROVED.getStatus()).build());
        when(batchService.getBatchByCode("B2")).thenReturn(MesWmBatchDO.builder().id(6L).itemId(3L).build());
        assertThrows(ServiceException.class, () -> details.createReturnSalesDetail(req));
        verifyNoInteractions(stockService);
    }
 
    @Test
    void returnOrderWithReelsRequiresMatchingBatch() {
        var req = new MesWmReturnSalesLineSaveReqVO();
        req.setReturnId(2L); req.setItemId(3L); req.setBatchId(5L);
        when(returnService.validateReturnSalesExistsAndPrepare(2L)).thenReturn(MesWmReturnSalesDO.builder()
                .saleOrderId(7L).clientId(8L).build());
        when(itemService.validateItemExistsAndEnable(3L)).thenReturn(MesMdmItemDO.builder().isBatchManaged(false).build());
        var batch = MesWmBatchDO.builder().id(5L).itemId(3L).code("B1").reelNo("OTHER").build();
        when(batchService.validateBatchExists(5L, 3L, 8L, null)).thenReturn(batch);
        var item = new ErpSaleOrderItemRespDTO(); item.setProductId(3L); item.setReelNo("REEL-1");
        when(saleOrderApi.getSaleOrderItemList(7L)).thenReturn(CommonResult.success(List.of(item)));
        assertThrows(ServiceException.class, () -> lines.createReturnSalesLine(req));
        batch.setReelNo("REEL-1");
        when(productSalesDetailMapper.countFinishedSaleBatch(eq(7L), eq(3L), eq(5L), anyInt())).thenReturn(0L, 1L);
        assertThrows(ServiceException.class, () -> lines.createReturnSalesLine(req));
        lines.createReturnSalesLine(req);
        assertEquals("B1", req.getBatchCode());
        verify(lineMapper).insert(any(MesWmReturnSalesLineDO.class));
    }
}