Commit 5159b19e2d234e6a8a4c56465284cecb6f85aa1a
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,沉淀本轮决策依据
Showing
4 changed files
with
67 additions
and
31 deletions
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 | ... | ... |