diff --git a/.image/common/ruoyi-vue-pro-biz.png b/.image/common/ruoyi-vue-pro-biz.png index 42dc849b5..355491963 100644 Binary files a/.image/common/ruoyi-vue-pro-biz.png and b/.image/common/ruoyi-vue-pro-biz.png differ diff --git a/.image/common/wms-feature.png b/.image/common/wms-feature.png new file mode 100644 index 000000000..0d7790b18 Binary files /dev/null and b/.image/common/wms-feature.png differ diff --git a/.image/common/wms-preview.png b/.image/common/wms-preview.png new file mode 100644 index 000000000..31c5187d9 Binary files /dev/null and b/.image/common/wms-preview.png differ diff --git a/README.md b/README.md index 65d5aafdc..de82d9613 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ | 【完整版】[yudao-cloud](https://gitee.com/zhijiantianya/yudao-cloud) | [`master`](https://gitee.com/zhijiantianya/yudao-cloud/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/zhijiantianya/yudao-cloud/tree/master-jdk17/) 分支 | | 【精简版】[yudao-cloud-mini](https://gitee.com/yudaocode/yudao-cloud-mini) | [`master`](https://gitee.com/yudaocode/yudao-cloud-mini/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/yudaocode/yudao-cloud-mini/tree/master-jdk17/) 分支 | -* 【完整版】:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、MES、AI 大模型、IoT 物联网 等功能 -* 【精简版】:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、MES、AI 大模型、IoT 物联网 等功能 +* 【完整版】:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、WMS、MES、AI 大模型、IoT 物联网 等功能 +* 【精简版】:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、WMS、MES、AI 大模型、IoT 物联网 等功能 可参考 [《迁移文档》](https://cloud.iocoder.cn/migrate-module/) ,只需要 5-10 分钟,即可将【完整版】按需迁移到【精简版】 @@ -115,7 +115,7 @@ * 通用模块(必选):系统功能、基础设施 * 通用模块(可选):工作流程、支付系统、数据报表、会员中心 -* 业务系统(按需):ERP 系统、CRM 系统、MES 系统、商城系统、微信公众号、AI 大模型、IoT 物联网 +* 业务系统(按需):Mall 电子商城、OA 办公自动化、ERP 企业资源计划系统、WMS 仓库管理系统、CRM 客户关系管理、CMS 内容管理系统、MES 执行制造系统、AI 大模型平台、IoT 物联网系统、IM 即时通讯系统、Mobile 手机移动端、Report 数据大屏 > 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。 > @@ -273,6 +273,14 @@ ![功能图](/.image/common/erp-feature.png) +### WMS 系统 + +演示地址: + +![功能图](/.image/common/wms-feature.png) + +![功能图](/.image/common/wms-preview.png) + ### CRM 系统 演示地址: @@ -321,6 +329,7 @@ | `yudao-module-erp` | ERP 系统的 Module 模块 | | `yudao-module-crm` | CRM 系统的 Module 模块 | | `yudao-module-mes` | MES 系统的 Module 模块 | +| `yudao-module-wms` | WMS 系统的 Module 模块 | | `yudao-module-ai` | AI 大模型的 Module 模块 | | `yudao-module-iot` | IoT 物联网的 Module 模块 | | `yudao-module-mp` | 微信公众号的 Module 模块 | diff --git a/pom.xml b/pom.xml index 6cb303544..db439782e 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ yudao-module-crm yudao-module-iot yudao-module-mes + yudao-module-wms diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java index 6c01b9497..517fd603e 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java @@ -37,8 +37,14 @@ public class BannerApplicationRunner implements ApplicationRunner { System.out.println("[商城系统 yudao-module-mall 教程][参考 https://cloud.iocoder.cn/mall/build/ 开启]"); // ERP 系统 System.out.println("[ERP 系统 yudao-module-erp - 教程][参考 https://cloud.iocoder.cn/erp/build/ 开启]"); + // WMS 仓库管理系统 + System.out.println("[WMS 仓库管理系统 yudao-module-wms - 教程][参考 https://cloud.iocoder.cn/wms/build/ 开启]"); // CRM 系统 System.out.println("[CRM 系统 yudao-module-crm - 教程][参考 https://cloud.iocoder.cn/crm/build/ 开启]"); + // MES 系统 + System.out.println("[MES 系统 yudao-module-mes - 教程][参考 https://cloud.iocoder.cn/mes/build/ 开启]"); + // IM 即时通讯 + System.out.println("[IM 即时通讯 yudao-module-im - 教程][参考 https://cloud.iocoder.cn/im/build/ 开启]"); // 微信公众号 System.out.println("[微信公众号 yudao-module-mp 教程][参考 https://cloud.iocoder.cn/mp/build/ 开启]"); // 支付平台 diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java index 156d7e04a..33cb31e5b 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java @@ -410,25 +410,43 @@ public class GlobalExceptionHandler { return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]"); } - // 6. CRM 系统 + // 6. WMS 仓库管理系统 + if (message.contains("wms_")) { + log.error("[WMS 仓库管理系统 yudao-module-wms - 表结构未导入][参考 https://cloud.iocoder.cn/wms/build/ 开启]"); + return CommonResult.error(NOT_IMPLEMENTED.getCode(), + "[WMS 仓库管理系统 yudao-module-wms - 表结构未导入][参考 https://cloud.iocoder.cn/wms/build/ 开启]"); + } + // 7. CRM 系统 if (message.contains("crm_")) { log.error("[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]"); } - // 7. 支付平台 + // 8. MES 系统 + if (message.contains("mes_")) { + log.error("[MES 系统 yudao-module-mes - 表结构未导入][参考 https://cloud.iocoder.cn/mes/build/ 开启]"); + return CommonResult.error(NOT_IMPLEMENTED.getCode(), + "[MES 系统 yudao-module-mes - 表结构未导入][参考 https://cloud.iocoder.cn/mes/build/ 开启]"); + } + // 9. IM 即时通讯 + if (message.contains("im_")) { + log.error("[IM 即时通讯 yudao-module-im - 表结构未导入][参考 https://cloud.iocoder.cn/im/build/ 开启]"); + return CommonResult.error(NOT_IMPLEMENTED.getCode(), + "[IM 即时通讯 yudao-module-im - 表结构未导入][参考 https://cloud.iocoder.cn/im/build/ 开启]"); + } + // 10. 支付平台 if (message.contains("pay_")) { log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]"); } - // 8. AI 大模型 + // 11. AI 大模型 if (message.contains("ai_")) { log.error("[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]"); } - // 9. IoT 物联网 + // 12. IoT 物联网 if (message.contains("iot_")) { log.error("[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/BannerApplicationRunner.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/BannerApplicationRunner.java index 3933ef262..a9ecd0bdc 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/BannerApplicationRunner.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/BannerApplicationRunner.java @@ -39,8 +39,14 @@ public class BannerApplicationRunner implements ApplicationRunner { System.out.println("[商城系统 yudao-module-mall 教程][参考 https://cloud.iocoder.cn/mall/build/ 开启]"); // ERP 系统 System.out.println("[ERP 系统 yudao-module-erp - 教程][参考 https://cloud.iocoder.cn/erp/build/ 开启]"); + // WMS 仓库管理系统 + System.out.println("[WMS 仓库管理系统 yudao-module-wms - 教程][参考 https://cloud.iocoder.cn/wms/build/ 开启]"); // CRM 系统 System.out.println("[CRM 系统 yudao-module-crm - 教程][参考 https://cloud.iocoder.cn/crm/build/ 开启]"); + // MES 系统 + System.out.println("[MES 系统 yudao-module-mes - 教程][参考 https://cloud.iocoder.cn/mes/build/ 开启]"); + // IM 即时通讯 + System.out.println("[IM 即时通讯 yudao-module-im - 教程][参考 https://cloud.iocoder.cn/im/build/ 开启]"); // 微信公众号 System.out.println("[微信公众号 yudao-module-mp 教程][参考 https://cloud.iocoder.cn/mp/build/ 开启]"); // 支付平台 diff --git a/yudao-module-wms/pom.xml b/yudao-module-wms/pom.xml new file mode 100644 index 000000000..6e3ee5fcb --- /dev/null +++ b/yudao-module-wms/pom.xml @@ -0,0 +1,24 @@ + + + + cn.iocoder.cloud + yudao + ${revision} + + + yudao-module-wms-api + yudao-module-wms-server + + 4.0.0 + yudao-module-wms + pom + + ${project.artifactId} + + wms 模块下,仓库管理系统(Warehouse Management System)。 + 例如说:仓库、物料、库存、入库、出库、移库、盘库等等 + + + diff --git a/yudao-module-wms/yudao-module-wms-api/pom.xml b/yudao-module-wms/yudao-module-wms-api/pom.xml new file mode 100644 index 000000000..91f1914ff --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-api/pom.xml @@ -0,0 +1,33 @@ + + + + cn.iocoder.cloud + yudao-module-wms + ${revision} + + 4.0.0 + yudao-module-wms-api + jar + + ${project.artifactId} + + wms 模块 API,暴露给其它模块调用 + + + + + cn.iocoder.cloud + yudao-common + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + + diff --git a/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/ApiConstants.java b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/ApiConstants.java new file mode 100644 index 000000000..c374a4f8d --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/ApiConstants.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.wms.enums; + +import cn.iocoder.yudao.framework.common.enums.RpcConstants; + +/** + * API 相关的枚举 + * + * @author 芋道源码 + */ +public class ApiConstants { + + /** + * 服务名 + * + * 注意,需要保证和 spring.application.name 保持一致 + */ + public static final String NAME = "wms-server"; + + public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/wms"; + + public static final String VERSION = "1.0.0"; + +} diff --git a/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/DictTypeConstants.java b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/DictTypeConstants.java new file mode 100644 index 000000000..e854bc530 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/DictTypeConstants.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.wms.enums; + +/** + * WMS 字典类型常量 + * + * @author 芋道源码 + */ +public interface DictTypeConstants { + + String MERCHANT_TYPE = "merchant_type"; + String ORDER_STATUS = "wms_order_status"; + String ORDER_TYPE = "wms_order_type"; + String RECEIPT_ORDER_TYPE = "wms_receipt_order_type"; + String SHIPMENT_ORDER_TYPE = "wms_shipment_order_type"; + +} diff --git a/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java new file mode 100644 index 000000000..217562741 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java @@ -0,0 +1,91 @@ +package cn.iocoder.yudao.module.wms.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * WMS 错误码枚举类 + *

+ * wms 系统,使用 1-060-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== WMS 基础数据-仓库 1-060-100-000 ========== + ErrorCode WAREHOUSE_NOT_EXISTS = new ErrorCode(1_060_100_000, "仓库不存在"); + ErrorCode WAREHOUSE_NAME_DUPLICATE = new ErrorCode(1_060_100_001, "仓库名称重复"); + ErrorCode WAREHOUSE_CODE_DUPLICATE = new ErrorCode(1_060_100_002, "仓库编号重复"); + ErrorCode WAREHOUSE_HAS_ORDER = new ErrorCode(1_060_100_004, "删除失败!仓库已被{}使用!"); + ErrorCode WAREHOUSE_HAS_INVENTORY = new ErrorCode(1_060_100_005, "删除失败!仓库已存在库存余额!"); + + // ========== WMS 基础数据-商品分类 1-060-102-000 ========== + ErrorCode ITEM_CATEGORY_NOT_EXISTS = new ErrorCode(1_060_102_000, "商品分类不存在"); + ErrorCode ITEM_CATEGORY_NAME_DUPLICATE = new ErrorCode(1_060_102_001, "商品分类名称重复"); + ErrorCode ITEM_CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_060_102_002, "父商品分类不存在"); + ErrorCode ITEM_CATEGORY_PARENT_ERROR = new ErrorCode(1_060_102_003, "不能设置自己为父商品分类"); + ErrorCode ITEM_CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_060_102_004, "不能设置自己的子商品分类为父商品分类"); + ErrorCode ITEM_CATEGORY_HAS_CHILDREN = new ErrorCode(1_060_102_005, "删除失败!请先删除该分类下的子分类!"); + ErrorCode ITEM_CATEGORY_HAS_ITEM = new ErrorCode(1_060_102_006, "删除失败!分类已被商品使用!"); + ErrorCode ITEM_CATEGORY_CODE_DUPLICATE = new ErrorCode(1_060_102_007, "商品分类编号重复"); + + // ========== WMS 基础数据-商品品牌 1-060-103-000 ========== + ErrorCode ITEM_BRAND_NOT_EXISTS = new ErrorCode(1_060_103_000, "商品品牌不存在"); + ErrorCode ITEM_BRAND_HAS_ITEM = new ErrorCode(1_060_103_001, "删除失败!品牌已被商品使用!"); + ErrorCode ITEM_BRAND_CODE_DUPLICATE = new ErrorCode(1_060_103_002, "商品品牌编号重复"); + ErrorCode ITEM_BRAND_NAME_DUPLICATE = new ErrorCode(1_060_103_003, "商品品牌名称重复"); + + // ========== WMS 基础数据-商品 1-060-104-000 ========== + ErrorCode ITEM_NOT_EXISTS = new ErrorCode(1_060_104_000, "商品不存在"); + ErrorCode ITEM_NAME_DUPLICATE = new ErrorCode(1_060_104_001, "商品名称重复"); + ErrorCode ITEM_CODE_DUPLICATE = new ErrorCode(1_060_104_007, "商品编号重复"); + ErrorCode ITEM_SKU_REQUIRED = new ErrorCode(1_060_104_002, "至少包含一个商品规格"); + ErrorCode ITEM_SKU_NAME_DUPLICATE = new ErrorCode(1_060_104_003, "商品规格名称【{}】重复"); + ErrorCode ITEM_SKU_NOT_EXISTS = new ErrorCode(1_060_104_004, "商品规格不存在"); + ErrorCode ITEM_SKU_HAS_INVENTORY = new ErrorCode(1_060_104_005, "删除失败!商品规格【{}】已被库存业务使用!"); + ErrorCode ITEM_SKU_HAS_ORDER = new ErrorCode(1_060_104_006, "删除失败!商品规格【{}】已被{}使用!"); + + // ========== WMS 基础数据-往来企业 1-060-105-000 ========== + ErrorCode MERCHANT_NOT_EXISTS = new ErrorCode(1_060_105_000, "往来企业不存在"); + ErrorCode MERCHANT_NOT_SUPPLIER = new ErrorCode(1_060_105_001, "往来企业必须是供应商或客户/供应商类型"); + ErrorCode MERCHANT_NOT_CUSTOMER = new ErrorCode(1_060_105_002, "往来企业必须是客户或客户/供应商类型"); + ErrorCode MERCHANT_HAS_ORDER = new ErrorCode(1_060_105_003, "删除失败!往来企业已被{}使用!"); + ErrorCode MERCHANT_CODE_DUPLICATE = new ErrorCode(1_060_105_004, "往来企业编号重复"); + ErrorCode MERCHANT_NAME_DUPLICATE = new ErrorCode(1_060_105_005, "往来企业名称重复"); + + // ========== WMS 入库单 1-060-200-000 ========== + ErrorCode RECEIPT_ORDER_NOT_EXISTS = new ErrorCode(1_060_200_000, "入库单不存在"); + ErrorCode RECEIPT_ORDER_NO_DUPLICATE = new ErrorCode(1_060_200_001, "入库单号重复"); + ErrorCode RECEIPT_ORDER_STATUS_NOT_PREPARE = new ErrorCode(1_060_200_002, "入库单状态不是草稿,不能操作"); + ErrorCode RECEIPT_ORDER_DETAIL_REQUIRED = new ErrorCode(1_060_200_003, "入库单至少包含一条明细"); + ErrorCode RECEIPT_ORDER_STATUS_NOT_DELETABLE = new ErrorCode(1_060_200_005, "入库单状态不是草稿或已作废,不能删除"); + ErrorCode RECEIPT_ORDER_DETAIL_NOT_EXISTS = new ErrorCode(1_060_200_007, "入库单明细不存在"); + + // ========== WMS 出库单 1-060-201-000 ========== + ErrorCode SHIPMENT_ORDER_NOT_EXISTS = new ErrorCode(1_060_201_000, "出库单不存在"); + ErrorCode SHIPMENT_ORDER_NO_DUPLICATE = new ErrorCode(1_060_201_001, "出库单号重复"); + ErrorCode SHIPMENT_ORDER_STATUS_NOT_PREPARE = new ErrorCode(1_060_201_002, "出库单状态不是草稿,不能操作"); + ErrorCode SHIPMENT_ORDER_DETAIL_REQUIRED = new ErrorCode(1_060_201_003, "出库单至少包含一条明细"); + ErrorCode SHIPMENT_ORDER_STATUS_NOT_DELETABLE = new ErrorCode(1_060_201_005, "出库单状态不是草稿或已作废,不能删除"); + ErrorCode SHIPMENT_ORDER_DETAIL_NOT_EXISTS = new ErrorCode(1_060_201_007, "出库单明细不存在"); + + // ========== WMS 移库单 1-060-202-000 ========== + ErrorCode MOVEMENT_ORDER_NOT_EXISTS = new ErrorCode(1_060_202_000, "移库单不存在"); + ErrorCode MOVEMENT_ORDER_NO_DUPLICATE = new ErrorCode(1_060_202_001, "移库单号重复"); + ErrorCode MOVEMENT_ORDER_STATUS_NOT_PREPARE = new ErrorCode(1_060_202_002, "移库单状态不是草稿,不能操作"); + ErrorCode MOVEMENT_ORDER_DETAIL_REQUIRED = new ErrorCode(1_060_202_003, "移库单至少包含一条明细"); + ErrorCode MOVEMENT_ORDER_STATUS_NOT_DELETABLE = new ErrorCode(1_060_202_005, "移库单状态不是草稿或已作废,不能删除"); + ErrorCode MOVEMENT_ORDER_DETAIL_NOT_EXISTS = new ErrorCode(1_060_202_006, "移库单明细不存在"); + ErrorCode MOVEMENT_ORDER_WAREHOUSE_SAME = new ErrorCode(1_060_202_007, "来源仓库和目标仓库不能相同"); + + // ========== WMS 盘库单 1-060-203-000 ========== + ErrorCode CHECK_ORDER_NOT_EXISTS = new ErrorCode(1_060_203_000, "盘库单不存在"); + ErrorCode CHECK_ORDER_NO_DUPLICATE = new ErrorCode(1_060_203_001, "盘库单号重复"); + ErrorCode CHECK_ORDER_STATUS_NOT_PREPARE = new ErrorCode(1_060_203_002, "盘库单状态不是草稿,不能操作"); + ErrorCode CHECK_ORDER_DETAIL_REQUIRED = new ErrorCode(1_060_203_003, "盘库单至少包含一条明细"); + ErrorCode CHECK_ORDER_STATUS_NOT_DELETABLE = new ErrorCode(1_060_203_005, "盘库单状态不是草稿或已作废,不能删除"); + ErrorCode CHECK_ORDER_DETAIL_NOT_EXISTS = new ErrorCode(1_060_203_006, "盘库单明细不存在"); + ErrorCode CHECK_ORDER_INVENTORY_CHANGED = new ErrorCode(1_060_203_007, "盘库单库存已变化,请重新加载库存后再完成"); + + // ========== WMS 库存 1-060-300-000 ========== + ErrorCode INVENTORY_QUANTITY_NOT_ENOUGH = new ErrorCode(1_060_300_000, + "库存不足,商品:{},商品规格:{},仓库:{},当前库存:{},变更数量:{}"); + +} diff --git a/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/md/WmsMerchantTypeEnum.java b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/md/WmsMerchantTypeEnum.java new file mode 100644 index 000000000..b6924e4c1 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/md/WmsMerchantTypeEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.wms.enums.md; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * WMS 往来企业类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum WmsMerchantTypeEnum implements ArrayValuable { + + CUSTOMER(1, "客户"), + SUPPLIER(2, "供应商"), + CUSTOMER_SUPPLIER(3, "客户/供应商"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(WmsMerchantTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * 名称 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsOrderStatusEnum.java b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsOrderStatusEnum.java new file mode 100644 index 000000000..065655bda --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsOrderStatusEnum.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.wms.enums.order; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * WMS 单据状态枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum WmsOrderStatusEnum implements ArrayValuable { + + PREPARE(0, "草稿"), + FINISHED(4, "已完成"), + CANCELED(5, "已作废"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(WmsOrderStatusEnum::getStatus) + .toArray(Integer[]::new); + + /** + * 状态 + */ + private final Integer status; + /** + * 名称 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsOrderTypeConstants.java b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsOrderTypeConstants.java new file mode 100644 index 000000000..3f9017d1d --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsOrderTypeConstants.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.wms.enums.order; + +/** + * WMS 单据业务类型常量 + * + * 集中管理业务类型枚举的编号,按业务域分段。 + * 各枚举类引用此处常量,避免硬编码数字。(也避免冲突!!!) + * + * @author 芋道源码 + */ +public final class WmsOrderTypeConstants { + + private WmsOrderTypeConstants() {} + + // ========== 入库单类型 [100, 200) ========== + + public static final int RECEIPT_PRODUCTION = 100; // 生产入库:WmsReceiptOrderDO + public static final int RECEIPT_PURCHASE = 101; // 采购入库:WmsReceiptOrderDO + public static final int RECEIPT_RETURN = 102; // 退货入库:WmsReceiptOrderDO + public static final int RECEIPT_GIVE_BACK = 103; // 归还入库:WmsReceiptOrderDO + + // ========== 出库单类型 [200, 300) ========== + + public static final int SHIPMENT_RETURN = 200; // 退货出库:WmsShipmentOrderDO + public static final int SHIPMENT_SALE = 201; // 销售出库:WmsShipmentOrderDO + public static final int SHIPMENT_PRODUCTION = 202; // 生产出库:WmsShipmentOrderDO + +} diff --git a/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsOrderTypeEnum.java b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsOrderTypeEnum.java new file mode 100644 index 000000000..701f7137b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsOrderTypeEnum.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.wms.enums.order; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * WMS 单据类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum WmsOrderTypeEnum implements ArrayValuable { + + RECEIPT(1, "入库单"), + SHIPMENT(2, "出库单"), + MOVEMENT(3, "移库单"), + CHECK(4, "盘库单"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(WmsOrderTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * 名称 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderTypeEnum.java b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderTypeEnum.java new file mode 100644 index 000000000..1ac21f49b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderTypeEnum.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.wms.enums.order; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * WMS 入库单类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum WmsReceiptOrderTypeEnum implements ArrayValuable { + + PRODUCTION(WmsOrderTypeConstants.RECEIPT_PRODUCTION, "生产入库"), + PURCHASE(WmsOrderTypeConstants.RECEIPT_PURCHASE, "采购入库"), + RETURN(WmsOrderTypeConstants.RECEIPT_RETURN, "退货入库"), + GIVE_BACK(WmsOrderTypeConstants.RECEIPT_GIVE_BACK, "归还入库"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(WmsReceiptOrderTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * 名称 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsShipmentOrderTypeEnum.java b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsShipmentOrderTypeEnum.java new file mode 100644 index 000000000..79545f12e --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-api/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsShipmentOrderTypeEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.wms.enums.order; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * WMS 出库单类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum WmsShipmentOrderTypeEnum implements ArrayValuable { + + RETURN(WmsOrderTypeConstants.SHIPMENT_RETURN, "退货出库"), + SALE(WmsOrderTypeConstants.SHIPMENT_SALE, "销售出库"), + PRODUCTION(WmsOrderTypeConstants.SHIPMENT_PRODUCTION, "生产出库"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(WmsShipmentOrderTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * 名称 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/Dockerfile b/yudao-module-wms/yudao-module-wms-server/Dockerfile new file mode 100644 index 000000000..200c60fae --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/Dockerfile @@ -0,0 +1,19 @@ +## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 +## 感谢复旦核博士的建议!灰子哥,牛皮! +FROM eclipse-temurin:21-jre + +## 创建目录,并使用它作为工作目录 +RUN mkdir -p /yudao-module-wms-server +WORKDIR /yudao-module-wms-server +## 将后端项目的 Jar 文件,复制到镜像中 +COPY ./target/yudao-module-wms-server.jar app.jar + +## 设置 TZ 时区 +## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 +ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m" + +## 暴露后端项目的 48092 端口 +EXPOSE 48092 + +## 启动后端项目 +CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar diff --git a/yudao-module-wms/yudao-module-wms-server/pom.xml b/yudao-module-wms/yudao-module-wms-server/pom.xml new file mode 100644 index 000000000..07590d70e --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/pom.xml @@ -0,0 +1,124 @@ + + + + cn.iocoder.cloud + yudao-module-wms + ${revision} + + 4.0.0 + yudao-module-wms-server + + ${project.artifactId} + + wms 模块下,仓库管理系统(Warehouse Management System)。 + 例如说:仓库、物料、库存、入库、出库、移库、盘库等等 + + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-env + + + + + cn.iocoder.cloud + yudao-module-system-api + ${revision} + + + cn.iocoder.cloud + yudao-module-wms-api + ${revision} + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-tenant + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-security + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-mybatis + + + + cn.iocoder.cloud + yudao-spring-boot-starter-redis + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-rpc + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-excel + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-monitor + + + + org.projectlombok + lombok + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-test + + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + + diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/WmsServerApplication.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/WmsServerApplication.java new file mode 100644 index 000000000..a5ba71599 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/WmsServerApplication.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.wms; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 项目的启动类 + *

+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + * + * @author 芋道源码 + */ +@SpringBootApplication +public class WmsServerApplication { + + public static void main(String[] args) { + // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + + SpringApplication.run(WmsServerApplication.class, args); + + // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/WmsHomeStatisticsController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/WmsHomeStatisticsController.java new file mode 100644 index 000000000..e9cbbef78 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/WmsHomeStatisticsController.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.wms.controller.admin.home; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.wms.controller.admin.home.vo.WmsHomeInventorySummaryRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.home.vo.WmsHomeOrderSummaryRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.home.vo.WmsHomeOrderTrendRespVO; +import cn.iocoder.yudao.module.wms.service.home.WmsHomeStatisticsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - WMS 首页统计") +@RestController +@RequestMapping("/wms/home-statistics") +@Validated +public class WmsHomeStatisticsController { + + @Resource + private WmsHomeStatisticsService homeStatisticsService; + + @GetMapping("/order-summary") + @Operation(summary = "获得首页单据汇总统计") + @Parameter(name = "warehouseId", description = "仓库编号", example = "1024") + @PreAuthorize("@ss.hasPermission('wms:home:query')") + public CommonResult> getOrderSummary( + @RequestParam(value = "warehouseId", required = false) Long warehouseId) { + return success(homeStatisticsService.getOrderSummary(warehouseId)); + } + + @GetMapping("/order-trend") + @Operation(summary = "获得首页单据趋势") + @Parameter(name = "days", description = "天数", example = "7") + @PreAuthorize("@ss.hasPermission('wms:home:query')") + public CommonResult> getOrderTrend( + @RequestParam(value = "days", defaultValue = "7") @Min(1) @Max(90) Integer days, + @RequestParam(value = "warehouseId", required = false) Long warehouseId) { + return success(homeStatisticsService.getOrderTrend(days, warehouseId)); + } + + @GetMapping("/inventory-summary") + @Operation(summary = "获得首页库存汇总统计") + @Parameter(name = "warehouseId", description = "仓库编号", example = "1024") + @PreAuthorize("@ss.hasPermission('wms:home:query')") + public CommonResult getInventorySummary( + @RequestParam(value = "warehouseId", required = false) Long warehouseId, + @RequestParam(value = "goodsLimit", defaultValue = "5") @Min(1) @Max(20) Integer goodsLimit, + @RequestParam(value = "warehouseLimit", defaultValue = "8") @Min(1) @Max(20) Integer warehouseLimit) { + return success(homeStatisticsService.getInventorySummary(warehouseId, goodsLimit, warehouseLimit)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeInventorySummaryRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeInventorySummaryRespVO.java new file mode 100644 index 000000000..18bb5c3e8 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeInventorySummaryRespVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.wms.controller.admin.home.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Schema(description = "管理后台 - WMS 首页库存汇总统计 Response VO") +@Data +public class WmsHomeInventorySummaryRespVO { + + @Schema(description = "总库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000.00") + private BigDecimal totalQuantity; + + @Schema(description = "商品库存占比列表") + private List goodsShareList; + + @Schema(description = "仓库库存分布列表") + private List warehouseDistributionList; + + @Schema(description = "管理后台 - WMS 首页商品库存排行 Response VO") + @Data + public static class ItemRank { + + @Schema(description = "商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "A4 复印纸") + private String name; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal quantity; + + } + + @Schema(description = "管理后台 - WMS 首页仓库库存排行 Response VO") + @Data + public static class WarehouseRank { + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "仓库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海仓") + private String name; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal quantity; + + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeOrderStatusRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeOrderStatusRespVO.java new file mode 100644 index 000000000..91b0fea11 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeOrderStatusRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.wms.controller.admin.home.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - WMS 首页单据状态统计 Response VO") +@Data +public class WmsHomeOrderStatusRespVO { + + @Schema(description = "状态值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer status; + + @Schema(description = "单据数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "12") + private Long count; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeOrderSummaryRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeOrderSummaryRespVO.java new file mode 100644 index 000000000..adb34dede --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeOrderSummaryRespVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.wms.controller.admin.home.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - WMS 首页单据汇总统计 Response VO") +@Data +public class WmsHomeOrderSummaryRespVO { + + @Schema(description = "单据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "单据总数", requiredMode = Schema.RequiredMode.REQUIRED, example = "12") + private Long total; + + @Schema(description = "状态分布") + private List statuses; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeOrderTrendRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeOrderTrendRespVO.java new file mode 100644 index 000000000..3ebb477e7 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/home/vo/WmsHomeOrderTrendRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.wms.controller.admin.home.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 首页单据趋势 Response VO") +@Data +public class WmsHomeOrderTrendRespVO { + + @Schema(description = "时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1778169600000") + private LocalDateTime time; + + @Schema(description = "入库单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "12") + private Long receiptCount; + + @Schema(description = "出库单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8") + private Long shipmentCount; + + @Schema(description = "移库单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") + private Long movementCount; + + @Schema(description = "盘库单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Long checkCount; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryController.java new file mode 100644 index 000000000..5142e05f8 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryController.java @@ -0,0 +1,87 @@ +package cn.iocoder.yudao.module.wms.controller.admin.inventory; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - WMS 库存统计") +@RestController +@RequestMapping("/wms/inventory") +@Validated +public class WmsInventoryController { + + @Resource + private WmsInventoryService inventoryService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseService warehouseService; + + @GetMapping("/page") + @Operation(summary = "获得库存统计分页") + @PreAuthorize("@ss.hasPermission('wms:inventory:query')") + public CommonResult> getInventoryPage(@Valid WmsInventoryPageReqVO pageReqVO) { + PageResult pageResult = inventoryService.getInventoryPage(pageReqVO); + return success(new PageResult<>(buildInventoryRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/list") + @Operation(summary = "获得库存统计列表") + @PreAuthorize("@ss.hasPermission('wms:inventory:query')") + public CommonResult> getInventoryList(@Valid WmsInventoryListReqVO listReqVO) { + List list = inventoryService.getInventoryList(listReqVO); + return success(buildInventoryRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private List buildInventoryRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsInventoryDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsInventoryDO::getWarehouseId)); + return BeanUtils.toBean(list, WmsInventoryRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryHistoryController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryHistoryController.java new file mode 100644 index 000000000..c7530bfaa --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryHistoryController.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.wms.controller.admin.inventory; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.history.WmsInventoryHistoryPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.history.WmsInventoryHistoryRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryHistoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryHistoryService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - WMS 库存流水") +@RestController +@RequestMapping("/wms/inventory-history") +@Validated +public class WmsInventoryHistoryController { + + @Resource + private WmsInventoryHistoryService inventoryHistoryService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseService warehouseService; + + @GetMapping("/page") + @Operation(summary = "获得库存流水分页") + @PreAuthorize("@ss.hasPermission('wms:inventory-history:query')") + public CommonResult> getInventoryHistoryPage( + @Valid WmsInventoryHistoryPageReqVO pageReqVO) { + PageResult pageResult = inventoryHistoryService.getInventoryHistoryPage(pageReqVO); + return success(new PageResult<>(buildInventoryHistoryRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + // ==================== 拼接 VO ==================== + + private List buildInventoryHistoryRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsInventoryHistoryDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsInventoryHistoryDO::getWarehouseId)); + return BeanUtils.toBean(list, WmsInventoryHistoryRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryListReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryListReqVO.java new file mode 100644 index 000000000..e3deb8cb2 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryListReqVO.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.wms.controller.admin.inventory.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - WMS 库存统计列表 Request VO") +@Data +public class WmsInventoryListReqVO { + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryPageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryPageReqVO.java new file mode 100644 index 000000000..4917a1e62 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryPageReqVO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.wms.controller.admin.inventory.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - WMS 库存统计分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsInventoryPageReqVO extends PageParam { + + /** + * 按仓库维度统计 + */ + public static final String TYPE_WAREHOUSE = "warehouse"; + + /** + * 按商品维度统计 + */ + public static final String TYPE_ITEM = "item"; + + @Schema(description = "统计维度", requiredMode = Schema.RequiredMode.REQUIRED, example = "warehouse") + @NotBlank(message = "统计维度不能为空") + private String type; + + @Schema(description = "商品编号", example = "ITEM001") + private String itemCode; + + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + + @Schema(description = "商品 SKU 编号", example = "1024") + private Long skuId; + + @Schema(description = "规格编号", example = "SKU001") + private String skuCode; + + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "仓库编号", example = "2048") + private Long warehouseId; + + @Schema(description = "最小库存数量", example = "0.01") + private BigDecimal minQuantity; + + @Schema(description = "是否只查询正库存", example = "true") + private Boolean onlyPositiveQuantity; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryRespVO.java new file mode 100644 index 000000000..c5c3d34ec --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryRespVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.wms.controller.admin.inventory.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 库存统计 Response VO") +@Data +public class WmsInventoryRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long itemId; + @Schema(description = "商品编码", example = "ITEM001") + private String itemCode; + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + @Schema(description = "商品单位", example = "箱") + private String unit; + + @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") + private Long skuId; + @Schema(description = "规格编号", example = "SKU001") + private String skuCode; + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8192") + private Long warehouseId; + @Schema(description = "仓库名称", example = "成品仓") + private String warehouseName; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal quantity; + @Schema(description = "备注", example = "备注") + private String remark; + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/history/WmsInventoryHistoryPageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/history/WmsInventoryHistoryPageReqVO.java new file mode 100644 index 000000000..23b42bcd5 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/history/WmsInventoryHistoryPageReqVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.history; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - WMS 库存流水分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsInventoryHistoryPageReqVO extends PageParam { + + @Schema(description = "商品编号", example = "ITEM001") + private String itemCode; + + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + + @Schema(description = "商品 SKU 编号", example = "1024") + private Long skuId; + + @Schema(description = "规格编号", example = "SKU001") + private String skuCode; + + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "仓库编号", example = "2048") + private Long warehouseId; + + @Schema(description = "单据号", example = "RK202605110001") + private String orderNo; + + @Schema(description = "单据类型", example = "1") + private Integer orderType; + + @Schema(description = "操作时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/history/WmsInventoryHistoryRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/history/WmsInventoryHistoryRespVO.java new file mode 100644 index 000000000..87c87251a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/history/WmsInventoryHistoryRespVO.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.history; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 库存流水 Response VO") +@Data +public class WmsInventoryHistoryRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long itemId; + @Schema(description = "商品编码", example = "ITEM001") + private String itemCode; + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + @Schema(description = "商品单位", example = "箱") + private String unit; + + @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") + private Long skuId; + @Schema(description = "规格编号", example = "SKU001") + private String skuCode; + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8192") + private Long warehouseId; + @Schema(description = "仓库名称", example = "成品仓") + private String warehouseName; + + @Schema(description = "库存变化数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.00") + private BigDecimal quantity; + @Schema(description = "变化前库存数量", example = "90.00") + private BigDecimal beforeQuantity; + @Schema(description = "变化后库存数量", example = "100.00") + private BigDecimal afterQuantity; + @Schema(description = "单价", example = "1000.00") + private BigDecimal price; + @Schema(description = "库存变化金额", example = "10000.00") + private BigDecimal totalPrice; + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "单据编号", example = "1024") + private Long orderId; + @Schema(description = "单据号", example = "RK202605110001") + private String orderNo; + @Schema(description = "单据类型", example = "1") + private Integer orderType; + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemBrandController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemBrandController.java new file mode 100644 index 000000000..6094e1150 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemBrandController.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand.WmsItemBrandPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand.WmsItemBrandRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand.WmsItemBrandSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemBrandDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemBrandService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - WMS 商品品牌") +@RestController +@RequestMapping("/wms/item-brand") +@Validated +public class WmsItemBrandController { + + @Resource + private WmsItemBrandService brandService; + + @PostMapping("/create") + @Operation(summary = "创建商品品牌") + @PreAuthorize("@ss.hasPermission('wms:item-brand:create')") + public CommonResult createItemBrand(@Valid @RequestBody WmsItemBrandSaveReqVO createReqVO) { + return success(brandService.createItemBrand(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新商品品牌") + @PreAuthorize("@ss.hasPermission('wms:item-brand:update')") + public CommonResult updateItemBrand(@Valid @RequestBody WmsItemBrandSaveReqVO updateReqVO) { + brandService.updateItemBrand(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除商品品牌") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:item-brand:delete')") + public CommonResult deleteItemBrand(@RequestParam("id") Long id) { + brandService.deleteItemBrand(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得商品品牌") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:item-brand:query')") + public CommonResult getItemBrand(@RequestParam("id") Long id) { + WmsItemBrandDO brand = brandService.getItemBrand(id); + return success(BeanUtils.toBean(brand, WmsItemBrandRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得商品品牌分页") + @PreAuthorize("@ss.hasPermission('wms:item-brand:query')") + public CommonResult> getItemBrandPage(@Valid WmsItemBrandPageReqVO pageReqVO) { + PageResult pageResult = brandService.getItemBrandPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, WmsItemBrandRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得商品品牌精简列表", description = "主要用于前端下拉") + @PreAuthorize("@ss.hasPermission('wms:item-brand:query')") + public CommonResult> getItemBrandSimpleList() { + List list = brandService.getItemBrandList(); + return success(BeanUtils.toBean(list, WmsItemBrandRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出商品品牌 Excel") + @PreAuthorize("@ss.hasPermission('wms:item-brand:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportItemBrandExcel(@Valid WmsItemBrandPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = brandService.getItemBrandPage(pageReqVO).getList(); + ExcelUtils.write(response, "商品品牌.xls", "数据", WmsItemBrandRespVO.class, + BeanUtils.toBean(list, WmsItemBrandRespVO.class)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemCategoryController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemCategoryController.java new file mode 100644 index 000000000..d30edfa24 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemCategoryController.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category.WmsItemCategoryListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category.WmsItemCategoryRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category.WmsItemCategorySaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemCategoryDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemCategoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - WMS 商品分类") +@RestController +@RequestMapping("/wms/item-category") +@Validated +public class WmsItemCategoryController { + + @Resource + private WmsItemCategoryService categoryService; + + @PostMapping("/create") + @Operation(summary = "创建商品分类") + @PreAuthorize("@ss.hasPermission('wms:item-category:create')") + public CommonResult createItemCategory(@Valid @RequestBody WmsItemCategorySaveReqVO createReqVO) { + return success(categoryService.createItemCategory(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新商品分类") + @PreAuthorize("@ss.hasPermission('wms:item-category:update')") + public CommonResult updateItemCategory(@Valid @RequestBody WmsItemCategorySaveReqVO updateReqVO) { + categoryService.updateItemCategory(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除商品分类") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:item-category:delete')") + public CommonResult deleteItemCategory(@RequestParam("id") Long id) { + categoryService.deleteItemCategory(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得商品分类") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:item-category:query')") + public CommonResult getItemCategory(@RequestParam("id") Long id) { + WmsItemCategoryDO category = categoryService.getItemCategory(id); + return success(BeanUtils.toBean(category, WmsItemCategoryRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得商品分类列表") + @PreAuthorize("@ss.hasPermission('wms:item-category:query')") + public CommonResult> getItemCategoryList(@Valid WmsItemCategoryListReqVO listReqVO) { + List list = categoryService.getItemCategoryList(listReqVO); + return success(BeanUtils.toBean(list, WmsItemCategoryRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得商品分类精简列表", description = "主要用于前端下拉") + @PreAuthorize("@ss.hasPermission('wms:item-category:query')") + public CommonResult> getItemCategorySimpleList() { + List list = categoryService.getItemCategoryList(new WmsItemCategoryListReqVO()); + return success(BeanUtils.toBean(list, WmsItemCategoryRespVO.class)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemController.java new file mode 100644 index 000000000..86a8592fc --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemController.java @@ -0,0 +1,150 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemBrandDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemCategoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemBrandService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemCategoryService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; + +@Tag(name = "管理后台 - WMS 商品") +@RestController +@RequestMapping("/wms/item") +@Validated +public class WmsItemController { + + @Resource + private WmsItemService itemService; + @Resource + private WmsItemCategoryService categoryService; + @Resource + private WmsItemBrandService brandService; + @Resource + private WmsItemSkuService itemSkuService; + + @PostMapping("/create") + @Operation(summary = "创建商品") + @PreAuthorize("@ss.hasPermission('wms:item:create')") + public CommonResult createItem(@Valid @RequestBody WmsItemSaveReqVO createReqVO) { + return success(itemService.createItem(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新商品") + @PreAuthorize("@ss.hasPermission('wms:item:update')") + public CommonResult updateItem(@Valid @RequestBody WmsItemSaveReqVO updateReqVO) { + itemService.updateItem(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除商品") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:item:delete')") + public CommonResult deleteItem(@RequestParam("id") Long id) { + itemService.deleteItem(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得商品") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:item:query')") + public CommonResult getItem(@RequestParam("id") Long id) { + WmsItemDO item = itemService.getItem(id); + return success(buildItemRespVO(item)); + } + + @GetMapping("/page") + @Operation(summary = "获得商品分页") + @PreAuthorize("@ss.hasPermission('wms:item:query')") + public CommonResult> getItemPage(@Valid WmsItemPageReqVO pageReqVO) { + PageResult pageResult = itemService.getItemPage(pageReqVO); + return success(new PageResult<>(buildItemRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得商品精简列表") + @PreAuthorize("@ss.hasPermission('wms:item:query')") + public CommonResult> getItemSimpleList(@Valid WmsItemListReqVO listReqVO) { + List list = itemService.getItemList(listReqVO); + return success(buildItemRespVOList(list)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出商品 Excel") + @PreAuthorize("@ss.hasPermission('wms:item:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportItemExcel(@Valid WmsItemPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = itemService.getItemPage(pageReqVO).getList(); + ExcelUtils.write(response, "商品.xls", "数据", WmsItemRespVO.class, + buildItemRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private WmsItemRespVO buildItemRespVO(WmsItemDO item) { + if (item == null) { + return null; + } + return getFirst(buildItemRespVOList(Collections.singletonList(item))); + } + + private List buildItemRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 查询关联数据 + Map categoryMap = categoryService.getItemCategoryMap( + convertSet(list, WmsItemDO::getCategoryId)); + Map brandMap = brandService.getItemBrandMap( + convertSet(list, WmsItemDO::getBrandId)); + Map> skuMap = itemSkuService.getItemSkuMultiMap( + convertSet(list, WmsItemDO::getId)); + // 拼接 VO + return BeanUtils.toBean(list, WmsItemRespVO.class, vo -> { + MapUtils.findAndThen(categoryMap, vo.getCategoryId(), category -> + vo.setCategoryName(category.getName())); + MapUtils.findAndThen(brandMap, vo.getBrandId(), brand -> + vo.setBrandName(brand.getName())); + vo.setSkus(BeanUtils.toBean(skuMap.getOrDefault(vo.getId(), Collections.emptyList()), + WmsItemSkuRespVO.class)); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemSkuController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemSkuController.java new file mode 100644 index 000000000..5ece6f3ba --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/WmsItemSkuController.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemBrandDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemCategoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemBrandService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemCategoryService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * WMS 商品 SKU Controller。 + * + *

SKU 维度的查询入口:弹窗 / 选择器场景使用,按 SKU 一行展开,支持商品 / 品牌 / 分类多表联查筛选。 + * 复用商品权限 {@code wms:item:query},不单独建权限点(lite 也是商品/SKU 共用 {@code wms:item:list})。 + * + *

SKU 的新增 / 修改 / 删除仍随商品弹窗一并维护,不在本 Controller 暴露。 + */ +@Tag(name = "管理后台 - WMS 商品 SKU") +@RestController +@RequestMapping("/wms/item-sku") +@Validated +public class WmsItemSkuController { + + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemCategoryService categoryService; + @Resource + private WmsItemBrandService brandService; + + @GetMapping("/page") + @Operation(summary = "获得商品 SKU 分页", description = "按 SKU 维度分页,支持商品 / 品牌 / 分类多表联查筛选") + @PreAuthorize("@ss.hasPermission('wms:item:query')") + public CommonResult> getItemSkuPage(@Valid WmsItemSkuPageReqVO pageReqVO) { + PageResult pageResult = itemSkuService.getItemSkuPage(pageReqVO); + return success(new PageResult<>(buildItemSkuRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + // ==================== 拼接 VO ==================== + + private List buildItemSkuRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 查询关联数据 + Map itemMap = itemService.getItemMap(convertSet(list, WmsItemSkuDO::getItemId)); + Map categoryMap = categoryService.getItemCategoryMap( + convertSet(itemMap.values(), WmsItemDO::getCategoryId)); + Map brandMap = brandService.getItemBrandMap( + convertSet(itemMap.values(), WmsItemDO::getBrandId)); + // 拼接 VO + return BeanUtils.toBean(list, WmsItemSkuRespVO.class, vo -> MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { + vo.setItemCode(item.getCode()).setItemName(item.getName()).setUnit(item.getUnit()) + .setCategoryId(item.getCategoryId()).setBrandId(item.getBrandId()); + MapUtils.findAndThen(categoryMap, item.getCategoryId(), category -> + vo.setCategoryName(category.getName())); + MapUtils.findAndThen(brandMap, item.getBrandId(), brand -> + vo.setBrandName(brand.getName())); + })); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/brand/WmsItemBrandPageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/brand/WmsItemBrandPageReqVO.java new file mode 100644 index 000000000..0e2d74525 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/brand/WmsItemBrandPageReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - WMS 商品品牌分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsItemBrandPageReqVO extends PageParam { + + @Schema(description = "品牌编号", example = "B00000001") + private String code; + + @Schema(description = "品牌名称", example = "华为") + private String name; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/brand/WmsItemBrandRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/brand/WmsItemBrandRespVO.java new file mode 100644 index 000000000..bc68efe24 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/brand/WmsItemBrandRespVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 商品品牌 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsItemBrandRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "品牌编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "B00000001") + @ExcelProperty("品牌编号") + private String code; + + @Schema(description = "品牌名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "华为") + @ExcelProperty("品牌名称") + private String name; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/brand/WmsItemBrandSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/brand/WmsItemBrandSaveReqVO.java new file mode 100644 index 000000000..7090cf30d --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/brand/WmsItemBrandSaveReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Schema(description = "管理后台 - WMS 商品品牌新增/修改 Request VO") +@Data +public class WmsItemBrandSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "品牌编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "B00000001") + @NotEmpty(message = "品牌编号不能为空") + @Size(max = 20, message = "品牌编号长度不能超过 20 个字符") + private String code; + + @Schema(description = "品牌名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "华为") + @NotEmpty(message = "品牌名称不能为空") + @Size(max = 30, message = "品牌名称长度不能超过 30 个字符") + private String name; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/category/WmsItemCategoryListReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/category/WmsItemCategoryListReqVO.java new file mode 100644 index 000000000..274cad45f --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/category/WmsItemCategoryListReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - WMS 商品分类列表 Request VO") +@Data +public class WmsItemCategoryListReqVO { + + @Schema(description = "父级编号", example = "1") + private Long parentId; + + @Schema(description = "分类编号", example = "C00000001") + private String code; + + @Schema(description = "分类名称", example = "原料") + private String name; + + @Schema(description = "状态", example = "0") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") + private Integer status; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/category/WmsItemCategoryRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/category/WmsItemCategoryRespVO.java new file mode 100644 index 000000000..d629b989a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/category/WmsItemCategoryRespVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 商品分类 Response VO") +@Data +public class WmsItemCategoryRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "父级编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Long parentId; + + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "C00000001") + private String code; + + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "原料") + private String name; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer sort; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/category/WmsItemCategorySaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/category/WmsItemCategorySaveReqVO.java new file mode 100644 index 000000000..8d0df2254 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/category/WmsItemCategorySaveReqVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Schema(description = "管理后台 - WMS 商品分类新增/修改 Request VO") +@Data +public class WmsItemCategorySaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "父级编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @NotNull(message = "父级编号不能为空") + private Long parentId; + + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "C00000001") + @NotEmpty(message = "分类编号不能为空") + @Size(max = 20, message = "分类编号长度不能超过 20 个字符") + private String code; + + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "原料") + @NotEmpty(message = "分类名称不能为空") + @Size(max = 30, message = "分类名称长度不能超过 30 个字符") + private String name; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "排序不能为空") + private Integer sort; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") + private Integer status; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemListReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemListReqVO.java new file mode 100644 index 000000000..8dde808fe --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemListReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - WMS 商品列表 Request VO") +@Data +public class WmsItemListReqVO { + + @Schema(description = "商品编号", example = "ITEM001") + private String code; + + @Schema(description = "商品名称", example = "华为 nova flip") + private String name; + + @Schema(description = "商品分类编号", example = "1024") + private Long categoryId; + + @Schema(description = "商品品牌编号", example = "2048") + private Long brandId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemPageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemPageReqVO.java new file mode 100644 index 000000000..5ae42c396 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemPageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - WMS 商品分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsItemPageReqVO extends PageParam { + + @Schema(description = "商品编号", example = "ITEM001") + private String code; + + @Schema(description = "商品名称", example = "华为 nova flip") + private String name; + + @Schema(description = "商品分类编号", example = "1024") + private Long categoryId; + + @Schema(description = "商品品牌编号", example = "2048") + private Long brandId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemRespVO.java new file mode 100644 index 000000000..d4e74167c --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemRespVO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 商品 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsItemRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "商品编号", example = "ITEM001") + @ExcelProperty("商品编号") + private String code; + + @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "华为 nova flip") + @ExcelProperty("商品名称") + private String name; + + @Schema(description = "商品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long categoryId; + + @Schema(description = "商品分类名称", example = "手机") + @ExcelProperty("商品分类") + private String categoryName; + + @Schema(description = "单位", example = "台") + @ExcelProperty("单位") + private String unit; + + @Schema(description = "商品品牌编号", example = "1") + private Long brandId; + + @Schema(description = "商品品牌名称", example = "华为") + @ExcelProperty("商品品牌") + private String brandName; + + @Schema(description = "备注", example = "备注") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "规格列表") + private List skus; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemSaveReqVO.java new file mode 100644 index 000000000..6b8cadf1e --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemSaveReqVO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item; + +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuSaveReqVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - WMS 商品创建/更新 Request VO") +@Data +public class WmsItemSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ITEM001") + @NotBlank(message = "商品编号不能为空") + @Size(max = 20, message = "商品编号长度不能超过 20 个字符") + private String code; + + @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "华为 nova flip") + @NotBlank(message = "商品名称不能为空") + @Size(max = 60, message = "商品名称长度不能超过 60 个字符") + private String name; + + @Schema(description = "商品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "商品分类不能为空") + private Long categoryId; + + @Schema(description = "单位", example = "台") + @Size(max = 20, message = "单位长度不能超过 20 个字符") + private String unit; + + @Schema(description = "商品品牌编号", example = "1") + private Long brandId; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + + @Schema(description = "规格列表") + @Valid + @NotEmpty(message = "至少包含一个商品规格") + private List skus; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/sku/WmsItemSkuPageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/sku/WmsItemSkuPageReqVO.java new file mode 100644 index 000000000..c091ea84e --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/sku/WmsItemSkuPageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - WMS 商品 SKU 分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsItemSkuPageReqVO extends PageParam { + + @Schema(description = "商品编号", example = "I00000001") + private String itemCode; + + @Schema(description = "商品名称", example = "华为 nova flip") + private String itemName; + + @Schema(description = "商品分类编号", example = "1") + private Long categoryId; + + @Schema(description = "商品品牌编号", example = "1") + private Long brandId; + + @Schema(description = "规格编号", example = "S00000001") + private String code; + + @Schema(description = "规格名称", example = "黑色") + private String name; + + @Schema(description = "条码", example = "12345678") + private String barCode; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/sku/WmsItemSkuRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/sku/WmsItemSkuRespVO.java new file mode 100644 index 000000000..8bd57aa6a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/sku/WmsItemSkuRespVO.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 商品 SKU Response VO") +@Data +public class WmsItemSkuRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "规格名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "黑色") + private String name; + + @Schema(description = "商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long itemId; + + @Schema(description = "商品编码", example = "ITEM001") + private String itemCode; + + @Schema(description = "商品名称", example = "华为 nova flip") + private String itemName; + + @Schema(description = "商品分类编号", example = "1") + private Long categoryId; + + @Schema(description = "商品分类名称", example = "手机") + private String categoryName; + + @Schema(description = "单位", example = "台") + private String unit; + + @Schema(description = "商品品牌编号", example = "1") + private Long brandId; + + @Schema(description = "商品品牌名称", example = "华为") + private String brandName; + + @Schema(description = "条码", example = "690000000001") + private String barCode; + + @Schema(description = "规格编号", example = "SKU001") + private String code; + + @Schema(description = "长,单位 cm", example = "10.0") + private BigDecimal length; + + @Schema(description = "宽,单位 cm", example = "8.0") + private BigDecimal width; + + @Schema(description = "高,单位 cm", example = "1.0") + private BigDecimal height; + + @Schema(description = "毛重,单位 kg", example = "1.000") + private BigDecimal grossWeight; + + @Schema(description = "净重,单位 kg", example = "0.900") + private BigDecimal netWeight; + + @Schema(description = "成本价,单位元", example = "5000.00") + private BigDecimal costPrice; + + @Schema(description = "销售价,单位元", example = "5288.00") + private BigDecimal sellingPrice; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/sku/WmsItemSkuSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/sku/WmsItemSkuSaveReqVO.java new file mode 100644 index 000000000..1a79dcbdc --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/sku/WmsItemSkuSaveReqVO.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - WMS 商品 SKU 创建/更新 Request VO") +@Data +public class WmsItemSkuSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "规格名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "黑色") + @NotBlank(message = "规格名称不能为空") + @Size(max = 255, message = "规格名称长度不能超过 255 个字符") + private String name; + + @Schema(description = "商品编号", example = "1") + private Long itemId; + + @Schema(description = "条码", example = "690000000001") + @Size(max = 64, message = "条码长度不能超过 64 个字符") + private String barCode; + + @Schema(description = "规格编号", example = "SKU001") + @Size(max = 64, message = "规格编号长度不能超过 64 个字符") + private String code; + + @Schema(description = "长,单位 cm", example = "10.0") + @DecimalMin(value = "0", message = "长不能小于 0") + private BigDecimal length; + + @Schema(description = "宽,单位 cm", example = "8.0") + @DecimalMin(value = "0", message = "宽不能小于 0") + private BigDecimal width; + + @Schema(description = "高,单位 cm", example = "1.0") + @DecimalMin(value = "0", message = "高不能小于 0") + private BigDecimal height; + + @Schema(description = "毛重,单位 kg", example = "1.000") + @DecimalMin(value = "0", message = "毛重不能小于 0") + private BigDecimal grossWeight; + + @Schema(description = "净重,单位 kg", example = "0.900") + @DecimalMin(value = "0", message = "净重不能小于 0") + private BigDecimal netWeight; + + @Schema(description = "成本价,单位元", example = "5000.00") + @DecimalMin(value = "0", message = "成本价不能小于 0") + private BigDecimal costPrice; + + @Schema(description = "销售价,单位元", example = "5288.00") + @DecimalMin(value = "0", message = "销售价不能小于 0") + private BigDecimal sellingPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/WmsMerchantController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/WmsMerchantController.java new file mode 100644 index 000000000..897f6351c --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/WmsMerchantController.java @@ -0,0 +1,101 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.merchant; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO; +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - WMS 往来企业") +@RestController +@RequestMapping("/wms/merchant") +@Validated +public class WmsMerchantController { + + @Resource + private WmsMerchantService merchantService; + + @PostMapping("/create") + @Operation(summary = "创建往来企业") + @PreAuthorize("@ss.hasPermission('wms:merchant:create')") + public CommonResult createMerchant(@Valid @RequestBody WmsMerchantSaveReqVO createReqVO) { + return success(merchantService.createMerchant(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新往来企业") + @PreAuthorize("@ss.hasPermission('wms:merchant:update')") + public CommonResult updateMerchant(@Valid @RequestBody WmsMerchantSaveReqVO updateReqVO) { + merchantService.updateMerchant(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除往来企业") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:merchant:delete')") + public CommonResult deleteMerchant(@RequestParam("id") Long id) { + merchantService.deleteMerchant(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得往来企业") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:merchant:query')") + public CommonResult getMerchant(@RequestParam("id") Long id) { + WmsMerchantDO merchant = merchantService.getMerchant(id); + return success(BeanUtils.toBean(merchant, WmsMerchantRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得往来企业分页") + @PreAuthorize("@ss.hasPermission('wms:merchant:query')") + public CommonResult> getMerchantPage(@Valid WmsMerchantPageReqVO pageReqVO) { + PageResult pageResult = merchantService.getMerchantPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, WmsMerchantRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得往来企业精简列表", description = "主要用于前端下拉") + @PreAuthorize("@ss.hasPermission('wms:merchant:query')") + public CommonResult> getMerchantSimpleList(@Valid WmsMerchantListReqVO listReqVO) { + List list = merchantService.getMerchantList(listReqVO); + return success(BeanUtils.toBean(list, WmsMerchantRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出往来企业 Excel") + @PreAuthorize("@ss.hasPermission('wms:merchant:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportMerchantExcel(@Valid WmsMerchantPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = merchantService.getMerchantPage(pageReqVO).getList(); + ExcelUtils.write(response, "往来企业.xls", "数据", WmsMerchantRespVO.class, + BeanUtils.toBean(list, WmsMerchantRespVO.class)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantListReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantListReqVO.java new file mode 100644 index 000000000..287a1bcc7 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantListReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.enums.md.WmsMerchantTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - WMS 往来企业列表 Request VO") +@Data +public class WmsMerchantListReqVO { + + @Schema(description = "往来企业编号", example = "MER001") + private String code; + + @Schema(description = "往来企业名称", example = "华为") + private String name; + + @Schema(description = "往来企业类型", example = "2") + @InEnum(value = WmsMerchantTypeEnum.class, message = "往来企业类型必须是 {value}") + private Integer type; + + @Schema(description = "往来企业类型数组", example = "2,3") + @InEnum(value = WmsMerchantTypeEnum.class, message = "往来企业类型必须是 {value}") + private List types; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantPageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantPageReqVO.java new file mode 100644 index 000000000..eb71ab00b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantPageReqVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.enums.md.WmsMerchantTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +@Schema(description = "管理后台 - WMS 往来企业分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsMerchantPageReqVO extends PageParam { + + @Schema(description = "往来企业编号", example = "MER001") + private String code; + + @Schema(description = "往来企业名称", example = "华为") + private String name; + + @Schema(description = "往来企业类型", example = "2") + @InEnum(value = WmsMerchantTypeEnum.class, message = "往来企业类型必须是 {value}") + private Integer type; + + @Schema(description = "往来企业类型数组", example = "2,3") + @InEnum(value = WmsMerchantTypeEnum.class, message = "往来企业类型必须是 {value}") + private List types; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantRespVO.java new file mode 100644 index 000000000..c88c8765e --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantRespVO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 往来企业 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsMerchantRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "往来企业编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "MER001") + @ExcelProperty("往来企业编号") + private String code; + + @Schema(description = "往来企业名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "华为") + @ExcelProperty("往来企业名称") + private String name; + + @Schema(description = "往来企业类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty(value = "往来企业类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.MERCHANT_TYPE) + private Integer type; + + @Schema(description = "级别", example = "A") + @ExcelProperty("级别") + private String level; + + @Schema(description = "开户行", example = "招商银行") + private String bankName; + + @Schema(description = "银行账户", example = "6225888888888888") + private String bankAccount; + + @Schema(description = "地址", example = "深圳市南山区") + private String address; + + @Schema(description = "手机号", example = "15601691300") + private String mobile; + + @Schema(description = "座机号", example = "0755-88888888") + private String telephone; + + @Schema(description = "联系人", example = "王五") + @ExcelProperty("联系人") + private String contact; + + @Schema(description = "Email", example = "wms@example.com") + private String email; + + @Schema(description = "备注", example = "备注") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantSaveReqVO.java new file mode 100644 index 000000000..5803a1d32 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/merchant/vo/WmsMerchantSaveReqVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.enums.md.WmsMerchantTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Schema(description = "管理后台 - WMS 往来企业创建/更新 Request VO") +@Data +public class WmsMerchantSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "往来企业编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "MER001") + @NotBlank(message = "往来企业编号不能为空") + @Size(max = 20, message = "往来企业编号长度不能超过 20 个字符") + private String code; + + @Schema(description = "往来企业名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "华为") + @NotBlank(message = "往来企业名称不能为空") + @Size(max = 60, message = "往来企业名称长度不能超过 60 个字符") + private String name; + + @Schema(description = "往来企业类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "往来企业类型不能为空") + @InEnum(value = WmsMerchantTypeEnum.class, message = "往来企业类型必须是 {value}") + private Integer type; + + @Schema(description = "级别", example = "A") + @Size(max = 10, message = "级别长度不能超过 10 个字符") + private String level; + + @Schema(description = "开户行", example = "招商银行") + @Size(max = 255, message = "开户行长度不能超过 255 个字符") + private String bankName; + + @Schema(description = "银行账户", example = "6225888888888888") + @Size(max = 40, message = "银行账户长度不能超过 40 个字符") + private String bankAccount; + + @Schema(description = "地址", example = "深圳市南山区") + @Size(max = 200, message = "地址长度不能超过 200 个字符") + private String address; + + @Schema(description = "手机号", example = "15601691300") + @Size(max = 13, message = "手机号长度不能超过 13 个字符") + private String mobile; + + @Schema(description = "座机号", example = "0755-88888888") + @Size(max = 13, message = "座机号长度不能超过 13 个字符") + private String telephone; + + @Schema(description = "联系人", example = "王五") + @Size(max = 30, message = "联系人长度不能超过 30 个字符") + private String contact; + + @Schema(description = "Email", example = "wms@example.com") + @Size(max = 50, message = "Email 长度不能超过 50 个字符") + private String email; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/WmsWarehouseController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/WmsWarehouseController.java new file mode 100644 index 000000000..fcb198137 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/WmsWarehouseController.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.warehouse; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehousePageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehouseRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehouseSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - WMS 仓库") +@RestController +@RequestMapping("/wms/warehouse") +@Validated +public class WmsWarehouseController { + + @Resource + private WmsWarehouseService warehouseService; + + @PostMapping("/create") + @Operation(summary = "创建仓库") + @PreAuthorize("@ss.hasPermission('wms:warehouse:create')") + public CommonResult createWarehouse(@Valid @RequestBody WmsWarehouseSaveReqVO createReqVO) { + return success(warehouseService.createWarehouse(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新仓库") + @PreAuthorize("@ss.hasPermission('wms:warehouse:update')") + public CommonResult updateWarehouse(@Valid @RequestBody WmsWarehouseSaveReqVO updateReqVO) { + warehouseService.updateWarehouse(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除仓库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:warehouse:delete')") + public CommonResult deleteWarehouse(@RequestParam("id") Long id) { + warehouseService.deleteWarehouse(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得仓库") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:warehouse:query')") + public CommonResult getWarehouse(@RequestParam("id") Long id) { + WmsWarehouseDO warehouse = warehouseService.getWarehouse(id); + return success(BeanUtils.toBean(warehouse, WmsWarehouseRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得仓库分页") + @PreAuthorize("@ss.hasPermission('wms:warehouse:query')") + public CommonResult> getWarehousePage(@Valid WmsWarehousePageReqVO pageReqVO) { + PageResult pageResult = warehouseService.getWarehousePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, WmsWarehouseRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得仓库精简列表", description = "主要用于前端下拉") + @PreAuthorize("@ss.hasPermission('wms:warehouse:query')") + public CommonResult> getWarehouseSimpleList() { + List list = warehouseService.getWarehouseList(); + return success(BeanUtils.toBean(list, WmsWarehouseRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出仓库 Excel") + @PreAuthorize("@ss.hasPermission('wms:warehouse:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportWarehouseExcel(@Valid WmsWarehousePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = warehouseService.getWarehousePage(pageReqVO).getList(); + ExcelUtils.write(response, "仓库.xls", "数据", WmsWarehouseRespVO.class, + BeanUtils.toBean(list, WmsWarehouseRespVO.class)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/vo/WmsWarehousePageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/vo/WmsWarehousePageReqVO.java new file mode 100644 index 000000000..43fcf5ca6 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/vo/WmsWarehousePageReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - WMS 仓库分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsWarehousePageReqVO extends PageParam { + + @Schema(description = "仓库编号", example = "WH001") + private String code; + + @Schema(description = "仓库名称", example = "原料仓") + private String name; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/vo/WmsWarehouseRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/vo/WmsWarehouseRespVO.java new file mode 100644 index 000000000..67c16bf95 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/vo/WmsWarehouseRespVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 仓库 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsWarehouseRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "仓库编号", example = "WH001") + @ExcelProperty("仓库编号") + private String code; + + @Schema(description = "仓库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "原料仓") + @ExcelProperty("仓库名称") + private String name; + + @Schema(description = "备注", example = "备注") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("排序") + private Integer sort; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/vo/WmsWarehouseSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/vo/WmsWarehouseSaveReqVO.java new file mode 100644 index 000000000..cb2da871b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/warehouse/vo/WmsWarehouseSaveReqVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Schema(description = "管理后台 - WMS 仓库新增/修改 Request VO") +@Data +public class WmsWarehouseSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "WH001") + @NotEmpty(message = "仓库编号不能为空") + @Size(max = 20, message = "仓库编号长度不能超过 20 个字符") + private String code; + + @Schema(description = "仓库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "原料仓") + @NotEmpty(message = "仓库名称不能为空") + private String name; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "排序不能为空") + private Integer sort; + + @Schema(description = "备注", example = "备注") + private String remark; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderController.java new file mode 100644 index 000000000..9c0cd0944 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderController.java @@ -0,0 +1,198 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.check.WmsCheckOrderDetailService; +import cn.iocoder.yudao.module.wms.service.order.check.WmsCheckOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +@Tag(name = "管理后台 - WMS 盘库单") +@RestController +@RequestMapping("/wms/check-order") +@Validated +public class WmsCheckOrderController { + + @Resource + private WmsCheckOrderService checkOrderService; + @Resource + private WmsCheckOrderDetailService checkOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建盘库单") + @PreAuthorize("@ss.hasPermission('wms:check-order:create')") + public CommonResult createCheckOrder(@Valid @RequestBody WmsCheckOrderSaveReqVO createReqVO) { + return success(checkOrderService.createCheckOrder(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新盘库单") + @PreAuthorize("@ss.hasPermission('wms:check-order:update')") + public CommonResult updateCheckOrder(@Valid @RequestBody WmsCheckOrderSaveReqVO updateReqVO) { + checkOrderService.updateCheckOrder(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除盘库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:check-order:delete')") + public CommonResult deleteCheckOrder(@RequestParam("id") Long id) { + checkOrderService.deleteCheckOrder(id); + return success(true); + } + + @PutMapping("/complete") + @Operation(summary = "完成盘库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:check-order:complete')") + public CommonResult completeCheckOrder(@RequestParam("id") Long id) { + checkOrderService.completeCheckOrder(id); + return success(true); + } + + @PutMapping("/cancel") + @Operation(summary = "作废盘库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:check-order:cancel')") + public CommonResult cancelCheckOrder(@RequestParam("id") Long id) { + checkOrderService.cancelCheckOrder(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得盘库单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:check-order:query')") + public CommonResult getCheckOrder(@RequestParam("id") Long id) { + WmsCheckOrderDO order = checkOrderService.getCheckOrder(id); + if (order == null) { + return success(null); + } + // 获得盘库单的明细列表 + List detailList = checkOrderDetailService.getCheckOrderDetailList(id); + // 拼接结果返回 + WmsCheckOrderRespVO respVO = buildCheckOrderRespVO(order) + .setDetails(buildCheckOrderDetailRespVOList(detailList)); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "获得盘库单分页") + @PreAuthorize("@ss.hasPermission('wms:check-order:query')") + public CommonResult> getCheckOrderPage(@Valid WmsCheckOrderPageReqVO pageReqVO) { + PageResult pageResult = checkOrderService.getCheckOrderPage(pageReqVO); + return success(new PageResult<>(buildCheckOrderRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出盘库单 Excel") + @PreAuthorize("@ss.hasPermission('wms:check-order:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportCheckOrderExcel(@Valid WmsCheckOrderPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = checkOrderService.getCheckOrderPage(pageReqVO).getList(); + ExcelUtils.write(response, "盘库单.xls", "数据", WmsCheckOrderRespVO.class, + buildCheckOrderRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private WmsCheckOrderRespVO buildCheckOrderRespVO(WmsCheckOrderDO order) { + if (order == null) { + return null; + } + List list = buildCheckOrderRespVOList(Collections.singletonList(order)); + return CollUtil.getFirst(list); + } + + private List buildCheckOrderRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的仓库、用户等数据 + Map warehouseMap = warehouseService.getWarehouseMap(convertSet(list, WmsCheckOrderDO::getWarehouseId)); + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(list, + order -> Stream.of(parseUserId(order.getCreator()), parseUserId(order.getUpdater())))); + // 拼接数据 + return BeanUtils.toBean(list, WmsCheckOrderRespVO.class, vo -> { + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + MapUtils.findAndThen(userMap, parseUserId(vo.getCreator()), user -> vo.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, parseUserId(vo.getUpdater()), user -> vo.setUpdaterName(user.getNickname())); + }); + } + + private Long parseUserId(String userId) { + return NumberUtil.parseLong(userId, null); + } + + private List buildCheckOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的商品、SKU、仓库等数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsCheckOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsCheckOrderDetailDO::getWarehouseId)); + // 拼接数据 + return BeanUtils.toBean(list, WmsCheckOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderDetailController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderDetailController.java new file mode 100644 index 000000000..25198254f --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderDetailController.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.check.WmsCheckOrderDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - WMS 盘库单明细") +@RestController +@RequestMapping("/wms/check-order-detail") +@Validated +public class WmsCheckOrderDetailController { + + @Resource + private WmsCheckOrderDetailService checkOrderDetailService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseService warehouseService; + + @GetMapping("/list-by-order-id") + @Operation(summary = "获得盘库单明细列表") + @Parameter(name = "orderId", description = "盘库单编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:check-order:query')") + public CommonResult> getCheckOrderDetailListByOrderId( + @RequestParam("orderId") Long orderId) { + List list = checkOrderDetailService.getCheckOrderDetailList(orderId); + return success(buildCheckOrderDetailRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private List buildCheckOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 查询关联数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsCheckOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsCheckOrderDetailDO::getWarehouseId)); + // 拼接数据 + return BeanUtils.toBean(list, WmsCheckOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailRespVO.java new file mode 100644 index 000000000..399caa3f9 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailRespVO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 盘库单明细 Response VO") +@Data +public class WmsCheckOrderDetailRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "盘库单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderId; + + @Schema(description = "商品编号", example = "2048") + private Long itemId; + + @Schema(description = "商品编号", example = "SPU-APPLE") + private String itemCode; + + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + + @Schema(description = "商品单位", example = "箱") + private String unit; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long skuId; + + @Schema(description = "规格编号", example = "SKU-APPLE-10KG") + private String skuCode; + + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "北京仓") + private String warehouseName; + + @Schema(description = "库存编号", example = "1024") + private Long inventoryId; + + @Schema(description = "入库时间") + private LocalDateTime receiptTime; + + @Schema(description = "账面数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal quantity; + + @Schema(description = "实盘数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "98.00") + private BigDecimal checkQuantity; + + @Schema(description = "单价", example = "1000.00") + private BigDecimal price; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailSaveReqVO.java new file mode 100644 index 000000000..709ddba9d --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailSaveReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 盘库单明细保存 Request VO") +@Data +public class WmsCheckOrderDetailSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "SKU 不能为空") + private Long skuId; + + @Schema(description = "库存编号", example = "1024") + private Long inventoryId; + + @Schema(description = "入库时间") + private LocalDateTime receiptTime; + + @Schema(description = "账面数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "账面数量不能为空") + @DecimalMin(value = "0", message = "账面数量不能小于 0") + private BigDecimal quantity; + + @Schema(description = "实盘数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "98.00") + @NotNull(message = "实盘数量不能为空") + @DecimalMin(value = "0", message = "实盘数量不能小于 0") + private BigDecimal checkQuantity; + + @Schema(description = "单价", example = "1000.00") + @DecimalMin(value = "0", message = "单价不能小于 0") + private BigDecimal price; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderPageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderPageReqVO.java new file mode 100644 index 000000000..2182fc69b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderPageReqVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - WMS 盘库单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsCheckOrderPageReqVO extends PageParam { + + @Schema(description = "盘库单号", example = "PK202605110001") + private String no; + + @Schema(description = "单据状态", example = "0") + @InEnum(WmsOrderStatusEnum.class) + private Integer status; + + @Schema(description = "仓库编号", example = "1024") + private Long warehouseId; + + @Schema(description = "单据日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] orderTime; + + @Schema(description = "最小盈亏数量", example = "-10.00") + private BigDecimal totalQuantityMin; + + @Schema(description = "最大盈亏数量", example = "100.00") + private BigDecimal totalQuantityMax; + + @Schema(description = "最小总金额", example = "1.00") + private BigDecimal totalPriceMin; + + @Schema(description = "最大总金额", example = "1000.00") + private BigDecimal totalPriceMax; + + @Schema(description = "最小实际金额", example = "1.00") + private BigDecimal actualPriceMin; + + @Schema(description = "最大实际金额", example = "1000.00") + private BigDecimal actualPriceMax; + + @Schema(description = "创建用户", example = "1") + private String creator; + + @Schema(description = "更新用户", example = "1") + private String updater; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "更新时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] updateTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderRespVO.java new file mode 100644 index 000000000..962de8235 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderRespVO.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 盘库单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsCheckOrderRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "盘库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "PK202605110001") + @ExcelProperty("盘库单号") + private String no; + + @Schema(description = "单据日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("单据日期") + private LocalDateTime orderTime; + + @Schema(description = "盘库状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @ExcelProperty(value = "盘库状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.ORDER_STATUS) + private Integer status; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "北京仓") + @ExcelProperty("仓库") + private String warehouseName; + + @Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @ExcelProperty("盈亏数量") + private BigDecimal totalQuantity; + + @Schema(description = "总金额(账面金额)", example = "1000.00") + @ExcelProperty("总金额") + private BigDecimal totalPrice; + + @Schema(description = "实际金额", example = "980.00") + @ExcelProperty("实际金额") + private BigDecimal actualPrice; + + @Schema(description = "盘库明细") + private List details; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "创建者", example = "1") + private String creator; + @Schema(description = "创建者名称", example = "芋道") + private String creatorName; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "更新者", example = "1") + private String updater; + @Schema(description = "更新者名称", example = "芋道") + private String updaterName; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderSaveReqVO.java new file mode 100644 index 000000000..21660ce87 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderSaveReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order; + +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailSaveReqVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 盘库单保存 Request VO") +@Data +public class WmsCheckOrderSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "盘库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "PK202605110001") + @NotBlank(message = "盘库单号不能为空") + @Size(max = 64, message = "盘库单号长度不能超过 64 个字符") + private String no; + + @Schema(description = "单据日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "单据日期不能为空") + private LocalDateTime orderTime; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "仓库不能为空") + private Long warehouseId; + + @Schema(description = "盘库明细") + @Valid + private List details; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderController.java new file mode 100644 index 000000000..d993392b9 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderController.java @@ -0,0 +1,206 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.movement.WmsMovementOrderDetailService; +import cn.iocoder.yudao.module.wms.service.order.movement.WmsMovementOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +@Tag(name = "管理后台 - WMS 移库单") +@RestController +@RequestMapping("/wms/movement-order") +@Validated +public class WmsMovementOrderController { + + @Resource + private WmsMovementOrderService movementOrderService; + @Resource + private WmsMovementOrderDetailService movementOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建移库单") + @PreAuthorize("@ss.hasPermission('wms:movement-order:create')") + public CommonResult createMovementOrder(@Valid @RequestBody WmsMovementOrderSaveReqVO createReqVO) { + return success(movementOrderService.createMovementOrder(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新移库单") + @PreAuthorize("@ss.hasPermission('wms:movement-order:update')") + public CommonResult updateMovementOrder(@Valid @RequestBody WmsMovementOrderSaveReqVO updateReqVO) { + movementOrderService.updateMovementOrder(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除移库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:movement-order:delete')") + public CommonResult deleteMovementOrder(@RequestParam("id") Long id) { + movementOrderService.deleteMovementOrder(id); + return success(true); + } + + @PutMapping("/complete") + @Operation(summary = "完成移库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:movement-order:complete')") + public CommonResult completeMovementOrder(@RequestParam("id") Long id) { + movementOrderService.completeMovementOrder(id); + return success(true); + } + + @PutMapping("/cancel") + @Operation(summary = "作废移库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:movement-order:cancel')") + public CommonResult cancelMovementOrder(@RequestParam("id") Long id) { + movementOrderService.cancelMovementOrder(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得移库单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:movement-order:query')") + public CommonResult getMovementOrder(@RequestParam("id") Long id) { + WmsMovementOrderDO order = movementOrderService.getMovementOrder(id); + if (order == null) { + return success(null); + } + // 获得移库单的明细列表 + List detailList = movementOrderDetailService.getMovementOrderDetailList(id); + // 拼接结果返回 + WmsMovementOrderRespVO respVO = buildMovementOrderRespVO(order) + .setDetails(buildMovementOrderDetailRespVOList(detailList)); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "获得移库单分页") + @PreAuthorize("@ss.hasPermission('wms:movement-order:query')") + public CommonResult> getMovementOrderPage( + @Valid WmsMovementOrderPageReqVO pageReqVO) { + PageResult pageResult = movementOrderService.getMovementOrderPage(pageReqVO); + return success(new PageResult<>(buildMovementOrderRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出移库单 Excel") + @PreAuthorize("@ss.hasPermission('wms:movement-order:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportMovementOrderExcel(@Valid WmsMovementOrderPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = movementOrderService.getMovementOrderPage(pageReqVO).getList(); + ExcelUtils.write(response, "移库单.xls", "数据", WmsMovementOrderRespVO.class, + buildMovementOrderRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private WmsMovementOrderRespVO buildMovementOrderRespVO(WmsMovementOrderDO order) { + if (order == null) { + return null; + } + List list = buildMovementOrderRespVOList(Collections.singletonList(order)); + return CollUtil.getFirst(list); + } + + private List buildMovementOrderRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的仓库、用户等数据 + Map warehouseMap = warehouseService.getWarehouseMap(convertSetByFlatMap(list, + order -> Stream.of(order.getSourceWarehouseId(), order.getTargetWarehouseId()))); + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(list, + order -> Stream.of(parseUserId(order.getCreator()), parseUserId(order.getUpdater())))); + // 拼接数据 + return BeanUtils.toBean(list, WmsMovementOrderRespVO.class, vo -> { + MapUtils.findAndThen(warehouseMap, vo.getSourceWarehouseId(), + warehouse -> vo.setSourceWarehouseName(warehouse.getName())); + MapUtils.findAndThen(warehouseMap, vo.getTargetWarehouseId(), + warehouse -> vo.setTargetWarehouseName(warehouse.getName())); + MapUtils.findAndThen(userMap, parseUserId(vo.getCreator()), user -> vo.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, parseUserId(vo.getUpdater()), user -> vo.setUpdaterName(user.getNickname())); + }); + } + + private Long parseUserId(String userId) { + return NumberUtil.parseLong(userId, null); + } + + private List buildMovementOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的商品、SKU、仓库等数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsMovementOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap(convertSetByFlatMap(list, + detail -> Stream.of(detail.getSourceWarehouseId(), detail.getTargetWarehouseId()))); + // 拼接数据 + return BeanUtils.toBean(list, WmsMovementOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getSourceWarehouseId(), + warehouse -> vo.setSourceWarehouseName(warehouse.getName())); + MapUtils.findAndThen(warehouseMap, vo.getTargetWarehouseId(), + warehouse -> vo.setTargetWarehouseName(warehouse.getName())); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderDetailController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderDetailController.java new file mode 100644 index 000000000..092a367fd --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderDetailController.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.movement.WmsMovementOrderDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +@Tag(name = "管理后台 - WMS 移库单明细") +@RestController +@RequestMapping("/wms/movement-order-detail") +@Validated +public class WmsMovementOrderDetailController { + + @Resource + private WmsMovementOrderDetailService movementOrderDetailService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseService warehouseService; + + @GetMapping("/list-by-order-id") + @Operation(summary = "获得移库单明细列表") + @Parameter(name = "orderId", description = "移库单编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:movement-order:query')") + public CommonResult> getMovementOrderDetailListByOrderId( + @RequestParam("orderId") Long orderId) { + List list = movementOrderDetailService.getMovementOrderDetailList(orderId); + return success(buildMovementOrderDetailRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private List buildMovementOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 查询关联数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsMovementOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap(convertSetByFlatMap(list, + detail -> Stream.of(detail.getSourceWarehouseId(), detail.getTargetWarehouseId()))); + // 拼接数据 + return BeanUtils.toBean(list, WmsMovementOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getSourceWarehouseId(), + warehouse -> vo.setSourceWarehouseName(warehouse.getName())); + MapUtils.findAndThen(warehouseMap, vo.getTargetWarehouseId(), + warehouse -> vo.setTargetWarehouseName(warehouse.getName())); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailRespVO.java new file mode 100644 index 000000000..513653460 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailRespVO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 移库单明细 Response VO") +@Data +public class WmsMovementOrderDetailRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "移库单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderId; + + @Schema(description = "商品编号", example = "2048") + private Long itemId; + + @Schema(description = "商品编号", example = "SPU-APPLE") + private String itemCode; + + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + + @Schema(description = "商品单位", example = "箱") + private String unit; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long skuId; + + @Schema(description = "规格编号", example = "SKU-APPLE-10KG") + private String skuCode; + + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "来源仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long sourceWarehouseId; + + @Schema(description = "来源仓库名称", example = "北京仓") + private String sourceWarehouseName; + + @Schema(description = "目标仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long targetWarehouseId; + + @Schema(description = "目标仓库名称", example = "上海仓") + private String targetWarehouseName; + + @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal quantity; + + @Schema(description = "单价", example = "100.00") + private BigDecimal price; + + @Schema(description = "行金额", example = "1000.00") + private BigDecimal totalPrice; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailSaveReqVO.java new file mode 100644 index 000000000..28b412edf --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailSaveReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - WMS 移库单明细保存 Request VO") +@Data +public class WmsMovementOrderDetailSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "SKU 不能为空") + private Long skuId; + + @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "移库数量不能为空") + @DecimalMin(value = "0", inclusive = false, message = "移库数量必须大于 0") + private BigDecimal quantity; + + @Schema(description = "单价", example = "100.00") + @DecimalMin(value = "0", message = "单价不能小于 0") + private BigDecimal price; + + @Schema(description = "行金额", example = "1000.00") + @DecimalMin(value = "0", message = "行金额不能小于 0") + private BigDecimal totalPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderPageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderPageReqVO.java new file mode 100644 index 000000000..c59bc113a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderPageReqVO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - WMS 移库单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsMovementOrderPageReqVO extends PageParam { + + @Schema(description = "移库单号", example = "YK202605110001") + private String no; + + @Schema(description = "单据状态", example = "0") + @InEnum(WmsOrderStatusEnum.class) + private Integer status; + + @Schema(description = "来源仓库编号", example = "1024") + private Long sourceWarehouseId; + + @Schema(description = "目标仓库编号", example = "2048") + private Long targetWarehouseId; + + @Schema(description = "单据日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] orderTime; + + @Schema(description = "最小数量", example = "1.00") + private BigDecimal totalQuantityMin; + + @Schema(description = "最大数量", example = "100.00") + private BigDecimal totalQuantityMax; + + @Schema(description = "最小总金额", example = "1.00") + private BigDecimal totalPriceMin; + + @Schema(description = "最大总金额", example = "1000.00") + private BigDecimal totalPriceMax; + + @Schema(description = "创建用户", example = "1") + private String creator; + + @Schema(description = "更新用户", example = "1") + private String updater; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "更新时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] updateTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderRespVO.java new file mode 100644 index 000000000..ecad02e05 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderRespVO.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 移库单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsMovementOrderRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "移库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "YK202605110001") + @ExcelProperty("移库单号") + private String no; + + @Schema(description = "单据日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("单据日期") + private LocalDateTime orderTime; + + @Schema(description = "移库状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @ExcelProperty(value = "移库状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.ORDER_STATUS) + private Integer status; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "来源仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long sourceWarehouseId; + + @Schema(description = "来源仓库名称", example = "北京仓") + @ExcelProperty("来源仓库") + private String sourceWarehouseName; + + @Schema(description = "目标仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long targetWarehouseId; + + @Schema(description = "目标仓库名称", example = "上海仓") + @ExcelProperty("目标仓库") + private String targetWarehouseName; + + @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @ExcelProperty("移库数量") + private BigDecimal totalQuantity; + + @Schema(description = "总金额", example = "1000.00") + @ExcelProperty("总金额") + private BigDecimal totalPrice; + + @Schema(description = "移库明细") + private List details; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "创建者", example = "1") + private String creator; + @Schema(description = "创建者名称", example = "芋道") + private String creatorName; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "更新者", example = "1") + private String updater; + @Schema(description = "更新者名称", example = "芋道") + private String updaterName; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderSaveReqVO.java new file mode 100644 index 000000000..982d30dff --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderSaveReqVO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order; + +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailSaveReqVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 移库单保存 Request VO") +@Data +public class WmsMovementOrderSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "移库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "YK202605110001") + @NotBlank(message = "移库单号不能为空") + @Size(max = 64, message = "移库单号长度不能超过 64 个字符") + private String no; + + @Schema(description = "单据日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "单据日期不能为空") + private LocalDateTime orderTime; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + + @Schema(description = "来源仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "来源仓库不能为空") + private Long sourceWarehouseId; + + @Schema(description = "目标仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "目标仓库不能为空") + private Long targetWarehouseId; + + @Schema(description = "移库明细") + @Valid + private List details; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderController.java new file mode 100644 index 000000000..c1a2fe6d4 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderController.java @@ -0,0 +1,204 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderDetailService; +import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +@Tag(name = "管理后台 - WMS 入库单") +@RestController +@RequestMapping("/wms/receipt-order") +@Validated +public class WmsReceiptOrderController { + + @Resource + private WmsReceiptOrderService receiptOrderService; + @Resource + private WmsReceiptOrderDetailService receiptOrderDetailService; + @Resource + private WmsMerchantService merchantService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建入库单") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:create')") + public CommonResult createReceiptOrder(@Valid @RequestBody WmsReceiptOrderSaveReqVO createReqVO) { + return success(receiptOrderService.createReceiptOrder(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新入库单") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:update')") + public CommonResult updateReceiptOrder(@Valid @RequestBody WmsReceiptOrderSaveReqVO updateReqVO) { + receiptOrderService.updateReceiptOrder(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除入库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:receipt-order:delete')") + public CommonResult deleteReceiptOrder(@RequestParam("id") Long id) { + receiptOrderService.deleteReceiptOrder(id); + return success(true); + } + + @PutMapping("/complete") + @Operation(summary = "完成入库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:receipt-order:complete')") + public CommonResult completeReceiptOrder(@RequestParam("id") Long id) { + receiptOrderService.completeReceiptOrder(id); + return success(true); + } + + @PutMapping("/cancel") + @Operation(summary = "作废入库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:receipt-order:cancel')") + public CommonResult cancelReceiptOrder(@RequestParam("id") Long id) { + receiptOrderService.cancelReceiptOrder(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得入库单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:query')") + public CommonResult getReceiptOrder(@RequestParam("id") Long id) { + WmsReceiptOrderDO order = receiptOrderService.getReceiptOrder(id); + if (order == null) { + return success(null); + } + // 获得入库单的明细列表 + List detailList = receiptOrderDetailService.getReceiptOrderDetailList(id); + // 拼接结果返回 + WmsReceiptOrderRespVO respVO = buildReceiptOrderRespVO(order) + .setDetails(buildReceiptOrderDetailRespVOList(detailList)); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "获得入库单分页") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:query')") + public CommonResult> getReceiptOrderPage(@Valid WmsReceiptOrderPageReqVO pageReqVO) { + PageResult pageResult = receiptOrderService.getReceiptOrderPage(pageReqVO); + return success(new PageResult<>(buildReceiptOrderRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出入库单 Excel") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportReceiptOrderExcel(@Valid WmsReceiptOrderPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = receiptOrderService.getReceiptOrderPage(pageReqVO).getList(); + ExcelUtils.write(response, "入库单.xls", "数据", WmsReceiptOrderRespVO.class, + buildReceiptOrderRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private WmsReceiptOrderRespVO buildReceiptOrderRespVO(WmsReceiptOrderDO order) { + if (order == null) { + return null; + } + List list = buildReceiptOrderRespVOList(Collections.singletonList(order)); + return CollUtil.getFirst(list); + } + + private List buildReceiptOrderRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的商户、仓库、用户等数据 + Map merchantMap = merchantService.getMerchantMap(convertSet(list, WmsReceiptOrderDO::getMerchantId)); + Map warehouseMap = warehouseService.getWarehouseMap(convertSet(list, WmsReceiptOrderDO::getWarehouseId)); + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(list, + order -> Stream.of(parseUserId(order.getCreator()), parseUserId(order.getUpdater())))); + // 拼接数据 + return BeanUtils.toBean(list, WmsReceiptOrderRespVO.class, vo -> { + MapUtils.findAndThen(merchantMap, vo.getMerchantId(), merchant -> vo.setMerchantName(merchant.getName())); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + MapUtils.findAndThen(userMap, parseUserId(vo.getCreator()), user -> vo.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, parseUserId(vo.getUpdater()), user -> vo.setUpdaterName(user.getNickname())); + }); + } + + private Long parseUserId(String userId) { + return NumberUtil.parseLong(userId, null); + } + + private List buildReceiptOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的商品、SKU、仓库等数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsReceiptOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsReceiptOrderDetailDO::getWarehouseId)); + // 拼接数据 + return BeanUtils.toBean(list, WmsReceiptOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderDetailController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderDetailController.java new file mode 100644 index 000000000..d443feb2f --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderDetailController.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - WMS 入库单明细") +@RestController +@RequestMapping("/wms/receipt-order-detail") +@Validated +public class WmsReceiptOrderDetailController { + + @Resource + private WmsReceiptOrderDetailService receiptOrderDetailService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseService warehouseService; + + @GetMapping("/list-by-order-id") + @Operation(summary = "获得入库单明细列表") + @Parameter(name = "orderId", description = "入库单编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:query')") + public CommonResult> getReceiptOrderDetailListByOrderId( + @RequestParam("orderId") Long orderId) { + List list = receiptOrderDetailService.getReceiptOrderDetailList(orderId); + return success(buildReceiptOrderDetailRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private List buildReceiptOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 查询关联数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsReceiptOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsReceiptOrderDetailDO::getWarehouseId)); + // 拼接数据 + return BeanUtils.toBean(list, WmsReceiptOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailRespVO.java new file mode 100644 index 000000000..4c72b9324 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailRespVO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 入库单明细 Response VO") +@Data +public class WmsReceiptOrderDetailRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "入库单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderId; + + @Schema(description = "商品编号", example = "2048") + private Long itemId; + + @Schema(description = "商品编号", example = "SPU-APPLE") + private String itemCode; + + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + + @Schema(description = "商品单位", example = "箱") + private String unit; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long skuId; + + @Schema(description = "规格编号", example = "SKU-APPLE-10KG") + private String skuCode; + + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "北京仓") + private String warehouseName; + + @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal quantity; + + @Schema(description = "单价", example = "1000.00") + private BigDecimal price; + + @Schema(description = "行金额", example = "1500.00") + private BigDecimal totalPrice; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailSaveReqVO.java new file mode 100644 index 000000000..a74cc905d --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailSaveReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - WMS 入库单明细保存 Request VO") +@Data +public class WmsReceiptOrderDetailSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "SKU 不能为空") + private Long skuId; + + @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "入库数量不能为空") + @DecimalMin(value = "0", inclusive = false, message = "入库数量必须大于 0") + private BigDecimal quantity; + + @Schema(description = "单价", example = "1000.00") + @DecimalMin(value = "0", message = "单价不能小于 0") + private BigDecimal price; + + @Schema(description = "行金额", example = "1500.00") + @DecimalMin(value = "0", message = "行金额不能小于 0") + private BigDecimal totalPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderPageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderPageReqVO.java new file mode 100644 index 000000000..351d15004 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderPageReqVO.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - WMS 入库单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsReceiptOrderPageReqVO extends PageParam { + + @Schema(description = "入库单号", example = "RK202605110001") + private String no; + + @Schema(description = "单据状态", example = "0") + @InEnum(WmsOrderStatusEnum.class) + private Integer status; + + @Schema(description = "仓库编号", example = "1024") + private Long warehouseId; + + @Schema(description = "供应商编号", example = "1024") + private Long merchantId; + + @Schema(description = "单据日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] orderTime; + + @Schema(description = "最小数量", example = "1.00") + private BigDecimal totalQuantityMin; + + @Schema(description = "最大数量", example = "100.00") + private BigDecimal totalQuantityMax; + + @Schema(description = "最小总金额", example = "1.00") + private BigDecimal totalPriceMin; + + @Schema(description = "最大总金额", example = "1000.00") + private BigDecimal totalPriceMax; + + @Schema(description = "入库类型", example = "101") + @InEnum(WmsReceiptOrderTypeEnum.class) + private Integer type; + + @Schema(description = "业务单号", example = "PO202605110001") + private String bizOrderNo; + + @Schema(description = "创建用户", example = "1") + private String creator; + + @Schema(description = "更新用户", example = "1") + private String updater; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "更新时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] updateTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderRespVO.java new file mode 100644 index 000000000..24d3b0449 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderRespVO.java @@ -0,0 +1,91 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 入库单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsReceiptOrderRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "入库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "RK202605110001") + @ExcelProperty("入库单号") + private String no; + + @Schema(description = "入库类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "101") + @ExcelProperty(value = "入库类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.RECEIPT_ORDER_TYPE) + private Integer type; + + @Schema(description = "单据日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("单据日期") + private LocalDateTime orderTime; + + @Schema(description = "入库状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @ExcelProperty(value = "入库状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.ORDER_STATUS) + private Integer status; + + @Schema(description = "业务单号", example = "PO202605110001") + @ExcelProperty("业务单号") + private String bizOrderNo; + + @Schema(description = "供应商编号", example = "1024") + private Long merchantId; + + @Schema(description = "供应商名称", example = "某某公司") + @ExcelProperty("供应商") + private String merchantName; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "北京仓") + @ExcelProperty("仓库") + private String warehouseName; + + @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @ExcelProperty("入库数量") + private BigDecimal totalQuantity; + + @Schema(description = "总金额", example = "1000.00") + @ExcelProperty("总金额") + private BigDecimal totalPrice; + + @Schema(description = "入库明细") + private List details; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "创建者", example = "1") + private String creator; + @Schema(description = "创建者名称", example = "芋道") + private String creatorName; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "更新者", example = "1") + private String updater; + @Schema(description = "更新者名称", example = "芋道") + private String updaterName; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderSaveReqVO.java new file mode 100644 index 000000000..b8619fc41 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderSaveReqVO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 入库单保存 Request VO") +@Data +public class WmsReceiptOrderSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "入库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "RK202605110001") + @NotBlank(message = "入库单号不能为空") + @Size(max = 64, message = "入库单号长度不能超过 64 个字符") + private String no; + + @Schema(description = "入库类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "101") + @NotNull(message = "入库类型不能为空") + @InEnum(WmsReceiptOrderTypeEnum.class) + private Integer type; + + @Schema(description = "单据日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "单据日期不能为空") + private LocalDateTime orderTime; + + @Schema(description = "业务单号", example = "PO202605110001") + @Size(max = 64, message = "业务单号长度不能超过 64 个字符") + private String bizOrderNo; + + @Schema(description = "供应商编号", example = "1024") + private Long merchantId; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "仓库不能为空") + private Long warehouseId; + + @Schema(description = "入库明细") + @Valid + private List details; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/WmsShipmentOrderController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/WmsShipmentOrderController.java new file mode 100644 index 000000000..65e9153fb --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/WmsShipmentOrderController.java @@ -0,0 +1,204 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderDetailService; +import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +@Tag(name = "管理后台 - WMS 出库单") +@RestController +@RequestMapping("/wms/shipment-order") +@Validated +public class WmsShipmentOrderController { + + @Resource + private WmsShipmentOrderService shipmentOrderService; + @Resource + private WmsShipmentOrderDetailService shipmentOrderDetailService; + @Resource + private WmsMerchantService merchantService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建出库单") + @PreAuthorize("@ss.hasPermission('wms:shipment-order:create')") + public CommonResult createShipmentOrder(@Valid @RequestBody WmsShipmentOrderSaveReqVO createReqVO) { + return success(shipmentOrderService.createShipmentOrder(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新出库单") + @PreAuthorize("@ss.hasPermission('wms:shipment-order:update')") + public CommonResult updateShipmentOrder(@Valid @RequestBody WmsShipmentOrderSaveReqVO updateReqVO) { + shipmentOrderService.updateShipmentOrder(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除出库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:shipment-order:delete')") + public CommonResult deleteShipmentOrder(@RequestParam("id") Long id) { + shipmentOrderService.deleteShipmentOrder(id); + return success(true); + } + + @PutMapping("/complete") + @Operation(summary = "完成出库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:shipment-order:complete')") + public CommonResult completeShipmentOrder(@RequestParam("id") Long id) { + shipmentOrderService.completeShipmentOrder(id); + return success(true); + } + + @PutMapping("/cancel") + @Operation(summary = "作废出库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:shipment-order:cancel')") + public CommonResult cancelShipmentOrder(@RequestParam("id") Long id) { + shipmentOrderService.cancelShipmentOrder(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得出库单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:shipment-order:query')") + public CommonResult getShipmentOrder(@RequestParam("id") Long id) { + WmsShipmentOrderDO order = shipmentOrderService.getShipmentOrder(id); + if (order == null) { + return success(null); + } + // 获得出库单的明细列表 + List detailList = shipmentOrderDetailService.getShipmentOrderDetailList(id); + // 拼接结果返回 + WmsShipmentOrderRespVO respVO = buildShipmentOrderRespVO(order) + .setDetails(buildShipmentOrderDetailRespVOList(detailList)); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "获得出库单分页") + @PreAuthorize("@ss.hasPermission('wms:shipment-order:query')") + public CommonResult> getShipmentOrderPage(@Valid WmsShipmentOrderPageReqVO pageReqVO) { + PageResult pageResult = shipmentOrderService.getShipmentOrderPage(pageReqVO); + return success(new PageResult<>(buildShipmentOrderRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出出库单 Excel") + @PreAuthorize("@ss.hasPermission('wms:shipment-order:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportShipmentOrderExcel(@Valid WmsShipmentOrderPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = shipmentOrderService.getShipmentOrderPage(pageReqVO).getList(); + ExcelUtils.write(response, "出库单.xls", "数据", WmsShipmentOrderRespVO.class, + buildShipmentOrderRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private WmsShipmentOrderRespVO buildShipmentOrderRespVO(WmsShipmentOrderDO order) { + if (order == null) { + return null; + } + List list = buildShipmentOrderRespVOList(Collections.singletonList(order)); + return CollUtil.getFirst(list); + } + + private List buildShipmentOrderRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的商户、仓库、用户等数据 + Map merchantMap = merchantService.getMerchantMap(convertSet(list, WmsShipmentOrderDO::getMerchantId)); + Map warehouseMap = warehouseService.getWarehouseMap(convertSet(list, WmsShipmentOrderDO::getWarehouseId)); + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(list, + order -> Stream.of(parseUserId(order.getCreator()), parseUserId(order.getUpdater())))); + // 拼接数据 + return BeanUtils.toBean(list, WmsShipmentOrderRespVO.class, vo -> { + MapUtils.findAndThen(merchantMap, vo.getMerchantId(), merchant -> vo.setMerchantName(merchant.getName())); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + MapUtils.findAndThen(userMap, parseUserId(vo.getCreator()), user -> vo.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, parseUserId(vo.getUpdater()), user -> vo.setUpdaterName(user.getNickname())); + }); + } + + private Long parseUserId(String userId) { + return NumberUtil.parseLong(userId, null); + } + + private List buildShipmentOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的商品、SKU、仓库等数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsShipmentOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsShipmentOrderDetailDO::getWarehouseId)); + // 拼接数据 + return BeanUtils.toBean(list, WmsShipmentOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/WmsShipmentOrderDetailController.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/WmsShipmentOrderDetailController.java new file mode 100644 index 000000000..f9cd14dd7 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/WmsShipmentOrderDetailController.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - WMS 出库单明细") +@RestController +@RequestMapping("/wms/shipment-order-detail") +@Validated +public class WmsShipmentOrderDetailController { + + @Resource + private WmsShipmentOrderDetailService shipmentOrderDetailService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseService warehouseService; + + @GetMapping("/list-by-order-id") + @Operation(summary = "获得出库单明细列表") + @Parameter(name = "orderId", description = "出库单编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:shipment-order:query')") + public CommonResult> getShipmentOrderDetailListByOrderId( + @RequestParam("orderId") Long orderId) { + List list = shipmentOrderDetailService.getShipmentOrderDetailList(orderId); + return success(buildShipmentOrderDetailRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private List buildShipmentOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 查询关联数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsShipmentOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsShipmentOrderDetailDO::getWarehouseId)); + // 拼接数据 + return BeanUtils.toBean(list, WmsShipmentOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/detail/WmsShipmentOrderDetailRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/detail/WmsShipmentOrderDetailRespVO.java new file mode 100644 index 000000000..80e9538c1 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/detail/WmsShipmentOrderDetailRespVO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 出库单明细 Response VO") +@Data +public class WmsShipmentOrderDetailRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "出库单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderId; + + @Schema(description = "商品编号", example = "2048") + private Long itemId; + + @Schema(description = "商品编号", example = "SPU-APPLE") + private String itemCode; + + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + + @Schema(description = "商品单位", example = "箱") + private String unit; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long skuId; + + @Schema(description = "规格编号", example = "SKU-APPLE-10KG") + private String skuCode; + + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "北京仓") + private String warehouseName; + + @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal quantity; + + @Schema(description = "单价", example = "100.00") + private BigDecimal price; + + @Schema(description = "行金额", example = "1000.00") + private BigDecimal totalPrice; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/detail/WmsShipmentOrderDetailSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/detail/WmsShipmentOrderDetailSaveReqVO.java new file mode 100644 index 000000000..be8a8baeb --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/detail/WmsShipmentOrderDetailSaveReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - WMS 出库单明细保存 Request VO") +@Data +public class WmsShipmentOrderDetailSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "SKU 不能为空") + private Long skuId; + + @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "出库数量不能为空") + @DecimalMin(value = "0", inclusive = false, message = "出库数量必须大于 0") + private BigDecimal quantity; + + @Schema(description = "单价", example = "100.00") + @DecimalMin(value = "0", message = "单价不能小于 0") + private BigDecimal price; + + @Schema(description = "行金额", example = "1000.00") + @DecimalMin(value = "0", message = "行金额不能小于 0") + private BigDecimal totalPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderPageReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderPageReqVO.java new file mode 100644 index 000000000..2a9f058ba --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderPageReqVO.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsShipmentOrderTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - WMS 出库单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsShipmentOrderPageReqVO extends PageParam { + + @Schema(description = "出库单号", example = "CK202605110001") + private String no; + + @Schema(description = "单据状态", example = "0") + @InEnum(WmsOrderStatusEnum.class) + private Integer status; + + @Schema(description = "仓库编号", example = "1024") + private Long warehouseId; + + @Schema(description = "客户编号", example = "1024") + private Long merchantId; + + @Schema(description = "单据日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] orderTime; + + @Schema(description = "最小数量", example = "1.00") + private BigDecimal totalQuantityMin; + + @Schema(description = "最大数量", example = "100.00") + private BigDecimal totalQuantityMax; + + @Schema(description = "最小总金额", example = "1.00") + private BigDecimal totalPriceMin; + + @Schema(description = "最大总金额", example = "1000.00") + private BigDecimal totalPriceMax; + + @Schema(description = "出库类型", example = "201") + @InEnum(WmsShipmentOrderTypeEnum.class) + private Integer type; + + @Schema(description = "业务单号", example = "SO202605110001") + private String bizOrderNo; + + @Schema(description = "创建用户", example = "1") + private String creator; + + @Schema(description = "更新用户", example = "1") + private String updater; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "更新时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] updateTime; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderRespVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderRespVO.java new file mode 100644 index 000000000..b391c1d5e --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderRespVO.java @@ -0,0 +1,91 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 出库单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsShipmentOrderRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "出库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "CK202605110001") + @ExcelProperty("出库单号") + private String no; + + @Schema(description = "出库类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "201") + @ExcelProperty(value = "出库类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.SHIPMENT_ORDER_TYPE) + private Integer type; + + @Schema(description = "单据日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("单据日期") + private LocalDateTime orderTime; + + @Schema(description = "出库状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @ExcelProperty(value = "出库状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.ORDER_STATUS) + private Integer status; + + @Schema(description = "业务单号", example = "SO202605110001") + @ExcelProperty("业务单号") + private String bizOrderNo; + + @Schema(description = "客户编号", example = "1024") + private Long merchantId; + + @Schema(description = "客户名称", example = "某某公司") + @ExcelProperty("客户") + private String merchantName; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "北京仓") + @ExcelProperty("仓库") + private String warehouseName; + + @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @ExcelProperty("出库数量") + private BigDecimal totalQuantity; + + @Schema(description = "总金额", example = "1000.00") + @ExcelProperty("总金额") + private BigDecimal totalPrice; + + @Schema(description = "出库明细") + private List details; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "创建者", example = "1") + private String creator; + @Schema(description = "创建者名称", example = "芋道") + private String creatorName; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "更新者", example = "1") + private String updater; + @Schema(description = "更新者名称", example = "芋道") + private String updaterName; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderSaveReqVO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderSaveReqVO.java new file mode 100644 index 000000000..232b92dc0 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderSaveReqVO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.enums.order.WmsShipmentOrderTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 出库单保存 Request VO") +@Data +public class WmsShipmentOrderSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "出库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "CK202605110001") + @NotBlank(message = "出库单号不能为空") + @Size(max = 64, message = "出库单号长度不能超过 64 个字符") + private String no; + + @Schema(description = "出库类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "201") + @NotNull(message = "出库类型不能为空") + @InEnum(WmsShipmentOrderTypeEnum.class) + private Integer type; + + @Schema(description = "单据日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "单据日期不能为空") + private LocalDateTime orderTime; + + @Schema(description = "业务单号", example = "SO202605110001") + @Size(max = 64, message = "业务单号长度不能超过 64 个字符") + private String bizOrderNo; + + @Schema(description = "客户编号", example = "1024") + private Long merchantId; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "仓库不能为空") + private Long warehouseId; + + @Schema(description = "出库明细") + @Valid + private List details; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/package-info.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/package-info.java new file mode 100644 index 000000000..7f2a55f59 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/package-info.java @@ -0,0 +1,4 @@ +/** + * WMS 管理后台 API + */ +package cn.iocoder.yudao.module.wms.controller.admin; diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/package-info.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/package-info.java new file mode 100644 index 000000000..c75d9df62 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/controller/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 RESTful API 给前端: + * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目 + * 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 + */ +package cn.iocoder.yudao.module.wms.controller; diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryDO.java new file mode 100644 index 000000000..577af728d --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryDO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.inventory; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * WMS 库存 DO + * + * @author 芋道源码 + */ +@TableName("wms_inventory") +@KeySequence("wms_inventory_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsInventoryDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 商品 SKU 编号 + * + * 关联 {@link WmsItemSkuDO#getId()} + */ + private Long skuId; + /** + * 仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 库存数量 + */ + private BigDecimal quantity; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryHistoryDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryHistoryDO.java new file mode 100644 index 000000000..224940ade --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryHistoryDO.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.inventory; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * WMS 库存流水 DO + * + * @author 芋道源码 + */ +@TableName("wms_inventory_history") +@KeySequence("wms_inventory_history_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsInventoryHistoryDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + + // ========= 库存维度相关字段 ========= + + /** + * 仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 商品 SKU 编号 + * + * 关联 {@link WmsItemSkuDO#getId()} + */ + private Long skuId; + /** + * 库存变化数量 + */ + private BigDecimal quantity; + /** + * 变化前库存数量 + */ + private BigDecimal beforeQuantity; + /** + * 变化后库存数量 + */ + private BigDecimal afterQuantity; + + // ========= 单价备注相关字段 ========= + + /** + * 单价 + */ + private BigDecimal price; + /** + * 库存变化金额 + */ + private BigDecimal totalPrice; + /** + * 备注 + */ + private String remark; + + // ========= 来源单据相关字段 ========= + + /** + * 单据编号 + */ + private Long orderId; + /** + * 单据号 + */ + private String orderNo; + /** + * 单据类型 + * + * 枚举 {@link WmsOrderTypeEnum#getType()} + */ + private Integer orderType; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemBrandDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemBrandDO.java new file mode 100644 index 000000000..ab41e05f0 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemBrandDO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.md.item; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * WMS 商品品牌 DO + * + * @author 芋道源码 + */ +@TableName("wms_item_brand") +@KeySequence("wms_item_brand_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsItemBrandDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 品牌编号 + */ + private String code; + /** + * 品牌名称 + */ + private String name; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemCategoryDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemCategoryDO.java new file mode 100644 index 000000000..ded92281e --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemCategoryDO.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.md.item; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * WMS 商品分类 DO + * + * @author 芋道源码 + */ +@TableName("wms_item_category") +@KeySequence("wms_item_category_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsItemCategoryDO extends BaseDO { + + /** + * 父级编号 - 根节点 + */ + public static final Long PARENT_ID_ROOT = 0L; + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 父级编号 + * + * 关联 {@link #id} + */ + private Long parentId; + /** + * 分类编号 + */ + private String code; + /** + * 分类名称 + */ + private String name; + /** + * 排序 + */ + private Integer sort; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemDO.java new file mode 100644 index 000000000..9a14d3ac2 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemDO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.md.item; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * WMS 商品 DO + * + * @author 芋道源码 + */ +@TableName("wms_item") +@KeySequence("wms_item_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsItemDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 商品编号 + */ + private String code; + /** + * 商品名称 + */ + private String name; + /** + * 单位 + */ + private String unit; + /** + * 商品分类编号 + * + * 关联 {@link WmsItemCategoryDO#getId()} + */ + private Long categoryId; + /** + * 商品品牌编号 + * + * 关联 {@link WmsItemBrandDO#getId()} + */ + private Long brandId; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemSkuDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemSkuDO.java new file mode 100644 index 000000000..7236b4928 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemSkuDO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.md.item; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * WMS 商品 SKU DO + * + * @author 芋道源码 + */ +@TableName("wms_item_sku") +@KeySequence("wms_item_sku_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsItemSkuDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 规格名称 + */ + private String name; + /** + * 商品编号 + * + * 关联 {@link WmsItemDO#getId()} + */ + private Long itemId; + /** + * 条码 + */ + private String barCode; + /** + * 规格编号 + */ + private String code; + + /** + * 长,单位 cm + */ + private BigDecimal length; + /** + * 宽,单位 cm + */ + private BigDecimal width; + /** + * 高,单位 cm + */ + private BigDecimal height; + + /** + * 毛重,单位 kg + */ + private BigDecimal grossWeight; + /** + * 净重,单位 kg + */ + private BigDecimal netWeight; + + /** + * 成本价,单位元 + */ + private BigDecimal costPrice; + /** + * 销售价,单位元 + */ + private BigDecimal sellingPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/merchant/WmsMerchantDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/merchant/WmsMerchantDO.java new file mode 100644 index 000000000..60b4c4dbf --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/merchant/WmsMerchantDO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.enums.md.WmsMerchantTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * WMS 往来企业 DO + * + * @author 芋道源码 + */ +@TableName("wms_merchant") +@KeySequence("wms_merchant_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsMerchantDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 往来企业编号 + */ + private String code; + /** + * 往来企业名称 + */ + private String name; + /** + * 往来企业类型 + * + * 枚举 {@link WmsMerchantTypeEnum} + */ + private Integer type; + /** + * 级别 + */ + private String level; + /** + * 开户行 + */ + private String bankName; + /** + * 银行账户 + */ + private String bankAccount; + /** + * 地址 + */ + private String address; + /** + * 手机号 + */ + private String mobile; + /** + * 座机号 + */ + private String telephone; + /** + * 联系人 + */ + private String contact; + /** + * Email + */ + private String email; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/warehouse/WmsWarehouseDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/warehouse/WmsWarehouseDO.java new file mode 100644 index 000000000..e73c1123a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/warehouse/WmsWarehouseDO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * WMS 仓库 DO + * + * @author 芋道源码 + */ +@TableName("wms_warehouse") +@KeySequence("wms_warehouse_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsWarehouseDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 仓库编号 + */ + private String code; + /** + * 名称 + */ + private String name; + /** + * 备注 + */ + private String remark; + /** + * 排序 + */ + private Integer sort; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDO.java new file mode 100644 index 000000000..7ebe50a74 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDO.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.check; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * WMS 盘库单 DO + * + * @author 芋道源码 + */ +@TableName("wms_check_order") +@KeySequence("wms_check_order_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsCheckOrderDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 盘库单号 + */ + private String no; + /** + * 单据日期 + */ + private LocalDateTime orderTime; + /** + * 盘库状态 + * + * 字典 {@link DictTypeConstants#ORDER_STATUS} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + + // ========= 仓库字段 ========= + + /** + * 仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long warehouseId; + + // ========= 汇总金额字段 ========= + + /** + * 盈亏数量(实盘数量 - 账面数量) + */ + private BigDecimal totalQuantity; + /** + * 总金额(账面数量 * 单价) + */ + private BigDecimal totalPrice; + /** + * 实际金额(实盘数量 * 单价) + */ + private BigDecimal actualPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDetailDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDetailDO.java new file mode 100644 index 000000000..56f35539f --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDetailDO.java @@ -0,0 +1,85 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.check; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * WMS 盘库单明细 DO + * + * @author 芋道源码 + */ +@TableName("wms_check_order_detail") +@KeySequence("wms_check_order_detail_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsCheckOrderDetailDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + // ========= 单据商品字段 ========= + + /** + * 盘库单编号 + * + * 关联 {@link WmsCheckOrderDO#getId()} + */ + private Long orderId; + /** + * 商品 SKU 编号 + * + * 关联 {@link WmsItemSkuDO#getId()} + */ + private Long skuId; + + // ========= 仓库字段 ========= + + /** + * 仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 库存编号 + * + * 关联 {@link WmsInventoryDO#getId()} + */ + private Long inventoryId; + + /** + * 入库时间 + */ + private LocalDateTime receiptTime; + + // ========= 数量金额字段 ========= + + /** + * 账面数量 + */ + private BigDecimal quantity; + /** + * 实盘数量 + */ + private BigDecimal checkQuantity; + /** + * 单价 + */ + private BigDecimal price; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDO.java new file mode 100644 index 000000000..628640991 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDO.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.movement; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * WMS 移库单 DO + * + * @author 芋道源码 + */ +@TableName("wms_movement_order") +@KeySequence("wms_movement_order_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsMovementOrderDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 移库单号 + */ + private String no; + /** + * 单据日期 + */ + private LocalDateTime orderTime; + /** + * 移库状态 + * + * 字典 {@link DictTypeConstants#ORDER_STATUS} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + + // ========= 来源仓库字段 ========= + + /** + * 来源仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long sourceWarehouseId; + + // ========= 目标仓库字段 ========= + + /** + * 目标仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long targetWarehouseId; + + // ========= 汇总金额字段 ========= + + /** + * 总数量 + */ + private BigDecimal totalQuantity; + /** + * 总金额 + */ + private BigDecimal totalPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDetailDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDetailDO.java new file mode 100644 index 000000000..e9e9d2f24 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDetailDO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.movement; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * WMS 移库单明细 DO + * + * @author 芋道源码 + */ +@TableName("wms_movement_order_detail") +@KeySequence("wms_movement_order_detail_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsMovementOrderDetailDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + // ========= 单据商品字段 ========= + + /** + * 移库单编号 + * + * 关联 {@link WmsMovementOrderDO#getId()} + */ + private Long orderId; + /** + * 商品 SKU 编号 + * + * 关联 {@link WmsItemSkuDO#getId()} + */ + private Long skuId; + + // ========= 来源仓库字段 ========= + + /** + * 来源仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long sourceWarehouseId; + + // ========= 目标仓库字段 ========= + + /** + * 目标仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long targetWarehouseId; + + // ========= 数量金额字段 ========= + + /** + * 移库数量 + */ + private BigDecimal quantity; + /** + * 单价 + */ + private BigDecimal price; + /** + * 行金额(数量 * 单价) + */ + private BigDecimal totalPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDO.java new file mode 100644 index 000000000..86458cfbb --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDO.java @@ -0,0 +1,92 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * WMS 入库单 DO + * + * @author 芋道源码 + */ +@TableName("wms_receipt_order") +@KeySequence("wms_receipt_order_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsReceiptOrderDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 入库单号 + */ + private String no; + /** + * 入库类型 + * + * 枚举 {@link WmsReceiptOrderTypeEnum} + * 字典 {@link DictTypeConstants#RECEIPT_ORDER_TYPE} + */ + private Integer type; + /** + * 单据日期 + */ + private LocalDateTime orderTime; + /** + * 入库状态 + * + * 字典 {@link DictTypeConstants#ORDER_STATUS} + */ + private Integer status; + /** + * 业务订单号 + */ + private String bizOrderNo; + /** + * 供应商编号 + * + * 关联 {@link WmsMerchantDO#getId()} + */ + private Long merchantId; + /** + * 备注 + */ + private String remark; + + // ========= 仓库字段 ========= + + /** + * 仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long warehouseId; + + // ========= 汇总金额字段 ========= + + /** + * 总数量 + */ + private BigDecimal totalQuantity; + /** + * 总金额 + */ + private BigDecimal totalPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDetailDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDetailDO.java new file mode 100644 index 000000000..6920d369d --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDetailDO.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * WMS 入库单明细 DO + * + * @author 芋道源码 + */ +@TableName("wms_receipt_order_detail") +@KeySequence("wms_receipt_order_detail_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsReceiptOrderDetailDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + // ========= 单据商品字段 ========= + + /** + * 入库单编号 + * + * 关联 {@link WmsReceiptOrderDO#getId()} + */ + private Long orderId; + /** + * 商品 SKU 编号 + * + * 关联 {@link WmsItemSkuDO#getId()} + */ + private Long skuId; + + // ========= 仓库字段 ========= + + /** + * 仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long warehouseId; + + // ========= 数量金额字段 ========= + + /** + * 入库数量 + */ + private BigDecimal quantity; + /** + * 单价 + */ + private BigDecimal price; + /** + * 行金额(数量 * 单价) + */ + private BigDecimal totalPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/shipment/WmsShipmentOrderDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/shipment/WmsShipmentOrderDO.java new file mode 100644 index 000000000..d2e3e4ac2 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/shipment/WmsShipmentOrderDO.java @@ -0,0 +1,92 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import cn.iocoder.yudao.module.wms.enums.order.WmsShipmentOrderTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * WMS 出库单 DO + * + * @author 芋道源码 + */ +@TableName("wms_shipment_order") +@KeySequence("wms_shipment_order_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsShipmentOrderDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 出库单号 + */ + private String no; + /** + * 出库类型 + * + * 枚举 {@link WmsShipmentOrderTypeEnum} + * 字典 {@link DictTypeConstants#SHIPMENT_ORDER_TYPE} + */ + private Integer type; + /** + * 单据日期 + */ + private LocalDateTime orderTime; + /** + * 出库状态 + * + * 字典 {@link DictTypeConstants#ORDER_STATUS} + */ + private Integer status; + /** + * 业务订单号 + */ + private String bizOrderNo; + /** + * 客户编号 + * + * 关联 {@link WmsMerchantDO#getId()} + */ + private Long merchantId; + /** + * 备注 + */ + private String remark; + + // ========= 仓库字段 ========= + + /** + * 仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long warehouseId; + + // ========= 汇总金额字段 ========= + + /** + * 总数量 + */ + private BigDecimal totalQuantity; + /** + * 总金额 + */ + private BigDecimal totalPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/shipment/WmsShipmentOrderDetailDO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/shipment/WmsShipmentOrderDetailDO.java new file mode 100644 index 000000000..fdcb67e45 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/shipment/WmsShipmentOrderDetailDO.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * WMS 出库单明细 DO + * + * @author 芋道源码 + */ +@TableName("wms_shipment_order_detail") +@KeySequence("wms_shipment_order_detail_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsShipmentOrderDetailDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + // ========= 单据商品字段 ========= + + /** + * 出库单编号 + * + * 关联 {@link WmsShipmentOrderDO#getId()} + */ + private Long orderId; + /** + * 商品 SKU 编号 + * + * 关联 {@link WmsItemSkuDO#getId()} + */ + private Long skuId; + + // ========= 仓库字段 ========= + + /** + * 仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long warehouseId; + + // ========= 数量金额字段 ========= + + /** + * 出库数量 + */ + private BigDecimal quantity; + /** + * 单价 + */ + private BigDecimal price; + /** + * 行金额(数量 * 单价) + */ + private BigDecimal totalPrice; + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/home/WmsHomeStatisticsMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/home/WmsHomeStatisticsMapper.java new file mode 100644 index 000000000..b752e42d4 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/home/WmsHomeStatisticsMapper.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.home; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * WMS 首页统计 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsHomeStatisticsMapper { + + /** + * 按单据类型和状态统计单据数量 + * + * @param warehouseId 仓库编号 + * @return [{ "orderType": 1, "status": 0, "count": 5 }, ...] + */ + List> selectOrderCountGroupByTypeAndStatus(@Param("warehouseId") Long warehouseId); + + /** + * 按天聚合单据数量 + * + * @param beginTime >= 开始时间 + * @param endTime < 结束时间 + * @param warehouseId 仓库编号 + * @return [{ "date": "2026-05-14", "orderType": 1, "count": 5 }, ...] + */ + List> selectDailyOrderTrend(@Param("beginTime") LocalDateTime beginTime, + @Param("endTime") LocalDateTime endTime, + @Param("warehouseId") Long warehouseId); + + /** + * 统计库存总数量 + * + * @param warehouseId 仓库编号 + * @return 库存总数量 + */ + BigDecimal selectInventoryTotalQuantity(@Param("warehouseId") Long warehouseId); + + /** + * 按商品统计库存数量排行 + * + * @param warehouseId 仓库编号 + * @param limit 数量限制 + * @return [{ "id": 1, "name": "A4 复印纸", "quantity": 100 }, ...] + */ + List> selectInventoryItemRank(@Param("warehouseId") Long warehouseId, + @Param("limit") Integer limit); + + /** + * 按仓库统计库存数量排行 + * + * @param warehouseId 仓库编号 + * @param limit 数量限制 + * @return [{ "id": 1, "name": "上海仓", "quantity": 100 }, ...] + */ + List> selectInventoryWarehouseRank(@Param("warehouseId") Long warehouseId, + @Param("limit") Integer limit); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryHistoryMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryHistoryMapper.java new file mode 100644 index 000000000..196980dac --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryHistoryMapper.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.inventory; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.history.WmsInventoryHistoryPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryHistoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * WMS 库存流水 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsInventoryHistoryMapper extends BaseMapperX { + + default PageResult selectPage(WmsInventoryHistoryPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .selectAll(WmsInventoryHistoryDO.class) + .leftJoin(WmsItemSkuDO.class, WmsItemSkuDO::getId, WmsInventoryHistoryDO::getSkuId) + .leftJoin(WmsItemDO.class, WmsItemDO::getId, WmsItemSkuDO::getItemId) + .likeIfPresent(WmsItemDO::getCode, reqVO.getItemCode()) + .likeIfPresent(WmsItemDO::getName, reqVO.getItemName()) + .eqIfPresent(WmsInventoryHistoryDO::getSkuId, reqVO.getSkuId()) + .likeIfPresent(WmsItemSkuDO::getCode, reqVO.getSkuCode()) + .likeIfPresent(WmsItemSkuDO::getName, reqVO.getSkuName()) + .eqIfPresent(WmsInventoryHistoryDO::getWarehouseId, reqVO.getWarehouseId()) + .eqIfPresent(WmsInventoryHistoryDO::getOrderNo, reqVO.getOrderNo()) + .eqIfPresent(WmsInventoryHistoryDO::getOrderType, reqVO.getOrderType()) + .betweenIfPresent(WmsInventoryHistoryDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(WmsInventoryHistoryDO::getCreateTime) + .orderByDesc(WmsInventoryHistoryDO::getId); + return selectJoinPage(reqVO, WmsInventoryHistoryDO.class, query); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java new file mode 100644 index 000000000..1e1a17a25 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java @@ -0,0 +1,126 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.inventory; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import org.apache.ibatis.annotations.Mapper; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * WMS 库存 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsInventoryMapper extends BaseMapperX { + + default PageResult selectPage(WmsInventoryPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .selectAll(WmsInventoryDO.class) + .innerJoin(WmsItemSkuDO.class, WmsItemSkuDO::getId, WmsInventoryDO::getSkuId) + .innerJoin(WmsItemDO.class, WmsItemDO::getId, WmsItemSkuDO::getItemId) + .likeIfPresent(WmsItemDO::getCode, reqVO.getItemCode()) + .likeIfPresent(WmsItemDO::getName, reqVO.getItemName()) + .eqIfPresent(WmsInventoryDO::getSkuId, reqVO.getSkuId()) + .likeIfPresent(WmsItemSkuDO::getCode, reqVO.getSkuCode()) + .likeIfPresent(WmsItemSkuDO::getName, reqVO.getSkuName()) + .eqIfPresent(WmsInventoryDO::getWarehouseId, reqVO.getWarehouseId()) + .geIfPresent(WmsInventoryDO::getQuantity, reqVO.getMinQuantity()); + if (Boolean.TRUE.equals(reqVO.getOnlyPositiveQuantity())) { + query.gt(WmsInventoryDO::getQuantity, BigDecimal.ZERO); + } + appendDimensionOrder(query, reqVO.getType()); + return selectJoinPage(reqVO, WmsInventoryDO.class, query); + } + + default Long selectCountBySkuId(Long skuId) { + return selectCount(WmsInventoryDO::getSkuId, skuId); + } + + default Long selectCountByWarehouseId(Long warehouseId) { + return selectCount(WmsInventoryDO::getWarehouseId, warehouseId); + } + + default List selectList(WmsInventoryListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eq(WmsInventoryDO::getWarehouseId, reqVO.getWarehouseId()) + .orderByAsc(WmsInventoryDO::getSkuId) + .orderByAsc(WmsInventoryDO::getId)); + } + + default WmsInventoryDO selectBySkuIdAndWarehouseId(Long skuId, Long warehouseId) { + return selectOne(WmsInventoryDO::getSkuId, skuId, + WmsInventoryDO::getWarehouseId, warehouseId); + } + + default WmsInventoryDO selectByIdForUpdate(Long id) { + return selectOne(new LambdaQueryWrapperX() + .eq(WmsInventoryDO::getId, id) + .last("FOR UPDATE")); + } + + /** + * 根据多个唯一键,批量查询库存列表 + * + * @param keys 唯一键列表:由 SKU 编号 + 仓库编号组成 + * @return 库存列表 + */ + default List selectListByKeys(Collection keys) { + if (CollUtil.isEmpty(keys)) { + return Collections.emptyList(); + } + return selectList(new LambdaQueryWrapperX() + .and(query -> { + boolean first = true; + for (WmsInventoryDO key : keys) { + if (!first) { + query.or(); + } + query.eq(WmsInventoryDO::getSkuId, key.getSkuId()) + .eq(WmsInventoryDO::getWarehouseId, key.getWarehouseId()); + first = false; + } + })); + } + + default List selectListByIdsForUpdate(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return selectList(new LambdaQueryWrapperX() + .in(WmsInventoryDO::getId, ids) + .orderByAsc(WmsInventoryDO::getId) + .last("FOR UPDATE")); + } + + static void appendDimensionOrder(MPJLambdaWrapperX query, String type) { + if (StrUtil.equals(WmsInventoryPageReqVO.TYPE_WAREHOUSE, type)) { + query.orderByAsc(WmsInventoryDO::getWarehouseId) + .orderByAsc(WmsItemSkuDO::getItemId) + .orderByAsc(WmsInventoryDO::getSkuId) + .orderByAsc(WmsInventoryDO::getId); + return; + } + if (StrUtil.equals(WmsInventoryPageReqVO.TYPE_ITEM, type)) { + query.orderByAsc(WmsItemSkuDO::getItemId) + .orderByAsc(WmsInventoryDO::getSkuId) + .orderByAsc(WmsInventoryDO::getWarehouseId) + .orderByAsc(WmsInventoryDO::getId); + return; + } + throw new IllegalArgumentException("未知库存统计维度:" + type); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemBrandMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemBrandMapper.java new file mode 100644 index 000000000..ffb94189b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemBrandMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.md.item; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand.WmsItemBrandPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemBrandDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * WMS 商品品牌 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsItemBrandMapper extends BaseMapperX { + + default PageResult selectPage(WmsItemBrandPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(WmsItemBrandDO::getCode, reqVO.getCode()) + .likeIfPresent(WmsItemBrandDO::getName, reqVO.getName()) + .orderByDesc(WmsItemBrandDO::getId)); + } + + default WmsItemBrandDO selectByCode(String code) { + return selectOne(WmsItemBrandDO::getCode, code); + } + + default WmsItemBrandDO selectByName(String name) { + return selectOne(WmsItemBrandDO::getName, name); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemCategoryMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemCategoryMapper.java new file mode 100644 index 000000000..f95bc30c1 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemCategoryMapper.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.md.item; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category.WmsItemCategoryListReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemCategoryDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 商品分类 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsItemCategoryMapper extends BaseMapperX { + + default List selectList(WmsItemCategoryListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(WmsItemCategoryDO::getParentId, reqVO.getParentId()) + .likeIfPresent(WmsItemCategoryDO::getCode, reqVO.getCode()) + .likeIfPresent(WmsItemCategoryDO::getName, reqVO.getName()) + .eqIfPresent(WmsItemCategoryDO::getStatus, reqVO.getStatus()) + .orderByAsc(WmsItemCategoryDO::getSort) + .orderByAsc(WmsItemCategoryDO::getId)); + } + + default WmsItemCategoryDO selectByParentIdAndName(Long parentId, String name) { + return selectOne(WmsItemCategoryDO::getParentId, parentId, WmsItemCategoryDO::getName, name); + } + + default WmsItemCategoryDO selectByCode(String code) { + return selectOne(WmsItemCategoryDO::getCode, code); + } + + default List selectListByParentIds(Collection parentIds) { + return selectList(new LambdaQueryWrapperX() + .in(WmsItemCategoryDO::getParentId, parentIds) + .orderByAsc(WmsItemCategoryDO::getSort) + .orderByAsc(WmsItemCategoryDO::getId)); + } + + default Long selectCountByParentId(Long parentId) { + return selectCount(WmsItemCategoryDO::getParentId, parentId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemMapper.java new file mode 100644 index 000000000..2645ed080 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemMapper.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.md.item; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 商品 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsItemMapper extends BaseMapperX { + + default PageResult selectPage(WmsItemPageReqVO reqVO, Collection categoryIds) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(WmsItemDO::getCode, reqVO.getCode()) + .likeIfPresent(WmsItemDO::getName, reqVO.getName()) + .inIfPresent(WmsItemDO::getCategoryId, categoryIds) + .eqIfPresent(WmsItemDO::getBrandId, reqVO.getBrandId()) + .betweenIfPresent(WmsItemDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(WmsItemDO::getId)); + } + + default List selectList(WmsItemListReqVO reqVO, Collection categoryIds) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(WmsItemDO::getCode, reqVO.getCode()) + .likeIfPresent(WmsItemDO::getName, reqVO.getName()) + .inIfPresent(WmsItemDO::getCategoryId, categoryIds) + .eqIfPresent(WmsItemDO::getBrandId, reqVO.getBrandId()) + .betweenIfPresent(WmsItemDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(WmsItemDO::getId)); + } + + default WmsItemDO selectByName(String name) { + return selectOne(WmsItemDO::getName, name); + } + + default WmsItemDO selectByCode(String code) { + return selectOne(WmsItemDO::getCode, code); + } + + default Long selectCountByCategoryId(Long categoryId) { + return selectCount(WmsItemDO::getCategoryId, categoryId); + } + + default Long selectCountByBrandId(Long brandId) { + return selectCount(WmsItemDO::getBrandId, brandId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemSkuMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemSkuMapper.java new file mode 100644 index 000000000..1a0d528f9 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemSkuMapper.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.md.item; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * WMS 商品 SKU Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsItemSkuMapper extends BaseMapperX { + + /** + * 按 SKU 维度分页查询,支持商品 / 品牌 / 分类多表联查筛选。 + * + * 使用 {@link MPJLambdaWrapperX} 替代 lite 的手写 XML JOIN(见 lite + * {@code ItemSkuMapper.selectByBo}),范式参照本模块 {@code WmsInventoryMapper.selectPage}。 + */ + default PageResult selectPage(WmsItemSkuPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .selectAll(WmsItemSkuDO.class) + .innerJoin(WmsItemDO.class, WmsItemDO::getId, WmsItemSkuDO::getItemId) + .likeIfPresent(WmsItemDO::getCode, reqVO.getItemCode()) + .likeIfPresent(WmsItemDO::getName, reqVO.getItemName()) + .eqIfPresent(WmsItemDO::getCategoryId, reqVO.getCategoryId()) + .eqIfPresent(WmsItemDO::getBrandId, reqVO.getBrandId()) + .likeIfPresent(WmsItemSkuDO::getCode, reqVO.getCode()) + .likeIfPresent(WmsItemSkuDO::getName, reqVO.getName()) + .likeIfPresent(WmsItemSkuDO::getBarCode, reqVO.getBarCode()) + .orderByDesc(WmsItemSkuDO::getId); + return selectJoinPage(reqVO, WmsItemSkuDO.class, query); + } + + default List selectListByItemId(Long itemId) { + return selectList(new LambdaQueryWrapperX() + .eq(WmsItemSkuDO::getItemId, itemId) + .orderByAsc(WmsItemSkuDO::getId)); + } + + default List selectListByItemIds(Collection itemIds) { + if (CollUtil.isEmpty(itemIds)) { + return Collections.emptyList(); + } + return selectList(new LambdaQueryWrapperX() + .inIfPresent(WmsItemSkuDO::getItemId, itemIds) + .orderByAsc(WmsItemSkuDO::getId)); + } + + default List selectList(Collection itemIds, String code, String name) { + if (itemIds != null && CollUtil.isEmpty(itemIds)) { + return Collections.emptyList(); + } + return selectList(new LambdaQueryWrapperX() + .inIfPresent(WmsItemSkuDO::getItemId, itemIds) + .likeIfPresent(WmsItemSkuDO::getCode, code) + .likeIfPresent(WmsItemSkuDO::getName, name) + .orderByAsc(WmsItemSkuDO::getId)); + } + + default void deleteByItemId(Long itemId) { + delete(WmsItemSkuDO::getItemId, itemId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/merchant/WmsMerchantMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/merchant/WmsMerchantMapper.java new file mode 100644 index 000000000..efc79f130 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/merchant/WmsMerchantMapper.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.md.merchant; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * WMS 往来企业 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsMerchantMapper extends BaseMapperX { + + default PageResult selectPage(WmsMerchantPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(WmsMerchantDO::getCode, reqVO.getCode()) + .likeIfPresent(WmsMerchantDO::getName, reqVO.getName()) + .eqIfPresent(WmsMerchantDO::getType, reqVO.getType()) + .inIfPresent(WmsMerchantDO::getType, reqVO.getTypes()) + .orderByDesc(WmsMerchantDO::getId)); + } + + default List selectList(WmsMerchantListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(WmsMerchantDO::getCode, reqVO.getCode()) + .likeIfPresent(WmsMerchantDO::getName, reqVO.getName()) + .eqIfPresent(WmsMerchantDO::getType, reqVO.getType()) + .inIfPresent(WmsMerchantDO::getType, reqVO.getTypes()) + .orderByDesc(WmsMerchantDO::getId)); + } + + default WmsMerchantDO selectByCode(String code) { + return selectOne(WmsMerchantDO::getCode, code); + } + + default WmsMerchantDO selectByName(String name) { + return selectOne(WmsMerchantDO::getName, name); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/warehouse/WmsWarehouseMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/warehouse/WmsWarehouseMapper.java new file mode 100644 index 000000000..ff4fe46a8 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/warehouse/WmsWarehouseMapper.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.md.warehouse; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehousePageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * WMS 仓库 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsWarehouseMapper extends BaseMapperX { + + default PageResult selectPage(WmsWarehousePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(WmsWarehouseDO::getCode, reqVO.getCode()) + .likeIfPresent(WmsWarehouseDO::getName, reqVO.getName()) + .orderByAsc(WmsWarehouseDO::getSort) + .orderByDesc(WmsWarehouseDO::getId)); + } + + default WmsWarehouseDO selectByCode(String code) { + return selectOne(WmsWarehouseDO::getCode, code); + } + + default WmsWarehouseDO selectByName(String name) { + return selectOne(WmsWarehouseDO::getName, name); + } + + default List selectSimpleList() { + return selectList(new LambdaQueryWrapperX() + .orderByAsc(WmsWarehouseDO::getSort) + .orderByDesc(WmsWarehouseDO::getId)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderDetailMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderDetailMapper.java new file mode 100644 index 000000000..f2cd89280 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderDetailMapper.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.check; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 盘库单明细 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsCheckOrderDetailMapper extends BaseMapperX { + + default List selectListByOrderId(Long orderId) { + return selectList(WmsCheckOrderDetailDO::getOrderId, orderId); + } + + default List selectListByOrderIds(Collection orderIds) { + return selectList(new LambdaQueryWrapperX() + .inIfPresent(WmsCheckOrderDetailDO::getOrderId, orderIds) + .orderByAsc(WmsCheckOrderDetailDO::getOrderId) + .orderByAsc(WmsCheckOrderDetailDO::getId)); + } + + default void deleteByOrderId(Long orderId) { + delete(WmsCheckOrderDetailDO::getOrderId, orderId); + } + + default Long selectCountBySkuId(Long skuId) { + return selectCount(WmsCheckOrderDetailDO::getSkuId, skuId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderMapper.java new file mode 100644 index 000000000..9871e76e5 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderMapper.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.check; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * WMS 盘库单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsCheckOrderMapper extends BaseMapperX { + + default PageResult selectPage(WmsCheckOrderPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(WmsCheckOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(WmsCheckOrderDO::getStatus, reqVO.getStatus()) + .eqIfPresent(WmsCheckOrderDO::getWarehouseId, reqVO.getWarehouseId()) + .betweenIfPresent(WmsCheckOrderDO::getOrderTime, reqVO.getOrderTime()) + .geIfPresent(WmsCheckOrderDO::getTotalQuantity, reqVO.getTotalQuantityMin()) + .leIfPresent(WmsCheckOrderDO::getTotalQuantity, reqVO.getTotalQuantityMax()) + .geIfPresent(WmsCheckOrderDO::getTotalPrice, reqVO.getTotalPriceMin()) + .leIfPresent(WmsCheckOrderDO::getTotalPrice, reqVO.getTotalPriceMax()) + .geIfPresent(WmsCheckOrderDO::getActualPrice, reqVO.getActualPriceMin()) + .leIfPresent(WmsCheckOrderDO::getActualPrice, reqVO.getActualPriceMax()) + .eqIfPresent(WmsCheckOrderDO::getCreator, reqVO.getCreator()) + .eqIfPresent(WmsCheckOrderDO::getUpdater, reqVO.getUpdater()) + .betweenIfPresent(WmsCheckOrderDO::getCreateTime, reqVO.getCreateTime()) + .betweenIfPresent(WmsCheckOrderDO::getUpdateTime, reqVO.getUpdateTime()) + .orderByDesc(WmsCheckOrderDO::getId)); + } + + default WmsCheckOrderDO selectByNo(String no) { + return selectOne(WmsCheckOrderDO::getNo, no); + } + + default Long selectCountByWarehouseId(Long warehouseId) { + return selectCount(WmsCheckOrderDO::getWarehouseId, warehouseId); + } + + default int updateByIdAndStatus(Long id, Integer status, WmsCheckOrderDO updateObj) { + return update(updateObj, new LambdaQueryWrapper() + .eq(WmsCheckOrderDO::getId, id) + .eq(WmsCheckOrderDO::getStatus, status)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderDetailMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderDetailMapper.java new file mode 100644 index 000000000..4e7eb3a56 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderDetailMapper.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.movement; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 移库单明细 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsMovementOrderDetailMapper extends BaseMapperX { + + default List selectListByOrderId(Long orderId) { + return selectList(WmsMovementOrderDetailDO::getOrderId, orderId); + } + + default List selectListByOrderIds(Collection orderIds) { + return selectList(new LambdaQueryWrapperX() + .inIfPresent(WmsMovementOrderDetailDO::getOrderId, orderIds) + .orderByAsc(WmsMovementOrderDetailDO::getOrderId) + .orderByAsc(WmsMovementOrderDetailDO::getId)); + } + + default void deleteByOrderId(Long orderId) { + delete(WmsMovementOrderDetailDO::getOrderId, orderId); + } + + default Long selectCountBySkuId(Long skuId) { + return selectCount(WmsMovementOrderDetailDO::getSkuId, skuId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderMapper.java new file mode 100644 index 000000000..337590278 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderMapper.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.movement; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * WMS 移库单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsMovementOrderMapper extends BaseMapperX { + + default PageResult selectPage(WmsMovementOrderPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(WmsMovementOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(WmsMovementOrderDO::getStatus, reqVO.getStatus()) + .eqIfPresent(WmsMovementOrderDO::getSourceWarehouseId, reqVO.getSourceWarehouseId()) + .eqIfPresent(WmsMovementOrderDO::getTargetWarehouseId, reqVO.getTargetWarehouseId()) + .betweenIfPresent(WmsMovementOrderDO::getOrderTime, reqVO.getOrderTime()) + .geIfPresent(WmsMovementOrderDO::getTotalQuantity, reqVO.getTotalQuantityMin()) + .leIfPresent(WmsMovementOrderDO::getTotalQuantity, reqVO.getTotalQuantityMax()) + .geIfPresent(WmsMovementOrderDO::getTotalPrice, reqVO.getTotalPriceMin()) + .leIfPresent(WmsMovementOrderDO::getTotalPrice, reqVO.getTotalPriceMax()) + .eqIfPresent(WmsMovementOrderDO::getCreator, reqVO.getCreator()) + .eqIfPresent(WmsMovementOrderDO::getUpdater, reqVO.getUpdater()) + .betweenIfPresent(WmsMovementOrderDO::getCreateTime, reqVO.getCreateTime()) + .betweenIfPresent(WmsMovementOrderDO::getUpdateTime, reqVO.getUpdateTime()) + .orderByDesc(WmsMovementOrderDO::getId)); + } + + default WmsMovementOrderDO selectByNo(String no) { + return selectOne(WmsMovementOrderDO::getNo, no); + } + + default Long selectCountByWarehouseId(Long warehouseId) { + return selectCount(new LambdaQueryWrapperX() + .and(query -> query.eq(WmsMovementOrderDO::getSourceWarehouseId, warehouseId) + .or().eq(WmsMovementOrderDO::getTargetWarehouseId, warehouseId))); + } + + default int updateByIdAndStatus(Long id, Integer status, WmsMovementOrderDO updateObj) { + return update(updateObj, new LambdaQueryWrapper() + .eq(WmsMovementOrderDO::getId, id) + .eq(WmsMovementOrderDO::getStatus, status)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderDetailMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderDetailMapper.java new file mode 100644 index 000000000..b3ce109bb --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderDetailMapper.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.receipt; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 入库单明细 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsReceiptOrderDetailMapper extends BaseMapperX { + + default List selectListByOrderId(Long orderId) { + return selectList(WmsReceiptOrderDetailDO::getOrderId, orderId); + } + + default List selectListByOrderIds(Collection orderIds) { + return selectList(new LambdaQueryWrapperX() + .inIfPresent(WmsReceiptOrderDetailDO::getOrderId, orderIds) + .orderByAsc(WmsReceiptOrderDetailDO::getOrderId) + .orderByAsc(WmsReceiptOrderDetailDO::getId)); + } + + default void deleteByOrderId(Long orderId) { + delete(WmsReceiptOrderDetailDO::getOrderId, orderId); + } + + default Long selectCountBySkuId(Long skuId) { + return selectCount(WmsReceiptOrderDetailDO::getSkuId, skuId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderMapper.java new file mode 100644 index 000000000..550e6e2df --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderMapper.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.receipt; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * WMS 入库单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsReceiptOrderMapper extends BaseMapperX { + + default PageResult selectPage(WmsReceiptOrderPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(WmsReceiptOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(WmsReceiptOrderDO::getStatus, reqVO.getStatus()) + .eqIfPresent(WmsReceiptOrderDO::getWarehouseId, reqVO.getWarehouseId()) + .eqIfPresent(WmsReceiptOrderDO::getMerchantId, reqVO.getMerchantId()) + .betweenIfPresent(WmsReceiptOrderDO::getOrderTime, reqVO.getOrderTime()) + .geIfPresent(WmsReceiptOrderDO::getTotalQuantity, reqVO.getTotalQuantityMin()) + .leIfPresent(WmsReceiptOrderDO::getTotalQuantity, reqVO.getTotalQuantityMax()) + .geIfPresent(WmsReceiptOrderDO::getTotalPrice, reqVO.getTotalPriceMin()) + .leIfPresent(WmsReceiptOrderDO::getTotalPrice, reqVO.getTotalPriceMax()) + .eqIfPresent(WmsReceiptOrderDO::getType, reqVO.getType()) + .likeIfPresent(WmsReceiptOrderDO::getBizOrderNo, reqVO.getBizOrderNo()) + .eqIfPresent(WmsReceiptOrderDO::getCreator, reqVO.getCreator()) + .eqIfPresent(WmsReceiptOrderDO::getUpdater, reqVO.getUpdater()) + .betweenIfPresent(WmsReceiptOrderDO::getCreateTime, reqVO.getCreateTime()) + .betweenIfPresent(WmsReceiptOrderDO::getUpdateTime, reqVO.getUpdateTime()) + .orderByDesc(WmsReceiptOrderDO::getId)); + } + + default WmsReceiptOrderDO selectByNo(String no) { + return selectOne(WmsReceiptOrderDO::getNo, no); + } + + default Long selectCountByMerchantId(Long merchantId) { + return selectCount(WmsReceiptOrderDO::getMerchantId, merchantId); + } + + default Long selectCountByWarehouseId(Long warehouseId) { + return selectCount(WmsReceiptOrderDO::getWarehouseId, warehouseId); + } + + default int updateByIdAndStatus(Long id, Integer status, WmsReceiptOrderDO updateObj) { + return update(updateObj, new LambdaQueryWrapper() + .eq(WmsReceiptOrderDO::getId, id) + .eq(WmsReceiptOrderDO::getStatus, status)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/shipment/WmsShipmentOrderDetailMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/shipment/WmsShipmentOrderDetailMapper.java new file mode 100644 index 000000000..d59df25fb --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/shipment/WmsShipmentOrderDetailMapper.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.shipment; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 出库单明细 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsShipmentOrderDetailMapper extends BaseMapperX { + + default List selectListByOrderId(Long orderId) { + return selectList(WmsShipmentOrderDetailDO::getOrderId, orderId); + } + + default List selectListByOrderIds(Collection orderIds) { + return selectList(new LambdaQueryWrapperX() + .inIfPresent(WmsShipmentOrderDetailDO::getOrderId, orderIds) + .orderByAsc(WmsShipmentOrderDetailDO::getOrderId) + .orderByAsc(WmsShipmentOrderDetailDO::getId)); + } + + default void deleteByOrderId(Long orderId) { + delete(WmsShipmentOrderDetailDO::getOrderId, orderId); + } + + default Long selectCountBySkuId(Long skuId) { + return selectCount(WmsShipmentOrderDetailDO::getSkuId, skuId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/shipment/WmsShipmentOrderMapper.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/shipment/WmsShipmentOrderMapper.java new file mode 100644 index 000000000..f14df68dd --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/shipment/WmsShipmentOrderMapper.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.shipment; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * WMS 出库单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsShipmentOrderMapper extends BaseMapperX { + + default PageResult selectPage(WmsShipmentOrderPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(WmsShipmentOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(WmsShipmentOrderDO::getStatus, reqVO.getStatus()) + .eqIfPresent(WmsShipmentOrderDO::getWarehouseId, reqVO.getWarehouseId()) + .eqIfPresent(WmsShipmentOrderDO::getMerchantId, reqVO.getMerchantId()) + .betweenIfPresent(WmsShipmentOrderDO::getOrderTime, reqVO.getOrderTime()) + .geIfPresent(WmsShipmentOrderDO::getTotalQuantity, reqVO.getTotalQuantityMin()) + .leIfPresent(WmsShipmentOrderDO::getTotalQuantity, reqVO.getTotalQuantityMax()) + .geIfPresent(WmsShipmentOrderDO::getTotalPrice, reqVO.getTotalPriceMin()) + .leIfPresent(WmsShipmentOrderDO::getTotalPrice, reqVO.getTotalPriceMax()) + .eqIfPresent(WmsShipmentOrderDO::getType, reqVO.getType()) + .likeIfPresent(WmsShipmentOrderDO::getBizOrderNo, reqVO.getBizOrderNo()) + .eqIfPresent(WmsShipmentOrderDO::getCreator, reqVO.getCreator()) + .eqIfPresent(WmsShipmentOrderDO::getUpdater, reqVO.getUpdater()) + .betweenIfPresent(WmsShipmentOrderDO::getCreateTime, reqVO.getCreateTime()) + .betweenIfPresent(WmsShipmentOrderDO::getUpdateTime, reqVO.getUpdateTime()) + .orderByDesc(WmsShipmentOrderDO::getId)); + } + + default WmsShipmentOrderDO selectByNo(String no) { + return selectOne(WmsShipmentOrderDO::getNo, no); + } + + default Long selectCountByMerchantId(Long merchantId) { + return selectCount(WmsShipmentOrderDO::getMerchantId, merchantId); + } + + default Long selectCountByWarehouseId(Long warehouseId) { + return selectCount(WmsShipmentOrderDO::getWarehouseId, warehouseId); + } + + default int updateByIdAndStatus(Long id, Integer status, WmsShipmentOrderDO updateObj) { + return update(updateObj, new LambdaQueryWrapper() + .eq(WmsShipmentOrderDO::getId, id) + .eq(WmsShipmentOrderDO::getStatus, status)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/package-info.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/package-info.java new file mode 100644 index 000000000..115397b8c --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/package-info.java @@ -0,0 +1,6 @@ +/** + * 属于 WMS 模块的 framework 封装 + * + * @author 芋道源码 + */ +package cn.iocoder.yudao.module.wms.framework; diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/rpc/config/RpcConfiguration.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/rpc/config/RpcConfiguration.java new file mode 100644 index 000000000..c1daef0c6 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/rpc/config/RpcConfiguration.java @@ -0,0 +1,10 @@ +package cn.iocoder.yudao.module.wms.framework.rpc.config; + +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +@Configuration(value = "wmsRpcConfiguration", proxyBeanMethods = false) +@EnableFeignClients(clients = AdminUserApi.class) +public class RpcConfiguration { +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/security/config/SecurityConfiguration.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/security/config/SecurityConfiguration.java new file mode 100644 index 000000000..1798c0b5b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/security/config/SecurityConfiguration.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.wms.framework.security.config; + +import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; +import cn.iocoder.yudao.module.wms.enums.ApiConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; + +/** + * WMS 模块的 Security 配置 + */ +@Configuration("wmsSecurityConfiguration") +public class SecurityConfiguration { + + @Bean("wmsAuthorizeRequestsCustomizer") + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { + return new AuthorizeRequestsCustomizer() { + + @Override + public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { + // Swagger 接口文档 + registry.requestMatchers("/v3/api-docs/**").permitAll() + .requestMatchers("/webjars/**").permitAll() + .requestMatchers("/swagger-ui").permitAll() + .requestMatchers("/swagger-ui/**").permitAll(); + // Spring Boot Actuator 的安全配置 + registry.requestMatchers("/actuator").permitAll() + .requestMatchers("/actuator/**").permitAll(); + // Druid 监控 + registry.requestMatchers("/druid/**").permitAll(); + // RPC 服务的安全配置 + registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); + } + + }; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/security/core/package-info.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/security/core/package-info.java new file mode 100644 index 000000000..0c573aa64 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/security/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package cn.iocoder.yudao.module.wms.framework.security.core; diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/web/config/WmsWebConfiguration.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/web/config/WmsWebConfiguration.java new file mode 100644 index 000000000..37c75718f --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/web/config/WmsWebConfiguration.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.wms.framework.web.config; + +import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration; +import org.springdoc.core.models.GroupedOpenApi; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * WMS 模块的 web 组件的 Configuration + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +public class WmsWebConfiguration { + + /** + * WMS 模块的 API 分组 + */ + @Bean + public GroupedOpenApi wmsGroupedOpenApi() { + return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("wms"); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/web/package-info.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/web/package-info.java new file mode 100644 index 000000000..13cb703d9 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/framework/web/package-info.java @@ -0,0 +1,4 @@ +/** + * WMS 模块的 web 配置 + */ +package cn.iocoder.yudao.module.wms.framework.web; diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/package-info.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/package-info.java new file mode 100644 index 000000000..099e22cb8 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/package-info.java @@ -0,0 +1,10 @@ +/** + * wms 包下,仓库管理系统(Warehouse Management System) + * 例如说:仓库、物料、库存、入库、出库、移库、盘库等等 + * + * 1. Controller URL:以 /wms/ 开头,避免和其它 Module 冲突 + * 2. DataObject 表名:以 wms_ 开头,方便在数据库中区分 + * + * 注意,由于 WMS 模块下,容易和其它模块重名,所以类名都加载 Wms 的前缀~ + */ +package cn.iocoder.yudao.module.wms; diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/home/WmsHomeStatisticsService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/home/WmsHomeStatisticsService.java new file mode 100644 index 000000000..75015ef54 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/home/WmsHomeStatisticsService.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.wms.service.home; + +import cn.iocoder.yudao.module.wms.controller.admin.home.vo.WmsHomeInventorySummaryRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.home.vo.WmsHomeOrderSummaryRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.home.vo.WmsHomeOrderTrendRespVO; + +import java.util.List; + +/** + * WMS 首页统计 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsHomeStatisticsService { + + /** + * 获得单据汇总统计 + * + * @param warehouseId 仓库编号 + * @return 单据汇总统计 + */ + List getOrderSummary(Long warehouseId); + + /** + * 获得单据趋势,按日期统计入库、出库、移库、盘库单据数量。 + * + * @param days 最近天数,包含今天 + * @param warehouseId 仓库编号,为空时统计全部仓库 + * @return 单据趋势 + */ + List getOrderTrend(Integer days, Long warehouseId); + + /** + * 获得库存汇总统计 + * + * @param warehouseId 仓库编号 + * @param goodsLimit 商品排行数量 + * @param warehouseLimit 仓库排行数量 + * @return 库存汇总统计 + */ + WmsHomeInventorySummaryRespVO getInventorySummary(Long warehouseId, Integer goodsLimit, Integer warehouseLimit); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/home/WmsHomeStatisticsServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/home/WmsHomeStatisticsServiceImpl.java new file mode 100644 index 000000000..ba5f4410f --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/home/WmsHomeStatisticsServiceImpl.java @@ -0,0 +1,134 @@ +package cn.iocoder.yudao.module.wms.service.home; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.module.wms.controller.admin.home.vo.WmsHomeInventorySummaryRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.home.vo.WmsHomeOrderStatusRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.home.vo.WmsHomeOrderSummaryRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.home.vo.WmsHomeOrderTrendRespVO; +import cn.iocoder.yudao.module.wms.dal.mysql.home.WmsHomeStatisticsMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.getBigDecimal; + +/** + * WMS 首页统计 Service 实现类 + * + * @author 芋道源码 + */ +@Service +public class WmsHomeStatisticsServiceImpl implements WmsHomeStatisticsService { + + @Resource + private WmsHomeStatisticsMapper homeStatisticsMapper; + @Resource + private WmsWarehouseService warehouseService; + + @Override + public List getOrderSummary(Long warehouseId) { + validateWarehouseIfPresent(warehouseId); + // 查询单据数量 + List> stats = homeStatisticsMapper.selectOrderCountGroupByTypeAndStatus(warehouseId); + // 按照单据类型 + 单据状态分组 + return convertList(WmsOrderTypeEnum.values(), orderTypeEnum -> { + List statuses = convertList(WmsOrderStatusEnum.values(), statusEnum -> + new WmsHomeOrderStatusRespVO().setStatus(statusEnum.getStatus()) + .setCount(getOrderCount(stats, orderTypeEnum.getType(), statusEnum.getStatus()))); + Long total = getSumValue(statuses, WmsHomeOrderStatusRespVO::getCount, Long::sum, 0L); + return new WmsHomeOrderSummaryRespVO().setType(orderTypeEnum.getType()).setTotal(total).setStatuses(statuses); + }); + } + + @Override + public List getOrderTrend(Integer days, Long warehouseId) { + validateWarehouseIfPresent(warehouseId); + // 统计区间为 [今天 - days + 1, 明天),保证最近 days 天包含今天。 + LocalDate endDate = LocalDate.now().plusDays(1); + LocalDate startDate = endDate.minusDays(days); + LocalDateTime beginTime = startDate.atStartOfDay(); + LocalDateTime endTime = endDate.atStartOfDay(); + // 查询每天的单据数量 + List> dbData = homeStatisticsMapper.selectDailyOrderTrend(beginTime, endTime, warehouseId); + Map> dateMap = new LinkedHashMap<>(); + for (Map row : dbData) { + String date = MapUtil.getStr(row, "date"); + Integer orderType = MapUtil.getInt(row, "orderType"); + Long count = MapUtil.getLong(row, "count", 0L); + dateMap.computeIfAbsent(date, key -> new HashMap<>()).put(orderType, count); + } + // 构造结果,保证每天都有数据 + return convertList(IntStream.range(0, days).mapToObj(startDate::plusDays).toList(), d -> { + String dateStr = DatePattern.NORM_DATE_FORMATTER.format(d); + Map row = dateMap.getOrDefault(dateStr, Collections.emptyMap()); + return new WmsHomeOrderTrendRespVO().setTime(d.atStartOfDay()) + .setReceiptCount(row.getOrDefault(WmsOrderTypeEnum.RECEIPT.getType(), 0L)) + .setShipmentCount(row.getOrDefault(WmsOrderTypeEnum.SHIPMENT.getType(), 0L)) + .setMovementCount(row.getOrDefault(WmsOrderTypeEnum.MOVEMENT.getType(), 0L)) + .setCheckCount(row.getOrDefault(WmsOrderTypeEnum.CHECK.getType(), 0L)); + }); + } + + @Override + public WmsHomeInventorySummaryRespVO getInventorySummary(Long warehouseId, Integer goodsLimit, Integer warehouseLimit) { + validateWarehouseIfPresent(warehouseId); + BigDecimal totalQuantity = ObjectUtil.defaultIfNull(homeStatisticsMapper.selectInventoryTotalQuantity(warehouseId), BigDecimal.ZERO); + List> itemRows = homeStatisticsMapper.selectInventoryItemRank(warehouseId, goodsLimit); + List> warehouseRows = homeStatisticsMapper.selectInventoryWarehouseRank(warehouseId, warehouseLimit); + return new WmsHomeInventorySummaryRespVO().setTotalQuantity(totalQuantity) + .setGoodsShareList(buildInventoryItemRankList(itemRows)) + .setWarehouseDistributionList(buildInventoryWarehouseRankList(warehouseRows)); + } + + // ==================== 工具方法 ==================== + + /** + * 仓库编号非空时,校验仓库是否存在,避免前端误传任意 id 直接落到 SQL。 + */ + private void validateWarehouseIfPresent(Long warehouseId) { + if (warehouseId != null) { + warehouseService.validateWarehouseExists(warehouseId); + } + } + + private long getOrderCount(List> stats, Integer orderType, Integer status) { + for (Map stat : stats) { + if (ObjectUtil.equal(MapUtil.getInt(stat, "orderType"), orderType) + && ObjectUtil.equal(MapUtil.getInt(stat, "status"), status)) { + return MapUtil.getLong(stat, "count", 0L); + } + } + return 0L; + } + + private List buildInventoryItemRankList(List> rows) { + return convertList(rows, row -> new WmsHomeInventorySummaryRespVO.ItemRank() + .setId(ObjectUtil.defaultIfNull(MapUtil.getLong(row, "id"), MapUtil.getLong(row, "itemId"))) + .setName(ObjectUtil.defaultIfNull(MapUtil.getStr(row, "name"), MapUtil.getStr(row, "itemName"))) + .setQuantity(getBigDecimal(row, "quantity", BigDecimal.ZERO))); + } + + private List buildInventoryWarehouseRankList(List> rows) { + return convertList(rows, row -> new WmsHomeInventorySummaryRespVO.WarehouseRank() + .setId(ObjectUtil.defaultIfNull(MapUtil.getLong(row, "id"), MapUtil.getLong(row, "warehouseId"))) + .setName(ObjectUtil.defaultIfNull(MapUtil.getStr(row, "name"), MapUtil.getStr(row, "warehouseName"))) + .setQuantity(getBigDecimal(row, "quantity", BigDecimal.ZERO))); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryHistoryService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryHistoryService.java new file mode 100644 index 000000000..ef2bc2c70 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryHistoryService.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.wms.service.inventory; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.history.WmsInventoryHistoryPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryHistoryDO; + +import java.util.List; + +/** + * WMS 库存流水 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsInventoryHistoryService { + + /** + * 获得库存流水分页 + * + * @param pageReqVO 分页查询 + * @return 库存流水分页 + */ + PageResult getInventoryHistoryPage(WmsInventoryHistoryPageReqVO pageReqVO); + + /** + * 创建库存流水列表 + * + * @param list 库存流水列表 + */ + void createInventoryHistoryList(List list); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryHistoryServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryHistoryServiceImpl.java new file mode 100644 index 000000000..5feef50d2 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryHistoryServiceImpl.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.wms.service.inventory; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.history.WmsInventoryHistoryPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryHistoryDO; +import cn.iocoder.yudao.module.wms.dal.mysql.inventory.WmsInventoryHistoryMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * WMS 库存流水 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsInventoryHistoryServiceImpl implements WmsInventoryHistoryService { + + @Resource + private WmsInventoryHistoryMapper inventoryHistoryMapper; + + @Override + public PageResult getInventoryHistoryPage(WmsInventoryHistoryPageReqVO pageReqVO) { + return inventoryHistoryMapper.selectPage(pageReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void createInventoryHistoryList(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + inventoryHistoryMapper.insertBatch(list); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryService.java new file mode 100644 index 000000000..8eb2c5d50 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryService.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.wms.service.inventory; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryCheckReqDTO; + +import java.util.List; + +/** + * WMS 库存 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsInventoryService { + + /** + * 获得库存统计分页 + * + * @param pageReqVO 分页查询 + * @return 库存统计分页 + */ + PageResult getInventoryPage(WmsInventoryPageReqVO pageReqVO); + + /** + * 获得库存余额列表 + * + * @param listReqVO 列表查询 + * @return 库存余额列表 + */ + List getInventoryList(WmsInventoryListReqVO listReqVO); + + /** + * 获得指定 SKU 的库存数量 + * + * @param skuId SKU 编号 + * @return 库存数量 + */ + long getInventoryCountBySkuId(Long skuId); + + /** + * 获得指定仓库的库存数量 + * + * @param warehouseId 仓库编号 + * @return 库存数量 + */ + long getInventoryCountByWarehouseId(Long warehouseId); + + /** + * 盘点库存 + * + * @param reqDTO 盘点库存请求 + */ + void checkInventory(WmsInventoryCheckReqDTO reqDTO); + + /** + * 变更库存 + * + * @param reqDTO 库存变更请求 + */ + void changeInventory(WmsInventoryChangeReqDTO reqDTO); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java new file mode 100644 index 000000000..250870eee --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java @@ -0,0 +1,298 @@ +package cn.iocoder.yudao.module.wms.service.inventory; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Tuple; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryHistoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.mysql.inventory.WmsInventoryMapper; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryCheckReqDTO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 库存 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class WmsInventoryServiceImpl implements WmsInventoryService { + + @Resource + private WmsInventoryMapper inventoryMapper; + + @Resource + private WmsInventoryHistoryService inventoryHistoryService; + + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsItemService itemService; + + @Override + public PageResult getInventoryPage(WmsInventoryPageReqVO pageReqVO) { + return inventoryMapper.selectPage(pageReqVO); + } + + @Override + public List getInventoryList(WmsInventoryListReqVO listReqVO) { + return inventoryMapper.selectList(listReqVO); + } + + @Override + public long getInventoryCountBySkuId(Long skuId) { + return inventoryMapper.selectCountBySkuId(skuId); + } + + @Override + public long getInventoryCountByWarehouseId(Long warehouseId) { + return inventoryMapper.selectCountByWarehouseId(warehouseId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void checkInventory(WmsInventoryCheckReqDTO reqDTO) { + if (reqDTO == null || CollUtil.isEmpty(reqDTO.getItems())) { + return; + } + + // 1. 逐条复核账面库存,并计算库存调整和库存流水 + List updateInventories = new ArrayList<>(reqDTO.getItems().size()); + List histories = new ArrayList<>(reqDTO.getItems().size()); + for (WmsInventoryCheckReqDTO.Item item : reqDTO.getItems()) { + // 1.1 锁定或创建库存余额行 + WmsInventoryDO inventory = getOrCreateCheckInventory(item); + // 1.2 无盈亏时不更新库存,也不生成库存流水 + BigDecimal beforeQuantity = inventory.getQuantity(); + BigDecimal afterQuantity = item.getCheckQuantity(); + if (beforeQuantity.compareTo(afterQuantity) == 0) { + continue; + } + updateInventories.add(new WmsInventoryDO().setId(inventory.getId()).setQuantity(afterQuantity)); + histories.add(buildInventoryHistory(reqDTO, item, beforeQuantity, afterQuantity)); + } + + // 2.1 批量更新库存余额 + if (CollUtil.isNotEmpty(updateInventories)) { + inventoryMapper.updateBatch(updateInventories); + } + // 2.2 批量写入库存流水 + if (CollUtil.isNotEmpty(histories)) { + inventoryHistoryService.createInventoryHistoryList(histories); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void changeInventory(WmsInventoryChangeReqDTO reqDTO) { + if (reqDTO == null || CollUtil.isEmpty(reqDTO.getItems())) { + return; + } + + // 1. 补齐并锁定本次涉及的库存余额行,再计算库存变更 + Map resultMap = changeInventoryList(reqDTO.getItems()); + + // 2. 批量写入库存流水 + List histories = new ArrayList<>(reqDTO.getItems().size()); + for (WmsInventoryChangeReqDTO.Item item : reqDTO.getItems()) { + histories.add(buildInventoryHistory(reqDTO, item, resultMap.get(item))); + } + inventoryHistoryService.createInventoryHistoryList(histories); + } + + /** + * 批量变更库存余额 + * + * 1. 库存行按 ID 批量加锁 + * 2. 库存变更先在内存计算并校验,全部通过后批量覆盖库存数量。 + * + * @param items 库存变更明细列表 + * @return 每条变更明细对应的变更前数量、变更后数量 + */ + private Map changeInventoryList(List items) { + // 1.1 创建或锁定库存行 + List inventories = getOrCreateInventoryList(items); + // 1.2 锁定库存:避免 quantity 变更时的并发问题,导致 quantity 前后计算不对 + inventories = inventoryMapper.selectListByIdsForUpdate(convertSet(inventories, WmsInventoryDO::getId)); + + // 2.1 校验库存充足 + Map resultMap = new IdentityHashMap<>(items.size()); + for (WmsInventoryChangeReqDTO.Item item : items) { + WmsInventoryDO inventory = findInventory(inventories, item); + if (inventory == null) { + throw new IllegalStateException("库存行不存在,skuId=" + item.getSkuId() + + ", warehouseId=" + item.getWarehouseId()); + } + BigDecimal beforeQuantity = inventory.getQuantity(); + BigDecimal afterQuantity = beforeQuantity.add(item.getQuantity()); + if (afterQuantity.compareTo(BigDecimal.ZERO) < 0) { + throw buildInventoryQuantityNotEnoughException(item, beforeQuantity); + } + inventory.setQuantity(afterQuantity); + resultMap.put(item, new Tuple(beforeQuantity, afterQuantity)); + } + // 2.2 批量更新库存数量(加锁安全) + if (CollUtil.isNotEmpty(inventories)) { + inventoryMapper.updateBatch(convertList(inventories, inventory -> + new WmsInventoryDO().setId(inventory.getId()).setQuantity(inventory.getQuantity()))); + } + return resultMap; + } + + private List getOrCreateInventoryList(List items) { + // 1.1 先按库存维度在内存去重,避免同一批明细重复查询或重复补行 + List inventoryDimensions = new ArrayList<>(items.size()); + for (WmsInventoryChangeReqDTO.Item item : items) { + if (findInventory(inventoryDimensions, item) == null) { + inventoryDimensions.add(new WmsInventoryDO().setSkuId(item.getSkuId()) + .setWarehouseId(item.getWarehouseId())); + } + } + // 1.2 批量查询已存在的库存行(这里不加锁,后续统一按库存 ID 批量加锁) + List inventories = inventoryMapper.selectListByKeys(inventoryDimensions); + + // 2.1 对比库存维度,找出数据库中还不存在的库存行 + List missingInventories = new ArrayList<>(inventoryDimensions.size()); + for (WmsInventoryDO inventoryDimension : inventoryDimensions) { + if (findInventory(inventories, inventoryDimension) == null) { + missingInventories.add(inventoryDimension); + } + } + // 2.2 如果库存行都已存在,直接返回待加锁库存列表 + if (CollUtil.isEmpty(missingInventories)) { + return inventories; + } + // 2.3 对缺失库存行执行创建;并发冲突时,内部会按唯一索引回查已创建的库存行 + inventories.addAll(createMissingInventoryList(missingInventories)); + return inventories; + } + + private List createMissingInventoryList(List missingInventories) { + List createdInventories = new ArrayList<>(missingInventories.size()); + for (WmsInventoryDO missingInventory : missingInventories) { + WmsInventoryDO inventory = new WmsInventoryDO().setSkuId(missingInventory.getSkuId()) + .setWarehouseId(missingInventory.getWarehouseId()) + .setQuantity(BigDecimal.ZERO); + try { + inventoryMapper.insert(inventory); + } catch (DuplicateKeyException ex) { + inventory = inventoryMapper.selectBySkuIdAndWarehouseId( + missingInventory.getSkuId(), missingInventory.getWarehouseId()); + log.warn("[createMissingInventoryList][missingInventory({}) 插入库存行冲突,回查已有库存行]", missingInventory); + if (inventory == null) { + throw ex; + } + } + createdInventories.add(inventory); + } + return createdInventories; + } + + private WmsInventoryHistoryDO buildInventoryHistory(WmsInventoryChangeReqDTO reqDTO, + WmsInventoryChangeReqDTO.Item item, + Tuple result) { + return new WmsInventoryHistoryDO() + .setWarehouseId(item.getWarehouseId()).setSkuId(item.getSkuId()) + .setQuantity(item.getQuantity()).setBeforeQuantity(result.get(0)).setAfterQuantity(result.get(1)) + .setPrice(item.getPrice()).setTotalPrice(item.getTotalPrice()).setRemark(item.getRemark()) + .setOrderId(reqDTO.getOrderId()).setOrderNo(reqDTO.getOrderNo()).setOrderType(reqDTO.getOrderType()); + } + + private WmsInventoryHistoryDO buildInventoryHistory(WmsInventoryCheckReqDTO reqDTO, + WmsInventoryCheckReqDTO.Item item, + BigDecimal beforeQuantity, + BigDecimal afterQuantity) { + BigDecimal quantity = afterQuantity.subtract(beforeQuantity); + return new WmsInventoryHistoryDO().setWarehouseId(item.getWarehouseId()).setSkuId(item.getSkuId()) + .setQuantity(quantity).setBeforeQuantity(beforeQuantity).setAfterQuantity(afterQuantity) + .setPrice(item.getPrice()).setTotalPrice(MoneyUtils.priceMultiply(item.getPrice(), quantity)) + .setOrderId(reqDTO.getOrderId()).setOrderNo(reqDTO.getOrderNo()).setOrderType(reqDTO.getOrderType()) + .setRemark(item.getRemark()); + } + + private WmsInventoryDO getOrCreateCheckInventory(WmsInventoryCheckReqDTO.Item item) { + if (item.getInventoryId() == null) { + return createCheckInventory(item); + } + WmsInventoryDO inventory = inventoryMapper.selectByIdForUpdate(item.getInventoryId()); + if (inventory == null || !isSameInventory(inventory, item) + || inventory.getQuantity().compareTo(item.getQuantity()) != 0) { + throw exception(CHECK_ORDER_INVENTORY_CHANGED); + } + return inventory; + } + + private WmsInventoryDO createCheckInventory(WmsInventoryCheckReqDTO.Item item) { + WmsInventoryDO inventory = new WmsInventoryDO() + .setSkuId(item.getSkuId()).setWarehouseId(item.getWarehouseId()) + .setQuantity(item.getCheckQuantity()); + try { + inventoryMapper.insert(inventory); + } catch (DuplicateKeyException ex) { + throw exception(CHECK_ORDER_INVENTORY_CHANGED); + } + // 内存中重置为 0,让主循环按 0 -> checkQuantity 生成盘库流水 + inventory.setQuantity(BigDecimal.ZERO); + return inventory; + } + + private static WmsInventoryDO findInventory(List inventories, WmsInventoryChangeReqDTO.Item item) { + return CollUtil.findOne(inventories, inventory -> isSameInventory(inventory, item)); + } + + private static WmsInventoryDO findInventory(List inventories, WmsInventoryDO key) { + return CollUtil.findOne(inventories, inventory -> isSameInventory(inventory, key)); + } + + private static boolean isSameInventory(WmsInventoryDO inventory, WmsInventoryChangeReqDTO.Item item) { + return ObjectUtil.equal(inventory.getSkuId(), item.getSkuId()) + && ObjectUtil.equal(inventory.getWarehouseId(), item.getWarehouseId()); + } + + private static boolean isSameInventory(WmsInventoryDO inventory, WmsInventoryCheckReqDTO.Item item) { + return ObjectUtil.equal(inventory.getSkuId(), item.getSkuId()) + && ObjectUtil.equal(inventory.getWarehouseId(), item.getWarehouseId()); + } + + private static boolean isSameInventory(WmsInventoryDO inventory, WmsInventoryDO key) { + return ObjectUtil.equal(inventory.getSkuId(), key.getSkuId()) + && ObjectUtil.equal(inventory.getWarehouseId(), key.getWarehouseId()); + } + + private ServiceException buildInventoryQuantityNotEnoughException(WmsInventoryChangeReqDTO.Item item, + BigDecimal beforeQuantity) { + WmsItemSkuDO skuDO = itemSkuService.validateItemSkuExists(item.getSkuId()); + WmsItemDO itemDO = itemService.validateItemExists(skuDO.getItemId()); + return exception(INVENTORY_QUANTITY_NOT_ENOUGH, itemDO.getName(), skuDO.getName(), + item.getWarehouseId(), beforeQuantity.setScale(6, RoundingMode.HALF_UP), item.getQuantity()); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/dto/WmsInventoryChangeReqDTO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/dto/WmsInventoryChangeReqDTO.java new file mode 100644 index 000000000..79636a666 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/dto/WmsInventoryChangeReqDTO.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.wms.service.inventory.dto; + +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * WMS 库存变更请求 DTO + * + * @author 芋道源码 + */ +@Data +public class WmsInventoryChangeReqDTO { + + /** + * 单据编号 + */ + private Long orderId; + /** + * 单据号 + */ + private String orderNo; + /** + * 单据类型 + * + * 枚举 {@link WmsOrderTypeEnum#getType()} + */ + private Integer orderType; + + /** + * 库存变更明细 + */ + private List items; + + /** + * WMS 库存变更明细 + */ + @Data + public static class Item { + + /** + * SKU 编号 + */ + private Long skuId; + /** + * 仓库编号 + */ + private Long warehouseId; + /** + * 变更数量 + */ + private BigDecimal quantity; + + // ========= 单价备注相关字段 ========= + + /** + * 单价 + */ + private BigDecimal price; + /** + * 库存变化金额 + */ + private BigDecimal totalPrice; + /** + * 备注 + */ + private String remark; + + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/dto/WmsInventoryCheckReqDTO.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/dto/WmsInventoryCheckReqDTO.java new file mode 100644 index 000000000..f7a93e29b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/dto/WmsInventoryCheckReqDTO.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.wms.service.inventory.dto; + +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * WMS 库存盘点请求 DTO + * + * @author 芋道源码 + */ +@Data +public class WmsInventoryCheckReqDTO { + + /** + * 单据编号 + */ + private Long orderId; + /** + * 单据号 + */ + private String orderNo; + /** + * 单据类型 + * + * 枚举 {@link WmsOrderTypeEnum#getType()} + */ + private Integer orderType; + + /** + * 库存盘点明细 + */ + private List items; + + /** + * WMS 库存盘点明细 + */ + @Data + public static class Item { + + /** + * 库存编号 + */ + private Long inventoryId; + /** + * SKU 编号 + */ + private Long skuId; + /** + * 仓库编号 + */ + private Long warehouseId; + /** + * 账面数量 + */ + private BigDecimal quantity; + /** + * 实盘数量 + */ + private BigDecimal checkQuantity; + + // ========= 单价备注相关字段 ========= + + /** + * 单价 + */ + private BigDecimal price; + /** + * 备注 + */ + private String remark; + + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemBrandService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemBrandService.java new file mode 100644 index 000000000..9ca393d5d --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemBrandService.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.wms.service.md.item; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand.WmsItemBrandPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand.WmsItemBrandSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemBrandDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * WMS 商品品牌 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsItemBrandService { + + /** + * 创建商品品牌 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createItemBrand(@Valid WmsItemBrandSaveReqVO createReqVO); + + /** + * 更新商品品牌 + * + * @param updateReqVO 更新信息 + */ + void updateItemBrand(@Valid WmsItemBrandSaveReqVO updateReqVO); + + /** + * 删除商品品牌 + * + * @param id 编号 + */ + void deleteItemBrand(Long id); + + /** + * 校验商品品牌存在 + * + * @param id 编号 + * @return 商品品牌 + */ + WmsItemBrandDO validateItemBrandExists(Long id); + + /** + * 获得商品品牌 + * + * @param id 编号 + * @return 商品品牌 + */ + WmsItemBrandDO getItemBrand(Long id); + + /** + * 获得商品品牌分页 + * + * @param pageReqVO 分页查询 + * @return 商品品牌分页 + */ + PageResult getItemBrandPage(WmsItemBrandPageReqVO pageReqVO); + + /** + * 获得商品品牌列表 + * + * @return 商品品牌列表 + */ + List getItemBrandList(); + + /** + * 按编号集合获得商品品牌列表 + * + * @param ids 编号集合 + * @return 商品品牌列表 + */ + List getItemBrandList(Collection ids); + + /** + * 按编号集合获得商品品牌 Map + * + * @param ids 编号集合 + * @return 商品品牌 Map + */ + default Map getItemBrandMap(Collection ids) { + return convertMap(getItemBrandList(ids), WmsItemBrandDO::getId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemBrandServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemBrandServiceImpl.java new file mode 100644 index 000000000..051bd9c4a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemBrandServiceImpl.java @@ -0,0 +1,137 @@ +package cn.iocoder.yudao.module.wms.service.md.item; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand.WmsItemBrandPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand.WmsItemBrandSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemBrandDO; +import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemBrandMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.ITEM_BRAND_CODE_DUPLICATE; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.ITEM_BRAND_HAS_ITEM; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.ITEM_BRAND_NAME_DUPLICATE; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.ITEM_BRAND_NOT_EXISTS; + +/** + * WMS 商品品牌 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsItemBrandServiceImpl implements WmsItemBrandService { + + @Resource + private WmsItemBrandMapper brandMapper; + @Resource + private WmsItemService itemService; + + @Override + public Long createItemBrand(WmsItemBrandSaveReqVO createReqVO) { + validateBrandSaveData(null, createReqVO); + + // 新增 + WmsItemBrandDO brand = BeanUtils.toBean(createReqVO, WmsItemBrandDO.class); + brandMapper.insert(brand); + return brand.getId(); + } + + @Override + public void updateItemBrand(WmsItemBrandSaveReqVO updateReqVO) { + // 校验存在 + validateItemBrandExists(updateReqVO.getId()); + validateBrandSaveData(updateReqVO.getId(), updateReqVO); + + // 更新 + WmsItemBrandDO updateObj = BeanUtils.toBean(updateReqVO, WmsItemBrandDO.class); + brandMapper.updateById(updateObj); + } + + private void validateBrandSaveData(Long id, WmsItemBrandSaveReqVO reqVO) { + validateBrandCodeUnique(id, reqVO.getCode()); + validateBrandNameUnique(id, reqVO.getName()); + } + + private void validateBrandCodeUnique(Long id, String code) { + WmsItemBrandDO brand = brandMapper.selectByCode(code); + if (brand == null) { + return; + } + // 如果 id 为空,说明新增;和数据库已有 code 重复 + if (id == null) { + throw exception(ITEM_BRAND_CODE_DUPLICATE); + } + if (ObjectUtil.notEqual(brand.getId(), id)) { + throw exception(ITEM_BRAND_CODE_DUPLICATE); + } + } + + private void validateBrandNameUnique(Long id, String name) { + WmsItemBrandDO brand = brandMapper.selectByName(name); + if (brand == null) { + return; + } + if (id == null) { + throw exception(ITEM_BRAND_NAME_DUPLICATE); + } + if (ObjectUtil.notEqual(brand.getId(), id)) { + throw exception(ITEM_BRAND_NAME_DUPLICATE); + } + } + + @Override + public void deleteItemBrand(Long id) { + // 校验存在 + validateItemBrandExists(id); + // 校验品牌下不存在商品 + if (itemService.getItemCountByBrandId(id) > 0) { + throw exception(ITEM_BRAND_HAS_ITEM); + } + + // 删除 + brandMapper.deleteById(id); + } + + @Override + public WmsItemBrandDO validateItemBrandExists(Long id) { + WmsItemBrandDO brand = brandMapper.selectById(id); + if (brand == null) { + throw exception(ITEM_BRAND_NOT_EXISTS); + } + return brand; + } + + @Override + public WmsItemBrandDO getItemBrand(Long id) { + return brandMapper.selectById(id); + } + + @Override + public PageResult getItemBrandPage(WmsItemBrandPageReqVO pageReqVO) { + return brandMapper.selectPage(pageReqVO); + } + + @Override + public List getItemBrandList() { + return brandMapper.selectList(); + } + + @Override + public List getItemBrandList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return brandMapper.selectByIds(ids); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemCategoryService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemCategoryService.java new file mode 100644 index 000000000..a036d7e78 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemCategoryService.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.wms.service.md.item; + +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category.WmsItemCategoryListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category.WmsItemCategorySaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemCategoryDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * WMS 商品分类 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsItemCategoryService { + + /** + * 创建商品分类 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createItemCategory(@Valid WmsItemCategorySaveReqVO createReqVO); + + /** + * 更新商品分类 + * + * @param updateReqVO 更新信息 + */ + void updateItemCategory(@Valid WmsItemCategorySaveReqVO updateReqVO); + + /** + * 删除商品分类 + * + * @param id 编号 + */ + void deleteItemCategory(Long id); + + /** + * 校验商品分类存在 + * + * @param id 编号 + * @return 商品分类 + */ + WmsItemCategoryDO validateItemCategoryExists(Long id); + + /** + * 获得商品分类 + * + * @param id 编号 + * @return 商品分类 + */ + WmsItemCategoryDO getItemCategory(Long id); + + /** + * 获得商品分类列表 + * + * @param listReqVO 查询条件 + * @return 商品分类列表 + */ + List getItemCategoryList(WmsItemCategoryListReqVO listReqVO); + + /** + * 按编号集合获得商品分类列表 + * + * @param ids 编号集合 + * @return 商品分类列表 + */ + List getItemCategoryList(Collection ids); + + /** + * 获得指定商品分类及其所有子分类编号集合 + * + * @param id 商品分类编号 + * @return 商品分类编号集合;当 id 为空时,返回 null + */ + Set getSelfAndChildItemCategoryIdList(Long id); + + /** + * 按编号集合获得商品分类 Map + * + * @param ids 编号集合 + * @return 商品分类 Map + */ + default Map getItemCategoryMap(Collection ids) { + return convertMap(getItemCategoryList(ids), WmsItemCategoryDO::getId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemCategoryServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemCategoryServiceImpl.java new file mode 100644 index 000000000..0f63fdb39 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemCategoryServiceImpl.java @@ -0,0 +1,193 @@ +package cn.iocoder.yudao.module.wms.service.md.item; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category.WmsItemCategoryListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.category.WmsItemCategorySaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemCategoryDO; +import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemCategoryMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 商品分类 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsItemCategoryServiceImpl implements WmsItemCategoryService { + + @Resource + private WmsItemCategoryMapper categoryMapper; + + @Resource + private WmsItemService itemService; + + @Override + public Long createItemCategory(WmsItemCategorySaveReqVO createReqVO) { + // 校验数据 + validateCategorySaveData(createReqVO); + + // 插入 + WmsItemCategoryDO category = BeanUtils.toBean(createReqVO, WmsItemCategoryDO.class); + categoryMapper.insert(category); + return category.getId(); + } + + @Override + public void updateItemCategory(WmsItemCategorySaveReqVO updateReqVO) { + // 校验存在 + validateItemCategoryExists(updateReqVO.getId()); + // 校验数据 + validateCategorySaveData(updateReqVO); + + // 更新 + WmsItemCategoryDO updateObj = BeanUtils.toBean(updateReqVO, WmsItemCategoryDO.class); + categoryMapper.updateById(updateObj); + } + + private void validateCategorySaveData(WmsItemCategorySaveReqVO reqVO) { + validateCategoryCodeUnique(reqVO.getId(), reqVO.getCode()); + validateCategoryNameUnique(reqVO.getId(), reqVO.getParentId(), reqVO.getName()); + validateParentCategory(reqVO.getId(), reqVO.getParentId()); + } + + private void validateCategoryCodeUnique(Long id, String code) { + WmsItemCategoryDO category = categoryMapper.selectByCode(code); + if (category == null) { + return; + } + // 如果 id 为空,说明新增;和数据库已有 code 重复 + if (id == null) { + throw exception(ITEM_CATEGORY_CODE_DUPLICATE); + } + if (ObjectUtil.notEqual(category.getId(), id)) { + throw exception(ITEM_CATEGORY_CODE_DUPLICATE); + } + } + + private void validateCategoryNameUnique(Long id, Long parentId, String name) { + WmsItemCategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name); + if (category == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的商品分类 + if (id == null) { + throw exception(ITEM_CATEGORY_NAME_DUPLICATE); + } + if (ObjectUtil.notEqual(category.getId(), id)) { + throw exception(ITEM_CATEGORY_NAME_DUPLICATE); + } + } + + private void validateParentCategory(Long id, Long parentId) { + if (parentId == null || WmsItemCategoryDO.PARENT_ID_ROOT.equals(parentId)) { + return; + } + // 1. 不能设置自己为父分类 + if (ObjectUtil.equal(parentId, id)) { + throw exception(ITEM_CATEGORY_PARENT_ERROR); + } + // 2. 父分类不存在 + WmsItemCategoryDO parentCategory = categoryMapper.selectById(parentId); + if (parentCategory == null) { + throw exception(ITEM_CATEGORY_PARENT_NOT_EXISTS); + } + // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路 + if (id == null) { // id 为空,说明新增,不需要考虑环路 + return; + } + for (int i = 0; i < Short.MAX_VALUE; i++) { + // 3.1 校验环路 + parentId = parentCategory.getParentId(); + if (ObjectUtil.equal(id, parentId)) { + throw exception(ITEM_CATEGORY_PARENT_IS_CHILD); + } + // 3.2 继续递归上级父分类 + if (parentId == null || WmsItemCategoryDO.PARENT_ID_ROOT.equals(parentId)) { + break; + } + parentCategory = categoryMapper.selectById(parentId); + if (parentCategory == null) { + break; + } + } + } + + @Override + public void deleteItemCategory(Long id) { + // 校验存在 + validateItemCategoryExists(id); + // 有子分类不能删 + if (categoryMapper.selectCountByParentId(id) > 0) { + throw exception(ITEM_CATEGORY_HAS_CHILDREN); + } + // 校验分类下不存在商品 + if (itemService.getItemCountByCategoryId(id) > 0) { + throw exception(ITEM_CATEGORY_HAS_ITEM); + } + + // 删除 + categoryMapper.deleteById(id); + } + + @Override + public WmsItemCategoryDO validateItemCategoryExists(Long id) { + WmsItemCategoryDO category = categoryMapper.selectById(id); + if (category == null) { + throw exception(ITEM_CATEGORY_NOT_EXISTS); + } + return category; + } + + @Override + public WmsItemCategoryDO getItemCategory(Long id) { + return categoryMapper.selectById(id); + } + + @Override + public List getItemCategoryList(WmsItemCategoryListReqVO listReqVO) { + return categoryMapper.selectList(listReqVO); + } + + @Override + public List getItemCategoryList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return categoryMapper.selectByIds(ids); + } + + @Override + public Set getSelfAndChildItemCategoryIdList(Long id) { + if (id == null) { + return null; + } + Set ids = new HashSet<>(); + ids.add(id); + Collection parentIds = Collections.singleton(id); + for (int i = 0; i < Short.MAX_VALUE; i++) { + List children = categoryMapper.selectListByParentIds(parentIds); + if (CollUtil.isEmpty(children)) { + break; + } + ids.addAll(convertSet(children, WmsItemCategoryDO::getId)); + parentIds = convertSet(children, WmsItemCategoryDO::getId); + } + return ids; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemService.java new file mode 100644 index 000000000..c7488292a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemService.java @@ -0,0 +1,111 @@ +package cn.iocoder.yudao.module.wms.service.md.item; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * WMS 商品 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsItemService { + + /** + * 创建商品 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createItem(@Valid WmsItemSaveReqVO createReqVO); + + /** + * 更新商品 + * + * @param updateReqVO 更新信息 + */ + void updateItem(@Valid WmsItemSaveReqVO updateReqVO); + + /** + * 删除商品 + * + * @param id 编号 + */ + void deleteItem(Long id); + + /** + * 校验商品存在 + * + * @param id 编号 + * @return 商品 + */ + WmsItemDO validateItemExists(Long id); + + /** + * 获得商品 + * + * @param id 编号 + * @return 商品 + */ + WmsItemDO getItem(Long id); + + /** + * 获得商品分页 + * + * @param pageReqVO 分页查询 + * @return 商品分页 + */ + PageResult getItemPage(WmsItemPageReqVO pageReqVO); + + /** + * 获得商品列表 + * + * @param listReqVO 查询条件 + * @return 商品列表 + */ + List getItemList(WmsItemListReqVO listReqVO); + + /** + * 按编号集合获得商品列表 + * + * @param ids 编号集合 + * @return 商品列表 + */ + List getItemList(Collection ids); + + /** + * 按编号集合获得商品 Map + * + * @param ids 编号集合 + * @return 商品 Map + */ + default Map getItemMap(Collection ids) { + return convertMap(getItemList(ids), WmsItemDO::getId); + } + + /** + * 获得指定商品分类下的商品数量 + * + * @param categoryId 商品分类编号 + * @return 商品数量 + */ + long getItemCountByCategoryId(Long categoryId); + + /** + * 获得指定商品品牌下的商品数量 + * + * @param brandId 商品品牌编号 + * @return 商品数量 + */ + long getItemCountByBrandId(Long brandId); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemServiceImpl.java new file mode 100644 index 000000000..191ca86a2 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemServiceImpl.java @@ -0,0 +1,171 @@ +package cn.iocoder.yudao.module.wms.service.md.item; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemMapper; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 商品 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsItemServiceImpl implements WmsItemService { + + @Resource + private WmsItemMapper itemMapper; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsItemCategoryService categoryService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsItemBrandService brandService; + @Resource + private WmsItemSkuService itemSkuService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createItem(WmsItemSaveReqVO createReqVO) { + // 校验数据 + validateItemSaveData(createReqVO); + + // 插入商品 + WmsItemDO item = BeanUtils.toBean(createReqVO, WmsItemDO.class); + itemMapper.insert(item); + // 插入 SKU + itemSkuService.createItemSkuList(item.getId(), createReqVO.getSkus()); + return item.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateItem(WmsItemSaveReqVO updateReqVO) { + // 校验存在 + validateItemExists(updateReqVO.getId()); + // 校验数据 + validateItemSaveData(updateReqVO); + + // 更新商品 + WmsItemDO updateObj = BeanUtils.toBean(updateReqVO, WmsItemDO.class); + itemMapper.updateById(updateObj); + // 更新 SKU + itemSkuService.updateItemSkuList(updateReqVO.getId(), updateReqVO.getSkus()); + } + + private void validateItemSaveData(WmsItemSaveReqVO reqVO) { + // 校验商品编号唯一 + validateItemCodeUnique(reqVO.getId(), reqVO.getCode()); + // 校验商品名称唯一 + validateItemNameUnique(reqVO.getId(), reqVO.getName()); + // 校验商品分类存在 + validateCategoryExists(reqVO.getCategoryId()); + // 校验商品品牌存在 + validateBrandExists(reqVO.getBrandId()); + } + + private void validateItemCodeUnique(Long id, String code) { + WmsItemDO item = itemMapper.selectByCode(code); + if (item == null) { + return; + } + if (id == null || ObjectUtil.notEqual(item.getId(), id)) { + throw exception(ITEM_CODE_DUPLICATE); + } + } + + private void validateItemNameUnique(Long id, String name) { + WmsItemDO item = itemMapper.selectByName(name); + if (item == null) { + return; + } + if (id == null || ObjectUtil.notEqual(item.getId(), id)) { + throw exception(ITEM_NAME_DUPLICATE); + } + } + + private void validateCategoryExists(Long categoryId) { + categoryService.validateItemCategoryExists(categoryId); + } + + private void validateBrandExists(Long brandId) { + if (brandId == null) { + return; + } + brandService.validateItemBrandExists(brandId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteItem(Long id) { + // 校验存在 + validateItemExists(id); + + // 删除商品和 SKU + itemSkuService.deleteItemSkuListByItemId(id); + itemMapper.deleteById(id); + } + + @Override + public WmsItemDO validateItemExists(Long id) { + WmsItemDO item = itemMapper.selectById(id); + if (item == null) { + throw exception(ITEM_NOT_EXISTS); + } + return item; + } + + @Override + public WmsItemDO getItem(Long id) { + return itemMapper.selectById(id); + } + + @Override + public PageResult getItemPage(WmsItemPageReqVO pageReqVO) { + return itemMapper.selectPage(pageReqVO, + categoryService.getSelfAndChildItemCategoryIdList(pageReqVO.getCategoryId())); + } + + @Override + public List getItemList(WmsItemListReqVO listReqVO) { + return itemMapper.selectList(listReqVO, + categoryService.getSelfAndChildItemCategoryIdList(listReqVO.getCategoryId())); + } + + @Override + public List getItemList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return itemMapper.selectByIds(ids); + } + + @Override + public long getItemCountByCategoryId(Long categoryId) { + return itemMapper.selectCountByCategoryId(categoryId); + } + + @Override + public long getItemCountByBrandId(Long brandId) { + return itemMapper.selectCountByBrandId(brandId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuService.java new file mode 100644 index 000000000..456742c34 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuService.java @@ -0,0 +1,116 @@ +package cn.iocoder.yudao.module.wms.service.md.item; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; + +/** + * WMS 商品 SKU Service 接口 + * + * @author 芋道源码 + */ +public interface WmsItemSkuService { + + /** + * 创建商品 SKU 列表 + * + * @param itemId 商品编号 + * @param skus SKU 列表 + */ + void createItemSkuList(Long itemId, @Valid List skus); + + /** + * 更新商品 SKU 列表 + * + * @param itemId 商品编号 + * @param skus SKU 列表 + */ + void updateItemSkuList(Long itemId, @Valid List skus); + + /** + * 按商品编号删除 SKU 列表 + * + * @param itemId 商品编号 + */ + void deleteItemSkuListByItemId(Long itemId); + + /** + * 校验 SKU 存在 + * + * @param id 编号 + * @return SKU + */ + WmsItemSkuDO validateItemSkuExists(Long id); + + /** + * 按商品编号获得 SKU 列表 + * + * @param itemId 商品编号 + * @return SKU 列表 + */ + List getItemSkuList(Long itemId); + + /** + * 按商品编号集合获得 SKU 列表 + * + * @param itemIds 商品编号集合 + * @return SKU 列表 + */ + List getItemSkuList(Collection itemIds); + + /** + * 获得 SKU 列表 + * + * @param itemIds 商品编号集合 + * @param code SKU 编号 + * @param name SKU 名称 + * @return SKU 列表 + */ + List getItemSkuList(Collection itemIds, String code, String name); + + /** + * 按编号集合获得 SKU 列表 + * + * @param ids 编号集合 + * @return SKU 列表 + */ + List getItemSkuListByIds(Collection ids); + + /** + * 按 SKU 维度分页查询,支持商品 / 品牌 / 分类多表联查筛选。 + * + * @param pageReqVO 分页与筛选条件 + * @return SKU 分页结果 + */ + PageResult getItemSkuPage(WmsItemSkuPageReqVO pageReqVO); + + /** + * 按编号集合获得 SKU Map + * + * @param ids 编号集合 + * @return SKU Map + */ + default Map getItemSkuMap(Collection ids) { + return convertMap(getItemSkuListByIds(ids), WmsItemSkuDO::getId); + } + + /** + * 按商品编号集合获得 SKU MultiMap + * + * @param itemIds 商品编号集合 + * @return 商品编号与 SKU 列表的映射 + */ + default Map> getItemSkuMultiMap(Collection itemIds) { + return convertMultiMap(getItemSkuList(itemIds), WmsItemSkuDO::getItemId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuServiceImpl.java new file mode 100644 index 000000000..8306fc03f --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuServiceImpl.java @@ -0,0 +1,190 @@ +package cn.iocoder.yudao.module.wms.service.md.item; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemSkuMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.order.check.WmsCheckOrderDetailService; +import cn.iocoder.yudao.module.wms.service.order.movement.WmsMovementOrderDetailService; +import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderDetailService; +import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderDetailService; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 商品 SKU Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsItemSkuServiceImpl implements WmsItemSkuService { + + @Resource + private WmsItemSkuMapper itemSkuMapper; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsInventoryService inventoryService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsReceiptOrderDetailService receiptOrderDetailService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsShipmentOrderDetailService shipmentOrderDetailService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsMovementOrderDetailService movementOrderDetailService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsCheckOrderDetailService checkOrderDetailService; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createItemSkuList(Long itemId, List skus) { + validateItemSkuList(skus); + List list = BeanUtils.toBean(skus, WmsItemSkuDO.class, sku -> + sku.setItemId(itemId).setId(null)); // id 由数据库自增分配,避免前端误操作 + itemSkuMapper.insertBatch(list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateItemSkuList(Long itemId, List skus) { + validateItemSkuList(skus); + + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = itemSkuMapper.selectListByItemId(itemId); + List newList = BeanUtils.toBean(skus, WmsItemSkuDO.class); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> ObjectUtil.equal(oldVal.getId(), newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(2))) { + List deleteSkuIds = convertList(diffList.get(2), WmsItemSkuDO::getId); + // 校验 SKU 是否被使用 + validateItemSkuUnused(diffList.get(2)); + // 删除 SKU + itemSkuMapper.deleteByIds(deleteSkuIds); + } + if (CollUtil.isNotEmpty(diffList.get(0))) { + // 新增场景下忽略前端误传的 SKU id:统一置 null,由数据库自增分配 + diffList.get(0).forEach(sku -> { + sku.setItemId(itemId); + sku.setId(null); + }); + itemSkuMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + diffList.get(1).forEach(sku -> sku.setItemId(itemId)); + itemSkuMapper.updateBatch(diffList.get(1)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteItemSkuListByItemId(Long itemId) { + // 校验 SKU 是否被使用 + List skus = itemSkuMapper.selectListByItemId(itemId); + validateItemSkuUnused(skus); + // 删除 SKU + itemSkuMapper.deleteByItemId(itemId); + } + + @Override + public WmsItemSkuDO validateItemSkuExists(Long id) { + WmsItemSkuDO sku = itemSkuMapper.selectById(id); + if (sku == null) { + throw exception(ITEM_SKU_NOT_EXISTS); + } + return sku; + } + + @Override + public List getItemSkuList(Long itemId) { + return itemSkuMapper.selectListByItemId(itemId); + } + + @Override + public List getItemSkuList(Collection itemIds) { + return itemSkuMapper.selectListByItemIds(itemIds); + } + + @Override + public List getItemSkuList(Collection itemIds, String code, String name) { + return itemSkuMapper.selectList(itemIds, code, name); + } + + @Override + public List getItemSkuListByIds(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.of(); + } + return itemSkuMapper.selectByIds(ids); + } + + @Override + public PageResult getItemSkuPage(WmsItemSkuPageReqVO pageReqVO) { + return itemSkuMapper.selectPage(pageReqVO); + } + + private void validateItemSkuList(List skus) { + // 校验至少存在一个商品 SKU + if (CollUtil.isEmpty(skus)) { + throw exception(ITEM_SKU_REQUIRED); + } + // 校验 SKU 名称不重复 + Set names = new HashSet<>(); + for (WmsItemSkuSaveReqVO sku : skus) { + if (!names.add(sku.getName())) { + throw exception(ITEM_SKU_NAME_DUPLICATE, sku.getName()); + } + } + } + + private void validateItemSkuUnused(List skus) { + if (CollUtil.isEmpty(skus)) { + return; + } + for (WmsItemSkuDO sku : skus) { + validateItemSkuOrderUnused(sku); + if (inventoryService.getInventoryCountBySkuId(sku.getId()) > 0) { + throw exception(ITEM_SKU_HAS_INVENTORY, sku.getName()); + } + } + } + + private void validateItemSkuOrderUnused(WmsItemSkuDO sku) { + if (receiptOrderDetailService.getReceiptOrderDetailCountBySkuId(sku.getId()) > 0) { + throw exception(ITEM_SKU_HAS_ORDER, sku.getName(), WmsOrderTypeEnum.RECEIPT.getName()); + } + if (shipmentOrderDetailService.getShipmentOrderDetailCountBySkuId(sku.getId()) > 0) { + throw exception(ITEM_SKU_HAS_ORDER, sku.getName(), WmsOrderTypeEnum.SHIPMENT.getName()); + } + if (movementOrderDetailService.getMovementOrderDetailCountBySkuId(sku.getId()) > 0) { + throw exception(ITEM_SKU_HAS_ORDER, sku.getName(), WmsOrderTypeEnum.MOVEMENT.getName()); + } + if (checkOrderDetailService.getCheckOrderDetailCountBySkuId(sku.getId()) > 0) { + throw exception(ITEM_SKU_HAS_ORDER, sku.getName(), WmsOrderTypeEnum.CHECK.getName()); + } + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/merchant/WmsMerchantService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/merchant/WmsMerchantService.java new file mode 100644 index 000000000..aa77e2674 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/merchant/WmsMerchantService.java @@ -0,0 +1,113 @@ +package cn.iocoder.yudao.module.wms.service.md.merchant; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * WMS 往来企业 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsMerchantService { + + /** + * 创建往来企业 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createMerchant(@Valid WmsMerchantSaveReqVO createReqVO); + + /** + * 更新往来企业 + * + * @param updateReqVO 更新信息 + */ + void updateMerchant(@Valid WmsMerchantSaveReqVO updateReqVO); + + /** + * 删除往来企业 + * + * @param id 编号 + */ + void deleteMerchant(Long id); + + /** + * 校验往来企业存在 + * + * @param id 编号 + * @return 往来企业 + */ + WmsMerchantDO validateMerchantExists(Long id); + + /** + * 校验供应商存在 + * + * @param id 编号 + * @return 往来企业 + */ + @SuppressWarnings("UnusedReturnValue") + WmsMerchantDO validateSupplierMerchantExists(Long id); + + /** + * 校验客户存在 + * + * @param id 编号 + * @return 往来企业 + */ + @SuppressWarnings("UnusedReturnValue") + WmsMerchantDO validateCustomerMerchantExists(Long id); + + /** + * 获得往来企业 + * + * @param id 编号 + * @return 往来企业 + */ + WmsMerchantDO getMerchant(Long id); + + /** + * 获得往来企业分页 + * + * @param pageReqVO 分页查询 + * @return 往来企业分页 + */ + PageResult getMerchantPage(WmsMerchantPageReqVO pageReqVO); + + /** + * 获得往来企业列表 + * + * @param listReqVO 查询条件 + * @return 往来企业列表 + */ + List getMerchantList(WmsMerchantListReqVO listReqVO); + + /** + * 按编号集合获得往来企业列表 + * + * @param ids 编号集合 + * @return 往来企业列表 + */ + List getMerchantList(Collection ids); + + /** + * 按编号集合获得往来企业 Map + * + * @param ids 编号集合 + * @return 往来企业 Map + */ + default Map getMerchantMap(Collection ids) { + return convertMap(getMerchantList(ids), WmsMerchantDO::getId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/merchant/WmsMerchantServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/merchant/WmsMerchantServiceImpl.java new file mode 100644 index 000000000..8b4acddf5 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/merchant/WmsMerchantServiceImpl.java @@ -0,0 +1,166 @@ +package cn.iocoder.yudao.module.wms.service.md.merchant; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantListReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.merchant.vo.WmsMerchantSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO; +import cn.iocoder.yudao.module.wms.dal.mysql.md.merchant.WmsMerchantMapper; +import cn.iocoder.yudao.module.wms.enums.md.WmsMerchantTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderService; +import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderService; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 往来企业 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsMerchantServiceImpl implements WmsMerchantService { + + @Resource + private WmsMerchantMapper merchantMapper; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsReceiptOrderService receiptOrderService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsShipmentOrderService shipmentOrderService; + + @Override + public Long createMerchant(WmsMerchantSaveReqVO createReqVO) { + validateMerchantSaveData(null, createReqVO); + + // 新增 + WmsMerchantDO merchant = BeanUtils.toBean(createReqVO, WmsMerchantDO.class); + merchantMapper.insert(merchant); + return merchant.getId(); + } + + @Override + public void updateMerchant(WmsMerchantSaveReqVO updateReqVO) { + // 校验存在 + validateMerchantExists(updateReqVO.getId()); + validateMerchantSaveData(updateReqVO.getId(), updateReqVO); + + // 更新 + WmsMerchantDO updateObj = BeanUtils.toBean(updateReqVO, WmsMerchantDO.class); + merchantMapper.updateById(updateObj); + } + + private void validateMerchantSaveData(Long id, WmsMerchantSaveReqVO reqVO) { + // 校验 code 唯一 + validateMerchantCodeUnique(id, reqVO.getCode()); + // 校验 name 唯一 + validateMerchantNameUnique(id, reqVO.getName()); + } + + private void validateMerchantCodeUnique(Long id, String code) { + WmsMerchantDO merchant = merchantMapper.selectByCode(code); + if (merchant == null) { + return; + } + if (id == null || ObjectUtil.notEqual(merchant.getId(), id)) { + throw exception(MERCHANT_CODE_DUPLICATE); + } + } + + private void validateMerchantNameUnique(Long id, String name) { + WmsMerchantDO merchant = merchantMapper.selectByName(name); + if (merchant == null) { + return; + } + if (id == null || ObjectUtil.notEqual(merchant.getId(), id)) { + throw exception(MERCHANT_NAME_DUPLICATE); + } + } + + @Override + public void deleteMerchant(Long id) { + // 校验存在 + validateMerchantExists(id); + // 校验未被单据使用 + validateMerchantUnused(id); + + // 删除 + merchantMapper.deleteById(id); + } + + private void validateMerchantUnused(Long id) { + if (receiptOrderService.getReceiptOrderCountByMerchantId(id) > 0) { + throw exception(MERCHANT_HAS_ORDER, WmsOrderTypeEnum.RECEIPT.getName()); + } + if (shipmentOrderService.getShipmentOrderCountByMerchantId(id) > 0) { + throw exception(MERCHANT_HAS_ORDER, WmsOrderTypeEnum.SHIPMENT.getName()); + } + } + + @Override + public WmsMerchantDO validateMerchantExists(Long id) { + WmsMerchantDO merchant = merchantMapper.selectById(id); + if (merchant == null) { + throw exception(MERCHANT_NOT_EXISTS); + } + return merchant; + } + + @Override + public WmsMerchantDO validateSupplierMerchantExists(Long id) { + WmsMerchantDO merchant = validateMerchantExists(id); + if (ObjectUtil.notEqual(merchant.getType(), WmsMerchantTypeEnum.SUPPLIER.getType()) + && ObjectUtil.notEqual(merchant.getType(), WmsMerchantTypeEnum.CUSTOMER_SUPPLIER.getType())) { + throw exception(MERCHANT_NOT_SUPPLIER); + } + return merchant; + } + + @Override + public WmsMerchantDO validateCustomerMerchantExists(Long id) { + WmsMerchantDO merchant = validateMerchantExists(id); + if (ObjectUtil.notEqual(merchant.getType(), WmsMerchantTypeEnum.CUSTOMER.getType()) + && ObjectUtil.notEqual(merchant.getType(), WmsMerchantTypeEnum.CUSTOMER_SUPPLIER.getType())) { + throw exception(MERCHANT_NOT_CUSTOMER); + } + return merchant; + } + + @Override + public WmsMerchantDO getMerchant(Long id) { + return merchantMapper.selectById(id); + } + + @Override + public PageResult getMerchantPage(WmsMerchantPageReqVO pageReqVO) { + return merchantMapper.selectPage(pageReqVO); + } + + @Override + public List getMerchantList(WmsMerchantListReqVO listReqVO) { + return merchantMapper.selectList(listReqVO); + } + + @Override + public List getMerchantList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.of(); + } + return merchantMapper.selectByIds(ids); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseService.java new file mode 100644 index 000000000..dd73e7544 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseService.java @@ -0,0 +1,87 @@ +package cn.iocoder.yudao.module.wms.service.md.warehouse; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehousePageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehouseSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * WMS 仓库 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsWarehouseService { + + /** + * 创建仓库 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createWarehouse(@Valid WmsWarehouseSaveReqVO createReqVO); + + /** + * 更新仓库 + * + * @param updateReqVO 更新信息 + */ + void updateWarehouse(@Valid WmsWarehouseSaveReqVO updateReqVO); + + /** + * 删除仓库 + * + * @param id 编号 + */ + void deleteWarehouse(Long id); + + /** + * 校验仓库存在 + * + * @param id 编号 + * @return 仓库 + */ + WmsWarehouseDO validateWarehouseExists(Long id); + + /** + * 获得仓库 + * + * @param id 编号 + * @return 仓库 + */ + WmsWarehouseDO getWarehouse(Long id); + + /** + * 获得仓库分页 + * + * @param pageReqVO 分页查询 + * @return 仓库分页 + */ + PageResult getWarehousePage(WmsWarehousePageReqVO pageReqVO); + + /** + * 获得仓库列表 + * + * @return 仓库列表 + */ + List getWarehouseList(); + + /** + * 按编号集合获得仓库列表 + * + * @param ids 编号集合 + * @return 仓库列表 + */ + List getWarehouseList(Collection ids); + + default Map getWarehouseMap(Collection ids) { + return convertMap(getWarehouseList(ids), WmsWarehouseDO::getId); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseServiceImpl.java new file mode 100644 index 000000000..cdcb69e40 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseServiceImpl.java @@ -0,0 +1,166 @@ +package cn.iocoder.yudao.module.wms.service.md.warehouse; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehousePageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehouseSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.mysql.md.warehouse.WmsWarehouseMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.order.check.WmsCheckOrderService; +import cn.iocoder.yudao.module.wms.service.order.movement.WmsMovementOrderService; +import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderService; +import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderService; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 仓库 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsWarehouseServiceImpl implements WmsWarehouseService { + + @Resource + private WmsWarehouseMapper warehouseMapper; + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsInventoryService inventoryService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsReceiptOrderService receiptOrderService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsShipmentOrderService shipmentOrderService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsMovementOrderService movementOrderService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private WmsCheckOrderService checkOrderService; + + @Override + public Long createWarehouse(WmsWarehouseSaveReqVO createReqVO) { + // 校验数据 + validateWarehouseSaveData(createReqVO); + + // 插入 + WmsWarehouseDO warehouse = BeanUtils.toBean(createReqVO, WmsWarehouseDO.class); + warehouseMapper.insert(warehouse); + return warehouse.getId(); + } + + @Override + public void updateWarehouse(WmsWarehouseSaveReqVO updateReqVO) { + // 校验存在 + validateWarehouseExists(updateReqVO.getId()); + // 校验数据 + validateWarehouseSaveData(updateReqVO); + + // 更新 + WmsWarehouseDO updateObj = BeanUtils.toBean(updateReqVO, WmsWarehouseDO.class); + warehouseMapper.updateById(updateObj); + } + + private void validateWarehouseSaveData(WmsWarehouseSaveReqVO reqVO) { + validateWarehouseNameUnique(reqVO.getId(), reqVO.getName()); + validateWarehouseCodeUnique(reqVO.getId(), reqVO.getCode()); + } + + private void validateWarehouseNameUnique(Long id, String name) { + WmsWarehouseDO warehouse = warehouseMapper.selectByName(name); + if (warehouse == null) { + return; + } + if (ObjUtil.notEqual(warehouse.getId(), id)) { + throw exception(WAREHOUSE_NAME_DUPLICATE); + } + } + + private void validateWarehouseCodeUnique(Long id, String code) { + WmsWarehouseDO warehouse = warehouseMapper.selectByCode(code); + if (warehouse == null) { + return; + } + if (ObjUtil.notEqual(warehouse.getId(), id)) { + throw exception(WAREHOUSE_CODE_DUPLICATE); + } + } + + @Override + public void deleteWarehouse(Long id) { + // 校验存在 + validateWarehouseExists(id); + // 校验未被单据使用 + validateWarehouseUnused(id); + + // 删除 + warehouseMapper.deleteById(id); + } + + private void validateWarehouseUnused(Long id) { + if (inventoryService.getInventoryCountByWarehouseId(id) > 0) { + throw exception(WAREHOUSE_HAS_INVENTORY); + } + if (receiptOrderService.getReceiptOrderCountByWarehouseId(id) > 0) { + throw exception(WAREHOUSE_HAS_ORDER, WmsOrderTypeEnum.RECEIPT.getName()); + } + if (shipmentOrderService.getShipmentOrderCountByWarehouseId(id) > 0) { + throw exception(WAREHOUSE_HAS_ORDER, WmsOrderTypeEnum.SHIPMENT.getName()); + } + if (movementOrderService.getMovementOrderCountByWarehouseId(id) > 0) { + throw exception(WAREHOUSE_HAS_ORDER, WmsOrderTypeEnum.MOVEMENT.getName()); + } + if (checkOrderService.getCheckOrderCountByWarehouseId(id) > 0) { + throw exception(WAREHOUSE_HAS_ORDER, WmsOrderTypeEnum.CHECK.getName()); + } + } + + @Override + public WmsWarehouseDO validateWarehouseExists(Long id) { + WmsWarehouseDO warehouse = warehouseMapper.selectById(id); + if (warehouse == null) { + throw exception(WAREHOUSE_NOT_EXISTS); + } + return warehouse; + } + + @Override + public WmsWarehouseDO getWarehouse(Long id) { + return warehouseMapper.selectById(id); + } + + @Override + public PageResult getWarehousePage(WmsWarehousePageReqVO pageReqVO) { + return warehouseMapper.selectPage(pageReqVO); + } + + @Override + public List getWarehouseList() { + return warehouseMapper.selectSimpleList(); + } + + @Override + public List getWarehouseList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return warehouseMapper.selectByIds(ids); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailService.java new file mode 100644 index 000000000..023195150 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailService.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.wms.service.order.check; + +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 盘库单明细 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsCheckOrderDetailService { + + /** + * 创建盘库单明细列表 + * + * @param orderId 盘库单编号 + * @param reqVO 盘库单保存信息 + */ + void createCheckOrderDetailList(Long orderId, WmsCheckOrderSaveReqVO reqVO); + + /** + * 更新盘库单明细列表 + * + * @param orderId 盘库单编号 + * @param reqVO 盘库单保存信息 + */ + void updateCheckOrderDetailList(Long orderId, WmsCheckOrderSaveReqVO reqVO); + + /** + * 按盘库单编号删除明细列表 + * + * @param orderId 盘库单编号 + */ + void deleteCheckOrderDetailListByOrderId(Long orderId); + + /** + * 按盘库单编号获得明细列表 + * + * @param orderId 盘库单编号 + * @return 明细列表 + */ + List getCheckOrderDetailList(Long orderId); + + /** + * 按盘库单编号集合获得明细列表 + * + * @param orderIds 盘库单编号集合 + * @return 明细列表 + */ + List getCheckOrderDetailList(Collection orderIds); + + /** + * 校验盘库单明细列表存在 + * + * @param orderId 盘库单编号 + * @return 明细列表 + */ + List validateCheckOrderDetailListExists(Long orderId); + + /** + * 获得指定 SKU 的盘库单明细数量 + * + * @param skuId SKU 编号 + * @return 盘库单明细数量 + */ + long getCheckOrderDetailCountBySkuId(Long skuId); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailServiceImpl.java new file mode 100644 index 000000000..6afeebc1e --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailServiceImpl.java @@ -0,0 +1,120 @@ +package cn.iocoder.yudao.module.wms.service.order.check; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderDetailMapper; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 盘库单明细 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsCheckOrderDetailServiceImpl implements WmsCheckOrderDetailService { + + @Resource + private WmsCheckOrderDetailMapper checkOrderDetailMapper; + @Resource + private WmsItemSkuService itemSkuService; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createCheckOrderDetailList(Long orderId, WmsCheckOrderSaveReqVO reqVO) { + List list = buildCheckOrderDetailList(reqVO); + if (CollUtil.isEmpty(list)) { + return; + } + list.forEach(detail -> detail.setId(null).setOrderId(orderId)); + checkOrderDetailMapper.insertBatch(list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateCheckOrderDetailList(Long orderId, WmsCheckOrderSaveReqVO reqVO) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = checkOrderDetailMapper.selectListByOrderId(orderId); + List list = buildCheckOrderDetailList(reqVO); + List newList = CollUtil.isEmpty(list) ? ListUtil.of() : list; + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> ObjectUtil.equal(oldVal.getId(), newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + if (CollUtil.isNotEmpty(convertList(diffList.get(0), WmsCheckOrderDetailDO::getId))) { + throw exception(CHECK_ORDER_DETAIL_NOT_EXISTS); + } + diffList.get(0).forEach(detail -> detail.setOrderId(orderId)); + checkOrderDetailMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + diffList.get(1).forEach(detail -> detail.setOrderId(orderId)); + checkOrderDetailMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + checkOrderDetailMapper.deleteByIds(convertList(diffList.get(2), WmsCheckOrderDetailDO::getId)); + } + } + + @Override + public void deleteCheckOrderDetailListByOrderId(Long orderId) { + checkOrderDetailMapper.deleteByOrderId(orderId); + } + + @Override + public List getCheckOrderDetailList(Long orderId) { + return checkOrderDetailMapper.selectListByOrderId(orderId); + } + + @Override + public List getCheckOrderDetailList(Collection orderIds) { + if (CollUtil.isEmpty(orderIds)) { + return ListUtil.of(); + } + return checkOrderDetailMapper.selectListByOrderIds(orderIds); + } + + @Override + public List validateCheckOrderDetailListExists(Long orderId) { + List details = checkOrderDetailMapper.selectListByOrderId(orderId); + if (CollUtil.isEmpty(details)) { + throw exception(CHECK_ORDER_DETAIL_REQUIRED); + } + return details; + } + + @Override + public long getCheckOrderDetailCountBySkuId(Long skuId) { + return checkOrderDetailMapper.selectCountBySkuId(skuId); + } + + private List buildCheckOrderDetailList(WmsCheckOrderSaveReqVO reqVO) { + if (CollUtil.isEmpty(reqVO.getDetails())) { + return ListUtil.of(); + } + return convertList(reqVO.getDetails(), detail -> { + // 校验 SKU 存在 + itemSkuService.validateItemSkuExists(detail.getSkuId()); + // 构建对象 + return BeanUtils.toBean(detail, WmsCheckOrderDetailDO.class).setWarehouseId(reqVO.getWarehouseId()); + }); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderService.java new file mode 100644 index 000000000..659d7c257 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderService.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.wms.service.order.check; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO; +import jakarta.validation.Valid; + +/** + * WMS 盘库单 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsCheckOrderService { + + /** + * 创建盘库单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createCheckOrder(@Valid WmsCheckOrderSaveReqVO createReqVO); + + /** + * 更新盘库单 + * + * @param updateReqVO 更新信息 + */ + void updateCheckOrder(@Valid WmsCheckOrderSaveReqVO updateReqVO); + + /** + * 删除盘库单 + * + * @param id 编号 + */ + void deleteCheckOrder(Long id); + + /** + * 完成盘库 + * + * @param id 编号 + */ + void completeCheckOrder(Long id); + + /** + * 作废盘库单 + * + * @param id 编号 + */ + void cancelCheckOrder(Long id); + + /** + * 获得盘库单 + * + * @param id 编号 + * @return 盘库单 + */ + WmsCheckOrderDO getCheckOrder(Long id); + + /** + * 获得盘库单分页 + * + * @param pageReqVO 分页查询 + * @return 盘库单分页 + */ + PageResult getCheckOrderPage(WmsCheckOrderPageReqVO pageReqVO); + + /** + * 获得指定仓库的盘库单数量 + * + * @param warehouseId 仓库编号 + * @return 盘库单数量 + */ + long getCheckOrderCountByWarehouseId(Long warehouseId); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImpl.java new file mode 100644 index 000000000..d7beb83b9 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImpl.java @@ -0,0 +1,222 @@ +package cn.iocoder.yudao.module.wms.service.order.check; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryCheckReqDTO; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 盘库单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsCheckOrderServiceImpl implements WmsCheckOrderService { + + @Resource + private WmsCheckOrderMapper checkOrderMapper; + @Resource + private WmsCheckOrderDetailService checkOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsInventoryService inventoryService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createCheckOrder(WmsCheckOrderSaveReqVO createReqVO) { + // 1. 校验盘库单保存数据 + validateCheckOrderSaveData(createReqVO); + + // 2.1 插入盘库单 + WmsCheckOrderDO order = BeanUtils.toBean(createReqVO, WmsCheckOrderDO.class); + order.setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + fillCheckOrderTotal(order, createReqVO); + checkOrderMapper.insert(order); + // 2.2 插入盘库单明细 + checkOrderDetailService.createCheckOrderDetailList(order.getId(), createReqVO); + return order.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateCheckOrder(WmsCheckOrderSaveReqVO updateReqVO) { + // 1. 校验盘库单保存数据 + validateCheckOrderPrepare(updateReqVO.getId()); + validateCheckOrderSaveData(updateReqVO); + + // 2.1 更新盘库单 + WmsCheckOrderDO updateObj = BeanUtils.toBean(updateReqVO, WmsCheckOrderDO.class) + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + fillCheckOrderTotal(updateObj, updateReqVO); + int updateCount = checkOrderMapper.updateByIdAndStatus(updateReqVO.getId(), + WmsOrderStatusEnum.PREPARE.getStatus(), updateObj); + if (updateCount == 0) { + throw exception(CHECK_ORDER_STATUS_NOT_PREPARE); + } + // 2.2 更新盘库单明细 + checkOrderDetailService.updateCheckOrderDetailList(updateReqVO.getId(), updateReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteCheckOrder(Long id) { + // 1. 校验存在,且可删除 + WmsCheckOrderDO order = validateCheckOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus()) + && ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.CANCELED.getStatus())) { + throw exception(CHECK_ORDER_STATUS_NOT_DELETABLE); + } + + // 2.1 删除盘库单 + checkOrderMapper.deleteById(id); + // 2.2 删除盘库单明细 + checkOrderDetailService.deleteCheckOrderDetailListByOrderId(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void completeCheckOrder(Long id) { + // 1.1 校验存在,且草稿 + WmsCheckOrderDO order = validateCheckOrderPrepare(id); + // 1.2 校验盘库单明细存在 + List details = checkOrderDetailService.validateCheckOrderDetailListExists(id); + + // 2. 完成盘库单 + if (checkOrderMapper.updateByIdAndStatus(id, WmsOrderStatusEnum.PREPARE.getStatus(), + new WmsCheckOrderDO().setStatus(WmsOrderStatusEnum.FINISHED.getStatus())) == 0) { + throw exception(CHECK_ORDER_STATUS_NOT_PREPARE); + } + + // 3. 盘点库存 + inventoryService.checkInventory(buildCheckInventoryReqDTO(order, details)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelCheckOrder(Long id) { + // 1. 校验存在,且草稿 + validateCheckOrderPrepare(id); + + // 2. 作废盘库单 + if (checkOrderMapper.updateByIdAndStatus(id, WmsOrderStatusEnum.PREPARE.getStatus(), + new WmsCheckOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus())) == 0) { + throw exception(CHECK_ORDER_STATUS_NOT_PREPARE); + } + } + + @Override + public WmsCheckOrderDO getCheckOrder(Long id) { + return checkOrderMapper.selectById(id); + } + + @Override + public PageResult getCheckOrderPage(WmsCheckOrderPageReqVO pageReqVO) { + return checkOrderMapper.selectPage(pageReqVO); + } + + @Override + public long getCheckOrderCountByWarehouseId(Long warehouseId) { + return checkOrderMapper.selectCountByWarehouseId(warehouseId); + } + + private void validateCheckOrderSaveData(WmsCheckOrderSaveReqVO reqVO) { + // 校验盘库单号唯一 + validateCheckOrderNoUnique(reqVO.getId(), reqVO.getNo()); + // 校验仓库存在 + warehouseService.validateWarehouseExists(reqVO.getWarehouseId()); + } + + private void validateCheckOrderNoUnique(Long id, String no) { + WmsCheckOrderDO order = checkOrderMapper.selectByNo(no); + if (order == null) { + return; + } + if (id == null || ObjectUtil.notEqual(order.getId(), id)) { + throw exception(CHECK_ORDER_NO_DUPLICATE); + } + } + + private void fillCheckOrderTotal(WmsCheckOrderDO order, WmsCheckOrderSaveReqVO reqVO) { + BigDecimal totalQuantity = BigDecimal.ZERO; + BigDecimal totalPrice = BigDecimal.ZERO; + BigDecimal actualPrice = BigDecimal.ZERO; + if (CollUtil.isNotEmpty(reqVO.getDetails())) { + for (WmsCheckOrderDetailSaveReqVO detail : reqVO.getDetails()) { + BigDecimal differenceQuantity = calculateDifferenceQuantity(detail.getQuantity(), detail.getCheckQuantity()); + totalQuantity = totalQuantity.add(differenceQuantity); + if (detail.getPrice() == null) { + continue; + } + if (detail.getQuantity() != null) { + totalPrice = totalPrice.add(MoneyUtils.priceMultiply(detail.getPrice(), detail.getQuantity())); + } + if (detail.getCheckQuantity() != null) { + actualPrice = actualPrice.add(MoneyUtils.priceMultiply(detail.getPrice(), detail.getCheckQuantity())); + } + } + } + order.setTotalQuantity(totalQuantity).setTotalPrice(totalPrice).setActualPrice(actualPrice); + } + + private BigDecimal calculateDifferenceQuantity(BigDecimal quantity, BigDecimal checkQuantity) { + if (quantity == null || checkQuantity == null) { + return BigDecimal.ZERO; + } + return checkQuantity.subtract(quantity); + } + + private WmsCheckOrderDO validateCheckOrderExists(Long id) { + WmsCheckOrderDO order = id == null ? null : checkOrderMapper.selectById(id); + if (order == null) { + throw exception(CHECK_ORDER_NOT_EXISTS); + } + return order; + } + + /** + * 校验盘库单存在且为草稿状态 + * + * @param id 盘库单编号 + * @return 盘库单 + */ + private WmsCheckOrderDO validateCheckOrderPrepare(Long id) { + WmsCheckOrderDO order = validateCheckOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus())) { + throw exception(CHECK_ORDER_STATUS_NOT_PREPARE); + } + return order; + } + + private WmsInventoryCheckReqDTO buildCheckInventoryReqDTO(WmsCheckOrderDO order, + List details) { + return new WmsInventoryCheckReqDTO() + .setOrderId(order.getId()).setOrderNo(order.getNo()) + .setOrderType(WmsOrderTypeEnum.CHECK.getType()) + .setItems(BeanUtils.toBean(details, WmsInventoryCheckReqDTO.Item.class)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailService.java new file mode 100644 index 000000000..7675ee7eb --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailService.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.wms.service.order.movement; + +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 移库单明细 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsMovementOrderDetailService { + + /** + * 创建移库单明细列表 + * + * @param orderId 移库单编号 + * @param reqVO 移库单保存信息 + */ + void createMovementOrderDetailList(Long orderId, WmsMovementOrderSaveReqVO reqVO); + + /** + * 更新移库单明细列表 + * + * @param orderId 移库单编号 + * @param reqVO 移库单保存信息 + */ + void updateMovementOrderDetailList(Long orderId, WmsMovementOrderSaveReqVO reqVO); + + /** + * 按移库单编号删除明细列表 + * + * @param orderId 移库单编号 + */ + void deleteMovementOrderDetailListByOrderId(Long orderId); + + /** + * 按移库单编号获得明细列表 + * + * @param orderId 移库单编号 + * @return 明细列表 + */ + List getMovementOrderDetailList(Long orderId); + + /** + * 按移库单编号集合获得明细列表 + * + * @param orderIds 移库单编号集合 + * @return 明细列表 + */ + List getMovementOrderDetailList(Collection orderIds); + + /** + * 校验移库单明细列表存在 + * + * @param orderId 移库单编号 + * @return 明细列表 + */ + List validateMovementOrderDetailListExists(Long orderId); + + /** + * 获得指定 SKU 的移库单明细数量 + * + * @param skuId SKU 编号 + * @return 移库单明细数量 + */ + long getMovementOrderDetailCountBySkuId(Long skuId); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailServiceImpl.java new file mode 100644 index 000000000..e5b4bb025 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailServiceImpl.java @@ -0,0 +1,132 @@ +package cn.iocoder.yudao.module.wms.service.order.movement; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderDetailMapper; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 移库单明细 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsMovementOrderDetailServiceImpl implements WmsMovementOrderDetailService { + + @Resource + private WmsMovementOrderDetailMapper movementOrderDetailMapper; + @Resource + private WmsItemSkuService itemSkuService; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createMovementOrderDetailList(Long orderId, WmsMovementOrderSaveReqVO reqVO) { + List list = buildMovementOrderDetailList(reqVO); + if (CollUtil.isEmpty(list)) { + return; + } + list.forEach(detail -> detail.setId(null).setOrderId(orderId)); + movementOrderDetailMapper.insertBatch(list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateMovementOrderDetailList(Long orderId, WmsMovementOrderSaveReqVO reqVO) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = movementOrderDetailMapper.selectListByOrderId(orderId); + List list = buildMovementOrderDetailList(reqVO); + List newList = CollUtil.isEmpty(list) ? ListUtil.of() : list; + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> ObjectUtil.equal(oldVal.getId(), newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + if (CollUtil.isNotEmpty(convertList(diffList.get(0), WmsMovementOrderDetailDO::getId))) { + throw exception(MOVEMENT_ORDER_DETAIL_NOT_EXISTS); + } + diffList.get(0).forEach(detail -> detail.setOrderId(orderId)); + movementOrderDetailMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + diffList.get(1).forEach(detail -> detail.setOrderId(orderId)); + movementOrderDetailMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + movementOrderDetailMapper.deleteByIds(convertList(diffList.get(2), WmsMovementOrderDetailDO::getId)); + } + } + + @Override + public void deleteMovementOrderDetailListByOrderId(Long orderId) { + movementOrderDetailMapper.deleteByOrderId(orderId); + } + + @Override + public List getMovementOrderDetailList(Long orderId) { + return movementOrderDetailMapper.selectListByOrderId(orderId); + } + + @Override + public List getMovementOrderDetailList(Collection orderIds) { + if (CollUtil.isEmpty(orderIds)) { + return ListUtil.of(); + } + return movementOrderDetailMapper.selectListByOrderIds(orderIds); + } + + @Override + public List validateMovementOrderDetailListExists(Long orderId) { + List details = movementOrderDetailMapper.selectListByOrderId(orderId); + if (CollUtil.isEmpty(details)) { + throw exception(MOVEMENT_ORDER_DETAIL_REQUIRED); + } + return details; + } + + @Override + public long getMovementOrderDetailCountBySkuId(Long skuId) { + return movementOrderDetailMapper.selectCountBySkuId(skuId); + } + + private List buildMovementOrderDetailList(WmsMovementOrderSaveReqVO reqVO) { + if (CollUtil.isEmpty(reqVO.getDetails())) { + return ListUtil.of(); + } + return convertList(reqVO.getDetails(), detail -> { + // 校验 SKU 存在 + itemSkuService.validateItemSkuExists(detail.getSkuId()); + // 构建对象 + WmsMovementOrderDetailDO detailDO = BeanUtils.toBean(detail, WmsMovementOrderDetailDO.class) + .setSourceWarehouseId(reqVO.getSourceWarehouseId()) + .setTargetWarehouseId(reqVO.getTargetWarehouseId()); + fillDetailTotalPrice(detailDO); + return detailDO; + }); + } + + private static void fillDetailTotalPrice(WmsMovementOrderDetailDO detail) { + if (detail.getTotalPrice() != null || detail.getQuantity() == null || detail.getPrice() == null) { + return; + } + detail.setTotalPrice(MoneyUtils.priceMultiply(detail.getPrice(), detail.getQuantity())); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderService.java new file mode 100644 index 000000000..52fbf7176 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderService.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.wms.service.order.movement; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO; +import jakarta.validation.Valid; + +/** + * WMS 移库单 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsMovementOrderService { + + /** + * 创建移库单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createMovementOrder(@Valid WmsMovementOrderSaveReqVO createReqVO); + + /** + * 更新移库单 + * + * @param updateReqVO 更新信息 + */ + void updateMovementOrder(@Valid WmsMovementOrderSaveReqVO updateReqVO); + + /** + * 删除移库单 + * + * @param id 编号 + */ + void deleteMovementOrder(Long id); + + /** + * 完成移库 + * + * @param id 编号 + */ + void completeMovementOrder(Long id); + + /** + * 作废移库单 + * + * @param id 编号 + */ + void cancelMovementOrder(Long id); + + /** + * 获得移库单 + * + * @param id 编号 + * @return 移库单 + */ + WmsMovementOrderDO getMovementOrder(Long id); + + /** + * 获得移库单分页 + * + * @param pageReqVO 分页查询 + * @return 移库单分页 + */ + PageResult getMovementOrderPage(WmsMovementOrderPageReqVO pageReqVO); + + /** + * 获得指定仓库的移库单数量 + * + * @param warehouseId 仓库编号 + * @return 移库单数量 + */ + long getMovementOrderCountByWarehouseId(Long warehouseId); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImpl.java new file mode 100644 index 000000000..ee2613e10 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImpl.java @@ -0,0 +1,243 @@ +package cn.iocoder.yudao.module.wms.service.order.movement; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 移库单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsMovementOrderServiceImpl implements WmsMovementOrderService { + + @Resource + private WmsMovementOrderMapper movementOrderMapper; + @Resource + private WmsMovementOrderDetailService movementOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsInventoryService inventoryService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createMovementOrder(WmsMovementOrderSaveReqVO createReqVO) { + // 1. 校验移库单保存数据 + validateMovementOrderSaveData(createReqVO); + + // 2.1 插入移库单 + WmsMovementOrderDO order = BeanUtils.toBean(createReqVO, WmsMovementOrderDO.class); + order.setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + fillMovementOrderTotal(order, createReqVO); + movementOrderMapper.insert(order); + // 2.2 插入移库单明细 + movementOrderDetailService.createMovementOrderDetailList(order.getId(), createReqVO); + return order.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateMovementOrder(WmsMovementOrderSaveReqVO updateReqVO) { + // 1. 校验移库单保存数据 + validateMovementOrderPrepare(updateReqVO.getId()); + validateMovementOrderSaveData(updateReqVO); + + // 2.1 更新移库单 + WmsMovementOrderDO updateObj = BeanUtils.toBean(updateReqVO, WmsMovementOrderDO.class) + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + fillMovementOrderTotal(updateObj, updateReqVO); + int updateCount = movementOrderMapper.updateByIdAndStatus(updateReqVO.getId(), + WmsOrderStatusEnum.PREPARE.getStatus(), updateObj); + if (updateCount == 0) { + throw exception(MOVEMENT_ORDER_STATUS_NOT_PREPARE); + } + // 2.2 更新移库单明细 + movementOrderDetailService.updateMovementOrderDetailList(updateReqVO.getId(), updateReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteMovementOrder(Long id) { + // 1. 校验存在,且可删除 + WmsMovementOrderDO order = validateMovementOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus()) + && ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.CANCELED.getStatus())) { + throw exception(MOVEMENT_ORDER_STATUS_NOT_DELETABLE); + } + + // 2.1 删除移库单 + movementOrderMapper.deleteById(id); + // 2.2 删除移库单明细 + movementOrderDetailService.deleteMovementOrderDetailListByOrderId(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void completeMovementOrder(Long id) { + // 1.1 校验存在,且草稿 + WmsMovementOrderDO order = validateMovementOrderPrepare(id); + // 1.2 校验移库单明细存在 + List details = movementOrderDetailService.validateMovementOrderDetailListExists(id); + + // 2. 完成移库单 + if (movementOrderMapper.updateByIdAndStatus(id, WmsOrderStatusEnum.PREPARE.getStatus(), + new WmsMovementOrderDO().setStatus(WmsOrderStatusEnum.FINISHED.getStatus())) == 0) { + throw exception(MOVEMENT_ORDER_STATUS_NOT_PREPARE); + } + + // 3. 移动库存 + changeInventory(order, details); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelMovementOrder(Long id) { + // 1. 校验存在,且草稿 + validateMovementOrderPrepare(id); + + // 2. 作废移库单 + if (movementOrderMapper.updateByIdAndStatus(id, WmsOrderStatusEnum.PREPARE.getStatus(), + new WmsMovementOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus())) == 0) { + throw exception(MOVEMENT_ORDER_STATUS_NOT_PREPARE); + } + } + + @Override + public WmsMovementOrderDO getMovementOrder(Long id) { + return movementOrderMapper.selectById(id); + } + + @Override + public PageResult getMovementOrderPage(WmsMovementOrderPageReqVO pageReqVO) { + return movementOrderMapper.selectPage(pageReqVO); + } + + @Override + public long getMovementOrderCountByWarehouseId(Long warehouseId) { + return movementOrderMapper.selectCountByWarehouseId(warehouseId); + } + + private void validateMovementOrderSaveData(WmsMovementOrderSaveReqVO reqVO) { + // 校验移库单号唯一 + validateMovementOrderNoUnique(reqVO.getId(), reqVO.getNo()); + // 校验仓库存在 + warehouseService.validateWarehouseExists(reqVO.getSourceWarehouseId()); + warehouseService.validateWarehouseExists(reqVO.getTargetWarehouseId()); + // 校验来源和目标不能相同 + if (ObjectUtil.equal(reqVO.getSourceWarehouseId(), reqVO.getTargetWarehouseId())) { + throw exception(MOVEMENT_ORDER_WAREHOUSE_SAME); + } + } + + private void validateMovementOrderNoUnique(Long id, String no) { + WmsMovementOrderDO order = movementOrderMapper.selectByNo(no); + if (order == null) { + return; + } + if (id == null || ObjectUtil.notEqual(order.getId(), id)) { + throw exception(MOVEMENT_ORDER_NO_DUPLICATE); + } + } + + private void fillMovementOrderTotal(WmsMovementOrderDO order, WmsMovementOrderSaveReqVO reqVO) { + BigDecimal totalQuantity = BigDecimal.ZERO; + BigDecimal totalPrice = BigDecimal.ZERO; + if (CollUtil.isNotEmpty(reqVO.getDetails())) { + for (WmsMovementOrderDetailSaveReqVO detail : reqVO.getDetails()) { + if (detail.getQuantity() != null) { + totalQuantity = totalQuantity.add(detail.getQuantity()); + } + BigDecimal detailTotalPrice = getDetailTotalPrice(detail.getQuantity(), detail.getPrice(), + detail.getTotalPrice()); + if (detailTotalPrice != null) { + totalPrice = totalPrice.add(detailTotalPrice); + } + } + } + order.setTotalQuantity(totalQuantity).setTotalPrice(totalPrice); + } + + private static BigDecimal getDetailTotalPrice(BigDecimal quantity, BigDecimal price, BigDecimal totalPrice) { + if (totalPrice != null) { + return totalPrice; + } + return MoneyUtils.priceMultiply(price, quantity); + } + + private WmsMovementOrderDO validateMovementOrderExists(Long id) { + WmsMovementOrderDO order = id == null ? null : movementOrderMapper.selectById(id); + if (order == null) { + throw exception(MOVEMENT_ORDER_NOT_EXISTS); + } + return order; + } + + /** + * 校验移库单存在且为草稿状态 + * + * @param id 移库单编号 + * @return 移库单 + */ + private WmsMovementOrderDO validateMovementOrderPrepare(Long id) { + WmsMovementOrderDO order = validateMovementOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus())) { + throw exception(MOVEMENT_ORDER_STATUS_NOT_PREPARE); + } + return order; + } + + /** + * 移动移库单对应库存 + * + * @param order 移库单 + * @param details 移库单明细列表 + */ + private void changeInventory(WmsMovementOrderDO order, List details) { + List items = new ArrayList<>(details.size() * 2); + for (WmsMovementOrderDetailDO detail : details) { + BigDecimal detailTotalPrice = getDetailTotalPrice(detail.getQuantity(), detail.getPrice(), + detail.getTotalPrice()); + items.add(BeanUtils.toBean(detail, WmsInventoryChangeReqDTO.Item.class) + .setWarehouseId(detail.getSourceWarehouseId()).setQuantity(detail.getQuantity().negate()) + .setTotalPrice(negate(detailTotalPrice))); + items.add(BeanUtils.toBean(detail, WmsInventoryChangeReqDTO.Item.class) + .setWarehouseId(detail.getTargetWarehouseId()).setQuantity(detail.getQuantity()) + .setTotalPrice(detailTotalPrice)); + } + inventoryService.changeInventory(new WmsInventoryChangeReqDTO() + .setOrderId(order.getId()).setOrderNo(order.getNo()) + .setOrderType(WmsOrderTypeEnum.MOVEMENT.getType()).setItems(items)); + } + + private static BigDecimal negate(BigDecimal value) { + return value == null ? null : value.negate(); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailService.java new file mode 100644 index 000000000..ba67d7b37 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailService.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 入库单明细 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsReceiptOrderDetailService { + + /** + * 创建入库单明细列表 + * + * @param orderId 入库单编号 + * @param reqVO 入库单保存信息 + */ + void createReceiptOrderDetailList(Long orderId, WmsReceiptOrderSaveReqVO reqVO); + + /** + * 更新入库单明细列表 + * + * @param orderId 入库单编号 + * @param reqVO 入库单保存信息 + */ + void updateReceiptOrderDetailList(Long orderId, WmsReceiptOrderSaveReqVO reqVO); + + /** + * 按入库单编号删除明细列表 + * + * @param orderId 入库单编号 + */ + void deleteReceiptOrderDetailListByOrderId(Long orderId); + + /** + * 按入库单编号获得明细列表 + * + * @param orderId 入库单编号 + * @return 明细列表 + */ + List getReceiptOrderDetailList(Long orderId); + + /** + * 按入库单编号集合获得明细列表 + * + * @param orderIds 入库单编号集合 + * @return 明细列表 + */ + List getReceiptOrderDetailList(Collection orderIds); + + /** + * 校验入库单明细列表存在 + * + * @param orderId 入库单编号 + * @return 明细列表 + */ + List validateReceiptOrderDetailListExists(Long orderId); + + /** + * 获得指定 SKU 的入库单明细数量 + * + * @param skuId SKU 编号 + * @return 入库单明细数量 + */ + long getReceiptOrderDetailCountBySkuId(Long skuId); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImpl.java new file mode 100644 index 000000000..67ae8d03e --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImpl.java @@ -0,0 +1,131 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderDetailMapper; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 入库单明细 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsReceiptOrderDetailServiceImpl implements WmsReceiptOrderDetailService { + + @Resource + private WmsReceiptOrderDetailMapper receiptOrderDetailMapper; + @Resource + private WmsItemSkuService itemSkuService; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createReceiptOrderDetailList(Long orderId, WmsReceiptOrderSaveReqVO reqVO) { + List list = buildReceiptOrderDetailList(reqVO); + if (CollUtil.isEmpty(list)) { + return; + } + list.forEach(detail -> detail.setId(null).setOrderId(orderId)); + receiptOrderDetailMapper.insertBatch(list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateReceiptOrderDetailList(Long orderId, WmsReceiptOrderSaveReqVO reqVO) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = receiptOrderDetailMapper.selectListByOrderId(orderId); + List list = buildReceiptOrderDetailList(reqVO); + List newList = CollUtil.isEmpty(list) ? ListUtil.of() : list; + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> ObjectUtil.equal(oldVal.getId(), newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + if (CollUtil.isNotEmpty(convertList(diffList.get(0), WmsReceiptOrderDetailDO::getId))) { + throw exception(RECEIPT_ORDER_DETAIL_NOT_EXISTS); + } + diffList.get(0).forEach(detail -> detail.setOrderId(orderId)); + receiptOrderDetailMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + diffList.get(1).forEach(detail -> detail.setOrderId(orderId)); + receiptOrderDetailMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + receiptOrderDetailMapper.deleteByIds(convertList(diffList.get(2), WmsReceiptOrderDetailDO::getId)); + } + } + + @Override + public void deleteReceiptOrderDetailListByOrderId(Long orderId) { + receiptOrderDetailMapper.deleteByOrderId(orderId); + } + + @Override + public List getReceiptOrderDetailList(Long orderId) { + return receiptOrderDetailMapper.selectListByOrderId(orderId); + } + + @Override + public List getReceiptOrderDetailList(Collection orderIds) { + if (CollUtil.isEmpty(orderIds)) { + return ListUtil.of(); + } + return receiptOrderDetailMapper.selectListByOrderIds(orderIds); + } + + @Override + public List validateReceiptOrderDetailListExists(Long orderId) { + List details = receiptOrderDetailMapper.selectListByOrderId(orderId); + if (CollUtil.isEmpty(details)) { + throw exception(RECEIPT_ORDER_DETAIL_REQUIRED); + } + return details; + } + + @Override + public long getReceiptOrderDetailCountBySkuId(Long skuId) { + return receiptOrderDetailMapper.selectCountBySkuId(skuId); + } + + private List buildReceiptOrderDetailList(WmsReceiptOrderSaveReqVO reqVO) { + if (CollUtil.isEmpty(reqVO.getDetails())) { + return ListUtil.of(); + } + return convertList(reqVO.getDetails(), detail -> { + // 校验 SKU 存在 + itemSkuService.validateItemSkuExists(detail.getSkuId()); + // 构建对象 + WmsReceiptOrderDetailDO detailDO = BeanUtils.toBean(detail, WmsReceiptOrderDetailDO.class) + .setWarehouseId(reqVO.getWarehouseId()); + fillDetailTotalPrice(detailDO); + return detailDO; + }); + } + + private static void fillDetailTotalPrice(WmsReceiptOrderDetailDO detail) { + if (detail.getTotalPrice() != null || detail.getQuantity() == null || detail.getPrice() == null) { + return; + } + detail.setTotalPrice(MoneyUtils.priceMultiply(detail.getPrice(), detail.getQuantity())); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderService.java new file mode 100644 index 000000000..4223bfb11 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderService.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO; +import jakarta.validation.Valid; + +/** + * WMS 入库单 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsReceiptOrderService { + + /** + * 创建入库单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createReceiptOrder(@Valid WmsReceiptOrderSaveReqVO createReqVO); + + /** + * 更新入库单 + * + * @param updateReqVO 更新信息 + */ + void updateReceiptOrder(@Valid WmsReceiptOrderSaveReqVO updateReqVO); + + /** + * 删除入库单 + * + * @param id 编号 + */ + void deleteReceiptOrder(Long id); + + /** + * 完成入库 + * + * @param id 编号 + */ + void completeReceiptOrder(Long id); + + /** + * 作废入库单 + * + * @param id 编号 + */ + void cancelReceiptOrder(Long id); + + /** + * 获得入库单 + * + * @param id 编号 + * @return 入库单 + */ + WmsReceiptOrderDO getReceiptOrder(Long id); + + /** + * 获得入库单分页 + * + * @param pageReqVO 分页查询 + * @return 入库单分页 + */ + PageResult getReceiptOrderPage(WmsReceiptOrderPageReqVO pageReqVO); + + /** + * 获得指定往来企业的入库单数量 + * + * @param merchantId 往来企业编号 + * @return 入库单数量 + */ + long getReceiptOrderCountByMerchantId(Long merchantId); + + /** + * 获得指定仓库的入库单数量 + * + * @param warehouseId 仓库编号 + * @return 入库单数量 + */ + long getReceiptOrderCountByWarehouseId(Long warehouseId); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImpl.java new file mode 100644 index 000000000..70ac13c4c --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImpl.java @@ -0,0 +1,241 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 入库单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsReceiptOrderServiceImpl implements WmsReceiptOrderService { + + @Resource + private WmsReceiptOrderMapper receiptOrderMapper; + @Resource + private WmsReceiptOrderDetailService receiptOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsMerchantService merchantService; + @Resource + private WmsInventoryService inventoryService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createReceiptOrder(WmsReceiptOrderSaveReqVO createReqVO) { + // 1. 校验入库单保存数据 + validateReceiptOrderSaveData(createReqVO); + + // 2.1 插入入库单 + WmsReceiptOrderDO order = BeanUtils.toBean(createReqVO, WmsReceiptOrderDO.class); + order.setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + fillReceiptOrderTotal(order, createReqVO); + receiptOrderMapper.insert(order); + // 2.2 插入入库单明细 + receiptOrderDetailService.createReceiptOrderDetailList(order.getId(), createReqVO); + return order.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateReceiptOrder(WmsReceiptOrderSaveReqVO updateReqVO) { + // 1. 校验入库单保存数据 + validateReceiptOrderPrepare(updateReqVO.getId()); + validateReceiptOrderSaveData(updateReqVO); + + // 2.1 更新入库单 + WmsReceiptOrderDO updateObj = BeanUtils.toBean(updateReqVO, WmsReceiptOrderDO.class) + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + fillReceiptOrderTotal(updateObj, updateReqVO); + int updateCount = receiptOrderMapper.updateByIdAndStatus(updateReqVO.getId(), + WmsOrderStatusEnum.PREPARE.getStatus(), updateObj); + if (updateCount == 0) { + throw exception(RECEIPT_ORDER_STATUS_NOT_PREPARE); + } + // 2.2 更新入库单明细 + receiptOrderDetailService.updateReceiptOrderDetailList(updateReqVO.getId(), updateReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteReceiptOrder(Long id) { + // 1. 校验存在,且可删除 + WmsReceiptOrderDO order = validateReceiptOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus()) + && ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.CANCELED.getStatus())) { + throw exception(RECEIPT_ORDER_STATUS_NOT_DELETABLE); + } + + // 2.1 删除入库单 + receiptOrderMapper.deleteById(id); + // 2.2 删除入库单明细 + receiptOrderDetailService.deleteReceiptOrderDetailListByOrderId(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void completeReceiptOrder(Long id) { + // 1.1 校验存在,且草稿 + WmsReceiptOrderDO order = validateReceiptOrderPrepare(id); + // 1.2 校验入库单明细存在 + List details = receiptOrderDetailService.validateReceiptOrderDetailListExists(id); + + // 2. 完成入库单 + if (receiptOrderMapper.updateByIdAndStatus(id, WmsOrderStatusEnum.PREPARE.getStatus(), + new WmsReceiptOrderDO().setStatus(WmsOrderStatusEnum.FINISHED.getStatus())) == 0) { + throw exception(RECEIPT_ORDER_STATUS_NOT_PREPARE); + } + + // 3. 写入库存 + createInventory(order, details); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelReceiptOrder(Long id) { + // 1. 校验存在,且草稿 + validateReceiptOrderPrepare(id); + + // 2. 作废入库单 + if (receiptOrderMapper.updateByIdAndStatus(id, WmsOrderStatusEnum.PREPARE.getStatus(), + new WmsReceiptOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus())) == 0) { + throw exception(RECEIPT_ORDER_STATUS_NOT_PREPARE); + } + } + + @Override + public WmsReceiptOrderDO getReceiptOrder(Long id) { + return receiptOrderMapper.selectById(id); + } + + @Override + public PageResult getReceiptOrderPage(WmsReceiptOrderPageReqVO pageReqVO) { + return receiptOrderMapper.selectPage(pageReqVO); + } + + @Override + public long getReceiptOrderCountByMerchantId(Long merchantId) { + return receiptOrderMapper.selectCountByMerchantId(merchantId); + } + + @Override + public long getReceiptOrderCountByWarehouseId(Long warehouseId) { + return receiptOrderMapper.selectCountByWarehouseId(warehouseId); + } + + private void validateReceiptOrderSaveData(WmsReceiptOrderSaveReqVO reqVO) { + // 校验入库单号唯一 + validateReceiptOrderNoUnique(reqVO.getId(), reqVO.getNo()); + // 校验仓库存在 + warehouseService.validateWarehouseExists(reqVO.getWarehouseId()); + // 校验供应商类型 + if (reqVO.getMerchantId() != null) { + merchantService.validateSupplierMerchantExists(reqVO.getMerchantId()); + } + } + + private void validateReceiptOrderNoUnique(Long id, String no) { + WmsReceiptOrderDO order = receiptOrderMapper.selectByNo(no); + if (order == null) { + return; + } + if (id == null || ObjectUtil.notEqual(order.getId(), id)) { + throw exception(RECEIPT_ORDER_NO_DUPLICATE); + } + } + + private void fillReceiptOrderTotal(WmsReceiptOrderDO order, WmsReceiptOrderSaveReqVO reqVO) { + BigDecimal totalQuantity = BigDecimal.ZERO; + BigDecimal totalPrice = BigDecimal.ZERO; + if (CollUtil.isNotEmpty(reqVO.getDetails())) { + for (WmsReceiptOrderDetailSaveReqVO detail : reqVO.getDetails()) { + if (detail.getQuantity() != null) { + totalQuantity = totalQuantity.add(detail.getQuantity()); + } + BigDecimal detailTotalPrice = getDetailTotalPrice(detail.getQuantity(), detail.getPrice(), + detail.getTotalPrice()); + if (detailTotalPrice != null) { + totalPrice = totalPrice.add(detailTotalPrice); + } + } + } + order.setTotalQuantity(totalQuantity).setTotalPrice(totalPrice); + } + + private static BigDecimal getDetailTotalPrice(BigDecimal quantity, BigDecimal price, BigDecimal totalPrice) { + if (totalPrice != null) { + return totalPrice; + } + return MoneyUtils.priceMultiply(price, quantity); + } + + private WmsReceiptOrderDO validateReceiptOrderExists(Long id) { + WmsReceiptOrderDO order = id == null ? null : receiptOrderMapper.selectById(id); + if (order == null) { + throw exception(RECEIPT_ORDER_NOT_EXISTS); + } + return order; + } + + /** + * 校验入库单存在且为草稿状态 + * + * @param id 入库单编号 + * @return 入库单 + */ + private WmsReceiptOrderDO validateReceiptOrderPrepare(Long id) { + WmsReceiptOrderDO order = validateReceiptOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus())) { + throw exception(RECEIPT_ORDER_STATUS_NOT_PREPARE); + } + return order; + } + + /** + * 增加入库单对应库存。 + * + * 入库在库存变更模型中使用正数数量。 + * + * @param order 入库单 + * @param details 入库单明细列表 + */ + private void createInventory(WmsReceiptOrderDO order, List details) { + List items = convertList(details, + detail -> BeanUtils.toBean(detail, WmsInventoryChangeReqDTO.Item.class) + .setTotalPrice(getDetailTotalPrice(detail.getQuantity(), detail.getPrice(), + detail.getTotalPrice()))); + inventoryService.changeInventory(new WmsInventoryChangeReqDTO() + .setOrderId(order.getId()).setOrderNo(order.getNo()) + .setOrderType(WmsOrderTypeEnum.RECEIPT.getType()).setItems(items)); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailService.java new file mode 100644 index 000000000..39f5d86db --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailService.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.wms.service.order.shipment; + +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 出库单明细 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsShipmentOrderDetailService { + + /** + * 创建出库单明细列表 + * + * @param orderId 出库单编号 + * @param reqVO 出库单保存信息 + */ + void createShipmentOrderDetailList(Long orderId, WmsShipmentOrderSaveReqVO reqVO); + + /** + * 更新出库单明细列表 + * + * @param orderId 出库单编号 + * @param reqVO 出库单保存信息 + */ + void updateShipmentOrderDetailList(Long orderId, WmsShipmentOrderSaveReqVO reqVO); + + /** + * 按出库单编号删除明细列表 + * + * @param orderId 出库单编号 + */ + void deleteShipmentOrderDetailListByOrderId(Long orderId); + + /** + * 按出库单编号获得明细列表 + * + * @param orderId 出库单编号 + * @return 明细列表 + */ + List getShipmentOrderDetailList(Long orderId); + + /** + * 按出库单编号集合获得明细列表 + * + * @param orderIds 出库单编号集合 + * @return 明细列表 + */ + List getShipmentOrderDetailList(Collection orderIds); + + /** + * 校验出库单明细列表存在 + * + * @param orderId 出库单编号 + * @return 明细列表 + */ + List validateShipmentOrderDetailListExists(Long orderId); + + /** + * 获得指定 SKU 的出库单明细数量 + * + * @param skuId SKU 编号 + * @return 出库单明细数量 + */ + long getShipmentOrderDetailCountBySkuId(Long skuId); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailServiceImpl.java new file mode 100644 index 000000000..ed9e6339a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailServiceImpl.java @@ -0,0 +1,131 @@ +package cn.iocoder.yudao.module.wms.service.order.shipment; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderDetailMapper; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 出库单明细 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsShipmentOrderDetailServiceImpl implements WmsShipmentOrderDetailService { + + @Resource + private WmsShipmentOrderDetailMapper shipmentOrderDetailMapper; + @Resource + private WmsItemSkuService itemSkuService; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createShipmentOrderDetailList(Long orderId, WmsShipmentOrderSaveReqVO reqVO) { + List list = buildShipmentOrderDetailList(reqVO); + if (CollUtil.isEmpty(list)) { + return; + } + list.forEach(detail -> detail.setId(null).setOrderId(orderId)); + shipmentOrderDetailMapper.insertBatch(list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateShipmentOrderDetailList(Long orderId, WmsShipmentOrderSaveReqVO reqVO) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = shipmentOrderDetailMapper.selectListByOrderId(orderId); + List list = buildShipmentOrderDetailList(reqVO); + List newList = CollUtil.isEmpty(list) ? ListUtil.of() : list; + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> ObjectUtil.equal(oldVal.getId(), newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + if (CollUtil.isNotEmpty(convertList(diffList.get(0), WmsShipmentOrderDetailDO::getId))) { + throw exception(SHIPMENT_ORDER_DETAIL_NOT_EXISTS); + } + diffList.get(0).forEach(detail -> detail.setOrderId(orderId)); + shipmentOrderDetailMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + diffList.get(1).forEach(detail -> detail.setOrderId(orderId)); + shipmentOrderDetailMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + shipmentOrderDetailMapper.deleteByIds(convertList(diffList.get(2), WmsShipmentOrderDetailDO::getId)); + } + } + + @Override + public void deleteShipmentOrderDetailListByOrderId(Long orderId) { + shipmentOrderDetailMapper.deleteByOrderId(orderId); + } + + @Override + public List getShipmentOrderDetailList(Long orderId) { + return shipmentOrderDetailMapper.selectListByOrderId(orderId); + } + + @Override + public List getShipmentOrderDetailList(Collection orderIds) { + if (CollUtil.isEmpty(orderIds)) { + return ListUtil.of(); + } + return shipmentOrderDetailMapper.selectListByOrderIds(orderIds); + } + + @Override + public List validateShipmentOrderDetailListExists(Long orderId) { + List details = shipmentOrderDetailMapper.selectListByOrderId(orderId); + if (CollUtil.isEmpty(details)) { + throw exception(SHIPMENT_ORDER_DETAIL_REQUIRED); + } + return details; + } + + @Override + public long getShipmentOrderDetailCountBySkuId(Long skuId) { + return shipmentOrderDetailMapper.selectCountBySkuId(skuId); + } + + private List buildShipmentOrderDetailList(WmsShipmentOrderSaveReqVO reqVO) { + if (CollUtil.isEmpty(reqVO.getDetails())) { + return ListUtil.of(); + } + return convertList(reqVO.getDetails(), detail -> { + // 校验 SKU 存在 + itemSkuService.validateItemSkuExists(detail.getSkuId()); + // 构建对象 + WmsShipmentOrderDetailDO detailDO = BeanUtils.toBean(detail, WmsShipmentOrderDetailDO.class) + .setWarehouseId(reqVO.getWarehouseId()); + fillDetailTotalPrice(detailDO); + return detailDO; + }); + } + + private static void fillDetailTotalPrice(WmsShipmentOrderDetailDO detail) { + if (detail.getTotalPrice() != null || detail.getQuantity() == null || detail.getPrice() == null) { + return; + } + detail.setTotalPrice(MoneyUtils.priceMultiply(detail.getPrice(), detail.getQuantity())); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderService.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderService.java new file mode 100644 index 000000000..2a2b2f6a8 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderService.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.wms.service.order.shipment; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO; +import jakarta.validation.Valid; + +/** + * WMS 出库单 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsShipmentOrderService { + + /** + * 创建出库单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createShipmentOrder(@Valid WmsShipmentOrderSaveReqVO createReqVO); + + /** + * 更新出库单 + * + * @param updateReqVO 更新信息 + */ + void updateShipmentOrder(@Valid WmsShipmentOrderSaveReqVO updateReqVO); + + /** + * 删除出库单 + * + * @param id 编号 + */ + void deleteShipmentOrder(Long id); + + /** + * 完成出库 + * + * @param id 编号 + */ + void completeShipmentOrder(Long id); + + /** + * 作废出库单 + * + * @param id 编号 + */ + void cancelShipmentOrder(Long id); + + /** + * 获得出库单 + * + * @param id 编号 + * @return 出库单 + */ + WmsShipmentOrderDO getShipmentOrder(Long id); + + /** + * 获得出库单分页 + * + * @param pageReqVO 分页查询 + * @return 出库单分页 + */ + PageResult getShipmentOrderPage(WmsShipmentOrderPageReqVO pageReqVO); + + /** + * 获得指定往来企业的出库单数量 + * + * @param merchantId 往来企业编号 + * @return 出库单数量 + */ + long getShipmentOrderCountByMerchantId(Long merchantId); + + /** + * 获得指定仓库的出库单数量 + * + * @param warehouseId 仓库编号 + * @return 出库单数量 + */ + long getShipmentOrderCountByWarehouseId(Long warehouseId); + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderServiceImpl.java b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderServiceImpl.java new file mode 100644 index 000000000..cebb35781 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderServiceImpl.java @@ -0,0 +1,244 @@ +package cn.iocoder.yudao.module.wms.service.order.shipment; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 出库单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsShipmentOrderServiceImpl implements WmsShipmentOrderService { + + @Resource + private WmsShipmentOrderMapper shipmentOrderMapper; + @Resource + private WmsShipmentOrderDetailService shipmentOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsMerchantService merchantService; + @Resource + private WmsInventoryService inventoryService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createShipmentOrder(WmsShipmentOrderSaveReqVO createReqVO) { + // 1. 校验出库单保存数据 + validateShipmentOrderSaveData(createReqVO); + + // 2.1 插入出库单 + WmsShipmentOrderDO order = BeanUtils.toBean(createReqVO, WmsShipmentOrderDO.class); + order.setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + fillShipmentOrderTotal(order, createReqVO); + shipmentOrderMapper.insert(order); + // 2.2 插入出库单明细 + shipmentOrderDetailService.createShipmentOrderDetailList(order.getId(), createReqVO); + return order.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateShipmentOrder(WmsShipmentOrderSaveReqVO updateReqVO) { + // 1. 校验出库单保存数据 + validateShipmentOrderPrepare(updateReqVO.getId()); + validateShipmentOrderSaveData(updateReqVO); + + // 2.1 更新出库单 + WmsShipmentOrderDO updateObj = BeanUtils.toBean(updateReqVO, WmsShipmentOrderDO.class) + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + fillShipmentOrderTotal(updateObj, updateReqVO); + int updateCount = shipmentOrderMapper.updateByIdAndStatus(updateReqVO.getId(), + WmsOrderStatusEnum.PREPARE.getStatus(), updateObj); + if (updateCount == 0) { + throw exception(SHIPMENT_ORDER_STATUS_NOT_PREPARE); + } + // 2.2 更新出库单明细 + shipmentOrderDetailService.updateShipmentOrderDetailList(updateReqVO.getId(), updateReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteShipmentOrder(Long id) { + // 1. 校验存在,且可删除 + WmsShipmentOrderDO order = validateShipmentOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus()) + && ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.CANCELED.getStatus())) { + throw exception(SHIPMENT_ORDER_STATUS_NOT_DELETABLE); + } + + // 2.1 删除出库单 + shipmentOrderMapper.deleteById(id); + // 2.2 删除出库单明细 + shipmentOrderDetailService.deleteShipmentOrderDetailListByOrderId(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void completeShipmentOrder(Long id) { + // 1.1 校验存在,且草稿 + WmsShipmentOrderDO order = validateShipmentOrderPrepare(id); + // 1.2 校验出库单明细存在 + List details = shipmentOrderDetailService.validateShipmentOrderDetailListExists(id); + + // 2. 完成出库单 + if (shipmentOrderMapper.updateByIdAndStatus(id, WmsOrderStatusEnum.PREPARE.getStatus(), + new WmsShipmentOrderDO().setStatus(WmsOrderStatusEnum.FINISHED.getStatus())) == 0) { + throw exception(SHIPMENT_ORDER_STATUS_NOT_PREPARE); + } + + // 3. 扣减库存 + changeInventory(order, details); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelShipmentOrder(Long id) { + // 1. 校验存在,且草稿 + validateShipmentOrderPrepare(id); + + // 2. 作废出库单 + if (shipmentOrderMapper.updateByIdAndStatus(id, WmsOrderStatusEnum.PREPARE.getStatus(), + new WmsShipmentOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus())) == 0) { + throw exception(SHIPMENT_ORDER_STATUS_NOT_PREPARE); + } + } + + @Override + public WmsShipmentOrderDO getShipmentOrder(Long id) { + return shipmentOrderMapper.selectById(id); + } + + @Override + public PageResult getShipmentOrderPage(WmsShipmentOrderPageReqVO pageReqVO) { + return shipmentOrderMapper.selectPage(pageReqVO); + } + + @Override + public long getShipmentOrderCountByMerchantId(Long merchantId) { + return shipmentOrderMapper.selectCountByMerchantId(merchantId); + } + + @Override + public long getShipmentOrderCountByWarehouseId(Long warehouseId) { + return shipmentOrderMapper.selectCountByWarehouseId(warehouseId); + } + + private void validateShipmentOrderSaveData(WmsShipmentOrderSaveReqVO reqVO) { + // 校验出库单号唯一 + validateShipmentOrderNoUnique(reqVO.getId(), reqVO.getNo()); + // 校验仓库存在 + warehouseService.validateWarehouseExists(reqVO.getWarehouseId()); + // 校验客户类型 + if (reqVO.getMerchantId() != null) { + merchantService.validateCustomerMerchantExists(reqVO.getMerchantId()); + } + } + + private void validateShipmentOrderNoUnique(Long id, String no) { + WmsShipmentOrderDO order = shipmentOrderMapper.selectByNo(no); + if (order == null) { + return; + } + if (id == null || ObjectUtil.notEqual(order.getId(), id)) { + throw exception(SHIPMENT_ORDER_NO_DUPLICATE); + } + } + + private void fillShipmentOrderTotal(WmsShipmentOrderDO order, WmsShipmentOrderSaveReqVO reqVO) { + BigDecimal totalQuantity = BigDecimal.ZERO; + BigDecimal totalPrice = BigDecimal.ZERO; + if (CollUtil.isNotEmpty(reqVO.getDetails())) { + for (WmsShipmentOrderDetailSaveReqVO detail : reqVO.getDetails()) { + if (detail.getQuantity() != null) { + totalQuantity = totalQuantity.add(detail.getQuantity()); + } + BigDecimal detailTotalPrice = getDetailTotalPrice(detail.getQuantity(), detail.getPrice(), + detail.getTotalPrice()); + if (detailTotalPrice != null) { + totalPrice = totalPrice.add(detailTotalPrice); + } + } + } + order.setTotalQuantity(totalQuantity).setTotalPrice(totalPrice); + } + + private static BigDecimal getDetailTotalPrice(BigDecimal quantity, BigDecimal price, BigDecimal totalPrice) { + if (totalPrice != null) { + return totalPrice; + } + return MoneyUtils.priceMultiply(price, quantity); + } + + private WmsShipmentOrderDO validateShipmentOrderExists(Long id) { + WmsShipmentOrderDO order = id == null ? null : shipmentOrderMapper.selectById(id); + if (order == null) { + throw exception(SHIPMENT_ORDER_NOT_EXISTS); + } + return order; + } + + /** + * 校验出库单存在且为草稿状态 + * + * @param id 出库单编号 + * @return 出库单 + */ + private WmsShipmentOrderDO validateShipmentOrderPrepare(Long id) { + WmsShipmentOrderDO order = validateShipmentOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus())) { + throw exception(SHIPMENT_ORDER_STATUS_NOT_PREPARE); + } + return order; + } + + /** + * 扣减出库单对应库存 + * + * @param order 出库单 + * @param details 出库单明细列表 + */ + private void changeInventory(WmsShipmentOrderDO order, List details) { + List items = convertList(details, + detail -> BeanUtils.toBean(detail, WmsInventoryChangeReqDTO.Item.class) + .setQuantity(detail.getQuantity().negate()) + .setTotalPrice(negate(getDetailTotalPrice(detail.getQuantity(), detail.getPrice(), + detail.getTotalPrice())))); + inventoryService.changeInventory(new WmsInventoryChangeReqDTO() + .setOrderId(order.getId()).setOrderNo(order.getNo()) + .setOrderType(WmsOrderTypeEnum.SHIPMENT.getType()).setItems(items)); + } + + private static BigDecimal negate(BigDecimal value) { + return value == null ? null : value.negate(); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-dev.yaml b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-dev.yaml new file mode 100644 index 000000000..522c0145e --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-dev.yaml @@ -0,0 +1,120 @@ +--- #################### 注册中心 + 配置中心相关配置 #################### + +spring: + cloud: + nacos: + server-addr: 127.0.0.1:8848 # Nacos 服务器地址 + username: # Nacos 账号 + password: # Nacos 密码 + discovery: # 【配置中心】配置项 + namespace: dev # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # 服务实例的版本号,可用于灰度发布 + config: # 【注册中心】配置项 + namespace: dev # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + +--- #################### 数据库相关配置 #################### +spring: + # 数据源配置项 + autoconfigure: + exclude: + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 5 # 初始连接数 + min-idle: 10 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 60000 # 配置获取连接等待超时的时间,单位:毫秒(1 分钟) + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间,单位:毫秒(10 分钟) + max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间,单位:毫秒(30 分钟) + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + pool-prepared-statements: true # 是否开启 PreparedStatement 缓存 + max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量 + primary: master + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: root + password: 123456 + slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: root + password: 123456 + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 400-infra.server.iocoder.cn # 地址 + port: 6379 # 端口 + database: 1 # 数据库索引 +# password: 123456 # 密码,建议生产环境开启 + +--- #################### MQ 消息队列相关配置 #################### + +--- #################### 定时任务相关配置 #################### +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30000 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + username: admin + password: admin + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + access-log: # 访问日志的配置项 + enable: false + demo: false # 关闭演示模式 diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-local.yaml b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-local.yaml new file mode 100644 index 000000000..c7fd54dfc --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application-local.yaml @@ -0,0 +1,139 @@ +--- #################### 注册中心 + 配置中心相关配置 #################### + +spring: + cloud: + nacos: + server-addr: 127.0.0.1:8848 # Nacos 服务器地址 + username: # Nacos 账号 + password: # Nacos 密码 + discovery: # 【配置中心】配置项 + namespace: dev # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # 服务实例的版本号,可用于灰度发布 + config: # 【注册中心】配置项 + namespace: dev # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + +--- #################### 数据库相关配置 #################### +spring: + # 数据源配置项 + autoconfigure: + exclude: + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 60000 # 配置获取连接等待超时的时间,单位:毫秒(1 分钟) + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间,单位:毫秒(10 分钟) + max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间,单位:毫秒(30 分钟) + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + pool-prepared-statements: true # 是否开启 PreparedStatement 缓存 + max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量 + primary: master + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 + # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 + # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 + # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例 + # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 + username: root + password: 123456 + # username: sa # SQL Server 连接的示例 + # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例 + # username: SYSDBA # DM 连接的示例 + # password: SYSDBA # DM 连接的示例 + slave: # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: 123456 + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 127.0.0.1 # 地址 + port: 6379 # 端口 + database: 0 # 数据库索引 +# password: 123456 # 密码,建议生产环境开启 + +--- #################### MQ 消息队列相关配置 #################### + +--- #################### 定时任务相关配置 #################### +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30000 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + username: admin + password: admin + +# 日志文件配置 +logging: + level: + # 配置自己写的 MyBatis Mapper 打印日志 + cn.iocoder.yudao.module.wms.dal.mysql: debug + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + env: # 多环境的配置项 + tag: ${HOSTNAME} + security: + mock-enable: true + access-log: # 访问日志的配置项 + enable: false diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/resources/application.yaml b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application.yaml new file mode 100644 index 000000000..82dfb4e4a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/resources/application.yaml @@ -0,0 +1,124 @@ +spring: + application: + name: wms-server + + profiles: + active: local + + main: + allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 + allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务 + + config: + import: + - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置 + - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置 + + # Servlet 配置 + servlet: + # 文件上传相关配置项 + multipart: + max-file-size: 16MB # 单个文件大小 + max-request-size: 32MB # 设置总上传的文件大小 + + # Jackson 配置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 + write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 + fail-on-empty-beans: false # 允许序列化无属性的 Bean + + # Cache 配置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 小时 + +server: + port: 48092 + +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + +--- #################### 接口文档配置 #################### + +springdoc: + api-docs: + enabled: true # 1. 是否开启 Swagger 接文档的元数据 + path: /v3/api-docs + swagger-ui: + enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面 + path: /swagger-ui + default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: true + setting: + language: zh_cn + +# MyBatis Plus 的配置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # "智能"模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 + # id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库 + # id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库 + # id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制台的 Banner 打印 + type-aliases-package: ${yudao.info.base-package}.dal.dataobject + encryptor: + password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 + +mybatis-plus-join: + banner: false # 关闭控制台的 Banner 打印 + +# Spring Data Redis 配置 +spring: + data: + redis: + repositories: + enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度 + +# VO 转换(数据翻译)相关 +easy-trans: + is-enable-global: false # 【默认禁用,对性能确认压力大】启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口 + +--- #################### MQ 消息队列相关配置 #################### + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + executor: + appname: ${spring.application.name} # 执行器 AppName + logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 + accessToken: default_token # 执行器通讯TOKEN + +--- #################### 芋道相关配置 #################### + +yudao: + info: + version: 1.0.0 + base-package: cn.iocoder.yudao.module.wms + web: + admin-ui: + url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址 + xss: + enable: false + exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 + swagger: + title: 管理后台 + description: 提供管理员管理的所有功能 + version: ${yudao.info.version} + tenant: # 多租户相关配置项 + enable: true + ignore-urls: + +debug: false diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/resources/logback-spring.xml b/yudao-module-wms/yudao-module-wms-server/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..15b28cfdf --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/resources/logback-spring.xml @@ -0,0 +1,56 @@ + + + + + + + + +       + + ${CONSOLE_LOG_PATTERN} + + + + + + + + ${FILE_LOG_PATTERN} + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd}.%i.log + 30 + 10MB + + + + + 0 + 512 + + + + + + + + + + + + + + + diff --git a/yudao-module-wms/yudao-module-wms-server/src/main/resources/mapper/home/WmsHomeStatisticsMapper.xml b/yudao-module-wms/yudao-module-wms-server/src/main/resources/mapper/home/WmsHomeStatisticsMapper.xml new file mode 100644 index 000000000..38c7bdf5d --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/main/resources/mapper/home/WmsHomeStatisticsMapper.xml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImplTest.java new file mode 100644 index 000000000..3dc11dbf1 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImplTest.java @@ -0,0 +1,632 @@ +package cn.iocoder.yudao.module.wms.service.inventory; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryHistoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.mysql.inventory.WmsInventoryHistoryMapper; +import cn.iocoder.yudao.module.wms.dal.mysql.inventory.WmsInventoryMapper; +import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemMapper; +import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemSkuMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryCheckReqDTO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_INVENTORY_CHANGED; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.INVENTORY_QUANTITY_NOT_ENOUGH; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.when; + +@Import({WmsInventoryServiceImpl.class, WmsInventoryHistoryServiceImpl.class}) +public class WmsInventoryServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsInventoryServiceImpl inventoryService; + + @Resource + private WmsInventoryMapper inventoryMapper; + @Resource + private WmsInventoryHistoryMapper inventoryHistoryMapper; + @Resource + private WmsItemMapper itemMapper; + @Resource + private WmsItemSkuMapper skuMapper; + + @MockitoBean + private WmsItemSkuService itemSkuService; + @MockitoBean + private WmsItemService itemService; + + @Test + public void testChangeInventory_createInventory() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "5.00"); + + // 调用 + inventoryService.changeInventory(reqDTO); + + // 断言:库存余额 + WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L); + assertNotNull(inventory); + assertEquals(0, new BigDecimal("5.00").compareTo(inventory.getQuantity())); + // 断言:库存流水 + List histories = inventoryHistoryMapper.selectList(); + assertEquals(1, histories.size()); + WmsInventoryHistoryDO history = histories.get(0); + assertEquals(sku.getId(), history.getSkuId()); + assertEquals(100L, history.getWarehouseId()); + assertEquals(0, BigDecimal.ZERO.compareTo(history.getBeforeQuantity())); + assertEquals(0, new BigDecimal("5.00").compareTo(history.getAfterQuantity())); + assertEquals(0, new BigDecimal("500.00").compareTo(history.getTotalPrice())); + assertEquals(reqDTO.getOrderId(), history.getOrderId()); + assertEquals(reqDTO.getOrderNo(), history.getOrderNo()); + assertEquals(reqDTO.getOrderType(), history.getOrderType()); + } + + @Test + public void testChangeInventory_updateInventory() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00")); + WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "3.00"); + + // 调用 + inventoryService.changeInventory(reqDTO); + + // 断言:库存余额 + WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L); + assertNotNull(inventory); + assertEquals(0, new BigDecimal("5.00").compareTo(inventory.getQuantity())); + // 断言:库存流水 + List histories = inventoryHistoryMapper.selectList(); + assertEquals(1, histories.size()); + WmsInventoryHistoryDO history = histories.get(0); + assertEquals(0, new BigDecimal("2.00").compareTo(history.getBeforeQuantity())); + assertEquals(0, new BigDecimal("5.00").compareTo(history.getAfterQuantity())); + assertEquals(0, new BigDecimal("3.00").compareTo(history.getQuantity())); + assertEquals(0, new BigDecimal("300.00").compareTo(history.getTotalPrice())); + } + + @Test + public void testChangeInventory_sameInventoryKeyKeepItemGranularity() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "5.00"); + List items = new ArrayList<>(reqDTO.getItems()); + items.add(new WmsInventoryChangeReqDTO.Item() + .setSkuId(sku.getId()) + .setWarehouseId(100L) + .setQuantity(new BigDecimal("7.00")) + .setPrice(new BigDecimal("140.00")) + .setTotalPrice(new BigDecimal("980.00")) + .setRemark("测试入库 2")); + reqDTO.setItems(items); + + // 调用 + inventoryService.changeInventory(reqDTO); + + // 断言:库存余额只保留一条,数量逐条累加 + WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L); + assertNotNull(inventory); + assertEquals(0, new BigDecimal("12.00").compareTo(inventory.getQuantity())); + // 断言:库存流水保持用户明细颗粒度 + List histories = inventoryHistoryMapper.selectList(); + histories.sort(Comparator.comparing(WmsInventoryHistoryDO::getId)); + assertEquals(2, histories.size()); + assertEquals(0, BigDecimal.ZERO.compareTo(histories.get(0).getBeforeQuantity())); + assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getAfterQuantity())); + assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getQuantity())); + assertEquals(0, new BigDecimal("500.00").compareTo(histories.get(0).getTotalPrice())); + assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(1).getBeforeQuantity())); + assertEquals(0, new BigDecimal("12.00").compareTo(histories.get(1).getAfterQuantity())); + assertEquals(0, new BigDecimal("7.00").compareTo(histories.get(1).getQuantity())); + assertEquals(0, new BigDecimal("980.00").compareTo(histories.get(1).getTotalPrice())); + } + + @Test + public void testChangeInventory_sameExistingInventoryKeyKeepItemGranularity() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00")); + WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "3.00"); + List items = new ArrayList<>(reqDTO.getItems()); + items.add(new WmsInventoryChangeReqDTO.Item() + .setSkuId(sku.getId()) + .setWarehouseId(100L) + .setQuantity(new BigDecimal("4.00")) + .setPrice(new BigDecimal("80.00")) + .setTotalPrice(new BigDecimal("320.00")) + .setRemark("测试入库 2")); + reqDTO.setItems(items); + + // 调用 + inventoryService.changeInventory(reqDTO); + + // 断言:库存余额只保留一条,数量逐条累加 + WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L); + assertNotNull(inventory); + assertEquals(0, new BigDecimal("9.00").compareTo(inventory.getQuantity())); + // 断言:库存流水保持用户明细颗粒度 + List histories = inventoryHistoryMapper.selectList(); + histories.sort(Comparator.comparing(WmsInventoryHistoryDO::getId)); + assertEquals(2, histories.size()); + assertEquals(0, new BigDecimal("2.00").compareTo(histories.get(0).getBeforeQuantity())); + assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getAfterQuantity())); + assertEquals(0, new BigDecimal("3.00").compareTo(histories.get(0).getQuantity())); + assertEquals(0, new BigDecimal("300.00").compareTo(histories.get(0).getTotalPrice())); + assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(1).getBeforeQuantity())); + assertEquals(0, new BigDecimal("9.00").compareTo(histories.get(1).getAfterQuantity())); + assertEquals(0, new BigDecimal("4.00").compareTo(histories.get(1).getQuantity())); + assertEquals(0, new BigDecimal("320.00").compareTo(histories.get(1).getTotalPrice())); + } + + @Test + public void testChangeInventory_multipleInventoryKeysKeepItemGranularity() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "5.00"); + List items = new ArrayList<>(reqDTO.getItems()); + items.add(new WmsInventoryChangeReqDTO.Item() + .setSkuId(sku.getId()) + .setWarehouseId(200L) + .setQuantity(new BigDecimal("3.00")) + .setPrice(new BigDecimal("60.00")) + .setTotalPrice(new BigDecimal("180.00")) + .setRemark("测试入库 2")); + items.add(new WmsInventoryChangeReqDTO.Item() + .setSkuId(sku.getId()) + .setWarehouseId(100L) + .setQuantity(new BigDecimal("-2.00")) + .setPrice(new BigDecimal("40.00")) + .setTotalPrice(new BigDecimal("-80.00")) + .setRemark("测试出库")); + reqDTO.setItems(items); + + // 调用 + inventoryService.changeInventory(reqDTO); + + // 断言:不同库存余额分别更新 + WmsInventoryDO inventory1 = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L); + assertNotNull(inventory1); + assertEquals(0, new BigDecimal("3.00").compareTo(inventory1.getQuantity())); + WmsInventoryDO inventory2 = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 200L); + assertNotNull(inventory2); + assertEquals(0, new BigDecimal("3.00").compareTo(inventory2.getQuantity())); + // 断言:批量加锁后仍按明细颗粒度记录 before/after + List histories = inventoryHistoryMapper.selectList(); + histories.sort(Comparator.comparing(WmsInventoryHistoryDO::getId)); + assertEquals(3, histories.size()); + assertEquals(100L, histories.get(0).getWarehouseId()); + assertEquals(0, BigDecimal.ZERO.compareTo(histories.get(0).getBeforeQuantity())); + assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getAfterQuantity())); + assertEquals(0, new BigDecimal("500.00").compareTo(histories.get(0).getTotalPrice())); + assertEquals(200L, histories.get(1).getWarehouseId()); + assertEquals(0, BigDecimal.ZERO.compareTo(histories.get(1).getBeforeQuantity())); + assertEquals(0, new BigDecimal("3.00").compareTo(histories.get(1).getAfterQuantity())); + assertEquals(0, new BigDecimal("180.00").compareTo(histories.get(1).getTotalPrice())); + assertEquals(100L, histories.get(2).getWarehouseId()); + assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(2).getBeforeQuantity())); + assertEquals(0, new BigDecimal("3.00").compareTo(histories.get(2).getAfterQuantity())); + assertEquals(0, new BigDecimal("-80.00").compareTo(histories.get(2).getTotalPrice())); + } + + @Test + public void testChangeInventory_concurrentCreateSameInventoryOnlyOneBalance() throws Exception { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + int threadCount = 4; + CountDownLatch readyLatch = new CountDownLatch(threadCount); + CountDownLatch startLatch = new CountDownLatch(1); + ExecutorService executorService = Executors.newFixedThreadPool(threadCount); + List> futures = new ArrayList<>(threadCount); + for (int i = 0; i < threadCount; i++) { + futures.add(executorService.submit(() -> { + readyLatch.countDown(); + try { + startLatch.await(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new IllegalStateException(ex); + } + inventoryService.changeInventory(createChangeReq(sku.getId(), 100L, "1.00")); + })); + } + + try { + // 调用 + assertTrue(readyLatch.await(5, TimeUnit.SECONDS)); + startLatch.countDown(); + for (Future future : futures) { + future.get(10, TimeUnit.SECONDS); + } + } finally { + executorService.shutdownNow(); + } + + // 断言:并发补行后仍只有一条库存余额,数量累计正确 + List inventories = inventoryMapper.selectListByKeys(Collections.singletonList( + new WmsInventoryDO().setSkuId(sku.getId()).setWarehouseId(100L))); + assertEquals(1, inventories.size()); + assertEquals(0, new BigDecimal("4.00").compareTo(inventories.get(0).getQuantity())); + assertEquals(threadCount, inventoryHistoryMapper.selectCount()); + } + + @Test + public void testChangeInventory_quantityNotEnough() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00")); + WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "-3.00"); + mockItemSkuAndItem(item, sku); + + // 调用,并断言 + assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH, + item.getName(), sku.getName(), 100L, new BigDecimal("2.000000"), new BigDecimal("-3.00")); + WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L); + assertNotNull(inventory); + assertEquals(0, new BigDecimal("2.00").compareTo(inventory.getQuantity())); + assertEquals(0L, inventoryHistoryMapper.selectCount()); + } + + @Test + public void testChangeInventory_quantityNotEnoughWithoutInventory() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "-3.00"); + mockItemSkuAndItem(item, sku); + + // 调用,并断言 + assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH, + item.getName(), sku.getName(), 100L, BigDecimal.ZERO.setScale(6), new BigDecimal("-3.00")); + assertEquals(0L, inventoryMapper.selectCount()); + assertEquals(0L, inventoryHistoryMapper.selectCount()); + } + + @Test + public void testChangeInventory_quantityNotEnoughRollbackCreatedInventories() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "5.00"); + List items = new ArrayList<>(reqDTO.getItems()); + items.add(new WmsInventoryChangeReqDTO.Item() + .setSkuId(sku.getId()) + .setWarehouseId(200L) + .setQuantity(new BigDecimal("-1.00")) + .setPrice(new BigDecimal("20.00")) + .setTotalPrice(new BigDecimal("-20.00")) + .setRemark("测试出库")); + reqDTO.setItems(items); + mockItemSkuAndItem(item, sku); + + // 调用,并断言:第二条明细库存不足,事务回滚前面补齐的库存行 + assertServiceException(() -> inventoryService.changeInventory(reqDTO), INVENTORY_QUANTITY_NOT_ENOUGH, + item.getName(), sku.getName(), 200L, BigDecimal.ZERO.setScale(6), new BigDecimal("-1.00")); + assertNull(inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L)); + assertNull(inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 200L)); + assertEquals(0L, inventoryHistoryMapper.selectCount()); + } + + @Test + public void testCheckInventory_updateExistingInventory() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + WmsInventoryDO inventory = createInventory(sku.getId(), 100L, "10.00"); + inventoryMapper.insert(inventory); + WmsInventoryCheckReqDTO reqDTO = createCheckReq(inventory.getId(), sku.getId(), 100L, + "10.00", "7.00"); + + // 调用 + inventoryService.checkInventory(reqDTO); + + // 断言:库存余额直接调整为实盘数量 + WmsInventoryDO dbInventory = inventoryMapper.selectById(inventory.getId()); + assertNotNull(dbInventory); + assertEquals(0, new BigDecimal("7.00").compareTo(dbInventory.getQuantity())); + // 断言:库存流水记录盘库差异 + List histories = inventoryHistoryMapper.selectList(); + assertEquals(1, histories.size()); + WmsInventoryHistoryDO history = histories.get(0); + assertEquals(sku.getId(), history.getSkuId()); + assertEquals(100L, history.getWarehouseId()); + assertEquals(0, new BigDecimal("10.00").compareTo(history.getBeforeQuantity())); + assertEquals(0, new BigDecimal("7.00").compareTo(history.getAfterQuantity())); + assertEquals(0, new BigDecimal("-3.00").compareTo(history.getQuantity())); + assertEquals(0, new BigDecimal("-300.00").compareTo(history.getTotalPrice())); + assertEquals(reqDTO.getOrderId(), history.getOrderId()); + assertEquals(reqDTO.getOrderNo(), history.getOrderNo()); + assertEquals(reqDTO.getOrderType(), history.getOrderType()); + } + + @Test + public void testCheckInventory_existingInventoryChanged() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + WmsInventoryDO inventory = createInventory(sku.getId(), 100L, "12.00"); + inventoryMapper.insert(inventory); + WmsInventoryCheckReqDTO reqDTO = createCheckReq(inventory.getId(), sku.getId(), 100L, + "10.00", "7.00"); + + // 调用,并断言 + assertServiceException(() -> inventoryService.checkInventory(reqDTO), CHECK_ORDER_INVENTORY_CHANGED); + WmsInventoryDO dbInventory = inventoryMapper.selectById(inventory.getId()); + assertNotNull(dbInventory); + assertEquals(0, new BigDecimal("12.00").compareTo(dbInventory.getQuantity())); + assertEquals(0L, inventoryHistoryMapper.selectCount()); + } + + @Test + public void testCheckInventory_createNewInventory() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + WmsInventoryCheckReqDTO reqDTO = createCheckReq(null, sku.getId(), 100L, + "0.00", "5.00"); + + // 调用 + inventoryService.checkInventory(reqDTO); + + // 断言:新增库存直接落为实盘数量 + WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L); + assertNotNull(inventory); + assertEquals(0, new BigDecimal("5.00").compareTo(inventory.getQuantity())); + // 断言:库存流水记录从 0 到实盘数量 + List histories = inventoryHistoryMapper.selectList(); + assertEquals(1, histories.size()); + assertEquals(0, BigDecimal.ZERO.compareTo(histories.get(0).getBeforeQuantity())); + assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getAfterQuantity())); + assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getQuantity())); + assertEquals(0, new BigDecimal("500.00").compareTo(histories.get(0).getTotalPrice())); + } + + @Test + public void testCheckInventory_createNewZeroInventory() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + WmsInventoryCheckReqDTO reqDTO = createCheckReq(null, sku.getId(), 100L, + "0.00", "0.00"); + + // 调用 + inventoryService.checkInventory(reqDTO); + + // 断言:新增零库存商品也会生成库存余额行 + WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L); + assertNotNull(inventory); + assertEquals(0, BigDecimal.ZERO.compareTo(inventory.getQuantity())); + assertEquals(0L, inventoryHistoryMapper.selectCount()); + } + + @Test + public void testCheckInventory_newInventoryAlreadyExists() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + inventoryMapper.insert(createInventory(sku.getId(), 100L, "3.00")); + WmsInventoryCheckReqDTO reqDTO = createCheckReq(null, sku.getId(), 100L, + "0.00", "5.00"); + + // 调用,并断言 + assertServiceException(() -> inventoryService.checkInventory(reqDTO), CHECK_ORDER_INVENTORY_CHANGED); + WmsInventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L); + assertNotNull(inventory); + assertEquals(0, new BigDecimal("3.00").compareTo(inventory.getQuantity())); + assertEquals(0L, inventoryHistoryMapper.selectCount()); + } + + @Test + public void testGetInventoryPage_filterByWarehouse() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00")); + inventoryMapper.insert(createInventory(sku.getId(), 200L, "5.00")); + // 准备参数 + WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO(); + reqVO.setType(WmsInventoryPageReqVO.TYPE_WAREHOUSE); + reqVO.setWarehouseId(100L); + + // 调用 + PageResult pageResult = inventoryService.getInventoryPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + WmsInventoryDO inventory = pageResult.getList().get(0); + assertEquals(100L, inventory.getWarehouseId()); + assertEquals(sku.getId(), inventory.getSkuId()); + assertEquals(0, new BigDecimal("2.00").compareTo(inventory.getQuantity())); + } + + @Test + public void testGetInventoryPage_groupByWarehouse_minQuantity() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00")); + inventoryMapper.insert(createInventory(sku.getId(), 200L, "7.00")); + // 准备参数 + WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO(); + reqVO.setType(WmsInventoryPageReqVO.TYPE_WAREHOUSE); + reqVO.setMinQuantity(new BigDecimal("6.00")); + + // 调用 + PageResult pageResult = inventoryService.getInventoryPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertEquals(200L, pageResult.getList().get(0).getWarehouseId()); + } + + @Test + public void testGetInventoryPage_onlyPositiveQuantity() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + inventoryMapper.insert(createInventory(sku.getId(), 100L, "0.00")); + inventoryMapper.insert(createInventory(sku.getId(), 200L, "0.01")); + inventoryMapper.insert(createInventory(sku.getId(), 300L, "1.00")); + // 准备参数 + WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO(); + reqVO.setType(WmsInventoryPageReqVO.TYPE_WAREHOUSE); + reqVO.setOnlyPositiveQuantity(true); + + // 调用 + PageResult pageResult = inventoryService.getInventoryPage(reqVO); + // 断言 + assertEquals(2, pageResult.getTotal()); + assertEquals(2, pageResult.getList().size()); + assertEquals(200L, pageResult.getList().get(0).getWarehouseId()); + assertEquals(0, new BigDecimal("0.01").compareTo(pageResult.getList().get(0).getQuantity())); + assertEquals(300L, pageResult.getList().get(1).getWarehouseId()); + assertEquals(0, new BigDecimal("1.00").compareTo(pageResult.getList().get(1).getQuantity())); + } + + @Test + public void testGetInventoryPage_sku() { + // mock 数据 + WmsItemDO item = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装"); + WmsItemSkuDO sku2 = createSku(item.getId(), "SKU-002", "5kg 箱装"); + inventoryMapper.insert(createInventory(sku.getId(), 100L, "2.00")); + inventoryMapper.insert(createInventory(sku2.getId(), 100L, "3.00")); + // 准备参数 + WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO(); + reqVO.setType(WmsInventoryPageReqVO.TYPE_ITEM); + reqVO.setWarehouseId(100L); + reqVO.setSkuId(sku.getId()); + + // 调用 + PageResult pageResult = inventoryService.getInventoryPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertEquals(sku.getId(), pageResult.getList().get(0).getSkuId()); + } + + @Test + public void testGetInventoryPage_orderByItemDimension() { + // mock 数据 + WmsItemDO item1 = createItem("ITEM-001", "红富士苹果"); + WmsItemSkuDO sku1 = createSku(item1.getId(), "SKU-001", "10kg 箱装"); + WmsItemDO item2 = createItem("ITEM-002", "香蕉"); + WmsItemSkuDO sku2 = createSku(item2.getId(), "SKU-002", "5kg 箱装"); + inventoryMapper.insert(createInventory(sku2.getId(), 100L, "3.00")); + inventoryMapper.insert(createInventory(sku1.getId(), 200L, "2.00")); + inventoryMapper.insert(createInventory(sku1.getId(), 100L, "1.00")); + // 准备参数 + WmsInventoryPageReqVO reqVO = new WmsInventoryPageReqVO(); + reqVO.setType(WmsInventoryPageReqVO.TYPE_ITEM); + + // 调用 + PageResult pageResult = inventoryService.getInventoryPage(reqVO); + // 断言 + assertEquals(3, pageResult.getTotal()); + assertEquals(sku1.getId(), pageResult.getList().get(0).getSkuId()); + assertEquals(100L, pageResult.getList().get(0).getWarehouseId()); + assertEquals(sku1.getId(), pageResult.getList().get(1).getSkuId()); + assertEquals(200L, pageResult.getList().get(1).getWarehouseId()); + assertEquals(sku2.getId(), pageResult.getList().get(2).getSkuId()); + assertEquals(100L, pageResult.getList().get(2).getWarehouseId()); + } + + private WmsItemDO createItem(String code, String name) { + WmsItemDO item = WmsItemDO.builder() + .code(code) + .name(name) + .unit("箱") + .categoryId(1L) + .build(); + itemMapper.insert(item); + return item; + } + + private WmsItemSkuDO createSku(Long itemId, String code, String name) { + WmsItemSkuDO sku = WmsItemSkuDO.builder() + .itemId(itemId) + .code(code) + .name(name) + .barCode("69010001") + .build(); + skuMapper.insert(sku); + return sku; + } + + private void mockItemSkuAndItem(WmsItemDO item, WmsItemSkuDO sku) { + when(itemSkuService.validateItemSkuExists(sku.getId())).thenReturn(sku); + when(itemService.validateItemExists(item.getId())).thenReturn(item); + } + + private static WmsInventoryDO createInventory(Long skuId, Long warehouseId, String quantity) { + return WmsInventoryDO.builder() + .skuId(skuId) + .warehouseId(warehouseId) + .quantity(new BigDecimal(quantity)) + .build(); + } + + private static WmsInventoryChangeReqDTO createChangeReq(Long skuId, Long warehouseId, String quantity) { + return new WmsInventoryChangeReqDTO() + .setOrderId(1L) + .setOrderNo("RK202605120001") + .setOrderType(WmsOrderTypeEnum.RECEIPT.getType()) + .setItems(Collections.singletonList(new WmsInventoryChangeReqDTO.Item() + .setSkuId(skuId) + .setWarehouseId(warehouseId) + .setQuantity(new BigDecimal(quantity)) + .setPrice(new BigDecimal("100.00")) + .setTotalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00"))) + .setRemark("测试入库"))); + } + + private static WmsInventoryCheckReqDTO createCheckReq(Long inventoryId, Long skuId, Long warehouseId, + String quantity, String checkQuantity) { + return new WmsInventoryCheckReqDTO() + .setOrderId(2L) + .setOrderNo("PK202605120001") + .setOrderType(WmsOrderTypeEnum.CHECK.getType()) + .setItems(Collections.singletonList(new WmsInventoryCheckReqDTO.Item() + .setInventoryId(inventoryId) + .setSkuId(skuId) + .setWarehouseId(warehouseId) + .setQuantity(new BigDecimal(quantity)) + .setCheckQuantity(new BigDecimal(checkQuantity)) + .setPrice(new BigDecimal("100.00")) + .setRemark("测试盘库"))); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemBrandServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemBrandServiceImplTest.java new file mode 100644 index 000000000..9571961a3 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemBrandServiceImplTest.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.wms.service.md.item; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.brand.WmsItemBrandSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemBrandDO; +import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemBrandMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.ITEM_BRAND_NAME_DUPLICATE; + +@Import(WmsItemBrandServiceImpl.class) +public class WmsItemBrandServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsItemBrandServiceImpl brandService; + + @Resource + private WmsItemBrandMapper brandMapper; + + @MockitoBean + private WmsItemService itemService; + + @Test + public void testCreateItemBrand_nameDuplicate() { + // mock 数据 + brandMapper.insert(createBrand("B001", "华为")); + WmsItemBrandSaveReqVO reqVO = createBrandSaveReqVO(null, "B002", "华为"); + + // 调用,并断言 + assertServiceException(() -> brandService.createItemBrand(reqVO), ITEM_BRAND_NAME_DUPLICATE); + } + + @Test + public void testUpdateItemBrand_nameDuplicate() { + // mock 数据 + brandMapper.insert(createBrand("B001", "华为")); + WmsItemBrandDO brand = createBrand("B002", "小米"); + brandMapper.insert(brand); + WmsItemBrandSaveReqVO reqVO = createBrandSaveReqVO(brand.getId(), "B002", "华为"); + + // 调用,并断言 + assertServiceException(() -> brandService.updateItemBrand(reqVO), ITEM_BRAND_NAME_DUPLICATE); + } + + private static WmsItemBrandDO createBrand(String code, String name) { + return WmsItemBrandDO.builder() + .code(code) + .name(name) + .build(); + } + + private static WmsItemBrandSaveReqVO createBrandSaveReqVO(Long id, String code, String name) { + WmsItemBrandSaveReqVO reqVO = new WmsItemBrandSaveReqVO(); + reqVO.setId(id); + reqVO.setCode(code); + reqVO.setName(name); + return reqVO; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseServiceImplTest.java new file mode 100644 index 000000000..ff4ea7019 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseServiceImplTest.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.wms.service.md.warehouse; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.WmsWarehouseSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.mysql.md.warehouse.WmsWarehouseMapper; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.order.check.WmsCheckOrderService; +import cn.iocoder.yudao.module.wms.service.order.movement.WmsMovementOrderService; +import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderService; +import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.WAREHOUSE_CODE_DUPLICATE; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.WAREHOUSE_HAS_INVENTORY; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.when; + +@Import(WmsWarehouseServiceImpl.class) +public class WmsWarehouseServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsWarehouseServiceImpl warehouseService; + + @Resource + private WmsWarehouseMapper warehouseMapper; + + @MockitoBean + private WmsInventoryService inventoryService; + @MockitoBean + private WmsReceiptOrderService receiptOrderService; + @MockitoBean + private WmsShipmentOrderService shipmentOrderService; + @MockitoBean + private WmsMovementOrderService movementOrderService; + @MockitoBean + private WmsCheckOrderService checkOrderService; + + @Test + public void testCreateWarehouse_codeDuplicate() { + // mock 数据 + WmsWarehouseDO warehouse = createWarehouse("WH001", "成品仓"); + warehouseMapper.insert(warehouse); + + // 调用,并断言 + WmsWarehouseSaveReqVO reqVO = createWarehouseSaveReqVO(); + assertServiceException(() -> warehouseService.createWarehouse(reqVO), WAREHOUSE_CODE_DUPLICATE); + } + + @Test + public void testUpdateWarehouse_codeDuplicate() { + // mock 数据 + WmsWarehouseDO warehouse = createWarehouse("WH001", "成品仓"); + warehouseMapper.insert(warehouse); + WmsWarehouseDO updateWarehouse = createWarehouse("WH002", "原料仓"); + warehouseMapper.insert(updateWarehouse); + + // 调用,并断言 + WmsWarehouseSaveReqVO reqVO = createWarehouseSaveReqVO(); + reqVO.setId(updateWarehouse.getId()); + assertServiceException(() -> warehouseService.updateWarehouse(reqVO), WAREHOUSE_CODE_DUPLICATE); + } + + @Test + public void testDeleteWarehouse_hasInventory() { + // mock 数据 + WmsWarehouseDO warehouse = createWarehouse(); + warehouseMapper.insert(warehouse); + when(inventoryService.getInventoryCountByWarehouseId(warehouse.getId())).thenReturn(1L); + + // 调用,并断言 + assertServiceException(() -> warehouseService.deleteWarehouse(warehouse.getId()), WAREHOUSE_HAS_INVENTORY); + assertNotNull(warehouseMapper.selectById(warehouse.getId())); + } + + private static WmsWarehouseDO createWarehouse() { + return createWarehouse("WH001", "成品仓"); + } + + private static WmsWarehouseDO createWarehouse(String code, String name) { + return WmsWarehouseDO.builder() + .code(code) + .name(name) + .sort(1) + .build(); + } + + private static WmsWarehouseSaveReqVO createWarehouseSaveReqVO() { + WmsWarehouseSaveReqVO reqVO = new WmsWarehouseSaveReqVO(); + reqVO.setCode("WH001"); + reqVO.setName("半成品仓"); + reqVO.setSort(1); + return reqVO; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailServiceImplTest.java new file mode 100644 index 000000000..fe3aae638 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailServiceImplTest.java @@ -0,0 +1,154 @@ +package cn.iocoder.yudao.module.wms.service.order.check; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderDetailMapper; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_DETAIL_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Import(WmsCheckOrderDetailServiceImpl.class) +public class WmsCheckOrderDetailServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsCheckOrderDetailServiceImpl checkOrderDetailService; + + @Resource + private WmsCheckOrderDetailMapper checkOrderDetailMapper; + + @MockitoBean + private WmsItemSkuService itemSkuService; + + @Test + public void testCreateCheckOrderDetailList_success() { + // mock 数据 + Long orderId = 10L; + WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO( + createCheckOrderDetailReqVO(null, 1001L, "10.00", "9.00"), + createCheckOrderDetailReqVO(null, 1002L, "20.00", "21.00")); + + // 调用 + checkOrderDetailService.createCheckOrderDetailList(orderId, reqVO); + + // 断言 + List details = checkOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + assertEquals(orderId, details.get(0).getOrderId()); + assertEquals(orderId, details.get(1).getOrderId()); + assertEquals(100L, details.get(0).getWarehouseId()); + } + + @Test + public void testCreateCheckOrderDetailList_ignoreId() { + // mock 数据 + WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO( + createCheckOrderDetailReqVO(999L, 1001L, "10.00", "9.00")); + + // 调用 + checkOrderDetailService.createCheckOrderDetailList(10L, reqVO); + + // 断言 + List details = checkOrderDetailMapper.selectListByOrderId(10L); + assertEquals(1, details.size()); + assertNotNull(details.get(0).getId()); + assertEquals(1001L, details.get(0).getSkuId()); + } + + @Test + public void testUpdateCheckOrderDetailList_diff() { + // mock 数据 + Long orderId = 10L; + WmsCheckOrderDetailDO detail01 = createCheckOrderDetail(orderId, 1001L, "10.00", "9.00"); + WmsCheckOrderDetailDO detail02 = createCheckOrderDetail(orderId, 1002L, "20.00", "21.00"); + checkOrderDetailMapper.insert(detail01); + checkOrderDetailMapper.insert(detail02); + WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO( + createCheckOrderDetailReqVO(detail01.getId(), 2001L, "11.00", "8.00"), + createCheckOrderDetailReqVO(null, 2002L, "22.00", "25.00")); + + // 调用 + checkOrderDetailService.updateCheckOrderDetailList(orderId, reqVO); + + // 断言:修改 + WmsCheckOrderDetailDO dbUpdateDetail = checkOrderDetailMapper.selectById(detail01.getId()); + assertNotNull(dbUpdateDetail); + assertEquals(orderId, dbUpdateDetail.getOrderId()); + assertEquals(2001L, dbUpdateDetail.getSkuId()); + assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity())); + assertEquals(0, new BigDecimal("8.00").compareTo(dbUpdateDetail.getCheckQuantity())); + assertEquals(0, new BigDecimal("100.00").compareTo(dbUpdateDetail.getPrice())); + assertEquals(100L, dbUpdateDetail.getWarehouseId()); + // 断言:新增 + List details = checkOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + WmsCheckOrderDetailDO dbCreateDetail = details.stream() + .filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId())) + .findFirst().orElse(null); + assertNotNull(dbCreateDetail); + assertEquals(orderId, dbCreateDetail.getOrderId()); + assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity())); + assertEquals(0, new BigDecimal("25.00").compareTo(dbCreateDetail.getCheckQuantity())); + // 断言:删除 + assertNull(checkOrderDetailMapper.selectById(detail02.getId())); + } + + @Test + public void testUpdateCheckOrderDetailList_detailNotExists() { + // mock 数据 + WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO( + createCheckOrderDetailReqVO(999L, 1001L, "10.00", "9.00")); + + // 调用,并断言 + assertServiceException(() -> checkOrderDetailService.updateCheckOrderDetailList(10L, reqVO), + CHECK_ORDER_DETAIL_NOT_EXISTS); + } + + private static WmsCheckOrderSaveReqVO createCheckOrderReqVO(WmsCheckOrderDetailSaveReqVO... details) { + WmsCheckOrderSaveReqVO reqVO = new WmsCheckOrderSaveReqVO(); + reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)); + reqVO.setWarehouseId(100L); + reqVO.setDetails(Arrays.asList(details)); + return reqVO; + } + + private static WmsCheckOrderDetailSaveReqVO createCheckOrderDetailReqVO(Long id, Long skuId, + String quantity, String checkQuantity) { + WmsCheckOrderDetailSaveReqVO reqVO = new WmsCheckOrderDetailSaveReqVO(); + reqVO.setId(id); + reqVO.setSkuId(skuId); + reqVO.setInventoryId(300L); + reqVO.setQuantity(new BigDecimal(quantity)); + reqVO.setCheckQuantity(new BigDecimal(checkQuantity)); + reqVO.setPrice(new BigDecimal("100.00")); + return reqVO; + } + + private static WmsCheckOrderDetailDO createCheckOrderDetail(Long orderId, Long skuId, + String quantity, String checkQuantity) { + return WmsCheckOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .warehouseId(100L) + .inventoryId(300L) + .quantity(new BigDecimal(quantity)) + .checkQuantity(new BigDecimal(checkQuantity)) + .price(new BigDecimal("100.00")) + .build(); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImplTest.java new file mode 100644 index 000000000..d1233f12a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImplTest.java @@ -0,0 +1,347 @@ +package cn.iocoder.yudao.module.wms.service.order.check; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderDetailMapper; +import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryCheckReqDTO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_DETAIL_REQUIRED; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_INVENTORY_CHANGED; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_STATUS_NOT_DELETABLE; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.CHECK_ORDER_STATUS_NOT_PREPARE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@Import({WmsCheckOrderServiceImpl.class, WmsCheckOrderDetailServiceImpl.class}) +public class WmsCheckOrderServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsCheckOrderServiceImpl checkOrderService; + + @Resource + private WmsCheckOrderMapper checkOrderMapper; + @Resource + private WmsCheckOrderDetailMapper checkOrderDetailMapper; + + @MockitoBean + private WmsWarehouseService warehouseService; + @MockitoBean + private WmsItemSkuService itemSkuService; + @MockitoBean + private WmsInventoryService inventoryService; + + @Test + public void testCreateCheckOrder_calculateTotals() { + // mock 数据 + WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(100L, + createCheckOrderDetailReqVO(null, 200L, "10.00", "7.00", "5.00"), + createCheckOrderDetailReqVO(null, 201L, "20.00", "25.00", "2.00")); + + // 调用 + Long orderId = checkOrderService.createCheckOrder(reqVO); + + // 断言:盘库单汇总由后端计算 + WmsCheckOrderDO order = checkOrderMapper.selectById(orderId); + assertNotNull(order); + assertEquals(0, new BigDecimal("2.00").compareTo(order.getTotalQuantity())); + assertEquals(0, new BigDecimal("90.00").compareTo(order.getTotalPrice())); + assertEquals(0, new BigDecimal("85.00").compareTo(order.getActualPrice())); + // 断言:盘库明细保存单价 + List details = checkOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + assertEquals(0, new BigDecimal("5.00").compareTo(details.get(0).getPrice())); + } + + @Test + public void testUpdateCheckOrder_calculateTotals() { + // mock 数据 + WmsCheckOrderDO order = createCheckOrder(100L); + checkOrderMapper.insert(order); + WmsCheckOrderDetailDO oldDetail = createCheckOrderDetail(order.getId(), 200L, 100L, "10.00", "7.00"); + checkOrderDetailMapper.insert(oldDetail); + WmsCheckOrderSaveReqVO reqVO = createCheckOrderReqVO(100L, + createCheckOrderDetailReqVO(oldDetail.getId(), 200L, "8.00", "10.00", "6.00"), + createCheckOrderDetailReqVO(null, 201L, "2.00", "1.00", "4.00")); + reqVO.setId(order.getId()); + reqVO.setNo(order.getNo()); + + // 调用 + checkOrderService.updateCheckOrder(reqVO); + + // 断言:盘库单汇总由后端重新计算 + WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(0, new BigDecimal("1.00").compareTo(dbOrder.getTotalQuantity())); + assertEquals(0, new BigDecimal("56.00").compareTo(dbOrder.getTotalPrice())); + assertEquals(0, new BigDecimal("64.00").compareTo(dbOrder.getActualPrice())); + List details = checkOrderDetailMapper.selectListByOrderId(order.getId()); + assertEquals(2, details.size()); + } + + @Test + public void testCompleteCheckOrder_success() { + // mock 数据 + Long warehouseId = 100L; + Long skuId = 200L; + WmsCheckOrderDO order = createCheckOrder(warehouseId); + checkOrderMapper.insert(order); + checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), skuId, warehouseId, + "10.00", "7.00")); + + // 调用 + checkOrderService.completeCheckOrder(order.getId()); + + // 断言:盘库单 + WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus()); + // 断言:库存盘点 + ArgumentCaptor captor = ArgumentCaptor.forClass(WmsInventoryCheckReqDTO.class); + verify(inventoryService).checkInventory(captor.capture()); + WmsInventoryCheckReqDTO inventoryReqDTO = captor.getValue(); + assertEquals(order.getId(), inventoryReqDTO.getOrderId()); + assertEquals(order.getNo(), inventoryReqDTO.getOrderNo()); + assertEquals(WmsOrderTypeEnum.CHECK.getType(), inventoryReqDTO.getOrderType()); + assertEquals(1, inventoryReqDTO.getItems().size()); + assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId()); + assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId()); + assertEquals(300L, inventoryReqDTO.getItems().get(0).getInventoryId()); + assertEquals(0, new BigDecimal("10.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity())); + assertEquals(0, new BigDecimal("7.00").compareTo(inventoryReqDTO.getItems().get(0).getCheckQuantity())); + assertEquals(0, new BigDecimal("30.00").compareTo(inventoryReqDTO.getItems().get(0).getPrice())); + } + + @Test + public void testCompleteCheckOrder_zeroDifference() { + // mock 数据 + WmsCheckOrderDO order = createCheckOrder(100L); + checkOrderMapper.insert(order); + checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), 200L, 100L, + "10.00", "10.00")); + + // 调用 + checkOrderService.completeCheckOrder(order.getId()); + + // 断言 + WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus()); + verify(inventoryService).checkInventory(any()); + } + + @Test + public void testCompleteCheckOrder_newInventoryCurrentMissing() { + // mock 数据 + Long warehouseId = 100L; + Long skuId = 200L; + WmsCheckOrderDO order = createCheckOrder(warehouseId); + checkOrderMapper.insert(order); + checkOrderDetailMapper.insert(createNewInventoryCheckOrderDetail(order.getId(), skuId, warehouseId, + "0.00", "5.00")); + // 调用 + checkOrderService.completeCheckOrder(order.getId()); + + // 断言 + ArgumentCaptor captor = ArgumentCaptor.forClass(WmsInventoryCheckReqDTO.class); + verify(inventoryService).checkInventory(captor.capture()); + WmsInventoryCheckReqDTO inventoryReqDTO = captor.getValue(); + assertEquals(1, inventoryReqDTO.getItems().size()); + assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId()); + assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId()); + assertNull(inventoryReqDTO.getItems().get(0).getInventoryId()); + assertEquals(0, new BigDecimal("0.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity())); + assertEquals(0, new BigDecimal("5.00").compareTo(inventoryReqDTO.getItems().get(0).getCheckQuantity())); + } + + @Test + public void testCompleteCheckOrder_checkInventoryChanged() { + // mock 数据 + Long warehouseId = 100L; + Long skuId = 200L; + WmsCheckOrderDO order = createCheckOrder(warehouseId); + checkOrderMapper.insert(order); + checkOrderDetailMapper.insert(createNewInventoryCheckOrderDetail(order.getId(), skuId, warehouseId, + "0.00", "5.00")); + doThrow(exception(CHECK_ORDER_INVENTORY_CHANGED)).when(inventoryService).checkInventory(any()); + + // 调用,并断言 + assertServiceException(() -> checkOrderService.completeCheckOrder(order.getId()), + CHECK_ORDER_INVENTORY_CHANGED); + assertEquals(WmsOrderStatusEnum.PREPARE.getStatus(), + checkOrderMapper.selectById(order.getId()).getStatus()); + } + + @Test + public void testCompleteCheckOrder_detailRequired() { + // mock 数据 + WmsCheckOrderDO order = createCheckOrder(100L); + checkOrderMapper.insert(order); + + // 调用,并断言 + assertServiceException(() -> checkOrderService.completeCheckOrder(order.getId()), + CHECK_ORDER_DETAIL_REQUIRED); + verify(inventoryService, never()).checkInventory(any()); + } + + @Test + public void testCompleteCheckOrder_duplicateComplete() { + // mock 数据 + WmsCheckOrderDO order = createCheckOrder(100L); + checkOrderMapper.insert(order); + checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), 200L, 100L, + "10.00", "7.00")); + + // 调用 + checkOrderService.completeCheckOrder(order.getId()); + + // 调用,并断言:二次完成不能再次写库存 + assertServiceException(() -> checkOrderService.completeCheckOrder(order.getId()), + CHECK_ORDER_STATUS_NOT_PREPARE); + verify(inventoryService).checkInventory(any()); + } + + @Test + public void testCancelCheckOrder_success() { + // mock 数据 + WmsCheckOrderDO order = createCheckOrder(100L); + checkOrderMapper.insert(order); + + // 调用 + checkOrderService.cancelCheckOrder(order.getId()); + + // 断言 + WmsCheckOrderDO dbOrder = checkOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus()); + verify(inventoryService, never()).checkInventory(any()); + } + + @Test + public void testUpdateByIdAndStatus_statusNotMatch() { + // mock 数据 + WmsCheckOrderDO order = createCheckOrder(100L); + checkOrderMapper.insert(order); + + // 调用 + int updateCount = checkOrderMapper.updateByIdAndStatus(order.getId(), + WmsOrderStatusEnum.FINISHED.getStatus(), + new WmsCheckOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus())); + + // 断言 + assertEquals(0, updateCount); + assertEquals(WmsOrderStatusEnum.PREPARE.getStatus(), + checkOrderMapper.selectById(order.getId()).getStatus()); + } + + @Test + public void testDeleteCheckOrder_canceled() { + // mock 数据 + WmsCheckOrderDO order = createCheckOrder(100L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus()); + checkOrderMapper.insert(order); + checkOrderDetailMapper.insert(createCheckOrderDetail(order.getId(), 200L, 100L, + "10.00", "8.00")); + + // 调用 + checkOrderService.deleteCheckOrder(order.getId()); + + // 断言 + assertNull(checkOrderMapper.selectById(order.getId())); + assertEquals(0, checkOrderDetailMapper.selectListByOrderId(order.getId()).size()); + } + + @Test + public void testDeleteCheckOrder_finished() { + // mock 数据 + WmsCheckOrderDO order = createCheckOrder(100L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus()); + checkOrderMapper.insert(order); + + // 调用,并断言 + assertServiceException(() -> checkOrderService.deleteCheckOrder(order.getId()), + CHECK_ORDER_STATUS_NOT_DELETABLE); + } + + private static WmsCheckOrderDO createCheckOrder(Long warehouseId) { + return new WmsCheckOrderDO() + .setNo("PK202605120001") + .setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)) + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus()) + .setWarehouseId(warehouseId) + .setTotalQuantity(new BigDecimal("-3.00")) + .setTotalPrice(new BigDecimal("300.00")) + .setActualPrice(new BigDecimal("210.00")); + } + + private static WmsCheckOrderDetailDO createCheckOrderDetail(Long orderId, Long skuId, Long warehouseId, + String quantity, String checkQuantity) { + return WmsCheckOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .warehouseId(warehouseId) + .inventoryId(300L) + .quantity(new BigDecimal(quantity)) + .checkQuantity(new BigDecimal(checkQuantity)) + .price(new BigDecimal("30.00")) + .build(); + } + + private static WmsCheckOrderDetailDO createNewInventoryCheckOrderDetail(Long orderId, Long skuId, Long warehouseId, + String quantity, String checkQuantity) { + return WmsCheckOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .warehouseId(warehouseId) + .quantity(new BigDecimal(quantity)) + .checkQuantity(new BigDecimal(checkQuantity)) + .price(new BigDecimal("30.00")) + .build(); + } + + private static WmsCheckOrderSaveReqVO createCheckOrderReqVO(Long warehouseId, WmsCheckOrderDetailSaveReqVO... details) { + WmsCheckOrderSaveReqVO reqVO = new WmsCheckOrderSaveReqVO(); + reqVO.setNo("PK202605120002"); + reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)); + reqVO.setWarehouseId(warehouseId); + reqVO.setDetails(Arrays.asList(details)); + return reqVO; + } + + private static WmsCheckOrderDetailSaveReqVO createCheckOrderDetailReqVO(Long id, Long skuId, + String quantity, String checkQuantity, + String price) { + WmsCheckOrderDetailSaveReqVO reqVO = new WmsCheckOrderDetailSaveReqVO(); + reqVO.setId(id); + reqVO.setSkuId(skuId); + reqVO.setInventoryId(300L); + reqVO.setQuantity(new BigDecimal(quantity)); + reqVO.setCheckQuantity(new BigDecimal(checkQuantity)); + reqVO.setPrice(new BigDecimal(price)); + return reqVO; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailServiceImplTest.java new file mode 100644 index 000000000..39d278189 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailServiceImplTest.java @@ -0,0 +1,155 @@ +package cn.iocoder.yudao.module.wms.service.order.movement; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderDetailMapper; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_DETAIL_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Import(WmsMovementOrderDetailServiceImpl.class) +public class WmsMovementOrderDetailServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsMovementOrderDetailServiceImpl movementOrderDetailService; + + @Resource + private WmsMovementOrderDetailMapper movementOrderDetailMapper; + + @MockitoBean + private WmsItemSkuService itemSkuService; + + @Test + public void testCreateMovementOrderDetailList_success() { + // mock 数据 + Long orderId = 10L; + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO( + createMovementOrderDetailReqVO(null, 1001L, "1.00"), + createMovementOrderDetailReqVO(null, 1002L, "2.00")); + + // 调用 + movementOrderDetailService.createMovementOrderDetailList(orderId, reqVO); + + // 断言 + List details = movementOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + assertEquals(orderId, details.get(0).getOrderId()); + assertEquals(orderId, details.get(1).getOrderId()); + assertEquals(100L, details.get(0).getSourceWarehouseId()); + assertEquals(200L, details.get(0).getTargetWarehouseId()); + assertEquals(0, new BigDecimal("100.00").compareTo(details.get(0).getTotalPrice())); + assertEquals(0, new BigDecimal("200.00").compareTo(details.get(1).getTotalPrice())); + } + + @Test + public void testCreateMovementOrderDetailList_ignoreId() { + // mock 数据 + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO( + createMovementOrderDetailReqVO(999L, 1001L, "1.00")); + + // 调用 + movementOrderDetailService.createMovementOrderDetailList(10L, reqVO); + + // 断言 + List details = movementOrderDetailMapper.selectListByOrderId(10L); + assertEquals(1, details.size()); + assertNotNull(details.get(0).getId()); + assertEquals(1001L, details.get(0).getSkuId()); + } + + @Test + public void testUpdateMovementOrderDetailList_diff() { + // mock 数据 + Long orderId = 10L; + WmsMovementOrderDetailDO detail01 = createMovementOrderDetail(orderId, 1001L, "1.00"); + WmsMovementOrderDetailDO detail02 = createMovementOrderDetail(orderId, 1002L, "2.00"); + movementOrderDetailMapper.insert(detail01); + movementOrderDetailMapper.insert(detail02); + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO( + createMovementOrderDetailReqVO(detail01.getId(), 2001L, "11.00"), + createMovementOrderDetailReqVO(null, 2002L, "22.00")); + + // 调用 + movementOrderDetailService.updateMovementOrderDetailList(orderId, reqVO); + + // 断言:修改 + WmsMovementOrderDetailDO dbUpdateDetail = movementOrderDetailMapper.selectById(detail01.getId()); + assertNotNull(dbUpdateDetail); + assertEquals(orderId, dbUpdateDetail.getOrderId()); + assertEquals(2001L, dbUpdateDetail.getSkuId()); + assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity())); + assertEquals(0, new BigDecimal("1100.00").compareTo(dbUpdateDetail.getTotalPrice())); + assertEquals(100L, dbUpdateDetail.getSourceWarehouseId()); + assertEquals(200L, dbUpdateDetail.getTargetWarehouseId()); + // 断言:新增 + List details = movementOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + WmsMovementOrderDetailDO dbCreateDetail = details.stream() + .filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId())) + .findFirst().orElse(null); + assertNotNull(dbCreateDetail); + assertEquals(orderId, dbCreateDetail.getOrderId()); + assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity())); + assertEquals(0, new BigDecimal("2200.00").compareTo(dbCreateDetail.getTotalPrice())); + // 断言:删除 + assertNull(movementOrderDetailMapper.selectById(detail02.getId())); + } + + @Test + public void testUpdateMovementOrderDetailList_detailNotExists() { + // mock 数据 + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO( + createMovementOrderDetailReqVO(999L, 1001L, "1.00")); + + // 调用,并断言 + assertServiceException(() -> movementOrderDetailService.updateMovementOrderDetailList(10L, reqVO), + MOVEMENT_ORDER_DETAIL_NOT_EXISTS); + } + + private static WmsMovementOrderSaveReqVO createMovementOrderReqVO(WmsMovementOrderDetailSaveReqVO... details) { + WmsMovementOrderSaveReqVO reqVO = new WmsMovementOrderSaveReqVO(); + reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)); + reqVO.setSourceWarehouseId(100L); + reqVO.setTargetWarehouseId(200L); + reqVO.setDetails(Arrays.asList(details)); + return reqVO; + } + + private static WmsMovementOrderDetailSaveReqVO createMovementOrderDetailReqVO(Long id, Long skuId, String quantity) { + WmsMovementOrderDetailSaveReqVO reqVO = new WmsMovementOrderDetailSaveReqVO(); + reqVO.setId(id); + reqVO.setSkuId(skuId); + reqVO.setQuantity(new BigDecimal(quantity)); + reqVO.setPrice(new BigDecimal("100.00")); + reqVO.setTotalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00"))); + return reqVO; + } + + private static WmsMovementOrderDetailDO createMovementOrderDetail(Long orderId, Long skuId, String quantity) { + return WmsMovementOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .sourceWarehouseId(100L) + .targetWarehouseId(200L) + .quantity(new BigDecimal(quantity)) + .price(new BigDecimal("100.00")) + .totalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00"))) + .build(); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImplTest.java new file mode 100644 index 000000000..a7222f3f5 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImplTest.java @@ -0,0 +1,291 @@ +package cn.iocoder.yudao.module.wms.service.order.movement; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderDetailMapper; +import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_DETAIL_REQUIRED; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_STATUS_NOT_DELETABLE; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_STATUS_NOT_PREPARE; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.MOVEMENT_ORDER_WAREHOUSE_SAME; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@Import({WmsMovementOrderServiceImpl.class, WmsMovementOrderDetailServiceImpl.class}) +public class WmsMovementOrderServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsMovementOrderServiceImpl movementOrderService; + + @Resource + private WmsMovementOrderMapper movementOrderMapper; + @Resource + private WmsMovementOrderDetailMapper movementOrderDetailMapper; + + @MockitoBean + private WmsWarehouseService warehouseService; + @MockitoBean + private WmsItemSkuService itemSkuService; + @MockitoBean + private WmsInventoryService inventoryService; + + @Test + public void testCreateMovementOrder_calculateTotal() { + // mock 数据 + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(null, 100L, 200L, + createMovementOrderDetailReqVO(null, 3001L, "1.50", "10.00", "16.00"), + createMovementOrderDetailReqVO(null, 3002L, "2.50", "20.00", "51.00")); + + // 调用 + Long orderId = movementOrderService.createMovementOrder(reqVO); + + // 断言 + WmsMovementOrderDO dbOrder = movementOrderMapper.selectById(orderId); + assertNotNull(dbOrder); + assertEquals(0, new BigDecimal("4.00").compareTo(dbOrder.getTotalQuantity())); + assertEquals(0, new BigDecimal("67.00").compareTo(dbOrder.getTotalPrice())); + List details = movementOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + assertEquals(0, new BigDecimal("16.00").compareTo(details.get(0).getTotalPrice())); + } + + @Test + public void testUpdateMovementOrder_calculateTotal() { + // mock 数据 + WmsMovementOrderDO order = createMovementOrder(100L, 200L); + movementOrderMapper.insert(order); + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(order.getId(), 100L, 200L, + createMovementOrderDetailReqVO(null, 3001L, "3.00", "30.00", "88.00")); + + // 调用 + movementOrderService.updateMovementOrder(reqVO); + + // 断言 + WmsMovementOrderDO dbOrder = movementOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(0, new BigDecimal("3.00").compareTo(dbOrder.getTotalQuantity())); + assertEquals(0, new BigDecimal("88.00").compareTo(dbOrder.getTotalPrice())); + List details = movementOrderDetailMapper.selectListByOrderId(order.getId()); + assertEquals(1, details.size()); + assertEquals(0, new BigDecimal("88.00").compareTo(details.get(0).getTotalPrice())); + } + + @Test + public void testCompleteMovementOrder_success() { + // mock 数据 + Long sourceWarehouseId = 100L; + Long targetWarehouseId = 200L; + Long skuId = 300L; + WmsMovementOrderDO order = createMovementOrder(sourceWarehouseId, targetWarehouseId); + movementOrderMapper.insert(order); + movementOrderDetailMapper.insert(createMovementOrderDetail(order.getId(), skuId, + sourceWarehouseId, targetWarehouseId)); + + // 调用 + movementOrderService.completeMovementOrder(order.getId()); + + // 断言:移库单 + WmsMovementOrderDO dbOrder = movementOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus()); + // 断言:库存变更 + ArgumentCaptor captor = ArgumentCaptor.forClass(WmsInventoryChangeReqDTO.class); + verify(inventoryService).changeInventory(captor.capture()); + WmsInventoryChangeReqDTO inventoryReqDTO = captor.getValue(); + assertEquals(order.getId(), inventoryReqDTO.getOrderId()); + assertEquals(order.getNo(), inventoryReqDTO.getOrderNo()); + assertEquals(WmsOrderTypeEnum.MOVEMENT.getType(), inventoryReqDTO.getOrderType()); + assertEquals(2, inventoryReqDTO.getItems().size()); + assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId()); + assertEquals(sourceWarehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId()); + assertEquals(0, new BigDecimal("-2.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity())); + assertEquals(0, new BigDecimal("-40.00").compareTo(inventoryReqDTO.getItems().get(0).getTotalPrice())); + assertEquals(skuId, inventoryReqDTO.getItems().get(1).getSkuId()); + assertEquals(targetWarehouseId, inventoryReqDTO.getItems().get(1).getWarehouseId()); + assertEquals(0, new BigDecimal("2.00").compareTo(inventoryReqDTO.getItems().get(1).getQuantity())); + assertEquals(0, new BigDecimal("40.00").compareTo(inventoryReqDTO.getItems().get(1).getTotalPrice())); + } + + @Test + public void testCompleteMovementOrder_detailRequired() { + // mock 数据 + WmsMovementOrderDO order = createMovementOrder(100L, 200L); + movementOrderMapper.insert(order); + + // 调用,并断言 + assertServiceException(() -> movementOrderService.completeMovementOrder(order.getId()), + MOVEMENT_ORDER_DETAIL_REQUIRED); + verify(inventoryService, never()).changeInventory(any()); + } + + @Test + public void testCompleteMovementOrder_duplicateComplete() { + // mock 数据 + WmsMovementOrderDO order = createMovementOrder(100L, 200L); + movementOrderMapper.insert(order); + movementOrderDetailMapper.insert(createMovementOrderDetail(order.getId(), 300L, 100L, 200L)); + + // 调用 + movementOrderService.completeMovementOrder(order.getId()); + + // 调用,并断言:二次完成不能再次写库存 + assertServiceException(() -> movementOrderService.completeMovementOrder(order.getId()), + MOVEMENT_ORDER_STATUS_NOT_PREPARE); + verify(inventoryService).changeInventory(any()); + } + + @Test + public void testCreateMovementOrder_sameWarehouse() { + // 准备参数 + WmsMovementOrderSaveReqVO reqVO = createMovementOrderReqVO(100L, 100L); + + // 调用,并断言 + assertServiceException(() -> movementOrderService.createMovementOrder(reqVO), MOVEMENT_ORDER_WAREHOUSE_SAME); + verify(inventoryService, never()).changeInventory(any()); + } + + @Test + public void testCancelMovementOrder_success() { + // mock 数据 + WmsMovementOrderDO order = createMovementOrder(100L, 200L); + movementOrderMapper.insert(order); + + // 调用 + movementOrderService.cancelMovementOrder(order.getId()); + + // 断言 + WmsMovementOrderDO dbOrder = movementOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus()); + verify(inventoryService, never()).changeInventory(any()); + } + + @Test + public void testUpdateByIdAndStatus_statusNotMatch() { + // mock 数据 + WmsMovementOrderDO order = createMovementOrder(100L, 200L); + movementOrderMapper.insert(order); + + // 调用 + int updateCount = movementOrderMapper.updateByIdAndStatus(order.getId(), + WmsOrderStatusEnum.FINISHED.getStatus(), + new WmsMovementOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus())); + + // 断言 + assertEquals(0, updateCount); + assertEquals(WmsOrderStatusEnum.PREPARE.getStatus(), + movementOrderMapper.selectById(order.getId()).getStatus()); + } + + @Test + public void testDeleteMovementOrder_canceled() { + // mock 数据 + WmsMovementOrderDO order = createMovementOrder(100L, 200L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus()); + movementOrderMapper.insert(order); + movementOrderDetailMapper.insert(createMovementOrderDetail(order.getId(), 300L, 100L, 200L)); + + // 调用 + movementOrderService.deleteMovementOrder(order.getId()); + + // 断言 + assertNull(movementOrderMapper.selectById(order.getId())); + assertEquals(0, movementOrderDetailMapper.selectListByOrderId(order.getId()).size()); + } + + @Test + public void testDeleteMovementOrder_finished() { + // mock 数据 + WmsMovementOrderDO order = createMovementOrder(100L, 200L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus()); + movementOrderMapper.insert(order); + + // 调用,并断言 + assertServiceException(() -> movementOrderService.deleteMovementOrder(order.getId()), + MOVEMENT_ORDER_STATUS_NOT_DELETABLE); + } + + private static WmsMovementOrderDO createMovementOrder(Long sourceWarehouseId, Long targetWarehouseId) { + return new WmsMovementOrderDO() + .setNo("YK202605120001") + .setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)) + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus()) + .setSourceWarehouseId(sourceWarehouseId) + .setTargetWarehouseId(targetWarehouseId) + .setTotalQuantity(new BigDecimal("2.00")) + .setTotalPrice(new BigDecimal("20.00")); + } + + private static WmsMovementOrderDetailDO createMovementOrderDetail(Long orderId, Long skuId, + Long sourceWarehouseId, Long targetWarehouseId) { + return WmsMovementOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .sourceWarehouseId(sourceWarehouseId) + .targetWarehouseId(targetWarehouseId) + .quantity(new BigDecimal("2.00")) + .price(new BigDecimal("20.00")) + .totalPrice(new BigDecimal("40.00")) + .build(); + } + + private static WmsMovementOrderSaveReqVO createMovementOrderReqVO(Long sourceWarehouseId, Long targetWarehouseId) { + return createMovementOrderReqVO(null, sourceWarehouseId, targetWarehouseId); + } + + private static WmsMovementOrderSaveReqVO createMovementOrderReqVO(Long id, Long sourceWarehouseId, + Long targetWarehouseId, + WmsMovementOrderDetailSaveReqVO... details) { + WmsMovementOrderSaveReqVO reqVO = new WmsMovementOrderSaveReqVO(); + reqVO.setId(id); + reqVO.setNo("YK202605120001"); + reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)); + reqVO.setSourceWarehouseId(sourceWarehouseId); + reqVO.setTargetWarehouseId(targetWarehouseId); + reqVO.setDetails(Arrays.asList(details)); + return reqVO; + } + + private static WmsMovementOrderDetailSaveReqVO createMovementOrderDetailReqVO(Long id, Long skuId, + String quantity, String price) { + return createMovementOrderDetailReqVO(id, skuId, quantity, price, null); + } + + private static WmsMovementOrderDetailSaveReqVO createMovementOrderDetailReqVO(Long id, Long skuId, + String quantity, String price, + String totalPrice) { + WmsMovementOrderDetailSaveReqVO reqVO = new WmsMovementOrderDetailSaveReqVO(); + reqVO.setId(id); + reqVO.setSkuId(skuId); + reqVO.setQuantity(new BigDecimal(quantity)); + reqVO.setPrice(new BigDecimal(price)); + if (totalPrice != null) { + reqVO.setTotalPrice(new BigDecimal(totalPrice)); + } + return reqVO; + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImplTest.java new file mode 100644 index 000000000..e8d93aa24 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImplTest.java @@ -0,0 +1,149 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderDetailMapper; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.RECEIPT_ORDER_DETAIL_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Import(WmsReceiptOrderDetailServiceImpl.class) +public class WmsReceiptOrderDetailServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsReceiptOrderDetailServiceImpl receiptOrderDetailService; + + @Resource + private WmsReceiptOrderDetailMapper receiptOrderDetailMapper; + + @MockitoBean + private WmsItemSkuService itemSkuService; + + @Test + public void testCreateReceiptOrderDetailList_success() { + // mock 数据 + Long orderId = 10L; + WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderReqVO( + createReceiptOrderDetailReqVO(null, 1001L, "1.00"), + createReceiptOrderDetailReqVO(null, 1002L, "2.00")); + + // 调用 + receiptOrderDetailService.createReceiptOrderDetailList(orderId, reqVO); + + // 断言 + List details = receiptOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + assertEquals(orderId, details.get(0).getOrderId()); + assertEquals(orderId, details.get(1).getOrderId()); + assertEquals(0, new BigDecimal("100.00").compareTo(details.get(0).getTotalPrice())); + assertEquals(0, new BigDecimal("200.00").compareTo(details.get(1).getTotalPrice())); + } + + @Test + public void testCreateReceiptOrderDetailList_ignoreId() { + // mock 数据 + WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderReqVO( + createReceiptOrderDetailReqVO(999L, 1001L, "1.00")); + + // 调用 + receiptOrderDetailService.createReceiptOrderDetailList(10L, reqVO); + + // 断言 + List details = receiptOrderDetailMapper.selectListByOrderId(10L); + assertEquals(1, details.size()); + assertNotNull(details.get(0).getId()); + assertEquals(1001L, details.get(0).getSkuId()); + } + + @Test + public void testUpdateReceiptOrderDetailList_diff() { + // mock 数据 + Long orderId = 10L; + WmsReceiptOrderDetailDO detail01 = createReceiptOrderDetail(orderId, 1001L, "1.00"); + WmsReceiptOrderDetailDO detail02 = createReceiptOrderDetail(orderId, 1002L, "2.00"); + receiptOrderDetailMapper.insert(detail01); + receiptOrderDetailMapper.insert(detail02); + WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderReqVO( + createReceiptOrderDetailReqVO(detail01.getId(), 2001L, "11.00"), + createReceiptOrderDetailReqVO(null, 2002L, "22.00")); + + // 调用 + receiptOrderDetailService.updateReceiptOrderDetailList(orderId, reqVO); + + // 断言:修改 + WmsReceiptOrderDetailDO dbUpdateDetail = receiptOrderDetailMapper.selectById(detail01.getId()); + assertNotNull(dbUpdateDetail); + assertEquals(orderId, dbUpdateDetail.getOrderId()); + assertEquals(2001L, dbUpdateDetail.getSkuId()); + assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity())); + assertEquals(0, new BigDecimal("1100.00").compareTo(dbUpdateDetail.getTotalPrice())); + // 断言:新增 + List details = receiptOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + WmsReceiptOrderDetailDO dbCreateDetail = details.stream() + .filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId())) + .findFirst().orElse(null); + assertNotNull(dbCreateDetail); + assertEquals(orderId, dbCreateDetail.getOrderId()); + assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity())); + assertEquals(0, new BigDecimal("2200.00").compareTo(dbCreateDetail.getTotalPrice())); + // 断言:删除 + assertNull(receiptOrderDetailMapper.selectById(detail02.getId())); + } + + @Test + public void testUpdateReceiptOrderDetailList_detailNotExists() { + // mock 数据 + WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderReqVO( + createReceiptOrderDetailReqVO(999L, 1001L, "1.00")); + + // 调用,并断言 + assertServiceException(() -> receiptOrderDetailService.updateReceiptOrderDetailList(10L, reqVO), + RECEIPT_ORDER_DETAIL_NOT_EXISTS); + } + + private static WmsReceiptOrderSaveReqVO createReceiptOrderReqVO(WmsReceiptOrderDetailSaveReqVO... details) { + WmsReceiptOrderSaveReqVO reqVO = new WmsReceiptOrderSaveReqVO(); + reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)); + reqVO.setWarehouseId(100L); + reqVO.setDetails(Arrays.asList(details)); + return reqVO; + } + + private static WmsReceiptOrderDetailSaveReqVO createReceiptOrderDetailReqVO(Long id, Long skuId, String quantity) { + WmsReceiptOrderDetailSaveReqVO reqVO = new WmsReceiptOrderDetailSaveReqVO(); + reqVO.setId(id); + reqVO.setSkuId(skuId); + reqVO.setQuantity(new BigDecimal(quantity)); + reqVO.setPrice(new BigDecimal("100.00")); + reqVO.setTotalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00"))); + return reqVO; + } + + private static WmsReceiptOrderDetailDO createReceiptOrderDetail(Long orderId, Long skuId, String quantity) { + return WmsReceiptOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .warehouseId(100L) + .quantity(new BigDecimal(quantity)) + .price(new BigDecimal("100.00")) + .totalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00"))) + .build(); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImplTest.java new file mode 100644 index 000000000..36f029c5c --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImplTest.java @@ -0,0 +1,271 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderDetailMapper; +import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderTypeEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.RECEIPT_ORDER_DETAIL_REQUIRED; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.RECEIPT_ORDER_STATUS_NOT_PREPARE; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.RECEIPT_ORDER_STATUS_NOT_DELETABLE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@Import({WmsReceiptOrderServiceImpl.class, WmsReceiptOrderDetailServiceImpl.class}) +public class WmsReceiptOrderServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsReceiptOrderServiceImpl receiptOrderService; + + @Resource + private WmsReceiptOrderMapper receiptOrderMapper; + @Resource + private WmsReceiptOrderDetailMapper receiptOrderDetailMapper; + + @MockitoBean + private WmsWarehouseService warehouseService; + @MockitoBean + private WmsMerchantService merchantService; + @MockitoBean + private WmsItemSkuService itemSkuService; + @MockitoBean + private WmsInventoryService inventoryService; + + @Test + public void testCreateReceiptOrder_calculateTotal() { + // mock 数据 + WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderSaveReqVO(null, + createReceiptOrderDetailReqVO(null, 2001L, "1.50", "10.00", "16.00"), + createReceiptOrderDetailReqVO(null, 2002L, "2.50", "20.00", "51.00")); + + // 调用 + Long orderId = receiptOrderService.createReceiptOrder(reqVO); + + // 断言 + WmsReceiptOrderDO dbOrder = receiptOrderMapper.selectById(orderId); + assertNotNull(dbOrder); + assertEquals(0, new BigDecimal("4.00").compareTo(dbOrder.getTotalQuantity())); + assertEquals(0, new BigDecimal("67.00").compareTo(dbOrder.getTotalPrice())); + List details = receiptOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + assertEquals(0, new BigDecimal("16.00").compareTo(details.get(0).getTotalPrice())); + } + + @Test + public void testUpdateReceiptOrder_calculateTotal() { + // mock 数据 + WmsReceiptOrderDO order = createReceiptOrder(100L); + receiptOrderMapper.insert(order); + WmsReceiptOrderSaveReqVO reqVO = createReceiptOrderSaveReqVO(order.getId(), + createReceiptOrderDetailReqVO(null, 2001L, "3.00", "30.00", "88.00")); + + // 调用 + receiptOrderService.updateReceiptOrder(reqVO); + + // 断言 + WmsReceiptOrderDO dbOrder = receiptOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(0, new BigDecimal("3.00").compareTo(dbOrder.getTotalQuantity())); + assertEquals(0, new BigDecimal("88.00").compareTo(dbOrder.getTotalPrice())); + List details = receiptOrderDetailMapper.selectListByOrderId(order.getId()); + assertEquals(1, details.size()); + assertEquals(0, new BigDecimal("88.00").compareTo(details.get(0).getTotalPrice())); + } + + @Test + public void testCompleteReceiptOrder_success() { + // mock 数据 + Long warehouseId = 100L; + Long skuId = 200L; + WmsReceiptOrderDO order = createReceiptOrder(warehouseId); + receiptOrderMapper.insert(order); + receiptOrderDetailMapper.insert(createReceiptOrderDetail(order.getId(), skuId, warehouseId)); + + // 调用 + receiptOrderService.completeReceiptOrder(order.getId()); + + // 断言:入库单 + WmsReceiptOrderDO dbOrder = receiptOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus()); + // 断言:库存变更 + ArgumentCaptor captor = ArgumentCaptor.forClass(WmsInventoryChangeReqDTO.class); + verify(inventoryService).changeInventory(captor.capture()); + WmsInventoryChangeReqDTO inventoryReqDTO = captor.getValue(); + assertEquals(order.getId(), inventoryReqDTO.getOrderId()); + assertEquals(order.getNo(), inventoryReqDTO.getOrderNo()); + assertEquals(WmsOrderTypeEnum.RECEIPT.getType(), inventoryReqDTO.getOrderType()); + assertEquals(1, inventoryReqDTO.getItems().size()); + assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId()); + assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId()); + assertEquals(0, new BigDecimal("2.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity())); + assertEquals(0, new BigDecimal("40.00").compareTo(inventoryReqDTO.getItems().get(0).getTotalPrice())); + } + + @Test + public void testCompleteReceiptOrder_detailRequired() { + // mock 数据 + WmsReceiptOrderDO order = createReceiptOrder(100L); + receiptOrderMapper.insert(order); + + // 调用,并断言 + assertServiceException(() -> receiptOrderService.completeReceiptOrder(order.getId()), + RECEIPT_ORDER_DETAIL_REQUIRED); + verify(inventoryService, never()).changeInventory(any()); + } + + @Test + public void testCompleteReceiptOrder_duplicateComplete() { + // mock 数据 + WmsReceiptOrderDO order = createReceiptOrder(100L); + receiptOrderMapper.insert(order); + receiptOrderDetailMapper.insert(createReceiptOrderDetail(order.getId(), 200L, 100L)); + + // 调用 + receiptOrderService.completeReceiptOrder(order.getId()); + + // 调用,并断言:二次完成不能再次写库存 + assertServiceException(() -> receiptOrderService.completeReceiptOrder(order.getId()), + RECEIPT_ORDER_STATUS_NOT_PREPARE); + verify(inventoryService).changeInventory(any()); + } + + @Test + public void testCancelReceiptOrder_success() { + // mock 数据 + WmsReceiptOrderDO order = createReceiptOrder(100L); + receiptOrderMapper.insert(order); + + // 调用 + receiptOrderService.cancelReceiptOrder(order.getId()); + + // 断言 + WmsReceiptOrderDO dbOrder = receiptOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus()); + verify(inventoryService, never()).changeInventory(any()); + } + + @Test + public void testUpdateByIdAndStatus_statusNotMatch() { + // mock 数据 + WmsReceiptOrderDO order = createReceiptOrder(100L); + receiptOrderMapper.insert(order); + + // 调用 + int updateCount = receiptOrderMapper.updateByIdAndStatus(order.getId(), + WmsOrderStatusEnum.FINISHED.getStatus(), + new WmsReceiptOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus())); + + // 断言 + assertEquals(0, updateCount); + assertEquals(WmsOrderStatusEnum.PREPARE.getStatus(), + receiptOrderMapper.selectById(order.getId()).getStatus()); + } + + @Test + public void testDeleteReceiptOrder_canceled() { + // mock 数据 + WmsReceiptOrderDO order = createReceiptOrder(100L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus()); + receiptOrderMapper.insert(order); + receiptOrderDetailMapper.insert(createReceiptOrderDetail(order.getId(), 200L, 100L)); + + // 调用 + receiptOrderService.deleteReceiptOrder(order.getId()); + + // 断言 + assertNull(receiptOrderMapper.selectById(order.getId())); + assertEquals(0, receiptOrderDetailMapper.selectListByOrderId(order.getId()).size()); + } + + @Test + public void testDeleteReceiptOrder_finished() { + // mock 数据 + WmsReceiptOrderDO order = createReceiptOrder(100L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus()); + receiptOrderMapper.insert(order); + + // 调用,并断言 + assertServiceException(() -> receiptOrderService.deleteReceiptOrder(order.getId()), + RECEIPT_ORDER_STATUS_NOT_DELETABLE); + } + + private static WmsReceiptOrderDO createReceiptOrder(Long warehouseId) { + return new WmsReceiptOrderDO() + .setNo("RK202605120001") + .setType(WmsReceiptOrderTypeEnum.PURCHASE.getType()) + .setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)) + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus()) + .setWarehouseId(warehouseId) + .setTotalQuantity(new BigDecimal("2.00")) + .setTotalPrice(new BigDecimal("20.00")); + } + + private static WmsReceiptOrderSaveReqVO createReceiptOrderSaveReqVO(Long id, + WmsReceiptOrderDetailSaveReqVO... details) { + WmsReceiptOrderSaveReqVO reqVO = new WmsReceiptOrderSaveReqVO(); + reqVO.setId(id); + reqVO.setNo("RK202605120001"); + reqVO.setType(WmsReceiptOrderTypeEnum.PURCHASE.getType()); + reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)); + reqVO.setWarehouseId(100L); + reqVO.setDetails(Arrays.asList(details)); + return reqVO; + } + + private static WmsReceiptOrderDetailSaveReqVO createReceiptOrderDetailReqVO(Long id, Long skuId, + String quantity, String price) { + return createReceiptOrderDetailReqVO(id, skuId, quantity, price, null); + } + + private static WmsReceiptOrderDetailSaveReqVO createReceiptOrderDetailReqVO(Long id, Long skuId, + String quantity, String price, + String totalPrice) { + WmsReceiptOrderDetailSaveReqVO reqVO = new WmsReceiptOrderDetailSaveReqVO(); + reqVO.setId(id); + reqVO.setSkuId(skuId); + reqVO.setQuantity(new BigDecimal(quantity)); + reqVO.setPrice(new BigDecimal(price)); + if (totalPrice != null) { + reqVO.setTotalPrice(new BigDecimal(totalPrice)); + } + return reqVO; + } + + private static WmsReceiptOrderDetailDO createReceiptOrderDetail(Long orderId, Long skuId, Long warehouseId) { + return WmsReceiptOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .warehouseId(warehouseId) + .quantity(new BigDecimal("2.00")) + .price(new BigDecimal("20.00")) + .totalPrice(new BigDecimal("40.00")) + .build(); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailServiceImplTest.java new file mode 100644 index 000000000..2ea341c0b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailServiceImplTest.java @@ -0,0 +1,150 @@ +package cn.iocoder.yudao.module.wms.service.order.shipment; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderDetailMapper; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_DETAIL_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Import(WmsShipmentOrderDetailServiceImpl.class) +public class WmsShipmentOrderDetailServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsShipmentOrderDetailServiceImpl shipmentOrderDetailService; + + @Resource + private WmsShipmentOrderDetailMapper shipmentOrderDetailMapper; + + @MockitoBean + private WmsItemSkuService itemSkuService; + + @Test + public void testCreateShipmentOrderDetailList_success() { + // mock 数据 + Long orderId = 10L; + WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO( + createShipmentOrderDetailReqVO(null, 1001L, "1.00"), + createShipmentOrderDetailReqVO(null, 1002L, "2.00")); + + // 调用 + shipmentOrderDetailService.createShipmentOrderDetailList(orderId, reqVO); + + // 断言 + List details = shipmentOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + assertEquals(orderId, details.get(0).getOrderId()); + assertEquals(orderId, details.get(1).getOrderId()); + assertEquals(100L, details.get(0).getWarehouseId()); + assertEquals(0, new BigDecimal("100.00").compareTo(details.get(0).getTotalPrice())); + assertEquals(0, new BigDecimal("200.00").compareTo(details.get(1).getTotalPrice())); + } + + @Test + public void testCreateShipmentOrderDetailList_ignoreId() { + // mock 数据 + WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO( + createShipmentOrderDetailReqVO(999L, 1001L, "1.00")); + + // 调用 + shipmentOrderDetailService.createShipmentOrderDetailList(10L, reqVO); + + // 断言 + List details = shipmentOrderDetailMapper.selectListByOrderId(10L); + assertEquals(1, details.size()); + assertNotNull(details.get(0).getId()); + assertEquals(1001L, details.get(0).getSkuId()); + } + + @Test + public void testUpdateShipmentOrderDetailList_diff() { + // mock 数据 + Long orderId = 10L; + WmsShipmentOrderDetailDO detail01 = createShipmentOrderDetail(orderId, 1001L, "1.00"); + WmsShipmentOrderDetailDO detail02 = createShipmentOrderDetail(orderId, 1002L, "2.00"); + shipmentOrderDetailMapper.insert(detail01); + shipmentOrderDetailMapper.insert(detail02); + WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO( + createShipmentOrderDetailReqVO(detail01.getId(), 2001L, "11.00"), + createShipmentOrderDetailReqVO(null, 2002L, "22.00")); + + // 调用 + shipmentOrderDetailService.updateShipmentOrderDetailList(orderId, reqVO); + + // 断言:修改 + WmsShipmentOrderDetailDO dbUpdateDetail = shipmentOrderDetailMapper.selectById(detail01.getId()); + assertNotNull(dbUpdateDetail); + assertEquals(orderId, dbUpdateDetail.getOrderId()); + assertEquals(2001L, dbUpdateDetail.getSkuId()); + assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity())); + assertEquals(0, new BigDecimal("1100.00").compareTo(dbUpdateDetail.getTotalPrice())); + // 断言:新增 + List details = shipmentOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + WmsShipmentOrderDetailDO dbCreateDetail = details.stream() + .filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId())) + .findFirst().orElse(null); + assertNotNull(dbCreateDetail); + assertEquals(orderId, dbCreateDetail.getOrderId()); + assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity())); + assertEquals(0, new BigDecimal("2200.00").compareTo(dbCreateDetail.getTotalPrice())); + // 断言:删除 + assertNull(shipmentOrderDetailMapper.selectById(detail02.getId())); + } + + @Test + public void testUpdateShipmentOrderDetailList_detailNotExists() { + // mock 数据 + WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderReqVO( + createShipmentOrderDetailReqVO(999L, 1001L, "1.00")); + + // 调用,并断言 + assertServiceException(() -> shipmentOrderDetailService.updateShipmentOrderDetailList(10L, reqVO), + SHIPMENT_ORDER_DETAIL_NOT_EXISTS); + } + + private static WmsShipmentOrderSaveReqVO createShipmentOrderReqVO(WmsShipmentOrderDetailSaveReqVO... details) { + WmsShipmentOrderSaveReqVO reqVO = new WmsShipmentOrderSaveReqVO(); + reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)); + reqVO.setWarehouseId(100L); + reqVO.setDetails(Arrays.asList(details)); + return reqVO; + } + + private static WmsShipmentOrderDetailSaveReqVO createShipmentOrderDetailReqVO(Long id, Long skuId, String quantity) { + WmsShipmentOrderDetailSaveReqVO reqVO = new WmsShipmentOrderDetailSaveReqVO(); + reqVO.setId(id); + reqVO.setSkuId(skuId); + reqVO.setQuantity(new BigDecimal(quantity)); + reqVO.setPrice(new BigDecimal("100.00")); + reqVO.setTotalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00"))); + return reqVO; + } + + private static WmsShipmentOrderDetailDO createShipmentOrderDetail(Long orderId, Long skuId, String quantity) { + return WmsShipmentOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .warehouseId(100L) + .quantity(new BigDecimal(quantity)) + .price(new BigDecimal("100.00")) + .totalPrice(new BigDecimal(quantity).multiply(new BigDecimal("100.00"))) + .build(); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderServiceImplTest.java b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderServiceImplTest.java new file mode 100644 index 000000000..6a4e86cb7 --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderServiceImplTest.java @@ -0,0 +1,271 @@ +package cn.iocoder.yudao.module.wms.service.order.shipment; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderDetailMapper; +import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderMapper; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsShipmentOrderTypeEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_DETAIL_REQUIRED; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_STATUS_NOT_DELETABLE; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_STATUS_NOT_PREPARE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@Import({WmsShipmentOrderServiceImpl.class, WmsShipmentOrderDetailServiceImpl.class}) +public class WmsShipmentOrderServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsShipmentOrderServiceImpl shipmentOrderService; + + @Resource + private WmsShipmentOrderMapper shipmentOrderMapper; + @Resource + private WmsShipmentOrderDetailMapper shipmentOrderDetailMapper; + + @MockitoBean + private WmsWarehouseService warehouseService; + @MockitoBean + private WmsMerchantService merchantService; + @MockitoBean + private WmsItemSkuService itemSkuService; + @MockitoBean + private WmsInventoryService inventoryService; + + @Test + public void testCreateShipmentOrder_calculateTotal() { + // mock 数据 + WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderSaveReqVO(null, + createShipmentOrderDetailReqVO(null, 2001L, "1.50", "10.00", "16.00"), + createShipmentOrderDetailReqVO(null, 2002L, "2.50", "20.00", "51.00")); + + // 调用 + Long orderId = shipmentOrderService.createShipmentOrder(reqVO); + + // 断言 + WmsShipmentOrderDO dbOrder = shipmentOrderMapper.selectById(orderId); + assertNotNull(dbOrder); + assertEquals(0, new BigDecimal("4.00").compareTo(dbOrder.getTotalQuantity())); + assertEquals(0, new BigDecimal("67.00").compareTo(dbOrder.getTotalPrice())); + List details = shipmentOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + assertEquals(0, new BigDecimal("16.00").compareTo(details.get(0).getTotalPrice())); + } + + @Test + public void testUpdateShipmentOrder_calculateTotal() { + // mock 数据 + WmsShipmentOrderDO order = createShipmentOrder(100L); + shipmentOrderMapper.insert(order); + WmsShipmentOrderSaveReqVO reqVO = createShipmentOrderSaveReqVO(order.getId(), + createShipmentOrderDetailReqVO(null, 2001L, "3.00", "30.00", "88.00")); + + // 调用 + shipmentOrderService.updateShipmentOrder(reqVO); + + // 断言 + WmsShipmentOrderDO dbOrder = shipmentOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(0, new BigDecimal("3.00").compareTo(dbOrder.getTotalQuantity())); + assertEquals(0, new BigDecimal("88.00").compareTo(dbOrder.getTotalPrice())); + List details = shipmentOrderDetailMapper.selectListByOrderId(order.getId()); + assertEquals(1, details.size()); + assertEquals(0, new BigDecimal("88.00").compareTo(details.get(0).getTotalPrice())); + } + + @Test + public void testCompleteShipmentOrder_success() { + // mock 数据 + Long warehouseId = 100L; + Long skuId = 200L; + WmsShipmentOrderDO order = createShipmentOrder(warehouseId); + shipmentOrderMapper.insert(order); + shipmentOrderDetailMapper.insert(createShipmentOrderDetail(order.getId(), skuId, warehouseId)); + + // 调用 + shipmentOrderService.completeShipmentOrder(order.getId()); + + // 断言:出库单 + WmsShipmentOrderDO dbOrder = shipmentOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus()); + // 断言:库存变更 + ArgumentCaptor captor = ArgumentCaptor.forClass(WmsInventoryChangeReqDTO.class); + verify(inventoryService).changeInventory(captor.capture()); + WmsInventoryChangeReqDTO inventoryReqDTO = captor.getValue(); + assertEquals(order.getId(), inventoryReqDTO.getOrderId()); + assertEquals(order.getNo(), inventoryReqDTO.getOrderNo()); + assertEquals(WmsOrderTypeEnum.SHIPMENT.getType(), inventoryReqDTO.getOrderType()); + assertEquals(1, inventoryReqDTO.getItems().size()); + assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId()); + assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId()); + assertEquals(0, new BigDecimal("-2.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity())); + assertEquals(0, new BigDecimal("-40.00").compareTo(inventoryReqDTO.getItems().get(0).getTotalPrice())); + } + + @Test + public void testCompleteShipmentOrder_detailRequired() { + // mock 数据 + WmsShipmentOrderDO order = createShipmentOrder(100L); + shipmentOrderMapper.insert(order); + + // 调用,并断言 + assertServiceException(() -> shipmentOrderService.completeShipmentOrder(order.getId()), + SHIPMENT_ORDER_DETAIL_REQUIRED); + verify(inventoryService, never()).changeInventory(any()); + } + + @Test + public void testCompleteShipmentOrder_duplicateComplete() { + // mock 数据 + WmsShipmentOrderDO order = createShipmentOrder(100L); + shipmentOrderMapper.insert(order); + shipmentOrderDetailMapper.insert(createShipmentOrderDetail(order.getId(), 200L, 100L)); + + // 调用 + shipmentOrderService.completeShipmentOrder(order.getId()); + + // 调用,并断言:二次完成不能再次写库存 + assertServiceException(() -> shipmentOrderService.completeShipmentOrder(order.getId()), + SHIPMENT_ORDER_STATUS_NOT_PREPARE); + verify(inventoryService).changeInventory(any()); + } + + @Test + public void testCancelShipmentOrder_success() { + // mock 数据 + WmsShipmentOrderDO order = createShipmentOrder(100L); + shipmentOrderMapper.insert(order); + + // 调用 + shipmentOrderService.cancelShipmentOrder(order.getId()); + + // 断言 + WmsShipmentOrderDO dbOrder = shipmentOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus()); + verify(inventoryService, never()).changeInventory(any()); + } + + @Test + public void testUpdateByIdAndStatus_statusNotMatch() { + // mock 数据 + WmsShipmentOrderDO order = createShipmentOrder(100L); + shipmentOrderMapper.insert(order); + + // 调用 + int updateCount = shipmentOrderMapper.updateByIdAndStatus(order.getId(), + WmsOrderStatusEnum.FINISHED.getStatus(), + new WmsShipmentOrderDO().setStatus(WmsOrderStatusEnum.CANCELED.getStatus())); + + // 断言 + assertEquals(0, updateCount); + assertEquals(WmsOrderStatusEnum.PREPARE.getStatus(), + shipmentOrderMapper.selectById(order.getId()).getStatus()); + } + + @Test + public void testDeleteShipmentOrder_canceled() { + // mock 数据 + WmsShipmentOrderDO order = createShipmentOrder(100L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus()); + shipmentOrderMapper.insert(order); + shipmentOrderDetailMapper.insert(createShipmentOrderDetail(order.getId(), 200L, 100L)); + + // 调用 + shipmentOrderService.deleteShipmentOrder(order.getId()); + + // 断言 + assertNull(shipmentOrderMapper.selectById(order.getId())); + assertEquals(0, shipmentOrderDetailMapper.selectListByOrderId(order.getId()).size()); + } + + @Test + public void testDeleteShipmentOrder_finished() { + // mock 数据 + WmsShipmentOrderDO order = createShipmentOrder(100L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus()); + shipmentOrderMapper.insert(order); + + // 调用,并断言 + assertServiceException(() -> shipmentOrderService.deleteShipmentOrder(order.getId()), + SHIPMENT_ORDER_STATUS_NOT_DELETABLE); + } + + private static WmsShipmentOrderDO createShipmentOrder(Long warehouseId) { + return new WmsShipmentOrderDO() + .setNo("CK202605120001") + .setType(WmsShipmentOrderTypeEnum.SALE.getType()) + .setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)) + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus()) + .setWarehouseId(warehouseId) + .setTotalQuantity(new BigDecimal("2.00")) + .setTotalPrice(new BigDecimal("20.00")); + } + + private static WmsShipmentOrderSaveReqVO createShipmentOrderSaveReqVO(Long id, + WmsShipmentOrderDetailSaveReqVO... details) { + WmsShipmentOrderSaveReqVO reqVO = new WmsShipmentOrderSaveReqVO(); + reqVO.setId(id); + reqVO.setNo("CK202605120001"); + reqVO.setType(WmsShipmentOrderTypeEnum.SALE.getType()); + reqVO.setOrderTime(LocalDateTime.of(2026, 5, 12, 0, 0)); + reqVO.setWarehouseId(100L); + reqVO.setDetails(Arrays.asList(details)); + return reqVO; + } + + private static WmsShipmentOrderDetailSaveReqVO createShipmentOrderDetailReqVO(Long id, Long skuId, + String quantity, String price) { + return createShipmentOrderDetailReqVO(id, skuId, quantity, price, null); + } + + private static WmsShipmentOrderDetailSaveReqVO createShipmentOrderDetailReqVO(Long id, Long skuId, + String quantity, String price, + String totalPrice) { + WmsShipmentOrderDetailSaveReqVO reqVO = new WmsShipmentOrderDetailSaveReqVO(); + reqVO.setId(id); + reqVO.setSkuId(skuId); + reqVO.setQuantity(new BigDecimal(quantity)); + reqVO.setPrice(new BigDecimal(price)); + if (totalPrice != null) { + reqVO.setTotalPrice(new BigDecimal(totalPrice)); + } + return reqVO; + } + + private static WmsShipmentOrderDetailDO createShipmentOrderDetail(Long orderId, Long skuId, Long warehouseId) { + return WmsShipmentOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .warehouseId(warehouseId) + .quantity(new BigDecimal("2.00")) + .price(new BigDecimal("20.00")) + .totalPrice(new BigDecimal("40.00")) + .build(); + } + +} diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/resources/application-unit-test.yaml b/yudao-module-wms/yudao-module-wms-server/src/test/resources/application-unit-test.yaml new file mode 100644 index 000000000..29615501a --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/resources/application-unit-test.yaml @@ -0,0 +1,35 @@ +spring: + main: + lazy-initialization: true # 开启懒加载,加快速度 + banner-mode: off # 单元测试,禁用 Banner + +--- #################### 数据库相关配置 #################### + +spring: + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 + initial-size: 1 # 单元测试,配置为 1,提升启动速度 + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + + data: + redis: + host: 127.0.0.1 # 地址 + port: 16379 # 端口(单元测试,使用 16379 端口) + database: 0 # 数据库索引 + +mybatis: + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + +--- #################### 芋道相关配置 #################### + +yudao: + info: + base-package: cn.iocoder.yudao.module diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/resources/sql/clean.sql b/yudao-module-wms/yudao-module-wms-server/src/test/resources/sql/clean.sql new file mode 100644 index 000000000..2ca91cb4b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/resources/sql/clean.sql @@ -0,0 +1,16 @@ +DELETE FROM "wms_check_order_detail"; +DELETE FROM "wms_check_order"; +DELETE FROM "wms_movement_order_detail"; +DELETE FROM "wms_movement_order"; +DELETE FROM "wms_shipment_order_detail"; +DELETE FROM "wms_shipment_order"; +DELETE FROM "wms_receipt_order_detail"; +DELETE FROM "wms_receipt_order"; +DELETE FROM "wms_inventory_history"; +DELETE FROM "wms_inventory"; +DELETE FROM "wms_item_sku"; +DELETE FROM "wms_item"; +DELETE FROM "wms_item_category"; +DELETE FROM "wms_item_brand"; +DELETE FROM "wms_warehouse"; +DELETE FROM "wms_merchant"; diff --git a/yudao-module-wms/yudao-module-wms-server/src/test/resources/sql/create_tables.sql b/yudao-module-wms/yudao-module-wms-server/src/test/resources/sql/create_tables.sql new file mode 100644 index 000000000..811ae5e4b --- /dev/null +++ b/yudao-module-wms/yudao-module-wms-server/src/test/resources/sql/create_tables.sql @@ -0,0 +1,320 @@ +CREATE TABLE IF NOT EXISTS "wms_warehouse" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(20) NOT NULL, + "name" varchar(50) NOT NULL, + "remark" varchar(255) DEFAULT NULL, + "sort" int DEFAULT '0', + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 仓库表'; + +CREATE TABLE IF NOT EXISTS "wms_merchant" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(20) NOT NULL, + "name" varchar(60) NOT NULL, + "type" tinyint NOT NULL, + "level" varchar(10) DEFAULT NULL, + "bank_name" varchar(255) DEFAULT NULL, + "bank_account" varchar(40) DEFAULT NULL, + "address" varchar(200) DEFAULT NULL, + "mobile" varchar(13) DEFAULT NULL, + "telephone" varchar(13) DEFAULT NULL, + "contact" varchar(30) DEFAULT NULL, + "email" varchar(50) DEFAULT NULL, + "remark" varchar(255) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 往来企业表'; + +CREATE TABLE IF NOT EXISTS "wms_item_brand" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(20) NOT NULL, + "name" varchar(30) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 商品品牌表'; + +CREATE TABLE IF NOT EXISTS "wms_item_category" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "parent_id" bigint NOT NULL DEFAULT '0', + "code" varchar(20) NOT NULL, + "name" varchar(30) NOT NULL, + "sort" int DEFAULT '0', + "status" tinyint NOT NULL DEFAULT '1', + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 商品分类表'; + +CREATE TABLE IF NOT EXISTS "wms_item" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(20) DEFAULT NULL, + "name" varchar(60) NOT NULL, + "category_id" bigint NOT NULL, + "unit" varchar(20) DEFAULT NULL, + "brand_id" bigint DEFAULT NULL, + "remark" varchar(255) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 商品表'; + +CREATE TABLE IF NOT EXISTS "wms_item_sku" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(255) NOT NULL, + "item_id" bigint NOT NULL, + "bar_code" varchar(64) DEFAULT NULL, + "code" varchar(64) DEFAULT NULL, + "length" decimal(10, 1) DEFAULT NULL, + "width" decimal(10, 1) DEFAULT NULL, + "height" decimal(10, 1) DEFAULT NULL, + "gross_weight" decimal(10, 3) DEFAULT NULL, + "net_weight" decimal(10, 3) DEFAULT NULL, + "cost_price" decimal(16, 2) DEFAULT NULL, + "selling_price" decimal(16, 2) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 商品 SKU 表'; + +CREATE TABLE IF NOT EXISTS "wms_inventory" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "sku_id" bigint NOT NULL, + "warehouse_id" bigint NOT NULL, + "area_id" bigint NOT NULL DEFAULT '0', + "quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "remark" varchar(255) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id"), + CONSTRAINT "uk_sku_id_warehouse_id" UNIQUE ("sku_id", "warehouse_id") +) COMMENT 'WMS 库存表'; + +CREATE TABLE IF NOT EXISTS "wms_inventory_history" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "warehouse_id" bigint NOT NULL, + "area_id" bigint NOT NULL DEFAULT '0', + "sku_id" bigint NOT NULL, + "quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "before_quantity" decimal(20, 2) DEFAULT NULL, + "after_quantity" decimal(20, 2) DEFAULT NULL, + "batch_no" varchar(64) DEFAULT NULL, + "production_date" timestamp DEFAULT NULL, + "expiration_date" timestamp DEFAULT NULL, + "price" decimal(16, 2) DEFAULT NULL, + "total_price" decimal(16, 2) DEFAULT NULL, + "remark" varchar(255) DEFAULT NULL, + "order_id" bigint DEFAULT NULL, + "order_no" varchar(64) DEFAULT NULL, + "order_type" int DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 库存流水表'; + +CREATE TABLE IF NOT EXISTS "wms_receipt_order" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "no" varchar(64) NOT NULL, + "type" int NOT NULL, + "order_time" timestamp NOT NULL, + "status" int NOT NULL DEFAULT '0', + "biz_order_no" varchar(64) DEFAULT NULL, + "merchant_id" bigint DEFAULT NULL, + "remark" varchar(255) DEFAULT NULL, + "warehouse_id" bigint NOT NULL, + "area_id" bigint NOT NULL DEFAULT '0', + "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "total_price" decimal(16, 2) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 入库单表'; + +CREATE TABLE IF NOT EXISTS "wms_receipt_order_detail" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "order_id" bigint NOT NULL, + "sku_id" bigint NOT NULL, + "warehouse_id" bigint NOT NULL, + "area_id" bigint NOT NULL DEFAULT '0', + "batch_no" varchar(64) DEFAULT NULL, + "production_date" timestamp DEFAULT NULL, + "expiration_date" timestamp DEFAULT NULL, + "quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "price" decimal(16, 2) DEFAULT NULL, + "total_price" decimal(16, 2) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 入库单明细表'; + +CREATE TABLE IF NOT EXISTS "wms_shipment_order" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "no" varchar(64) NOT NULL, + "type" int NOT NULL, + "order_time" timestamp NOT NULL, + "status" int NOT NULL DEFAULT '0', + "biz_order_no" varchar(64) DEFAULT NULL, + "merchant_id" bigint DEFAULT NULL, + "remark" varchar(255) DEFAULT NULL, + "warehouse_id" bigint NOT NULL, + "area_id" bigint NOT NULL DEFAULT '0', + "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "total_price" decimal(16, 2) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 出库单表'; + +CREATE TABLE IF NOT EXISTS "wms_shipment_order_detail" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "order_id" bigint NOT NULL, + "sku_id" bigint NOT NULL, + "warehouse_id" bigint NOT NULL, + "area_id" bigint NOT NULL DEFAULT '0', + "inventory_detail_id" bigint DEFAULT NULL, + "batch_no" varchar(64) DEFAULT NULL, + "production_date" timestamp DEFAULT NULL, + "expiration_date" timestamp DEFAULT NULL, + "quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "price" decimal(16, 2) DEFAULT NULL, + "total_price" decimal(16, 2) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 出库单明细表'; + +CREATE TABLE IF NOT EXISTS "wms_movement_order" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "no" varchar(64) NOT NULL, + "order_time" timestamp NOT NULL, + "status" int NOT NULL DEFAULT '0', + "remark" varchar(255) DEFAULT NULL, + "source_warehouse_id" bigint NOT NULL, + "target_warehouse_id" bigint NOT NULL, + "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "total_price" decimal(16, 2) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 移库单表'; + +CREATE TABLE IF NOT EXISTS "wms_movement_order_detail" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "order_id" bigint NOT NULL, + "sku_id" bigint NOT NULL, + "source_warehouse_id" bigint NOT NULL, + "target_warehouse_id" bigint NOT NULL, + "inventory_detail_id" bigint DEFAULT NULL, + "batch_no" varchar(64) DEFAULT NULL, + "production_date" timestamp DEFAULT NULL, + "expiration_date" timestamp DEFAULT NULL, + "quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "price" decimal(16, 2) DEFAULT NULL, + "total_price" decimal(16, 2) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 移库单明细表'; + +CREATE TABLE IF NOT EXISTS "wms_check_order" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "no" varchar(64) NOT NULL, + "order_time" timestamp NOT NULL, + "status" int NOT NULL DEFAULT '0', + "remark" varchar(255) DEFAULT NULL, + "warehouse_id" bigint NOT NULL, + "area_id" bigint NOT NULL DEFAULT '0', + "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "total_price" decimal(16, 2) DEFAULT NULL, + "actual_price" decimal(16, 2) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 盘库单表'; + +CREATE TABLE IF NOT EXISTS "wms_check_order_detail" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "order_id" bigint NOT NULL, + "sku_id" bigint NOT NULL, + "warehouse_id" bigint NOT NULL, + "area_id" bigint NOT NULL DEFAULT '0', + "inventory_id" bigint DEFAULT NULL, + "inventory_detail_id" bigint DEFAULT NULL, + "batch_no" varchar(64) DEFAULT NULL, + "production_date" timestamp DEFAULT NULL, + "expiration_date" timestamp DEFAULT NULL, + "receipt_time" timestamp DEFAULT NULL, + "quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "check_quantity" decimal(20, 2) NOT NULL DEFAULT '0', + "price" decimal(16, 2) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT 'WMS 盘库单明细表'; diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index c06cab00d..f6834031e 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -117,8 +117,15 @@ - - + + + + + + + + + diff --git a/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java b/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java index f39655733..59dd1def4 100644 --- a/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java +++ b/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java @@ -47,12 +47,30 @@ public class DefaultController { "[ERP 模块 yudao-module-erp - 已禁用][参考 https://doc.iocoder.cn/erp/build/ 开启]"); } + @RequestMapping(value = { "/admin-api/wms/**"}) + public CommonResult wms404() { + return CommonResult.error(NOT_IMPLEMENTED.getCode(), + "[WMS 仓库管理系统 yudao-module-wms - 已禁用][参考 https://doc.iocoder.cn/wms/build/ 开启]"); + } + @RequestMapping("/admin-api/crm/**") public CommonResult crm404() { return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[CRM 模块 yudao-module-crm - 已禁用][参考 https://doc.iocoder.cn/crm/build/ 开启]"); } + @RequestMapping(value = { "/admin-api/mes/**"}) + public CommonResult mes404() { + return CommonResult.error(NOT_IMPLEMENTED.getCode(), + "[MES 系统 yudao-module-mes - 已禁用][参考 https://doc.iocoder.cn/mes/build/ 开启]"); + } + + @RequestMapping(value = { "/admin-api/im/**"}) + public CommonResult im404() { + return CommonResult.error(NOT_IMPLEMENTED.getCode(), + "[IM 即时通讯 yudao-module-im - 已禁用][参考 https://doc.iocoder.cn/im/build/ 开启]"); + } + @RequestMapping(value = { "/admin-api/report/**"}) public CommonResult report404() { return CommonResult.error(NOT_IMPLEMENTED.getCode(), diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 92766be20..f02cbf706 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -331,6 +331,7 @@ yudao: vo-type: 10 # VO 的类型,参见 CodegenVOTypeEnum 枚举类 delete-batch-enable: true # 是否生成批量删除接口 unit-test-enable: false # 是否生成单元测试 + import-enable: false # 是否生成 Excel 导入接口 tenant: # 多租户相关配置项 enable: true ignore-urls: