diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java index e748c2d..bb34abf 100644 --- a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java @@ -35,6 +35,30 @@ public interface WmsInventoryMapper extends BaseMapperX { return selectJoinPage(reqVO, WmsInventoryDO.class, query); } + default PageResult selectPageGroupByWarehouse(WmsInventoryPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .selectMin(WmsInventoryDO::getId, WmsInventoryDO::getId) + .selectAs(WmsInventoryDO::getWarehouseId, WmsInventoryDO::getWarehouseId) + .selectAs("0", WmsInventoryDO::getAreaId) + .selectAs(WmsInventoryDO::getSkuId, WmsInventoryDO::getSkuId) + .selectSum(WmsInventoryDO::getQuantity, WmsInventoryDO::getQuantity) + .innerJoin(WmsItemSkuDO.class, WmsItemSkuDO::getId, WmsInventoryDO::getSkuId) + .innerJoin(WmsItemDO.class, WmsItemDO::getId, WmsItemSkuDO::getItemId) + .likeIfPresent(WmsItemDO::getCode, reqVO.getItemCode()) + .likeIfPresent(WmsItemDO::getName, reqVO.getItemName()) + .eqIfPresent(WmsInventoryDO::getSkuId, reqVO.getSkuId()) + .likeIfPresent(WmsItemSkuDO::getCode, reqVO.getSkuCode()) + .likeIfPresent(WmsItemSkuDO::getName, reqVO.getSkuName()) + .eqIfPresent(WmsInventoryDO::getWarehouseId, reqVO.getWarehouseId()); + query.groupBy(WmsInventoryDO::getWarehouseId, WmsInventoryDO::getSkuId); + if (reqVO.getMinQuantity() != null) { + query.having("SUM(t.quantity) >= {0}", reqVO.getMinQuantity()); + } + query.orderByAsc(WmsInventoryDO::getWarehouseId) + .orderByAsc(WmsInventoryDO::getSkuId); + return selectJoinPage(reqVO, WmsInventoryDO.class, query); + } + default Long selectCountBySkuId(Long skuId) { return selectCount(WmsInventoryDO::getSkuId, skuId); } diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java index 8296bcf..e08e1cc 100644 --- a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.wms.service.inventory; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO; import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; @@ -22,6 +23,9 @@ public class WmsInventoryServiceImpl implements WmsInventoryService { @Override public PageResult getInventoryPage(WmsInventoryPageReqVO pageReqVO) { + if (StrUtil.equals(WmsInventoryPageReqVO.TYPE_WAREHOUSE, pageReqVO.getType())) { + return inventoryMapper.selectPageGroupByWarehouse(pageReqVO); + } return inventoryMapper.selectPage(pageReqVO); } diff --git a/yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImplTest.java b/yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImplTest.java new file mode 100644 index 0000000..7df3bac --- /dev/null +++ b/yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImplTest.java @@ -0,0 +1,128 @@ +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.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.WmsInventoryMapper; +import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemMapper; +import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemSkuMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Import(WmsInventoryServiceImpl.class) +public class WmsInventoryServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsInventoryServiceImpl inventoryService; + + @Resource + private WmsInventoryMapper inventoryMapper; + @Resource + private WmsItemMapper itemMapper; + @Resource + private WmsItemSkuMapper skuMapper; + + @Test + public void testGetInventoryPage_groupByWarehouse() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + inventoryMapper.insert(createInventory(sku.getId(), 100L, 1001L, "2.00")); + inventoryMapper.insert(createInventory(sku.getId(), 100L, 1002L, "3.00")); + inventoryMapper.insert(createInventory(sku.getId(), 200L, 1001L, "5.00")); + // 准备参数 + WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO(); + reqVO.setType(WmsInventoryPageReqVO.TYPE_WAREHOUSE); + reqVO.setWarehouseId(100L); + reqVO.setAreaId(1001L); // 仓库维度不按库区过滤 + + // 调用 + PageResult pageResult = inventoryService.getInventoryPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + WmsInventoryDO inventory = pageResult.getList().get(0); + assertEquals(100L, inventory.getWarehouseId()); + assertEquals(0L, inventory.getAreaId()); + assertEquals(sku.getId(), inventory.getSkuId()); + assertEquals(0, new BigDecimal("5.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, 1001L, "2.00")); + inventoryMapper.insert(createInventory(sku.getId(), 100L, 1002L, "3.00")); + // 准备参数 + WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO(); + reqVO.setType(WmsInventoryPageReqVO.TYPE_WAREHOUSE); + reqVO.setMinQuantity(new BigDecimal("6.00")); + + // 调用 + PageResult pageResult = inventoryService.getInventoryPage(reqVO); + // 断言 + assertEquals(0, pageResult.getTotal()); + assertEquals(0, pageResult.getList().size()); + } + + @Test + public void testGetInventoryPage_area() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + inventoryMapper.insert(createInventory(sku.getId(), 100L, 1001L, "2.00")); + inventoryMapper.insert(createInventory(sku.getId(), 100L, 1002L, "3.00")); + // 准备参数 + WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO(); + reqVO.setType(WmsInventoryPageReqVO.TYPE_AREA); + reqVO.setWarehouseId(100L); + + // 调用 + PageResult pageResult = inventoryService.getInventoryPage(reqVO); + // 断言 + assertEquals(2, pageResult.getTotal()); + assertEquals(2, pageResult.getList().size()); + } + + 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 static WmsInventoryDO createInventory(Long skuId, Long warehouseId, Long areaId, String quantity) { + return WmsInventoryDO.builder() + .skuId(skuId) + .warehouseId(warehouseId) + .areaId(areaId) + .quantity(new BigDecimal(quantity)) + .build(); + } + +} diff --git a/yudao-module-wms/src/test/resources/application-unit-test.yaml b/yudao-module-wms/src/test/resources/application-unit-test.yaml new file mode 100644 index 0000000..2961550 --- /dev/null +++ b/yudao-module-wms/src/test/resources/application-unit-test.yaml @@ -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 diff --git a/yudao-module-wms/src/test/resources/sql/clean.sql b/yudao-module-wms/src/test/resources/sql/clean.sql new file mode 100644 index 0000000..0a725e4 --- /dev/null +++ b/yudao-module-wms/src/test/resources/sql/clean.sql @@ -0,0 +1,3 @@ +DELETE FROM "wms_inventory"; +DELETE FROM "wms_item_sku"; +DELETE FROM "wms_item"; diff --git a/yudao-module-wms/src/test/resources/sql/create_tables.sql b/yudao-module-wms/src/test/resources/sql/create_tables.sql new file mode 100644 index 0000000..9337130 --- /dev/null +++ b/yudao-module-wms/src/test/resources/sql/create_tables.sql @@ -0,0 +1,54 @@ +CREATE TABLE IF NOT EXISTS "wms_item" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(20) NOT NULL, + "name" varchar(60) NOT NULL, + "unit" varchar(20) DEFAULT NULL, + "category_id" bigint NOT 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(24, 6) DEFAULT NULL, + "width" decimal(24, 6) DEFAULT NULL, + "height" decimal(24, 6) DEFAULT NULL, + "gross_weight" decimal(24, 6) DEFAULT NULL, + "net_weight" decimal(24, 6) DEFAULT NULL, + "cost_price" decimal(24, 6) DEFAULT NULL, + "selling_price" decimal(24, 6) 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(24, 6) 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") +) COMMENT 'WMS 库存表';