Commit 86d760fdb1593fdd591aa5447cc75058f0681e03

Authored by YunaiV
1 parent fe38e4fd

feat(wms):减法,去掉批次号等字段

Showing 29 changed files with 939 additions and 231 deletions
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/history/WmsInventoryHistoryRespVO.java
@@ -40,12 +40,6 @@ public class WmsInventoryHistoryRespVO { @@ -40,12 +40,6 @@ public class WmsInventoryHistoryRespVO {
40 private BigDecimal beforeQuantity; 40 private BigDecimal beforeQuantity;
41 @Schema(description = "变化后库存数量", example = "100.00") 41 @Schema(description = "变化后库存数量", example = "100.00")
42 private BigDecimal afterQuantity; 42 private BigDecimal afterQuantity;
43 - @Schema(description = "批号", example = "BATCH001")  
44 - private String batchNo;  
45 - @Schema(description = "生产日期")  
46 - private LocalDateTime productionDate;  
47 - @Schema(description = "过期日期")  
48 - private LocalDateTime expirationDate;  
49 @Schema(description = "金额", example = "1000.00") 43 @Schema(description = "金额", example = "1000.00")
50 private BigDecimal amount; 44 private BigDecimal amount;
51 @Schema(description = "备注", example = "备注") 45 @Schema(description = "备注", example = "备注")
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailRespVO.java
@@ -46,15 +46,6 @@ public class WmsCheckOrderDetailRespVO { @@ -46,15 +46,6 @@ public class WmsCheckOrderDetailRespVO {
46 @Schema(description = "库存编号", example = "1024") 46 @Schema(description = "库存编号", example = "1024")
47 private Long inventoryId; 47 private Long inventoryId;
48 48
49 - @Schema(description = "批号", example = "B202605110001")  
50 - private String batchNo;  
51 -  
52 - @Schema(description = "生产日期")  
53 - private LocalDateTime productionDate;  
54 -  
55 - @Schema(description = "过期日期")  
56 - private LocalDateTime expirationDate;  
57 -  
58 @Schema(description = "入库时间") 49 @Schema(description = "入库时间")
59 private LocalDateTime receiptTime; 50 private LocalDateTime receiptTime;
60 51
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailSaveReqVO.java
@@ -23,16 +23,6 @@ public class WmsCheckOrderDetailSaveReqVO { @@ -23,16 +23,6 @@ public class WmsCheckOrderDetailSaveReqVO {
23 @Schema(description = "库存编号", example = "1024") 23 @Schema(description = "库存编号", example = "1024")
24 private Long inventoryId; 24 private Long inventoryId;
25 25
26 - @Schema(description = "批号", example = "B202605110001")  
27 - @Size(max = 64, message = "批号长度不能超过 64 个字符")  
28 - private String batchNo;  
29 -  
30 - @Schema(description = "生产日期")  
31 - private LocalDateTime productionDate;  
32 -  
33 - @Schema(description = "过期日期")  
34 - private LocalDateTime expirationDate;  
35 -  
36 @Schema(description = "入库时间") 26 @Schema(description = "入库时间")
37 private LocalDateTime receiptTime; 27 private LocalDateTime receiptTime;
38 28
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailRespVO.java
@@ -49,15 +49,6 @@ public class WmsMovementOrderDetailRespVO { @@ -49,15 +49,6 @@ public class WmsMovementOrderDetailRespVO {
49 @Schema(description = "目标仓库名称", example = "上海仓") 49 @Schema(description = "目标仓库名称", example = "上海仓")
50 private String targetWarehouseName; 50 private String targetWarehouseName;
51 51
52 - @Schema(description = "批号", example = "B202605110001")  
53 - private String batchNo;  
54 -  
55 - @Schema(description = "生产日期")  
56 - private LocalDateTime productionDate;  
57 -  
58 - @Schema(description = "过期日期")  
59 - private LocalDateTime expirationDate;  
60 -  
61 @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") 52 @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
62 private BigDecimal quantity; 53 private BigDecimal quantity;
63 54
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailSaveReqVO.java
@@ -7,7 +7,6 @@ import jakarta.validation.constraints.Size; @@ -7,7 +7,6 @@ import jakarta.validation.constraints.Size;
7 import lombok.Data; 7 import lombok.Data;
8 8
9 import java.math.BigDecimal; 9 import java.math.BigDecimal;
10 -import java.time.LocalDateTime;  
11 10
12 @Schema(description = "管理后台 - WMS 移库单明细保存 Request VO") 11 @Schema(description = "管理后台 - WMS 移库单明细保存 Request VO")
13 @Data 12 @Data
@@ -20,16 +19,6 @@ public class WmsMovementOrderDetailSaveReqVO { @@ -20,16 +19,6 @@ public class WmsMovementOrderDetailSaveReqVO {
20 @NotNull(message = "SKU 不能为空") 19 @NotNull(message = "SKU 不能为空")
21 private Long skuId; 20 private Long skuId;
22 21
23 - @Schema(description = "批号", example = "B202605110001")  
24 - @Size(max = 64, message = "批号长度不能超过 64 个字符")  
25 - private String batchNo;  
26 -  
27 - @Schema(description = "生产日期")  
28 - private LocalDateTime productionDate;  
29 -  
30 - @Schema(description = "过期日期")  
31 - private LocalDateTime expirationDate;  
32 -  
33 @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") 22 @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
34 @NotNull(message = "移库数量不能为空") 23 @NotNull(message = "移库数量不能为空")
35 @DecimalMin(value = "0", inclusive = false, message = "移库数量必须大于 0") 24 @DecimalMin(value = "0", inclusive = false, message = "移库数量必须大于 0")
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailRespVO.java
@@ -43,15 +43,6 @@ public class WmsReceiptOrderDetailRespVO { @@ -43,15 +43,6 @@ public class WmsReceiptOrderDetailRespVO {
43 @Schema(description = "仓库名称", example = "北京仓") 43 @Schema(description = "仓库名称", example = "北京仓")
44 private String warehouseName; 44 private String warehouseName;
45 45
46 - @Schema(description = "批号", example = "B202605110001")  
47 - private String batchNo;  
48 -  
49 - @Schema(description = "生产日期")  
50 - private LocalDateTime productionDate;  
51 -  
52 - @Schema(description = "过期日期")  
53 - private LocalDateTime expirationDate;  
54 -  
55 @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") 46 @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
56 private BigDecimal quantity; 47 private BigDecimal quantity;
57 48
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailSaveReqVO.java
@@ -7,7 +7,6 @@ import jakarta.validation.constraints.Size; @@ -7,7 +7,6 @@ import jakarta.validation.constraints.Size;
7 import lombok.Data; 7 import lombok.Data;
8 8
9 import java.math.BigDecimal; 9 import java.math.BigDecimal;
10 -import java.time.LocalDateTime;  
11 10
12 @Schema(description = "管理后台 - WMS 入库单明细保存 Request VO") 11 @Schema(description = "管理后台 - WMS 入库单明细保存 Request VO")
13 @Data 12 @Data
@@ -20,16 +19,6 @@ public class WmsReceiptOrderDetailSaveReqVO { @@ -20,16 +19,6 @@ public class WmsReceiptOrderDetailSaveReqVO {
20 @NotNull(message = "SKU 不能为空") 19 @NotNull(message = "SKU 不能为空")
21 private Long skuId; 20 private Long skuId;
22 21
23 - @Schema(description = "批号", example = "B202605110001")  
24 - @Size(max = 64, message = "批号长度不能超过 64 个字符")  
25 - private String batchNo;  
26 -  
27 - @Schema(description = "生产日期")  
28 - private LocalDateTime productionDate;  
29 -  
30 - @Schema(description = "过期日期")  
31 - private LocalDateTime expirationDate;  
32 -  
33 @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") 22 @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
34 @NotNull(message = "入库数量不能为空") 23 @NotNull(message = "入库数量不能为空")
35 @DecimalMin(value = "0", inclusive = false, message = "入库数量必须大于 0") 24 @DecimalMin(value = "0", inclusive = false, message = "入库数量必须大于 0")
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/detail/WmsShipmentOrderDetailRespVO.java
@@ -43,15 +43,6 @@ public class WmsShipmentOrderDetailRespVO { @@ -43,15 +43,6 @@ public class WmsShipmentOrderDetailRespVO {
43 @Schema(description = "仓库名称", example = "北京仓") 43 @Schema(description = "仓库名称", example = "北京仓")
44 private String warehouseName; 44 private String warehouseName;
45 45
46 - @Schema(description = "批号", example = "B202605110001")  
47 - private String batchNo;  
48 -  
49 - @Schema(description = "生产日期")  
50 - private LocalDateTime productionDate;  
51 -  
52 - @Schema(description = "过期日期")  
53 - private LocalDateTime expirationDate;  
54 -  
55 @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") 46 @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
56 private BigDecimal quantity; 47 private BigDecimal quantity;
57 48
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/detail/WmsShipmentOrderDetailSaveReqVO.java
@@ -7,7 +7,6 @@ import jakarta.validation.constraints.Size; @@ -7,7 +7,6 @@ import jakarta.validation.constraints.Size;
7 import lombok.Data; 7 import lombok.Data;
8 8
9 import java.math.BigDecimal; 9 import java.math.BigDecimal;
10 -import java.time.LocalDateTime;  
11 10
12 @Schema(description = "管理后台 - WMS 出库单明细保存 Request VO") 11 @Schema(description = "管理后台 - WMS 出库单明细保存 Request VO")
13 @Data 12 @Data
@@ -20,16 +19,6 @@ public class WmsShipmentOrderDetailSaveReqVO { @@ -20,16 +19,6 @@ public class WmsShipmentOrderDetailSaveReqVO {
20 @NotNull(message = "SKU 不能为空") 19 @NotNull(message = "SKU 不能为空")
21 private Long skuId; 20 private Long skuId;
22 21
23 - @Schema(description = "批号", example = "B202605110001")  
24 - @Size(max = 64, message = "批号长度不能超过 64 个字符")  
25 - private String batchNo;  
26 -  
27 - @Schema(description = "生产日期")  
28 - private LocalDateTime productionDate;  
29 -  
30 - @Schema(description = "过期日期")  
31 - private LocalDateTime expirationDate;  
32 -  
33 @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") 22 @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
34 @NotNull(message = "出库数量不能为空") 23 @NotNull(message = "出库数量不能为空")
35 @DecimalMin(value = "0", inclusive = false, message = "出库数量必须大于 0") 24 @DecimalMin(value = "0", inclusive = false, message = "出库数量必须大于 0")
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryHistoryDO.java
@@ -10,7 +10,6 @@ import com.baomidou.mybatisplus.annotation.TableName; @@ -10,7 +10,6 @@ import com.baomidou.mybatisplus.annotation.TableName;
10 import lombok.*; 10 import lombok.*;
11 11
12 import java.math.BigDecimal; 12 import java.math.BigDecimal;
13 -import java.time.LocalDateTime;  
14 13
15 /** 14 /**
16 * WMS 库存流水 DO 15 * WMS 库存流水 DO
@@ -60,21 +59,6 @@ public class WmsInventoryHistoryDO extends BaseDO { @@ -60,21 +59,6 @@ public class WmsInventoryHistoryDO extends BaseDO {
60 */ 59 */
61 private BigDecimal afterQuantity; 60 private BigDecimal afterQuantity;
62 61
63 - // ========= 批次效期相关字段 =========  
64 -  
65 - /**  
66 - * 批号  
67 - */  
68 - private String batchNo;  
69 - /**  
70 - * 生产日期  
71 - */  
72 - private LocalDateTime productionDate;  
73 - /**  
74 - * 过期日期  
75 - */  
76 - private LocalDateTime expirationDate;  
77 -  
78 // ========= 金额备注相关字段 ========= 62 // ========= 金额备注相关字段 =========
79 63
80 /** 64 /**
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDetailDO.java
@@ -67,20 +67,6 @@ public class WmsCheckOrderDetailDO extends BaseDO { @@ -67,20 +67,6 @@ public class WmsCheckOrderDetailDO extends BaseDO {
67 */ 67 */
68 private Long inventoryId; 68 private Long inventoryId;
69 69
70 - // ========= 批次效期字段 =========  
71 -  
72 - /**  
73 - * 批号  
74 - */  
75 - private String batchNo;  
76 - /**  
77 - * 生产日期  
78 - */  
79 - private LocalDateTime productionDate;  
80 - /**  
81 - * 过期日期  
82 - */  
83 - private LocalDateTime expirationDate;  
84 /** 70 /**
85 * 入库时间 71 * 入库时间
86 */ 72 */
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDetailDO.java
@@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.annotation.TableName; @@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.annotation.TableName;
9 import lombok.*; 9 import lombok.*;
10 10
11 import java.math.BigDecimal; 11 import java.math.BigDecimal;
12 -import java.time.LocalDateTime;  
13 12
14 /** 13 /**
15 * WMS 移库单明细 DO 14 * WMS 移库单明细 DO
@@ -69,21 +68,6 @@ public class WmsMovementOrderDetailDO extends BaseDO { @@ -69,21 +68,6 @@ public class WmsMovementOrderDetailDO extends BaseDO {
69 */ 68 */
70 private Long targetWarehouseId; 69 private Long targetWarehouseId;
71 70
72 - // ========= 批次效期字段 =========  
73 -  
74 - /**  
75 - * 批号  
76 - */  
77 - private String batchNo;  
78 - /**  
79 - * 生产日期  
80 - */  
81 - private LocalDateTime productionDate;  
82 - /**  
83 - * 过期日期  
84 - */  
85 - private LocalDateTime expirationDate;  
86 -  
87 // ========= 汇总金额字段 ========= 71 // ========= 汇总金额字段 =========
88 72
89 /** 73 /**
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDetailDO.java
@@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.annotation.TableName; @@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.annotation.TableName;
9 import lombok.*; 9 import lombok.*;
10 10
11 import java.math.BigDecimal; 11 import java.math.BigDecimal;
12 -import java.time.LocalDateTime;  
13 12
14 /** 13 /**
15 * WMS 入库单明细 DO 14 * WMS 入库单明细 DO
@@ -60,21 +59,6 @@ public class WmsReceiptOrderDetailDO extends BaseDO { @@ -60,21 +59,6 @@ public class WmsReceiptOrderDetailDO extends BaseDO {
60 */ 59 */
61 private Long warehouseId; 60 private Long warehouseId;
62 61
63 - // ========= 批次效期字段 =========  
64 -  
65 - /**  
66 - * 批号  
67 - */  
68 - private String batchNo;  
69 - /**  
70 - * 生产日期  
71 - */  
72 - private LocalDateTime productionDate;  
73 - /**  
74 - * 过期日期  
75 - */  
76 - private LocalDateTime expirationDate;  
77 -  
78 // ========= 汇总金额字段 ========= 62 // ========= 汇总金额字段 =========
79 63
80 /** 64 /**
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/shipment/WmsShipmentOrderDetailDO.java
@@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.annotation.TableName; @@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.annotation.TableName;
9 import lombok.*; 9 import lombok.*;
10 10
11 import java.math.BigDecimal; 11 import java.math.BigDecimal;
12 -import java.time.LocalDateTime;  
13 12
14 /** 13 /**
15 * WMS 出库单明细 DO 14 * WMS 出库单明细 DO
@@ -60,21 +59,6 @@ public class WmsShipmentOrderDetailDO extends BaseDO { @@ -60,21 +59,6 @@ public class WmsShipmentOrderDetailDO extends BaseDO {
60 */ 59 */
61 private Long warehouseId; 60 private Long warehouseId;
62 61
63 - // ========= 批次效期字段 =========  
64 -  
65 - /**  
66 - * 批号  
67 - */  
68 - private String batchNo;  
69 - /**  
70 - * 生产日期  
71 - */  
72 - private LocalDateTime productionDate;  
73 - /**  
74 - * 过期日期  
75 - */  
76 - private LocalDateTime expirationDate;  
77 -  
78 // ========= 汇总金额字段 ========= 62 // ========= 汇总金额字段 =========
79 63
80 /** 64 /**
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/framework/config/package-info.java deleted
1 -/**  
2 - * WMS 模块配置  
3 - */  
4 -package cn.iocoder.yudao.module.wms.framework.config;  
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java
@@ -121,50 +121,50 @@ public class WmsInventoryServiceImpl implements WmsInventoryService { @@ -121,50 +121,50 @@ public class WmsInventoryServiceImpl implements WmsInventoryService {
121 } 121 }
122 122
123 private List<WmsInventoryDO> getOrCreateInventoryList(List<WmsInventoryChangeReqDTO.Item> items) { 123 private List<WmsInventoryDO> getOrCreateInventoryList(List<WmsInventoryChangeReqDTO.Item> items) {
124 - // 先按库存维度在内存去重,避免同一批明细重复查询或重复补行。  
125 - List<WmsInventoryDO> inventoryKeyList = new ArrayList<>(items.size()); 124 + // 1.1 先按库存维度在内存去重,避免同一批明细重复查询或重复补行
  125 + List<WmsInventoryDO> inventoryDimensions = new ArrayList<>(items.size());
126 for (WmsInventoryChangeReqDTO.Item item : items) { 126 for (WmsInventoryChangeReqDTO.Item item : items) {
127 - if (findInventory(inventoryKeyList, item) == null) {  
128 - inventoryKeyList.add(new WmsInventoryDO().setSkuId(item.getSkuId()) 127 + if (findInventory(inventoryDimensions, item) == null) {
  128 + inventoryDimensions.add(new WmsInventoryDO().setSkuId(item.getSkuId())
129 .setWarehouseId(item.getWarehouseId())); 129 .setWarehouseId(item.getWarehouseId()));
130 } 130 }
131 } 131 }
132 -  
133 - // 批量查询已存在的库存行;这里不加锁,后续统一按库存 ID 批量加锁。  
134 - List<WmsInventoryDO> inventories = inventoryMapper.selectListByKeys(inventoryKeyList);  
135 -  
136 - // 对比库存维度,找出数据库中还不存在的库存行,再按唯一索引兜底补齐。  
137 - List<WmsInventoryDO> missingInventoryList = new ArrayList<>(inventoryKeyList.size());  
138 - for (WmsInventoryDO inventoryKey : inventoryKeyList) {  
139 - if (findInventory(inventories, inventoryKey) == null) {  
140 - missingInventoryList.add(inventoryKey); 132 + // 1.2 批量查询已存在的库存行(这里不加锁,后续统一按库存 ID 批量加锁)
  133 + List<WmsInventoryDO> inventories = inventoryMapper.selectListByKeys(inventoryDimensions);
  134 +
  135 + // 2.1 对比库存维度,找出数据库中还不存在的库存行
  136 + List<WmsInventoryDO> missingInventories = new ArrayList<>(inventoryDimensions.size());
  137 + for (WmsInventoryDO inventoryDimension : inventoryDimensions) {
  138 + if (findInventory(inventories, inventoryDimension) == null) {
  139 + missingInventories.add(inventoryDimension);
141 } 140 }
142 } 141 }
143 - if (CollUtil.isEmpty(missingInventoryList)) { 142 + // 2.2 如果库存行都已存在,直接返回待加锁库存列表
  143 + if (CollUtil.isEmpty(missingInventories)) {
144 return inventories; 144 return inventories;
145 } 145 }
146 -  
147 - inventories.addAll(createMissingInventoryList(missingInventoryList)); 146 + // 2.3 对缺失库存行执行创建;并发冲突时,内部会按唯一索引回查已创建的库存行
  147 + inventories.addAll(createMissingInventoryList(missingInventories));
148 return inventories; 148 return inventories;
149 } 149 }
150 150
151 - private List<WmsInventoryDO> createMissingInventoryList(List<WmsInventoryDO> missingInventoryList) { 151 + private List<WmsInventoryDO> createMissingInventoryList(List<WmsInventoryDO> missingInventories) {
152 // 优先批量插入缺失库存行;并发唯一键冲突时,再逐个插入并回查冲突行。 152 // 优先批量插入缺失库存行;并发唯一键冲突时,再逐个插入并回查冲突行。
153 - List<WmsInventoryDO> newInventoryList = convertList(missingInventoryList, missingInventory -> 153 + List<WmsInventoryDO> newInventories = convertList(missingInventories, missingInventory ->
154 new WmsInventoryDO().setSkuId(missingInventory.getSkuId()).setWarehouseId(missingInventory.getWarehouseId()) 154 new WmsInventoryDO().setSkuId(missingInventory.getSkuId()).setWarehouseId(missingInventory.getWarehouseId())
155 .setQuantity(BigDecimal.ZERO)); 155 .setQuantity(BigDecimal.ZERO));
156 try { 156 try {
157 - inventoryMapper.insertBatch(newInventoryList);  
158 - return newInventoryList; 157 + inventoryMapper.insertBatch(newInventories);
  158 + return newInventories;
159 } catch (DuplicateKeyException ex) { 159 } catch (DuplicateKeyException ex) {
160 // 并发事务可能已经补齐部分库存行,降级为逐个插入并回查唯一键冲突的行。 160 // 并发事务可能已经补齐部分库存行,降级为逐个插入并回查唯一键冲突的行。
161 - log.warn("[createMissingInventoryList][missingInventoryList({}) 批量插入库存行冲突,降级为逐个插入]",  
162 - missingInventoryList, ex); 161 + log.warn("[createMissingInventoryList][missingInventories({}) 批量插入库存行冲突,降级为逐个插入]",
  162 + missingInventories, ex);
163 } 163 }
164 164
165 // 批量插入失败后逐条补齐;单条唯一键冲突时,回查并使用并发事务创建的库存行。 165 // 批量插入失败后逐条补齐;单条唯一键冲突时,回查并使用并发事务创建的库存行。
166 - List<WmsInventoryDO> resultList = new ArrayList<>(missingInventoryList.size());  
167 - for (WmsInventoryDO missingInventory : missingInventoryList) { 166 + List<WmsInventoryDO> createdInventories = new ArrayList<>(missingInventories.size());
  167 + for (WmsInventoryDO missingInventory : missingInventories) {
168 WmsInventoryDO inventory = new WmsInventoryDO().setSkuId(missingInventory.getSkuId()) 168 WmsInventoryDO inventory = new WmsInventoryDO().setSkuId(missingInventory.getSkuId())
169 .setWarehouseId(missingInventory.getWarehouseId()) 169 .setWarehouseId(missingInventory.getWarehouseId())
170 .setQuantity(BigDecimal.ZERO); 170 .setQuantity(BigDecimal.ZERO);
@@ -178,9 +178,9 @@ public class WmsInventoryServiceImpl implements WmsInventoryService { @@ -178,9 +178,9 @@ public class WmsInventoryServiceImpl implements WmsInventoryService {
178 throw ex; 178 throw ex;
179 } 179 }
180 } 180 }
181 - resultList.add(inventory); 181 + createdInventories.add(inventory);
182 } 182 }
183 - return resultList; 183 + return createdInventories;
184 } 184 }
185 185
186 private WmsInventoryHistoryDO buildInventoryHistory(WmsInventoryChangeReqDTO reqDTO, 186 private WmsInventoryHistoryDO buildInventoryHistory(WmsInventoryChangeReqDTO reqDTO,
@@ -189,8 +189,6 @@ public class WmsInventoryServiceImpl implements WmsInventoryService { @@ -189,8 +189,6 @@ public class WmsInventoryServiceImpl implements WmsInventoryService {
189 return new WmsInventoryHistoryDO() 189 return new WmsInventoryHistoryDO()
190 .setWarehouseId(item.getWarehouseId()).setSkuId(item.getSkuId()) 190 .setWarehouseId(item.getWarehouseId()).setSkuId(item.getSkuId())
191 .setQuantity(item.getQuantity()).setBeforeQuantity(result.get(0)).setAfterQuantity(result.get(1)) 191 .setQuantity(item.getQuantity()).setBeforeQuantity(result.get(0)).setAfterQuantity(result.get(1))
192 - .setBatchNo(item.getBatchNo()).setProductionDate(item.getProductionDate())  
193 - .setExpirationDate(item.getExpirationDate())  
194 .setAmount(item.getAmount()).setRemark(item.getRemark()) 192 .setAmount(item.getAmount()).setRemark(item.getRemark())
195 .setOrderId(reqDTO.getOrderId()).setOrderNo(reqDTO.getOrderNo()).setOrderType(reqDTO.getOrderType()); 193 .setOrderId(reqDTO.getOrderId()).setOrderNo(reqDTO.getOrderNo()).setOrderType(reqDTO.getOrderType());
196 } 194 }
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/dto/WmsInventoryChangeReqDTO.java
@@ -4,7 +4,6 @@ import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum; @@ -4,7 +4,6 @@ import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum;
4 import lombok.Data; 4 import lombok.Data;
5 5
6 import java.math.BigDecimal; 6 import java.math.BigDecimal;
7 -import java.time.LocalDateTime;  
8 import java.util.List; 7 import java.util.List;
9 8
10 /** 9 /**
@@ -54,21 +53,6 @@ public class WmsInventoryChangeReqDTO { @@ -54,21 +53,6 @@ public class WmsInventoryChangeReqDTO {
54 */ 53 */
55 private BigDecimal quantity; 54 private BigDecimal quantity;
56 55
57 - // ========= 批次效期相关字段 =========  
58 -  
59 - /**  
60 - * 批号  
61 - */  
62 - private String batchNo;  
63 - /**  
64 - * 生产日期  
65 - */  
66 - private LocalDateTime productionDate;  
67 - /**  
68 - * 过期日期  
69 - */  
70 - private LocalDateTime expirationDate;  
71 -  
72 // ========= 金额备注相关字段 ========= 56 // ========= 金额备注相关字段 =========
73 57
74 /** 58 /**
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImpl.java
1 package cn.iocoder.yudao.module.wms.service.order.check; 1 package cn.iocoder.yudao.module.wms.service.order.check;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -171,9 +172,7 @@ public class WmsCheckOrderServiceImpl implements WmsCheckOrderService { @@ -171,9 +172,7 @@ public class WmsCheckOrderServiceImpl implements WmsCheckOrderService {
171 } 172 }
172 173
173 /** 174 /**
174 - * 盘点盘库单对应库存。  
175 - *  
176 - * 盘库只对盈亏数量生成库存变更:实盘数量大于账面数量时增加库存,反之扣减库存,批次字段随库存流水记录。 175 + * 盘点盘库单对应库存
177 * 176 *
178 * @param order 盘库单 177 * @param order 盘库单
179 * @param details 盘库单明细列表 178 * @param details 盘库单明细列表
@@ -189,6 +188,9 @@ public class WmsCheckOrderServiceImpl implements WmsCheckOrderService { @@ -189,6 +188,9 @@ public class WmsCheckOrderServiceImpl implements WmsCheckOrderService {
189 .setQuantity(differenceQuantity); 188 .setQuantity(differenceQuantity);
190 items.add(item); 189 items.add(item);
191 } 190 }
  191 + if (CollUtil.isEmpty(items)) {
  192 + return;
  193 + }
192 inventoryService.changeInventory(new WmsInventoryChangeReqDTO() 194 inventoryService.changeInventory(new WmsInventoryChangeReqDTO()
193 .setOrderId(order.getId()).setOrderNo(order.getNo()) 195 .setOrderId(order.getId()).setOrderNo(order.getNo())
194 .setOrderType(WmsInventoryOrderTypeEnum.CHECK.getType()).setItems(items)); 196 .setOrderType(WmsInventoryOrderTypeEnum.CHECK.getType()).setItems(items));
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImpl.java
@@ -175,9 +175,7 @@ public class WmsMovementOrderServiceImpl implements WmsMovementOrderService { @@ -175,9 +175,7 @@ public class WmsMovementOrderServiceImpl implements WmsMovementOrderService {
175 } 175 }
176 176
177 /** 177 /**
178 - * 移动移库单对应库存。  
179 - *  
180 - * 移库会生成两条库存变更:来源仓库扣减库存,目标仓库增加库存,批次字段随库存流水记录。 178 + * 移动移库单对应库存
181 * 179 *
182 * @param order 移库单 180 * @param order 移库单
183 * @param details 移库单明细列表 181 * @param details 移库单明细列表
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImpl.java
@@ -184,7 +184,7 @@ public class WmsReceiptOrderServiceImpl implements WmsReceiptOrderService { @@ -184,7 +184,7 @@ public class WmsReceiptOrderServiceImpl implements WmsReceiptOrderService {
184 /** 184 /**
185 * 增加入库单对应库存。 185 * 增加入库单对应库存。
186 * 186 *
187 - * 入库在库存变更模型中使用正数数量,批次字段随库存流水记录。 187 + * 入库在库存变更模型中使用正数数量。
188 * 188 *
189 * @param order 入库单 189 * @param order 入库单
190 * @param details 入库单明细列表 190 * @param details 入库单明细列表
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderServiceImpl.java
@@ -183,9 +183,7 @@ public class WmsShipmentOrderServiceImpl implements WmsShipmentOrderService { @@ -183,9 +183,7 @@ public class WmsShipmentOrderServiceImpl implements WmsShipmentOrderService {
183 } 183 }
184 184
185 /** 185 /**
186 - * 扣减出库单对应库存。  
187 - *  
188 - * 出库在库存变更模型中使用负数数量,批次字段随库存流水记录。 186 + * 扣减出库单对应库存
189 * 187 *
190 * @param order 出库单 188 * @param order 出库单
191 * @param details 出库单明细列表 189 * @param details 出库单明细列表
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImplTest.java
@@ -30,6 +30,7 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic @@ -30,6 +30,7 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic
30 import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.INVENTORY_QUANTITY_NOT_ENOUGH; 30 import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.INVENTORY_QUANTITY_NOT_ENOUGH;
31 import static org.junit.jupiter.api.Assertions.assertEquals; 31 import static org.junit.jupiter.api.Assertions.assertEquals;
32 import static org.junit.jupiter.api.Assertions.assertNotNull; 32 import static org.junit.jupiter.api.Assertions.assertNotNull;
  33 +import static org.junit.jupiter.api.Assertions.assertNull;
33 import static org.mockito.Mockito.when; 34 import static org.mockito.Mockito.when;
34 35
35 @Import({WmsInventoryServiceImpl.class, WmsInventoryHistoryServiceImpl.class}) 36 @Import({WmsInventoryServiceImpl.class, WmsInventoryHistoryServiceImpl.class})
@@ -230,6 +231,10 @@ public class WmsInventoryServiceImplTest extends BaseDbUnitTest { @@ -230,6 +231,10 @@ public class WmsInventoryServiceImplTest extends BaseDbUnitTest {
230 // 调用,并断言 231 // 调用,并断言
231 assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH, 232 assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH,
232 item.getName(), sku.getName(), 100L, new BigDecimal("2.000000"), new BigDecimal("-3.00")); 233 item.getName(), sku.getName(), 100L, new BigDecimal("2.000000"), new BigDecimal("-3.00"));
  234 + WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
  235 + assertNotNull(inventory);
  236 + assertEquals(0, new BigDecimal("2.00").compareTo(inventory.getQuantity()));
  237 + assertEquals(0L, inventoryHistoryMapper.selectCount());
233 } 238 }
234 239
235 @Test 240 @Test
@@ -243,7 +248,32 @@ public class WmsInventoryServiceImplTest extends BaseDbUnitTest { @@ -243,7 +248,32 @@ public class WmsInventoryServiceImplTest extends BaseDbUnitTest {
243 // 调用,并断言 248 // 调用,并断言
244 assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH, 249 assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH,
245 item.getName(), sku.getName(), 100L, BigDecimal.ZERO.setScale(6), new BigDecimal("-3.00")); 250 item.getName(), sku.getName(), 100L, BigDecimal.ZERO.setScale(6), new BigDecimal("-3.00"));
246 - assertEquals(0, inventoryMapper.selectCount()); 251 + assertEquals(0L, inventoryMapper.selectCount());
  252 + assertEquals(0L, inventoryHistoryMapper.selectCount());
  253 + }
  254 +
  255 + @Test
  256 + public void testChangeInventory_quantityNotEnoughRollbackCreatedInventories() {
  257 + // mock 数据
  258 + WmsItemDO item = createItem("ITEM-001", "红富士苹果");
  259 + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
  260 + WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "5.00");
  261 + List<WmsInventoryChangeReqDTO.Item> items = new ArrayList<>(reqDTO.getItems());
  262 + items.add(new WmsInventoryChangeReqDTO.Item()
  263 + .setSkuId(sku.getId())
  264 + .setWarehouseId(200L)
  265 + .setQuantity(new BigDecimal("-1.00"))
  266 + .setAmount(new BigDecimal("20.00"))
  267 + .setRemark("测试出库"));
  268 + reqDTO.setItems(items);
  269 + mockItemSkuAndItem(item, sku);
  270 +
  271 + // 调用,并断言:第二条明细库存不足,事务回滚前面补齐的库存行
  272 + assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH,
  273 + item.getName(), sku.getName(), 200L, BigDecimal.ZERO.setScale(6), new BigDecimal("-1.00"));
  274 + assertNull(inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L));
  275 + assertNull(inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 200L));
  276 + assertEquals(0L, inventoryHistoryMapper.selectCount());
247 } 277 }
248 278
249 @Test 279 @Test
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.service.order.check;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailSaveReqVO;
  5 +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO;
  6 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO;
  7 +import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderDetailMapper;
  8 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
  9 +import jakarta.annotation.Resource;
  10 +import org.junit.jupiter.api.Test;
  11 +import org.springframework.context.annotation.Import;
  12 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  13 +
  14 +import java.math.BigDecimal;
  15 +import java.util.Arrays;
  16 +import java.util.List;
  17 +
  18 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  19 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_DETAIL_NOT_EXISTS;
  20 +import static org.junit.jupiter.api.Assertions.assertEquals;
  21 +import static org.junit.jupiter.api.Assertions.assertNotNull;
  22 +import static org.junit.jupiter.api.Assertions.assertNull;
  23 +
  24 +@Import(WmsCheckOrderDetailServiceImpl.class)
  25 +public class WmsCheckOrderDetailServiceImplTest extends BaseDbUnitTest {
  26 +
  27 + @Resource
  28 + private WmsCheckOrderDetailServiceImpl checkOrderDetailService;
  29 +
  30 + @Resource
  31 + private WmsCheckOrderDetailMapper checkOrderDetailMapper;
  32 +
  33 + @MockitoBean
  34 + private WmsItemSkuService itemSkuService;
  35 +
  36 + @Test
  37 + public void testCreateCheckOrderDetailList_success() {
  38 + // mock 数据
  39 + Long orderId = 10L;
  40 + WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(
  41 + createCheckOrderDetailReqVO(null, 1001L, "10.00", "9.00"),
  42 + createCheckOrderDetailReqVO(null, 1002L, "20.00", "21.00"));
  43 +
  44 + // 调用
  45 + checkOrderDetailService.createCheckOrderDetailList(orderId, reqVO);
  46 +
  47 + // 断言
  48 + List<WmsCheckOrderDetailDO> details = checkOrderDetailMapper.selectListByOrderId(orderId);
  49 + assertEquals(2, details.size());
  50 + assertEquals(orderId, details.get(0).getOrderId());
  51 + assertEquals(orderId, details.get(1).getOrderId());
  52 + assertEquals(100L, details.get(0).getWarehouseId());
  53 + }
  54 +
  55 + @Test
  56 + public void testCreateCheckOrderDetailList_ignoreId() {
  57 + // mock 数据
  58 + WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(
  59 + createCheckOrderDetailReqVO(999L, 1001L, "10.00", "9.00"));
  60 +
  61 + // 调用
  62 + checkOrderDetailService.createCheckOrderDetailList(10L, reqVO);
  63 +
  64 + // 断言
  65 + List<WmsCheckOrderDetailDO> details = checkOrderDetailMapper.selectListByOrderId(10L);
  66 + assertEquals(1, details.size());
  67 + assertNotNull(details.get(0).getId());
  68 + assertEquals(1001L, details.get(0).getSkuId());
  69 + }
  70 +
  71 + @Test
  72 + public void testUpdateCheckOrderDetailList_diff() {
  73 + // mock 数据
  74 + Long orderId = 10L;
  75 + WmsCheckOrderDetailDO detail01 = createCheckOrderDetail(orderId, 1001L, "10.00", "9.00");
  76 + WmsCheckOrderDetailDO detail02 = createCheckOrderDetail(orderId, 1002L, "20.00", "21.00");
  77 + checkOrderDetailMapper.insert(detail01);
  78 + checkOrderDetailMapper.insert(detail02);
  79 + WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(
  80 + createCheckOrderDetailReqVO(detail01.getId(), 2001L, "11.00", "8.00"),
  81 + createCheckOrderDetailReqVO(null, 2002L, "22.00", "25.00"));
  82 +
  83 + // 调用
  84 + checkOrderDetailService.updateCheckOrderDetailList(orderId, reqVO);
  85 +
  86 + // 断言:修改
  87 + WmsCheckOrderDetailDO dbUpdateDetail = checkOrderDetailMapper.selectById(detail01.getId());
  88 + assertNotNull(dbUpdateDetail);
  89 + assertEquals(orderId, dbUpdateDetail.getOrderId());
  90 + assertEquals(2001L, dbUpdateDetail.getSkuId());
  91 + assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity()));
  92 + assertEquals(0, new BigDecimal("8.00").compareTo(dbUpdateDetail.getCheckQuantity()));
  93 + assertEquals(100L, dbUpdateDetail.getWarehouseId());
  94 + // 断言:新增
  95 + List<WmsCheckOrderDetailDO> details = checkOrderDetailMapper.selectListByOrderId(orderId);
  96 + assertEquals(2, details.size());
  97 + WmsCheckOrderDetailDO dbCreateDetail = details.stream()
  98 + .filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId()))
  99 + .findFirst().orElse(null);
  100 + assertNotNull(dbCreateDetail);
  101 + assertEquals(orderId, dbCreateDetail.getOrderId());
  102 + assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity()));
  103 + assertEquals(0, new BigDecimal("25.00").compareTo(dbCreateDetail.getCheckQuantity()));
  104 + // 断言:删除
  105 + assertNull(checkOrderDetailMapper.selectById(detail02.getId()));
  106 + }
  107 +
  108 + @Test
  109 + public void testUpdateCheckOrderDetailList_detailNotExists() {
  110 + // mock 数据
  111 + WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(
  112 + createCheckOrderDetailReqVO(999L, 1001L, "10.00", "9.00"));
  113 +
  114 + // 调用,并断言
  115 + assertServiceException(() -> checkOrderDetailService.updateCheckOrderDetailList(10L, reqVO),
  116 + CHECK_ORDER_DETAIL_NOT_EXISTS);
  117 + }
  118 +
  119 + private static WmsCheckOrderSaveReqVO createCheckOrderReqVO(WmsCheckOrderDetailSaveReqVO... details) {
  120 + WmsCheckOrderSaveReqVO reqVO = new WmsCheckOrderSaveReqVO();
  121 + reqVO.setWarehouseId(100L);
  122 + reqVO.setDetails(Arrays.asList(details));
  123 + return reqVO;
  124 + }
  125 +
  126 + private static WmsCheckOrderDetailSaveReqVO createCheckOrderDetailReqVO(Long id, Long skuId,
  127 + String quantity, String checkQuantity) {
  128 + WmsCheckOrderDetailSaveReqVO reqVO = new WmsCheckOrderDetailSaveReqVO();
  129 + reqVO.setId(id);
  130 + reqVO.setSkuId(skuId);
  131 + reqVO.setInventoryId(300L);
  132 + reqVO.setQuantity(new BigDecimal(quantity));
  133 + reqVO.setCheckQuantity(new BigDecimal(checkQuantity));
  134 + reqVO.setAmount(new BigDecimal("100.00"));
  135 + return reqVO;
  136 + }
  137 +
  138 + private static WmsCheckOrderDetailDO createCheckOrderDetail(Long orderId, Long skuId,
  139 + String quantity, String checkQuantity) {
  140 + return WmsCheckOrderDetailDO.builder()
  141 + .orderId(orderId)
  142 + .skuId(skuId)
  143 + .warehouseId(100L)
  144 + .inventoryId(300L)
  145 + .quantity(new BigDecimal(quantity))
  146 + .checkQuantity(new BigDecimal(checkQuantity))
  147 + .amount(new BigDecimal("100.00"))
  148 + .build();
  149 + }
  150 +
  151 +}
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.service.order.check;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO;
  5 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO;
  6 +import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderDetailMapper;
  7 +import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderMapper;
  8 +import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum;
  9 +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum;
  10 +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
  11 +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO;
  12 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
  13 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
  14 +import jakarta.annotation.Resource;
  15 +import org.junit.jupiter.api.Test;
  16 +import org.mockito.ArgumentCaptor;
  17 +import org.springframework.context.annotation.Import;
  18 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  19 +
  20 +import java.math.BigDecimal;
  21 +
  22 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  23 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_DETAIL_REQUIRED;
  24 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_STATUS_NOT_DELETABLE;
  25 +import static org.junit.jupiter.api.Assertions.assertEquals;
  26 +import static org.junit.jupiter.api.Assertions.assertNotNull;
  27 +import static org.junit.jupiter.api.Assertions.assertNull;
  28 +import static org.mockito.ArgumentMatchers.any;
  29 +import static org.mockito.Mockito.never;
  30 +import static org.mockito.Mockito.verify;
  31 +
  32 +@Import({WmsCheckOrderServiceImpl.class, WmsCheckOrderDetailServiceImpl.class})
  33 +public class WmsCheckOrderServiceImplTest extends BaseDbUnitTest {
  34 +
  35 + @Resource
  36 + private WmsCheckOrderServiceImpl checkOrderService;
  37 +
  38 + @Resource
  39 + private WmsCheckOrderMapper checkOrderMapper;
  40 + @Resource
  41 + private WmsCheckOrderDetailMapper checkOrderDetailMapper;
  42 +
  43 + @MockitoBean
  44 + private WmsWarehouseService warehouseService;
  45 + @MockitoBean
  46 + private WmsItemSkuService itemSkuService;
  47 + @MockitoBean
  48 + private WmsInventoryService inventoryService;
  49 +
  50 + @Test
  51 + public void testCompleteCheckOrder_success() {
  52 + // mock 数据
  53 + Long warehouseId = 100L;
  54 + Long skuId = 200L;
  55 + WmsCheckOrderDO order = createCheckOrder(warehouseId);
  56 + checkOrderMapper.insert(order);
  57 + checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), skuId, warehouseId,
  58 + "10.00", "7.00"));
  59 +
  60 + // 调用
  61 + checkOrderService.completeCheckOrder(order.getId());
  62 +
  63 + // 断言:盘库单
  64 + WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId());
  65 + assertNotNull(dbOrder);
  66 + assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus());
  67 + // 断言:库存变更
  68 + ArgumentCaptor<WmsInventoryChangeReqDTO> captor = ArgumentCaptor.forClass(WmsInventoryChangeReqDTO.class);
  69 + verify(inventoryService).changeInventory(captor.capture());
  70 + WmsInventoryChangeReqDTO inventoryReqDTO = captor.getValue();
  71 + assertEquals(order.getId(), inventoryReqDTO.getOrderId());
  72 + assertEquals(order.getNo(), inventoryReqDTO.getOrderNo());
  73 + assertEquals(WmsInventoryOrderTypeEnum.CHECK.getType(), inventoryReqDTO.getOrderType());
  74 + assertEquals(1, inventoryReqDTO.getItems().size());
  75 + assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId());
  76 + assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId());
  77 + assertEquals(0, new BigDecimal("-3.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity()));
  78 + }
  79 +
  80 + @Test
  81 + public void testCompleteCheckOrder_zeroDifference() {
  82 + // mock 数据
  83 + WmsCheckOrderDO order = createCheckOrder(100L);
  84 + checkOrderMapper.insert(order);
  85 + checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), 200L, 100L,
  86 + "10.00", "10.00"));
  87 +
  88 + // 调用
  89 + checkOrderService.completeCheckOrder(order.getId());
  90 +
  91 + // 断言
  92 + WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId());
  93 + assertNotNull(dbOrder);
  94 + assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus());
  95 + verify(inventoryService, never()).changeInventory(any());
  96 + }
  97 +
  98 + @Test
  99 + public void testCompleteCheckOrder_detailRequired() {
  100 + // mock 数据
  101 + WmsCheckOrderDO order = createCheckOrder(100L);
  102 + checkOrderMapper.insert(order);
  103 +
  104 + // 调用,并断言
  105 + assertServiceException(() -> checkOrderService.completeCheckOrder(order.getId()),
  106 + CHECK_ORDER_DETAIL_REQUIRED);
  107 + verify(inventoryService, never()).changeInventory(any());
  108 + }
  109 +
  110 + @Test
  111 + public void testCancelCheckOrder_success() {
  112 + // mock 数据
  113 + WmsCheckOrderDO order = createCheckOrder(100L);
  114 + checkOrderMapper.insert(order);
  115 +
  116 + // 调用
  117 + checkOrderService.cancelCheckOrder(order.getId());
  118 +
  119 + // 断言
  120 + WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId());
  121 + assertNotNull(dbOrder);
  122 + assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus());
  123 + verify(inventoryService, never()).changeInventory(any());
  124 + }
  125 +
  126 + @Test
  127 + public void testDeleteCheckOrder_canceled() {
  128 + // mock 数据
  129 + WmsCheckOrderDO order = createCheckOrder(100L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus());
  130 + checkOrderMapper.insert(order);
  131 + checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), 200L, 100L,
  132 + "10.00", "8.00"));
  133 +
  134 + // 调用
  135 + checkOrderService.deleteCheckOrder(order.getId());
  136 +
  137 + // 断言
  138 + assertNull(checkOrderMapper.selectById(order.getId()));
  139 + assertEquals(0, checkOrderDetailMapper.selectListByOrderId(order.getId()).size());
  140 + }
  141 +
  142 + @Test
  143 + public void testDeleteCheckOrder_finished() {
  144 + // mock 数据
  145 + WmsCheckOrderDO order = createCheckOrder(100L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus());
  146 + checkOrderMapper.insert(order);
  147 +
  148 + // 调用,并断言
  149 + assertServiceException(() -> checkOrderService.deleteCheckOrder(order.getId()),
  150 + CHECK_ORDER_STATUS_NOT_DELETABLE);
  151 + }
  152 +
  153 + private static WmsCheckOrderDO createCheckOrder(Long warehouseId) {
  154 + return new WmsCheckOrderDO()
  155 + .setNo("PK202605120001")
  156 + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus())
  157 + .setWarehouseId(warehouseId)
  158 + .setTotalQuantity(new BigDecimal("-3.00"))
  159 + .setTotalAmount(new BigDecimal("30.00"));
  160 + }
  161 +
  162 + private static WmsCheckOrderDetailDO createCheckOrderDetail(Long orderId, Long skuId, Long warehouseId,
  163 + String quantity, String checkQuantity) {
  164 + return WmsCheckOrderDetailDO.builder()
  165 + .orderId(orderId)
  166 + .skuId(skuId)
  167 + .warehouseId(warehouseId)
  168 + .inventoryId(300L)
  169 + .quantity(new BigDecimal(quantity))
  170 + .checkQuantity(new BigDecimal(checkQuantity))
  171 + .amount(new BigDecimal("30.00"))
  172 + .build();
  173 + }
  174 +
  175 +}
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.service.order.movement;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailSaveReqVO;
  5 +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO;
  6 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO;
  7 +import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderDetailMapper;
  8 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
  9 +import jakarta.annotation.Resource;
  10 +import org.junit.jupiter.api.Test;
  11 +import org.springframework.context.annotation.Import;
  12 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  13 +
  14 +import java.math.BigDecimal;
  15 +import java.util.Arrays;
  16 +import java.util.List;
  17 +
  18 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  19 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_DETAIL_NOT_EXISTS;
  20 +import static org.junit.jupiter.api.Assertions.assertEquals;
  21 +import static org.junit.jupiter.api.Assertions.assertNotNull;
  22 +import static org.junit.jupiter.api.Assertions.assertNull;
  23 +
  24 +@Import(WmsMovementOrderDetailServiceImpl.class)
  25 +public class WmsMovementOrderDetailServiceImplTest extends BaseDbUnitTest {
  26 +
  27 + @Resource
  28 + private WmsMovementOrderDetailServiceImpl movementOrderDetailService;
  29 +
  30 + @Resource
  31 + private WmsMovementOrderDetailMapper movementOrderDetailMapper;
  32 +
  33 + @MockitoBean
  34 + private WmsItemSkuService itemSkuService;
  35 +
  36 + @Test
  37 + public void testCreateMovementOrderDetailList_success() {
  38 + // mock 数据
  39 + Long orderId = 10L;
  40 + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(
  41 + createMovementOrderDetailReqVO(null, 1001L, "1.00"),
  42 + createMovementOrderDetailReqVO(null, 1002L, "2.00"));
  43 +
  44 + // 调用
  45 + movementOrderDetailService.createMovementOrderDetailList(orderId, reqVO);
  46 +
  47 + // 断言
  48 + List<WmsMovementOrderDetailDO> details = movementOrderDetailMapper.selectListByOrderId(orderId);
  49 + assertEquals(2, details.size());
  50 + assertEquals(orderId, details.get(0).getOrderId());
  51 + assertEquals(orderId, details.get(1).getOrderId());
  52 + assertEquals(100L, details.get(0).getSourceWarehouseId());
  53 + assertEquals(200L, details.get(0).getTargetWarehouseId());
  54 + }
  55 +
  56 + @Test
  57 + public void testCreateMovementOrderDetailList_ignoreId() {
  58 + // mock 数据
  59 + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(
  60 + createMovementOrderDetailReqVO(999L, 1001L, "1.00"));
  61 +
  62 + // 调用
  63 + movementOrderDetailService.createMovementOrderDetailList(10L, reqVO);
  64 +
  65 + // 断言
  66 + List<WmsMovementOrderDetailDO> details = movementOrderDetailMapper.selectListByOrderId(10L);
  67 + assertEquals(1, details.size());
  68 + assertNotNull(details.get(0).getId());
  69 + assertEquals(1001L, details.get(0).getSkuId());
  70 + }
  71 +
  72 + @Test
  73 + public void testUpdateMovementOrderDetailList_diff() {
  74 + // mock 数据
  75 + Long orderId = 10L;
  76 + WmsMovementOrderDetailDO detail01 = createMovementOrderDetail(orderId, 1001L, "1.00");
  77 + WmsMovementOrderDetailDO detail02 = createMovementOrderDetail(orderId, 1002L, "2.00");
  78 + movementOrderDetailMapper.insert(detail01);
  79 + movementOrderDetailMapper.insert(detail02);
  80 + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(
  81 + createMovementOrderDetailReqVO(detail01.getId(), 2001L, "11.00"),
  82 + createMovementOrderDetailReqVO(null, 2002L, "22.00"));
  83 +
  84 + // 调用
  85 + movementOrderDetailService.updateMovementOrderDetailList(orderId, reqVO);
  86 +
  87 + // 断言:修改
  88 + WmsMovementOrderDetailDO dbUpdateDetail = movementOrderDetailMapper.selectById(detail01.getId());
  89 + assertNotNull(dbUpdateDetail);
  90 + assertEquals(orderId, dbUpdateDetail.getOrderId());
  91 + assertEquals(2001L, dbUpdateDetail.getSkuId());
  92 + assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity()));
  93 + assertEquals(100L, dbUpdateDetail.getSourceWarehouseId());
  94 + assertEquals(200L, dbUpdateDetail.getTargetWarehouseId());
  95 + // 断言:新增
  96 + List<WmsMovementOrderDetailDO> details = movementOrderDetailMapper.selectListByOrderId(orderId);
  97 + assertEquals(2, details.size());
  98 + WmsMovementOrderDetailDO dbCreateDetail = details.stream()
  99 + .filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId()))
  100 + .findFirst().orElse(null);
  101 + assertNotNull(dbCreateDetail);
  102 + assertEquals(orderId, dbCreateDetail.getOrderId());
  103 + assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity()));
  104 + // 断言:删除
  105 + assertNull(movementOrderDetailMapper.selectById(detail02.getId()));
  106 + }
  107 +
  108 + @Test
  109 + public void testUpdateMovementOrderDetailList_detailNotExists() {
  110 + // mock 数据
  111 + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(
  112 + createMovementOrderDetailReqVO(999L, 1001L, "1.00"));
  113 +
  114 + // 调用,并断言
  115 + assertServiceException(() -> movementOrderDetailService.updateMovementOrderDetailList(10L, reqVO),
  116 + MOVEMENT_ORDER_DETAIL_NOT_EXISTS);
  117 + }
  118 +
  119 + private static WmsMovementOrderSaveReqVO createMovementOrderReqVO(WmsMovementOrderDetailSaveReqVO... details) {
  120 + WmsMovementOrderSaveReqVO reqVO = new WmsMovementOrderSaveReqVO();
  121 + reqVO.setSourceWarehouseId(100L);
  122 + reqVO.setTargetWarehouseId(200L);
  123 + reqVO.setDetails(Arrays.asList(details));
  124 + return reqVO;
  125 + }
  126 +
  127 + private static WmsMovementOrderDetailSaveReqVO createMovementOrderDetailReqVO(Long id, Long skuId, String quantity) {
  128 + WmsMovementOrderDetailSaveReqVO reqVO = new WmsMovementOrderDetailSaveReqVO();
  129 + reqVO.setId(id);
  130 + reqVO.setSkuId(skuId);
  131 + reqVO.setQuantity(new BigDecimal(quantity));
  132 + reqVO.setAmount(new BigDecimal("100.00"));
  133 + return reqVO;
  134 + }
  135 +
  136 + private static WmsMovementOrderDetailDO createMovementOrderDetail(Long orderId, Long skuId, String quantity) {
  137 + return WmsMovementOrderDetailDO.builder()
  138 + .orderId(orderId)
  139 + .skuId(skuId)
  140 + .sourceWarehouseId(100L)
  141 + .targetWarehouseId(200L)
  142 + .quantity(new BigDecimal(quantity))
  143 + .amount(new BigDecimal("100.00"))
  144 + .build();
  145 + }
  146 +
  147 +}
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.service.order.movement;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO;
  5 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO;
  6 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO;
  7 +import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderDetailMapper;
  8 +import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderMapper;
  9 +import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum;
  10 +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum;
  11 +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
  12 +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO;
  13 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
  14 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
  15 +import jakarta.annotation.Resource;
  16 +import org.junit.jupiter.api.Test;
  17 +import org.mockito.ArgumentCaptor;
  18 +import org.springframework.context.annotation.Import;
  19 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  20 +
  21 +import java.math.BigDecimal;
  22 +
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  24 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_DETAIL_REQUIRED;
  25 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_STATUS_NOT_DELETABLE;
  26 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_WAREHOUSE_SAME;
  27 +import static org.junit.jupiter.api.Assertions.assertEquals;
  28 +import static org.junit.jupiter.api.Assertions.assertNotNull;
  29 +import static org.junit.jupiter.api.Assertions.assertNull;
  30 +import static org.mockito.ArgumentMatchers.any;
  31 +import static org.mockito.Mockito.never;
  32 +import static org.mockito.Mockito.verify;
  33 +
  34 +@Import({WmsMovementOrderServiceImpl.class, WmsMovementOrderDetailServiceImpl.class})
  35 +public class WmsMovementOrderServiceImplTest extends BaseDbUnitTest {
  36 +
  37 + @Resource
  38 + private WmsMovementOrderServiceImpl movementOrderService;
  39 +
  40 + @Resource
  41 + private WmsMovementOrderMapper movementOrderMapper;
  42 + @Resource
  43 + private WmsMovementOrderDetailMapper movementOrderDetailMapper;
  44 +
  45 + @MockitoBean
  46 + private WmsWarehouseService warehouseService;
  47 + @MockitoBean
  48 + private WmsItemSkuService itemSkuService;
  49 + @MockitoBean
  50 + private WmsInventoryService inventoryService;
  51 +
  52 + @Test
  53 + public void testCompleteMovementOrder_success() {
  54 + // mock 数据
  55 + Long sourceWarehouseId = 100L;
  56 + Long targetWarehouseId = 200L;
  57 + Long skuId = 300L;
  58 + WmsMovementOrderDO order = createMovementOrder(sourceWarehouseId, targetWarehouseId);
  59 + movementOrderMapper.insert(order);
  60 + movementOrderDetailMapper.insert(createMovementOrderDetail(order.getId(), skuId,
  61 + sourceWarehouseId, targetWarehouseId));
  62 +
  63 + // 调用
  64 + movementOrderService.completeMovementOrder(order.getId());
  65 +
  66 + // 断言:移库单
  67 + WmsMovementOrderDO dbOrder = movementOrderMapper.selectById(order.getId());
  68 + assertNotNull(dbOrder);
  69 + assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus());
  70 + // 断言:库存变更
  71 + ArgumentCaptor<WmsInventoryChangeReqDTO> captor = ArgumentCaptor.forClass(WmsInventoryChangeReqDTO.class);
  72 + verify(inventoryService).changeInventory(captor.capture());
  73 + WmsInventoryChangeReqDTO inventoryReqDTO = captor.getValue();
  74 + assertEquals(order.getId(), inventoryReqDTO.getOrderId());
  75 + assertEquals(order.getNo(), inventoryReqDTO.getOrderNo());
  76 + assertEquals(WmsInventoryOrderTypeEnum.MOVEMENT.getType(), inventoryReqDTO.getOrderType());
  77 + assertEquals(2, inventoryReqDTO.getItems().size());
  78 + assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId());
  79 + assertEquals(sourceWarehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId());
  80 + assertEquals(0, new BigDecimal("-2.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity()));
  81 + assertEquals(skuId, inventoryReqDTO.getItems().get(1).getSkuId());
  82 + assertEquals(targetWarehouseId, inventoryReqDTO.getItems().get(1).getWarehouseId());
  83 + assertEquals(0, new BigDecimal("2.00").compareTo(inventoryReqDTO.getItems().get(1).getQuantity()));
  84 + }
  85 +
  86 + @Test
  87 + public void testCompleteMovementOrder_detailRequired() {
  88 + // mock 数据
  89 + WmsMovementOrderDO order = createMovementOrder(100L, 200L);
  90 + movementOrderMapper.insert(order);
  91 +
  92 + // 调用,并断言
  93 + assertServiceException(() -> movementOrderService.completeMovementOrder(order.getId()),
  94 + MOVEMENT_ORDER_DETAIL_REQUIRED);
  95 + verify(inventoryService, never()).changeInventory(any());
  96 + }
  97 +
  98 + @Test
  99 + public void testCreateMovementOrder_sameWarehouse() {
  100 + // 准备参数
  101 + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(100L, 100L);
  102 +
  103 + // 调用,并断言
  104 + assertServiceException(() -> movementOrderService.createMovementOrder(reqVO), MOVEMENT_ORDER_WAREHOUSE_SAME);
  105 + verify(inventoryService, never()).changeInventory(any());
  106 + }
  107 +
  108 + @Test
  109 + public void testCancelMovementOrder_success() {
  110 + // mock 数据
  111 + WmsMovementOrderDO order = createMovementOrder(100L, 200L);
  112 + movementOrderMapper.insert(order);
  113 +
  114 + // 调用
  115 + movementOrderService.cancelMovementOrder(order.getId());
  116 +
  117 + // 断言
  118 + WmsMovementOrderDO dbOrder = movementOrderMapper.selectById(order.getId());
  119 + assertNotNull(dbOrder);
  120 + assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus());
  121 + verify(inventoryService, never()).changeInventory(any());
  122 + }
  123 +
  124 + @Test
  125 + public void testDeleteMovementOrder_canceled() {
  126 + // mock 数据
  127 + WmsMovementOrderDO order = createMovementOrder(100L, 200L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus());
  128 + movementOrderMapper.insert(order);
  129 + movementOrderDetailMapper.insert(createMovementOrderDetail(order.getId(), 300L, 100L, 200L));
  130 +
  131 + // 调用
  132 + movementOrderService.deleteMovementOrder(order.getId());
  133 +
  134 + // 断言
  135 + assertNull(movementOrderMapper.selectById(order.getId()));
  136 + assertEquals(0, movementOrderDetailMapper.selectListByOrderId(order.getId()).size());
  137 + }
  138 +
  139 + @Test
  140 + public void testDeleteMovementOrder_finished() {
  141 + // mock 数据
  142 + WmsMovementOrderDO order = createMovementOrder(100L, 200L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus());
  143 + movementOrderMapper.insert(order);
  144 +
  145 + // 调用,并断言
  146 + assertServiceException(() -> movementOrderService.deleteMovementOrder(order.getId()),
  147 + MOVEMENT_ORDER_STATUS_NOT_DELETABLE);
  148 + }
  149 +
  150 + private static WmsMovementOrderDO createMovementOrder(Long sourceWarehouseId, Long targetWarehouseId) {
  151 + return new WmsMovementOrderDO()
  152 + .setNo("YK202605120001")
  153 + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus())
  154 + .setSourceWarehouseId(sourceWarehouseId)
  155 + .setTargetWarehouseId(targetWarehouseId)
  156 + .setTotalQuantity(new BigDecimal("2.00"))
  157 + .setTotalAmount(new BigDecimal("20.00"));
  158 + }
  159 +
  160 + private static WmsMovementOrderDetailDO createMovementOrderDetail(Long orderId, Long skuId,
  161 + Long sourceWarehouseId, Long targetWarehouseId) {
  162 + return WmsMovementOrderDetailDO.builder()
  163 + .orderId(orderId)
  164 + .skuId(skuId)
  165 + .sourceWarehouseId(sourceWarehouseId)
  166 + .targetWarehouseId(targetWarehouseId)
  167 + .quantity(new BigDecimal("2.00"))
  168 + .amount(new BigDecimal("20.00"))
  169 + .build();
  170 + }
  171 +
  172 + private static WmsMovementOrderSaveReqVO createMovementOrderReqVO(Long sourceWarehouseId, Long targetWarehouseId) {
  173 + WmsMovementOrderSaveReqVO reqVO = new WmsMovementOrderSaveReqVO();
  174 + reqVO.setNo("YK202605120001");
  175 + reqVO.setSourceWarehouseId(sourceWarehouseId);
  176 + reqVO.setTargetWarehouseId(targetWarehouseId);
  177 + reqVO.setTotalQuantity(new BigDecimal("2.00"));
  178 + reqVO.setTotalAmount(new BigDecimal("20.00"));
  179 + return reqVO;
  180 + }
  181 +
  182 +}
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.service.order.shipment;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailSaveReqVO;
  5 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO;
  6 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO;
  7 +import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderDetailMapper;
  8 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
  9 +import jakarta.annotation.Resource;
  10 +import org.junit.jupiter.api.Test;
  11 +import org.springframework.context.annotation.Import;
  12 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  13 +
  14 +import java.math.BigDecimal;
  15 +import java.util.Arrays;
  16 +import java.util.List;
  17 +
  18 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  19 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_DETAIL_NOT_EXISTS;
  20 +import static org.junit.jupiter.api.Assertions.assertEquals;
  21 +import static org.junit.jupiter.api.Assertions.assertNotNull;
  22 +import static org.junit.jupiter.api.Assertions.assertNull;
  23 +
  24 +@Import(WmsShipmentOrderDetailServiceImpl.class)
  25 +public class WmsShipmentOrderDetailServiceImplTest extends BaseDbUnitTest {
  26 +
  27 + @Resource
  28 + private WmsShipmentOrderDetailServiceImpl shipmentOrderDetailService;
  29 +
  30 + @Resource
  31 + private WmsShipmentOrderDetailMapper shipmentOrderDetailMapper;
  32 +
  33 + @MockitoBean
  34 + private WmsItemSkuService itemSkuService;
  35 +
  36 + @Test
  37 + public void testCreateShipmentOrderDetailList_success() {
  38 + // mock 数据
  39 + Long orderId = 10L;
  40 + WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO(
  41 + createShipmentOrderDetailReqVO(null, 1001L, "1.00"),
  42 + createShipmentOrderDetailReqVO(null, 1002L, "2.00"));
  43 +
  44 + // 调用
  45 + shipmentOrderDetailService.createShipmentOrderDetailList(orderId, reqVO);
  46 +
  47 + // 断言
  48 + List<WmsShipmentOrderDetailDO> details = shipmentOrderDetailMapper.selectListByOrderId(orderId);
  49 + assertEquals(2, details.size());
  50 + assertEquals(orderId, details.get(0).getOrderId());
  51 + assertEquals(orderId, details.get(1).getOrderId());
  52 + assertEquals(100L, details.get(0).getWarehouseId());
  53 + }
  54 +
  55 + @Test
  56 + public void testCreateShipmentOrderDetailList_ignoreId() {
  57 + // mock 数据
  58 + WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO(
  59 + createShipmentOrderDetailReqVO(999L, 1001L, "1.00"));
  60 +
  61 + // 调用
  62 + shipmentOrderDetailService.createShipmentOrderDetailList(10L, reqVO);
  63 +
  64 + // 断言
  65 + List<WmsShipmentOrderDetailDO> details = shipmentOrderDetailMapper.selectListByOrderId(10L);
  66 + assertEquals(1, details.size());
  67 + assertNotNull(details.get(0).getId());
  68 + assertEquals(1001L, details.get(0).getSkuId());
  69 + }
  70 +
  71 + @Test
  72 + public void testUpdateShipmentOrderDetailList_diff() {
  73 + // mock 数据
  74 + Long orderId = 10L;
  75 + WmsShipmentOrderDetailDO detail01 = createShipmentOrderDetail(orderId, 1001L, "1.00");
  76 + WmsShipmentOrderDetailDO detail02 = createShipmentOrderDetail(orderId, 1002L, "2.00");
  77 + shipmentOrderDetailMapper.insert(detail01);
  78 + shipmentOrderDetailMapper.insert(detail02);
  79 + WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO(
  80 + createShipmentOrderDetailReqVO(detail01.getId(), 2001L, "11.00"),
  81 + createShipmentOrderDetailReqVO(null, 2002L, "22.00"));
  82 +
  83 + // 调用
  84 + shipmentOrderDetailService.updateShipmentOrderDetailList(orderId, reqVO);
  85 +
  86 + // 断言:修改
  87 + WmsShipmentOrderDetailDO dbUpdateDetail = shipmentOrderDetailMapper.selectById(detail01.getId());
  88 + assertNotNull(dbUpdateDetail);
  89 + assertEquals(orderId, dbUpdateDetail.getOrderId());
  90 + assertEquals(2001L, dbUpdateDetail.getSkuId());
  91 + assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity()));
  92 + // 断言:新增
  93 + List<WmsShipmentOrderDetailDO> details = shipmentOrderDetailMapper.selectListByOrderId(orderId);
  94 + assertEquals(2, details.size());
  95 + WmsShipmentOrderDetailDO dbCreateDetail = details.stream()
  96 + .filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId()))
  97 + .findFirst().orElse(null);
  98 + assertNotNull(dbCreateDetail);
  99 + assertEquals(orderId, dbCreateDetail.getOrderId());
  100 + assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity()));
  101 + // 断言:删除
  102 + assertNull(shipmentOrderDetailMapper.selectById(detail02.getId()));
  103 + }
  104 +
  105 + @Test
  106 + public void testUpdateShipmentOrderDetailList_detailNotExists() {
  107 + // mock 数据
  108 + WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO(
  109 + createShipmentOrderDetailReqVO(999L, 1001L, "1.00"));
  110 +
  111 + // 调用,并断言
  112 + assertServiceException(() -> shipmentOrderDetailService.updateShipmentOrderDetailList(10L, reqVO),
  113 + SHIPMENT_ORDER_DETAIL_NOT_EXISTS);
  114 + }
  115 +
  116 + private static WmsShipmentOrderSaveReqVO createShipmentOrderReqVO(WmsShipmentOrderDetailSaveReqVO... details) {
  117 + WmsShipmentOrderSaveReqVO reqVO = new WmsShipmentOrderSaveReqVO();
  118 + reqVO.setWarehouseId(100L);
  119 + reqVO.setDetails(Arrays.asList(details));
  120 + return reqVO;
  121 + }
  122 +
  123 + private static WmsShipmentOrderDetailSaveReqVO createShipmentOrderDetailReqVO(Long id, Long skuId, String quantity) {
  124 + WmsShipmentOrderDetailSaveReqVO reqVO = new WmsShipmentOrderDetailSaveReqVO();
  125 + reqVO.setId(id);
  126 + reqVO.setSkuId(skuId);
  127 + reqVO.setQuantity(new BigDecimal(quantity));
  128 + reqVO.setAmount(new BigDecimal("100.00"));
  129 + return reqVO;
  130 + }
  131 +
  132 + private static WmsShipmentOrderDetailDO createShipmentOrderDetail(Long orderId, Long skuId, String quantity) {
  133 + return WmsShipmentOrderDetailDO.builder()
  134 + .orderId(orderId)
  135 + .skuId(skuId)
  136 + .warehouseId(100L)
  137 + .quantity(new BigDecimal(quantity))
  138 + .amount(new BigDecimal("100.00"))
  139 + .build();
  140 + }
  141 +
  142 +}
yudao-module-wms/src/test/resources/sql/clean.sql
  1 +DELETE FROM "wms_check_order_detail";
  2 +DELETE FROM "wms_check_order";
  3 +DELETE FROM "wms_movement_order_detail";
  4 +DELETE FROM "wms_movement_order";
1 DELETE FROM "wms_shipment_order_detail"; 5 DELETE FROM "wms_shipment_order_detail";
2 DELETE FROM "wms_shipment_order"; 6 DELETE FROM "wms_shipment_order";
3 DELETE FROM "wms_receipt_order_detail"; 7 DELETE FROM "wms_receipt_order_detail";
yudao-module-wms/src/test/resources/sql/create_tables.sql
@@ -60,9 +60,6 @@ CREATE TABLE IF NOT EXISTS &quot;wms_inventory_history&quot; ( @@ -60,9 +60,6 @@ CREATE TABLE IF NOT EXISTS &quot;wms_inventory_history&quot; (
60 "quantity" decimal(24, 6) NOT NULL DEFAULT '0', 60 "quantity" decimal(24, 6) NOT NULL DEFAULT '0',
61 "before_quantity" decimal(24, 6) DEFAULT NULL, 61 "before_quantity" decimal(24, 6) DEFAULT NULL,
62 "after_quantity" decimal(24, 6) DEFAULT NULL, 62 "after_quantity" decimal(24, 6) DEFAULT NULL,
63 - "batch_no" varchar(64) DEFAULT NULL,  
64 - "production_date" timestamp DEFAULT NULL,  
65 - "expiration_date" timestamp DEFAULT NULL,  
66 "amount" decimal(24, 6) DEFAULT NULL, 63 "amount" decimal(24, 6) DEFAULT NULL,
67 "remark" varchar(255) DEFAULT NULL, 64 "remark" varchar(255) DEFAULT NULL,
68 "order_id" bigint DEFAULT NULL, 65 "order_id" bigint DEFAULT NULL,
@@ -103,9 +100,6 @@ CREATE TABLE IF NOT EXISTS &quot;wms_receipt_order_detail&quot; ( @@ -103,9 +100,6 @@ CREATE TABLE IF NOT EXISTS &quot;wms_receipt_order_detail&quot; (
103 "order_id" bigint NOT NULL, 100 "order_id" bigint NOT NULL,
104 "sku_id" bigint NOT NULL, 101 "sku_id" bigint NOT NULL,
105 "warehouse_id" bigint NOT NULL, 102 "warehouse_id" bigint NOT NULL,
106 - "batch_no" varchar(64) DEFAULT NULL,  
107 - "production_date" timestamp DEFAULT NULL,  
108 - "expiration_date" timestamp DEFAULT NULL,  
109 "quantity" decimal(20, 2) NOT NULL DEFAULT '0', 103 "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
110 "amount" decimal(10, 2) DEFAULT NULL, 104 "amount" decimal(10, 2) DEFAULT NULL,
111 "creator" varchar(64) DEFAULT '', 105 "creator" varchar(64) DEFAULT '',
@@ -143,9 +137,6 @@ CREATE TABLE IF NOT EXISTS &quot;wms_shipment_order_detail&quot; ( @@ -143,9 +137,6 @@ CREATE TABLE IF NOT EXISTS &quot;wms_shipment_order_detail&quot; (
143 "order_id" bigint NOT NULL, 137 "order_id" bigint NOT NULL,
144 "sku_id" bigint NOT NULL, 138 "sku_id" bigint NOT NULL,
145 "warehouse_id" bigint NOT NULL, 139 "warehouse_id" bigint NOT NULL,
146 - "batch_no" varchar(64) DEFAULT NULL,  
147 - "production_date" timestamp DEFAULT NULL,  
148 - "expiration_date" timestamp DEFAULT NULL,  
149 "quantity" decimal(20, 2) NOT NULL DEFAULT '0', 140 "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
150 "amount" decimal(10, 2) DEFAULT NULL, 141 "amount" decimal(10, 2) DEFAULT NULL,
151 "creator" varchar(64) DEFAULT '', 142 "creator" varchar(64) DEFAULT '',
@@ -156,3 +147,76 @@ CREATE TABLE IF NOT EXISTS &quot;wms_shipment_order_detail&quot; ( @@ -156,3 +147,76 @@ CREATE TABLE IF NOT EXISTS &quot;wms_shipment_order_detail&quot; (
156 "tenant_id" bigint NOT NULL DEFAULT '0', 147 "tenant_id" bigint NOT NULL DEFAULT '0',
157 PRIMARY KEY ("id") 148 PRIMARY KEY ("id")
158 ) COMMENT 'WMS 出库单明细表'; 149 ) COMMENT 'WMS 出库单明细表';
  150 +
  151 +CREATE TABLE IF NOT EXISTS "wms_movement_order" (
  152 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  153 + "no" varchar(64) NOT NULL,
  154 + "status" int NOT NULL DEFAULT '0',
  155 + "remark" varchar(255) DEFAULT NULL,
  156 + "source_warehouse_id" bigint NOT NULL,
  157 + "target_warehouse_id" bigint NOT NULL,
  158 + "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
  159 + "total_amount" decimal(10, 2) DEFAULT NULL,
  160 + "creator" varchar(64) DEFAULT '',
  161 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  162 + "updater" varchar(64) DEFAULT '',
  163 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  164 + "deleted" bit NOT NULL DEFAULT FALSE,
  165 + "tenant_id" bigint NOT NULL DEFAULT '0',
  166 + PRIMARY KEY ("id")
  167 +) COMMENT 'WMS 移库单表';
  168 +
  169 +CREATE TABLE IF NOT EXISTS "wms_movement_order_detail" (
  170 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  171 + "remark" varchar(255) DEFAULT NULL,
  172 + "order_id" bigint NOT NULL,
  173 + "sku_id" bigint NOT NULL,
  174 + "source_warehouse_id" bigint NOT NULL,
  175 + "target_warehouse_id" bigint NOT NULL,
  176 + "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
  177 + "amount" decimal(10, 2) DEFAULT NULL,
  178 + "creator" varchar(64) DEFAULT '',
  179 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  180 + "updater" varchar(64) DEFAULT '',
  181 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  182 + "deleted" bit NOT NULL DEFAULT FALSE,
  183 + "tenant_id" bigint NOT NULL DEFAULT '0',
  184 + PRIMARY KEY ("id")
  185 +) COMMENT 'WMS 移库单明细表';
  186 +
  187 +CREATE TABLE IF NOT EXISTS "wms_check_order" (
  188 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  189 + "no" varchar(64) NOT NULL,
  190 + "status" int NOT NULL DEFAULT '0',
  191 + "remark" varchar(255) DEFAULT NULL,
  192 + "warehouse_id" bigint NOT NULL,
  193 + "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
  194 + "total_amount" decimal(10, 2) DEFAULT NULL,
  195 + "creator" varchar(64) DEFAULT '',
  196 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  197 + "updater" varchar(64) DEFAULT '',
  198 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  199 + "deleted" bit NOT NULL DEFAULT FALSE,
  200 + "tenant_id" bigint NOT NULL DEFAULT '0',
  201 + PRIMARY KEY ("id")
  202 +) COMMENT 'WMS 盘库单表';
  203 +
  204 +CREATE TABLE IF NOT EXISTS "wms_check_order_detail" (
  205 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  206 + "remark" varchar(255) DEFAULT NULL,
  207 + "order_id" bigint NOT NULL,
  208 + "sku_id" bigint NOT NULL,
  209 + "warehouse_id" bigint NOT NULL,
  210 + "inventory_id" bigint DEFAULT NULL,
  211 + "receipt_time" timestamp DEFAULT NULL,
  212 + "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
  213 + "check_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
  214 + "amount" decimal(10, 2) DEFAULT NULL,
  215 + "creator" varchar(64) DEFAULT '',
  216 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  217 + "updater" varchar(64) DEFAULT '',
  218 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  219 + "deleted" bit NOT NULL DEFAULT FALSE,
  220 + "tenant_id" bigint NOT NULL DEFAULT '0',
  221 + PRIMARY KEY ("id")
  222 +) COMMENT 'WMS 盘库单明细表';