🎉 Initial commit

This commit is contained in:
2026-07-27 14:26:14 +08:00
commit fd54f07889
6935 changed files with 617410 additions and 0 deletions

View File

@@ -0,0 +1,672 @@
package cn.iocoder.yudao.module.wms.service.inventory;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryHistoryDO;
import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO;
import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO;
import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
import cn.iocoder.yudao.module.wms.dal.mysql.inventory.WmsInventoryHistoryMapper;
import cn.iocoder.yudao.module.wms.dal.mysql.inventory.WmsInventoryMapper;
import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemMapper;
import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemSkuMapper;
import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum;
import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO;
import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryCheckReqDTO;
import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService;
import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_INVENTORY_CHANGED;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.INVENTORY_QUANTITY_NOT_ENOUGH;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.when;
@Import({WmsInventoryServiceImpl.class, WmsInventoryHistoryServiceImpl.class})
public class WmsInventoryServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsInventoryServiceImpl inventoryService;
@Resource
private WmsInventoryMapper inventoryMapper;
@Resource
private WmsInventoryHistoryMapper inventoryHistoryMapper;
@Resource
private WmsItemMapper itemMapper;
@Resource
private WmsItemSkuMapper skuMapper;
@MockitoBean
private WmsItemSkuService itemSkuService;
@MockitoBean
private WmsItemService itemService;
@Test
public void testChangeInventory_createInventory() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "5.00");
// 调用
inventoryService.changeInventory(reqDTO);
// 断言:库存余额
WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
assertNotNull(inventory);
assertEquals(0, new BigDecimal("5.00").compareTo(inventory.getQuantity()));
// 断言:库存流水
List<WmsInventoryHistoryDO> histories = inventoryHistoryMapper.selectList();
assertEquals(1, histories.size());
WmsInventoryHistoryDO history = histories.get(0);
assertEquals(sku.getId(), history.getSkuId());
assertEquals(100L, history.getWarehouseId());
assertEquals(0, BigDecimal.ZERO.compareTo(history.getBeforeQuantity()));
assertEquals(0, new BigDecimal("5.00").compareTo(history.getAfterQuantity()));
assertEquals(0, new BigDecimal("500.00").compareTo(history.getTotalPrice()));
assertEquals(reqDTO.getOrderId(), history.getOrderId());
assertEquals(reqDTO.getOrderNo(), history.getOrderNo());
assertEquals(reqDTO.getOrderType(), history.getOrderType());
}
@Test
public void testChangeInventory_updateInventory() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00"));
WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "3.00");
// 调用
inventoryService.changeInventory(reqDTO);
// 断言:库存余额
WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
assertNotNull(inventory);
assertEquals(0, new BigDecimal("5.00").compareTo(inventory.getQuantity()));
// 断言:库存流水
List<WmsInventoryHistoryDO> histories = inventoryHistoryMapper.selectList();
assertEquals(1, histories.size());
WmsInventoryHistoryDO history = histories.get(0);
assertEquals(0, new BigDecimal("2.00").compareTo(history.getBeforeQuantity()));
assertEquals(0, new BigDecimal("5.00").compareTo(history.getAfterQuantity()));
assertEquals(0, new BigDecimal("3.00").compareTo(history.getQuantity()));
assertEquals(0, new BigDecimal("300.00").compareTo(history.getTotalPrice()));
}
@Test
public void testChangeInventory_sameInventoryKeyKeepItemGranularity() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "5.00");
List<WmsInventoryChangeReqDTO.Item> items = new ArrayList<>(reqDTO.getItems());
items.add(new WmsInventoryChangeReqDTO.Item()
.setSkuId(sku.getId())
.setWarehouseId(100L)
.setQuantity(new BigDecimal("7.00"))
.setPrice(new BigDecimal("140.00"))
.setTotalPrice(new BigDecimal("980.00"))
.setRemark("测试入库 2"));
reqDTO.setItems(items);
// 调用
inventoryService.changeInventory(reqDTO);
// 断言:库存余额只保留一条,数量逐条累加
WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
assertNotNull(inventory);
assertEquals(0, new BigDecimal("12.00").compareTo(inventory.getQuantity()));
// 断言:库存流水保持用户明细颗粒度
List<WmsInventoryHistoryDO> histories = inventoryHistoryMapper.selectList();
histories.sort(Comparator.comparing(WmsInventoryHistoryDO::getId));
assertEquals(2, histories.size());
assertEquals(0, BigDecimal.ZERO.compareTo(histories.get(0).getBeforeQuantity()));
assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getAfterQuantity()));
assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getQuantity()));
assertEquals(0, new BigDecimal("500.00").compareTo(histories.get(0).getTotalPrice()));
assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(1).getBeforeQuantity()));
assertEquals(0, new BigDecimal("12.00").compareTo(histories.get(1).getAfterQuantity()));
assertEquals(0, new BigDecimal("7.00").compareTo(histories.get(1).getQuantity()));
assertEquals(0, new BigDecimal("980.00").compareTo(histories.get(1).getTotalPrice()));
}
@Test
public void testChangeInventory_sameExistingInventoryKeyKeepItemGranularity() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00"));
WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "3.00");
List<WmsInventoryChangeReqDTO.Item> items = new ArrayList<>(reqDTO.getItems());
items.add(new WmsInventoryChangeReqDTO.Item()
.setSkuId(sku.getId())
.setWarehouseId(100L)
.setQuantity(new BigDecimal("4.00"))
.setPrice(new BigDecimal("80.00"))
.setTotalPrice(new BigDecimal("320.00"))
.setRemark("测试入库 2"));
reqDTO.setItems(items);
// 调用
inventoryService.changeInventory(reqDTO);
// 断言:库存余额只保留一条,数量逐条累加
WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
assertNotNull(inventory);
assertEquals(0, new BigDecimal("9.00").compareTo(inventory.getQuantity()));
// 断言:库存流水保持用户明细颗粒度
List<WmsInventoryHistoryDO> histories = inventoryHistoryMapper.selectList();
histories.sort(Comparator.comparing(WmsInventoryHistoryDO::getId));
assertEquals(2, histories.size());
assertEquals(0, new BigDecimal("2.00").compareTo(histories.get(0).getBeforeQuantity()));
assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getAfterQuantity()));
assertEquals(0, new BigDecimal("3.00").compareTo(histories.get(0).getQuantity()));
assertEquals(0, new BigDecimal("300.00").compareTo(histories.get(0).getTotalPrice()));
assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(1).getBeforeQuantity()));
assertEquals(0, new BigDecimal("9.00").compareTo(histories.get(1).getAfterQuantity()));
assertEquals(0, new BigDecimal("4.00").compareTo(histories.get(1).getQuantity()));
assertEquals(0, new BigDecimal("320.00").compareTo(histories.get(1).getTotalPrice()));
}
@Test
public void testChangeInventory_multipleInventoryKeysKeepItemGranularity() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "5.00");
List<WmsInventoryChangeReqDTO.Item> items = new ArrayList<>(reqDTO.getItems());
items.add(new WmsInventoryChangeReqDTO.Item()
.setSkuId(sku.getId())
.setWarehouseId(200L)
.setQuantity(new BigDecimal("3.00"))
.setPrice(new BigDecimal("60.00"))
.setTotalPrice(new BigDecimal("180.00"))
.setRemark("测试入库 2"));
items.add(new WmsInventoryChangeReqDTO.Item()
.setSkuId(sku.getId())
.setWarehouseId(100L)
.setQuantity(new BigDecimal("-2.00"))
.setPrice(new BigDecimal("40.00"))
.setTotalPrice(new BigDecimal("-80.00"))
.setRemark("测试出库"));
reqDTO.setItems(items);
// 调用
inventoryService.changeInventory(reqDTO);
// 断言:不同库存余额分别更新
WmsInventoryDO inventory1 = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
assertNotNull(inventory1);
assertEquals(0, new BigDecimal("3.00").compareTo(inventory1.getQuantity()));
WmsInventoryDO inventory2 = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 200L);
assertNotNull(inventory2);
assertEquals(0, new BigDecimal("3.00").compareTo(inventory2.getQuantity()));
// 断言:批量加锁后仍按明细颗粒度记录 before/after
List<WmsInventoryHistoryDO> histories = inventoryHistoryMapper.selectList();
histories.sort(Comparator.comparing(WmsInventoryHistoryDO::getId));
assertEquals(3, histories.size());
assertEquals(100L, histories.get(0).getWarehouseId());
assertEquals(0, BigDecimal.ZERO.compareTo(histories.get(0).getBeforeQuantity()));
assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getAfterQuantity()));
assertEquals(0, new BigDecimal("500.00").compareTo(histories.get(0).getTotalPrice()));
assertEquals(200L, histories.get(1).getWarehouseId());
assertEquals(0, BigDecimal.ZERO.compareTo(histories.get(1).getBeforeQuantity()));
assertEquals(0, new BigDecimal("3.00").compareTo(histories.get(1).getAfterQuantity()));
assertEquals(0, new BigDecimal("180.00").compareTo(histories.get(1).getTotalPrice()));
assertEquals(100L, histories.get(2).getWarehouseId());
assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(2).getBeforeQuantity()));
assertEquals(0, new BigDecimal("3.00").compareTo(histories.get(2).getAfterQuantity()));
assertEquals(0, new BigDecimal("-80.00").compareTo(histories.get(2).getTotalPrice()));
}
@Test
public void testChangeInventory_multipleExistingInventoryKeys() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
inventoryMapper.insert(createInventory(sku.getId(), 200L, "7.00"));
inventoryMapper.insert(createInventory(sku.getId(), 100L, "5.00"));
WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "2.00");
List<WmsInventoryChangeReqDTO.Item> items = new ArrayList<>(reqDTO.getItems());
items.add(new WmsInventoryChangeReqDTO.Item()
.setSkuId(sku.getId())
.setWarehouseId(200L)
.setQuantity(new BigDecimal("3.00"))
.setPrice(new BigDecimal("100.00"))
.setTotalPrice(new BigDecimal("300.00"))
.setRemark("测试入库 2"));
reqDTO.setItems(items);
// 调用
inventoryService.changeInventory(reqDTO);
// 断言:多条已存在库存余额分别更新
WmsInventoryDO inventory1 = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
assertNotNull(inventory1);
assertEquals(0, new BigDecimal("7.00").compareTo(inventory1.getQuantity()));
WmsInventoryDO inventory2 = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 200L);
assertNotNull(inventory2);
assertEquals(0, new BigDecimal("10.00").compareTo(inventory2.getQuantity()));
// 断言:库存流水保持入参明细顺序
List<WmsInventoryHistoryDO> histories = inventoryHistoryMapper.selectList();
histories.sort(Comparator.comparing(WmsInventoryHistoryDO::getId));
assertEquals(2, histories.size());
assertEquals(100L, histories.get(0).getWarehouseId());
assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getBeforeQuantity()));
assertEquals(0, new BigDecimal("7.00").compareTo(histories.get(0).getAfterQuantity()));
assertEquals(200L, histories.get(1).getWarehouseId());
assertEquals(0, new BigDecimal("7.00").compareTo(histories.get(1).getBeforeQuantity()));
assertEquals(0, new BigDecimal("10.00").compareTo(histories.get(1).getAfterQuantity()));
}
@Test
public void testChangeInventory_concurrentCreateSameInventoryOnlyOneBalance() throws Exception {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
int threadCount = 4;
CountDownLatch readyLatch = new CountDownLatch(threadCount);
CountDownLatch startLatch = new CountDownLatch(1);
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
List<Future<?>> futures = new ArrayList<>(threadCount);
for (int i = 0; i < threadCount; i++) {
futures.add(executorService.submit(() -> {
readyLatch.countDown();
try {
startLatch.await();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException(ex);
}
inventoryService.changeInventory(createChangeReq(sku.getId(), 100L, "1.00"));
}));
}
try {
// 调用
assertTrue(readyLatch.await(5, TimeUnit.SECONDS));
startLatch.countDown();
for (Future<?> future : futures) {
future.get(10, TimeUnit.SECONDS);
}
} finally {
executorService.shutdownNow();
}
// 断言:并发补行后仍只有一条库存余额,数量累计正确
List<WmsInventoryDO> inventories = inventoryMapper.selectListByKeys(Collections.singletonList(
new WmsInventoryDO().setSkuId(sku.getId()).setWarehouseId(100L)));
assertEquals(1, inventories.size());
assertEquals(0, new BigDecimal("4.00").compareTo(inventories.get(0).getQuantity()));
assertEquals(threadCount, inventoryHistoryMapper.selectCount());
}
@Test
public void testChangeInventory_quantityNotEnough() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00"));
WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "-3.00");
mockItemSkuAndItem(item, sku);
// 调用,并断言
assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH,
item.getName(), sku.getName(), 100L, new BigDecimal("2.00"), new BigDecimal("-3.00"));
WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
assertNotNull(inventory);
assertEquals(0, new BigDecimal("2.00").compareTo(inventory.getQuantity()));
assertEquals(0L, inventoryHistoryMapper.selectCount());
}
@Test
public void testChangeInventory_quantityNotEnoughWithoutInventory() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "-3.00");
mockItemSkuAndItem(item, sku);
// 调用,并断言
assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH,
item.getName(), sku.getName(), 100L, new BigDecimal("0.00"), new BigDecimal("-3.00"));
assertEquals(0L, inventoryMapper.selectCount());
assertEquals(0L, inventoryHistoryMapper.selectCount());
}
@Test
public void testChangeInventory_quantityNotEnoughRollbackCreatedInventories() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "5.00");
List<WmsInventoryChangeReqDTO.Item> items = new ArrayList<>(reqDTO.getItems());
items.add(new WmsInventoryChangeReqDTO.Item()
.setSkuId(sku.getId())
.setWarehouseId(200L)
.setQuantity(new BigDecimal("-1.00"))
.setPrice(new BigDecimal("20.00"))
.setTotalPrice(new BigDecimal("-20.00"))
.setRemark("测试出库"));
reqDTO.setItems(items);
mockItemSkuAndItem(item, sku);
// 调用,并断言:第二条明细库存不足,事务回滚前面补齐的库存行
assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH,
item.getName(), sku.getName(), 200L, new BigDecimal("0.00"), new BigDecimal("-1.00"));
assertNull(inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L));
assertNull(inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 200L));
assertEquals(0L, inventoryHistoryMapper.selectCount());
}
@Test
public void testCheckInventory_updateExistingInventory() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
WmsInventoryDO inventory = createInventory(sku.getId(), 100L, "10.00");
inventoryMapper.insert(inventory);
WmsInventoryCheckReqDTO reqDTO = createCheckReq(inventory.getId(), sku.getId(), 100L,
"10.00", "7.00");
// 调用
inventoryService.checkInventory(reqDTO);
// 断言:库存余额直接调整为实盘数量
WmsInventoryDO dbInventory = inventoryMapper.selectById(inventory.getId());
assertNotNull(dbInventory);
assertEquals(0, new BigDecimal("7.00").compareTo(dbInventory.getQuantity()));
// 断言:库存流水记录盘库差异
List<WmsInventoryHistoryDO> histories = inventoryHistoryMapper.selectList();
assertEquals(1, histories.size());
WmsInventoryHistoryDO history = histories.get(0);
assertEquals(sku.getId(), history.getSkuId());
assertEquals(100L, history.getWarehouseId());
assertEquals(0, new BigDecimal("10.00").compareTo(history.getBeforeQuantity()));
assertEquals(0, new BigDecimal("7.00").compareTo(history.getAfterQuantity()));
assertEquals(0, new BigDecimal("-3.00").compareTo(history.getQuantity()));
assertEquals(0, new BigDecimal("-300.00").compareTo(history.getTotalPrice()));
assertEquals(reqDTO.getOrderId(), history.getOrderId());
assertEquals(reqDTO.getOrderNo(), history.getOrderNo());
assertEquals(reqDTO.getOrderType(), history.getOrderType());
}
@Test
public void testCheckInventory_existingInventoryChanged() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
WmsInventoryDO inventory = createInventory(sku.getId(), 100L, "12.00");
inventoryMapper.insert(inventory);
WmsInventoryCheckReqDTO reqDTO = createCheckReq(inventory.getId(), sku.getId(), 100L,
"10.00", "7.00");
// 调用,并断言
assertServiceException(() -> inventoryService.checkInventory(reqDTO), CHECK_ORDER_INVENTORY_CHANGED);
WmsInventoryDO dbInventory = inventoryMapper.selectById(inventory.getId());
assertNotNull(dbInventory);
assertEquals(0, new BigDecimal("12.00").compareTo(dbInventory.getQuantity()));
assertEquals(0L, inventoryHistoryMapper.selectCount());
}
@Test
public void testCheckInventory_createNewInventory() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
WmsInventoryCheckReqDTO reqDTO = createCheckReq(null, sku.getId(), 100L,
"0.00", "5.00");
// 调用
inventoryService.checkInventory(reqDTO);
// 断言:新增库存直接落为实盘数量
WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
assertNotNull(inventory);
assertEquals(0, new BigDecimal("5.00").compareTo(inventory.getQuantity()));
// 断言:库存流水记录从 0 到实盘数量
List<WmsInventoryHistoryDO> histories = inventoryHistoryMapper.selectList();
assertEquals(1, histories.size());
assertEquals(0, BigDecimal.ZERO.compareTo(histories.get(0).getBeforeQuantity()));
assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getAfterQuantity()));
assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getQuantity()));
assertEquals(0, new BigDecimal("500.00").compareTo(histories.get(0).getTotalPrice()));
}
@Test
public void testCheckInventory_createNewZeroInventory() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
WmsInventoryCheckReqDTO reqDTO = createCheckReq(null, sku.getId(), 100L,
"0.00", "0.00");
// 调用
inventoryService.checkInventory(reqDTO);
// 断言:新增零库存商品也会生成库存余额行
WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
assertNotNull(inventory);
assertEquals(0, BigDecimal.ZERO.compareTo(inventory.getQuantity()));
assertEquals(0L, inventoryHistoryMapper.selectCount());
}
@Test
public void testCheckInventory_newInventoryAlreadyExists() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
inventoryMapper.insert(createInventory(sku.getId(), 100L, "3.00"));
WmsInventoryCheckReqDTO reqDTO = createCheckReq(null, sku.getId(), 100L,
"0.00", "5.00");
// 调用,并断言
assertServiceException(() -> inventoryService.checkInventory(reqDTO), CHECK_ORDER_INVENTORY_CHANGED);
WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
assertNotNull(inventory);
assertEquals(0, new BigDecimal("3.00").compareTo(inventory.getQuantity()));
assertEquals(0L, inventoryHistoryMapper.selectCount());
}
@Test
public void testGetInventoryPage_filterByWarehouse() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00"));
inventoryMapper.insert(createInventory(sku.getId(), 200L, "5.00"));
// 准备参数
WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO();
reqVO.setType(WmsInventoryPageReqVO.TYPE_WAREHOUSE);
reqVO.setWarehouseId(100L);
// 调用
PageResult<WmsInventoryDO> pageResult = inventoryService.getInventoryPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
WmsInventoryDO inventory = pageResult.getList().get(0);
assertEquals(100L, inventory.getWarehouseId());
assertEquals(sku.getId(), inventory.getSkuId());
assertEquals(0, new BigDecimal("2.00").compareTo(inventory.getQuantity()));
}
@Test
public void testGetInventoryPage_groupByWarehouse_minQuantity() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00"));
inventoryMapper.insert(createInventory(sku.getId(), 200L, "7.00"));
// 准备参数
WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO();
reqVO.setType(WmsInventoryPageReqVO.TYPE_WAREHOUSE);
reqVO.setMinQuantity(new BigDecimal("6.00"));
// 调用
PageResult<WmsInventoryDO> pageResult = inventoryService.getInventoryPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertEquals(200L, pageResult.getList().get(0).getWarehouseId());
}
@Test
public void testGetInventoryPage_onlyPositiveQuantity() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
inventoryMapper.insert(createInventory(sku.getId(), 100L, "0.00"));
inventoryMapper.insert(createInventory(sku.getId(), 200L, "0.01"));
inventoryMapper.insert(createInventory(sku.getId(), 300L, "1.00"));
// 准备参数
WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO();
reqVO.setType(WmsInventoryPageReqVO.TYPE_WAREHOUSE);
reqVO.setOnlyPositiveQuantity(true);
// 调用
PageResult<WmsInventoryDO> pageResult = inventoryService.getInventoryPage(reqVO);
// 断言
assertEquals(2, pageResult.getTotal());
assertEquals(2, pageResult.getList().size());
assertEquals(200L, pageResult.getList().get(0).getWarehouseId());
assertEquals(0, new BigDecimal("0.01").compareTo(pageResult.getList().get(0).getQuantity()));
assertEquals(300L, pageResult.getList().get(1).getWarehouseId());
assertEquals(0, new BigDecimal("1.00").compareTo(pageResult.getList().get(1).getQuantity()));
}
@Test
public void testGetInventoryPage_sku() {
// mock 数据
WmsItemDO item = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
WmsItemSkuDO sku2 = createSku(item.getId(), "SKU-002", "5kg 箱装");
inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00"));
inventoryMapper.insert(createInventory(sku2.getId(), 100L, "3.00"));
// 准备参数
WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO();
reqVO.setType(WmsInventoryPageReqVO.TYPE_ITEM);
reqVO.setWarehouseId(100L);
reqVO.setSkuId(sku.getId());
// 调用
PageResult<WmsInventoryDO> pageResult = inventoryService.getInventoryPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertEquals(sku.getId(), pageResult.getList().get(0).getSkuId());
}
@Test
public void testGetInventoryPage_orderByItemDimension() {
// mock 数据
WmsItemDO item1 = createItem("ITEM-001", "红富士苹果");
WmsItemSkuDO sku1 = createSku(item1.getId(), "SKU-001", "10kg 箱装");
WmsItemDO item2 = createItem("ITEM-002", "香蕉");
WmsItemSkuDO sku2 = createSku(item2.getId(), "SKU-002", "5kg 箱装");
inventoryMapper.insert(createInventory(sku2.getId(), 100L, "3.00"));
inventoryMapper.insert(createInventory(sku1.getId(), 200L, "2.00"));
inventoryMapper.insert(createInventory(sku1.getId(), 100L, "1.00"));
// 准备参数
WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO();
reqVO.setType(WmsInventoryPageReqVO.TYPE_ITEM);
// 调用
PageResult<WmsInventoryDO> pageResult = inventoryService.getInventoryPage(reqVO);
// 断言
assertEquals(3, pageResult.getTotal());
assertEquals(sku1.getId(), pageResult.getList().get(0).getSkuId());
assertEquals(100L, pageResult.getList().get(0).getWarehouseId());
assertEquals(sku1.getId(), pageResult.getList().get(1).getSkuId());
assertEquals(200L, pageResult.getList().get(1).getWarehouseId());
assertEquals(sku2.getId(), pageResult.getList().get(2).getSkuId());
assertEquals(100L, pageResult.getList().get(2).getWarehouseId());
}
private WmsItemDO createItem(String code, String name) {
WmsItemDO item = WmsItemDO.builder()
.code(code)
.name(name)
.unit("")
.categoryId(1L)
.build();
itemMapper.insert(item);
return item;
}
private WmsItemSkuDO createSku(Long itemId, String code, String name) {
WmsItemSkuDO sku = WmsItemSkuDO.builder()
.itemId(itemId)
.code(code)
.name(name)
.barCode("69010001")
.build();
skuMapper.insert(sku);
return sku;
}
private void mockItemSkuAndItem(WmsItemDO item, WmsItemSkuDO sku) {
when(itemSkuService.validateItemSkuExists(sku.getId())).thenReturn(sku);
when(itemService.validateItemExists(item.getId())).thenReturn(item);
}
private static WmsInventoryDO createInventory(Long skuId, Long warehouseId, String quantity) {
return WmsInventoryDO.builder()
.skuId(skuId)
.warehouseId(warehouseId)
.quantity(new BigDecimal(quantity))
.build();
}
private static WmsInventoryChangeReqDTO createChangeReq(Long skuId, Long warehouseId, String quantity) {
return new WmsInventoryChangeReqDTO()
.setOrderId(1L)
.setOrderNo("RK202605120001")
.setOrderType(WmsOrderTypeEnum.RECEIPT.getType())
.setItems(Collections.singletonList(new WmsInventoryChangeReqDTO.Item()
.setSkuId(skuId)
.setWarehouseId(warehouseId)
.setQuantity(new BigDecimal(quantity))
.setPrice(new BigDecimal("100.00"))
.setTotalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00")))
.setRemark("测试入库")));
}
private static WmsInventoryCheckReqDTO createCheckReq(Long inventoryId, Long skuId, Long warehouseId,
String quantity, String checkQuantity) {
return new WmsInventoryCheckReqDTO()
.setOrderId(2L)
.setOrderNo("PK202605120001")
.setOrderType(WmsOrderTypeEnum.CHECK.getType())
.setItems(Collections.singletonList(new WmsInventoryCheckReqDTO.Item()
.setInventoryId(inventoryId)
.setSkuId(skuId)
.setWarehouseId(warehouseId)
.setQuantity(new BigDecimal(quantity))
.setCheckQuantity(new BigDecimal(checkQuantity))
.setPrice(new BigDecimal("100.00"))
.setRemark("测试盘库")));
}
}

View File

@@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.wms.service.md.item;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand.WmsItemBrandSaveReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemBrandDO;
import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemBrandMapper;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.ITEM_BRAND_NAME_DUPLICATE;
@Import(WmsItemBrandServiceImpl.class)
public class WmsItemBrandServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsItemBrandServiceImpl brandService;
@Resource
private WmsItemBrandMapper brandMapper;
@MockitoBean
private WmsItemService itemService;
@Test
public void testCreateItemBrand_nameDuplicate() {
// mock 数据
brandMapper.insert(createBrand("B001", "华为"));
WmsItemBrandSaveReqVO reqVO = createBrandSaveReqVO(null, "B002", "华为");
// 调用,并断言
assertServiceException(() -> brandService.createItemBrand(reqVO), ITEM_BRAND_NAME_DUPLICATE);
}
@Test
public void testUpdateItemBrand_nameDuplicate() {
// mock 数据
brandMapper.insert(createBrand("B001", "华为"));
WmsItemBrandDO brand = createBrand("B002", "小米");
brandMapper.insert(brand);
WmsItemBrandSaveReqVO reqVO = createBrandSaveReqVO(brand.getId(), "B002", "华为");
// 调用,并断言
assertServiceException(() -> brandService.updateItemBrand(reqVO), ITEM_BRAND_NAME_DUPLICATE);
}
private static WmsItemBrandDO createBrand(String code, String name) {
return WmsItemBrandDO.builder()
.code(code)
.name(name)
.build();
}
private static WmsItemBrandSaveReqVO createBrandSaveReqVO(Long id, String code, String name) {
WmsItemBrandSaveReqVO reqVO = new WmsItemBrandSaveReqVO();
reqVO.setId(id);
reqVO.setCode(code);
reqVO.setName(name);
return reqVO;
}
}

View File

@@ -0,0 +1,100 @@
package cn.iocoder.yudao.module.wms.service.md.warehouse;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehouseSaveReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
import cn.iocoder.yudao.module.wms.dal.mysql.md.warehouse.WmsWarehouseMapper;
import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
import cn.iocoder.yudao.module.wms.service.order.check.WmsCheckOrderService;
import cn.iocoder.yudao.module.wms.service.order.movement.WmsMovementOrderService;
import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderService;
import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.WAREHOUSE_CODE_DUPLICATE;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.WAREHOUSE_HAS_INVENTORY;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.when;
@Import(WmsWarehouseServiceImpl.class)
public class WmsWarehouseServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsWarehouseServiceImpl warehouseService;
@Resource
private WmsWarehouseMapper warehouseMapper;
@MockitoBean
private WmsInventoryService inventoryService;
@MockitoBean
private WmsReceiptOrderService receiptOrderService;
@MockitoBean
private WmsShipmentOrderService shipmentOrderService;
@MockitoBean
private WmsMovementOrderService movementOrderService;
@MockitoBean
private WmsCheckOrderService checkOrderService;
@Test
public void testCreateWarehouse_codeDuplicate() {
// mock 数据
WmsWarehouseDO warehouse = createWarehouse("WH001", "成品仓");
warehouseMapper.insert(warehouse);
// 调用,并断言
WmsWarehouseSaveReqVO reqVO = createWarehouseSaveReqVO();
assertServiceException(() -> warehouseService.createWarehouse(reqVO), WAREHOUSE_CODE_DUPLICATE);
}
@Test
public void testUpdateWarehouse_codeDuplicate() {
// mock 数据
WmsWarehouseDO warehouse = createWarehouse("WH001", "成品仓");
warehouseMapper.insert(warehouse);
WmsWarehouseDO updateWarehouse = createWarehouse("WH002", "原料仓");
warehouseMapper.insert(updateWarehouse);
// 调用,并断言
WmsWarehouseSaveReqVO reqVO = createWarehouseSaveReqVO();
reqVO.setId(updateWarehouse.getId());
assertServiceException(() -> warehouseService.updateWarehouse(reqVO), WAREHOUSE_CODE_DUPLICATE);
}
@Test
public void testDeleteWarehouse_hasInventory() {
// mock 数据
WmsWarehouseDO warehouse = createWarehouse();
warehouseMapper.insert(warehouse);
when(inventoryService.getInventoryCountByWarehouseId(warehouse.getId())).thenReturn(1L);
// 调用,并断言
assertServiceException(() -> warehouseService.deleteWarehouse(warehouse.getId()), WAREHOUSE_HAS_INVENTORY);
assertNotNull(warehouseMapper.selectById(warehouse.getId()));
}
private static WmsWarehouseDO createWarehouse() {
return createWarehouse("WH001", "成品仓");
}
private static WmsWarehouseDO createWarehouse(String code, String name) {
return WmsWarehouseDO.builder()
.code(code)
.name(name)
.sort(1)
.build();
}
private static WmsWarehouseSaveReqVO createWarehouseSaveReqVO() {
WmsWarehouseSaveReqVO reqVO = new WmsWarehouseSaveReqVO();
reqVO.setCode("WH001");
reqVO.setName("半成品仓");
reqVO.setSort(1);
return reqVO;
}
}

View File

@@ -0,0 +1,154 @@
package cn.iocoder.yudao.module.wms.service.order.check;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailSaveReqVO;
import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO;
import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderDetailMapper;
import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_DETAIL_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@Import(WmsCheckOrderDetailServiceImpl.class)
public class WmsCheckOrderDetailServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsCheckOrderDetailServiceImpl checkOrderDetailService;
@Resource
private WmsCheckOrderDetailMapper checkOrderDetailMapper;
@MockitoBean
private WmsItemSkuService itemSkuService;
@Test
public void testCreateCheckOrderDetailList_success() {
// mock 数据
Long orderId = 10L;
WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(
createCheckOrderDetailReqVO(null, 1001L, "10.00", "9.00"),
createCheckOrderDetailReqVO(null, 1002L, "20.00", "21.00"));
// 调用
checkOrderDetailService.createCheckOrderDetailList(orderId, reqVO);
// 断言
List<WmsCheckOrderDetailDO> details = checkOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
assertEquals(orderId, details.get(0).getOrderId());
assertEquals(orderId, details.get(1).getOrderId());
assertEquals(100L, details.get(0).getWarehouseId());
}
@Test
public void testCreateCheckOrderDetailList_ignoreId() {
// mock 数据
WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(
createCheckOrderDetailReqVO(999L, 1001L, "10.00", "9.00"));
// 调用
checkOrderDetailService.createCheckOrderDetailList(10L, reqVO);
// 断言
List<WmsCheckOrderDetailDO> details = checkOrderDetailMapper.selectListByOrderId(10L);
assertEquals(1, details.size());
assertNotNull(details.get(0).getId());
assertEquals(1001L, details.get(0).getSkuId());
}
@Test
public void testUpdateCheckOrderDetailList_diff() {
// mock 数据
Long orderId = 10L;
WmsCheckOrderDetailDO detail01 = createCheckOrderDetail(orderId, 1001L, "10.00", "9.00");
WmsCheckOrderDetailDO detail02 = createCheckOrderDetail(orderId, 1002L, "20.00", "21.00");
checkOrderDetailMapper.insert(detail01);
checkOrderDetailMapper.insert(detail02);
WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(
createCheckOrderDetailReqVO(detail01.getId(), 2001L, "11.00", "8.00"),
createCheckOrderDetailReqVO(null, 2002L, "22.00", "25.00"));
// 调用
checkOrderDetailService.updateCheckOrderDetailList(orderId, reqVO);
// 断言:修改
WmsCheckOrderDetailDO dbUpdateDetail = checkOrderDetailMapper.selectById(detail01.getId());
assertNotNull(dbUpdateDetail);
assertEquals(orderId, dbUpdateDetail.getOrderId());
assertEquals(2001L, dbUpdateDetail.getSkuId());
assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity()));
assertEquals(0, new BigDecimal("8.00").compareTo(dbUpdateDetail.getCheckQuantity()));
assertEquals(0, new BigDecimal("100.00").compareTo(dbUpdateDetail.getPrice()));
assertEquals(100L, dbUpdateDetail.getWarehouseId());
// 断言:新增
List<WmsCheckOrderDetailDO> details = checkOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
WmsCheckOrderDetailDO dbCreateDetail = details.stream()
.filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId()))
.findFirst().orElse(null);
assertNotNull(dbCreateDetail);
assertEquals(orderId, dbCreateDetail.getOrderId());
assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity()));
assertEquals(0, new BigDecimal("25.00").compareTo(dbCreateDetail.getCheckQuantity()));
// 断言:删除
assertNull(checkOrderDetailMapper.selectById(detail02.getId()));
}
@Test
public void testUpdateCheckOrderDetailList_detailNotExists() {
// mock 数据
WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(
createCheckOrderDetailReqVO(999L, 1001L, "10.00", "9.00"));
// 调用,并断言
assertServiceException(() -> checkOrderDetailService.updateCheckOrderDetailList(10L, reqVO),
CHECK_ORDER_DETAIL_NOT_EXISTS);
}
private static WmsCheckOrderSaveReqVO createCheckOrderReqVO(WmsCheckOrderDetailSaveReqVO... details) {
WmsCheckOrderSaveReqVO reqVO = new WmsCheckOrderSaveReqVO();
reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0));
reqVO.setWarehouseId(100L);
reqVO.setDetails(Arrays.asList(details));
return reqVO;
}
private static WmsCheckOrderDetailSaveReqVO createCheckOrderDetailReqVO(Long id, Long skuId,
String quantity, String checkQuantity) {
WmsCheckOrderDetailSaveReqVO reqVO = new WmsCheckOrderDetailSaveReqVO();
reqVO.setId(id);
reqVO.setSkuId(skuId);
reqVO.setInventoryId(300L);
reqVO.setQuantity(new BigDecimal(quantity));
reqVO.setCheckQuantity(new BigDecimal(checkQuantity));
reqVO.setPrice(new BigDecimal("100.00"));
return reqVO;
}
private static WmsCheckOrderDetailDO createCheckOrderDetail(Long orderId, Long skuId,
String quantity, String checkQuantity) {
return WmsCheckOrderDetailDO.builder()
.orderId(orderId)
.skuId(skuId)
.warehouseId(100L)
.inventoryId(300L)
.quantity(new BigDecimal(quantity))
.checkQuantity(new BigDecimal(checkQuantity))
.price(new BigDecimal("100.00"))
.build();
}
}

View File

@@ -0,0 +1,347 @@
package cn.iocoder.yudao.module.wms.service.order.check;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailSaveReqVO;
import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO;
import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderDetailMapper;
import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderMapper;
import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum;
import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum;
import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryCheckReqDTO;
import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_DETAIL_REQUIRED;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_INVENTORY_CHANGED;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_STATUS_NOT_DELETABLE;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_STATUS_NOT_PREPARE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@Import({WmsCheckOrderServiceImpl.class, WmsCheckOrderDetailServiceImpl.class})
public class WmsCheckOrderServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsCheckOrderServiceImpl checkOrderService;
@Resource
private WmsCheckOrderMapper checkOrderMapper;
@Resource
private WmsCheckOrderDetailMapper checkOrderDetailMapper;
@MockitoBean
private WmsWarehouseService warehouseService;
@MockitoBean
private WmsItemSkuService itemSkuService;
@MockitoBean
private WmsInventoryService inventoryService;
@Test
public void testCreateCheckOrder_calculateTotals() {
// mock 数据
WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(100L,
createCheckOrderDetailReqVO(null, 200L, "10.00", "7.00", "5.00"),
createCheckOrderDetailReqVO(null, 201L, "20.00", "25.00", "2.00"));
// 调用
Long orderId = checkOrderService.createCheckOrder(reqVO);
// 断言:盘库单汇总由后端计算
WmsCheckOrderDO order = checkOrderMapper.selectById(orderId);
assertNotNull(order);
assertEquals(0, new BigDecimal("2.00").compareTo(order.getTotalQuantity()));
assertEquals(0, new BigDecimal("90.00").compareTo(order.getTotalPrice()));
assertEquals(0, new BigDecimal("85.00").compareTo(order.getActualPrice()));
// 断言:盘库明细保存单价
List<WmsCheckOrderDetailDO> details = checkOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
assertEquals(0, new BigDecimal("5.00").compareTo(details.get(0).getPrice()));
}
@Test
public void testUpdateCheckOrder_calculateTotals() {
// mock 数据
WmsCheckOrderDO order = createCheckOrder(100L);
checkOrderMapper.insert(order);
WmsCheckOrderDetailDO oldDetail = createCheckOrderDetail(order.getId(), 200L, 100L, "10.00", "7.00");
checkOrderDetailMapper.insert(oldDetail);
WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(100L,
createCheckOrderDetailReqVO(oldDetail.getId(), 200L, "8.00", "10.00", "6.00"),
createCheckOrderDetailReqVO(null, 201L, "2.00", "1.00", "4.00"));
reqVO.setId(order.getId());
reqVO.setNo(order.getNo());
// 调用
checkOrderService.updateCheckOrder(reqVO);
// 断言:盘库单汇总由后端重新计算
WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(0, new BigDecimal("1.00").compareTo(dbOrder.getTotalQuantity()));
assertEquals(0, new BigDecimal("56.00").compareTo(dbOrder.getTotalPrice()));
assertEquals(0, new BigDecimal("64.00").compareTo(dbOrder.getActualPrice()));
List<WmsCheckOrderDetailDO> details = checkOrderDetailMapper.selectListByOrderId(order.getId());
assertEquals(2, details.size());
}
@Test
public void testCompleteCheckOrder_success() {
// mock 数据
Long warehouseId = 100L;
Long skuId = 200L;
WmsCheckOrderDO order = createCheckOrder(warehouseId);
checkOrderMapper.insert(order);
checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), skuId, warehouseId,
"10.00", "7.00"));
// 调用
checkOrderService.completeCheckOrder(order.getId());
// 断言:盘库单
WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus());
// 断言:库存盘点
ArgumentCaptor<WmsInventoryCheckReqDTO> captor = ArgumentCaptor.forClass(WmsInventoryCheckReqDTO.class);
verify(inventoryService).checkInventory(captor.capture());
WmsInventoryCheckReqDTO inventoryReqDTO = captor.getValue();
assertEquals(order.getId(), inventoryReqDTO.getOrderId());
assertEquals(order.getNo(), inventoryReqDTO.getOrderNo());
assertEquals(WmsOrderTypeEnum.CHECK.getType(), inventoryReqDTO.getOrderType());
assertEquals(1, inventoryReqDTO.getItems().size());
assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId());
assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId());
assertEquals(300L, inventoryReqDTO.getItems().get(0).getInventoryId());
assertEquals(0, new BigDecimal("10.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity()));
assertEquals(0, new BigDecimal("7.00").compareTo(inventoryReqDTO.getItems().get(0).getCheckQuantity()));
assertEquals(0, new BigDecimal("30.00").compareTo(inventoryReqDTO.getItems().get(0).getPrice()));
}
@Test
public void testCompleteCheckOrder_zeroDifference() {
// mock 数据
WmsCheckOrderDO order = createCheckOrder(100L);
checkOrderMapper.insert(order);
checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), 200L, 100L,
"10.00", "10.00"));
// 调用
checkOrderService.completeCheckOrder(order.getId());
// 断言
WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus());
verify(inventoryService).checkInventory(any());
}
@Test
public void testCompleteCheckOrder_newInventoryCurrentMissing() {
// mock 数据
Long warehouseId = 100L;
Long skuId = 200L;
WmsCheckOrderDO order = createCheckOrder(warehouseId);
checkOrderMapper.insert(order);
checkOrderDetailMapper.insert(createNewInventoryCheckOrderDetail(order.getId(), skuId, warehouseId,
"0.00", "5.00"));
// 调用
checkOrderService.completeCheckOrder(order.getId());
// 断言
ArgumentCaptor<WmsInventoryCheckReqDTO> captor = ArgumentCaptor.forClass(WmsInventoryCheckReqDTO.class);
verify(inventoryService).checkInventory(captor.capture());
WmsInventoryCheckReqDTO inventoryReqDTO = captor.getValue();
assertEquals(1, inventoryReqDTO.getItems().size());
assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId());
assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId());
assertNull(inventoryReqDTO.getItems().get(0).getInventoryId());
assertEquals(0, new BigDecimal("0.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity()));
assertEquals(0, new BigDecimal("5.00").compareTo(inventoryReqDTO.getItems().get(0).getCheckQuantity()));
}
@Test
public void testCompleteCheckOrder_checkInventoryChanged() {
// mock 数据
Long warehouseId = 100L;
Long skuId = 200L;
WmsCheckOrderDO order = createCheckOrder(warehouseId);
checkOrderMapper.insert(order);
checkOrderDetailMapper.insert(createNewInventoryCheckOrderDetail(order.getId(), skuId, warehouseId,
"0.00", "5.00"));
doThrow(exception(CHECK_ORDER_INVENTORY_CHANGED)).when(inventoryService).checkInventory(any());
// 调用,并断言
assertServiceException(() -> checkOrderService.completeCheckOrder(order.getId()),
CHECK_ORDER_INVENTORY_CHANGED);
assertEquals(WmsOrderStatusEnum.PREPARE.getStatus(),
checkOrderMapper.selectById(order.getId()).getStatus());
}
@Test
public void testCompleteCheckOrder_detailRequired() {
// mock 数据
WmsCheckOrderDO order = createCheckOrder(100L);
checkOrderMapper.insert(order);
// 调用,并断言
assertServiceException(() -> checkOrderService.completeCheckOrder(order.getId()),
CHECK_ORDER_DETAIL_REQUIRED);
verify(inventoryService, never()).checkInventory(any());
}
@Test
public void testCompleteCheckOrder_duplicateComplete() {
// mock 数据
WmsCheckOrderDO order = createCheckOrder(100L);
checkOrderMapper.insert(order);
checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), 200L, 100L,
"10.00", "7.00"));
// 调用
checkOrderService.completeCheckOrder(order.getId());
// 调用,并断言:二次完成不能再次写库存
assertServiceException(() -> checkOrderService.completeCheckOrder(order.getId()),
CHECK_ORDER_STATUS_NOT_PREPARE);
verify(inventoryService).checkInventory(any());
}
@Test
public void testCancelCheckOrder_success() {
// mock 数据
WmsCheckOrderDO order = createCheckOrder(100L);
checkOrderMapper.insert(order);
// 调用
checkOrderService.cancelCheckOrder(order.getId());
// 断言
WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus());
verify(inventoryService, never()).checkInventory(any());
}
@Test
public void testUpdateByIdAndStatus_statusNotMatch() {
// mock 数据
WmsCheckOrderDO order = createCheckOrder(100L);
checkOrderMapper.insert(order);
// 调用
int updateCount = checkOrderMapper.updateByIdAndStatus(order.getId(),
WmsOrderStatusEnum.FINISHED.getStatus(),
new WmsCheckOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus()));
// 断言
assertEquals(0, updateCount);
assertEquals(WmsOrderStatusEnum.PREPARE.getStatus(),
checkOrderMapper.selectById(order.getId()).getStatus());
}
@Test
public void testDeleteCheckOrder_canceled() {
// mock 数据
WmsCheckOrderDO order = createCheckOrder(100L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus());
checkOrderMapper.insert(order);
checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), 200L, 100L,
"10.00", "8.00"));
// 调用
checkOrderService.deleteCheckOrder(order.getId());
// 断言
assertNull(checkOrderMapper.selectById(order.getId()));
assertEquals(0, checkOrderDetailMapper.selectListByOrderId(order.getId()).size());
}
@Test
public void testDeleteCheckOrder_finished() {
// mock 数据
WmsCheckOrderDO order = createCheckOrder(100L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus());
checkOrderMapper.insert(order);
// 调用,并断言
assertServiceException(() -> checkOrderService.deleteCheckOrder(order.getId()),
CHECK_ORDER_STATUS_NOT_DELETABLE);
}
private static WmsCheckOrderDO createCheckOrder(Long warehouseId) {
return new WmsCheckOrderDO()
.setNo("PK202605120001")
.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0))
.setStatus(WmsOrderStatusEnum.PREPARE.getStatus())
.setWarehouseId(warehouseId)
.setTotalQuantity(new BigDecimal("-3.00"))
.setTotalPrice(new BigDecimal("300.00"))
.setActualPrice(new BigDecimal("210.00"));
}
private static WmsCheckOrderDetailDO createCheckOrderDetail(Long orderId, Long skuId, Long warehouseId,
String quantity, String checkQuantity) {
return WmsCheckOrderDetailDO.builder()
.orderId(orderId)
.skuId(skuId)
.warehouseId(warehouseId)
.inventoryId(300L)
.quantity(new BigDecimal(quantity))
.checkQuantity(new BigDecimal(checkQuantity))
.price(new BigDecimal("30.00"))
.build();
}
private static WmsCheckOrderDetailDO createNewInventoryCheckOrderDetail(Long orderId, Long skuId, Long warehouseId,
String quantity, String checkQuantity) {
return WmsCheckOrderDetailDO.builder()
.orderId(orderId)
.skuId(skuId)
.warehouseId(warehouseId)
.quantity(new BigDecimal(quantity))
.checkQuantity(new BigDecimal(checkQuantity))
.price(new BigDecimal("30.00"))
.build();
}
private static WmsCheckOrderSaveReqVO createCheckOrderReqVO(Long warehouseId, WmsCheckOrderDetailSaveReqVO... details) {
WmsCheckOrderSaveReqVO reqVO = new WmsCheckOrderSaveReqVO();
reqVO.setNo("PK202605120002");
reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0));
reqVO.setWarehouseId(warehouseId);
reqVO.setDetails(Arrays.asList(details));
return reqVO;
}
private static WmsCheckOrderDetailSaveReqVO createCheckOrderDetailReqVO(Long id, Long skuId,
String quantity, String checkQuantity,
String price) {
WmsCheckOrderDetailSaveReqVO reqVO = new WmsCheckOrderDetailSaveReqVO();
reqVO.setId(id);
reqVO.setSkuId(skuId);
reqVO.setInventoryId(300L);
reqVO.setQuantity(new BigDecimal(quantity));
reqVO.setCheckQuantity(new BigDecimal(checkQuantity));
reqVO.setPrice(new BigDecimal(price));
return reqVO;
}
}

View File

@@ -0,0 +1,155 @@
package cn.iocoder.yudao.module.wms.service.order.movement;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailSaveReqVO;
import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO;
import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderDetailMapper;
import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_DETAIL_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@Import(WmsMovementOrderDetailServiceImpl.class)
public class WmsMovementOrderDetailServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsMovementOrderDetailServiceImpl movementOrderDetailService;
@Resource
private WmsMovementOrderDetailMapper movementOrderDetailMapper;
@MockitoBean
private WmsItemSkuService itemSkuService;
@Test
public void testCreateMovementOrderDetailList_success() {
// mock 数据
Long orderId = 10L;
WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(
createMovementOrderDetailReqVO(null, 1001L, "1.00"),
createMovementOrderDetailReqVO(null, 1002L, "2.00"));
// 调用
movementOrderDetailService.createMovementOrderDetailList(orderId, reqVO);
// 断言
List<WmsMovementOrderDetailDO> details = movementOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
assertEquals(orderId, details.get(0).getOrderId());
assertEquals(orderId, details.get(1).getOrderId());
assertEquals(100L, details.get(0).getSourceWarehouseId());
assertEquals(200L, details.get(0).getTargetWarehouseId());
assertEquals(0, new BigDecimal("100.00").compareTo(details.get(0).getTotalPrice()));
assertEquals(0, new BigDecimal("200.00").compareTo(details.get(1).getTotalPrice()));
}
@Test
public void testCreateMovementOrderDetailList_ignoreId() {
// mock 数据
WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(
createMovementOrderDetailReqVO(999L, 1001L, "1.00"));
// 调用
movementOrderDetailService.createMovementOrderDetailList(10L, reqVO);
// 断言
List<WmsMovementOrderDetailDO> details = movementOrderDetailMapper.selectListByOrderId(10L);
assertEquals(1, details.size());
assertNotNull(details.get(0).getId());
assertEquals(1001L, details.get(0).getSkuId());
}
@Test
public void testUpdateMovementOrderDetailList_diff() {
// mock 数据
Long orderId = 10L;
WmsMovementOrderDetailDO detail01 = createMovementOrderDetail(orderId, 1001L, "1.00");
WmsMovementOrderDetailDO detail02 = createMovementOrderDetail(orderId, 1002L, "2.00");
movementOrderDetailMapper.insert(detail01);
movementOrderDetailMapper.insert(detail02);
WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(
createMovementOrderDetailReqVO(detail01.getId(), 2001L, "11.00"),
createMovementOrderDetailReqVO(null, 2002L, "22.00"));
// 调用
movementOrderDetailService.updateMovementOrderDetailList(orderId, reqVO);
// 断言:修改
WmsMovementOrderDetailDO dbUpdateDetail = movementOrderDetailMapper.selectById(detail01.getId());
assertNotNull(dbUpdateDetail);
assertEquals(orderId, dbUpdateDetail.getOrderId());
assertEquals(2001L, dbUpdateDetail.getSkuId());
assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity()));
assertEquals(0, new BigDecimal("1100.00").compareTo(dbUpdateDetail.getTotalPrice()));
assertEquals(100L, dbUpdateDetail.getSourceWarehouseId());
assertEquals(200L, dbUpdateDetail.getTargetWarehouseId());
// 断言:新增
List<WmsMovementOrderDetailDO> details = movementOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
WmsMovementOrderDetailDO dbCreateDetail = details.stream()
.filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId()))
.findFirst().orElse(null);
assertNotNull(dbCreateDetail);
assertEquals(orderId, dbCreateDetail.getOrderId());
assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity()));
assertEquals(0, new BigDecimal("2200.00").compareTo(dbCreateDetail.getTotalPrice()));
// 断言:删除
assertNull(movementOrderDetailMapper.selectById(detail02.getId()));
}
@Test
public void testUpdateMovementOrderDetailList_detailNotExists() {
// mock 数据
WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(
createMovementOrderDetailReqVO(999L, 1001L, "1.00"));
// 调用,并断言
assertServiceException(() -> movementOrderDetailService.updateMovementOrderDetailList(10L, reqVO),
MOVEMENT_ORDER_DETAIL_NOT_EXISTS);
}
private static WmsMovementOrderSaveReqVO createMovementOrderReqVO(WmsMovementOrderDetailSaveReqVO... details) {
WmsMovementOrderSaveReqVO reqVO = new WmsMovementOrderSaveReqVO();
reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0));
reqVO.setSourceWarehouseId(100L);
reqVO.setTargetWarehouseId(200L);
reqVO.setDetails(Arrays.asList(details));
return reqVO;
}
private static WmsMovementOrderDetailSaveReqVO createMovementOrderDetailReqVO(Long id, Long skuId, String quantity) {
WmsMovementOrderDetailSaveReqVO reqVO = new WmsMovementOrderDetailSaveReqVO();
reqVO.setId(id);
reqVO.setSkuId(skuId);
reqVO.setQuantity(new BigDecimal(quantity));
reqVO.setPrice(new BigDecimal("100.00"));
reqVO.setTotalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00")));
return reqVO;
}
private static WmsMovementOrderDetailDO createMovementOrderDetail(Long orderId, Long skuId, String quantity) {
return WmsMovementOrderDetailDO.builder()
.orderId(orderId)
.skuId(skuId)
.sourceWarehouseId(100L)
.targetWarehouseId(200L)
.quantity(new BigDecimal(quantity))
.price(new BigDecimal("100.00"))
.totalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00")))
.build();
}
}

View File

@@ -0,0 +1,291 @@
package cn.iocoder.yudao.module.wms.service.order.movement;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailSaveReqVO;
import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO;
import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderDetailMapper;
import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderMapper;
import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum;
import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum;
import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO;
import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_DETAIL_REQUIRED;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_STATUS_NOT_DELETABLE;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_STATUS_NOT_PREPARE;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_WAREHOUSE_SAME;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@Import({WmsMovementOrderServiceImpl.class, WmsMovementOrderDetailServiceImpl.class})
public class WmsMovementOrderServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsMovementOrderServiceImpl movementOrderService;
@Resource
private WmsMovementOrderMapper movementOrderMapper;
@Resource
private WmsMovementOrderDetailMapper movementOrderDetailMapper;
@MockitoBean
private WmsWarehouseService warehouseService;
@MockitoBean
private WmsItemSkuService itemSkuService;
@MockitoBean
private WmsInventoryService inventoryService;
@Test
public void testCreateMovementOrder_calculateTotal() {
// mock 数据
WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(null, 100L, 200L,
createMovementOrderDetailReqVO(null, 3001L, "1.50", "10.00", "16.00"),
createMovementOrderDetailReqVO(null, 3002L, "2.50", "20.00", "51.00"));
// 调用
Long orderId = movementOrderService.createMovementOrder(reqVO);
// 断言
WmsMovementOrderDO dbOrder = movementOrderMapper.selectById(orderId);
assertNotNull(dbOrder);
assertEquals(0, new BigDecimal("4.00").compareTo(dbOrder.getTotalQuantity()));
assertEquals(0, new BigDecimal("67.00").compareTo(dbOrder.getTotalPrice()));
List<WmsMovementOrderDetailDO> details = movementOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
assertEquals(0, new BigDecimal("16.00").compareTo(details.get(0).getTotalPrice()));
}
@Test
public void testUpdateMovementOrder_calculateTotal() {
// mock 数据
WmsMovementOrderDO order = createMovementOrder(100L, 200L);
movementOrderMapper.insert(order);
WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(order.getId(), 100L, 200L,
createMovementOrderDetailReqVO(null, 3001L, "3.00", "30.00", "88.00"));
// 调用
movementOrderService.updateMovementOrder(reqVO);
// 断言
WmsMovementOrderDO dbOrder = movementOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(0, new BigDecimal("3.00").compareTo(dbOrder.getTotalQuantity()));
assertEquals(0, new BigDecimal("88.00").compareTo(dbOrder.getTotalPrice()));
List<WmsMovementOrderDetailDO> details = movementOrderDetailMapper.selectListByOrderId(order.getId());
assertEquals(1, details.size());
assertEquals(0, new BigDecimal("88.00").compareTo(details.get(0).getTotalPrice()));
}
@Test
public void testCompleteMovementOrder_success() {
// mock 数据
Long sourceWarehouseId = 100L;
Long targetWarehouseId = 200L;
Long skuId = 300L;
WmsMovementOrderDO order = createMovementOrder(sourceWarehouseId, targetWarehouseId);
movementOrderMapper.insert(order);
movementOrderDetailMapper.insert(createMovementOrderDetail(order.getId(), skuId,
sourceWarehouseId, targetWarehouseId));
// 调用
movementOrderService.completeMovementOrder(order.getId());
// 断言:移库单
WmsMovementOrderDO dbOrder = movementOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus());
// 断言:库存变更
ArgumentCaptor<WmsInventoryChangeReqDTO> captor = ArgumentCaptor.forClass(WmsInventoryChangeReqDTO.class);
verify(inventoryService).changeInventory(captor.capture());
WmsInventoryChangeReqDTO inventoryReqDTO = captor.getValue();
assertEquals(order.getId(), inventoryReqDTO.getOrderId());
assertEquals(order.getNo(), inventoryReqDTO.getOrderNo());
assertEquals(WmsOrderTypeEnum.MOVEMENT.getType(), inventoryReqDTO.getOrderType());
assertEquals(2, inventoryReqDTO.getItems().size());
assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId());
assertEquals(sourceWarehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId());
assertEquals(0, new BigDecimal("-2.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity()));
assertEquals(0, new BigDecimal("-40.00").compareTo(inventoryReqDTO.getItems().get(0).getTotalPrice()));
assertEquals(skuId, inventoryReqDTO.getItems().get(1).getSkuId());
assertEquals(targetWarehouseId, inventoryReqDTO.getItems().get(1).getWarehouseId());
assertEquals(0, new BigDecimal("2.00").compareTo(inventoryReqDTO.getItems().get(1).getQuantity()));
assertEquals(0, new BigDecimal("40.00").compareTo(inventoryReqDTO.getItems().get(1).getTotalPrice()));
}
@Test
public void testCompleteMovementOrder_detailRequired() {
// mock 数据
WmsMovementOrderDO order = createMovementOrder(100L, 200L);
movementOrderMapper.insert(order);
// 调用,并断言
assertServiceException(() -> movementOrderService.completeMovementOrder(order.getId()),
MOVEMENT_ORDER_DETAIL_REQUIRED);
verify(inventoryService, never()).changeInventory(any());
}
@Test
public void testCompleteMovementOrder_duplicateComplete() {
// mock 数据
WmsMovementOrderDO order = createMovementOrder(100L, 200L);
movementOrderMapper.insert(order);
movementOrderDetailMapper.insert(createMovementOrderDetail(order.getId(), 300L, 100L, 200L));
// 调用
movementOrderService.completeMovementOrder(order.getId());
// 调用,并断言:二次完成不能再次写库存
assertServiceException(() -> movementOrderService.completeMovementOrder(order.getId()),
MOVEMENT_ORDER_STATUS_NOT_PREPARE);
verify(inventoryService).changeInventory(any());
}
@Test
public void testCreateMovementOrder_sameWarehouse() {
// 准备参数
WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(100L, 100L);
// 调用,并断言
assertServiceException(() -> movementOrderService.createMovementOrder(reqVO), MOVEMENT_ORDER_WAREHOUSE_SAME);
verify(inventoryService, never()).changeInventory(any());
}
@Test
public void testCancelMovementOrder_success() {
// mock 数据
WmsMovementOrderDO order = createMovementOrder(100L, 200L);
movementOrderMapper.insert(order);
// 调用
movementOrderService.cancelMovementOrder(order.getId());
// 断言
WmsMovementOrderDO dbOrder = movementOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus());
verify(inventoryService, never()).changeInventory(any());
}
@Test
public void testUpdateByIdAndStatus_statusNotMatch() {
// mock 数据
WmsMovementOrderDO order = createMovementOrder(100L, 200L);
movementOrderMapper.insert(order);
// 调用
int updateCount = movementOrderMapper.updateByIdAndStatus(order.getId(),
WmsOrderStatusEnum.FINISHED.getStatus(),
new WmsMovementOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus()));
// 断言
assertEquals(0, updateCount);
assertEquals(WmsOrderStatusEnum.PREPARE.getStatus(),
movementOrderMapper.selectById(order.getId()).getStatus());
}
@Test
public void testDeleteMovementOrder_canceled() {
// mock 数据
WmsMovementOrderDO order = createMovementOrder(100L, 200L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus());
movementOrderMapper.insert(order);
movementOrderDetailMapper.insert(createMovementOrderDetail(order.getId(), 300L, 100L, 200L));
// 调用
movementOrderService.deleteMovementOrder(order.getId());
// 断言
assertNull(movementOrderMapper.selectById(order.getId()));
assertEquals(0, movementOrderDetailMapper.selectListByOrderId(order.getId()).size());
}
@Test
public void testDeleteMovementOrder_finished() {
// mock 数据
WmsMovementOrderDO order = createMovementOrder(100L, 200L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus());
movementOrderMapper.insert(order);
// 调用,并断言
assertServiceException(() -> movementOrderService.deleteMovementOrder(order.getId()),
MOVEMENT_ORDER_STATUS_NOT_DELETABLE);
}
private static WmsMovementOrderDO createMovementOrder(Long sourceWarehouseId, Long targetWarehouseId) {
return new WmsMovementOrderDO()
.setNo("YK202605120001")
.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0))
.setStatus(WmsOrderStatusEnum.PREPARE.getStatus())
.setSourceWarehouseId(sourceWarehouseId)
.setTargetWarehouseId(targetWarehouseId)
.setTotalQuantity(new BigDecimal("2.00"))
.setTotalPrice(new BigDecimal("20.00"));
}
private static WmsMovementOrderDetailDO createMovementOrderDetail(Long orderId, Long skuId,
Long sourceWarehouseId, Long targetWarehouseId) {
return WmsMovementOrderDetailDO.builder()
.orderId(orderId)
.skuId(skuId)
.sourceWarehouseId(sourceWarehouseId)
.targetWarehouseId(targetWarehouseId)
.quantity(new BigDecimal("2.00"))
.price(new BigDecimal("20.00"))
.totalPrice(new BigDecimal("40.00"))
.build();
}
private static WmsMovementOrderSaveReqVO createMovementOrderReqVO(Long sourceWarehouseId, Long targetWarehouseId) {
return createMovementOrderReqVO(null, sourceWarehouseId, targetWarehouseId);
}
private static WmsMovementOrderSaveReqVO createMovementOrderReqVO(Long id, Long sourceWarehouseId,
Long targetWarehouseId,
WmsMovementOrderDetailSaveReqVO... details) {
WmsMovementOrderSaveReqVO reqVO = new WmsMovementOrderSaveReqVO();
reqVO.setId(id);
reqVO.setNo("YK202605120001");
reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0));
reqVO.setSourceWarehouseId(sourceWarehouseId);
reqVO.setTargetWarehouseId(targetWarehouseId);
reqVO.setDetails(Arrays.asList(details));
return reqVO;
}
private static WmsMovementOrderDetailSaveReqVO createMovementOrderDetailReqVO(Long id, Long skuId,
String quantity, String price) {
return createMovementOrderDetailReqVO(id, skuId, quantity, price, null);
}
private static WmsMovementOrderDetailSaveReqVO createMovementOrderDetailReqVO(Long id, Long skuId,
String quantity, String price,
String totalPrice) {
WmsMovementOrderDetailSaveReqVO reqVO = new WmsMovementOrderDetailSaveReqVO();
reqVO.setId(id);
reqVO.setSkuId(skuId);
reqVO.setQuantity(new BigDecimal(quantity));
reqVO.setPrice(new BigDecimal(price));
if (totalPrice != null) {
reqVO.setTotalPrice(new BigDecimal(totalPrice));
}
return reqVO;
}
}

View File

@@ -0,0 +1,149 @@
package cn.iocoder.yudao.module.wms.service.order.receipt;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailSaveReqVO;
import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO;
import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderDetailMapper;
import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.RECEIPT_ORDER_DETAIL_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@Import(WmsReceiptOrderDetailServiceImpl.class)
public class WmsReceiptOrderDetailServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsReceiptOrderDetailServiceImpl receiptOrderDetailService;
@Resource
private WmsReceiptOrderDetailMapper receiptOrderDetailMapper;
@MockitoBean
private WmsItemSkuService itemSkuService;
@Test
public void testCreateReceiptOrderDetailList_success() {
// mock 数据
Long orderId = 10L;
WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderReqVO(
createReceiptOrderDetailReqVO(null, 1001L, "1.00"),
createReceiptOrderDetailReqVO(null, 1002L, "2.00"));
// 调用
receiptOrderDetailService.createReceiptOrderDetailList(orderId, reqVO);
// 断言
List<WmsReceiptOrderDetailDO> details = receiptOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
assertEquals(orderId, details.get(0).getOrderId());
assertEquals(orderId, details.get(1).getOrderId());
assertEquals(0, new BigDecimal("100.00").compareTo(details.get(0).getTotalPrice()));
assertEquals(0, new BigDecimal("200.00").compareTo(details.get(1).getTotalPrice()));
}
@Test
public void testCreateReceiptOrderDetailList_ignoreId() {
// mock 数据
WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderReqVO(
createReceiptOrderDetailReqVO(999L, 1001L, "1.00"));
// 调用
receiptOrderDetailService.createReceiptOrderDetailList(10L, reqVO);
// 断言
List<WmsReceiptOrderDetailDO> details = receiptOrderDetailMapper.selectListByOrderId(10L);
assertEquals(1, details.size());
assertNotNull(details.get(0).getId());
assertEquals(1001L, details.get(0).getSkuId());
}
@Test
public void testUpdateReceiptOrderDetailList_diff() {
// mock 数据
Long orderId = 10L;
WmsReceiptOrderDetailDO detail01 = createReceiptOrderDetail(orderId, 1001L, "1.00");
WmsReceiptOrderDetailDO detail02 = createReceiptOrderDetail(orderId, 1002L, "2.00");
receiptOrderDetailMapper.insert(detail01);
receiptOrderDetailMapper.insert(detail02);
WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderReqVO(
createReceiptOrderDetailReqVO(detail01.getId(), 2001L, "11.00"),
createReceiptOrderDetailReqVO(null, 2002L, "22.00"));
// 调用
receiptOrderDetailService.updateReceiptOrderDetailList(orderId, reqVO);
// 断言:修改
WmsReceiptOrderDetailDO dbUpdateDetail = receiptOrderDetailMapper.selectById(detail01.getId());
assertNotNull(dbUpdateDetail);
assertEquals(orderId, dbUpdateDetail.getOrderId());
assertEquals(2001L, dbUpdateDetail.getSkuId());
assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity()));
assertEquals(0, new BigDecimal("1100.00").compareTo(dbUpdateDetail.getTotalPrice()));
// 断言:新增
List<WmsReceiptOrderDetailDO> details = receiptOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
WmsReceiptOrderDetailDO dbCreateDetail = details.stream()
.filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId()))
.findFirst().orElse(null);
assertNotNull(dbCreateDetail);
assertEquals(orderId, dbCreateDetail.getOrderId());
assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity()));
assertEquals(0, new BigDecimal("2200.00").compareTo(dbCreateDetail.getTotalPrice()));
// 断言:删除
assertNull(receiptOrderDetailMapper.selectById(detail02.getId()));
}
@Test
public void testUpdateReceiptOrderDetailList_detailNotExists() {
// mock 数据
WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderReqVO(
createReceiptOrderDetailReqVO(999L, 1001L, "1.00"));
// 调用,并断言
assertServiceException(() -> receiptOrderDetailService.updateReceiptOrderDetailList(10L, reqVO),
RECEIPT_ORDER_DETAIL_NOT_EXISTS);
}
private static WmsReceiptOrderSaveReqVO createReceiptOrderReqVO(WmsReceiptOrderDetailSaveReqVO... details) {
WmsReceiptOrderSaveReqVO reqVO = new WmsReceiptOrderSaveReqVO();
reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0));
reqVO.setWarehouseId(100L);
reqVO.setDetails(Arrays.asList(details));
return reqVO;
}
private static WmsReceiptOrderDetailSaveReqVO createReceiptOrderDetailReqVO(Long id, Long skuId, String quantity) {
WmsReceiptOrderDetailSaveReqVO reqVO = new WmsReceiptOrderDetailSaveReqVO();
reqVO.setId(id);
reqVO.setSkuId(skuId);
reqVO.setQuantity(new BigDecimal(quantity));
reqVO.setPrice(new BigDecimal("100.00"));
reqVO.setTotalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00")));
return reqVO;
}
private static WmsReceiptOrderDetailDO createReceiptOrderDetail(Long orderId, Long skuId, String quantity) {
return WmsReceiptOrderDetailDO.builder()
.orderId(orderId)
.skuId(skuId)
.warehouseId(100L)
.quantity(new BigDecimal(quantity))
.price(new BigDecimal("100.00"))
.totalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00")))
.build();
}
}

View File

@@ -0,0 +1,271 @@
package cn.iocoder.yudao.module.wms.service.order.receipt;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailSaveReqVO;
import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO;
import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderDetailMapper;
import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderMapper;
import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum;
import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum;
import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderTypeEnum;
import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO;
import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService;
import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.RECEIPT_ORDER_DETAIL_REQUIRED;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.RECEIPT_ORDER_STATUS_NOT_PREPARE;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.RECEIPT_ORDER_STATUS_NOT_DELETABLE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@Import({WmsReceiptOrderServiceImpl.class, WmsReceiptOrderDetailServiceImpl.class})
public class WmsReceiptOrderServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsReceiptOrderServiceImpl receiptOrderService;
@Resource
private WmsReceiptOrderMapper receiptOrderMapper;
@Resource
private WmsReceiptOrderDetailMapper receiptOrderDetailMapper;
@MockitoBean
private WmsWarehouseService warehouseService;
@MockitoBean
private WmsMerchantService merchantService;
@MockitoBean
private WmsItemSkuService itemSkuService;
@MockitoBean
private WmsInventoryService inventoryService;
@Test
public void testCreateReceiptOrder_calculateTotal() {
// mock 数据
WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderSaveReqVO(null,
createReceiptOrderDetailReqVO(null, 2001L, "1.50", "10.00", "16.00"),
createReceiptOrderDetailReqVO(null, 2002L, "2.50", "20.00", "51.00"));
// 调用
Long orderId = receiptOrderService.createReceiptOrder(reqVO);
// 断言
WmsReceiptOrderDO dbOrder = receiptOrderMapper.selectById(orderId);
assertNotNull(dbOrder);
assertEquals(0, new BigDecimal("4.00").compareTo(dbOrder.getTotalQuantity()));
assertEquals(0, new BigDecimal("67.00").compareTo(dbOrder.getTotalPrice()));
List<WmsReceiptOrderDetailDO> details = receiptOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
assertEquals(0, new BigDecimal("16.00").compareTo(details.get(0).getTotalPrice()));
}
@Test
public void testUpdateReceiptOrder_calculateTotal() {
// mock 数据
WmsReceiptOrderDO order = createReceiptOrder(100L);
receiptOrderMapper.insert(order);
WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderSaveReqVO(order.getId(),
createReceiptOrderDetailReqVO(null, 2001L, "3.00", "30.00", "88.00"));
// 调用
receiptOrderService.updateReceiptOrder(reqVO);
// 断言
WmsReceiptOrderDO dbOrder = receiptOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(0, new BigDecimal("3.00").compareTo(dbOrder.getTotalQuantity()));
assertEquals(0, new BigDecimal("88.00").compareTo(dbOrder.getTotalPrice()));
List<WmsReceiptOrderDetailDO> details = receiptOrderDetailMapper.selectListByOrderId(order.getId());
assertEquals(1, details.size());
assertEquals(0, new BigDecimal("88.00").compareTo(details.get(0).getTotalPrice()));
}
@Test
public void testCompleteReceiptOrder_success() {
// mock 数据
Long warehouseId = 100L;
Long skuId = 200L;
WmsReceiptOrderDO order = createReceiptOrder(warehouseId);
receiptOrderMapper.insert(order);
receiptOrderDetailMapper.insert(createReceiptOrderDetail(order.getId(), skuId, warehouseId));
// 调用
receiptOrderService.completeReceiptOrder(order.getId());
// 断言:入库单
WmsReceiptOrderDO dbOrder = receiptOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus());
// 断言:库存变更
ArgumentCaptor<WmsInventoryChangeReqDTO> captor = ArgumentCaptor.forClass(WmsInventoryChangeReqDTO.class);
verify(inventoryService).changeInventory(captor.capture());
WmsInventoryChangeReqDTO inventoryReqDTO = captor.getValue();
assertEquals(order.getId(), inventoryReqDTO.getOrderId());
assertEquals(order.getNo(), inventoryReqDTO.getOrderNo());
assertEquals(WmsOrderTypeEnum.RECEIPT.getType(), inventoryReqDTO.getOrderType());
assertEquals(1, inventoryReqDTO.getItems().size());
assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId());
assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId());
assertEquals(0, new BigDecimal("2.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity()));
assertEquals(0, new BigDecimal("40.00").compareTo(inventoryReqDTO.getItems().get(0).getTotalPrice()));
}
@Test
public void testCompleteReceiptOrder_detailRequired() {
// mock 数据
WmsReceiptOrderDO order = createReceiptOrder(100L);
receiptOrderMapper.insert(order);
// 调用,并断言
assertServiceException(() -> receiptOrderService.completeReceiptOrder(order.getId()),
RECEIPT_ORDER_DETAIL_REQUIRED);
verify(inventoryService, never()).changeInventory(any());
}
@Test
public void testCompleteReceiptOrder_duplicateComplete() {
// mock 数据
WmsReceiptOrderDO order = createReceiptOrder(100L);
receiptOrderMapper.insert(order);
receiptOrderDetailMapper.insert(createReceiptOrderDetail(order.getId(), 200L, 100L));
// 调用
receiptOrderService.completeReceiptOrder(order.getId());
// 调用,并断言:二次完成不能再次写库存
assertServiceException(() -> receiptOrderService.completeReceiptOrder(order.getId()),
RECEIPT_ORDER_STATUS_NOT_PREPARE);
verify(inventoryService).changeInventory(any());
}
@Test
public void testCancelReceiptOrder_success() {
// mock 数据
WmsReceiptOrderDO order = createReceiptOrder(100L);
receiptOrderMapper.insert(order);
// 调用
receiptOrderService.cancelReceiptOrder(order.getId());
// 断言
WmsReceiptOrderDO dbOrder = receiptOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus());
verify(inventoryService, never()).changeInventory(any());
}
@Test
public void testUpdateByIdAndStatus_statusNotMatch() {
// mock 数据
WmsReceiptOrderDO order = createReceiptOrder(100L);
receiptOrderMapper.insert(order);
// 调用
int updateCount = receiptOrderMapper.updateByIdAndStatus(order.getId(),
WmsOrderStatusEnum.FINISHED.getStatus(),
new WmsReceiptOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus()));
// 断言
assertEquals(0, updateCount);
assertEquals(WmsOrderStatusEnum.PREPARE.getStatus(),
receiptOrderMapper.selectById(order.getId()).getStatus());
}
@Test
public void testDeleteReceiptOrder_canceled() {
// mock 数据
WmsReceiptOrderDO order = createReceiptOrder(100L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus());
receiptOrderMapper.insert(order);
receiptOrderDetailMapper.insert(createReceiptOrderDetail(order.getId(), 200L, 100L));
// 调用
receiptOrderService.deleteReceiptOrder(order.getId());
// 断言
assertNull(receiptOrderMapper.selectById(order.getId()));
assertEquals(0, receiptOrderDetailMapper.selectListByOrderId(order.getId()).size());
}
@Test
public void testDeleteReceiptOrder_finished() {
// mock 数据
WmsReceiptOrderDO order = createReceiptOrder(100L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus());
receiptOrderMapper.insert(order);
// 调用,并断言
assertServiceException(() -> receiptOrderService.deleteReceiptOrder(order.getId()),
RECEIPT_ORDER_STATUS_NOT_DELETABLE);
}
private static WmsReceiptOrderDO createReceiptOrder(Long warehouseId) {
return new WmsReceiptOrderDO()
.setNo("RK202605120001")
.setType(WmsReceiptOrderTypeEnum.PURCHASE.getType())
.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0))
.setStatus(WmsOrderStatusEnum.PREPARE.getStatus())
.setWarehouseId(warehouseId)
.setTotalQuantity(new BigDecimal("2.00"))
.setTotalPrice(new BigDecimal("20.00"));
}
private static WmsReceiptOrderSaveReqVO createReceiptOrderSaveReqVO(Long id,
WmsReceiptOrderDetailSaveReqVO... details) {
WmsReceiptOrderSaveReqVO reqVO = new WmsReceiptOrderSaveReqVO();
reqVO.setId(id);
reqVO.setNo("RK202605120001");
reqVO.setType(WmsReceiptOrderTypeEnum.PURCHASE.getType());
reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0));
reqVO.setWarehouseId(100L);
reqVO.setDetails(Arrays.asList(details));
return reqVO;
}
private static WmsReceiptOrderDetailSaveReqVO createReceiptOrderDetailReqVO(Long id, Long skuId,
String quantity, String price) {
return createReceiptOrderDetailReqVO(id, skuId, quantity, price, null);
}
private static WmsReceiptOrderDetailSaveReqVO createReceiptOrderDetailReqVO(Long id, Long skuId,
String quantity, String price,
String totalPrice) {
WmsReceiptOrderDetailSaveReqVO reqVO = new WmsReceiptOrderDetailSaveReqVO();
reqVO.setId(id);
reqVO.setSkuId(skuId);
reqVO.setQuantity(new BigDecimal(quantity));
reqVO.setPrice(new BigDecimal(price));
if (totalPrice != null) {
reqVO.setTotalPrice(new BigDecimal(totalPrice));
}
return reqVO;
}
private static WmsReceiptOrderDetailDO createReceiptOrderDetail(Long orderId, Long skuId, Long warehouseId) {
return WmsReceiptOrderDetailDO.builder()
.orderId(orderId)
.skuId(skuId)
.warehouseId(warehouseId)
.quantity(new BigDecimal("2.00"))
.price(new BigDecimal("20.00"))
.totalPrice(new BigDecimal("40.00"))
.build();
}
}

View File

@@ -0,0 +1,150 @@
package cn.iocoder.yudao.module.wms.service.order.shipment;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailSaveReqVO;
import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO;
import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderDetailMapper;
import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_DETAIL_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@Import(WmsShipmentOrderDetailServiceImpl.class)
public class WmsShipmentOrderDetailServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsShipmentOrderDetailServiceImpl shipmentOrderDetailService;
@Resource
private WmsShipmentOrderDetailMapper shipmentOrderDetailMapper;
@MockitoBean
private WmsItemSkuService itemSkuService;
@Test
public void testCreateShipmentOrderDetailList_success() {
// mock 数据
Long orderId = 10L;
WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO(
createShipmentOrderDetailReqVO(null, 1001L, "1.00"),
createShipmentOrderDetailReqVO(null, 1002L, "2.00"));
// 调用
shipmentOrderDetailService.createShipmentOrderDetailList(orderId, reqVO);
// 断言
List<WmsShipmentOrderDetailDO> details = shipmentOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
assertEquals(orderId, details.get(0).getOrderId());
assertEquals(orderId, details.get(1).getOrderId());
assertEquals(100L, details.get(0).getWarehouseId());
assertEquals(0, new BigDecimal("100.00").compareTo(details.get(0).getTotalPrice()));
assertEquals(0, new BigDecimal("200.00").compareTo(details.get(1).getTotalPrice()));
}
@Test
public void testCreateShipmentOrderDetailList_ignoreId() {
// mock 数据
WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO(
createShipmentOrderDetailReqVO(999L, 1001L, "1.00"));
// 调用
shipmentOrderDetailService.createShipmentOrderDetailList(10L, reqVO);
// 断言
List<WmsShipmentOrderDetailDO> details = shipmentOrderDetailMapper.selectListByOrderId(10L);
assertEquals(1, details.size());
assertNotNull(details.get(0).getId());
assertEquals(1001L, details.get(0).getSkuId());
}
@Test
public void testUpdateShipmentOrderDetailList_diff() {
// mock 数据
Long orderId = 10L;
WmsShipmentOrderDetailDO detail01 = createShipmentOrderDetail(orderId, 1001L, "1.00");
WmsShipmentOrderDetailDO detail02 = createShipmentOrderDetail(orderId, 1002L, "2.00");
shipmentOrderDetailMapper.insert(detail01);
shipmentOrderDetailMapper.insert(detail02);
WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO(
createShipmentOrderDetailReqVO(detail01.getId(), 2001L, "11.00"),
createShipmentOrderDetailReqVO(null, 2002L, "22.00"));
// 调用
shipmentOrderDetailService.updateShipmentOrderDetailList(orderId, reqVO);
// 断言:修改
WmsShipmentOrderDetailDO dbUpdateDetail = shipmentOrderDetailMapper.selectById(detail01.getId());
assertNotNull(dbUpdateDetail);
assertEquals(orderId, dbUpdateDetail.getOrderId());
assertEquals(2001L, dbUpdateDetail.getSkuId());
assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity()));
assertEquals(0, new BigDecimal("1100.00").compareTo(dbUpdateDetail.getTotalPrice()));
// 断言:新增
List<WmsShipmentOrderDetailDO> details = shipmentOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
WmsShipmentOrderDetailDO dbCreateDetail = details.stream()
.filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId()))
.findFirst().orElse(null);
assertNotNull(dbCreateDetail);
assertEquals(orderId, dbCreateDetail.getOrderId());
assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity()));
assertEquals(0, new BigDecimal("2200.00").compareTo(dbCreateDetail.getTotalPrice()));
// 断言:删除
assertNull(shipmentOrderDetailMapper.selectById(detail02.getId()));
}
@Test
public void testUpdateShipmentOrderDetailList_detailNotExists() {
// mock 数据
WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO(
createShipmentOrderDetailReqVO(999L, 1001L, "1.00"));
// 调用,并断言
assertServiceException(() -> shipmentOrderDetailService.updateShipmentOrderDetailList(10L, reqVO),
SHIPMENT_ORDER_DETAIL_NOT_EXISTS);
}
private static WmsShipmentOrderSaveReqVO createShipmentOrderReqVO(WmsShipmentOrderDetailSaveReqVO... details) {
WmsShipmentOrderSaveReqVO reqVO = new WmsShipmentOrderSaveReqVO();
reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0));
reqVO.setWarehouseId(100L);
reqVO.setDetails(Arrays.asList(details));
return reqVO;
}
private static WmsShipmentOrderDetailSaveReqVO createShipmentOrderDetailReqVO(Long id, Long skuId, String quantity) {
WmsShipmentOrderDetailSaveReqVO reqVO = new WmsShipmentOrderDetailSaveReqVO();
reqVO.setId(id);
reqVO.setSkuId(skuId);
reqVO.setQuantity(new BigDecimal(quantity));
reqVO.setPrice(new BigDecimal("100.00"));
reqVO.setTotalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00")));
return reqVO;
}
private static WmsShipmentOrderDetailDO createShipmentOrderDetail(Long orderId, Long skuId, String quantity) {
return WmsShipmentOrderDetailDO.builder()
.orderId(orderId)
.skuId(skuId)
.warehouseId(100L)
.quantity(new BigDecimal(quantity))
.price(new BigDecimal("100.00"))
.totalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00")))
.build();
}
}

View File

@@ -0,0 +1,271 @@
package cn.iocoder.yudao.module.wms.service.order.shipment;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailSaveReqVO;
import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO;
import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO;
import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderDetailMapper;
import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderMapper;
import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum;
import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum;
import cn.iocoder.yudao.module.wms.enums.order.WmsShipmentOrderTypeEnum;
import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO;
import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService;
import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_DETAIL_REQUIRED;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_STATUS_NOT_DELETABLE;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_STATUS_NOT_PREPARE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@Import({WmsShipmentOrderServiceImpl.class, WmsShipmentOrderDetailServiceImpl.class})
public class WmsShipmentOrderServiceImplTest extends BaseDbUnitTest {
@Resource
private WmsShipmentOrderServiceImpl shipmentOrderService;
@Resource
private WmsShipmentOrderMapper shipmentOrderMapper;
@Resource
private WmsShipmentOrderDetailMapper shipmentOrderDetailMapper;
@MockitoBean
private WmsWarehouseService warehouseService;
@MockitoBean
private WmsMerchantService merchantService;
@MockitoBean
private WmsItemSkuService itemSkuService;
@MockitoBean
private WmsInventoryService inventoryService;
@Test
public void testCreateShipmentOrder_calculateTotal() {
// mock 数据
WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderSaveReqVO(null,
createShipmentOrderDetailReqVO(null, 2001L, "1.50", "10.00", "16.00"),
createShipmentOrderDetailReqVO(null, 2002L, "2.50", "20.00", "51.00"));
// 调用
Long orderId = shipmentOrderService.createShipmentOrder(reqVO);
// 断言
WmsShipmentOrderDO dbOrder = shipmentOrderMapper.selectById(orderId);
assertNotNull(dbOrder);
assertEquals(0, new BigDecimal("4.00").compareTo(dbOrder.getTotalQuantity()));
assertEquals(0, new BigDecimal("67.00").compareTo(dbOrder.getTotalPrice()));
List<WmsShipmentOrderDetailDO> details = shipmentOrderDetailMapper.selectListByOrderId(orderId);
assertEquals(2, details.size());
assertEquals(0, new BigDecimal("16.00").compareTo(details.get(0).getTotalPrice()));
}
@Test
public void testUpdateShipmentOrder_calculateTotal() {
// mock 数据
WmsShipmentOrderDO order = createShipmentOrder(100L);
shipmentOrderMapper.insert(order);
WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderSaveReqVO(order.getId(),
createShipmentOrderDetailReqVO(null, 2001L, "3.00", "30.00", "88.00"));
// 调用
shipmentOrderService.updateShipmentOrder(reqVO);
// 断言
WmsShipmentOrderDO dbOrder = shipmentOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(0, new BigDecimal("3.00").compareTo(dbOrder.getTotalQuantity()));
assertEquals(0, new BigDecimal("88.00").compareTo(dbOrder.getTotalPrice()));
List<WmsShipmentOrderDetailDO> details = shipmentOrderDetailMapper.selectListByOrderId(order.getId());
assertEquals(1, details.size());
assertEquals(0, new BigDecimal("88.00").compareTo(details.get(0).getTotalPrice()));
}
@Test
public void testCompleteShipmentOrder_success() {
// mock 数据
Long warehouseId = 100L;
Long skuId = 200L;
WmsShipmentOrderDO order = createShipmentOrder(warehouseId);
shipmentOrderMapper.insert(order);
shipmentOrderDetailMapper.insert(createShipmentOrderDetail(order.getId(), skuId, warehouseId));
// 调用
shipmentOrderService.completeShipmentOrder(order.getId());
// 断言:出库单
WmsShipmentOrderDO dbOrder = shipmentOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus());
// 断言:库存变更
ArgumentCaptor<WmsInventoryChangeReqDTO> captor = ArgumentCaptor.forClass(WmsInventoryChangeReqDTO.class);
verify(inventoryService).changeInventory(captor.capture());
WmsInventoryChangeReqDTO inventoryReqDTO = captor.getValue();
assertEquals(order.getId(), inventoryReqDTO.getOrderId());
assertEquals(order.getNo(), inventoryReqDTO.getOrderNo());
assertEquals(WmsOrderTypeEnum.SHIPMENT.getType(), inventoryReqDTO.getOrderType());
assertEquals(1, inventoryReqDTO.getItems().size());
assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId());
assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId());
assertEquals(0, new BigDecimal("-2.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity()));
assertEquals(0, new BigDecimal("-40.00").compareTo(inventoryReqDTO.getItems().get(0).getTotalPrice()));
}
@Test
public void testCompleteShipmentOrder_detailRequired() {
// mock 数据
WmsShipmentOrderDO order = createShipmentOrder(100L);
shipmentOrderMapper.insert(order);
// 调用,并断言
assertServiceException(() -> shipmentOrderService.completeShipmentOrder(order.getId()),
SHIPMENT_ORDER_DETAIL_REQUIRED);
verify(inventoryService, never()).changeInventory(any());
}
@Test
public void testCompleteShipmentOrder_duplicateComplete() {
// mock 数据
WmsShipmentOrderDO order = createShipmentOrder(100L);
shipmentOrderMapper.insert(order);
shipmentOrderDetailMapper.insert(createShipmentOrderDetail(order.getId(), 200L, 100L));
// 调用
shipmentOrderService.completeShipmentOrder(order.getId());
// 调用,并断言:二次完成不能再次写库存
assertServiceException(() -> shipmentOrderService.completeShipmentOrder(order.getId()),
SHIPMENT_ORDER_STATUS_NOT_PREPARE);
verify(inventoryService).changeInventory(any());
}
@Test
public void testCancelShipmentOrder_success() {
// mock 数据
WmsShipmentOrderDO order = createShipmentOrder(100L);
shipmentOrderMapper.insert(order);
// 调用
shipmentOrderService.cancelShipmentOrder(order.getId());
// 断言
WmsShipmentOrderDO dbOrder = shipmentOrderMapper.selectById(order.getId());
assertNotNull(dbOrder);
assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus());
verify(inventoryService, never()).changeInventory(any());
}
@Test
public void testUpdateByIdAndStatus_statusNotMatch() {
// mock 数据
WmsShipmentOrderDO order = createShipmentOrder(100L);
shipmentOrderMapper.insert(order);
// 调用
int updateCount = shipmentOrderMapper.updateByIdAndStatus(order.getId(),
WmsOrderStatusEnum.FINISHED.getStatus(),
new WmsShipmentOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus()));
// 断言
assertEquals(0, updateCount);
assertEquals(WmsOrderStatusEnum.PREPARE.getStatus(),
shipmentOrderMapper.selectById(order.getId()).getStatus());
}
@Test
public void testDeleteShipmentOrder_canceled() {
// mock 数据
WmsShipmentOrderDO order = createShipmentOrder(100L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus());
shipmentOrderMapper.insert(order);
shipmentOrderDetailMapper.insert(createShipmentOrderDetail(order.getId(), 200L, 100L));
// 调用
shipmentOrderService.deleteShipmentOrder(order.getId());
// 断言
assertNull(shipmentOrderMapper.selectById(order.getId()));
assertEquals(0, shipmentOrderDetailMapper.selectListByOrderId(order.getId()).size());
}
@Test
public void testDeleteShipmentOrder_finished() {
// mock 数据
WmsShipmentOrderDO order = createShipmentOrder(100L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus());
shipmentOrderMapper.insert(order);
// 调用,并断言
assertServiceException(() -> shipmentOrderService.deleteShipmentOrder(order.getId()),
SHIPMENT_ORDER_STATUS_NOT_DELETABLE);
}
private static WmsShipmentOrderDO createShipmentOrder(Long warehouseId) {
return new WmsShipmentOrderDO()
.setNo("CK202605120001")
.setType(WmsShipmentOrderTypeEnum.SALE.getType())
.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0))
.setStatus(WmsOrderStatusEnum.PREPARE.getStatus())
.setWarehouseId(warehouseId)
.setTotalQuantity(new BigDecimal("2.00"))
.setTotalPrice(new BigDecimal("20.00"));
}
private static WmsShipmentOrderSaveReqVO createShipmentOrderSaveReqVO(Long id,
WmsShipmentOrderDetailSaveReqVO... details) {
WmsShipmentOrderSaveReqVO reqVO = new WmsShipmentOrderSaveReqVO();
reqVO.setId(id);
reqVO.setNo("CK202605120001");
reqVO.setType(WmsShipmentOrderTypeEnum.SALE.getType());
reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0));
reqVO.setWarehouseId(100L);
reqVO.setDetails(Arrays.asList(details));
return reqVO;
}
private static WmsShipmentOrderDetailSaveReqVO createShipmentOrderDetailReqVO(Long id, Long skuId,
String quantity, String price) {
return createShipmentOrderDetailReqVO(id, skuId, quantity, price, null);
}
private static WmsShipmentOrderDetailSaveReqVO createShipmentOrderDetailReqVO(Long id, Long skuId,
String quantity, String price,
String totalPrice) {
WmsShipmentOrderDetailSaveReqVO reqVO = new WmsShipmentOrderDetailSaveReqVO();
reqVO.setId(id);
reqVO.setSkuId(skuId);
reqVO.setQuantity(new BigDecimal(quantity));
reqVO.setPrice(new BigDecimal(price));
if (totalPrice != null) {
reqVO.setTotalPrice(new BigDecimal(totalPrice));
}
return reqVO;
}
private static WmsShipmentOrderDetailDO createShipmentOrderDetail(Long orderId, Long skuId, Long warehouseId) {
return WmsShipmentOrderDetailDO.builder()
.orderId(orderId)
.skuId(skuId)
.warehouseId(warehouseId)
.quantity(new BigDecimal("2.00"))
.price(new BigDecimal("20.00"))
.totalPrice(new BigDecimal("40.00"))
.build();
}
}

View File

@@ -0,0 +1,35 @@
spring:
main:
lazy-initialization: true # 开启懒加载,加快速度
banner-mode: off # 单元测试,禁用 Banner
--- #################### 数据库相关配置 ####################
spring:
datasource:
name: ruoyi-vue-pro
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式DATABASE_TO_UPPER 配置表和字段使用小写
driver-class-name: org.h2.Driver
username: sa
password:
druid:
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
initial-size: 1 # 单元测试,配置为 1提升启动速度
sql:
init:
schema-locations: classpath:/sql/create_tables.sql
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
--- #################### 芋道相关配置 ####################
yudao:
info:
base-package: cn.iocoder.yudao.module

View File

@@ -0,0 +1,16 @@
DELETE FROM "wms_check_order_detail";
DELETE FROM "wms_check_order";
DELETE FROM "wms_movement_order_detail";
DELETE FROM "wms_movement_order";
DELETE FROM "wms_shipment_order_detail";
DELETE FROM "wms_shipment_order";
DELETE FROM "wms_receipt_order_detail";
DELETE FROM "wms_receipt_order";
DELETE FROM "wms_inventory_history";
DELETE FROM "wms_inventory";
DELETE FROM "wms_item_sku";
DELETE FROM "wms_item";
DELETE FROM "wms_item_category";
DELETE FROM "wms_item_brand";
DELETE FROM "wms_warehouse";
DELETE FROM "wms_merchant";

View File

@@ -0,0 +1,320 @@
CREATE TABLE IF NOT EXISTS "wms_warehouse" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"code" varchar(20) NOT NULL,
"name" varchar(50) NOT NULL,
"remark" varchar(255) DEFAULT NULL,
"sort" int DEFAULT '0',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 仓库表';
CREATE TABLE IF NOT EXISTS "wms_merchant" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"code" varchar(20) NOT NULL,
"name" varchar(60) NOT NULL,
"type" tinyint NOT NULL,
"level" varchar(10) DEFAULT NULL,
"bank_name" varchar(255) DEFAULT NULL,
"bank_account" varchar(40) DEFAULT NULL,
"address" varchar(200) DEFAULT NULL,
"mobile" varchar(13) DEFAULT NULL,
"telephone" varchar(13) DEFAULT NULL,
"contact" varchar(30) DEFAULT NULL,
"email" varchar(50) DEFAULT NULL,
"remark" varchar(255) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 往来企业表';
CREATE TABLE IF NOT EXISTS "wms_item_brand" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"code" varchar(20) NOT NULL,
"name" varchar(30) NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 商品品牌表';
CREATE TABLE IF NOT EXISTS "wms_item_category" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"parent_id" bigint NOT NULL DEFAULT '0',
"code" varchar(20) NOT NULL,
"name" varchar(30) NOT NULL,
"sort" int DEFAULT '0',
"status" tinyint NOT NULL DEFAULT '1',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 商品分类表';
CREATE TABLE IF NOT EXISTS "wms_item" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"code" varchar(20) DEFAULT NULL,
"name" varchar(60) NOT NULL,
"category_id" bigint NOT NULL,
"unit" varchar(20) DEFAULT NULL,
"brand_id" bigint DEFAULT NULL,
"remark" varchar(255) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 商品表';
CREATE TABLE IF NOT EXISTS "wms_item_sku" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(255) NOT NULL,
"item_id" bigint NOT NULL,
"bar_code" varchar(64) DEFAULT NULL,
"code" varchar(64) DEFAULT NULL,
"length" decimal(10, 1) DEFAULT NULL,
"width" decimal(10, 1) DEFAULT NULL,
"height" decimal(10, 1) DEFAULT NULL,
"gross_weight" decimal(10, 3) DEFAULT NULL,
"net_weight" decimal(10, 3) DEFAULT NULL,
"cost_price" decimal(16, 2) DEFAULT NULL,
"selling_price" decimal(16, 2) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 商品 SKU 表';
CREATE TABLE IF NOT EXISTS "wms_inventory" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"sku_id" bigint NOT NULL,
"warehouse_id" bigint NOT NULL,
"area_id" bigint NOT NULL DEFAULT '0',
"quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"remark" varchar(255) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id"),
CONSTRAINT "uk_sku_id_warehouse_id" UNIQUE ("sku_id", "warehouse_id")
) COMMENT 'WMS 库存表';
CREATE TABLE IF NOT EXISTS "wms_inventory_history" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"warehouse_id" bigint NOT NULL,
"area_id" bigint NOT NULL DEFAULT '0',
"sku_id" bigint NOT NULL,
"quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"before_quantity" decimal(20, 2) DEFAULT NULL,
"after_quantity" decimal(20, 2) DEFAULT NULL,
"batch_no" varchar(64) DEFAULT NULL,
"production_date" timestamp DEFAULT NULL,
"expiration_date" timestamp DEFAULT NULL,
"price" decimal(16, 2) DEFAULT NULL,
"total_price" decimal(16, 2) DEFAULT NULL,
"remark" varchar(255) DEFAULT NULL,
"order_id" bigint DEFAULT NULL,
"order_no" varchar(64) DEFAULT NULL,
"order_type" int DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 库存流水表';
CREATE TABLE IF NOT EXISTS "wms_receipt_order" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"no" varchar(64) NOT NULL,
"type" int NOT NULL,
"order_time" timestamp NOT NULL,
"status" int NOT NULL DEFAULT '0',
"biz_order_no" varchar(64) DEFAULT NULL,
"merchant_id" bigint DEFAULT NULL,
"remark" varchar(255) DEFAULT NULL,
"warehouse_id" bigint NOT NULL,
"area_id" bigint NOT NULL DEFAULT '0',
"total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"total_price" decimal(16, 2) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 入库单表';
CREATE TABLE IF NOT EXISTS "wms_receipt_order_detail" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"order_id" bigint NOT NULL,
"sku_id" bigint NOT NULL,
"warehouse_id" bigint NOT NULL,
"area_id" bigint NOT NULL DEFAULT '0',
"batch_no" varchar(64) DEFAULT NULL,
"production_date" timestamp DEFAULT NULL,
"expiration_date" timestamp DEFAULT NULL,
"quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"price" decimal(16, 2) DEFAULT NULL,
"total_price" decimal(16, 2) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 入库单明细表';
CREATE TABLE IF NOT EXISTS "wms_shipment_order" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"no" varchar(64) NOT NULL,
"type" int NOT NULL,
"order_time" timestamp NOT NULL,
"status" int NOT NULL DEFAULT '0',
"biz_order_no" varchar(64) DEFAULT NULL,
"merchant_id" bigint DEFAULT NULL,
"remark" varchar(255) DEFAULT NULL,
"warehouse_id" bigint NOT NULL,
"area_id" bigint NOT NULL DEFAULT '0',
"total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"total_price" decimal(16, 2) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 出库单表';
CREATE TABLE IF NOT EXISTS "wms_shipment_order_detail" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"order_id" bigint NOT NULL,
"sku_id" bigint NOT NULL,
"warehouse_id" bigint NOT NULL,
"area_id" bigint NOT NULL DEFAULT '0',
"inventory_detail_id" bigint DEFAULT NULL,
"batch_no" varchar(64) DEFAULT NULL,
"production_date" timestamp DEFAULT NULL,
"expiration_date" timestamp DEFAULT NULL,
"quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"price" decimal(16, 2) DEFAULT NULL,
"total_price" decimal(16, 2) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 出库单明细表';
CREATE TABLE IF NOT EXISTS "wms_movement_order" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"no" varchar(64) NOT NULL,
"order_time" timestamp NOT NULL,
"status" int NOT NULL DEFAULT '0',
"remark" varchar(255) DEFAULT NULL,
"source_warehouse_id" bigint NOT NULL,
"target_warehouse_id" bigint NOT NULL,
"total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"total_price" decimal(16, 2) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 移库单表';
CREATE TABLE IF NOT EXISTS "wms_movement_order_detail" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"order_id" bigint NOT NULL,
"sku_id" bigint NOT NULL,
"source_warehouse_id" bigint NOT NULL,
"target_warehouse_id" bigint NOT NULL,
"inventory_detail_id" bigint DEFAULT NULL,
"batch_no" varchar(64) DEFAULT NULL,
"production_date" timestamp DEFAULT NULL,
"expiration_date" timestamp DEFAULT NULL,
"quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"price" decimal(16, 2) DEFAULT NULL,
"total_price" decimal(16, 2) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 移库单明细表';
CREATE TABLE IF NOT EXISTS "wms_check_order" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"no" varchar(64) NOT NULL,
"order_time" timestamp NOT NULL,
"status" int NOT NULL DEFAULT '0',
"remark" varchar(255) DEFAULT NULL,
"warehouse_id" bigint NOT NULL,
"area_id" bigint NOT NULL DEFAULT '0',
"total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"total_price" decimal(16, 2) DEFAULT NULL,
"actual_price" decimal(16, 2) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 盘库单表';
CREATE TABLE IF NOT EXISTS "wms_check_order_detail" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"order_id" bigint NOT NULL,
"sku_id" bigint NOT NULL,
"warehouse_id" bigint NOT NULL,
"area_id" bigint NOT NULL DEFAULT '0',
"inventory_id" bigint DEFAULT NULL,
"inventory_detail_id" bigint DEFAULT NULL,
"batch_no" varchar(64) DEFAULT NULL,
"production_date" timestamp DEFAULT NULL,
"expiration_date" timestamp DEFAULT NULL,
"receipt_time" timestamp DEFAULT NULL,
"quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"check_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
"price" decimal(16, 2) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT 'WMS 盘库单明细表';