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,7 +2,6 @@ package cn.iocoder.yudao.module.wms.service.md.warehouse;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
5 -import cn.hutool.core.util.StrUtil;  
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehousePageReqVO; 7 import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehousePageReqVO;
@@ -94,9 +93,6 @@ public class WmsWarehouseServiceImpl implements WmsWarehouseService { @@ -94,9 +93,6 @@ public class WmsWarehouseServiceImpl implements WmsWarehouseService {
94 } 93 }
95 94
96 private void validateWarehouseCodeUnique(Long id, String code) { 95 private void validateWarehouseCodeUnique(Long id, String code) {
97 - if (StrUtil.isBlank(code)) {  
98 - return;  
99 - }  
100 WmsWarehouseDO warehouse = warehouseMapper.selectByCode(code); 96 WmsWarehouseDO warehouse = warehouseMapper.selectByCode(code);
101 if (warehouse == null) { 97 if (warehouse == null) {
102 return; 98 return;
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseServiceImplTest.java
1 package cn.iocoder.yudao.module.wms.service.md.warehouse; 1 package cn.iocoder.yudao.module.wms.service.md.warehouse;
2 2
3 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 3 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehouseSaveReqVO;
4 import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; 5 import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
5 import cn.iocoder.yudao.module.wms.dal.mysql.md.warehouse.WmsWarehouseMapper; 6 import cn.iocoder.yudao.module.wms.dal.mysql.md.warehouse.WmsWarehouseMapper;
6 import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; 7 import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
@@ -14,6 +15,7 @@ import org.springframework.context.annotation.Import; @@ -14,6 +15,7 @@ import org.springframework.context.annotation.Import;
14 import org.springframework.test.context.bean.override.mockito.MockitoBean; 15 import org.springframework.test.context.bean.override.mockito.MockitoBean;
15 16
16 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; 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 import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.WAREHOUSE_HAS_INVENTORY; 19 import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.WAREHOUSE_HAS_INVENTORY;
18 import static org.junit.jupiter.api.Assertions.assertNotNull; 20 import static org.junit.jupiter.api.Assertions.assertNotNull;
19 import static org.mockito.Mockito.when; 21 import static org.mockito.Mockito.when;
@@ -39,6 +41,31 @@ public class WmsWarehouseServiceImplTest extends BaseDbUnitTest { @@ -39,6 +41,31 @@ public class WmsWarehouseServiceImplTest extends BaseDbUnitTest {
39 private WmsCheckOrderService checkOrderService; 41 private WmsCheckOrderService checkOrderService;
40 42
41 @Test 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 public void testDeleteWarehouse_hasInventory() { 69 public void testDeleteWarehouse_hasInventory() {
43 // mock 数据 70 // mock 数据
44 WmsWarehouseDO warehouse = createWarehouse(); 71 WmsWarehouseDO warehouse = createWarehouse();
@@ -51,11 +78,23 @@ public class WmsWarehouseServiceImplTest extends BaseDbUnitTest { @@ -51,11 +78,23 @@ public class WmsWarehouseServiceImplTest extends BaseDbUnitTest {
51 } 78 }
52 79
53 private static WmsWarehouseDO createWarehouse() { 80 private static WmsWarehouseDO createWarehouse() {
  81 + return createWarehouse("WH001", "成品仓");
  82 + }
  83 +
  84 + private static WmsWarehouseDO createWarehouse(String code, String name) {
54 return WmsWarehouseDO.builder() 85 return WmsWarehouseDO.builder()
55 - .code("WH001")  
56 - .name("成品仓") 86 + .code(code)
  87 + .name(name)
57 .sort(1) 88 .sort(1)
58 .build(); 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,13 +88,13 @@ CREATE TABLE IF NOT EXISTS "wms_item_sku" (
88 "item_id" bigint NOT NULL, 88 "item_id" bigint NOT NULL,
89 "bar_code" varchar(64) DEFAULT NULL, 89 "bar_code" varchar(64) DEFAULT NULL,
90 "code" varchar(64) DEFAULT NULL, 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 "creator" varchar(64) DEFAULT '', 98 "creator" varchar(64) DEFAULT '',
99 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 99 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
100 "updater" varchar(64) DEFAULT '', 100 "updater" varchar(64) DEFAULT '',
@@ -108,7 +108,7 @@ CREATE TABLE IF NOT EXISTS "wms_inventory" ( @@ -108,7 +108,7 @@ CREATE TABLE IF NOT EXISTS "wms_inventory" (
108 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, 108 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
109 "sku_id" bigint NOT NULL, 109 "sku_id" bigint NOT NULL,
110 "warehouse_id" bigint NOT NULL, 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 "remark" varchar(255) DEFAULT NULL, 112 "remark" varchar(255) DEFAULT NULL,
113 "creator" varchar(64) DEFAULT '', 113 "creator" varchar(64) DEFAULT '',
114 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 114 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -124,11 +124,11 @@ CREATE TABLE IF NOT EXISTS "wms_inventory_history" ( @@ -124,11 +124,11 @@ CREATE TABLE IF NOT EXISTS "wms_inventory_history" (
124 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, 124 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
125 "warehouse_id" bigint NOT NULL, 125 "warehouse_id" bigint NOT NULL,
126 "sku_id" bigint NOT NULL, 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 "remark" varchar(255) DEFAULT NULL, 132 "remark" varchar(255) DEFAULT NULL,
133 "order_id" bigint DEFAULT NULL, 133 "order_id" bigint DEFAULT NULL,
134 "order_no" varchar(64) DEFAULT NULL, 134 "order_no" varchar(64) DEFAULT NULL,
@@ -153,7 +153,7 @@ CREATE TABLE IF NOT EXISTS "wms_receipt_order" ( @@ -153,7 +153,7 @@ CREATE TABLE IF NOT EXISTS "wms_receipt_order" (
153 "remark" varchar(255) DEFAULT NULL, 153 "remark" varchar(255) DEFAULT NULL,
154 "warehouse_id" bigint NOT NULL, 154 "warehouse_id" bigint NOT NULL,
155 "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0', 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 "creator" varchar(64) DEFAULT '', 157 "creator" varchar(64) DEFAULT '',
158 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 158 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
159 "updater" varchar(64) DEFAULT '', 159 "updater" varchar(64) DEFAULT '',
@@ -169,8 +169,8 @@ CREATE TABLE IF NOT EXISTS "wms_receipt_order_detail" ( @@ -169,8 +169,8 @@ CREATE TABLE IF NOT EXISTS "wms_receipt_order_detail" (
169 "sku_id" bigint NOT NULL, 169 "sku_id" bigint NOT NULL,
170 "warehouse_id" bigint NOT NULL, 170 "warehouse_id" bigint NOT NULL,
171 "quantity" decimal(20, 2) NOT NULL DEFAULT '0', 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 "creator" varchar(64) DEFAULT '', 174 "creator" varchar(64) DEFAULT '',
175 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 175 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
176 "updater" varchar(64) DEFAULT '', 176 "updater" varchar(64) DEFAULT '',
@@ -191,7 +191,7 @@ CREATE TABLE IF NOT EXISTS "wms_shipment_order" ( @@ -191,7 +191,7 @@ CREATE TABLE IF NOT EXISTS "wms_shipment_order" (
191 "remark" varchar(255) DEFAULT NULL, 191 "remark" varchar(255) DEFAULT NULL,
192 "warehouse_id" bigint NOT NULL, 192 "warehouse_id" bigint NOT NULL,
193 "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0', 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 "creator" varchar(64) DEFAULT '', 195 "creator" varchar(64) DEFAULT '',
196 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 196 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
197 "updater" varchar(64) DEFAULT '', 197 "updater" varchar(64) DEFAULT '',
@@ -207,8 +207,8 @@ CREATE TABLE IF NOT EXISTS "wms_shipment_order_detail" ( @@ -207,8 +207,8 @@ CREATE TABLE IF NOT EXISTS "wms_shipment_order_detail" (
207 "sku_id" bigint NOT NULL, 207 "sku_id" bigint NOT NULL,
208 "warehouse_id" bigint NOT NULL, 208 "warehouse_id" bigint NOT NULL,
209 "quantity" decimal(20, 2) NOT NULL DEFAULT '0', 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 "creator" varchar(64) DEFAULT '', 212 "creator" varchar(64) DEFAULT '',
213 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 213 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
214 "updater" varchar(64) DEFAULT '', 214 "updater" varchar(64) DEFAULT '',
@@ -227,7 +227,7 @@ CREATE TABLE IF NOT EXISTS "wms_movement_order" ( @@ -227,7 +227,7 @@ CREATE TABLE IF NOT EXISTS "wms_movement_order" (
227 "source_warehouse_id" bigint NOT NULL, 227 "source_warehouse_id" bigint NOT NULL,
228 "target_warehouse_id" bigint NOT NULL, 228 "target_warehouse_id" bigint NOT NULL,
229 "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0', 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 "creator" varchar(64) DEFAULT '', 231 "creator" varchar(64) DEFAULT '',
232 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 232 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
233 "updater" varchar(64) DEFAULT '', 233 "updater" varchar(64) DEFAULT '',
@@ -244,8 +244,8 @@ CREATE TABLE IF NOT EXISTS "wms_movement_order_detail" ( @@ -244,8 +244,8 @@ CREATE TABLE IF NOT EXISTS "wms_movement_order_detail" (
244 "source_warehouse_id" bigint NOT NULL, 244 "source_warehouse_id" bigint NOT NULL,
245 "target_warehouse_id" bigint NOT NULL, 245 "target_warehouse_id" bigint NOT NULL,
246 "quantity" decimal(20, 2) NOT NULL DEFAULT '0', 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 "creator" varchar(64) DEFAULT '', 249 "creator" varchar(64) DEFAULT '',
250 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 250 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
251 "updater" varchar(64) DEFAULT '', 251 "updater" varchar(64) DEFAULT '',
@@ -263,8 +263,8 @@ CREATE TABLE IF NOT EXISTS "wms_check_order" ( @@ -263,8 +263,8 @@ CREATE TABLE IF NOT EXISTS "wms_check_order" (
263 "remark" varchar(255) DEFAULT NULL, 263 "remark" varchar(255) DEFAULT NULL,
264 "warehouse_id" bigint NOT NULL, 264 "warehouse_id" bigint NOT NULL,
265 "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0', 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 "creator" varchar(64) DEFAULT '', 268 "creator" varchar(64) DEFAULT '',
269 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 269 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
270 "updater" varchar(64) DEFAULT '', 270 "updater" varchar(64) DEFAULT '',
@@ -283,7 +283,7 @@ CREATE TABLE IF NOT EXISTS "wms_check_order_detail" ( @@ -283,7 +283,7 @@ CREATE TABLE IF NOT EXISTS "wms_check_order_detail" (
283 "receipt_time" timestamp DEFAULT NULL, 283 "receipt_time" timestamp DEFAULT NULL,
284 "quantity" decimal(20, 2) NOT NULL DEFAULT '0', 284 "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
285 "check_quantity" decimal(20, 2) NOT NULL DEFAULT '0', 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 "creator" varchar(64) DEFAULT '', 287 "creator" varchar(64) DEFAULT '',
288 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 288 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
289 "updater" varchar(64) DEFAULT '', 289 "updater" varchar(64) DEFAULT '',
yudao-server/src/main/resources/application-local.yaml
@@ -189,6 +189,7 @@ logging: @@ -189,6 +189,7 @@ logging:
189 cn.iocoder.yudao.module.crm.dal.mysql: debug 189 cn.iocoder.yudao.module.crm.dal.mysql: debug
190 cn.iocoder.yudao.module.erp.dal.mysql: debug 190 cn.iocoder.yudao.module.erp.dal.mysql: debug
191 cn.iocoder.yudao.module.mes.dal.mysql: debug 191 cn.iocoder.yudao.module.mes.dal.mysql: debug
  192 + cn.iocoder.yudao.module.wms.dal.mysql: debug
192 cn.iocoder.yudao.module.iot.dal.mysql: debug 193 cn.iocoder.yudao.module.iot.dal.mysql: debug
193 cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG 194 cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG
194 cn.iocoder.yudao.module.iot.service.rule: debug 195 cn.iocoder.yudao.module.iot.service.rule: debug