Commit 5159b19e2d234e6a8a4c56465284cecb6f85aa1a

Authored by YunaiV
1 parent c62630f7

feat(wms): 统一数量金额精度并清理 schema 脱钩

后端:
- 新增 sql/mysql/wms/20260515_wms_amount_precision.sql
  11 张表金额字段统一升到 decimal(16,2),覆盖 SKU 单价、单据主表/明细
  总金额/行金额、盘库实际金额、库存流水单价/行金额
- 新增 sql/mysql/wms/20260515_wms_sku_dimension_precision.sql
  SKU 长宽高对齐 lite 改为 decimal(10,1)、毛/净重改为 decimal(10,3)
- 测试 SQL create_tables.sql 全量同步生产 MySQL:数量 (20,2)、
  金额 (16,2)、长度 (10,1)、重量 (10,3),修复"测试 schema 与生产
  脱钩"导致单测假阳性的隐患
- WmsWarehouseServiceImpl.validateWarehouseCodeUnique 去掉
  StrUtil.isBlank 提前 return,因 code 已由 VO 层 @NotBlank 强制非空
- WmsWarehouseServiceImplTest 同步调整

前端:
- ReceiptOrderForm / ReceiptOrderDetail 合计行去掉"单价合计"派生展示,
  单价不能跨行相加;保留数量合计与行金额合计

文档:
- review-opus.md 收口至仅剩 F10 (SQL 导出,用户认领)
- 新增 fix-plan.md 与 精度调整-codex讨论.md,沉淀本轮决策依据
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseServiceImpl.java
... ... @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.wms.service.md.warehouse;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.ObjUtil;
5   -import cn.hutool.core.util.StrUtil;
6 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 7 import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehousePageReqVO;
... ... @@ -94,9 +93,6 @@ public class WmsWarehouseServiceImpl implements WmsWarehouseService {
94 93 }
95 94  
96 95 private void validateWarehouseCodeUnique(Long id, String code) {
97   - if (StrUtil.isBlank(code)) {
98   - return;
99   - }
100 96 WmsWarehouseDO warehouse = warehouseMapper.selectByCode(code);
101 97 if (warehouse == null) {
102 98 return;
... ...
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseServiceImplTest.java
1 1 package cn.iocoder.yudao.module.wms.service.md.warehouse;
2 2  
3 3 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehouseSaveReqVO;
4 5 import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
5 6 import cn.iocoder.yudao.module.wms.dal.mysql.md.warehouse.WmsWarehouseMapper;
6 7 import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
... ... @@ -14,6 +15,7 @@ import org.springframework.context.annotation.Import;
14 15 import org.springframework.test.context.bean.override.mockito.MockitoBean;
15 16  
16 17 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  18 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.WAREHOUSE_CODE_DUPLICATE;
17 19 import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.WAREHOUSE_HAS_INVENTORY;
18 20 import static org.junit.jupiter.api.Assertions.assertNotNull;
19 21 import static org.mockito.Mockito.when;
... ... @@ -39,6 +41,31 @@ public class WmsWarehouseServiceImplTest extends BaseDbUnitTest {
39 41 private WmsCheckOrderService checkOrderService;
40 42  
41 43 @Test
  44 + public void testCreateWarehouse_codeDuplicate() {
  45 + // mock 数据
  46 + WmsWarehouseDO warehouse = createWarehouse("WH001", "成品仓");
  47 + warehouseMapper.insert(warehouse);
  48 +
  49 + // 调用,并断言
  50 + WmsWarehouseSaveReqVO reqVO = createWarehouseSaveReqVO();
  51 + assertServiceException(() -> warehouseService.createWarehouse(reqVO), WAREHOUSE_CODE_DUPLICATE);
  52 + }
  53 +
  54 + @Test
  55 + public void testUpdateWarehouse_codeDuplicate() {
  56 + // mock 数据
  57 + WmsWarehouseDO warehouse = createWarehouse("WH001", "成品仓");
  58 + warehouseMapper.insert(warehouse);
  59 + WmsWarehouseDO updateWarehouse = createWarehouse("WH002", "原料仓");
  60 + warehouseMapper.insert(updateWarehouse);
  61 +
  62 + // 调用,并断言
  63 + WmsWarehouseSaveReqVO reqVO = createWarehouseSaveReqVO();
  64 + reqVO.setId(updateWarehouse.getId());
  65 + assertServiceException(() -> warehouseService.updateWarehouse(reqVO), WAREHOUSE_CODE_DUPLICATE);
  66 + }
  67 +
  68 + @Test
42 69 public void testDeleteWarehouse_hasInventory() {
43 70 // mock 数据
44 71 WmsWarehouseDO warehouse = createWarehouse();
... ... @@ -51,11 +78,23 @@ public class WmsWarehouseServiceImplTest extends BaseDbUnitTest {
51 78 }
52 79  
53 80 private static WmsWarehouseDO createWarehouse() {
  81 + return createWarehouse("WH001", "成品仓");
  82 + }
  83 +
  84 + private static WmsWarehouseDO createWarehouse(String code, String name) {
54 85 return WmsWarehouseDO.builder()
55   - .code("WH001")
56   - .name("成品仓")
  86 + .code(code)
  87 + .name(name)
57 88 .sort(1)
58 89 .build();
59 90 }
60 91  
  92 + private static WmsWarehouseSaveReqVO createWarehouseSaveReqVO() {
  93 + WmsWarehouseSaveReqVO reqVO = new WmsWarehouseSaveReqVO();
  94 + reqVO.setCode("WH001");
  95 + reqVO.setName("半成品仓");
  96 + reqVO.setSort(1);
  97 + return reqVO;
  98 + }
  99 +
61 100 }
... ...
yudao-module-wms/src/test/resources/sql/create_tables.sql
... ... @@ -88,13 +88,13 @@ CREATE TABLE IF NOT EXISTS "wms_item_sku" (
88 88 "item_id" bigint NOT NULL,
89 89 "bar_code" varchar(64) DEFAULT NULL,
90 90 "code" varchar(64) DEFAULT NULL,
91   - "length" decimal(24, 6) DEFAULT NULL,
92   - "width" decimal(24, 6) DEFAULT NULL,
93   - "height" decimal(24, 6) DEFAULT NULL,
94   - "gross_weight" decimal(24, 6) DEFAULT NULL,
95   - "net_weight" decimal(24, 6) DEFAULT NULL,
96   - "cost_price" decimal(24, 6) DEFAULT NULL,
97   - "selling_price" decimal(24, 6) DEFAULT NULL,
  91 + "length" decimal(10, 1) DEFAULT NULL,
  92 + "width" decimal(10, 1) DEFAULT NULL,
  93 + "height" decimal(10, 1) DEFAULT NULL,
  94 + "gross_weight" decimal(10, 3) DEFAULT NULL,
  95 + "net_weight" decimal(10, 3) DEFAULT NULL,
  96 + "cost_price" decimal(16, 2) DEFAULT NULL,
  97 + "selling_price" decimal(16, 2) DEFAULT NULL,
98 98 "creator" varchar(64) DEFAULT '',
99 99 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
100 100 "updater" varchar(64) DEFAULT '',
... ... @@ -108,7 +108,7 @@ CREATE TABLE IF NOT EXISTS "wms_inventory" (
108 108 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
109 109 "sku_id" bigint NOT NULL,
110 110 "warehouse_id" bigint NOT NULL,
111   - "quantity" decimal(24, 6) NOT NULL DEFAULT '0',
  111 + "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
112 112 "remark" varchar(255) DEFAULT NULL,
113 113 "creator" varchar(64) DEFAULT '',
114 114 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
... ... @@ -124,11 +124,11 @@ CREATE TABLE IF NOT EXISTS "wms_inventory_history" (
124 124 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
125 125 "warehouse_id" bigint NOT NULL,
126 126 "sku_id" bigint NOT NULL,
127   - "quantity" decimal(24, 6) NOT NULL DEFAULT '0',
128   - "before_quantity" decimal(24, 6) DEFAULT NULL,
129   - "after_quantity" decimal(24, 6) DEFAULT NULL,
130   - "price" decimal(24, 6) DEFAULT NULL,
131   - "total_price" decimal(24, 6) DEFAULT NULL,
  127 + "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
  128 + "before_quantity" decimal(20, 2) DEFAULT NULL,
  129 + "after_quantity" decimal(20, 2) DEFAULT NULL,
  130 + "price" decimal(16, 2) DEFAULT NULL,
  131 + "total_price" decimal(16, 2) DEFAULT NULL,
132 132 "remark" varchar(255) DEFAULT NULL,
133 133 "order_id" bigint DEFAULT NULL,
134 134 "order_no" varchar(64) DEFAULT NULL,
... ... @@ -153,7 +153,7 @@ CREATE TABLE IF NOT EXISTS "wms_receipt_order" (
153 153 "remark" varchar(255) DEFAULT NULL,
154 154 "warehouse_id" bigint NOT NULL,
155 155 "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
156   - "total_price" decimal(10, 2) DEFAULT NULL,
  156 + "total_price" decimal(16, 2) DEFAULT NULL,
157 157 "creator" varchar(64) DEFAULT '',
158 158 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
159 159 "updater" varchar(64) DEFAULT '',
... ... @@ -169,8 +169,8 @@ CREATE TABLE IF NOT EXISTS "wms_receipt_order_detail" (
169 169 "sku_id" bigint NOT NULL,
170 170 "warehouse_id" bigint NOT NULL,
171 171 "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
172   - "price" decimal(10, 2) DEFAULT NULL,
173   - "total_price" decimal(10, 2) DEFAULT NULL,
  172 + "price" decimal(16, 2) DEFAULT NULL,
  173 + "total_price" decimal(16, 2) DEFAULT NULL,
174 174 "creator" varchar(64) DEFAULT '',
175 175 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
176 176 "updater" varchar(64) DEFAULT '',
... ... @@ -191,7 +191,7 @@ CREATE TABLE IF NOT EXISTS "wms_shipment_order" (
191 191 "remark" varchar(255) DEFAULT NULL,
192 192 "warehouse_id" bigint NOT NULL,
193 193 "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
194   - "total_price" decimal(10, 2) DEFAULT NULL,
  194 + "total_price" decimal(16, 2) DEFAULT NULL,
195 195 "creator" varchar(64) DEFAULT '',
196 196 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
197 197 "updater" varchar(64) DEFAULT '',
... ... @@ -207,8 +207,8 @@ CREATE TABLE IF NOT EXISTS "wms_shipment_order_detail" (
207 207 "sku_id" bigint NOT NULL,
208 208 "warehouse_id" bigint NOT NULL,
209 209 "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
210   - "price" decimal(10, 2) DEFAULT NULL,
211   - "total_price" decimal(10, 2) DEFAULT NULL,
  210 + "price" decimal(16, 2) DEFAULT NULL,
  211 + "total_price" decimal(16, 2) DEFAULT NULL,
212 212 "creator" varchar(64) DEFAULT '',
213 213 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
214 214 "updater" varchar(64) DEFAULT '',
... ... @@ -227,7 +227,7 @@ CREATE TABLE IF NOT EXISTS "wms_movement_order" (
227 227 "source_warehouse_id" bigint NOT NULL,
228 228 "target_warehouse_id" bigint NOT NULL,
229 229 "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
230   - "total_price" decimal(10, 2) DEFAULT NULL,
  230 + "total_price" decimal(16, 2) DEFAULT NULL,
231 231 "creator" varchar(64) DEFAULT '',
232 232 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
233 233 "updater" varchar(64) DEFAULT '',
... ... @@ -244,8 +244,8 @@ CREATE TABLE IF NOT EXISTS "wms_movement_order_detail" (
244 244 "source_warehouse_id" bigint NOT NULL,
245 245 "target_warehouse_id" bigint NOT NULL,
246 246 "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
247   - "price" decimal(10, 2) DEFAULT NULL,
248   - "total_price" decimal(10, 2) DEFAULT NULL,
  247 + "price" decimal(16, 2) DEFAULT NULL,
  248 + "total_price" decimal(16, 2) DEFAULT NULL,
249 249 "creator" varchar(64) DEFAULT '',
250 250 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
251 251 "updater" varchar(64) DEFAULT '',
... ... @@ -263,8 +263,8 @@ CREATE TABLE IF NOT EXISTS "wms_check_order" (
263 263 "remark" varchar(255) DEFAULT NULL,
264 264 "warehouse_id" bigint NOT NULL,
265 265 "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
266   - "total_price" decimal(10, 2) DEFAULT NULL,
267   - "actual_price" decimal(10, 2) DEFAULT NULL,
  266 + "total_price" decimal(16, 2) DEFAULT NULL,
  267 + "actual_price" decimal(16, 2) DEFAULT NULL,
268 268 "creator" varchar(64) DEFAULT '',
269 269 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
270 270 "updater" varchar(64) DEFAULT '',
... ... @@ -283,7 +283,7 @@ CREATE TABLE IF NOT EXISTS "wms_check_order_detail" (
283 283 "receipt_time" timestamp DEFAULT NULL,
284 284 "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
285 285 "check_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
286   - "price" decimal(10, 2) DEFAULT NULL,
  286 + "price" decimal(16, 2) DEFAULT NULL,
287 287 "creator" varchar(64) DEFAULT '',
288 288 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
289 289 "updater" varchar(64) DEFAULT '',
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -189,6 +189,7 @@ logging:
189 189 cn.iocoder.yudao.module.crm.dal.mysql: debug
190 190 cn.iocoder.yudao.module.erp.dal.mysql: debug
191 191 cn.iocoder.yudao.module.mes.dal.mysql: debug
  192 + cn.iocoder.yudao.module.wms.dal.mysql: debug
192 193 cn.iocoder.yudao.module.iot.dal.mysql: debug
193 194 cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG
194 195 cn.iocoder.yudao.module.iot.service.rule: debug
... ...