diff --git a/.image/common/erp-feature.png b/.image/common/erp-feature.png new file mode 100644 index 0000000000..d30b30eed9 Binary files /dev/null and b/.image/common/erp-feature.png differ diff --git a/README.md b/README.md index 56eeefbf22..296df47ec8 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,8 @@ * 数据报表 * 商城系统 * 微信公众号 +* ERP 系统 +* CRM 系统 > 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。 > @@ -247,23 +249,31 @@ | 🚀 | 会员分组 | 对会员进行分组,用于用户画像、内容推送等运营手段 | | 🚀 | 积分签到 | 回馈给签到、消费等行为的积分,会员可订单抵现、积分兑换等途径消耗 | +### ERP 系统 + +![功能图](/.image/common/erp-feature.png) + +演示地址: + ## 🐨 技术栈 ### 模块 -| 项目 | 说明 | -|--------------------------------------------------------------------------|--------------------| -| `yudao-dependencies` | Maven 依赖版本管理 | -| `yudao-framework` | Java 框架拓展 | -| `yudao-server` | 管理后台 + 用户 APP 的服务端 | -| `yudao-module-system` | 系统功能的 Module 模块 | -| `yudao-module-member` | 会员中心的 Module 模块 | -| `yudao-module-infra` | 基础设施的 Module 模块 | -| `yudao-module-bpm` | 工作流程的 Module 模块 | -| `yudao-module-pay` | 支付系统的 Module 模块 | -| `yudao-module-mall` | 商城系统的 Module 模块 | -| `yudao-module-mp` | 微信公众号的 Module 模块 | -| `yudao-module-report` | 大屏报表 Module 模块 | +| 项目 | 说明 | +|-----------------------|--------------------| +| `yudao-dependencies` | Maven 依赖版本管理 | +| `yudao-framework` | Java 框架拓展 | +| `yudao-server` | 管理后台 + 用户 APP 的服务端 | +| `yudao-module-system` | 系统功能的 Module 模块 | +| `yudao-module-member` | 会员中心的 Module 模块 | +| `yudao-module-infra` | 基础设施的 Module 模块 | +| `yudao-module-bpm` | 工作流程的 Module 模块 | +| `yudao-module-pay` | 支付系统的 Module 模块 | +| `yudao-module-mall` | 商城系统的 Module 模块 | +| `yudao-module-erp` | ERP 系统的 Module 模块 | +| `yudao-module-crm` | CRM 系统的 Module 模块 | +| `yudao-module-mp` | 微信公众号的 Module 模块 | +| `yudao-module-report` | 大屏报表 Module 模块 | ### 框架 diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql index 0a01aa9dcc..3306b7c720 100644 --- a/sql/mysql/ruoyi-vue-pro.sql +++ b/sql/mysql/ruoyi-vue-pro.sql @@ -11,7 +11,7 @@ Target Server Version : 80200 (8.2.0) File Encoding : 65001 - Date: 26/01/2024 20:11:43 + Date: 17/02/2024 21:00:07 */ SET NAMES utf8mb4; @@ -385,7 +385,7 @@ CREATE TABLE `infra_api_error_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 15093 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; +) ENGINE = InnoDB AUTO_INCREMENT = 15324 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; -- ---------------------------- -- Records of infra_api_error_log @@ -423,7 +423,7 @@ CREATE TABLE `infra_codegen_column` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2015 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义'; +) ENGINE = InnoDB AUTO_INCREMENT = 2248 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义'; -- ---------------------------- -- Records of infra_codegen_column @@ -461,7 +461,7 @@ CREATE TABLE `infra_codegen_table` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 156 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义'; +) ENGINE = InnoDB AUTO_INCREMENT = 171 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义'; -- ---------------------------- -- Records of infra_codegen_table @@ -690,7 +690,7 @@ CREATE TABLE `infra_file` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1242 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; +) ENGINE = InnoDB AUTO_INCREMENT = 1261 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; -- ---------------------------- -- Records of infra_file @@ -879,7 +879,7 @@ CREATE TABLE `system_dict_data` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1486 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; +) ENGINE = InnoDB AUTO_INCREMENT = 1508 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; -- ---------------------------- -- Records of system_dict_data @@ -1236,6 +1236,28 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1483, 3, '转账成功', '20', 'pay_transfer_status', 0, 'success', '', '', '1', '2023-10-28 16:23:50', '1', '2023-10-28 16:23:50', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1484, 2, '转账进行中', '10', 'pay_transfer_status', 0, 'info', '', '', '1', '2023-10-28 16:23:12', '1', '2023-10-28 16:23:12', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1485, 1, '等待转账', '0', 'pay_transfer_status', 0, 'default', '', '', '1', '2023-10-28 16:21:43', '1', '2023-10-28 16:23:22', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1486, 10, '其它入库', '10', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:07:25', '1', '2024-02-05 18:07:43', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1487, 11, '其它入库(作废)', '11', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:08:07', '1', '2024-02-05 19:20:16', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1488, 20, '其它出库', '20', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-05 18:08:51', '1', '2024-02-05 18:08:51', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1489, 21, '其它出库(作废)', '21', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-05 18:09:00', '1', '2024-02-05 19:20:10', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1490, 10, '未审核', '10', 'erp_audit_status', 0, 'default', '', '', '1', '2024-02-06 00:00:21', '1', '2024-02-06 00:00:21', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1491, 20, '已审核', '20', 'erp_audit_status', 0, 'success', '', '', '1', '2024-02-06 00:00:35', '1', '2024-02-06 00:00:35', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1492, 30, '调拨入库', '30', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:19', '1', '2024-02-07 12:36:31', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1493, 31, '调拨入库(作废)', '31', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:29', '1', '2024-02-07 20:37:11', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1494, 32, '调拨出库', '32', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-07 20:34:38', '1', '2024-02-07 12:36:33', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1495, 33, '调拨出库(作废)', '33', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-07 20:34:49', '1', '2024-02-07 20:37:06', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1496, 40, '盘盈入库', '40', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:53:00', '1', '2024-02-08 08:53:09', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1497, 41, '盘盈入库(作废)', '41', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:53:39', '1', '2024-02-16 19:40:54', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1498, 42, '盘亏出库', '42', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-08 08:54:16', '1', '2024-02-08 08:54:16', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1499, 43, '盘亏出库(作废)', '43', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-08 08:54:31', '1', '2024-02-16 19:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1500, 50, '销售出库', '50', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-11 21:47:25', '1', '2024-02-11 21:50:40', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1501, 51, '销售出库(作废)', '51', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-11 21:47:37', '1', '2024-02-11 21:51:12', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1502, 60, '销售退货入库', '60', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-12 06:51:05', '1', '2024-02-12 06:51:05', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1503, 61, '销售退货入库(作废)', '61', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-12 06:51:18', '1', '2024-02-12 06:51:18', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1504, 70, '采购入库', '70', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:02', '1', '2024-02-16 13:10:02', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1505, 71, '采购入库(作废)', '71', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:10', '1', '2024-02-16 19:40:40', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1506, 80, '采购退货出库', '80', 'erp_stock_record_biz_type', 0, '', '', '', '1', '2024-02-16 13:10:17', '1', '2024-02-16 13:10:17', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1507, 81, '采购退货出库(作废)', '81', 'erp_stock_record_biz_type', 0, 'danger', '', '', '1', '2024-02-16 13:10:26', '1', '2024-02-16 19:40:33', b'0'); COMMIT; -- ---------------------------- @@ -1256,7 +1278,7 @@ CREATE TABLE `system_dict_type` ( `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `dict_type`(`type` ASC) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 611 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; +) ENGINE = InnoDB AUTO_INCREMENT = 613 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; -- ---------------------------- -- Records of system_dict_type @@ -1343,6 +1365,8 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (608, 'CRM 跟进方式', 'crm_follow_up_type', 0, '', '1', '2024-01-15 20:48:05', '1', '2024-01-15 20:48:05', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (609, '支付转账类型', 'pay_transfer_type', 0, '', '1', '2023-10-28 16:27:18', '1', '2023-10-28 16:27:18', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (610, '转账订单状态', 'pay_transfer_status', 0, '', '1', '2023-10-28 16:18:32', '1', '2023-10-28 16:18:32', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (611, 'ERP 库存明细的业务类型', 'erp_stock_record_biz_type', 0, 'ERP 库存明细的业务类型', '1', '2024-02-05 18:07:02', '1', '2024-02-05 18:07:02', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (612, 'ERP 审批状态', 'erp_audit_status', 0, '', '1', '2024-02-06 00:00:07', '1', '2024-02-06 00:00:07', b'0', '1970-01-01 00:00:00'); COMMIT; -- ---------------------------- @@ -1391,7 +1415,7 @@ CREATE TABLE `system_login_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2861 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; +) ENGINE = InnoDB AUTO_INCREMENT = 2906 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; -- ---------------------------- -- Records of system_login_log @@ -1521,7 +1545,7 @@ CREATE TABLE `system_menu` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2560 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表'; +) ENGINE = InnoDB AUTO_INCREMENT = 2702 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表'; -- ---------------------------- -- Records of system_menu @@ -2013,44 +2037,44 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2388, '商城首页', '', 2, 1, 2362, 'home', 'ep:home-filled', 'mall/home/index', 'MallHome', 0, b'1', b'1', b'1', '', '2023-10-16 12:10:33', '', '2023-10-16 12:10:33', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2389, '核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder', 0, b'1', b'1', b'1', '', '2023-10-19 16:09:51', '', '2023-10-19 16:09:51', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2390, '优惠活动', '', 1, 99, 2030, 'youhui', 'ep:aim', '', '', 0, b'1', b'1', b'1', '1', '2023-10-21 19:23:49', '1', '2023-10-21 19:23:49', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2391, '客户管理', '', 2, 0, 2397, 'customer', 'fa:address-book-o', 'crm/customer/index', 'CrmCustomer', 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '1', '2024-01-15 21:27:42', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2391, '客户管理', '', 2, 10, 2397, 'customer', 'fa:address-book-o', 'crm/customer/index', 'CrmCustomer', 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '1', '2024-02-17 17:13:32', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2392, '客户查询', 'crm:customer:query', 3, 1, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2393, '客户创建', 'crm:customer:create', 3, 2, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2394, '客户更新', 'crm:customer:update', 3, 3, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2395, '客户删除', 'crm:customer:delete', 3, 4, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2396, '客户导出', 'crm:customer:export', 3, 5, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2397, '客户管理系统', '', 1, 200, 0, '/crm', 'ep:avatar', '', '', 0, b'1', b'1', b'1', '1', '2023-10-29 17:08:30', '1', '2023-10-29 17:08:30', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2398, '合同管理', '', 2, 1, 2397, 'contract', 'ep:notebook', 'crm/contract/index', 'CrmContract', 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '1', '2023-10-29 18:55:53', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2397, 'CRM 系统', '', 1, 200, 0, '/crm', 'ep:avatar', '', '', 0, b'1', b'1', b'1', '1', '2023-10-29 17:08:30', '1', '2024-02-04 15:37:31', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2398, '合同管理', '', 2, 50, 2397, 'contract', 'ep:notebook', 'crm/contract/index', 'CrmContract', 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '1', '2024-02-17 17:15:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2399, '合同查询', 'crm:contract:query', 3, 1, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2400, '合同创建', 'crm:contract:create', 3, 2, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2401, '合同更新', 'crm:contract:update', 3, 3, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2402, '合同删除', 'crm:contract:delete', 3, 4, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2403, '合同导出', 'crm:contract:export', 3, 5, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2404, '线索管理', '', 2, 0, 2397, 'clue', 'fa:pagelines', 'crm/clue/index', 'CrmClue', 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '1', '2023-10-29 19:08:35', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2404, '线索管理', '', 2, 8, 2397, 'clue', 'fa:pagelines', 'crm/clue/index', 'CrmClue', 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '1', '2024-02-17 17:15:41', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2405, '线索查询', 'crm:clue:query', 3, 1, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2406, '线索创建', 'crm:clue:create', 3, 2, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2407, '线索更新', 'crm:clue:update', 3, 3, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2408, '线索删除', 'crm:clue:delete', 3, 4, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2409, '线索导出', 'crm:clue:export', 3, 5, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2410, '商机管理', '', 2, 0, 2397, 'business', 'fa:bus', 'crm/business/index', 'CrmBusiness', 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '1', '2023-10-29 19:13:01', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2410, '商机管理', '', 2, 40, 2397, 'business', 'fa:bus', 'crm/business/index', 'CrmBusiness', 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '1', '2024-02-17 17:14:55', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2411, '商机查询', 'crm:business:query', 3, 1, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2412, '商机创建', 'crm:business:create', 3, 2, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2413, '商机更新', 'crm:business:update', 3, 3, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2414, '商机删除', 'crm:business:delete', 3, 4, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2415, '商机导出', 'crm:business:export', 3, 5, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2416, '联系人管理', '', 2, 0, 2397, 'contact', 'fa:address-book-o', 'crm/contact/index', 'CrmContact', 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '1', '2023-12-01 19:39:50', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2416, '联系人管理', '', 2, 20, 2397, 'contact', 'fa:address-book-o', 'crm/contact/index', 'CrmContact', 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '1', '2024-02-17 17:13:49', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2417, '联系人查询', 'crm:contact:query', 3, 1, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2418, '联系人创建', 'crm:contact:create', 3, 2, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2419, '联系人更新', 'crm:contact:update', 3, 3, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2420, '联系人删除', 'crm:contact:delete', 3, 4, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2421, '联系人导出', 'crm:contact:export', 3, 5, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2422, '回款管理', '', 2, 0, 2397, 'receivable', 'ep:money', 'crm/receivable/index', 'CrmReceivable', 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '1', '2023-10-29 19:18:52', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2422, '回款管理', '', 2, 60, 2397, 'receivable', 'ep:money', 'crm/receivable/index', 'CrmReceivable', 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:18', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2423, '回款管理查询', 'crm:receivable:query', 3, 1, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2424, '回款管理创建', 'crm:receivable:create', 3, 2, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2425, '回款管理更新', 'crm:receivable:update', 3, 3, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2426, '回款管理删除', 'crm:receivable:delete', 3, 4, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2427, '回款管理导出', 'crm:receivable:export', 3, 5, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2428, '回款计划', '', 2, 0, 2397, 'receivable-plan', 'fa:money', 'crm/receivable/plan/index', 'CrmReceivablePlan', 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '1', '2023-12-01 19:55:48', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2428, '回款计划', '', 2, 61, 2397, 'receivable-plan', 'fa:money', 'crm/receivable/plan/index', 'CrmReceivablePlan', 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '1', '2024-02-17 17:16:11', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2429, '回款计划查询', 'crm:receivable-plan:query', 3, 1, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2430, '回款计划创建', 'crm:receivable-plan:create', 3, 2, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2431, '回款计划更新', 'crm:receivable-plan:update', 3, 3, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); @@ -2103,7 +2127,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2521, '客户限制配置更新', 'crm:customer-limit-config:update', 3, 3, 2518, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2522, '客户限制配置删除', 'crm:customer-limit-config:delete', 3, 4, 2518, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2523, '客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, 2518, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-18 13:33:53', '', '2023-11-18 13:33:53', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2524, '系统配置', '', 1, 99, 2397, 'config', 'ep:connection', '', '', 0, b'1', b'1', b'1', '1', '2023-11-18 21:58:00', '1', '2023-11-18 21:58:00', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2524, '系统配置', '', 1, 999, 2397, 'config', 'ep:connection', '', '', 0, b'1', b'1', b'1', '1', '2023-11-18 21:58:00', '1', '2024-02-17 17:14:34', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2525, 'WebSocket 测试', '', 2, 7, 2, 'websocket', 'ep:connection', 'infra/webSocket/index', 'InfraWebSocket', 0, b'1', b'1', b'1', '1', '2023-11-23 19:41:55', '1', '2023-11-24 19:22:30', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2526, '产品管理', '', 2, 10, 2397, 'product', 'fa:product-hunt', 'crm/product/index', 'CrmProduct', 0, b'1', b'1', b'1', '1', '2023-12-05 22:45:26', '1', '2023-12-05 22:46:45', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2527, '产品查询', 'crm:product:query', 3, 1, 2526, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-12-05 22:47:16', '1', '2023-12-05 22:47:16', b'0'); @@ -2125,7 +2149,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2543, '关联商机', 'crm:contact:create-business', 3, 10, 2416, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-01-02 17:28:25', '1', '2024-01-02 17:28:25', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2544, '取关商机', 'crm:contact:delete-business', 3, 11, 2416, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-01-02 17:28:43', '1', '2024-01-02 17:28:51', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2545, '商品统计', '', 2, 3, 2358, 'product', 'fa:product-hunt', 'statistics/product/index', 'ProductStatistics', 0, b'1', b'1', b'1', '', '2023-12-15 18:54:28', '1', '2024-01-17 23:11:54', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2546, '客户公海', '', 2, -1, 2397, 'customer/pool', 'fa-solid:swimming-pool', 'crm/customer/pool/index', 'CrmCustomerPool', 0, b'1', b'1', b'1', '1', '2024-01-15 21:29:34', '1', '2023-12-29 19:47:32', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2546, '客户公海', '', 2, 30, 2397, 'customer/pool', 'fa-solid:swimming-pool', 'crm/customer/pool/index', 'CrmCustomerPool', 0, b'1', b'1', b'1', '1', '2024-01-15 21:29:34', '1', '2024-02-17 17:14:18', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2547, '订单查询', 'trade:order:query', 3, 1, 2076, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-01-16 08:52:00', '1', '2024-01-16 08:52:00', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2548, '订单更新', 'trade:order:update', 3, 2, 2076, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-01-16 08:52:21', '1', '2024-01-16 08:52:21', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2549, '支付&退款案例', '', 2, 1, 2161, 'order', 'fa:paypal', 'pay/demo/order/index', '', 0, b'1', b'1', b'1', '1', '2024-01-18 23:45:00', '1', '2024-01-18 23:47:21', b'0'); @@ -2139,6 +2163,148 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2557, '钱包余额', '', 2, 1, 2551, 'wallet-balance', 'fa:leaf', 'pay/wallet/balance/index', 'WalletBalance', 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2558, '钱包余额查询', 'pay:wallet:query', 3, 1, 2557, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2559, '转账订单', '', 2, 3, 1117, 'transfer', 'ep:credit-card', 'pay/transfer/index', 'PayTransfer', 0, b'1', b'1', b'1', '', '2023-12-29 02:32:54', '', '2023-12-29 02:32:54', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2560, '商业智能', '', 1, 200, 2397, 'bi', 'ep:data-line', '', '', 0, b'1', b'1', b'1', '1', '2024-01-26 22:50:35', '1', '2024-02-17 17:14:39', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2561, '排行榜', '', 2, 1, 2560, 'ranking', 'fa:area-chart', 'crm/bi/rank/index', 'BiRanking', 0, b'1', b'1', b'1', '1', '2024-01-26 22:52:09', '1', '2024-02-03 01:10:43', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2562, '客户导入', 'crm:customer:import', 3, 6, 2391, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-01 13:09:00', '1', '2024-02-01 13:09:05', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2563, 'ERP 系统', '', 1, 300, 0, '/erp', 'fa-solid:store', '', '', 0, b'1', b'1', b'1', '1', '2024-02-04 15:37:25', '1', '2024-02-04 15:37:25', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2564, '产品管理', '', 1, 40, 2563, 'product', 'fa:product-hunt', '', '', 0, b'1', b'1', b'1', '1', '2024-02-04 15:38:43', '1', '2024-02-04 15:38:43', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2565, '产品信息', '', 2, 0, 2564, 'product', 'fa-solid:apple-alt', 'erp/product/product/index', 'ErpProduct', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-05 14:42:11', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2566, '产品查询', 'erp:product:query', 3, 1, 2565, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:21:57', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2567, '产品创建', 'erp:product:create', 3, 2, 2565, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:12', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2568, '产品更新', 'erp:product:update', 3, 3, 2565, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:16', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2569, '产品删除', 'erp:product:delete', 3, 4, 2565, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:22', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2570, '产品导出', 'erp:product:export', 3, 5, 2565, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2024-02-04 17:22:26', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2571, '产品分类', '', 2, 1, 2564, 'product-category', 'fa:certificate', 'erp/product/category/index', 'ErpProductCategory', 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '1', '2024-02-04 17:24:58', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2572, '分类查询', 'erp:product-category:query', 3, 1, 2571, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2573, '分类创建', 'erp:product-category:create', 3, 2, 2571, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2574, '分类更新', 'erp:product-category:update', 3, 3, 2571, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2575, '分类删除', 'erp:product-category:delete', 3, 4, 2571, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2576, '分类导出', 'erp:product-category:export', 3, 5, 2571, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 09:21:04', '', '2024-02-04 09:21:04', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2577, '产品单位', '', 2, 2, 2564, 'unit', 'ep:opportunity', 'erp/product/unit/index', 'ErpProductUnit', 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '1', '2024-02-04 19:54:37', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2578, '单位查询', 'erp:product-unit:query', 3, 1, 2577, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2579, '单位创建', 'erp:product-unit:create', 3, 2, 2577, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2580, '单位更新', 'erp:product-unit:update', 3, 3, 2577, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2581, '单位删除', 'erp:product-unit:delete', 3, 4, 2577, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2582, '单位导出', 'erp:product-unit:export', 3, 5, 2577, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 11:54:08', '', '2024-02-04 11:54:08', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2583, '库存管理', '', 1, 30, 2563, 'stock', 'fa:window-restore', '', '', 0, b'1', b'1', b'1', '1', '2024-02-05 00:29:37', '1', '2024-02-05 00:29:37', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2584, '仓库信息', '', 2, 0, 2583, 'warehouse', 'ep:house', 'erp/stock/warehouse/index', 'ErpWarehouse', 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '1', '2024-02-05 01:12:53', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2585, '仓库查询', 'erp:warehouse:query', 3, 1, 2584, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2586, '仓库创建', 'erp:warehouse:create', 3, 2, 2584, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2587, '仓库更新', 'erp:warehouse:update', 3, 3, 2584, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2588, '仓库删除', 'erp:warehouse:delete', 3, 4, 2584, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2589, '仓库导出', 'erp:warehouse:export', 3, 5, 2584, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-04 17:12:09', '', '2024-02-04 17:12:09', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2590, '产品库存', '', 2, 1, 2583, 'stock', 'ep:coffee', 'erp/stock/stock/index', 'ErpStock', 0, b'1', b'1', b'1', '', '2024-02-05 06:40:50', '1', '2024-02-05 14:42:44', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2591, '库存查询', 'erp:stock:query', 3, 1, 2590, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2592, '库存导出', 'erp:stock:export', 3, 5, 2590, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 06:40:50', '', '2024-02-05 06:40:50', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2593, '出入库明细', '', 2, 2, 2583, 'record', 'fa-solid:blog', 'erp/stock/record/index', 'ErpStockRecord', 0, b'1', b'1', b'1', '', '2024-02-05 10:27:21', '1', '2024-02-06 17:26:11', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2594, '库存明细查询', 'erp:stock-record:query', 3, 1, 2593, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2595, '库存明细导出', 'erp:stock-record:export', 3, 5, 2593, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 10:27:21', '', '2024-02-05 10:27:21', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2596, '其它入库', '', 2, 3, 2583, 'in', 'ep:zoom-in', 'erp/stock/in/index', 'ErpStockIn', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:51', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2597, '其它入库单查询', 'erp:stock-in:query', 3, 1, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2598, '其它入库单创建', 'erp:stock-in:create', 3, 2, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2599, '其它入库单更新', 'erp:stock-in:update', 3, 3, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2600, '其它入库单删除', 'erp:stock-in:delete', 3, 4, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2601, '其它入库单导出', 'erp:stock-in:export', 3, 5, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2602, '采购管理', '', 1, 10, 2563, 'purchase', 'fa:buysellads', '', '', 0, b'1', b'1', b'1', '1', '2024-02-06 16:01:01', '1', '2024-02-06 16:01:23', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2603, '供应商信息', '', 2, 4, 2602, 'supplier', 'fa:superpowers', 'erp/purchase/supplier/index', 'ErpSupplier', 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '1', '2024-02-06 16:22:25', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2604, '供应商查询', 'erp:supplier:query', 3, 1, 2603, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2605, '供应商创建', 'erp:supplier:create', 3, 2, 2603, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2606, '供应商更新', 'erp:supplier:update', 3, 3, 2603, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2607, '供应商删除', 'erp:supplier:delete', 3, 4, 2603, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2608, '供应商导出', 'erp:supplier:export', 3, 5, 2603, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-06 08:21:55', '', '2024-02-06 08:21:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2609, '其它入库单审批', 'erp:stock-in:update-status', 3, 6, 2596, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-05 16:08:56', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2610, '其它出库', '', 2, 4, 2583, 'out', 'ep:zoom-out', 'erp/stock/out/index', 'ErpStockOut', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-07 19:06:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2611, '其它出库单查询', 'erp:stock-out:query', 3, 1, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:39', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2612, '其它出库单创建', 'erp:stock-out:create', 3, 2, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:42', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2613, '其它出库单更新', 'erp:stock-out:update', 3, 3, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:44', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2614, '其它出库单删除', 'erp:stock-out:delete', 3, 4, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:56', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2615, '其它出库单导出', 'erp:stock-out:export', 3, 5, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:57', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2616, '其它出库单审批', 'erp:stock-out:update-status', 3, 6, 2610, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 06:43:58', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2617, '销售管理', '', 1, 20, 2563, 'sale', 'fa:sellsy', '', '', 0, b'1', b'1', b'1', '1', '2024-02-07 15:12:32', '1', '2024-02-07 15:12:32', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2618, '客户信息', '', 2, 4, 2617, 'customer', 'ep:avatar', 'erp/sale/customer/index', 'ErpCustomer', 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '1', '2024-02-07 15:22:25', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2619, '客户查询', 'erp:customer:query', 3, 1, 2618, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2620, '客户创建', 'erp:customer:create', 3, 2, 2618, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2621, '客户更新', 'erp:customer:update', 3, 3, 2618, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2622, '客户删除', 'erp:customer:delete', 3, 4, 2618, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2623, '客户导出', 'erp:customer:export', 3, 5, 2618, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-07 07:21:45', '', '2024-02-07 07:21:45', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2624, '库存调拨', '', 2, 5, 2583, 'move', 'ep:folder-remove', 'erp/stock/move/index', 'ErpStockMove', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-16 18:53:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2625, '库存调度单查询', 'erp:stock-move:query', 3, 1, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2626, '库存调度单创建', 'erp:stock-move:create', 3, 2, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2627, '库存调度单更新', 'erp:stock-move:update', 3, 3, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2628, '库存调度单删除', 'erp:stock-move:delete', 3, 4, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2629, '库存调度单导出', 'erp:stock-move:export', 3, 5, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2630, '库存调度单审批', 'erp:stock-move:update-status', 3, 6, 2624, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2631, '库存盘点', '', 2, 6, 2583, 'check', 'ep:circle-check-filled', 'erp/stock/check/index', 'ErpStockCheck', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-08 08:31:09', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2632, '库存盘点单查询', 'erp:stock-check:query', 3, 1, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2633, '库存盘点单创建', 'erp:stock-check:create', 3, 2, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2634, '库存盘点单更新', 'erp:stock-check:update', 3, 3, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2635, '库存盘点单删除', 'erp:stock-check:delete', 3, 4, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2636, '库存盘点单导出', 'erp:stock-check:export', 3, 5, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2637, '库存盘点单审批', 'erp:stock-check:update-status', 3, 6, 2631, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2638, '销售订单', '', 2, 1, 2617, 'order', 'fa:first-order', 'erp/sale/order/index', 'ErpSaleOrder', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-10 21:59:20', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2639, '销售订单查询', 'erp:sale-order:query', 3, 1, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2640, '销售订单创建', 'erp:sale-order:create', 3, 2, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2641, '销售订单更新', 'erp:sale-order:update', 3, 3, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2642, '销售订单删除', 'erp:sale-order:delete', 3, 4, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2643, '销售订单导出', 'erp:sale-order:export', 3, 5, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2644, '销售订单审批', 'erp:sale-order:update-status', 3, 6, 2638, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2645, '财务管理', '', 1, 50, 2563, 'finance', 'ep:money', '', '', 0, b'1', b'1', b'1', '1', '2024-02-10 08:05:58', '1', '2024-02-10 08:06:07', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2646, '结算账户', '', 2, 10, 2645, 'account', 'fa:universal-access', 'erp/finance/account/index', 'ErpAccount', 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '1', '2024-02-14 08:24:31', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2647, '结算账户查询', 'erp:account:query', 3, 1, 2646, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2648, '结算账户创建', 'erp:account:create', 3, 2, 2646, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2649, '结算账户更新', 'erp:account:update', 3, 3, 2646, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2650, '结算账户删除', 'erp:account:delete', 3, 4, 2646, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2651, '结算账户导出', 'erp:account:export', 3, 5, 2646, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-10 00:15:07', '', '2024-02-10 00:15:07', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2652, '销售出库', '', 2, 2, 2617, 'out', 'ep:sold-out', 'erp/sale/out/index', 'ErpSaleOut', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-10 22:02:07', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2653, '销售出库查询', 'erp:sale-out:query', 3, 1, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2654, '销售出库创建', 'erp:sale-out:create', 3, 2, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2655, '销售出库更新', 'erp:sale-out:update', 3, 3, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2656, '销售出库删除', 'erp:sale-out:delete', 3, 4, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2657, '销售出库导出', 'erp:sale-out:export', 3, 5, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2658, '销售出库审批', 'erp:sale-out:update-status', 3, 6, 2652, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2659, '销售退货', '', 2, 3, 2617, 'return', 'fa-solid:bone', 'erp/sale/return/index', 'ErpSaleReturn', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-12 06:12:58', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2660, '销售退货查询', 'erp:sale-return:query', 3, 1, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:49', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2661, '销售退货创建', 'erp:sale-return:create', 3, 2, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:52', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2662, '销售退货更新', 'erp:sale-return:update', 3, 3, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:55', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2663, '销售退货删除', 'erp:sale-return:delete', 3, 4, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:57', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2664, '销售退货导出', 'erp:sale-return:export', 3, 5, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:12:59', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2665, '销售退货审批', 'erp:sale-return:update-status', 3, 6, 2659, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-07 11:13:03', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2666, '采购订单', '', 2, 1, 2602, 'order', 'fa-solid:border-all', 'erp/purchase/order/index', 'ErpPurchaseOrder', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-12 08:51:49', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2667, '采购订单查询', 'erp:purchase-order:query', 3, 1, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2668, '采购订单创建', 'erp:purchase-order:create', 3, 2, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2669, '采购订单更新', 'erp:purchase-order:update', 3, 3, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2670, '采购订单删除', 'erp:purchase-order:delete', 3, 4, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2671, '采购订单导出', 'erp:purchase-order:export', 3, 5, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2672, '采购订单审批', 'erp:purchase-order:update-status', 3, 6, 2666, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2673, '采购入库', '', 2, 2, 2602, 'in', 'fa-solid:gopuram', 'erp/purchase/in/index', 'ErpPurchaseIn', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-12 11:19:27', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2674, '采购入库查询', 'erp:purchase-in:query', 3, 1, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2675, '采购入库创建', 'erp:purchase-in:create', 3, 2, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2676, '采购入库更新', 'erp:purchase-in:update', 3, 3, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2677, '采购入库删除', 'erp:purchase-in:delete', 3, 4, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2678, '采购入库导出', 'erp:purchase-in:export', 3, 5, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2679, '采购入库审批', 'erp:purchase-in:update-status', 3, 6, 2673, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2680, '采购退货', '', 2, 3, 2602, 'return', 'ep:minus', 'erp/purchase/return/index', 'ErpPurchaseReturn', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-12 20:51:02', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2681, '采购退货查询', 'erp:purchase-return:query', 3, 1, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2682, '采购退货创建', 'erp:purchase-return:create', 3, 2, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2683, '采购退货更新', 'erp:purchase-return:update', 3, 3, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2684, '采购退货删除', 'erp:purchase-return:delete', 3, 4, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2685, '采购退货导出', 'erp:purchase-return:export', 3, 5, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2686, '采购退货审批', 'erp:purchase-return:update-status', 3, 6, 2680, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2687, '付款单', '', 2, 1, 2645, 'payment', 'ep:caret-right', 'erp/finance/payment/index', 'ErpFinancePayment', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-14 08:24:23', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2688, '付款单查询', 'erp:finance-payment:query', 3, 1, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2689, '付款单创建', 'erp:finance-payment:create', 3, 2, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2690, '付款单更新', 'erp:finance-payment:update', 3, 3, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2691, '付款单删除', 'erp:finance-payment:delete', 3, 4, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2692, '付款单导出', 'erp:finance-payment:export', 3, 5, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2693, '付款单审批', 'erp:finance-payment:update-status', 3, 6, 2687, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2694, '收款单', '', 2, 2, 2645, 'receipt', 'ep:expand', 'erp/finance/receipt/index', 'ErpFinanceReceipt', 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '1', '2024-02-15 19:35:45', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2695, '收款单查询', 'erp:finance-receipt:query', 3, 1, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:17', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2696, '收款单创建', 'erp:finance-receipt:create', 3, 2, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:54', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2697, '收款单更新', 'erp:finance-receipt:update', 3, 3, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:44:58', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2698, '收款单删除', 'erp:finance-receipt:delete', 3, 4, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:00', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2699, '收款单导出', 'erp:finance-receipt:export', 3, 5, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:05', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2700, '收款单审批', 'erp:finance-receipt:update-status', 3, 6, 2694, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-02-05 16:08:56', '', '2024-02-12 00:45:08', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2701, '待办事项', '', 2, 0, 2397, 'backlog', 'fa-solid:tasks', 'crm/backlog/index', 'CrmBacklog', 0, b'1', b'1', b'1', '1', '2024-02-17 17:17:11', '1', '2024-02-17 17:17:11', b'0'); COMMIT; -- ---------------------------- @@ -2259,7 +2425,7 @@ CREATE TABLE `system_oauth2_access_token` ( PRIMARY KEY (`id`) USING BTREE, INDEX `idx_access_token`(`access_token` ASC) USING BTREE, INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 4335 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; +) ENGINE = InnoDB AUTO_INCREMENT = 4781 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; -- ---------------------------- -- Records of system_oauth2_access_token @@ -2381,7 +2547,7 @@ CREATE TABLE `system_oauth2_refresh_token` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1309 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; +) ENGINE = InnoDB AUTO_INCREMENT = 1346 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; -- ---------------------------- -- Records of system_oauth2_refresh_token @@ -2421,7 +2587,7 @@ CREATE TABLE `system_operate_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 9706 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录'; +) ENGINE = InnoDB AUTO_INCREMENT = 10187 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录'; -- ---------------------------- -- Records of system_operate_log @@ -2454,7 +2620,7 @@ CREATE TABLE `system_operate_log_v2` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 8888 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本'; +) ENGINE = InnoDB AUTO_INCREMENT = 8892 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本'; -- ---------------------------- -- Records of system_operate_log_v2 @@ -4517,7 +4683,7 @@ CREATE TABLE `system_sms_log` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 586 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; +) ENGINE = InnoDB AUTO_INCREMENT = 588 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; -- ---------------------------- -- Records of system_sms_log @@ -4826,7 +4992,7 @@ CREATE TABLE `system_users` ( -- Records of system_users -- ---------------------------- BEGIN; -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '18818260277', 2, 'http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png', 0, '0:0:0:0:0:0:0:1', '2024-01-22 21:01:28', 'admin', '2021-01-05 17:03:47', NULL, '2024-01-22 21:01:28', b'0', 1); +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '18818260277', 2, 'http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png', 0, '0:0:0:0:0:0:0:1', '2024-02-17 17:10:34', 'admin', '2021-01-05 17:03:47', NULL, '2024-02-17 17:10:34', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '127.0.0.1', '2022-07-08 01:26:27', '', '2021-01-13 23:50:35', NULL, '2022-07-08 01:26:27', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$10$GP8zvqHB//TekuzYZSBYAuBQJiNq1.fxQVDYJ.uBCOnWCtDVKE4H6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2023-09-24 18:21:19', '', '2021-01-21 02:13:53', NULL, '2023-09-24 18:21:19', b'0', 1); diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java index 35ebc8ff38..8ffd21cccf 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java @@ -288,11 +288,16 @@ public class CollectionUtils { public static > V getSumValue(List from, Function valueFunc, BinaryOperator accumulator) { + return getSumValue(from, valueFunc, accumulator, null); + } + + public static > V getSumValue(Collection from, Function valueFunc, + BinaryOperator accumulator, V defaultValue) { if (CollUtil.isEmpty(from)) { - return null; + return defaultValue; } - assert from.size() > 0; // 断言,避免告警 - return from.stream().map(valueFunc).reduce(accumulator).get(); + assert !from.isEmpty(); // 断言,避免告警 + return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue); } public static void addIfNotNull(Collection coll, T item) { @@ -302,8 +307,12 @@ public class CollectionUtils { coll.add(item); } - public static Collection singleton(T deptId) { - return deptId == null ? Collections.emptyList() : Collections.singleton(deptId); + public static Collection singleton(T obj) { + return obj == null ? Collections.emptyList() : Collections.singleton(obj); + } + + public static List newArrayList(List> list) { + return list.stream().flatMap(Collection::stream).collect(Collectors.toList()); } } diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java index e0b7399207..90888d7d33 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java @@ -13,6 +13,16 @@ import java.math.RoundingMode; */ public class MoneyUtils { + /** + * 金额的小数位数 + */ + private static final int PRICE_SCALE = 2; + + /** + * 百分比对应的 BigDecimal 对象 + */ + public static final BigDecimal PERCENT_100 = BigDecimal.valueOf(100); + /** * 计算百分比金额,四舍五入 * @@ -35,6 +45,22 @@ public class MoneyUtils { return calculateRatePrice(price, rate, 0, RoundingMode.FLOOR).intValue(); } + /** + * 计算百分比金额 + * + * @param price 金额(单位分) + * @param count 数量 + * @param percent 折扣(单位分),列如 60.2%,则传入 6020 + * @return 商品总价 + */ + public static Integer calculator(Integer price, Integer count, Integer percent) { + price = price * count; + if (percent == null) { + return price; + } + return MoneyUtils.calculateRatePriceFloor(price, (double) (percent / 100)); + } + /** * 计算百分比金额 * @@ -70,4 +96,36 @@ public class MoneyUtils { return new Money(0, fen).toString(); } + /** + * 金额相乘,默认进行四舍五入 + * + * 位数:{@link #PRICE_SCALE} + * + * @param price 金额 + * @param count 数量 + * @return 金额相乘结果 + */ + public static BigDecimal priceMultiply(BigDecimal price, BigDecimal count) { + if (price == null || count == null) { + return null; + } + return price.multiply(count).setScale(PRICE_SCALE, RoundingMode.HALF_UP); + } + + /** + * 金额相乘(百分比),默认进行四舍五入 + * + * 位数:{@link #PRICE_SCALE} + * + * @param price 金额 + * @param percent 百分比 + * @return 金额相乘结果 + */ + public static BigDecimal priceMultiplyPercent(BigDecimal price, BigDecimal percent) { + if (price == null || percent == null) { + return null; + } + return price.multiply(percent).divide(PERCENT_100, PRICE_SCALE, RoundingMode.HALF_UP); + } + } diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java index 55ab367a30..ea131e86e4 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java @@ -1,7 +1,10 @@ package cn.iocoder.yudao.framework.common.util.number; +import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; +import java.math.BigDecimal; + /** * 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能 * @@ -37,4 +40,21 @@ public class NumberUtils { return distance; } + /** + * 提供精确的乘法运算 + * + * 和 hutool {@link NumberUtil#mul(BigDecimal...)} 的差别是,如果存在 null,则返回 null + * + * @param values 多个被乘值 + * @return 积 + */ + public static BigDecimal mul(BigDecimal... values) { + for (BigDecimal value : values) { + if (value == null) { + return null; + } + } + return NumberUtil.mul(values); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java index fb576d5084..2944ca72cc 100644 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java +++ b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/FileClient.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.framework.file.core.client; +import cn.iocoder.yudao.framework.file.core.client.s3.FilePresignedUrlRespDTO; + /** * 文件客户端 * @@ -18,11 +20,11 @@ public interface FileClient { * 上传文件 * * @param content 文件流 - * @param path 相对路径 + * @param path 相对路径 * @return 完整路径,即 HTTP 访问地址 * @throws Exception 上传文件时,抛出 Exception 异常 */ - String upload(byte[] content, String path, String type) throws Exception; + String upload(byte[] content, String path, String type) throws Exception; /** * 删除文件 @@ -40,4 +42,14 @@ public interface FileClient { */ byte[] getContent(String path) throws Exception; + /** + * 获得文件预签名地址 + * + * @param path 相对路径 + * @return 文件预签名地址 + */ + default FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception { + throw new UnsupportedOperationException("不支持的操作"); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/FilePresignedUrlRespDTO.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/FilePresignedUrlRespDTO.java new file mode 100644 index 0000000000..6048494ed7 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/FilePresignedUrlRespDTO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.framework.file.core.client.s3; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 文件预签名地址 Response DTO + * + * @author owen + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class FilePresignedUrlRespDTO { + + /** + * 文件上传 URL(用于上传) + * + * 例如说: + */ + private String uploadUrl; + + /** + * 文件 URL(用于读取、下载等) + */ + private String url; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java index 49238f8f9c..e7b470badc 100644 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java +++ b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java @@ -5,8 +5,10 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.file.core.client.AbstractFileClient; import io.minio.*; +import io.minio.http.Method; import java.io.ByteArrayInputStream; +import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_ALIYUN; import static cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig.ENDPOINT_TENCENT; @@ -117,4 +119,16 @@ public class S3FileClient extends AbstractFileClient { return IoUtil.readBytes(response); } + @Override + public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception { + String uploadUrl = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() + .method(Method.PUT) + .bucket(config.getBucket()) + .object(path) + .expiry(10, TimeUnit.MINUTES) // 过期时间(秒数)取值范围:1 秒 ~ 7 天 + .build() + ); + return new FilePresignedUrlRespDTO(uploadUrl, config.getDomain() + "/" + path); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/enums/BpmnModelConstants.java b/yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/enums/BpmnModelConstants.java new file mode 100644 index 0000000000..c3cce4272c --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/enums/BpmnModelConstants.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.framework.flowable.core.enums; + +/** + * 流程常量信息 + */ +public interface BpmnModelConstants { + + String BPMN_FILE_SUFFIX = ".bpmn"; + + /** + * BPMN 中的命名空间 + * + * 这个东西有可能导致无法切换工作流程的实现 + */ + String NAMESPACE = "http://flowable.org/bpmn"; + + /** + * 自定义属性 dataType + */ + String PROCESS_CUSTOM_DATA_TYPE = "dataType"; + +} 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 19aaf046cf..028ac8d71f 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 @@ -46,6 +46,10 @@ public class BannerApplicationRunner implements ApplicationRunner { if (isNotPresent("cn.iocoder.yudao.module.trade.framework.web.config.TradeWebConfiguration")) { System.out.println("[商城系统 yudao-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]"); } + // ERP 系统 + if (isNotPresent("cn.iocoder.yudao.module.erp.framework.web.config.ErpWebConfiguration")) { + System.out.println("[ERP 系统 yudao-module-erp - 已禁用][参考 https://doc.iocoder.cn/erp/build/ 开启]"); + } // 支付平台 if (isNotPresent("cn.iocoder.yudao.module.pay.framework.pay.config.PayConfiguration")) { System.out.println("[支付系统 yudao-module-pay - 已禁用][参考 https://doc.iocoder.cn/pay/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 303b645a3d..c494bfefb3 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 @@ -313,7 +313,13 @@ public class GlobalExceptionHandler { return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[商城系统 yudao-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]"); } - // 5. 支付平台 + // 5. ERP 系统 + if (message.contains("erp_")) { + log.error("[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://doc.iocoder.cn/erp/build/ 开启]"); + return CommonResult.error(NOT_IMPLEMENTED.getCode(), + "[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://doc.iocoder.cn/erp/build/ 开启]"); + } + // 6. 支付平台 if (message.contains("pay_")) { log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/BpmResultListenerApi.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/BpmResultListenerApi.java new file mode 100644 index 0000000000..d519ed9edf --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/BpmResultListenerApi.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.bpm.api.listener; + +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; + +// TODO @芋艿:后续改成支持 RPC +/** + * 业务流程实例的结果发生变化的监听器 Api + * + * @author HUIHUI + */ +public interface BpmResultListenerApi { + + /** + * 监听的流程定义 Key + * + * @return 返回监听的流程定义 Key + */ + String getProcessDefinitionKey(); + + /** + * 处理事件 + * + * @param event 事件 + */ + void onEvent(BpmResultListenerRespDTO event); + +} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/dto/BpmResultListenerRespDTO.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/dto/BpmResultListenerRespDTO.java new file mode 100644 index 0000000000..30721785d4 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/dto/BpmResultListenerRespDTO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.bpm.api.listener.dto; + +import lombok.Data; + +// TODO @芋艿:后续改成支持 RPC +/** + * 业务流程实例的结果 Response DTO + * + * @author HUIHUI + */ +@Data +public class BpmResultListenerRespDTO { + + /** + * 流程实例的编号 + */ + private String id; + /** + * 流程实例的 key + */ + private String processDefinitionKey; + /** + * 流程实例的结果 + */ + private Integer result; + /** + * 流程实例对应的业务标识 + * 例如说,请假 + */ + private String businessKey; + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java index 9156c42557..4e7195ccdd 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java @@ -6,16 +6,15 @@ import cn.iocoder.yudao.framework.common.util.io.IoUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*; import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert; import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; 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 javax.annotation.Resource; -import javax.validation.Valid; - import java.io.IOException; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 5478641d0d..98b2c15f93 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; -import cn.iocoder.yudao.module.bpm.service.cc.BpmProcessInstanceCopyService; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -27,9 +26,6 @@ public class BpmProcessInstanceController { @Resource private BpmProcessInstanceService processInstanceService; - @Resource - private BpmProcessInstanceCopyService processInstanceCopyService; - @GetMapping("/my-page") @Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用") @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") @@ -61,21 +57,4 @@ public class BpmProcessInstanceController { return success(true); } - // TODO @kyle:抄送要不单独 controller? - - @PostMapping("/cc/create") - @Operation(summary = "抄送流程") - @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:create')") - public CommonResult createProcessInstanceCC(@Valid @RequestBody BpmProcessInstanceCCReqVO createReqVO) { - return success(processInstanceCopyService.ccProcessInstance(getLoginUserId(), createReqVO)); - } - - @GetMapping("/cc/my-page") - @Operation(summary = "获得抄送流程分页列表") - @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:query')") - public CommonResult> getProcessInstanceCCPage( - @Valid BpmProcessInstanceCCMyPageReqVO pageReqVO) { - return success(processInstanceCopyService.getMyProcessInstanceCCPage(getLoginUserId(), pageReqVO)); - } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java new file mode 100644 index 0000000000..e1ad107ab0 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.bpm.controller.admin.task; + +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.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyCreateReqVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyMyPageReqVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageItemRespVO; +import cn.iocoder.yudao.module.bpm.convert.cc.BpmProcessInstanceCopyConvert; +import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; +import cn.iocoder.yudao.module.bpm.service.task.cc.BpmProcessInstanceCopyService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.*; + +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.convertListByFlatMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 流程实例抄送") +@RestController +@RequestMapping("/bpm/process-instance/cc") +@Validated +public class BpmProcessInstanceCopyController { + + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + @Resource + private BpmProcessInstanceService bpmProcessInstanceService; + + @Resource + private AdminUserApi adminUserApi; + + @Resource + private BpmTaskService bpmTaskService; + + @PostMapping("/create") + @Operation(summary = "抄送流程") + @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:create')") + public CommonResult createProcessInstanceCopy(@Valid @RequestBody BpmProcessInstanceCopyCreateReqVO createReqVO) { + processInstanceCopyService.createProcessInstanceCopy(getLoginUserId(), createReqVO); + return success(true); + } + + @GetMapping("/my-page") + @Operation(summary = "获得抄送流程分页列表") + @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:query')") + public CommonResult> getProcessInstanceCopyPage( + @Valid BpmProcessInstanceCopyMyPageReqVO pageReqVO) { + PageResult pageResult = processInstanceCopyService.getMyProcessInstanceCopyPage(getLoginUserId(), pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(new PageResult<>(pageResult.getTotal())); + } + + // 拼接返回 + Map taskNameMap = bpmTaskService.getTaskNameByTaskIds( + convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getTaskId)); + Map processNameMap = bpmProcessInstanceService.getProcessInstanceNameMap( + convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId)); + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(), + copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator())))); + return success(BpmProcessInstanceCopyConvert.INSTANCE.convertPage(pageResult, taskNameMap, processNameMap, userMap)); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCReqVO.java deleted file mode 100644 index 34c781b1cc..0000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCReqVO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; - -import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -// TODO @kyle:这个 VO 可以改成 BpmProcessInstanceCopyCreateReqVO -@Schema(description = "管理后台 - 流程实例的抄送 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class BpmProcessInstanceCCReqVO extends BpmTaskCandidateRuleVO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务编号不能为空") - private String taskKey; - - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务名称不能为空") - private String taskName; - - @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "流程实例的编号不能为空") - private String processInstanceKey; - - @Schema(description = "发起流程的用户的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "发起流程的用户的编号不能为空") - private Long startUserId; - - @Schema(description = "任务实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务实例名称不能为空") - private String processInstanceName; - - @Schema(description = "抄送原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "请帮忙审查下!") - @NotBlank(message = "抄送原因不能为空") - private String reason; - - // TODO @kyle:看了下字段有点多,尽量不传递可推导的字段; - // 需要传递:taskId(任务编号)、reason、userIds(被抄送的人) - // 不需要传递:taskKey、taskName、processInstanceKey、startUserId、processInstanceName 因为这些可以后端查询到 - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyCreateReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyCreateReqVO.java new file mode 100644 index 0000000000..3a1b1d45c2 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyCreateReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 流程实例抄送的创建 Request VO") +@Data +public class BpmProcessInstanceCopyCreateReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String taskId; + + @Schema(description = "抄送原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "请帮忙审查下!") + @NotBlank(message = "抄送原因不能为空") + private String reason; + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCMyPageReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyMyPageReqVO.java similarity index 81% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCMyPageReqVO.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyMyPageReqVO.java index 928ea8d975..7b4effaddf 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCMyPageReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyMyPageReqVO.java @@ -11,12 +11,11 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -// TODO @kyle:建议改成 BpmProcessInstanceCopyMyPageReqVO;cc 缩写不容易理解,所以改成 copy,虽然会长一点,但是可读性更重要; @Schema(description = "管理后台 - 流程实例抄送的分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class BpmProcessInstanceCCMyPageReqVO extends PageParam { +public class BpmProcessInstanceCopyMyPageReqVO extends PageParam { @Schema(description = "流程名称", example = "芋道") private String processInstanceName; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCPageItemRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageItemRespVO.java similarity index 77% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCPageItemRespVO.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageItemRespVO.java index 176350c248..4b149a65e4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCCPageItemRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageItemRespVO.java @@ -7,35 +7,23 @@ import java.time.LocalDateTime; @Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO") @Data -public class BpmProcessInstanceCCPageItemRespVO { +public class BpmProcessInstanceCopyPageItemRespVO { - // TODO @kyle:如果已经写了 swagger 注解,可以不用写 java 注释哈; - /** - * 编号 - */ @Schema(description = "抄送主键") private Long id; - /** - * 发起人Id - */ - @Schema(description = "发起人Id") + @Schema(description = "发起人 ID") private Long startUserId; @Schema(description = "发起人别名") private String startUserNickname; - /** - * 流程主键 - */ @Schema(description = "流程实例的主键") private String processInstanceId; @Schema(description = "流程实例的名称") private String processInstanceName; - /** - * 任务主键 - */ + @Schema(description = "发起抄送的任务编号") private String taskId; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/cc/BpmProcessInstanceCopyConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/cc/BpmProcessInstanceCopyConvert.java index 3be0c131fd..f482c71e6f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/cc/BpmProcessInstanceCopyConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/cc/BpmProcessInstanceCopyConvert.java @@ -2,18 +2,18 @@ package cn.iocoder.yudao.module.bpm.convert.cc; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCPageItemRespVO; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageItemRespVO; import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO; -import cn.iocoder.yudao.module.bpm.service.cc.BpmProcessInstanceCopyVO; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; import java.util.List; import java.util.Map; -// TODO kyle:类注释不太对 /** - * 动态表单 Convert + * 流程抄送 Convert * * @author 芋艿 */ @@ -22,39 +22,18 @@ public interface BpmProcessInstanceCopyConvert { BpmProcessInstanceCopyConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceCopyConvert.class); - // TODO @kyle:可以使用 BeanUtils copy 替代这些简单的哈; - BpmProcessInstanceCopyDO copy(BpmProcessInstanceCopyDO bean); - - BpmProcessInstanceCopyVO convert(BpmProcessInstanceCopyDO bean); - - List convertList(List list); - - // TODO @kyle:/* taskId */ 这种注释一般不用写,可以一眼看明白的;避免变量看着略微不清晰哈 - default PageResult convertPage(PageResult page - , Map taskMap - , Map processInstaneMap - , Map userMap - ) { - List list = convertList(page.getList()); - for (BpmProcessInstanceCCPageItemRespVO vo : list) { - MapUtils.findAndThen(userMap, Long.valueOf(vo.getCreator()), - vo::setCreatorNickname); - MapUtils.findAndThen(userMap, vo.getStartUserId(), - vo::setStartUserNickname); - MapUtils.findAndThen(taskMap, vo.getTaskId(), - vo::setTaskName); - MapUtils.findAndThen(processInstaneMap, vo.getProcessInstanceId(), - vo::setProcessInstanceName); - } - // TODO @kyle:可以精简成下面的哈; -// List list2 = BeanUtils.toBean(page.getList(), -// BpmProcessInstanceCCPageItemRespVO.class, -// copy -> { -// MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()), copy::setCreatorNickname); -// MapUtils.findAndThen(userMap, copy.getStartUserId(), copy::setStartUserNickname); -// MapUtils.findAndThen(taskMap, copy.getTaskId(), copy::setTaskName); -// MapUtils.findAndThen(processInstaneMap, copy.getProcessInstanceId(), copy::setProcessInstanceName); -// }); + default PageResult convertPage(PageResult page, + Map taskNameMap, + Map processInstaneNameMap, + Map userMap) { + List list = BeanUtils.toBean(page.getList(), + BpmProcessInstanceCopyPageItemRespVO.class, + copy -> { + MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()), user -> user.setNickname(user.getNickname())); + MapUtils.findAndThen(userMap, copy.getStartUserId(), user -> copy.setStartUserNickname(user.getNickname())); + MapUtils.findAndThen(taskNameMap, copy.getTaskId(), copy::setTaskName); + MapUtils.findAndThen(processInstaneNameMap, copy.getProcessInstanceId(), copy::setProcessInstanceName); + }); return new PageResult<>(list, page.getTotal()); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/cc/BpmProcessInstanceCopyDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/cc/BpmProcessInstanceCopyDO.java index 3bd2874094..7ca65f3db2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/cc/BpmProcessInstanceCopyDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/cc/BpmProcessInstanceCopyDO.java @@ -9,7 +9,7 @@ import lombok.*; * 流程抄送 DO * * @author kyle - * @date 2022-05-19 TODO @kyle:@date 不是标准 java doc,可以使用 @since 替代,然后日期是不是不对 + * @since 2024-01-22 */ @TableName(value = "bpm_process_instance_copy", autoResultMap = true) @Data @@ -26,13 +26,16 @@ public class BpmProcessInstanceCopyDO extends BaseDO { @TableId private Long id; - // TODO @kyle:字段如果是关联或者冗余,要写下注释。以 processInstanceId 举例子。 /** * 发起人 Id + * + * 关联 system_users 的 id 属性 */ private Long startUserId; /** * 流程名 + * + * 冗余 ProcessInstance 的 name 字段 */ private String processInstanceName; /** @@ -44,16 +47,22 @@ public class BpmProcessInstanceCopyDO extends BaseDO { /** * 任务主键 + * + * 关联 Task 的 id 属性 */ private String taskId; /** * 任务名称 + * + * 冗余 Task 的 name 属性 */ private String taskName; /** * 用户编号 + * + * 关联 system_users 的 id 属性 */ private Long userId; @@ -62,10 +71,11 @@ public class BpmProcessInstanceCopyDO extends BaseDO { */ private String reason; - // TODO @kyle:这个字段,可以用 category 简化点 /** * 流程分类 + * + * 冗余 ProcessInstance 的 category 字段 */ - private String processDefinitionCategory; + private String category; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/cc/BpmProcessInstanceCopyMapper.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/cc/BpmProcessInstanceCopyMapper.java index 3b8848428b..de6fa14f53 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/cc/BpmProcessInstanceCopyMapper.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/cc/BpmProcessInstanceCopyMapper.java @@ -3,13 +3,14 @@ package cn.iocoder.yudao.module.bpm.dal.mysql.cc; 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.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCMyPageReqVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyMyPageReqVO; import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO; import org.apache.ibatis.annotations.Mapper; @Mapper -public interface BpmProcessInstanceCopyMapper extends BaseMapperX { // TODO @kyle:方法和类之间要空行下; - default PageResult selectPage(Long loginUserId, BpmProcessInstanceCCMyPageReqVO reqVO){ +public interface BpmProcessInstanceCopyMapper extends BaseMapperX { + + default PageResult selectPage(Long loginUserId, BpmProcessInstanceCopyMyPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .eqIfPresent(BpmProcessInstanceCopyDO::getUserId, loginUserId) .eqIfPresent(BpmProcessInstanceCopyDO::getProcessInstanceId, reqVO.getProcessInstanceId()) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/listener/BpmServiceResultListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/listener/BpmServiceResultListener.java new file mode 100644 index 0000000000..34a045a232 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/listener/BpmServiceResultListener.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.bpm.framework.bpm.listener; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi; +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; +import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent; +import jakarta.annotation.Resource; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import java.util.List; + +// TODO @芋艿:后续改成支持 RPC +/** + * 业务流程结果监听器实现类 + * + * @author HUIHUI + */ +@Component +public class BpmServiceResultListener implements ApplicationListener { + + @Resource + private List bpmResultListenerApis; + + @Override + public final void onApplicationEvent(BpmProcessInstanceResultEvent event) { + bpmResultListenerApis.forEach(bpmResultListenerApi -> { + if (!StrUtil.equals(event.getProcessDefinitionKey(), bpmResultListenerApi.getProcessDefinitionKey())) { + return; + } + bpmResultListenerApi.onEvent(BeanUtils.toBean(event, BpmResultListenerRespDTO.class)); + }); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/handler/MultiInstanceHandler.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/handler/MultiInstanceHandler.java new file mode 100644 index 0000000000..ee81a9cf46 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/handler/MultiInstanceHandler.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.flowable.core.enums.BpmnModelConstants; +import cn.iocoder.yudao.module.system.api.permission.PermissionApi; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import lombok.AllArgsConstructor; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +// TODO @芋艿:bpmn 分配人融合时,需要搞下这块; +/** + * 多实例处理类 + */ +@AllArgsConstructor +@Component("multiInstanceHandler") +public class MultiInstanceHandler { + + @Resource + private AdminUserApi userApi; + + @Resource + private PermissionApi permissionApi; + + /** + * 流程发起人那种情况不需要处理, + * 由 flowable 完成 + * + * @param execution flowable的执行对象 + * @return 用户ID + */ + public Set getUserIds(DelegateExecution execution) { + Set candidateUserIds = new LinkedHashSet<>(); + FlowElement flowElement = execution.getCurrentFlowElement(); + if (ObjectUtil.isNotEmpty(flowElement) && flowElement instanceof UserTask userTask) { + String dataType = userTask.getAttributeValue(BpmnModelConstants.NAMESPACE, BpmnModelConstants.PROCESS_CUSTOM_DATA_TYPE); + if ("USERS".equals(dataType) && CollUtil.isNotEmpty(userTask.getCandidateUsers())) { + // 添加候选用户id + candidateUserIds.addAll(userTask.getCandidateUsers()); + } else if (CollUtil.isNotEmpty(userTask.getCandidateGroups())) { + // 获取组的ID,角色ID集合或部门ID集合 + List groups = userTask.getCandidateGroups().stream() + // 例如部门DEPT100,100才是部门id + .map(item -> Long.parseLong(item.substring(4))) + .collect(Collectors.toList()); + List userIds = new ArrayList<>(); + if ("ROLES".equals(dataType)) { + // 通过角色id,获取所有用户id集合 + Set userRoleIdListByRoleIds = permissionApi.getUserRoleIdListByRoleIds(groups); + userIds = new ArrayList<>(userRoleIdListByRoleIds); + } else if ("DEPTS".equals(dataType)) { + // 通过部门id,获取所有用户id集合 + List userListByDeptIds = userApi.getUserListByDeptIds(groups); + userIds = convertList(userListByDeptIds, AdminUserRespDTO::getId); + } + // 添加候选用户id + userIds.forEach(id -> candidateUserIds.add(String.valueOf(id))); + } + } + return candidateUserIds; + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java index 98bc37f9fd..52c56061e7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java @@ -50,4 +50,5 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent protected void processCompleted(FlowableEngineEntityEvent event) { processInstanceService.updateProcessInstanceExtComplete((ProcessInstance)event.getEntity()); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java index 6dda9ebf2f..0c5f099d68 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java @@ -2,12 +2,12 @@ package cn.iocoder.yudao.module.bpm.service.candidate; import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; import java.util.HashSet; import java.util.Set; @@ -18,11 +18,6 @@ import java.util.Set; @NoArgsConstructor @Data public class BpmCandidateSourceInfo { - - @Schema(description = "流程id") - @NotNull - private String processInstanceId; - @Schema(description = "当前任务ID") @NotNull private String taskId; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyService.java deleted file mode 100644 index 799656fdeb..0000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyService.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.iocoder.yudao.module.bpm.service.cc; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCMyPageReqVO; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCPageItemRespVO; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCReqVO; -import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; - -// TODO @kyle:这个 Service 要不挪到 task 包下;保持统一,task 下有流程、任务、抄送等; -// TODO @kyle:中文和英文之间,有个空格,会更清晰点;例如说;流程抄送 Service 接口;中文写作习惯~ -/** - * 流程抄送Service接口 - * - * 现在是在审批的时候进行流程抄送 - */ -public interface BpmProcessInstanceCopyService { - - // TODO @kyle:无用的方法,可以去掉哈;另外,考虑到避免过多的 VO,这里就可以返回 BpmProcessInstanceCopyDO - /** - * 查询流程抄送 - * - * @param copyId 流程抄送主键 - * @return 流程抄送 - */ - BpmProcessInstanceCopyVO queryById(Long copyId); - - // TODO 芋艿:这块要 review 下;思考下~~ - /** - * 抄送 - * @param sourceInfo 抄送源信息,方便抄送处理 - * @return - */ - boolean makeCopy(BpmCandidateSourceInfo sourceInfo); - - // TODO @kyle:可以方法名改成 createProcessInstanceCopy;现在项目一般新增都用 create 为主; - /** - * 流程实例的抄送 - * - * @param userId 当前登录用户 - * @param createReqVO 创建的抄送请求 - * @return 是否抄送成功,抄送成功则返回true TODO @kyle:这里可以不用返回哈;目前一般是失败,就抛出业务异常; - */ - boolean ccProcessInstance(Long userId, BpmProcessInstanceCCReqVO createReqVO); - - /** - * 抄送的流程 - * @param loginUserId 登录用户id - * @param pageReqVO 分页请求 - * @return 抄送的分页结果 - */ - PageResult getMyProcessInstanceCCPage(Long loginUserId, - BpmProcessInstanceCCMyPageReqVO pageReqVO); -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceImpl.java deleted file mode 100644 index b9f8dbe3ff..0000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceImpl.java +++ /dev/null @@ -1,165 +0,0 @@ -package cn.iocoder.yudao.module.bpm.service.cc; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCMyPageReqVO; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCPageItemRespVO; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCCReqVO; -import cn.iocoder.yudao.module.bpm.convert.cc.BpmProcessInstanceCopyConvert; -import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO; -import cn.iocoder.yudao.module.bpm.dal.mysql.cc.BpmProcessInstanceCopyMapper; -import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; -import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessorChain; -import cn.iocoder.yudao.module.bpm.service.cc.dto.BpmDelegateExecutionDTO; -import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; -import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; -import cn.iocoder.yudao.module.bpm.util.FlowableUtils; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.RuntimeService; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.repository.ProcessDefinition; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.*; -import java.util.stream.Collectors; - -// TODO @kyle:类注释要写下 -@Slf4j // TODO @kyle:按照 @Service、@Validated、@Slf4j,从重要到不重要的顺序; -@Service -@Validated -public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopyService { - @Resource // TODO @kyle:第一个变量,和类之间要有空行; - private BpmProcessInstanceCopyMapper processInstanceCopyMapper; - - /** - * 和flowable有关的,查询流程名用的 TODO @kyle:可以不写哈注释; - */ - @Resource - private RuntimeService runtimeService; - - /** - * 找抄送人用的 TODO @kyle:可以不写哈注释; - */ - @Resource - private BpmCandidateSourceInfoProcessorChain processorChain; - - // TODO @kyle:多余的变量,可以去掉哈 - @Resource - @Lazy // 解决循环依赖 - private BpmTaskService bpmTaskService; - @Resource - @Lazy // 解决循环依赖 - private BpmProcessInstanceService bpmProcessInstanceService; - @Resource - private AdminUserApi adminUserApi; - - @Override - public BpmProcessInstanceCopyVO queryById(Long copyId) { - BpmProcessInstanceCopyDO bpmProcessInstanceCopyDO = processInstanceCopyMapper.selectById(copyId); - return BpmProcessInstanceCopyConvert.INSTANCE.convert(bpmProcessInstanceCopyDO); - } - - // TODO @kyle:makeCopy 和 ccProcessInstance 的调用关系,感受上反了; - // makeCopy 有点像基于规则,查找抄送人,然后创建; - // ccProcessInstance 是已经有了抄送人,然后创建; - // 建议的改造:独立基于 processInstanceCopyMapper 做 insert - @Override - public boolean makeCopy(BpmCandidateSourceInfo sourceInfo) { - if (null == sourceInfo) { - return false; - } - - DelegateExecution executionEntity = new BpmDelegateExecutionDTO(sourceInfo.getProcessInstanceId()); - Set ccCandidates = processorChain.calculateTaskCandidateUsers(executionEntity, sourceInfo); - if (CollUtil.isEmpty(ccCandidates)) { - log.warn("相关抄送人不存在 {}", sourceInfo.getTaskId()); - return false; - } else { - BpmProcessInstanceCopyDO copyDO = new BpmProcessInstanceCopyDO(); - // 调用 - // 设置任务id - copyDO.setTaskId(sourceInfo.getTaskId()); - copyDO.setTaskName(FlowableUtils.getTaskNameByTaskId(sourceInfo.getTaskId())); - copyDO.setProcessInstanceId(sourceInfo.getProcessInstanceId()); - ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() - .processInstanceId(sourceInfo.getProcessInstanceId()) - .singleResult(); - if (null == processInstance) { - log.warn("相关流程实例不存在 {}", sourceInfo.getTaskId()); - return false; - } - copyDO.setStartUserId(FlowableUtils.getStartUserIdFromProcessInstance(processInstance)); - copyDO.setProcessInstanceName(processInstance.getName()); - ProcessDefinition processDefinition = FlowableUtils.getProcessDefinition(processInstance.getProcessDefinitionId()); - copyDO.setProcessDefinitionCategory(processDefinition.getCategory()); - copyDO.setReason(sourceInfo.getReason()); - copyDO.setCreator(sourceInfo.getCreator()); - copyDO.setCreateTime(LocalDateTime.now()); - List copyList = new ArrayList<>(ccCandidates.size()); - for (Long userId : ccCandidates) { - BpmProcessInstanceCopyDO copy = BpmProcessInstanceCopyConvert.INSTANCE.copy(copyDO); - copy.setUserId(userId); - copyList.add(copy); - } - return processInstanceCopyMapper.insertBatch(copyList); - } - } - - @Override - public boolean ccProcessInstance(Long userId, BpmProcessInstanceCCReqVO reqVO) { - // 在能正常审批的情况下抄送流程 - BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo(); - sourceInfo.setTaskId(reqVO.getTaskKey()); - sourceInfo.setProcessInstanceId(reqVO.getProcessInstanceKey()); - sourceInfo.addRule(reqVO); - sourceInfo.setCreator(String.valueOf(userId)); - sourceInfo.setReason(reqVO.getReason()); - if (!makeCopy(sourceInfo)) { - throw new RuntimeException("抄送任务失败"); - } - return false; - } - - //获取流程抄送分页 TODO @kyle:接口已经注释,这里不用注释了哈; - @Override - public PageResult getMyProcessInstanceCCPage(Long loginUserId, BpmProcessInstanceCCMyPageReqVO pageReqVO) { - // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 - // TODO @kyle:一般读逻辑,Service 返回 PageResult 即可。关联数据的查询和拼接,交给 Controller;目的是:保证 Service 聚焦写逻辑,清晰简洁; - PageResult pageResult = processInstanceCopyMapper.selectPage(loginUserId, pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return new PageResult<>(pageResult.getTotal()); - } - - // TODO @kyle:这种可以简洁点;参考如下 -// Map processInstanceMap = bpmProcessInstanceService.getProcessInstanceMap( -// convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId)); - - Set taskIds = new HashSet<>(); - Set processInstaneIds = new HashSet<>(); - Set userIds = new HashSet<>(); - for (BpmProcessInstanceCopyDO doItem : pageResult.getList()) { - taskIds.add(doItem.getTaskId()); - processInstaneIds.add(doItem.getProcessInstanceId()); - userIds.add(doItem.getStartUserId()); - Long userId = Long.valueOf(doItem.getCreator()); - userIds.add(userId); - } - - Map taskNameByTaskIds = FlowableUtils.getTaskNameByTaskIds(taskIds); - Map processInstanceNameByTaskIds = FlowableUtils.getProcessInstanceNameByTaskIds(processInstaneIds); - - Map userMap = adminUserApi.getUserList(userIds).stream().collect(Collectors.toMap( - AdminUserRespDTO::getId, AdminUserRespDTO::getNickname)); - - // 转换返回 - return BpmProcessInstanceCopyConvert.INSTANCE.convertPage(pageResult, taskNameByTaskIds, processInstanceNameByTaskIds, userMap); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyVO.java deleted file mode 100644 index 881ec6fd3d..0000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyVO.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.bpm.service.cc; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -// TODO @kyle:看看是不是要删除 -/** - * 流程抄送视图对象 wf_copy - * - * @author ruoyi - * @date 2022-05-19 - */ -@Data -public class BpmProcessInstanceCopyVO { - - /** - * 编号 - */ - @Schema(description = "抄送主键") - private Long id; - - /** - * 发起人Id - */ - @Schema(description = "发起人Id") - private Long startUserId; - - @Schema(description = "发起人别名") - private String startUserNickname; - - /** - * 流程主键 - */ - @Schema(description = "流程实例的主键") - private String processInstanceId; - - @Schema(description = "流程实例的名字") - private String processInstanceName; - - /** - * 任务主键 - */ - @Schema(description = "发起抄送的任务编号") - private String taskId; - - @Schema(description = "发起抄送的任务名称") - private String taskName; - /** - * 用户主键 - */ - @Schema(description = "用户编号") - private Long userId; - - @Schema(description = "用户别名") - private Long userNickname; - - @Schema(description = "抄送原因") - private String reason; - - @Schema(description = "抄送人") - private String creator; - - @Schema(description = "抄送时间") - private LocalDateTime createTime; -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java index 03fc99e033..bddd790816 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java @@ -2,10 +2,9 @@ package cn.iocoder.yudao.module.bpm.service.definition; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*; +import jakarta.validation.Valid; import org.flowable.bpmn.model.BpmnModel; -import javax.validation.Valid; - /** * Flowable流程模型接口 * @@ -62,7 +61,7 @@ public interface BpmModelService { /** * 修改模型的状态,实际更新的部署的流程定义的状态 * - * @param id 编号 + * @param id 编号 * @param state 状态 */ void updateModelState(String id, Integer state); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 090a68f840..08f8acf6bd 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -14,6 +14,8 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.model.BpmnModel; @@ -29,8 +31,6 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; -import javax.validation.Valid; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java index aa6632854a..c9b3872cdc 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java @@ -5,8 +5,8 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.PageUtils; +import cn.iocoder.yudao.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils; -import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO; @@ -17,6 +17,8 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionExtMapper; import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.model.BpmnModel; @@ -29,13 +31,10 @@ import org.flowable.engine.repository.ProcessDefinitionQuery; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; -import javax.validation.Valid; import java.util.*; 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.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_KEY_NOT_MATCH; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_NAME_NOT_MATCH; import static java.util.Collections.emptyList; @@ -53,8 +52,6 @@ import static java.util.Collections.emptyList; @Slf4j public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService { - private static final String BPMN_FILE_SUFFIX = ".bpmn"; - @Resource private RepositoryService repositoryService; @@ -125,7 +122,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ // 创建 Deployment 部署 Deployment deploy = repositoryService.createDeployment() .key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory()) - .addBytes(createReqDTO.getKey() + BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes()) + .addBytes(createReqDTO.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes()) .tenantId(TenantContextHolder.getTenantIdStr()) .deploy(); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index cff0ec976c..4dbd1bfd85 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -46,6 +46,17 @@ public interface BpmProcessInstanceService { return CollectionUtils.convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId); } + /** + * 获得流程实例名字 Map + * + * @param ids 流程实例的编号集合 + * @return 对应的映射关系 + */ + default Map getProcessInstanceNameMap(Set ids) { + return CollectionUtils.convertMap(getProcessInstances(ids), + ProcessInstance::getProcessInstanceId, ProcessInstance::getName); + } + /** * 获得流程实例的分页 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index 42be9260ad..97018451d6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -4,9 +4,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; +import jakarta.validation.Valid; import org.flowable.task.api.Task; -import javax.validation.Valid; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -132,6 +133,8 @@ public interface BpmTaskService { */ void updateTaskExtAssign(Task task); + Task getTask(String id); + /** * 获取当前任务的可回退的流程集合 * @@ -181,4 +184,12 @@ public interface BpmTaskService { */ List getChildrenTaskList(String parentId); + /** + * 通过任务 ID,查询任务名 Map + * + * @param taskIds 任务 ID + * @return 任务 ID 与名字的 Map + */ + Map getTaskNameByTaskIds(Collection taskIds); + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 6667bd4d02..6e9929d379 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -19,14 +19,14 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskAddSignTypeEnum; -import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; -import cn.iocoder.yudao.module.bpm.service.cc.BpmProcessInstanceCopyService; import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.FlowElement; @@ -51,8 +51,6 @@ import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; -import javax.annotation.Resource; -import javax.validation.Valid; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Stream; @@ -96,9 +94,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Resource private ManagementService managementService; - @Resource - private BpmProcessInstanceCopyService processInstanceCopyService; - @Override public PageResult getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) { // 查询待办任务 @@ -558,7 +553,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { return task; } - private Task getTask(String id) { + @Override + public Task getTask(String id) { return taskService.createTaskQuery().taskId(id).singleResult(); } @@ -971,4 +967,13 @@ public class BpmTaskServiceImpl implements BpmTaskService { return BpmTaskConvert.INSTANCE.convertList(taskExtList, userMap, idTaskMap); } + @Override + public Map getTaskNameByTaskIds(Collection taskIds) { + if (CollUtil.isEmpty(taskIds)) { + return Collections.emptyMap(); + } + List tasks = taskService.createTaskQuery().taskIds(taskIds).list(); + return convertMap(tasks, Task::getId, Task::getName); + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/BpmProcessInstanceCopyService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/BpmProcessInstanceCopyService.java new file mode 100644 index 0000000000..208749a576 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/BpmProcessInstanceCopyService.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.bpm.service.task.cc; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyCreateReqVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyMyPageReqVO; +import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; + +/** + * 流程抄送 Service 接口 + * + * 现在是在审批的时候进行流程抄送 + */ +public interface BpmProcessInstanceCopyService { + + // TODO 芋艿:这块要 review 下;思考下~~ + /** + * 抄送 + * @param sourceInfo 抄送源信息,方便抄送处理 + * @return + */ + boolean makeCopy(BpmCandidateSourceInfo sourceInfo); + + /** + * 流程实例的抄送 + * + * @param userId 当前登录用户 + * @param createReqVO 创建的抄送请求 + */ + void createProcessInstanceCopy(Long userId, BpmProcessInstanceCopyCreateReqVO createReqVO); + + /** + * 抄送的流程的分页 + * @param userId 当前登录用户 + * @param pageReqVO 分页请求 + * @return 抄送的分页结果 + */ + PageResult getMyProcessInstanceCopyPage(Long userId, + BpmProcessInstanceCopyMyPageReqVO pageReqVO); +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/BpmProcessInstanceCopyServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/BpmProcessInstanceCopyServiceImpl.java new file mode 100644 index 0000000000..50d042cdc8 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/BpmProcessInstanceCopyServiceImpl.java @@ -0,0 +1,139 @@ +package cn.iocoder.yudao.module.bpm.service.task.cc; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyCreateReqVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyMyPageReqVO; +import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO; +import cn.iocoder.yudao.module.bpm.dal.mysql.cc.BpmProcessInstanceCopyMapper; +import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo; +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessorChain; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; +import cn.iocoder.yudao.module.bpm.service.task.cc.dto.BpmDelegateExecutionDTO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; + +/** + * 流程抄送 Service 实现类 + * + * @author kyle + */ +@Service +@Validated +@Slf4j +public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopyService { + + @Resource + private BpmProcessInstanceCopyMapper processInstanceCopyMapper; + + @Resource + private RuntimeService runtimeService; + + @Resource + private BpmCandidateSourceInfoProcessorChain processorChain; + + @Resource + @Lazy + private BpmTaskService bpmTaskService; + @Resource + @Lazy + private BpmProcessInstanceService bpmProcessInstanceService; + + @Override + public boolean makeCopy(BpmCandidateSourceInfo sourceInfo) { + if (null == sourceInfo) { + return false; + } + + Task task = bpmTaskService.getTask(sourceInfo.getTaskId()); + if (ObjectUtil.isNull(task)) { + return false; + } + String processInstanceId = task.getProcessInstanceId(); + if (StrUtil.isBlank(processInstanceId)) { + return false; + } + DelegateExecution executionEntity = new BpmDelegateExecutionDTO(processInstanceId); + Set ccCandidates = processorChain.calculateTaskCandidateUsers(executionEntity, sourceInfo); + if (CollUtil.isEmpty(ccCandidates)) { + log.warn("相关抄送人不存在 {}", sourceInfo.getTaskId()); + return false; + } else { + BpmProcessInstanceCopyDO copyDO = new BpmProcessInstanceCopyDO(); + // 调用 + // 设置任务id + copyDO.setTaskId(sourceInfo.getTaskId()); + copyDO.setTaskName(task.getName()); + copyDO.setProcessInstanceId(processInstanceId); + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(processInstanceId) + .singleResult(); + if (null == processInstance) { + log.warn("相关流程实例不存在 {}", sourceInfo.getTaskId()); + return false; + } + copyDO.setStartUserId(Long.parseLong(processInstance.getStartUserId())); + copyDO.setProcessInstanceName(processInstance.getName()); + copyDO.setCategory(processInstance.getProcessDefinitionCategory()); + copyDO.setReason(sourceInfo.getReason()); + copyDO.setCreator(sourceInfo.getCreator()); + copyDO.setCreateTime(LocalDateTime.now()); + List copyList = new ArrayList<>(ccCandidates.size()); + for (Long userId : ccCandidates) { + BpmProcessInstanceCopyDO copy = BeanUtil.copyProperties(copyDO, BpmProcessInstanceCopyDO.class); + copy.setUserId(userId); + copyList.add(copy); + } + return processInstanceCopyMapper.insertBatch(copyList); + } + } + + @Override + public void createProcessInstanceCopy(Long userId, BpmProcessInstanceCopyCreateReqVO reqVO) { + // 1.1 校验任务存在 + Task task = bpmTaskService.getTask(reqVO.getTaskId()); + if (ObjectUtil.isNull(task)) { + throw exception(ErrorCodeConstants.TASK_NOT_EXISTS); + } + // 1.2 校验流程存在 + String processInstanceId = task.getProcessInstanceId(); + ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(processInstanceId); + if (processInstance == null) { + log.warn("[createProcessInstanceCopy][任务({}) 对应的流程不存在]", reqVO.getTaskId()); + throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); + } + + // 2. 创建抄送流程 + BpmProcessInstanceCopyDO copy = new BpmProcessInstanceCopyDO() + .setTaskId(reqVO.getTaskId()).setTaskName(task.getName()) + .setProcessInstanceId(processInstanceId).setStartUserId(Long.valueOf(processInstance.getStartUserId())) + .setProcessInstanceName(processInstance.getName()).setCategory(processInstance.getProcessDefinitionCategory()) + .setReason(reqVO.getReason()); + processInstanceCopyMapper.insert(copy); + } + + @Override + public PageResult getMyProcessInstanceCopyPage(Long userId, BpmProcessInstanceCopyMyPageReqVO pageReqVO) { + return processInstanceCopyMapper.selectPage(userId, pageReqVO); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/dto/BpmDelegateExecutionDTO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/dto/BpmDelegateExecutionDTO.java similarity index 99% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/dto/BpmDelegateExecutionDTO.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/dto/BpmDelegateExecutionDTO.java index 47213ae52b..d010d89691 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/cc/dto/BpmDelegateExecutionDTO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/dto/BpmDelegateExecutionDTO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.bpm.service.cc.dto; +package cn.iocoder.yudao.module.bpm.service.task.cc.dto; import org.flowable.bpmn.model.FlowElement; import org.flowable.bpmn.model.FlowableListener; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/util/FlowableUtils.java deleted file mode 100644 index 0217ea382f..0000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/util/FlowableUtils.java +++ /dev/null @@ -1,127 +0,0 @@ -package cn.iocoder.yudao.module.bpm.util; - - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.util.number.NumberUtils; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.ExtensionElement; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.bpmn.model.FlowNode; -import org.flowable.engine.RepositoryService; -import org.flowable.engine.RuntimeService; -import org.flowable.engine.TaskService; -import org.flowable.engine.repository.ProcessDefinition; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.task.api.Task; - -import java.util.*; - -/** - * 流程引擎工具类封装 - * - * @author: linjinp - * @create: 2019-12-24 13:51 - **/ -public class FlowableUtils { - - /** - * 获取流程名称 - * - * @param processDefinitionId - * @return - */ - public static String getProcessDefinitionName(String processDefinitionId) { - RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class); - ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId); - return processDefinition.getName(); - } - - public static ProcessDefinition getProcessDefinition(String processDefinitionId) { - RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class); - return repositoryService.getProcessDefinition(processDefinitionId); - } - - /** - * 获取节点数据 - * - * @param processInstanceId - * @param nodeId - * @return - */ - public static FlowNode getFlowNode(String processInstanceId, String nodeId) { - - RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class); - RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class); - - String definitionld = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult().getProcessDefinitionId(); // 获取bpm(模型)对象 - BpmnModel bpmnModel = repositoryService.getBpmnModel(definitionld); - // 传节点定义key获取当前节点 - FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(nodeId); - return flowNode; - } - - public static ExtensionElement generateFlowNodeIdExtension(String nodeId) { - ExtensionElement extensionElement = new ExtensionElement(); - extensionElement.setElementText(nodeId); - extensionElement.setName("nodeId"); - extensionElement.setNamespacePrefix("flowable"); - extensionElement.setNamespace("nodeId"); - return extensionElement; - } - - public static String getNodeIdFromExtension(FlowElement flowElement) { - Map> extensionElements = flowElement.getExtensionElements(); - return extensionElements.get("nodeId").get(0).getElementText(); - } - - public static Long getStartUserIdFromProcessInstance(ProcessInstance instance) { - if (null == instance) { - return null; - } - return NumberUtils.parseLong(instance.getStartUserId()); - } - - public static String getTaskNameByTaskId(String taskId) { - TaskService taskService = SpringUtil.getBean(TaskService.class); - Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); - return task.getName(); - } - - // TODO @kyle:Utils 里不做查询;可以封装到 bpmTaskService 里 - public static Map getTaskNameByTaskIds(Collection taskIds) { - TaskService taskService = SpringUtil.getBean(TaskService.class); - List tasks = taskService.createTaskQuery().taskIds(taskIds).list(); - if (CollUtil.isNotEmpty(tasks)) { - Map taskMap = new HashMap<>(tasks.size()); - for (Task task : tasks) { - taskMap.putIfAbsent(task.getId(), task.getName()); - } - return taskMap; - } - return Collections.emptyMap(); - } - - public static String getProcessInstanceNameByTaskId(String processInstanceId) { - RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class); - ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() - .processInstanceId(processInstanceId) - .singleResult(); - return processInstance.getName(); - } - - // TODO @kyle:Utils 里不做查询;可以封装到 bpmTaskService 里 - public static Map getProcessInstanceNameByTaskIds(Set taskIds) { - RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class); - List processInstances = runtimeService.createProcessInstanceQuery().processInstanceIds(taskIds).list(); - if (CollUtil.isNotEmpty(processInstances)) { - Map processInstaneMap = new HashMap<>(processInstances.size()); - for (ProcessInstance processInstance : processInstances) { - processInstaneMap.putIfAbsent(processInstance.getId(), processInstance.getName()); - } - return processInstaneMap; - } - return Collections.emptyMap(); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceTest.java deleted file mode 100644 index 42a1d46e8c..0000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/cc/BpmProcessInstanceCopyServiceTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.bpm.service.cc; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import javax.annotation.Resource; - -@Import({BpmProcessInstanceCopyServiceImpl.class}) -class BpmProcessInstanceCopyServiceTest extends BaseDbUnitTest { - @Resource - private BpmProcessInstanceCopyServiceImpl service; - - @Test - void queryById() { - } -} \ No newline at end of file diff --git a/yudao-module-crm/pom.xml b/yudao-module-crm/pom.xml index 2a7b748c38..48ef0eac42 100644 --- a/yudao-module-crm/pom.xml +++ b/yudao-module-crm/pom.xml @@ -16,10 +16,10 @@ pom ${project.artifactId} - crm 包下,客户关系管理(Customer Relationship Management)。 例如说:客户、联系人、商机、合同、回款等等 + 商业智能 BI 模块,包括:报表、图表、数据大屏等等 diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java index 616c8685c9..d536d8a40e 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java @@ -11,6 +11,8 @@ public interface ErrorCodeConstants { // ========== 合同管理 1-020-000-000 ========== ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在"); + ErrorCode CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_000_001, "更新合同失败,原因:禁止编辑"); + ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态"); // ========== 线索管理 1-020-001-000 ========== ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在"); @@ -26,8 +28,8 @@ public interface ErrorCodeConstants { // ========== 联系人管理 1-020-003-000 ========== ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在"); - ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode( 1_020_003_001, "联系人商机关联不存在"); - ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode( 1_020_003_002, "联系人已关联合同,不能删除"); + ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode(1_020_003_001, "联系人商机关联不存在"); + ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, "联系人已关联合同,不能删除"); // ========== 回款 1-020-004-000 ========== ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款不存在"); @@ -48,6 +50,9 @@ public interface ErrorCodeConstants { ErrorCode CUSTOMER_LOCK_EXCEED_LIMIT = new ErrorCode(1_020_006_009, "锁定客户失败,超出锁定规则上限"); ErrorCode CUSTOMER_OWNER_EXCEED_LIMIT = new ErrorCode(1_020_006_010, "操作失败,超出客户数拥有上限"); ErrorCode CUSTOMER_DELETE_FAIL_HAVE_REFERENCE = new ErrorCode(1_020_006_011, "删除客户失败,有关联{}"); + ErrorCode CUSTOMER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_020_006_012, "导入客户数据不能为空!"); + ErrorCode CUSTOMER_CREATE_NAME_NOT_NULL = new ErrorCode(1_020_006_013, "客户名称不能为空!"); + ErrorCode CUSTOMER_NAME_EXISTS = new ErrorCode(1_020_006_014, "已存在名为【{}】的客户!"); // ========== 权限管理 1_020_007_000 ========== ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在"); @@ -80,10 +85,13 @@ public interface ErrorCodeConstants { ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_011_000, "商机状态不存在"); // ========== 客户公海规则设置 1_020_012_000 ========== - ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_000, "客户限制配置不存在"); + ErrorCode CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_020_012_000, "客户公海配置不存在或未启用"); + ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_001, "客户限制配置不存在"); // ========== 跟进记录 1_020_013_000 ========== ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在"); ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限"); + // ========== 待办消息 1_020_014_000 ========== + } diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java index 2239947250..98a66d2c9b 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java @@ -11,6 +11,16 @@ public interface LogRecordConstants { // ======================= CRM_LEADS 线索 ======================= String CRM_LEADS_TYPE = "CRM 线索"; + String CRM_LEADS_CREATE_SUB_TYPE = "创建线索"; + String CRM_LEADS_CREATE_SUCCESS = "创建了线索{{#clue.name}}"; + String CRM_LEADS_UPDATE_SUB_TYPE = "更新线索"; + String CRM_LEADS_UPDATE_SUCCESS = "更新了线索【{{#clueName}}】: {_DIFF{#updateReq}}"; + String CRM_LEADS_DELETE_SUB_TYPE = "删除线索"; + String CRM_LEADS_DELETE_SUCCESS = "删除了线索【{{#clueName}}】"; + String CRM_LEADS_TRANSFER_SUB_TYPE = "转移线索"; + String CRM_LEADS_TRANSFER_SUCCESS = "将线索【{{#clue.name}}】的负责人从【{getAdminUserById{#clue.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】"; + String CRM_LEADS_TRANSLATE_SUB_TYPE = "线索转化为客户"; + String CRM_LEADS_TRANSLATE_SUCCESS = "将线索【{{#clue.name}}】转化为客户"; // ======================= CRM_CUSTOMER 客户 ======================= @@ -29,6 +39,8 @@ public interface LogRecordConstants { String CRM_CUSTOMER_POOL_SUCCESS = "将客户【{{#customerName}}】放入了公海"; String CRM_CUSTOMER_RECEIVE_SUB_TYPE = "{{#ownerUserName != null ? '分配客户' : '领取客户'}}"; String CRM_CUSTOMER_RECEIVE_SUCCESS = "{{#ownerUserName != null ? '将客户【' + #customer.name + '】分配给【' + #ownerUserName + '】' : '领取客户【' + #customer.name + '】'}}"; + String CRM_CUSTOMER_IMPORT_SUB_TYPE = "{{#isUpdate ? '导入并更新客户' : '导入客户'}}"; + String CRM_CUSTOMER_IMPORT_SUCCESS = "{{#isUpdate ? '导入并更新了客户【'+ #customer.name +'】' : '导入了客户【'+ #customer.name +'】'}}"; // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 ======================= @@ -81,6 +93,8 @@ public interface LogRecordConstants { String CRM_CONTRACT_DELETE_SUCCESS = "删除了合同【{{#contractName}}】"; String CRM_CONTRACT_TRANSFER_SUB_TYPE = "转移合同"; String CRM_CONTRACT_TRANSFER_SUCCESS = "将合同【{{#contract.name}}】的负责人从【{getAdminUserById{#contract.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】"; + String CRM_CONTRACT_SUBMIT_SUB_TYPE = "提交合同审批"; + String CRM_CONTRACT_SUBMIT_SUCCESS = "提交合同【{{#contractName}}】审批成功"; // ======================= CRM_PRODUCT 产品 ======================= diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/message/CrmContactStatusEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/message/CrmContactStatusEnum.java deleted file mode 100644 index 6ca5f52dcd..0000000000 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/message/CrmContactStatusEnum.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.crm.enums.message; - -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * CRM 联系状态 - * - * @author dhb52 - */ -@RequiredArgsConstructor -@Getter -public enum CrmContactStatusEnum implements IntArrayValuable { - - NEEDED_TODAY(1, "今日需联系"), - EXPIRED(2, "已逾期"), - ALREADY_CONTACT(3, "已联系"), - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmContactStatusEnum::getType).toArray(); - - /** - * 状态 - */ - private final Integer type; - /** - * 状态名 - */ - private final String name; - - @Override - public int[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/pom.xml b/yudao-module-crm/yudao-module-crm-biz/pom.xml index 9e1a9e1521..b905bf6dac 100644 --- a/yudao-module-crm/yudao-module-crm-biz/pom.xml +++ b/yudao-module-crm/yudao-module-crm-biz/pom.xml @@ -27,6 +27,11 @@ yudao-module-crm-api ${revision} + + cn.iocoder.boot + yudao-module-bpm-api + ${revision} + @@ -37,6 +42,10 @@ cn.iocoder.boot yudao-spring-boot-starter-biz-ip + + cn.iocoder.boot + yudao-spring-boot-starter-biz-tenant + diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/message/CrmMessageController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/CrmBacklogController.java similarity index 71% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/message/CrmMessageController.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/CrmBacklogController.java index 771d9fb839..50abd9cccd 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/message/CrmMessageController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/CrmBacklogController.java @@ -1,12 +1,12 @@ -package cn.iocoder.yudao.module.crm.controller.admin.message; +package cn.iocoder.yudao.module.crm.controller.admin.backlog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.message.vo.CrmTodayCustomerPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; -import cn.iocoder.yudao.module.crm.service.message.CrmMessageService; +import cn.iocoder.yudao.module.crm.service.message.CrmBacklogService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.security.access.prepost.PreAuthorize; @@ -21,19 +21,19 @@ import javax.validation.Valid; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; -@Tag(name = "管理后台 - CRM消息") +@Tag(name = "管理后台 - CRM待办消息") @RestController -@RequestMapping("/crm/message") +@RequestMapping("/crm/backlog") @Validated -public class CrmMessageController { +public class CrmBacklogController { @Resource - private CrmMessageService crmMessageService; + private CrmBacklogService crmMessageService; // TODO 芋艿:未来可能合并到 CrmCustomerController - @GetMapping("/todayCustomer") // TODO @dbh52:【优先级低】url 使用中划线,项目规范。然后叫 today-customer-page,通过 page 体现出它是个分页接口 + @GetMapping("/today-customer-page") @Operation(summary = "今日需联系客户") - @PreAuthorize("@ss.hasPermission('crm:message:todayCustomer')") + @PreAuthorize("@ss.hasPermission('crm:customer:query')") public CommonResult> getTodayCustomerPage(@Valid CrmTodayCustomerPageReqVO pageReqVO) { PageResult pageResult = crmMessageService.getTodayCustomerPage(pageReqVO, getLoginUserId()); return success(BeanUtils.toBean(pageResult, CrmCustomerRespVO.class)); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/message/vo/CrmTodayCustomerPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/vo/CrmTodayCustomerPageReqVO.java similarity index 65% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/message/vo/CrmTodayCustomerPageReqVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/vo/CrmTodayCustomerPageReqVO.java index f47dfb4683..21fd88c0b3 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/message/vo/CrmTodayCustomerPageReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/vo/CrmTodayCustomerPageReqVO.java @@ -1,9 +1,8 @@ -package cn.iocoder.yudao.module.crm.controller.admin.message.vo; +package cn.iocoder.yudao.module.crm.controller.admin.backlog.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; -import cn.iocoder.yudao.module.crm.enums.message.CrmContactStatusEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,10 +14,20 @@ import lombok.ToString; @ToString(callSuper = true) public class CrmTodayCustomerPageReqVO extends PageParam { - // TODO @dbh52:CrmContactStatusEnum 可以直接枚举三个 Integer;一般来说,枚举类尽量给数据模型用,这样枚举类少,更聚焦;这里的枚举,更多是专门给这个接口用的哈 + /** + * 联系状态 - 今日需联系 + */ + public static final int CONTACT_TODAY = 1; + /** + * 联系状态 - 已逾期 + */ + public static final int CONTACT_EXPIRED = 2; + /** + * 联系状态 - 已联系 + */ + public static final int CONTACT_ALREADY = 3; @Schema(description = "联系状态", example = "1") - @InEnum(CrmContactStatusEnum.class) private Integer contactStatus; @Schema(description = "场景类型", example = "1") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.http new file mode 100644 index 0000000000..b9e9a4edf8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.http @@ -0,0 +1,9 @@ +### 合同金额排行榜 +GET {{baseUrl}}/crm/bi-rank/get-contract-price-rank?deptId=100×[0]=2022-12-12 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +### 回款金额排行榜 +GET {{baseUrl}}/crm/bi-rank/get-receivable-price-rank?deptId=100×[0]=2022-12-12 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.java new file mode 100644 index 0000000000..21463aed09 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.java @@ -0,0 +1,87 @@ +package cn.iocoder.yudao.module.crm.controller.admin.bi; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO; +import cn.iocoder.yudao.module.crm.service.bi.CrmBiRankingService; +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.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + + +@Tag(name = "管理后台 - CRM BI 排行榜") +@RestController +@RequestMapping("/crm/bi-rank") +@Validated +public class CrmBiRankController { + + @Resource + private CrmBiRankingService rankingService; + + @GetMapping("/get-contract-price-rank") + @Operation(summary = "获得合同金额排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getContractPriceRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getContractPriceRank(rankingReqVO)); + } + + @GetMapping("/get-receivable-price-rank") + @Operation(summary = "获得回款金额排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getReceivablePriceRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getReceivablePriceRank(rankingReqVO)); + } + + @GetMapping("/get-contract-count-rank") + @Operation(summary = "获得签约合同数量排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getContractCountRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getContractCountRank(rankingReqVO)); + } + + @GetMapping("/get-product-sales-rank") + @Operation(summary = "获得产品销量排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getProductSalesRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getProductSalesRank(rankingReqVO)); + } + + @GetMapping("/get-customer-count-rank") + @Operation(summary = "获得新增客户数排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getCustomerCountRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getCustomerCountRank(rankingReqVO)); + } + + @GetMapping("/get-contacts-count-rank") + @Operation(summary = "获得新增联系人数排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getContactsCountRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getContactsCountRank(rankingReqVO)); + } + + @GetMapping("/get-follow-count-rank") + @Operation(summary = "获得跟进次数排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getFollowCountRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getFollowCountRank(rankingReqVO)); + } + + @GetMapping("/get-follow-customer-count-rank") + @Operation(summary = "获得跟进客户数排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getFollowCustomerCountRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getFollowCustomerCountRank(rankingReqVO)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRanKRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRanKRespVO.java new file mode 100644 index 0000000000..404ee33520 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRanKRespVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.crm.controller.admin.bi.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Schema(description = "管理后台 - CRM BI 排行榜 Response VO") +@Data +public class CrmBiRanKRespVO { + + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long ownerUserId; + + @Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String nickname; + + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String deptName; + + /** + * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义 + * + * 1. 金额:合同金额排行、回款金额排行 + * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行 + */ + @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer count; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRankReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRankReqVO.java new file mode 100644 index 0000000000..6d36f6d6f7 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRankReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.crm.controller.admin.bi.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - CRM BI 排行榜 Request VO") +@Data +public class CrmBiRankReqVO { + + @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "部门 id 不能为空") + private Long deptId; + + /** + * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 + * + * 后续,可能会支持选择部分用户进行查询 + */ + @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") + private List userIds; + + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @NotEmpty(message = "时间范围不能为空") + private LocalDateTime[] times; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java index 1a25664b06..5742c99987 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java @@ -41,7 +41,7 @@ import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.E import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS; -@Tag(name = "管理后台 - 商机") +@Tag(name = "管理后台 - CRM 商机") @RestController @RequestMapping("/crm/business") @Validated @@ -169,7 +169,7 @@ public class CrmBusinessController { @PutMapping("/transfer") @Operation(summary = "商机转移") @PreAuthorize("@ss.hasPermission('crm:business:update')") - public CommonResult transfer(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) { + public CommonResult transferBusiness(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) { businessService.transferBusiness(reqVO, getLoginUserId()); return success(true); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java index 790cc53fbe..89099a45d0 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java @@ -14,8 +14,8 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusiness import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeRespVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO; -import cn.iocoder.yudao.module.crm.convert.businessstatus.CrmBusinessStatusConvert; -import cn.iocoder.yudao.module.crm.convert.businessstatustype.CrmBusinessStatusTypeConvert; +import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessStatusConvert; +import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessStatusTypeConvert; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService; @@ -40,7 +40,7 @@ import java.util.Set; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; -@Tag(name = "管理后台 - 商机状态类型") +@Tag(name = "管理后台 - CRM 商机状态类型") @RestController @RequestMapping("/crm/business-status-type") @Validated diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java index 7270ae98fe..0be6264eba 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java @@ -1,18 +1,18 @@ package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO; import cn.iocoder.yudao.module.crm.enums.business.CrmBizEndStatus; import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction; import com.mzt.logapi.starter.annotation.DiffLogField; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; -import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -58,7 +58,6 @@ public class CrmBusinessSaveReqVO { @DiffLogField(name = "商机金额") private Integer price; - // TODO @lzxhqs:折扣使用 Integer 类型,存储时,默认 * 100;展示的时候,前端需要 / 100;避免精度丢失问题 @Schema(description = "整单折扣") @DiffLogField(name = "整单折扣") private Integer discountPercent; @@ -75,11 +74,30 @@ public class CrmBusinessSaveReqVO { @InEnum(CrmBizEndStatus.class) private Integer endStatus; - // TODO @lzxhqs:不设置默认 new ArrayList<>();一般 pojo 不设置默认值哈 - @Schema(description = "商机产品列表") - private List products = new ArrayList<>(); - @Schema(description = "联系人编号", example = "110") private Long contactId; // 使用场景,在【联系人详情】添加商机时,如果需要关联两者,需要传递 contactId 字段 + // TODO @puhui999:传递 items 就行啦; + @Schema(description = "产品列表") + private List productItems; + + @Schema(description = "产品列表") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class CrmBusinessProductItem { + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529") + @NotNull(message = "产品编号不能为空") + private Long id; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + @NotNull(message = "产品数量不能为空") + private Integer count; + + @Schema(description = "产品折扣") + private Integer discountPercent; + + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductPageReqVO.java deleted file mode 100644 index 4804768a54..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductPageReqVO.java +++ /dev/null @@ -1,15 +0,0 @@ -package cn.iocoder.yudao.module.crm.controller.admin.business.vo.product; - -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; - -// TODO @lzxhqs:这个类,如果没用到,可以考虑删除哈 -@Schema(description = "管理后台 - 商机产品分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class CrmBusinessProductPageReqVO extends PageParam { -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductRespVO.java deleted file mode 100644 index d4996816f0..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductRespVO.java +++ /dev/null @@ -1,11 +0,0 @@ -package cn.iocoder.yudao.module.crm.controller.admin.business.vo.product; - -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 商机产品关联 Response VO") -@Data -@ExcelIgnoreUnannotated -public class CrmBusinessProductRespVO { -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductSaveReqVO.java deleted file mode 100644 index 4a4cae596d..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductSaveReqVO.java +++ /dev/null @@ -1,51 +0,0 @@ -package cn.iocoder.yudao.module.crm.controller.admin.business.vo.product; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; -import java.math.BigDecimal; - -@Schema(description = "管理后台 - CRM 商机产品关联表 创建/更新 Request VO") -@Data -public class CrmBusinessProductSaveReqVO { - - @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129") - private Long id; - - // TODO @lzxhqs:这个字段,应该是 Long 类型 - @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") - @NotNull(message = "商机编号不能为空") - private Integer businessId; - - // TODO @lzxhqs:这个字段,应该是 Long 类型 - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") - @NotNull(message = "产品编号不能为空") - private Integer productId; - - @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") - @NotNull(message = "产品单价不能为空") - private BigDecimal price; - - @Schema(description = "销售价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") - @NotNull(message = "销售价格不能为空") - private BigDecimal salesPrice; - - @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") - @NotNull(message = "数量不能为空") - private BigDecimal num; - - @Schema(description = "折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") - @NotNull(message = "折扣不能为空") - private BigDecimal discount; - - @Schema(description = "小计(折扣后价格)", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") - @NotNull(message = "小计(折扣后价格)不能为空") - private BigDecimal subtotal; - - // TODO @lzxhqs:字符串,用 @NotEmpty,因为要考虑 "" 前端搞了这个玩意 - @Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") - @NotNull(message = "单位不能为空") - private String unit; - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java index faae0fcef3..be50f54dcc 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java @@ -39,7 +39,7 @@ public class CrmClueController { @Operation(summary = "创建线索") @PreAuthorize("@ss.hasPermission('crm:clue:create')") public CommonResult createClue(@Valid @RequestBody CrmClueSaveReqVO createReqVO) { - return success(clueService.createClue(createReqVO)); + return success(clueService.createClue(createReqVO, getLoginUserId())); } @PutMapping("/update") @@ -91,7 +91,7 @@ public class CrmClueController { @PutMapping("/transfer") @Operation(summary = "线索转移") @PreAuthorize("@ss.hasPermission('crm:clue:update')") - public CommonResult transfer(@Valid @RequestBody CrmClueTransferReqVO reqVO) { + public CommonResult transferClue(@Valid @RequestBody CrmClueTransferReqVO reqVO) { clueService.transferClue(reqVO, getLoginUserId()); return success(true); } @@ -99,7 +99,7 @@ public class CrmClueController { @PostMapping("/transform") @Operation(summary = "线索转化为客户") @PreAuthorize("@ss.hasPermission('crm:clue:update')") - public CommonResult translateCustomer(@Valid @RequestBody CrmClueTransformReqVO reqVO) { + public CommonResult translateCustomer(@Valid @RequestBody CrmClueTranslateReqVO reqVO) { clueService.translateCustomer(reqVO, getLoginUserId()); return success(Boolean.TRUE); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java index 0b6e7a50a0..3ba823d545 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java @@ -30,4 +30,13 @@ public class CrmCluePageReqVO extends PageParam { @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") private Boolean pool; // null 则表示为不是公海数据 + @Schema(description = "所属行业", example = "1") + private Integer industryId; + + @Schema(description = "客户等级", example = "1") + private Integer level; + + @Schema(description = "客户来源", example = "1") + private Integer source; + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java index b9e41ed827..35d30956e1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java @@ -77,4 +77,39 @@ public class CrmClueRespVO { @ExcelProperty("创建时间") private LocalDateTime createTime; + @Schema(description = "所属行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "所属行业", converter = DictConvert.class) + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY) + private Integer industryId; + + @Schema(description = "客户等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "客户等级", converter = DictConvert.class) + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL) + private Integer level; + + @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "客户来源", converter = DictConvert.class) + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE) + private Integer source; + + @Schema(description = "网址", example = "25682") + @ExcelProperty("网址") + private String website; + + @Schema(description = "QQ", example = "25682") + @ExcelProperty("QQ") + private String qq; + + @Schema(description = "wechat", example = "25682") + @ExcelProperty("wechat") + private String wechat; + + @Schema(description = "email", example = "25682") + @ExcelProperty("email") + private String email; + + @Schema(description = "客户描述", example = "25682") + @ExcelProperty("客户描述") + private String description; + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java index 13c9705d3f..4ca004a59a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java @@ -1,15 +1,25 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; +import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.Mobile; import cn.iocoder.yudao.framework.common.validation.Telephone; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerIndustryParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerLevelParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerSourceParseFunction; +import com.mzt.logapi.starter.annotation.DiffLogField; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; -import javax.validation.constraints.NotEmpty; import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY; @Schema(description = "管理后台 - CRM 线索 创建/更新 Request VO") @Data @@ -19,32 +29,77 @@ public class CrmClueSaveReqVO { private Long id; @Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx") + @DiffLogField(name = "线索名称") @NotEmpty(message = "线索名称不能为空") private String name; @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00") + @DiffLogField(name = "下次联系时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime contactNextTime; @Schema(description = "电话", example = "18000000000") + @DiffLogField(name = "电话") @Telephone private String telephone; @Schema(description = "手机号", example = "18000000000") + @DiffLogField(name = "手机号") @Mobile private String mobile; @Schema(description = "地址", example = "北京市海淀区") + @DiffLogField(name = "地址") private String address; @Schema(description = "最后跟进时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @DiffLogField(name = "最后跟进时间") private LocalDateTime contactLastTime; @Schema(description = "负责人编号", example = "2048") private Long ownerUserId; @Schema(description = "备注", example = "随便") + @DiffLogField(name = "备注") private String remark; + @Schema(description = "所属行业", example = "1") + @DiffLogField(name = "所属行业", function = CrmCustomerIndustryParseFunction.NAME) + @DictFormat(CRM_CUSTOMER_INDUSTRY) + private Integer industryId; + + @Schema(description = "客户等级", example = "2") + @DiffLogField(name = "客户等级", function = CrmCustomerLevelParseFunction.NAME) + @InEnum(CrmCustomerLevelEnum.class) + private Integer level; + + @Schema(description = "客户来源", example = "3") + @DiffLogField(name = "客户来源", function = CrmCustomerSourceParseFunction.NAME) + private Integer source; + + @Schema(description = "网址", example = "https://www.baidu.com") + @DiffLogField(name = "网址") + private String website; + + @Schema(description = "QQ", example = "123456789") + @DiffLogField(name = "QQ") + @Size(max = 20, message = "QQ长度不能超过 20 个字符") + private String qq; + + @Schema(description = "微信", example = "123456789") + @DiffLogField(name = "微信") + @Size(max = 255, message = "微信长度不能超过 255 个字符") + private String wechat; + + @Schema(description = "邮箱", example = "123456789@qq.com") + @DiffLogField(name = "邮箱") + @Email(message = "邮箱格式不正确") + @Size(max = 255, message = "邮箱长度不能超过 255 个字符") + private String email; + + @Schema(description = "客户描述", example = "任意文字") + @DiffLogField(name = "客户描述") + @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符") + private String description; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransformReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTranslateReqVO.java similarity index 92% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransformReqVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTranslateReqVO.java index 90701bfe08..271b02dd01 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransformReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTranslateReqVO.java @@ -8,7 +8,7 @@ import java.util.Set; @Schema(description = "管理后台 - 线索转化为客户 Request VO") @Data -public class CrmClueTransformReqVO { +public class CrmClueTranslateReqVO { @Schema(description = "线索编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024, 1025]") @NotEmpty(message = "线索编号不能为空") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java index a1fa44a0aa..587a08aa87 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java @@ -17,9 +17,6 @@ import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService; import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; -import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import com.google.common.collect.Lists; @@ -46,7 +43,6 @@ import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; -import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CONTACT_TYPE; @Tag(name = "管理后台 - CRM 联系人") @RestController @@ -64,8 +60,6 @@ public class CrmContactController { @Resource private AdminUserApi adminUserApi; - @Resource - private OperateLogApi operateLogApi; @PostMapping("/create") @Operation(summary = "创建联系人") @@ -108,7 +102,7 @@ public class CrmContactController { List customerList = customerService.getCustomerList( Collections.singletonList(contact.getCustomerId())); // 3. 直属上级 - List parentContactList = contactService.getContactList( + List parentContactList = contactService.getContactListByIds( Collections.singletonList(contact.getParentId()), getLoginUserId()); return success(CrmContactConvert.INSTANCE.convert(contact, userMap, customerList, parentContactList)); } @@ -118,7 +112,7 @@ public class CrmContactController { @Parameter(name = "ids", description = "编号", required = true, example = "[1024]") @PreAuthorize("@ss.hasPermission('crm:contact:query')") public CommonResult> getContactListByIds(@RequestParam("ids") List ids) { - return success(BeanUtils.toBean(contactService.getContactList(ids, getLoginUserId()), CrmContactRespVO.class)); + return success(BeanUtils.toBean(contactService.getContactListByIds(ids, getLoginUserId()), CrmContactRespVO.class)); } @GetMapping("/simple-all-list") @@ -158,17 +152,6 @@ public class CrmContactController { buildContactDetailPage(pageResult).getList()); } - @GetMapping("/operate-log-page") - @Operation(summary = "获得客户操作日志") - @PreAuthorize("@ss.hasPermission('crm:customer:query')") - public CommonResult> getCustomerOperateLog(@RequestParam("bizId") Long bizId) { - OperateLogV2PageReqDTO reqVO = new OperateLogV2PageReqDTO(); - reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页 - reqVO.setBizType(CRM_CONTACT_TYPE); - reqVO.setBizId(bizId); - return success(operateLogApi.getOperateLogPage(BeanUtils.toBean(reqVO, OperateLogV2PageReqDTO.class))); - } - /** * 构建详细的联系人分页结果 * @@ -187,7 +170,7 @@ public class CrmContactController { Map userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList, contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); // 3. 直属上级 - List parentContactList = contactService.getContactList( + List parentContactList = contactService.getContactListByIds( convertSet(contactList, CrmContactDO::getParentId), getLoginUserId()); return CrmContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList); } @@ -195,7 +178,7 @@ public class CrmContactController { @PutMapping("/transfer") @Operation(summary = "联系人转移") @PreAuthorize("@ss.hasPermission('crm:contact:update')") - public CommonResult transfer(@Valid @RequestBody CrmContactTransferReqVO reqVO) { + public CommonResult transferContact(@Valid @RequestBody CrmContactTransferReqVO reqVO) { contactService.transferContact(reqVO, getLoginUserId()); return success(true); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java index 73cb4fb11f..e1d8ef5736 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java @@ -8,12 +8,22 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.product.CrmProductService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import io.swagger.v3.oas.annotations.Operation; @@ -27,15 +37,16 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; 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.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static java.util.Collections.singletonList; @Tag(name = "管理后台 - CRM 合同") @RestController @@ -47,7 +58,12 @@ public class CrmContractController { private CrmContractService contractService; @Resource private CrmCustomerService customerService; - + @Resource + private CrmContactService contactService; + @Resource + private CrmBusinessService businessService; + @Resource + private CrmProductService productService; @Resource private AdminUserApi adminUserApi; @@ -80,8 +96,15 @@ public class CrmContractController { @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('crm:contract:query')") public CommonResult getContract(@RequestParam("id") Long id) { + // 1. 查询合同 CrmContractDO contract = contractService.getContract(id); - return success(BeanUtils.toBean(contract, CrmContractRespVO.class)); + if (contract == null) { + return success(null); + } + + // 2. 拼接合同信息 + List respVOList = buildContractDetailList(singletonList(contract)); + return success(respVOList.get(0)); } @GetMapping("/page") @@ -89,15 +112,15 @@ public class CrmContractController { @PreAuthorize("@ss.hasPermission('crm:contract:query')") public CommonResult> getContractPage(@Valid CrmContractPageReqVO pageVO) { PageResult pageResult = contractService.getContractPage(pageVO, getLoginUserId()); - return success(buildContractDetailPage(pageResult)); + return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList()))); } @GetMapping("/page-by-customer") - @Operation(summary = "获得联系人分页,基于指定客户") + @Operation(summary = "获得合同分页,基于指定客户") public CommonResult> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) { Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空"); PageResult pageResult = contractService.getContractPageByCustomerId(pageVO); - return success(buildContractDetailPage(pageResult)); + return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList()))); } @GetMapping("/export-excel") @@ -108,36 +131,57 @@ public class CrmContractController { HttpServletResponse response) throws IOException { PageResult pageResult = contractService.getContractPage(exportReqVO, getLoginUserId()); // 导出 Excel - ExcelUtils.write(response, "合同.xls", "数据", CrmContractExcelVO.class, - BeanUtils.toBean(pageResult.getList(), CrmContractExcelVO.class)); - } - - /** - * 构建详细的合同分页结果 - * - * @param pageResult 简单的合同分页结果 - * @return 详细的合同分页结果 - */ - private PageResult buildContractDetailPage(PageResult pageResult) { - List contactList = pageResult.getList(); - if (CollUtil.isEmpty(contactList)) { - return PageResult.empty(pageResult.getTotal()); - } - // 1. 获取客户列表 - List customerList = customerService.getCustomerList( - convertSet(contactList, CrmContractDO::getCustomerId)); - // 2. 获取创建人、负责人列表 - Map userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList, - contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); - return CrmContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList); + ExcelUtils.write(response, "合同.xls", "数据", CrmContractRespVO.class, + BeanUtils.toBean(pageResult.getList(), CrmContractRespVO.class)); } @PutMapping("/transfer") @Operation(summary = "合同转移") @PreAuthorize("@ss.hasPermission('crm:contract:update')") - public CommonResult transfer(@Valid @RequestBody CrmContractTransferReqVO reqVO) { + public CommonResult transferContract(@Valid @RequestBody CrmContractTransferReqVO reqVO) { contractService.transferContract(reqVO, getLoginUserId()); return success(true); } + @PutMapping("/submit") + @Operation(summary = "提交合同审批") + @PreAuthorize("@ss.hasPermission('crm:contract:update')") + public CommonResult submitContract(@RequestParam("id") Long id) { + contractService.submitContract(id, getLoginUserId()); + return success(true); + } + + /** + * 构建详细的合同结果 + * + * @param contractList 原始合同信息 + * @return 细的合同结果 + */ + private List buildContractDetailList(List contractList) { + if (CollUtil.isEmpty(contractList)) { + return Collections.emptyList(); + } + // 1. 获取客户列表 + List customerList = customerService.getCustomerList( + convertSet(contractList, CrmContractDO::getCustomerId)); + // 2. 获取创建人、负责人列表 + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(contractList, + contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); + // 3. 获取联系人 + Map contactMap = convertMap(contactService.getContactListByIds(convertSet(contractList, + CrmContractDO::getContactId)), CrmContactDO::getId); + // 4. 获取商机 + Map businessMap = convertMap(businessService.getBusinessList(convertSet(contractList, + CrmContractDO::getBusinessId)), CrmBusinessDO::getId); + // 5. 获取合同关联的商品 + Map contractProductMap = null; + List productList = null; + if (contractList.size() == 1) { + List contractProductList = contractService.getContractProductListByContractId(contractList.get(0).getId()); + contractProductMap = convertMap(contractProductList, CrmContractProductDO::getProductId); + productList = productService.getProductListByIds(convertSet(contractProductList, CrmContractProductDO::getProductId)); + } + return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap, contractProductMap, productList); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractExcelVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractExcelVO.java deleted file mode 100644 index 72f9f3cfaf..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractExcelVO.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; - -import com.alibaba.excel.annotation.ExcelProperty; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * CRM 合同 Excel VO - * - * @author dhb52 - */ -@Data -public class CrmContractExcelVO { - - @ExcelProperty("合同编号") - private Long id; - - @ExcelProperty("合同名称") - private String name; - - @ExcelProperty("客户编号") - private Long customerId; - - @ExcelProperty("商机编号") - private Long businessId; - - @ExcelProperty("工作流编号") - private Long processInstanceId; - - @ExcelProperty("下单日期") - private LocalDateTime orderDate; - - @ExcelProperty("负责人的用户编号") - private Long ownerUserId; - - @ExcelProperty("合同编号") - private String no; - - @ExcelProperty("开始时间") - private LocalDateTime startTime; - - @ExcelProperty("结束时间") - private LocalDateTime endTime; - - @ExcelProperty("合同金额") - private Integer price; - - @ExcelProperty("整单折扣") - private Integer discountPercent; - - @ExcelProperty("产品总金额") - private Integer productPrice; - - @ExcelProperty("联系人编号") - private Long contactId; - - @ExcelProperty("公司签约人") - private Long signUserId; - - @ExcelProperty("最后跟进时间") - private LocalDateTime contactLastTime; - - @ExcelProperty("备注") - private String remark; - - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java index 94199ada64..c61a64ccf7 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -14,6 +15,15 @@ import lombok.ToString; @ToString(callSuper = true) public class CrmContractPageReqVO extends PageParam { + /** + * 过期类型 - 即将过期 + */ + public static final Integer EXPIRY_TYPE_ABOUT_TO_EXPIRE = 1; + /** + * 过期类型 - 已过期 + */ + public static final Integer EXPIRY_TYPE_EXPIRED = 2; + @Schema(description = "合同编号", example = "XYZ008") private String no; @@ -30,4 +40,11 @@ public class CrmContractPageReqVO extends PageParam { @InEnum(CrmSceneTypeEnum.class) private Integer sceneType; // 场景类型,为 null 时则表示全部 + @Schema(description = "审批状态", example = "20") + @InEnum(CrmAuditStatusEnum.class) + private Integer auditStatus; + + @Schema(description = "过期类型", example = "1") + private Integer expiryType; // 过期类型,为 null 时则表示全部 + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java index 1164f4a0cd..da62394144 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java @@ -3,10 +3,13 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; +import java.util.List; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -26,10 +29,16 @@ public class CrmContractRespVO { @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336") @ExcelProperty("客户编号") private Long customerId; + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336") + @ExcelProperty("客户名称") + private String customerName; @Schema(description = "商机编号", example = "10864") @ExcelProperty("商机编号") private Long businessId; + @Schema(description = "商机名称", example = "10864") + @ExcelProperty("商机名称") + private String businessName; @Schema(description = "工作流编号", example = "1043") @ExcelProperty("工作流编号") @@ -74,10 +83,16 @@ public class CrmContractRespVO { @Schema(description = "联系人编号", example = "18546") @ExcelProperty("联系人编号") private Long contactId; + @Schema(description = "联系人编号", example = "18546") + @ExcelProperty("联系人编号") + private String contactName; @Schema(description = "公司签约人", example = "14036") @ExcelProperty("公司签约人") private Long signUserId; + @Schema(description = "公司签约人", example = "14036") + @ExcelProperty("公司签约人") + private String signUserName; @Schema(description = "最后跟进时间") @ExcelProperty("最后跟进时间") @@ -101,9 +116,10 @@ public class CrmContractRespVO { @ExcelProperty("创建人名字") private String creatorName; - @Schema(description = "客户名字", example = "test") - @ExcelProperty("客户名字") - private String customerName; + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("更新时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime updateTime; @Schema(description = "负责人", example = "test") @ExcelProperty("负责人") @@ -113,4 +129,37 @@ public class CrmContractRespVO { @ExcelProperty("审批状态") private Integer auditStatus; + @Schema(description = "产品列表") + private List productItems; + + // TODO @puhui999:可以直接叫 Item + @Schema(description = "产品列表") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class CrmContractProductItemRespVO { + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529") + private Long id; + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是产品") + private String name; + + @Schema(description = "产品编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "N881") + private String no; + + @Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer unit; + + @Schema(description = "价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer price; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + private Integer count; + + @Schema(description = "产品折扣", example = "99") + private Integer discountPercent; + + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java index f79e241e49..20b20580e7 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java @@ -6,11 +6,14 @@ import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFun import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction; import com.mzt.logapi.starter.annotation.DiffLogField; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; -import javax.validation.constraints.NotNull; import java.time.LocalDateTime; +import java.util.List; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -35,10 +38,6 @@ public class CrmContractSaveReqVO { @DiffLogField(name = "商机", function = CrmBusinessParseFunction.NAME) private Long businessId; - @Schema(description = "工作流编号", example = "1043") - @DiffLogField(name = "工作流编号") - private Long processInstanceId; - @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED) @DiffLogField(name = "下单日期") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @@ -86,16 +85,31 @@ public class CrmContractSaveReqVO { @DiffLogField(name = "公司签约人", function = SysAdminUserParseFunction.NAME) private Long signUserId; - @Schema(description = "最后跟进时间") - @DiffLogField(name = "最后跟进时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime contactLastTime; - @Schema(description = "备注", example = "你猜") @DiffLogField(name = "备注") private String remark; - // TODO @dhb52:增加一个 status 字段:具体有哪些值,你来枚举下;主要页面上有个【草稿】【提交审核】的流程,可以看看。然后要对接工作流,这块也可以看看,不确定的地方问我。 + @Schema(description = "产品列表") + private List productItems; + + @Schema(description = "产品列表") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class CrmContractProductItem { + + @Schema(description = "产品编号", example = "20529") + @NotNull(message = "产品编号不能为空") + private Long id; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + @NotNull(message = "产品数量不能为空") + private Integer count; + + @Schema(description = "产品折扣") + private Integer discountPercent; + + } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java index 243e404ed3..3d7c5d2de0 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java @@ -17,9 +17,6 @@ import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; -import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import io.swagger.v3.oas.annotations.Operation; @@ -35,16 +32,18 @@ import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.IOException; import java.time.LocalDateTime; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; -import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER_TYPE; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED; @Tag(name = "管理后台 - CRM 客户") @RestController @@ -60,8 +59,6 @@ public class CrmCustomerController { private DeptApi deptApi; @Resource private AdminUserApi adminUserApi; - @Resource - private OperateLogApi operateLogApi; @PostMapping("/create") @Operation(summary = "创建客户") @@ -123,6 +120,34 @@ public class CrmCustomerController { return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap)); } + @GetMapping("/put-in-pool-remind-page") + @Operation(summary = "获得待进入公海客户分页") + @PreAuthorize("@ss.hasPermission('crm:customer:query')") + public CommonResult> getPutInPoolRemindCustomerPage(@Valid CrmCustomerPageReqVO pageVO) { + // 获取公海配置 TODO @dbh52:合并到 getPutInPoolRemindCustomerPage 会更合适哈; + CrmCustomerPoolConfigDO poolConfigDO = customerPoolConfigService.getCustomerPoolConfig(); + if (ObjUtil.isNull(poolConfigDO) + || Boolean.FALSE.equals(poolConfigDO.getEnabled()) + || Boolean.FALSE.equals(poolConfigDO.getNotifyEnabled()) + ) { // TODO @dbh52:这个括号,一般不换行,在 java 这里; + throw exception(CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED); + } + + // 1. 查询客户分页 + PageResult pageResult = customerService.getPutInPoolRemindCustomerPage(pageVO, poolConfigDO, getLoginUserId()); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 2. 拼接数据 + // TODO @芋艿:合并 getCustomerPage 和 getPutInPoolRemindCustomerPage 的后置处理; + Map poolDayMap = getPoolDayMap(pageResult.getList()); // 客户界面,需要查看距离进入公海的时间 + Map userMap = adminUserApi.getUserMap( + convertSetByFlatMap(pageResult.getList(), user -> Stream.of(Long.parseLong(user.getCreator()), user.getOwnerUserId()))); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap)); + } + /** * 获取距离进入公海的时间 * @@ -161,6 +186,7 @@ public class CrmCustomerController { new CrmCustomerRespVO().setId(customer.getId()).setName(customer.getName()))); } + // TODO @puhui999:公海的导出,前端可以接下 @GetMapping("/export-excel") @Operation(summary = "导出客户 Excel") @PreAuthorize("@ss.hasPermission('crm:customer:export')") @@ -174,26 +200,39 @@ public class CrmCustomerController { BeanUtils.toBean(list, CrmCustomerRespVO.class)); } + @GetMapping("/get-import-template") + @Operation(summary = "获得导入客户模板") + public void importTemplate(HttpServletResponse response) throws IOException { + // 手动创建导出 demo + List list = Arrays.asList( + CrmCustomerImportExcelVO.builder().name("芋道").industryId(1).level(1).source(1).mobile("15601691300").telephone("") + .website("https://doc.iocoder.cn/").qq("").wechat("").email("yunai@iocoder.cn").description("").remark("") + .areaId(null).detailAddress("").build(), + CrmCustomerImportExcelVO.builder().name("源码").industryId(1).level(1).source(1).mobile("15601691300").telephone("") + .website("https://doc.iocoder.cn/").qq("").wechat("").email("yunai@iocoder.cn").description("").remark("") + .areaId(null).detailAddress("").build() + ); + // 输出 + ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list); + } + + @PostMapping("/import") + @Operation(summary = "导入客户") + @PreAuthorize("@ss.hasPermission('system:customer:import')") + public CommonResult importExcel(@Valid @RequestBody CrmCustomerImportReqVO importReqVO) + throws Exception { + List list = ExcelUtils.read(importReqVO.getFile(), CrmCustomerImportExcelVO.class); + return success(customerService.importCustomerList(list, importReqVO)); + } + @PutMapping("/transfer") @Operation(summary = "转移客户") @PreAuthorize("@ss.hasPermission('crm:customer:update')") - public CommonResult transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { + public CommonResult transferCustomer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { customerService.transferCustomer(reqVO, getLoginUserId()); return success(true); } - @GetMapping("/operate-log-page") - @Operation(summary = "获得客户操作日志") - @Parameter(name = "id", description = "客户编号", required = true) - @PreAuthorize("@ss.hasPermission('crm:customer:query')") - public CommonResult> getCustomerOperateLog(@RequestParam("id") Long id) { - OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO(); - reqDTO.setPageSize(PAGE_SIZE_NONE); // 不分页 - reqDTO.setBizType(CRM_CUSTOMER_TYPE); - reqDTO.setBizId(id); - return success(operateLogApi.getOperateLogPage(reqDTO)); - } - @PutMapping("/lock") @Operation(summary = "锁定/解锁客户") @PreAuthorize("@ss.hasPermission('crm:customer:update')") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportExcelVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportExcelVO.java new file mode 100644 index 0000000000..4f57564dd2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportExcelVO.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; + +/** + * 客户 Excel 导入 VO + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题 +public class CrmCustomerImportExcelVO { + + @ExcelProperty("客户名称") + private String name; + + // TODO @puhui999:industryId、level、source 字段,可以研究下怎么搞下拉框 + @ExcelProperty(value = "所属行业", converter = DictConvert.class) + @DictFormat(CRM_CUSTOMER_INDUSTRY) + private Integer industryId; + + @ExcelProperty(value = "客户等级", converter = DictConvert.class) + @DictFormat(CRM_CUSTOMER_LEVEL) + private Integer level; + + @ExcelProperty(value = "客户来源", converter = DictConvert.class) + @DictFormat(CRM_CUSTOMER_SOURCE) + private Integer source; + + @ExcelProperty("手机") + private String mobile; + + @ExcelProperty("电话") + private String telephone; + + @ExcelProperty("网址") + private String website; + + @ExcelProperty("QQ") + private String qq; + + @ExcelProperty("微信") + private String wechat; + + @ExcelProperty("邮箱") + private String email; + + @ExcelProperty("客户描述") + private String description; + + @ExcelProperty("备注") + private String remark; + + // TODO @puhui999:需要选择省市区,需要研究下,怎么搞合理点; + @ExcelProperty("地区编号") + private Integer areaId; + + @ExcelProperty("详细地址") + private String detailAddress; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportReqVO.java new file mode 100644 index 0000000000..a396dc50b0 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +@Schema(description = "管理后台 - 客户导入 Request VO") +@Data +@Builder +public class CrmCustomerImportReqVO { + + @Schema(description = "Excel 文件", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "Excel 文件不能为空") + private MultipartFile file; + + @Schema(description = "是否支持更新", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否支持更新不能为空") + private Boolean updateSupport; + + @Schema(description = "负责人", example = "1") + private Long ownerUserId; // 为 null 则客户进入公海 + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportRespVO.java new file mode 100644 index 0000000000..de35b7b928 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 客户导入 Response VO") +@Data +@Builder +public class CrmCustomerImportRespVO { + + @Schema(description = "创建成功的客户名数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List createCustomerNames; + + @Schema(description = "更新成功的客户名数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List updateCustomerNames; + + @Schema(description = "导入失败的客户集合,key 为客户名,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) + private Map failureCustomerNames; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java index 09ad826394..88ad9f9c15 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java @@ -75,7 +75,7 @@ public class CrmFollowUpRecordController { public CommonResult> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) { PageResult pageResult = followUpRecordService.getFollowUpRecordPage(pageReqVO); /// 拼接数据 - Map contactMap = convertMap(contactService.getContactList( + Map contactMap = convertMap(contactService.getContactListByIds( convertSetByFlatMap(pageResult.getList(), item -> item.getContactIds().stream())), CrmContactDO::getId); Map businessMap = convertMap(businessService.getBusinessList( convertSetByFlatMap(pageResult.getList(), item -> item.getBusinessIds().stream())), CrmBusinessDO::getId); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java index 8d4b145b6f..83bfd9edc1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java @@ -44,6 +44,11 @@ public class CrmFollowUpRecordRespVO { @Schema(description = "关联的联系人名称数组") private List contactNames; + @Schema(description = "图片") + private List picUrls; + @Schema(description = "附件") + private List fileUrls; + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java index e531129c15..27960a3851 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java @@ -37,8 +37,12 @@ public class CrmFollowUpRecordSaveReqVO { @Schema(description = "关联的商机编号数组") private List businessIds; - @Schema(description = "关联的联系人编号数组") private List contactIds; + @Schema(description = "图片") + private List picUrls; + @Schema(description = "附件") + private List fileUrls; + } \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java new file mode 100644 index 0000000000..982ad3c0b1 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.crm.controller.admin.operatelog; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogV2RespVO; +import cn.iocoder.yudao.module.crm.enums.LogRecordConstants; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; +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.HashMap; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; + +@Tag(name = "管理后台 - CRM 操作日志") +@RestController +@RequestMapping("/crm/operate-log") +@Validated +public class CrmOperateLogController { + + @Resource + private OperateLogApi operateLogApi; + + /** + * {@link CrmBizTypeEnum} 与 {@link LogRecordConstants} 的映射关系 + */ + private static final Map BIZ_TYPE_MAP = new HashMap<>(); + + static { + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_LEADS.getType(), CRM_LEADS_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CUSTOMER.getType(), CRM_CUSTOMER_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTACT.getType(), CRM_CONTACT_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_BUSINESS.getType(), CRM_BUSINESS_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTRACT.getType(), CRM_CONTRACT_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_PRODUCT.getType(), CRM_PRODUCT_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), CRM_RECEIVABLE_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), CRM_RECEIVABLE_PLAN_TYPE); + } + + @GetMapping("/page") + @Operation(summary = "获得操作日志") + @PreAuthorize("@ss.hasPermission('crm:operate-log:query')") + public CommonResult> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) { + OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO(); + reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页,需要分页需注释 + reqDTO.setBizType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId()); + return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO), CrmOperateLogV2RespVO.class)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogPageReqVO.java new file mode 100644 index 0000000000..f49ccb38b2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - CRM 操作日志 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CrmOperateLogPageReqVO extends PageParam { + + @Schema(description = "数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @InEnum(CrmBizTypeEnum.class) + @NotNull(message = "数据类型不能为空") + private Integer bizType; + + @Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "数据编号不能为空") + private Long bizId; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java new file mode 100644 index 0000000000..b3405428fb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - CRM 跟进 Response VO") +@Data +@ExcelIgnoreUnannotated +public class CrmOperateLogV2RespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + private Long id; + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long userId; + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String userName; + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer userType; + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + private String type; + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "修改客户") + private String subType; + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + private Long bizId; + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "将什么从什么改为了什么") + private String action; + + @Schema(description = "编号", example = "{orderId: 1}") + private String extra; + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-01-01") + private LocalDateTime createTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java index 8751bb9374..a0990b3ca4 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.permission; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionCreateReqVO; import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO; import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; @@ -11,6 +12,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.PostApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; @@ -28,6 +30,7 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.validation.Valid; import java.util.*; +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; @@ -54,7 +57,7 @@ public class CrmPermissionController { @PreAuthorize("@ss.hasPermission('crm:permission:create')") @CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) public CommonResult addPermission(@Valid @RequestBody CrmPermissionCreateReqVO reqVO) { - permissionService.createPermission(CrmPermissionConvert.INSTANCE.convert(reqVO)); + permissionService.createPermission(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class)); return success(true); } @@ -103,7 +106,8 @@ public class CrmPermissionController { // 拼接数据 List userList = adminUserApi.getUserList(convertSet(permission, CrmPermissionDO::getUserId)); Map deptMap = deptApi.getDeptMap(convertSet(userList, AdminUserRespDTO::getDeptId)); - Set postIds = CollectionUtils.convertSetByFlatMap(userList, AdminUserRespDTO::getPostIds, Collection::stream); + Set postIds = CollectionUtils.convertSetByFlatMap(userList, AdminUserRespDTO::getPostIds, + item -> item != null ? item.stream() : Stream.empty()); Map postMap = postApi.getPostMap(postIds); return success(CrmPermissionConvert.INSTANCE.convert(permission, userList, deptMap, postMap)); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java index 5565b189c5..3a9f2e4f5c 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java @@ -5,7 +5,6 @@ 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.SetUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; @@ -16,9 +15,6 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; import cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService; import cn.iocoder.yudao.module.crm.service.product.CrmProductService; -import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import io.swagger.v3.oas.annotations.Operation; @@ -38,12 +34,10 @@ 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.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; -import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_PRODUCT_TYPE; @Tag(name = "管理后台 - CRM 产品") @RestController @@ -56,8 +50,6 @@ public class CrmProductController { @Resource private CrmProductCategoryService productCategoryService; @Resource - private OperateLogApi operateLogApi; - @Resource private AdminUserApi adminUserApi; @PostMapping("/create") @@ -131,14 +123,4 @@ public class CrmProductController { return CrmProductConvert.INSTANCE.convertList(list, userMap, productCategoryList); } - @GetMapping("/operate-log-page") - @Operation(summary = "获得产品操作日志") - @PreAuthorize("@ss.hasPermission('crm:product:query')") - public CommonResult> getProductOperateLog(@RequestParam("bizId") Long bizId) { - OperateLogV2PageReqDTO reqVO = new OperateLogV2PageReqDTO(); - reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页 - reqVO.setBizType(CRM_PRODUCT_TYPE).setBizId(bizId); - return success(operateLogApi.getOperateLogPage(BeanUtils.toBean(reqVO, OperateLogV2PageReqDTO.class))); - } - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java index 6497ef4490..252d714f4a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java @@ -25,13 +25,14 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 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.context.annotation.Lazy; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; import java.io.IOException; import java.util.List; import java.util.Map; @@ -55,6 +56,7 @@ public class CrmReceivablePlanController { @Resource private CrmReceivableService receivableService; @Resource + @Lazy private CrmContractService contractService; @Resource private CrmCustomerService customerService; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java index f86aa346de..3675fba1f6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java @@ -14,6 +14,19 @@ import lombok.ToString; @ToString(callSuper = true) public class CrmReceivablePlanPageReqVO extends PageParam { + /** + * 提醒类型 - 待回款 + */ + public final static Integer REMIND_TYPE_NEEDED = 1; + /** + * 提醒类型 - 已逾期 + */ + public final static Integer REMIND_TYPE_EXPIRED = 2; + /** + * 提醒类型 - 已回款 + */ + public final static Integer REMIND_TYPE_RECEIVED = 3; + @Schema(description = "客户编号", example = "18026") private Long customerId; @@ -25,4 +38,7 @@ public class CrmReceivablePlanPageReqVO extends PageParam { @InEnum(CrmSceneTypeEnum.class) private Integer sceneType; // 场景类型,为 null 时则表示全部 + @Schema(description = "提醒类型", example = "1") + private Integer remindType; // 提醒类型,为 null 时则表示全部 + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java index 1bca32fa32..e1fe83087b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -27,4 +28,8 @@ public class CrmReceivablePageReqVO extends PageParam { @InEnum(CrmSceneTypeEnum.class) private Integer sceneType; // 场景类型,为 null 时则表示全部 + @Schema(description = "审批状态", example = "20") + @InEnum(CrmAuditStatusEnum.class) + private Integer auditStatus; + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java index 63c8ab7d6e..d7f990043e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.crm.convert.business; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; @@ -8,6 +9,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -45,4 +47,11 @@ public interface CrmBusinessConvert { return voPageResult; } + @Mapping(target = "id", source = "reqBO.bizId") + CrmBusinessDO convert(CrmUpdateFollowUpReqBO reqBO); + + default List convertList(List list) { + return CollectionUtils.convertList(list, INSTANCE::convert); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessstatus/CrmBusinessStatusConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusConvert.java similarity index 92% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessstatus/CrmBusinessStatusConvert.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusConvert.java index df2532b27a..52186e3d93 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessstatus/CrmBusinessStatusConvert.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusConvert.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.crm.convert.businessstatus; +package cn.iocoder.yudao.module.crm.convert.business; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusRespVO; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessstatustype/CrmBusinessStatusTypeConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusTypeConvert.java similarity index 96% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessstatustype/CrmBusinessStatusTypeConvert.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusTypeConvert.java index be203b5803..4876fb5377 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessstatustype/CrmBusinessStatusTypeConvert.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusTypeConvert.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.crm.convert.businessstatustype; +package cn.iocoder.yudao.module.crm.convert.business; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeRespVO; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessproduct/CrmBusinessProductConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessproduct/CrmBusinessProductConvert.java deleted file mode 100644 index 2fcd54d841..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessproduct/CrmBusinessProductConvert.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.crm.convert.businessproduct; - -import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO; -import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -// TODO @lzxhqs:看看是不是用 BeanUtils 替代了 -/** - * @author lzxhqs - * @version 1.0 - * @title CrmBusinessProductConvert - * @description - * @create 2024/1/12 - */ -@Mapper -public interface CrmBusinessProductConvert { - CrmBusinessProductConvert INSTANCE = Mappers.getMapper(CrmBusinessProductConvert.class); - - CrmBusinessProductDO convert(CrmBusinessProductSaveReqVO product); -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java index 363fd4f600..5fd2afa67e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactRespVO; import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import org.mapstruct.Mapper; @@ -66,4 +67,11 @@ public interface CrmContactConvert { findAndThen(userMap, Long.parseLong(contactRespVO.getCreator()), user -> contactRespVO.setCreatorName(user.getNickname())); } + @Mapping(target = "id", source = "reqBO.bizId") + CrmContactDO convert(CrmUpdateFollowUpReqBO reqBO); + + default List convertList(List list) { + return CollectionUtils.convertList(list, INSTANCE::convert); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java index 599d998a67..0d2e499341 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java @@ -1,11 +1,16 @@ package cn.iocoder.yudao.module.crm.convert.contract; -import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import org.mapstruct.Mapper; @@ -31,17 +36,35 @@ public interface CrmContractConvert { @Mapping(target = "bizId", source = "reqVO.id") CrmPermissionTransferReqBO convert(CrmContractTransferReqVO reqVO, Long userId); - default PageResult convertPage(PageResult pageResult, Map userMap, - List customerList) { - PageResult voPageResult = BeanUtils.toBean(pageResult, CrmContractRespVO.class); + default List convertList(List contractList, Map userMap, + List customerList, Map contactMap, + Map businessMap, Map contractProductMap, + List productList) { + List respVOList = BeanUtils.toBean(contractList, CrmContractRespVO.class); // 拼接关联字段 Map customerMap = convertMap(customerList, CrmCustomerDO::getId); - voPageResult.getList().forEach(contract -> { + respVOList.forEach(contract -> { findAndThen(userMap, contract.getOwnerUserId(), user -> contract.setOwnerUserName(user.getNickname())); findAndThen(userMap, Long.parseLong(contract.getCreator()), user -> contract.setCreatorName(user.getNickname())); + findAndThen(userMap, contract.getSignUserId(), user -> contract.setSignUserName(user.getNickname())); findAndThen(customerMap, contract.getCustomerId(), customer -> contract.setCustomerName(customer.getName())); + findAndThen(contactMap, contract.getContactId(), contact -> contract.setContactName(contact.getName())); + findAndThen(businessMap, contract.getBusinessId(), business -> contract.setBusinessName(business.getName())); }); - return voPageResult; + if (CollUtil.isNotEmpty(respVOList) && respVOList.size() == 1) { + setContractRespVOProductItems(respVOList.get(0), contractProductMap, productList); + } + return respVOList; + } + + default void setContractRespVOProductItems(CrmContractRespVO respVO, Map contractProductMap, + List productList) { + respVO.setProductItems(CollectionUtils.convertList(productList, product -> { + CrmContractRespVO.CrmContractProductItemRespVO productItemRespVO = BeanUtils.toBean(product, CrmContractRespVO.CrmContractProductItemRespVO.class); + findAndThen(contractProductMap, product.getId(), contractProduct -> + productItemRespVO.setCount(contractProduct.getCount()).setDiscountPercent(contractProduct.getDiscountPercent())); + return productItemRespVO; + })); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java index 5b81e67ade..f51544caea 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java @@ -3,12 +3,10 @@ package cn.iocoder.yudao.module.crm.convert.permission; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionCreateReqVO; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO; import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; -import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; -import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionUpdateReqBO; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; @@ -32,29 +30,19 @@ public interface CrmPermissionConvert { CrmPermissionConvert INSTANCE = Mappers.getMapper(CrmPermissionConvert.class); - CrmPermissionDO convert(CrmPermissionCreateReqBO createBO); - - CrmPermissionDO convert(CrmPermissionUpdateReqBO updateBO); - - CrmPermissionCreateReqBO convert(CrmPermissionCreateReqVO reqVO); - - CrmPermissionUpdateReqBO convert(CrmPermissionUpdateReqVO updateReqVO); - - List convert(List permission); - - default List convert(List permission, List userList, + default List convert(List permissions, List userList, Map deptMap, Map postMap) { Map userMap = CollectionUtils.convertMap(userList, AdminUserRespDTO::getId); - return CollectionUtils.convertList(convert(permission), item -> { + return CollectionUtils.convertList(BeanUtils.toBean(permissions, CrmPermissionRespVO.class), item -> { findAndThen(userMap, item.getUserId(), user -> { item.setNickname(user.getNickname()); findAndThen(deptMap, user.getDeptId(), deptRespDTO -> item.setDeptName(deptRespDTO.getName())); - List postRespList = MapUtils.getList(Multimaps.forMap(postMap), user.getPostIds()); - if (CollUtil.isEmpty(postRespList)) { + if (CollUtil.isEmpty(user.getPostIds())) { item.setPostNames(Collections.emptySet()); return; } - item.setPostNames(CollectionUtils.convertSet(postRespList, PostRespDTO::getName)); + List postList = MapUtils.getList(Multimaps.forMap(postMap), user.getPostIds()); + item.setPostNames(CollectionUtils.convertSet(postList, PostRespDTO::getName)); }); return item; }); @@ -65,6 +53,4 @@ public interface CrmPermissionConvert { id -> new CrmPermissionDO().setId(id).setLevel(updateReqVO.getLevel())); } - List convertList(List list); - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java index e11ec59350..3a543712b1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java @@ -95,10 +95,8 @@ public class CrmBusinessDO extends BaseDO { private LocalDateTime contactLastTime; /** * 跟进状态 - * - * TODO @lzxhqs:目前就是 Boolean;是否跟进 */ - private Integer followUpStatus; + private Boolean followUpStatus; /** * 负责人的用户编号 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java index 4558684e9e..79d6a2a7b2 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java @@ -7,8 +7,6 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; -import java.math.BigDecimal; - /** * 商机产品关联表 DO * @@ -29,52 +27,37 @@ public class CrmBusinessProductDO extends BaseDO { */ @TableId private Long id; - /** * 商机编号 * * 关联 {@link CrmBusinessDO#getId()} */ private Long businessId; - /** * 产品编号 * * 关联 {@link CrmProductDO#getId()} */ private Long productId; - - // TODO @lzxhqs:改成 Integer,单位:分。目前整体倾向放大 100 倍哈 /** * 产品单价 */ - private BigDecimal price; - + private Integer price; /** - * 销售价格 + * 销售价格, 单位:分 */ - private BigDecimal salesPrice; - - // TODO @lzxhqs:改成 count + private Integer salesPrice; /** * 数量 */ - private BigDecimal num; - - // TODO @lzxhqs:改成 discountPercent + private Integer count; /** * 折扣 */ - private BigDecimal discount; - - // TODO @lzxhqs:改成 totalPrice;总计价格,和现有项目风格一致; + private Integer discountPercent; /** - * 小计(折扣后价格) + * 总计价格(折扣后价格) */ - private BigDecimal subtotal; + private Integer totalPrice; - /** - * 单位 - */ - private String unit; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java index 7d85c6ac1c..5ea1dea287 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.clue; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -72,16 +73,44 @@ public class CrmClueDO extends BaseDO { * 备注 */ private String remark; - /** * 负责人的用户编号 - * * 关联 AdminUserDO 的 id 字段 */ private Long ownerUserId; - - // TODO 芋艿:客户级别; - // TODO 芋艿:线索来源; - // TODO 芋艿:客户行业; - + /** + * 所属行业 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY} + */ + private Integer industryId; + /** + * 客户等级 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL} + */ + private Integer level; + /** + * 客户来源 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE} + */ + private Integer source; + /** + * 网址 + */ + private String website; + /** + * QQ + */ + private String qq; + /** + * wechat + */ + private String wechat; + /** + * email + */ + private String email; + /** + * 客户描述 + */ + private String description; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java index bdaff72712..0c01a33943 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.contract; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; @@ -30,30 +33,36 @@ public class CrmContractDO extends BaseDO { */ @TableId private Long id; + /** + * 合同编号 + */ + private String no; /** * 合同名称 */ private String name; /** * 客户编号 + * + * 关联 {@link CrmCustomerDO#getId()} */ private Long customerId; /** * 商机编号 + * + * 关联 {@link CrmBusinessDO#getId()} */ private Long businessId; /** * 工作流编号 + * + * 关联 ProcessInstance 的 id 属性 */ - private Long processInstanceId; + private String processInstanceId; /** * 下单日期 */ private LocalDateTime orderDate; - /** - * 合同编号 - */ - private String no; /** * 开始时间 */ @@ -63,7 +72,7 @@ public class CrmContractDO extends BaseDO { */ private LocalDateTime endTime; /** - * 合同金额 + * 合同金额,单位:分 */ private Integer price; /** @@ -71,15 +80,19 @@ public class CrmContractDO extends BaseDO { */ private Integer discountPercent; /** - * 产品总金额 + * 产品总金额,单位:分 */ private Integer productPrice; /** - * 联系人编号 + * 客户签约人 + * + * 关联 {@link CrmContactDO#getId()} */ private Long contactId; /** * 公司签约人 + * + * 关联 AdminUserDO 的 id 字段 */ private Long signUserId; /** diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java new file mode 100644 index 0000000000..bc977c78f3 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.contract; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 合同产品关联表 DO + * + * @author HUIHUI + */ +@TableName("crm_contract_product") +@KeySequence("crm_contract_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmContractProductDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 产品编号 + * + * 关联 {@link CrmProductDO#getId()} + */ + private Long productId; + /** + * 合同编号 + * + * 关联 {@link CrmContractDO#getId()} + */ + private Long contractId; + /** + * 产品单价 + */ + private Integer price; + /** + * 销售价格, 单位:分 + */ + private Integer salesPrice; + /** + * 数量 + */ + private Integer count; + /** + * 折扣 + */ + private Integer discountPercent; + /** + * 总计价格(折扣后价格) + * + * TODO @puhui999:可以写下计算公式哈; + */ + private Integer totalPrice; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/package-info.java deleted file mode 100644 index a981b5dfc5..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 合同 - */ -package cn.iocoder.yudao.module.crm.dal.dataobject.contract; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java index 96e4bf5200..2bd614f571 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java @@ -114,10 +114,15 @@ public class CrmCustomerDO extends BaseDO { */ private String detailAddress; + /** + * 最后接收时间 + */ + private LocalDateTime receiveTime; /** * 最后跟进时间 */ private LocalDateTime contactLastTime; + /** * 最后跟进内容 */ diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java index 896ad0297a..a01d47af9b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.followup; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; @@ -66,6 +67,17 @@ public class CrmFollowUpRecordDO extends BaseDO { */ private LocalDateTime nextTime; + /** + * 图片 + */ + @TableField(typeHandler = StringListTypeHandler.class) + private List picUrls; + /** + * 附件 + */ + @TableField(typeHandler = StringListTypeHandler.class) + private List fileUrls; + /** * 关联的商机编号数组 * @@ -81,4 +93,5 @@ public class CrmFollowUpRecordDO extends BaseDO { @TableField(typeHandler = LongListTypeHandler.class) private List contactIds; + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java index a4ac3d3d71..a3c56ccc9b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.product; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; import cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; @@ -38,13 +39,13 @@ public class CrmProductDO extends BaseDO { /** * 单位 * - * 字典 {@link cn.iocoder.yudao.module.crm.enums.DictTypeConstants#CRM_PRODUCT_UNIT} + * 字典 {@link DictTypeConstants#CRM_PRODUCT_UNIT} */ private Integer unit; /** * 价格,单位:分 */ - private Long price; + private Integer price; /** * 状态 * diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/bi/CrmBiRankingMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/bi/CrmBiRankingMapper.java new file mode 100644 index 0000000000..9b71df7b69 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/bi/CrmBiRankingMapper.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.bi; + +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * CRM BI 排行榜 Mapper + * + * @author anhaohao + */ +@Mapper +public interface CrmBiRankingMapper { + + /** + * 查询合同金额排行榜 + * + * @param rankReqVO 参数 + * @return 合同金额排行榜 + */ + List selectContractPriceRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询回款金额排行榜 + * + * @param rankReqVO 参数 + * @return 回款金额排行榜 + */ + List selectReceivablePriceRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询签约合同数量排行榜 + * + * @param rankReqVO 参数 + * @return 签约合同数量排行榜 + */ + List selectContractCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询产品销量排行榜 + * + * @param rankReqVO 参数 + * @return 产品销量排行榜 + */ + List selectProductSalesRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询新增客户数排行榜 + * + * @param rankReqVO 参数 + * @return 新增客户数排行榜 + */ + List selectCustomerCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询联系人数量排行榜 + * + * @param rankReqVO 参数 + * @return 联系人数量排行榜 + */ + List selectContactsCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询跟进次数排行榜 + * + * @param rankReqVO 参数 + * @return 跟进次数排行榜 + */ + List selectFollowCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询跟进客户数排行榜 + * + * @param rankReqVO 参数 + * @return 跟进客户数排行榜 + */ + List selectFollowCustomerCountRank(CrmBiRankReqVO rankReqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java index d013520078..d7438484f1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java @@ -58,6 +58,8 @@ public interface CrmBusinessMapper extends BaseMapperX { MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); // 拼接数据权限的查询条件 CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), ids, userId); + // 拼接自身的查询条件 + query.selectAll(CrmBusinessDO.class).in(CrmBusinessDO::getId, ids).orderByDesc(CrmBusinessDO::getId); return selectJoinList(CrmBusinessDO.class, query); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java index 5750491d88..2d1471c73d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java @@ -2,20 +2,32 @@ package cn.iocoder.yudao.module.crm.dal.mysql.business; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** - * 商机产品 Mapper // TODO @lzxhqs:类注释,和作者之间要有一个空行 + * 商机产品 Mapper + * * @author lzxhqs */ @Mapper public interface CrmBusinessProductMapper extends BaseMapperX { - default void deleteByBusinessId(Long id) { // TODO @lzxhqs:第一个方法,和类之间最好空一行; - delete(CrmBusinessProductDO::getBusinessId, id); + + // TODO @puhui999:用不到的方法,看看是不是删除哈 + default void deleteByBusinessId(Long getBusinessId) { // TODO @lzxhqs:第一个方法,和类之间最好空一行; + delete(CrmBusinessProductDO::getBusinessId, getBusinessId); } - default CrmBusinessProductDO selectByBusinessId(Long id) { // TODO @lzxhqs:id 最好改成 businessId,上面也是;这样一看更容易懂 - return selectOne(CrmBusinessProductDO::getBusinessId, id); + default CrmBusinessProductDO selectByBusinessId(Long getBusinessId) { + return selectOne(CrmBusinessProductDO::getBusinessId, getBusinessId); } + + default List selectListByBusinessId(Long businessId) { + // TODO @puhui999:可以简化,selectList(CrmBusinessProductDO::getBusinessId, businessId) + return selectList(new LambdaQueryWrapperX().eq(CrmBusinessProductDO::getBusinessId, businessId)); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java index 2253e69f6c..cab4c46625 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java @@ -37,6 +37,9 @@ public interface CrmClueMapper extends BaseMapperX { .likeIfPresent(CrmClueDO::getName, pageReqVO.getName()) .likeIfPresent(CrmClueDO::getTelephone, pageReqVO.getTelephone()) .likeIfPresent(CrmClueDO::getMobile, pageReqVO.getMobile()) + .eqIfPresent(CrmClueDO::getIndustryId, pageReqVO.getIndustryId()) + .eqIfPresent(CrmClueDO::getLevel, pageReqVO.getLevel()) + .eqIfPresent(CrmClueDO::getSource, pageReqVO.getSource()) .orderByDesc(CrmClueDO::getId); return selectJoinPage(pageReqVO, CrmClueDO.class, query); } @@ -45,6 +48,8 @@ public interface CrmClueMapper extends BaseMapperX { MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); // 拼接数据权限的查询条件 CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(), ids, userId); + query.selectAll(CrmClueDO.class).in(CrmClueDO::getId, ids).orderByDesc(CrmClueDO::getId); + // 拼接自身的查询条件 return selectJoinList(CrmClueDO.class, query); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java index 8b2fb76bdd..c35df47d33 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java @@ -67,6 +67,8 @@ public interface CrmContactMapper extends BaseMapperX { MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); // 拼接数据权限的查询条件 CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId); + // 拼接自身的查询条件 + query.selectAll(CrmContactDO.class).in(CrmContactDO::getId, ids).orderByDesc(CrmContactDO::getId); return selectJoinList(CrmContactDO.class, query); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java index eb21a3d5b8..3ecd93fcb3 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java @@ -1,16 +1,19 @@ package cn.iocoder.yudao.module.crm.dal.mysql.contract; +import cn.hutool.core.date.LocalDateTimeUtil; 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.crm.controller.admin.contract.vo.CrmContractPageReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; +import java.time.LocalDateTime; import java.util.Collection; import java.util.List; @@ -39,32 +42,49 @@ public interface CrmContractMapper extends BaseMapperX { } default PageResult selectPage(CrmContractPageReqVO pageReqVO, Long userId) { - MPJLambdaWrapperX mpjLambdaWrapperX = new MPJLambdaWrapperX<>(); + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); // 拼接数据权限的查询条件 - CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(), CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); // 拼接自身的查询条件 - mpjLambdaWrapperX.selectAll(CrmContractDO.class) + query.selectAll(CrmContractDO.class) .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo()) .likeIfPresent(CrmContractDO::getName, pageReqVO.getName()) .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId()) .eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId()) + .eqIfPresent(CrmContractDO::getAuditStatus, pageReqVO.getAuditStatus()) .orderByDesc(CrmContractDO::getId); - return selectJoinPage(pageReqVO, CrmContractDO.class, mpjLambdaWrapperX); + + // Backlog: 即将到期的合同 + LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now()); + LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now()); + if (CrmContractPageReqVO.EXPIRY_TYPE_ABOUT_TO_EXPIRE.equals(pageReqVO.getExpiryType())) { // 即将到期 + // TODO: @芋艿 需要配置 提前提醒天数 + int REMIND_DAYS = 20; + query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus()) + .between(CrmContractDO::getEndTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS)); + } else if (CrmContractPageReqVO.EXPIRY_TYPE_EXPIRED.equals(pageReqVO.getExpiryType())) { // 已到期 + query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus()) + .lt(CrmContractDO::getEndTime, endOfToday); + } + return selectJoinPage(pageReqVO, CrmContractDO.class, query); } default List selectBatchIds(Collection ids, Long userId) { - MPJLambdaWrapperX mpjLambdaWrapperX = new MPJLambdaWrapperX<>(); + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); // 构建数据权限连表条件 - CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId); - return selectJoinList(CrmContractDO.class, mpjLambdaWrapperX); + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(), ids, userId); + // 拼接自身的查询条件 + query.selectAll(CrmContractDO.class).in(CrmContractDO::getId, ids).orderByDesc(CrmContractDO::getId); + return selectJoinList(CrmContractDO.class, query); } default Long selectCountByContactId(Long contactId) { return selectCount(CrmContractDO::getContactId, contactId); } - default CrmContractDO selectByBizId(Long businessId) { // TODO @lzxhqs:1)方法和方法之间要有空行;2)selectCountByBusinessId,一个是应该求数量,一个是不要缩写 BizId 可读性; - return selectOne(CrmContractDO::getBusinessId, businessId); + + default Long selectCountByBusinessId(Long businessId) { + return selectCount(CrmContractDO::getBusinessId, businessId); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractProductMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractProductMapper.java new file mode 100644 index 0000000000..814024125b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractProductMapper.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.contract; + + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 合同产品 Mapper + * + * @author HUIHUI + */ +@Mapper +public interface CrmContractProductMapper extends BaseMapperX { + + // TODO @puhui999:用不到的方法,看看是不是删除哈 + default void deleteByContractId(Long contractId) { // TODO @lzxhqs:第一个方法,和类之间最好空一行; + delete(CrmContractProductDO::getContractId, contractId); + } + + default CrmContractProductDO selectByContractId(Long contractId) { + return selectOne(CrmContractProductDO::getContractId, contractId); + } + + default List selectListByContractId(Long contractId) { + return selectList(new LambdaQueryWrapperX().eq(CrmContractProductDO::getContractId, contractId)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java index 125249d144..29cd47c663 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java @@ -1,21 +1,22 @@ package cn.iocoder.yudao.module.crm.dal.mysql.customer; +import cn.hutool.core.date.LocalDateTimeUtil; 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.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.message.vo.CrmTodayCustomerPageReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; -import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; -import cn.iocoder.yudao.module.crm.enums.message.CrmContactStatusEnum; import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; import org.springframework.lang.Nullable; -import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Collection; import java.util.List; @@ -64,6 +65,8 @@ public interface CrmCustomerMapper extends BaseMapperX { MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); // 拼接数据权限的查询条件 CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, userId); + // 拼接自身的查询条件 + query.selectAll(CrmCustomerDO.class).in(CrmCustomerDO::getId, ids).orderByDesc(CrmCustomerDO::getId); return selectJoinList(CrmCustomerDO.class, query); } @@ -80,43 +83,67 @@ public interface CrmCustomerMapper extends BaseMapperX { CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), null); - query.selectAll(CrmCustomerDO.class) - .leftJoin(CrmFollowUpRecordDO.class, CrmFollowUpRecordDO::getBizId, CrmCustomerDO::getId) - .eq(CrmFollowUpRecordDO::getType, CrmBizTypeEnum.CRM_CUSTOMER.getType()); + // 拼接自身的查询条件 + query.selectAll(CrmCustomerDO.class); + if (pageReqVO.getContactStatus() != null) { + LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now()); + LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now()); + if (pageReqVO.getContactStatus().equals(CrmTodayCustomerPageReqVO.CONTACT_TODAY)) { // 今天需联系 + query.between(CrmCustomerDO::getContactNextTime, beginOfToday, endOfToday); + } else if (pageReqVO.getContactStatus().equals(CrmTodayCustomerPageReqVO.CONTACT_EXPIRED)) { // 已逾期 + query.lt(CrmCustomerDO::getContactNextTime, beginOfToday); + } else if (pageReqVO.getContactStatus().equals(CrmTodayCustomerPageReqVO.CONTACT_ALREADY)) { // 已联系 + query.between(CrmCustomerDO::getContactLastTime, beginOfToday, endOfToday); + } else { + throw new IllegalArgumentException("未知联系状态:" + pageReqVO.getContactStatus()); + } + } + return selectJoinPage(pageReqVO, CrmCustomerDO.class, query); + } + + default List selectListByLockAndNotPool(Boolean lockStatus) { + return selectList(new LambdaQueryWrapper() + .eq(CrmCustomerDO::getLockStatus, lockStatus) + .gt(CrmCustomerDO::getOwnerUserId, 0)); + } + + default CrmCustomerDO selectByCustomerName(String name) { + return selectOne(CrmCustomerDO::getName, name); + } + + default PageResult selectPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO, + CrmCustomerPoolConfigDO poolConfigDO, + Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数据权限的查询条件 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), + CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), null); + // TODO @dhb52:lock 的情况,不需要提醒哈; // 拼接自身的查询条件 - // TODO @dbh52:这里不仅仅要获得 today、tomorrow。而是 today 要获取今天的 00:00:00 这种; - LocalDate today = LocalDate.now(); - LocalDate tomorrow = today.plusDays(1); - LocalDate yesterday = today.minusDays(1); - if (pageReqVO.getContactStatus().equals(CrmContactStatusEnum.NEEDED_TODAY.getType())) { - // 今天需联系: - // 1.【客户】的【下一次联系时间】 是【今天】 - // 2. 无法找到【今天】创建的【跟进】记录 - query.between(CrmCustomerDO::getContactNextTime, today, tomorrow) - // TODO @dbh52:是不是查询 CrmCustomerDO::contactLastTime < today?因为今天联系过,应该会更新该字段,减少链表查询; - .between(CrmFollowUpRecordDO::getCreateTime, today, tomorrow) - .isNull(CrmFollowUpRecordDO::getId); - } else if (pageReqVO.getContactStatus().equals(CrmContactStatusEnum.EXPIRED.getType())) { - // 已逾期: - // 1. 【客户】的【下一次联系时间】 <= 【昨天】 - // 2. 无法找到【今天】创建的【跟进】记录 - // TODO @dbh52:是不是 contactNextTime 在当前时间之前,且 contactLastTime < contactNextTime?说白了,就是下次联系时间超过当前时间,且最后联系时间没去联系; - query.le(CrmCustomerDO::getContactNextTime, yesterday) - .between(CrmFollowUpRecordDO::getCreateTime, today, tomorrow) - .isNull(CrmFollowUpRecordDO::getId); - } else if (pageReqVO.getContactStatus().equals(CrmContactStatusEnum.ALREADY_CONTACT.getType())) { - // 已联系: - // 1.【客户】的【下一次联系时间】 是【今天】 - // 2. 找到【今天】创建的【跟进】记录 - query.between(CrmCustomerDO::getContactNextTime, today, tomorrow) - // TODO @dbh52:contactLastTime 是今天 - .between(CrmFollowUpRecordDO::getCreateTime, today, tomorrow) - .isNotNull(CrmFollowUpRecordDO::getId); - } else { - // TODO: 参数错误,是不是要兜一下底;直接抛出异常就好啦; - } - + query.selectAll(CrmCustomerDO.class); + // 情况一:未成交提醒日期区间 + Integer dealExpireDays = poolConfigDO.getDealExpireDays(); + LocalDateTime startDealRemindDate = LocalDateTimeUtil.beginOfDay(LocalDateTime.now()) + .minusDays(dealExpireDays); + LocalDateTime endDealRemindDate = LocalDateTimeUtil.endOfDay(LocalDateTime.now()) + .minusDays(Math.max(dealExpireDays - poolConfigDO.getNotifyDays(), 0)); + // 情况二:未跟进提醒日期区间 + Integer contactExpireDays = poolConfigDO.getContactExpireDays(); + LocalDateTime startContactRemindDate = LocalDateTimeUtil.beginOfDay(LocalDateTime.now()) + .minusDays(contactExpireDays); + LocalDateTime endContactRemindDate = LocalDateTimeUtil.endOfDay(LocalDateTime.now()) + .minusDays(Math.max(contactExpireDays - poolConfigDO.getNotifyDays(), 0)); + query + // 情况一:1. 未成交放入公海提醒 + .eq(CrmCustomerDO::getDealStatus, false) + .between(CrmCustomerDO::getCreateTime, startDealRemindDate, endDealRemindDate) + // 情况二:未跟进放入公海提醒 + .or() // 2.1 contactLastTime 为空 TODO 芋艿:这个要不要搞个默认值; + .isNull(CrmCustomerDO::getContactLastTime) + .between(CrmCustomerDO::getCreateTime, startContactRemindDate, endContactRemindDate) + .or() // 2.2 ContactLastTime 不为空 + .between(CrmCustomerDO::getContactLastTime, startContactRemindDate, endContactRemindDate); return selectJoinPage(pageReqVO, CrmCustomerDO.class, query); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java index a9b1dc315b..45e2b412ed 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java @@ -7,6 +7,9 @@ import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecor import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; import org.apache.ibatis.annotations.Mapper; +import java.util.Collection; +import java.util.List; + /** * 跟进记录 Mapper * @@ -22,4 +25,16 @@ public interface CrmFollowUpRecordMapper extends BaseMapperX() + .eq(CrmFollowUpRecordDO::getBizType, bizType) + .eq(CrmFollowUpRecordDO::getBizId, bizId)); + } + + default List selectListByBiz(Integer bizType, Collection bizIds) { + return selectList(new LambdaQueryWrapperX() + .eq(CrmFollowUpRecordDO::getBizType, bizType) + .in(CrmFollowUpRecordDO::getBizId, bizIds)); + } + } \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java index 71c0368ca1..26f212e5eb 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java @@ -49,8 +49,13 @@ public interface CrmPermissionMapper extends BaseMapperX { } default CrmPermissionDO selectByIdAndUserId(Long id, Long userId) { - return selectOne(new LambdaQueryWrapperX() - .eq(CrmPermissionDO::getId, id).eq(CrmPermissionDO::getUserId, userId)); + return selectOne(CrmPermissionDO::getId, id, + CrmPermissionDO::getUserId, userId); + } + + default CrmPermissionDO selectByBizIdAndUserId(Long bizId, Long userId) { + return selectOne(CrmPermissionDO::getBizId, bizId, + CrmPermissionDO::getUserId, userId); } default int deletePermission(Integer bizType, Long bizId) { diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java index a20da86a63..28b2298ec7 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java @@ -45,6 +45,7 @@ public interface CrmReceivableMapper extends BaseMapperX { query.selectAll(CrmReceivableDO.class) .eqIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo()) .eqIfPresent(CrmReceivableDO::getPlanId, pageReqVO.getPlanId()) + .eqIfPresent(CrmReceivableDO::getAuditStatus, pageReqVO.getAuditStatus()) .orderByDesc(CrmReceivableDO::getId); return selectJoinPage(pageReqVO, CrmReceivableDO.class, query); } @@ -53,6 +54,8 @@ public interface CrmReceivableMapper extends BaseMapperX { MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); // 拼接数据权限的查询条件 CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(), ids, userId); + // 拼接自身的查询条件 + query.selectAll(CrmReceivableDO.class).in(CrmReceivableDO::getId, ids).orderByDesc(CrmReceivableDO::getId); return selectJoinList(CrmReceivableDO.class, query); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java index ed578f1fd2..b21d30c126 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.crm.dal.mysql.receivable; +import cn.hutool.core.date.LocalDateTimeUtil; 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; @@ -11,6 +12,7 @@ import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; +import java.time.LocalDateTime; import java.util.Collection; import java.util.List; @@ -45,6 +47,25 @@ public interface CrmReceivablePlanMapper extends BaseMapperX query = new MPJLambdaWrapperX<>(); // 拼接数据权限的查询条件 CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), ids, userId); + // 拼接自身的查询条件 + query.selectAll(CrmReceivablePlanDO.class).in(CrmReceivablePlanDO::getId, ids).orderByDesc(CrmReceivablePlanDO::getId); return selectJoinList(CrmReceivablePlanDO.class, query); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java new file mode 100644 index 0000000000..6f5817015c --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.crm.job.customer; + +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +/** + * 客户自动掉入公海 Job + * + * @author 芋道源码 + */ +@Component +public class CrmCustomerAutoPutPoolJob implements JobHandler { + + @Resource + private CrmCustomerService customerService; + + @Override + @TenantJob + public String execute(String param) { + int count = customerService.autoPutCustomerPool(); + return String.format("掉入公海客户 %s 个", count); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java new file mode 100644 index 0000000000..144f64d02b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 芋艿:临时占位,后续可删除 + */ +package cn.iocoder.yudao.module.crm.job; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingService.java new file mode 100644 index 0000000000..2ff28d3857 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingService.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.crm.service.bi; + + +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO; + +import java.util.List; + +/** + * CRM BI 排行榜 Service 接口 + * + * @author anhaohao + */ +public interface CrmBiRankingService { + + /** + * 获得合同金额排行榜 + * + * @param rankReqVO 排行参数 + * @return 合同金额排行榜 + */ + List getContractPriceRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得回款金额排行榜 + * + * @param rankReqVO 排行参数 + * @return 回款金额排行榜 + */ + List getReceivablePriceRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得签约合同数量排行榜 + * + * @param rankReqVO 排行参数 + * @return 签约合同数量排行榜 + */ + List getContractCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得产品销量排行榜 + * + * @param rankReqVO 排行参数 + * @return 产品销量排行榜 + */ + List getProductSalesRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得新增客户数排行榜 + * + * @param rankReqVO 排行参数 + * @return 新增客户数排行榜 + */ + List getCustomerCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得联系人数量排行榜 + * + * @param rankReqVO 排行参数 + * @return 联系人数量排行榜 + */ + List getContactsCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得跟进次数排行榜 + * + * @param rankReqVO 排行参数 + * @return 跟进次数排行榜 + */ + List getFollowCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得跟进客户数排行榜 + * + * @param rankReqVO 排行参数 + * @return 跟进客户数排行榜 + */ + List getFollowCustomerCountRank(CrmBiRankReqVO rankReqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingServiceImpl.java new file mode 100644 index 0000000000..60e1b4ecb2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingServiceImpl.java @@ -0,0 +1,134 @@ +package cn.iocoder.yudao.module.crm.service.bi; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO; +import cn.iocoder.yudao.module.crm.dal.mysql.bi.CrmBiRankingMapper; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * CRM BI 排行榜 Service 实现类 + * + * @author anhaohao + */ +@Service +@Validated +public class CrmBiRankingServiceImpl implements CrmBiRankingService { + + @Resource + private CrmBiRankingMapper biRankingMapper; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @Override + public List getContractPriceRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectContractPriceRank); + } + + @Override + public List getReceivablePriceRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectReceivablePriceRank); + } + + @Override + public List getContractCountRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectContractCountRank); + } + + @Override + public List getProductSalesRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectProductSalesRank); + } + + @Override + public List getCustomerCountRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectCustomerCountRank); + } + + @Override + public List getContactsCountRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectContactsCountRank); + } + + @Override + public List getFollowCountRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectFollowCountRank); + } + + @Override + public List getFollowCustomerCountRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectFollowCustomerCountRank); + } + + /** + * 获得排行版数据 + * + * @param rankReqVO 参数 + * @param rankFunction 排行榜方法 + * @return 排行版数据 + */ + private List getRank(CrmBiRankReqVO rankReqVO, Function> rankFunction) { + // 1. 获得用户编号数组 + rankReqVO.setUserIds(getUserIds(rankReqVO.getDeptId())); + if (CollUtil.isEmpty(rankReqVO.getUserIds())) { + return Collections.emptyList(); + } + // 2. 获得排行数据 + List ranks = rankFunction.apply(rankReqVO); + if (CollUtil.isEmpty(ranks)) { + return Collections.emptyList(); + } + ranks.sort(Comparator.comparing(CrmBiRanKRespVO::getCount).reversed()); + // 3. 拼接用户信息 + appendUserInfo(ranks); + return ranks; + } + + /** + * 拼接用户信息(昵称、部门) + * + * @param ranks 排行榜数据 + */ + private void appendUserInfo(List ranks) { + Map userMap = adminUserApi.getUserMap(convertSet(ranks, CrmBiRanKRespVO::getOwnerUserId)); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + ranks.forEach(rank -> MapUtils.findAndThen(userMap, rank.getOwnerUserId(), user -> { + rank.setNickname(user.getNickname()); + MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> rank.setDeptName(dept.getName())); + })); + } + + /** + * 获得部门下的用户编号数组,包括子部门的 + * + * @param deptId 部门编号 + * @return 用户编号数组 + */ + public List getUserIds(Long deptId) { + // 1. 获得部门列表 + List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); + deptIds.add(deptId); + // 2. 获得用户编号 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java index eddf072969..683070d025 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java @@ -7,9 +7,10 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; -import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import jakarta.validation.Valid; -import javax.validation.Valid; import java.util.Collection; import java.util.List; @@ -41,7 +42,7 @@ public interface CrmBusinessService { * * @param updateFollowUpReqBOList 跟进信息 */ - void updateContactFollowUpBatch(List updateFollowUpReqBOList); + void updateBusinessFollowUpBatch(List updateFollowUpReqBOList); /** * 删除商机 @@ -50,6 +51,21 @@ public interface CrmBusinessService { */ void deleteBusiness(Long id); + /** + * 商机转移 + * + * @param reqVO 请求 + * @param userId 用户编号 + */ + void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId); + + /** + * 更新商机关联商品 + * + * @param updateProductReqBO 请求 + */ + void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO); + /** * 获得商机 * @@ -105,14 +121,6 @@ public interface CrmBusinessService { */ PageResult getBusinessPageByContact(CrmBusinessPageReqVO pageReqVO); - /** - * 商机转移 - * - * @param reqVO 请求 - * @param userId 用户编号 - */ - void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId); - /** * 获取关联客户的商机数量 * diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java index b86ff6cc8f..535578fd2f 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java @@ -2,45 +2,48 @@ package cn.iocoder.yudao.module.crm.service.business; 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.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO; import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert; -import cn.iocoder.yudao.module.crm.convert.businessproduct.CrmBusinessProductConvert; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO; -import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper; import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper; -import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessMapper; -import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; -import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO; import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.crm.service.product.CrmProductService; import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.starter.annotation.LogRecord; +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 javax.annotation.Resource; -import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Map; +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.crm.enums.ErrorCodeConstants.BUSINESS_CONTRACT_EXISTS; -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; /** @@ -54,20 +57,18 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @Resource private CrmBusinessMapper businessMapper; - @Resource private CrmBusinessProductMapper businessProductMapper; - // TODO @lzxhqs:不直接调用这个 mapper,要调用对方的 service;每个业务独立收敛 - @Resource - private CrmContractMapper contractMapper; - // TODO @lzxhqs:不直接调用这个 mapper,要调用对方的 service;每个业务独立收敛 @Resource - private CrmContactBusinessMapper contactBusinessMapper; + @Lazy // 延迟加载,避免循环依赖 + private CrmContractService contractService; @Resource private CrmPermissionService permissionService; @Resource private CrmContactBusinessService contactBusinessService; + @Resource + private CrmProductService productService; @Override @Transactional(rollbackFor = Exception.class) @@ -76,17 +77,17 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) { createReqVO.setId(null); // 1. 插入商机 - CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class) - .setOwnerUserId(userId); + CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class).setOwnerUserId(userId); businessMapper.insert(business); - // TODO 商机待定:插入商机与产品的关联表;校验商品存在 - // TODO lzxhqs:新增时,是不是不用调用这个方法哈; - verifyCrmBusinessProduct(business.getId()); - // TODO @lzxhqs:用 CollUtils.isNotEmpty; - if (!createReqVO.getProducts().isEmpty()) { - createBusinessProducts(createReqVO.getProducts(), business.getId()); + // 1.2 插入商机关联商品 + if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话 + List productList = buildBusinessProductList(createReqVO.getProductItems(), business.getId()); + businessProductMapper.insertBatch(productList); + // 更新合同商品总金额 + businessMapper.updateById(new CrmBusinessDO().setId(business.getId()).setProductPrice( + getSumValue(productList, CrmBusinessProductDO::getTotalPrice, Integer::sum))); } - // TODO 商机待定:在联系人的详情页,如果直接【新建商机】,则需要关联下。这里要搞个 CrmContactBusinessDO 表 + // TODO @puhui999:在联系人的详情页,如果直接【新建商机】,则需要关联下。这里要搞个 CrmContactBusinessDO 表 createContactBusiness(business.getId(), createReqVO.getContactId()); // 2. 创建数据权限 @@ -94,56 +95,17 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()) .setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); - // 4. 记录操作日志上下文 + // 3. 记录操作日志上下文 LogRecordContext.putVariable("business", business); return business.getId(); } // TODO @lzxhqs:CrmContactBusinessService 调用这个;这样逻辑才能收敛哈; - /** - * @param businessId 商机id - * @param contactId 联系人id - * @throws - * @description 联系人与商机的关联 - * @author lzxhqs - */ private void createContactBusiness(Long businessId, Long contactId) { CrmContactBusinessDO contactBusiness = new CrmContactBusinessDO(); contactBusiness.setBusinessId(businessId); contactBusiness.setContactId(contactId); - contactBusinessMapper.insert(contactBusiness); - - } - - // TODO @lzxhqs:这个方法注释格式不对;删除@description,然后把 插入商机产品关联表 作为方法注释; - /** - * @param products 产品集合 - * @description 插入商机产品关联表 - * @author lzxhqs - */ - private void createBusinessProducts(List products, Long businessId) { - // TODO @lzxhqs:可以用 CollectionUtils.convertList; - List list = new ArrayList<>(); - for (CrmBusinessProductSaveReqVO product : products) { - CrmBusinessProductDO businessProductDO = CrmBusinessProductConvert.INSTANCE.convert(product); - businessProductDO.setBusinessId(businessId); - list.add(businessProductDO); - } - businessProductMapper.insertBatch(list); - } - - /** - * @param id businessId - * @description 校验管理的产品存在则删除 - * @author lzxhqs - */ - private void verifyCrmBusinessProduct(Long id) { - CrmBusinessProductDO businessProductDO = businessProductMapper.selectByBusinessId(id); - if (businessProductDO != null) { - //通过商机Id删除 - businessProductMapper.deleteByBusinessId(id); - } - + contactBusinessService.insert(contactBusiness); } @Override @@ -155,15 +117,12 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { // 1. 校验存在 CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId()); - // 2. 更新商机 + // 2.1 更新商机 CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class); businessMapper.updateById(updateObj); - // TODO 商机待定:插入商机与产品的关联表;校验商品存在 - // TODO @lzxhqs:更新时,可以调用 CollectionUtils 的 diffList,尽量避免这种先删除再插入;而是新增的插入、变更的更新,没的删除;不然这个表每次更新,会多好多数据; - verifyCrmBusinessProduct(updateReqVO.getId()); - if (!updateReqVO.getProducts().isEmpty()) { - createBusinessProducts(updateReqVO.getProducts(), updateReqVO.getId()); - } + // 2.2 更新商机关联商品 + List productList = buildBusinessProductList(updateReqVO.getProductItems(), updateObj.getId()); + updateBusinessProduct(productList, updateObj.getId()); // TODO @商机待定:如果状态发生变化,插入商机状态变更记录表 // 3. 记录操作日志上下文 @@ -172,8 +131,8 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { } @Override - public void updateContactFollowUpBatch(List updateFollowUpReqBOList) { - businessMapper.updateBatch(BeanUtils.toBean(updateFollowUpReqBOList, CrmBusinessDO.class)); + public void updateBusinessFollowUpBatch(List updateFollowUpReqBOList) { + businessMapper.updateBatch(CrmBusinessConvert.INSTANCE.convertList(updateFollowUpReqBOList)); } @Override @@ -196,15 +155,52 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { LogRecordContext.putVariable("businessName", business.getName()); } + private void updateBusinessProduct(List newProductList, Long businessId) { + List oldProducts = businessProductMapper.selectListByBusinessId(businessId); + List> diffList = CollectionUtils.diffList(oldProducts, newProductList, (oldValue, newValue) -> { + boolean condition = ObjectUtil.equal(oldValue.getProductId(), newValue.getProductId()); + if (condition) { + newValue.setId(oldValue.getId()); // 更新需要原始编号 + } + return condition; + }); + if (CollUtil.isNotEmpty(diffList.get(0))) { + businessProductMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + businessProductMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + businessProductMapper.deleteBatchIds(convertSet(diffList.get(2), CrmBusinessProductDO::getId)); + } + } + + private List buildBusinessProductList(List productItems, + Long businessId) { + // 校验商品存在 + Set productIds = convertSet(productItems, CrmBusinessSaveReqVO.CrmBusinessProductItem::getId); + List productList = productService.getProductList(productIds); + if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) { + throw exception(PRODUCT_NOT_EXISTS); + } + Map productMap = convertMap(productList, CrmProductDO::getId); + return convertList(productItems, productItem -> { + CrmProductDO product = productMap.get(productItem.getId()); + return BeanUtils.toBean(product, CrmBusinessProductDO.class) + .setId(null).setProductId(productItem.getId()).setBusinessId(businessId) + .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()) + .setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent())); + }); + } + /** + * 删除校验合同是关联合同 + * * @param businessId 商机id - * @throws - * @description 删除校验合同是关联合同 * @author lzxhqs */ private void validateContractExists(Long businessId) { - CrmContractDO contract = contractMapper.selectByBizId(businessId); - if (contract != null) { + if (contractService.getContractCountByBusinessId(businessId) > 0) { throw exception(BUSINESS_CONTRACT_EXISTS); } } @@ -237,6 +233,14 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { LogRecordContext.putVariable("business", business); } + @Override + public void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO) { + // 更新商机关联商品 + List productList = buildBusinessProductList( + BeanUtils.toBean(updateProductReqBO.getProductItems(), CrmBusinessSaveReqVO.CrmBusinessProductItem.class), updateProductReqBO.getId()); + updateBusinessProduct(productList, updateProductReqBO.getId()); + } + //======================= 查询相关 ======================= @Override diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateFollowUpReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateFollowUpReqBO.java deleted file mode 100644 index 035882bc98..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateFollowUpReqBO.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.business.bo; - -import com.mzt.logapi.starter.annotation.DiffLogField; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; -import java.time.LocalDateTime; - -// TODO @puhui999:是不是搞个通用的 ReqBO 就好了 -/** - * 商机跟进信息 Update Req BO - * - * @author HUIHUI - */ -@Data -public class CrmBusinessUpdateFollowUpReqBO { - - @Schema(description = "商机编号", example = "3167") - @NotNull(message = "商机编号不能为空") - private Long id; - - @Schema(description = "最后跟进时间") - @DiffLogField(name = "最后跟进时间") - @NotNull(message = "最后跟进时间不能为空") - private LocalDateTime contactLastTime; - - @Schema(description = "下次联系时间") - @DiffLogField(name = "下次联系时间") - @NotNull(message = "下次联系时间不能为空") - private LocalDateTime contactNextTime; - - @Schema(description = "最后更进内容") - @DiffLogField(name = "最后更进内容") - @NotNull(message = "最后更进内容不能为空") - private String contactLastContent; - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateProductReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateProductReqBO.java new file mode 100644 index 0000000000..34b2fa3810 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateProductReqBO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.crm.service.business.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 更新商机商品 Update Req BO + * + * @author HUIHUI + */ +@Data +public class CrmBusinessUpdateProductReqBO { + + /** + * 商机编号 + */ + @NotNull(message = "商机编号不能为空") + private Long id; + + // TODO @芋艿:再想想 + @NotEmpty(message = "产品列表不能为空") + private List productItems; + + @Schema(description = "产品列表") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class CrmBusinessProductItem { + + @Schema(description = "产品编号", example = "20529") + @NotNull(message = "产品编号不能为空") + private Long id; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + @NotNull(message = "产品数量不能为空") + private Integer count; + + @Schema(description = "产品折扣") + private Integer discountPercent; + + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java index 23d53af3ce..1472bf02f8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java @@ -4,11 +4,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransformReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTranslateReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; -import cn.iocoder.yudao.module.crm.service.clue.bo.CrmClueUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import jakarta.validation.Valid; -import javax.validation.Valid; import java.util.Collection; import java.util.List; @@ -23,9 +23,10 @@ public interface CrmClueService { * 创建线索 * * @param createReqVO 创建信息 + * @param userId 用户编号 * @return 编号 */ - Long createClue(@Valid CrmClueSaveReqVO createReqVO); + Long createClue(@Valid CrmClueSaveReqVO createReqVO, Long userId); /** * 更新线索 @@ -39,7 +40,7 @@ public interface CrmClueService { * * @param clueUpdateFollowUpReqBO 信息 */ - void updateClueFollowUp(CrmClueUpdateFollowUpReqBO clueUpdateFollowUpReqBO); + void updateClueFollowUp(CrmUpdateFollowUpReqBO clueUpdateFollowUpReqBO); /** * 删除线索 @@ -87,6 +88,6 @@ public interface CrmClueService { * @param reqVO 线索编号 * @param userId 用户编号 */ - void translateCustomer(CrmClueTransformReqVO reqVO, Long userId); + void translateCustomer(CrmClueTranslateReqVO reqVO, Long userId); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java index e113d9553c..dfb044f503 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java @@ -2,39 +2,47 @@ package cn.iocoder.yudao.module.crm.service.clue; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransformReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTranslateReqVO; import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert; import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; -import cn.iocoder.yudao.module.crm.service.clue.bo.CrmClueUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; +import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; +import java.util.*; 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.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS; +import static java.util.Collections.singletonList; /** * 线索 Service 实现类 @@ -53,56 +61,191 @@ public class CrmClueServiceImpl implements CrmClueService { @Resource private CrmPermissionService crmPermissionService; - + @Resource + private CrmFollowUpRecordService followUpRecordService; @Resource private AdminUserApi adminUserApi; @Override - // TODO @min:补充相关几个方法的操作日志; - public Long createClue(CrmClueSaveReqVO createReqVO) { - // 校验关联数据 + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_CREATE_SUB_TYPE, bizNo = "{{#clue.id}}", + success = CRM_LEADS_CREATE_SUCCESS) + public Long createClue(CrmClueSaveReqVO createReqVO, Long userId) { + // 1.1 校验关联数据 validateRelationDataExists(createReqVO); + // 1.2 校验负责人是否存在 + if (createReqVO.getOwnerUserId() != null) { + adminUserApi.validateUserList(singletonList(createReqVO.getOwnerUserId())); + } else { + createReqVO.setOwnerUserId(userId); // 如果没有设置负责人那么默认操作人为负责人 + } - // 插入 - CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class); + // 2. 插入 + CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class).setId(null); clueMapper.insert(clue); - // 返回 + + // 3. 创建数据权限 + CrmPermissionCreateReqBO createReqBO = new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_LEADS.getType()) + .setBizId(clue.getId()).setUserId(clue.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()); + crmPermissionService.createPermission(createReqBO); + + // 4. 记录操作日志上下文 + LogRecordContext.putVariable("clue", clue); return clue.getId(); } @Override - @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) - public void updateClue(CrmClueSaveReqVO updateReqVO) { + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_LEADS_UPDATE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#updateReq.id", level = CrmPermissionLevelEnum.OWNER) + public void updateClue(CrmClueSaveReqVO updateReq) { + Assert.notNull(updateReq.getId(), "线索编号不能为空"); + // 1. 校验线索是否存在 + CrmClueDO oldClue = validateClueExists(updateReq.getId()); + // 2. 校验关联数据 + validateRelationDataExists(updateReq); + + // 3. 更新 + CrmClueDO updateObj = BeanUtils.toBean(updateReq, CrmClueDO.class); + clueMapper.updateById(updateObj); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmCustomerSaveReqVO.class)); + LogRecordContext.putVariable("clueName", oldClue.getName()); + } + + @Override + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReq.bizId}", + success = CRM_LEADS_UPDATE_SUCCESS) + public void updateClueFollowUp(CrmUpdateFollowUpReqBO updateReq) { // 校验线索是否存在 - validateClueExists(updateReqVO.getId()); - // 校验关联数据 - validateRelationDataExists(updateReqVO); + CrmClueDO oldClue = validateClueExists(updateReq.getBizId()); // 更新 - CrmClueDO updateObj = BeanUtils.toBean(updateReqVO, CrmClueDO.class); - clueMapper.updateById(updateObj); - } - - @Override - public void updateClueFollowUp(CrmClueUpdateFollowUpReqBO clueUpdateFollowUpReqBO) { - clueMapper.updateById(BeanUtils.toBean(clueUpdateFollowUpReqBO, CrmClueDO.class)); + clueMapper.updateById(BeanUtils.toBean(updateReq, CrmClueDO.class).setId(updateReq.getBizId())); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmUpdateFollowUpReqBO.class)); + LogRecordContext.putVariable("clueName", oldClue.getName()); + } @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_LEADS_DELETE_SUCCESS) @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) public void deleteClue(Long id) { - // 校验存在 - validateClueExists(id); - // 删除 + // 1. 校验存在 + CrmClueDO clue = validateClueExists(id); + + // 2. 删除 clueMapper.deleteById(id); - // 删除数据权限 + + // 3. 删除数据权限 crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_LEADS.getType(), id); + + // 4. 删除跟进 + followUpRecordService.deleteFollowUpRecordByBiz(CrmBizTypeEnum.CRM_LEADS.getType(), id); + + // 记录操作日志上下文 + LogRecordContext.putVariable("clueName", clue.getName()); } - private void validateClueExists(Long id) { - if (clueMapper.selectById(id) == null) { + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + success = CRM_LEADS_TRANSFER_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void transferClue(CrmClueTransferReqVO reqVO, Long userId) { + // 1 校验线索是否存在 + CrmClueDO clue = validateClueExists(reqVO.getId()); + + // 2.1 数据权限转移 + crmPermissionService.transferPermission(CrmClueConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_LEADS.getType())); + // 2.2 设置新的负责人 + clueMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); + + // 3. 记录转移日志 + LogRecordContext.putVariable("clue", clue); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void translateCustomer(CrmClueTranslateReqVO reqVO, Long userId) { + // 1.1 校验线索都存在 + Set clueIds = reqVO.getIds(); + List clues = getClueList(clueIds, userId); + if (CollUtil.isEmpty(clues) || ObjectUtil.notEqual(clues.size(), clueIds.size())) { + clueIds.removeAll(convertSet(clues, CrmClueDO::getId)); + throw exception(CLUE_ANY_CLUE_NOT_EXISTS, clueIds); + } + // 1.2 存在已经转化的,直接提示哈。避免操作的用户,以为都转化成功了 + List translatedClues = filterList(clues, + clue -> ObjectUtil.equal(Boolean.TRUE, clue.getTransformStatus())); + if (CollUtil.isNotEmpty(translatedClues)) { + throw exception(CLUE_ANY_CLUE_ALREADY_TRANSLATED, convertSet(translatedClues, CrmClueDO::getId)); + } + + // 2.1 遍历线索(未转化的线索),创建对应的客户 + clues.forEach(clue -> { + Long customerId = customerService.createCustomer(BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class), userId); + clue.setCustomerId(customerId); + }); + // 2.2 更新线索 + clueMapper.updateBatch(convertList(clues, clue -> new CrmClueDO().setId(clue.getId()) + .setTransformStatus(Boolean.TRUE).setCustomerId(clue.getCustomerId()))); + // 2.3 复制跟进记录 + copyFollowUpRecords(clues); + + // 3. 记录操作日志 + for (CrmClueDO clue : clues) { + getSelf().translateCustomerLog(clue); + } + } + + /** + * 线索被转换客户后,需要将线索的跟进记录,复制到客户上 + * + * @param clues 被转化的线索 + */ + private void copyFollowUpRecords(List clues) { + List followUpRecords = followUpRecordService.getFollowUpRecordByBiz( + CrmBizTypeEnum.CRM_LEADS.getType(), convertSet(clues, CrmClueDO::getId)); + if (CollUtil.isEmpty(followUpRecords)) { + return; + } + // 创建跟进 + Map clueMap = convertMap(clues, CrmClueDO::getId); + followUpRecordService.createFollowUpRecordBatch(convertList(followUpRecords, followUpRecord -> + BeanUtils.toBean(followUpRecord, CrmFollowUpCreateReqBO.class).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) + .setBizId(clueMap.get(followUpRecord.getBizId()).getCustomerId()))); + } + + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSLATE_SUB_TYPE, bizNo = "{{#clue.id}}", + success = CRM_LEADS_TRANSLATE_SUCCESS) + public void translateCustomerLog(CrmClueDO clue) { + // 记录操作日志上下文 + LogRecordContext.putVariable("clue", clue); + } + + private void validateRelationDataExists(CrmClueSaveReqVO reqVO) { + // 校验负责人 + if (Objects.nonNull(reqVO.getOwnerUserId()) && + Objects.isNull(adminUserApi.getUser(reqVO.getOwnerUserId()))) { + throw exception(USER_NOT_EXISTS); + } + } + + private CrmClueDO validateClueExists(Long id) { + CrmClueDO crmClueDO = clueMapper.selectById(id); + if (crmClueDO == null) { throw exception(CLUE_NOT_EXISTS); } + return crmClueDO; } @Override @@ -124,60 +267,13 @@ public class CrmClueServiceImpl implements CrmClueService { return clueMapper.selectPage(pageReqVO, userId); } - @Override - public void transferClue(CrmClueTransferReqVO reqVO, Long userId) { - // 1 校验线索是否存在 - validateClueExists(reqVO.getId()); - - // 2.1 数据权限转移 - crmPermissionService.transferPermission(CrmClueConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_LEADS.getType())); - // 2.2 设置新的负责人 - clueMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); - - // 3. TODO 记录转移日志 - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void translateCustomer(CrmClueTransformReqVO reqVO, Long userId) { - // 校验线索都存在 - Set clueIds = reqVO.getIds(); - List clues = getClueList(clueIds, userId); - if (CollUtil.isEmpty(clues) || ObjectUtil.notEqual(clues.size(), clueIds.size())) { - clueIds.removeAll(convertSet(clues, CrmClueDO::getId)); - // TODO @min:可以使用 StrUtil.join(",", clueIds) 简化这种常见操作 - throw exception(CLUE_ANY_CLUE_NOT_EXISTS, clueIds.stream().map(String::valueOf).collect(Collectors.joining(","))); - } - - // 过滤出未转化的客户 - // TODO @min:1)存在已经转化的,直接提示哈。避免操作的用户,以为都转化成功了;2)常见的过滤逻辑,可以使用 CollectionUtils.filterList() - List unTransformClues = clues.stream() - .filter(clue -> ObjectUtil.notEqual(Boolean.TRUE, clue.getTransformStatus())).collect(Collectors.toList()); - // 传入的线索中包含已经转化的情况,抛出业务异常 - if (ObjectUtil.notEqual(clues.size(), unTransformClues.size())) { - // TODO @min:可以使用 StrUtil.join(",", clueIds) 简化这种常见操作 - clueIds.removeAll(convertSet(unTransformClues, CrmClueDO::getId)); - throw exception(CLUE_ANY_CLUE_ALREADY_TRANSLATED, clueIds.stream().map(String::valueOf).collect(Collectors.joining(","))); - } - - // 遍历线索(未转化的线索),创建对应的客户 - unTransformClues.forEach(clue -> { - // 1. 创建客户 - CrmCustomerSaveReqVO customerSaveReqVO = BeanUtils.toBean(clue, CrmCustomerSaveReqVO.class).setId(null); - Long customerId = customerService.createCustomer(customerSaveReqVO, userId); - // TODO @puhui999:如果有跟进记录,需要一起转过去;提问:艿艿这里是复制线索所有的跟进吗?还是直接把线索相关的跟进 bizType、bizId 全改为关联客户? - // 2. 更新线索 - clueMapper.updateById(new CrmClueDO().setId(clue.getId()) - .setTransformStatus(Boolean.TRUE).setCustomerId(customerId)); - }); - } - - private void validateRelationDataExists(CrmClueSaveReqVO reqVO) { - // 校验负责人 - if (Objects.nonNull(reqVO.getOwnerUserId()) && - Objects.isNull(adminUserApi.getUser(reqVO.getOwnerUserId()))) { - throw exception(USER_NOT_EXISTS); - } + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private CrmClueServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/bo/CrmClueUpdateFollowUpReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/bo/CrmClueUpdateFollowUpReqBO.java deleted file mode 100644 index 41c1070bcc..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/bo/CrmClueUpdateFollowUpReqBO.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.clue.bo; - -import com.mzt.logapi.starter.annotation.DiffLogField; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; -import java.time.LocalDateTime; - -// TODO @puhui999:是不是搞个通用的 ReqBO 就好了 -/** - * 线索跟进信息 Update Req BO - * - * @author HUIHUI - */ -@Data -public class CrmClueUpdateFollowUpReqBO { - - @Schema(description = "线索编号", example = "3167") - @NotNull(message = "线索编号不能为空") - private Long id; - - @Schema(description = "最后跟进时间") - @DiffLogField(name = "最后跟进时间") - @NotNull(message = "最后跟进时间不能为空") - private LocalDateTime contactLastTime; - - @Schema(description = "下次联系时间") - @DiffLogField(name = "下次联系时间") - @NotNull(message = "下次联系时间不能为空") - private LocalDateTime contactNextTime; - - @Schema(description = "最后更进内容") - @DiffLogField(name = "最后更进内容") - @NotNull(message = "最后更进内容不能为空") - private String contactLastContent; - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java index 8cae738151..daeccb62a6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java @@ -42,4 +42,11 @@ public interface CrmContactBusinessService { */ List getContactBusinessListByContactId(Long contactId); + /** + * 新增联系人与商机的关联 + * + * @param contactBusiness 新增联系人与商机的对象 + */ + void insert(CrmContactBusinessDO contactBusiness); + } \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java index 41a4430f6c..d38eab4fa0 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java @@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -19,7 +22,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTACT_NOT_EXISTS; -// TODO @puhui999:数据权限的校验;每个操作; /** * 联系人与商机的关联 Service 实现类 * @@ -40,6 +42,7 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService private CrmContactService contactService; @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#createReqVO.contactId", level = CrmPermissionLevelEnum.WRITE) public void createContactBusinessList(CrmContactBusinessReqVO createReqVO) { CrmContactDO contact = contactService.getContact(createReqVO.getContactId()); if (contact == null) { @@ -65,6 +68,7 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService } @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#deleteReqVO.contactId", level = CrmPermissionLevelEnum.WRITE) public void deleteContactBusinessList(CrmContactBusinessReqVO deleteReqVO) { CrmContactDO contact = contactService.getContact(deleteReqVO.getContactId()); if (contact == null) { @@ -76,13 +80,20 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService } @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.WRITE) public void deleteContactBusinessByContactId(Long contactId) { - contactBusinessMapper.delete(CrmContactBusinessDO::getContactId,contactId); + contactBusinessMapper.delete(CrmContactBusinessDO::getContactId, contactId); } @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.READ) public List getContactBusinessListByContactId(Long contactId) { return contactBusinessMapper.selectListByContactId(contactId); } + @Override + public void insert(CrmContactBusinessDO contactBusiness) { + contactBusinessMapper.insert(contactBusiness); + } + } \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java index 5c8699b791..d7688b8fb2 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java @@ -6,9 +6,9 @@ import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactSaveReq import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; -import cn.iocoder.yudao.module.crm.service.contact.bo.CrmContactUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import jakarta.validation.Valid; -import javax.validation.Valid; import java.util.Collection; import java.util.List; @@ -63,7 +63,7 @@ public interface CrmContactService { * * @param updateFollowUpReqBOList 跟进信息 */ - void updateContactFollowUpBatch(List updateFollowUpReqBOList); + void updateContactFollowUpBatch(List updateFollowUpReqBOList); /** * 获得联系人 @@ -80,7 +80,7 @@ public interface CrmContactService { * @param userId 用户编号 * @return 联系人列表 */ - List getContactList(Collection ids, Long userId); + List getContactListByIds(Collection ids, Long userId); /** * 获得联系人列表 @@ -88,7 +88,7 @@ public interface CrmContactService { * @param ids 编号 * @return 联系人列表 */ - List getContactList(Collection ids); + List getContactListByIds(Collection ids); /** * 获得联系人列表 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java index 3185ed3c8c..08ce78b810 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java @@ -15,20 +15,21 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; -import cn.iocoder.yudao.module.crm.service.contact.bo.CrmContactUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.starter.annotation.LogRecord; +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 javax.annotation.Resource; import java.util.Collection; import java.util.List; @@ -56,6 +57,7 @@ public class CrmContactServiceImpl implements CrmContactService { @Resource private CrmPermissionService permissionService; @Resource + @Lazy private CrmContractService contractService; @Resource private CrmContactBusinessService contactBusinessService; @@ -195,8 +197,8 @@ public class CrmContactServiceImpl implements CrmContactService { } @Override - public void updateContactFollowUpBatch(List updateFollowUpReqBOList) { - contactMapper.updateBatch(BeanUtils.toBean(updateFollowUpReqBOList, CrmContactDO.class)); + public void updateContactFollowUpBatch(List updateFollowUpReqBOList) { + contactMapper.updateBatch(CrmContactConvert.INSTANCE.convertList(updateFollowUpReqBOList)); } //======================= 查询相关 ======================= @@ -208,7 +210,7 @@ public class CrmContactServiceImpl implements CrmContactService { } @Override - public List getContactList(Collection ids, Long userId) { + public List getContactListByIds(Collection ids, Long userId) { if (CollUtil.isEmpty(ids)) { return ListUtil.empty(); } @@ -216,7 +218,7 @@ public class CrmContactServiceImpl implements CrmContactService { } @Override - public List getContactList(Collection ids) { + public List getContactListByIds(Collection ids) { if (CollUtil.isEmpty(ids)) { return ListUtil.empty(); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/bo/CrmContactUpdateFollowUpReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/bo/CrmContactUpdateFollowUpReqBO.java deleted file mode 100644 index bd1128a2ec..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/bo/CrmContactUpdateFollowUpReqBO.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.contact.bo; - -import com.mzt.logapi.starter.annotation.DiffLogField; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; -import java.time.LocalDateTime; - -// TODO @puhui999:是不是搞个通用的 ReqBO 就好了 -/** - * 联系人跟进信息 Update Req BO - * - * @author HUIHUI - */ -@Data -public class CrmContactUpdateFollowUpReqBO { - - @Schema(description = "联系人编号", example = "3167") - @NotNull(message = "联系人编号不能为空") - private Long id; - - @Schema(description = "最后跟进时间") - @DiffLogField(name = "最后跟进时间") - @NotNull(message = "最后跟进时间不能为空") - private LocalDateTime contactLastTime; - - @Schema(description = "下次联系时间") - @DiffLogField(name = "下次联系时间") - @NotNull(message = "下次联系时间不能为空") - private LocalDateTime contactNextTime; - - @Schema(description = "最后更进内容") - @DiffLogField(name = "最后更进内容") - @NotNull(message = "最后更进内容不能为空") - private String contactLastContent; - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java index 6ab71e0d42..0bd527ae05 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java @@ -1,14 +1,16 @@ package cn.iocoder.yudao.module.crm.service.contract; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; -import cn.iocoder.yudao.module.crm.service.contract.bo.CrmContractUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import jakarta.validation.Valid; -import javax.validation.Valid; import java.util.Collection; import java.util.List; @@ -55,7 +57,22 @@ public interface CrmContractService { * * @param contractUpdateFollowUpReqBO 信息 */ - void updateContractFollowUp(CrmContractUpdateFollowUpReqBO contractUpdateFollowUpReqBO); + void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO); + + /** + * 发起合同审批流程 + * + * @param id 合同编号 + * @param userId 用户编号 + */ + void submitContract(Long id, Long userId); + + /** + * 更新合同流程审批结果 + * + * @param event 审批结果 + */ + void updateContractAuditStatus(BpmResultListenerRespDTO event); /** * 获得合同 @@ -110,4 +127,20 @@ public interface CrmContractService { */ Long getContractCountByCustomerId(Long customerId); + /** + * 根据商机ID获取关联客户的合同数量 + * + * @param businessId 商机编号 + * @return 数量 + */ + Long getContractCountByBusinessId(Long businessId); + + /** + * 获取合同商品列表 + * + * @param contactId 合同编号 + * @return 合同商品列表 + */ + List getContractProductListByContractId(Long contactId); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java index e652d7258f..f5a1558a13 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java @@ -2,20 +2,37 @@ package cn.iocoder.yudao.module.crm.service.contract; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjUtil; 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.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; +import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; +import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert; import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper; +import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractProductMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; -import cn.iocoder.yudao.module.crm.service.contract.bo.CrmContractUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.crm.service.product.CrmProductService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.starter.annotation.LogRecord; @@ -26,10 +43,14 @@ import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.Set; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTRACT_NOT_EXISTS; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS; /** * CRM 合同 Service 实现类 @@ -40,29 +61,59 @@ import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; @Validated public class CrmContractServiceImpl implements CrmContractService { + /** + * BPM 合同审批流程标识 + */ + public static final String CONTRACT_APPROVE = "contract-approve"; + @Resource private CrmContractMapper contractMapper; + @Resource + private CrmContractProductMapper contractProductMapper; @Resource private CrmPermissionService crmPermissionService; + @Resource + private CrmProductService productService; + @Resource + private CrmCustomerService customerService; + @Resource + private CrmBusinessService businessService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private BpmProcessInstanceApi bpmProcessInstanceApi; @Override @Transactional(rollbackFor = Exception.class) @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}", success = CRM_CONTRACT_CREATE_SUCCESS) public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) { - createReqVO.setId(null); - // TODO @合同待定:插入合同商品;需要搞个 BusinessProductDO - // 插入合同 - CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class); + validateRelationDataExists(createReqVO); + // 1.1 插入合同 + CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null); contractMapper.insert(contract); + // 1.2 插入合同关联商品 + if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话 + List productList = convertContractProductList(createReqVO, contract.getId()); + contractProductMapper.insertBatch(productList); + // 更新合同商品总金额 + contractMapper.updateById(new CrmContractDO().setId(contract.getId()).setProductPrice( + getSumValue(productList, CrmContractProductDO::getTotalPrice, Integer::sum))); + // 如果存在合同关联了商机则更新商机商品关联 + if (contract.getBusinessId() != null) { + businessService.updateBusinessProduct(new CrmBusinessUpdateProductReqBO().setId(contract.getBusinessId()) + .setProductItems(BeanUtils.toBean(createReqVO.getProductItems(), CrmBusinessUpdateProductReqBO.CrmBusinessProductItem.class))); + } + } - // 创建数据权限 + // 2. 创建数据权限 crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId) .setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId()) .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); - // 4. 记录操作日志上下文 + // 3. 记录操作日志上下文 LogRecordContext.putVariable("contract", contract); return contract.getId(); } @@ -73,23 +124,89 @@ public class CrmContractServiceImpl implements CrmContractService { success = CRM_CONTRACT_UPDATE_SUCCESS) @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) public void updateContract(CrmContractSaveReqVO updateReqVO) { - // TODO @合同待定:只有草稿、审批中,可以编辑; - // 校验存在 - CrmContractDO oldContract = validateContractExists(updateReqVO.getId()); - // 更新合同 + Assert.notNull(updateReqVO.getId(), "合同编号不能为空"); + // 1.1 校验存在 + CrmContractDO contract = validateContractExists(updateReqVO.getId()); + // 1.2 只有草稿、审批中,可以编辑; + if (!ObjectUtils.equalsAny(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), + CrmAuditStatusEnum.PROCESS.getStatus())) { + throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED); + } + validateRelationDataExists(updateReqVO); + + // 2.1 更新合同 CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class); contractMapper.updateById(updateObj); - // TODO @合同待定:插入合同商品;需要搞个 BusinessProductDO + // 2.2 更新合同关联商品 + updateContractProduct(updateReqVO, updateObj.getId()); // 3. 记录操作日志上下文 - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class)); - LogRecordContext.putVariable("contractName", oldContract.getName()); + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(contract, CrmContractSaveReqVO.class)); + LogRecordContext.putVariable("contractName", contract.getName()); + } + + private void updateContractProduct(CrmContractSaveReqVO updateReqVO, Long contractId) { + if (CollUtil.isEmpty(updateReqVO.getProductItems())) { + return; + } + List newProductList = convertContractProductList(updateReqVO, contractId); + List oldProductList = contractProductMapper.selectListByContractId(contractId); + List> diffList = diffList(oldProductList, newProductList, (oldObj, newObj) -> { + boolean match = ObjUtil.equal(oldObj.getProductId(), newObj.getProductId()); + if (match) { + newObj.setId(oldObj.getId()); // 设置一下老的编号更新时需要使用 + } + return match; + }); + if (CollUtil.isNotEmpty(diffList.get(0))) { + contractProductMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + contractProductMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + contractProductMapper.deleteBatchIds(convertList(diffList.get(2), CrmContractProductDO::getId)); + } } // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum - // TODO @合同待定:缺一个发起审批的接口;只有草稿可以发起审批;CrmAuditStatusEnum + private List convertContractProductList(CrmContractSaveReqVO reqVO, Long contractId) { + // 校验商品存在 + Set productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId); + List productList = productService.getProductList(productIds); + if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) { + throw exception(PRODUCT_NOT_EXISTS); + } + Map productMap = convertMap(productList, CrmProductDO::getId); + return convertList(reqVO.getProductItems(), productItem -> { + CrmProductDO product = productMap.get(productItem.getId()); + return BeanUtils.toBean(product, CrmContractProductDO.class) + .setId(null).setProductId(productItem.getId()).setContractId(contractId) + .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()) + .setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent())); + }); + } + /** + * 校验关联数据是否存在 + * + * @param reqVO 请求 + */ + private void validateRelationDataExists(CrmContractSaveReqVO reqVO) { + // 1. 校验客户 + if (reqVO.getCustomerId() != null && customerService.getCustomer(reqVO.getCustomerId()) == null) { + throw exception(CUSTOMER_NOT_EXISTS); + } + // 2. 校验负责人 + if (reqVO.getOwnerUserId() != null && adminUserApi.getUser(reqVO.getOwnerUserId()) == null) { + throw exception(USER_NOT_EXISTS); + } + // 3. 如果有关联商机,则需要校验存在 + if (reqVO.getBusinessId() != null && businessService.getBusiness(reqVO.getBusinessId()) == null) { + throw exception(BUSINESS_NOT_EXISTS); + } + } @Override @Transactional(rollbackFor = Exception.class) @@ -137,8 +254,63 @@ public class CrmContractServiceImpl implements CrmContractService { } @Override - public void updateContractFollowUp(CrmContractUpdateFollowUpReqBO contractUpdateFollowUpReqBO) { - contractMapper.updateById(BeanUtils.toBean(contractUpdateFollowUpReqBO, CrmContractDO.class)); + public void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO) { + contractMapper.updateById(BeanUtils.toBean(contractUpdateFollowUpReqBO, CrmContractDO.class).setId(contractUpdateFollowUpReqBO.getBizId())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_SUBMIT_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_CONTRACT_SUBMIT_SUCCESS) + public void submitContract(Long id, Long userId) { + // 1. 校验合同是否在审批 + CrmContractDO contract = validateContractExists(id); + if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) { + throw exception(CONTRACT_SUBMIT_FAIL_NOT_DRAFT); + } + + // 2. 创建合同审批流程实例 + String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() + .setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id))); + + // 3. 更新合同工作流编号 + contractMapper.updateById(new CrmContractDO().setId(id).setProcessInstanceId(processInstanceId) + .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus())); + + // 3. 记录日志 + LogRecordContext.putVariable("contractName", contract.getName()); + } + + @Override + public void updateContractAuditStatus(BpmResultListenerRespDTO event) { + // 判断下状态是否符合预期 + if (!isEndResult(event.getResult())) { + return; + } + // 状态转换 + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult())) { + event.setResult(CrmAuditStatusEnum.APPROVE.getStatus()); + } + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult())) { + event.setResult(CrmAuditStatusEnum.REJECT.getStatus()); + } + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult())) { + event.setResult(CrmAuditStatusEnum.CANCEL.getStatus()); + } + // 更新合同状态 + contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey())) + .setAuditStatus(event.getResult())); + } + + /** + * 判断该结果是否处于 End 最终结果 + * + * @param result 结果 + * @return 是否 + */ + public static boolean isEndResult(Integer result) { + return ObjectUtils.equalsAny(result, BpmProcessInstanceResultEnum.APPROVE.getResult(), + BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult()); } //======================= 查询相关 ======================= @@ -178,5 +350,15 @@ public class CrmContractServiceImpl implements CrmContractService { return contractMapper.selectCount(CrmContractDO::getCustomerId, customerId); } + @Override + public Long getContractCountByBusinessId(Long businessId) { + return contractMapper.selectCountByBusinessId(businessId); + } + + @Override + public List getContractProductListByContractId(Long contactId) { + return contractProductMapper.selectListByContractId(contactId); + } + // TODO @合同待定:需要新增一个 ContractConfigDO 表,合同配置,重点是到期提醒; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/bo/CrmContractUpdateFollowUpReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/bo/CrmContractUpdateFollowUpReqBO.java deleted file mode 100644 index d122cea939..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/bo/CrmContractUpdateFollowUpReqBO.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.contract.bo; - -import com.mzt.logapi.starter.annotation.DiffLogField; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; -import java.time.LocalDateTime; - -// TODO @puhui999:是不是搞个通用的 ReqBO 就好了 -/** - * 合同跟进信息 Update Req BO - * - * @author HUIHUI - */ -@Data -public class CrmContractUpdateFollowUpReqBO { - - @Schema(description = "合同编号", example = "3167") - @NotNull(message = "合同编号不能为空") - private Long id; - - @Schema(description = "最后跟进时间") - @DiffLogField(name = "最后跟进时间") - @NotNull(message = "最后跟进时间不能为空") - private LocalDateTime contactLastTime; - - @Schema(description = "下次联系时间") - @DiffLogField(name = "下次联系时间") - @NotNull(message = "下次联系时间不能为空") - private LocalDateTime contactNextTime; - - @Schema(description = "最后更进内容") - @DiffLogField(name = "最后更进内容") - @NotNull(message = "最后更进内容不能为空") - private String contactLastContent; - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java new file mode 100644 index 0000000000..c5ee407bdc --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.crm.service.contract.listener; + +import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi; +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractServiceImpl; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +// TODO @芋艿:后续改成支持 RPC +/** + * 合同审批的结果的监听器实现类 + * + * @author HUIHUI + */ +@Component +public class CrmContractResultListener implements BpmResultListenerApi { + + @Resource + private CrmContractService contractService; + + @Override + public String getProcessDefinitionKey() { + return CrmContractServiceImpl.CONTRACT_APPROVE; + } + + @Override + public void onEvent(BpmResultListenerRespDTO event) { + contractService.updateContractAuditStatus(event); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java index 26ec0e3fac..911260f769 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java @@ -1,14 +1,13 @@ package cn.iocoder.yudao.module.crm.service.customer; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; -import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import jakarta.validation.Valid; -import javax.validation.Valid; import java.util.Collection; import java.util.List; @@ -96,7 +95,25 @@ public interface CrmCustomerService { * * @param customerUpdateFollowUpReqBO 请求 */ - void updateCustomerFollowUp(CrmCustomerUpdateFollowUpReqBO customerUpdateFollowUpReqBO); + void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO); + + /** + * 创建客户 + * + * @param customerCreateReq 请求信息 + * @param userId 用户编号 + * @return 客户列表 + */ + Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId); + + /** + * 批量导入客户 + * + * @param importCustomers 导入客户列表 + * @param importReqVO 请求 + * @return 导入结果 + */ + CrmCustomerImportRespVO importCustomerList(List importCustomers, CrmCustomerImportReqVO importReqVO); // ==================== 公海相关操作 ==================== @@ -116,4 +133,14 @@ public interface CrmCustomerService { */ void receiveCustomer(List ids, Long ownerUserId, Boolean isReceive); + /** + * 【系统】客户自动掉入公海 + * + * @return 掉入公海数量 + */ + int autoPutCustomerPool(); + + PageResult getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, + CrmCustomerPoolConfigDO poolConfigDO, + Long loginUserId); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java index d66e92458c..bc02a6b71d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java @@ -2,17 +2,19 @@ package cn.iocoder.yudao.module.crm.service.customer; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*; import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; @@ -21,7 +23,8 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionU import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; -import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; @@ -29,19 +32,18 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT; @@ -54,6 +56,7 @@ import static java.util.Collections.singletonList; * @author Wanwan */ @Service +@Slf4j @Validated public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -66,6 +69,9 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { private CrmCustomerLimitConfigService customerLimitConfigService; @Resource @Lazy + private CrmCustomerPoolConfigService customerPoolConfigService; + @Resource + @Lazy private CrmContactService contactService; @Resource @Lazy @@ -87,10 +93,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1); // 2. 插入客户 - CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class) - .setLockStatus(false).setDealStatus(false) - .setContactLastTime(LocalDateTime.now()); - // TODO @puhui999:可能要加个 receiveTime 字段,记录最后接收时间 + CrmCustomerDO customer = initCustomer(createReqVO, userId); customerMapper.insert(customer); // 3. 创建数据权限 @@ -102,6 +105,18 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { return customer.getId(); } + /** + * 初始化客户的通用字段 + * + * @param customer 客户信息 + * @param ownerUserId 负责人编号 + * @return 客户信息 DO + */ + private static CrmCustomerDO initCustomer(Object customer, Long ownerUserId) { + return BeanUtils.toBean(customer, CrmCustomerDO.class).setOwnerUserId(ownerUserId) + .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now()); + } + @Override @Transactional(rollbackFor = Exception.class) @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", @@ -205,8 +220,97 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { } @Override - public void updateCustomerFollowUp(CrmCustomerUpdateFollowUpReqBO customerUpdateFollowUpReqBO) { - customerMapper.updateById(BeanUtils.toBean(customerUpdateFollowUpReqBO, CrmCustomerDO.class)); + public void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO) { + customerMapper.updateById(BeanUtils.toBean(customerUpdateFollowUpReqBO, CrmCustomerDO.class).setId(customerUpdateFollowUpReqBO.getBizId())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}", + success = CRM_CUSTOMER_CREATE_SUCCESS) + public Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId) { + // 1. 插入客户 + CrmCustomerDO customer = BeanUtils.toBean(customerCreateReq, CrmCustomerDO.class).setOwnerUserId(userId) + .setLockStatus(false).setDealStatus(false).setReceiveTime(LocalDateTime.now()); + customerMapper.insert(customer); + + // 2. 创建数据权限 + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) + .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人 + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("customer", customer); + return customer.getId(); + } + + @Override + public CrmCustomerImportRespVO importCustomerList(List importCustomers, CrmCustomerImportReqVO importReqVO) { + if (CollUtil.isEmpty(importCustomers)) { + throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY); + } + CrmCustomerImportRespVO respVO = CrmCustomerImportRespVO.builder().createCustomerNames(new ArrayList<>()) + .updateCustomerNames(new ArrayList<>()).failureCustomerNames(new LinkedHashMap<>()).build(); + importCustomers.forEach(importCustomer -> { + // 校验,判断是否有不符合的原因 + // TODO @puhui999:可以用 ValidationUtils 做参数校验;可能要封装一个方法,返回 message;这样的话,就可以在 CrmCustomerImportExcelVO 写需要校验的参数啦; + try { + validateCustomerForCreate(importCustomer); + } catch (ServiceException ex) { + respVO.getFailureCustomerNames().put(importCustomer.getName(), ex.getMessage()); + return; + } + // 情况一:判断如果不存在,在进行插入 + CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName()); + if (existCustomer == null) { + // 1.1 插入客户信息 + CrmCustomerDO customer = initCustomer(importCustomer, importReqVO.getOwnerUserId()); + customerMapper.insert(customer); + respVO.getCreateCustomerNames().add(importCustomer.getName()); + // 1.2 创建数据权限 + if (importReqVO.getOwnerUserId() != null) { + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) + .setBizId(customer.getId()).setUserId(importReqVO.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + } + // 1.3 记录操作日志 + getSelf().importCustomerLog(customer, false); + return; + } + + // 情况二:如果存在,判断是否允许更新 + if (!importReqVO.getUpdateSupport()) { + respVO.getFailureCustomerNames().put(importCustomer.getName(), + StrUtil.format(CUSTOMER_NAME_EXISTS.getMsg(), importCustomer.getName())); + return; + } + // 2.1 更新客户信息 + CrmCustomerDO updateCustomer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class) + .setId(existCustomer.getId()); + customerMapper.updateById(updateCustomer); + respVO.getUpdateCustomerNames().add(importCustomer.getName()); + // 2.2 记录操作日志 + getSelf().importCustomerLog(updateCustomer, true); + }); + return respVO; + } + + /** + * 记录导入客户时的操作日志 + * + * @param customer 客户信息 + * @param isUpdate 是否更新;true - 更新,false - 新增 + */ + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_IMPORT_SUB_TYPE, bizNo = "{{#customer.id}}", + success = CRM_CUSTOMER_IMPORT_SUCCESS) + public void importCustomerLog(CrmCustomerDO customer, boolean isUpdate) { + LogRecordContext.putVariable("customer", customer); + LogRecordContext.putVariable("isUpdate", isUpdate); + } + + private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) { + // 校验客户名称不能为空 + if (StrUtil.isEmptyIfStr(importCustomer.getName())) { + throw exception(CUSTOMER_CREATE_NAME_NOT_NULL); + } } // ==================== 公海相关操作 ==================== @@ -227,17 +331,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { // 1.3. 校验客户是否锁定 validateCustomerIsLocked(customer, true); - // 2.1 设置负责人为 NULL - int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null); - if (updateOwnerUserIncr == 0) { - throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL); - } - // 2.2 删除负责人数据权限 - permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(), - CrmPermissionLevelEnum.OWNER.getLevel()); - - // 3. 联系人的负责人,也要设置为 null。因为:因为领取后,负责人也要关联过来,这块和 receiveCustomer 是对应的 - contactService.updateOwnerUserIdByCustomerId(customer.getId(), null); + // 2. 客户放入公海 + putCustomerPool(customer); // 记录操作日志上下文 LogRecordContext.putVariable("customerName", customer.getName()); @@ -295,6 +390,53 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { } } + @Override + public int autoPutCustomerPool() { + CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig(); + if (poolConfig == null || !poolConfig.getEnabled()) { + return 0; + } + // 1.1 获取没有锁定的不在公海的客户 + List customerList = customerMapper.selectListByLockAndNotPool(Boolean.FALSE); + // TODO @puhui999:下面也搞到 sql 里去哈;写 or 查询,问题不大的;低 393 到 402;原因是,避免无用的太多数据查询到 java 进程里; + List poolCustomerList = new ArrayList<>(); + poolCustomerList.addAll(filterList(customerList, customer -> + !customer.getDealStatus() && (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0)); + poolCustomerList.addAll(filterList(customerList, customer -> { + if (!customer.getDealStatus()) { // 这里只处理成交的 + return false; + } + LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime()); + return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0; + })); + + // 2. 逐个放入公海 + int count = 0; + for (CrmCustomerDO customer : poolCustomerList) { + try { + getSelf().putCustomerPool(customer); + count++; + } catch (Throwable e) { + log.error("[autoPutCustomerPool][Customer 客户({}) 放入公海异常]", customer.getId(), e); + } + } + return count; + } + + private void putCustomerPool(CrmCustomerDO customer) { + // 1. 设置负责人为 NULL + int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null); + if (updateOwnerUserIncr == 0) { + throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL); + } + // 2. 删除负责人数据权限 + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(), + CrmPermissionLevelEnum.OWNER.getLevel()); + + // 3. 联系人的负责人,也要设置为 null。因为:因为领取后,负责人也要关联过来,这块和 receiveCustomer 是对应的 + contactService.updateOwnerUserIdByCustomerId(customer.getId(), null); + } + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}", success = CRM_CUSTOMER_RECEIVE_SUCCESS) public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) { @@ -324,6 +466,12 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { return customerMapper.selectPage(pageReqVO, userId); } + public PageResult getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO, + CrmCustomerPoolConfigDO poolConfigDO, + Long userId) { + return customerMapper.selectPutInPoolRemindCustomerPage(pageReqVO, poolConfigDO, userId); + } + // ======================= 校验相关 ======================= /** diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerCreateReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerCreateReqBO.java new file mode 100644 index 0000000000..6d80b0e4c4 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerCreateReqBO.java @@ -0,0 +1,125 @@ +package cn.iocoder.yudao.module.crm.service.customer.bo; + +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.framework.common.validation.Telephone; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 客户创建 Create Req BO + * + * @author HUIHUI + */ +@Data +public class CrmCustomerCreateReqBO { + + /** + * 客户名称 + */ + @NotEmpty(message = "客户名称不能为空") + private String name; + /** + * 跟进状态 + */ + private Boolean followUpStatus; + /** + * 锁定状态 + */ + private Boolean lockStatus; + /** + * 成交状态 + */ + private Boolean dealStatus; + /** + * 所属行业 + * + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY} + */ + private Integer industryId; + /** + * 客户等级 + * + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL} + */ + private Integer level; + /** + * 客户来源 + * + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE} + */ + private Integer source; + + /** + * 手机 + */ + @Mobile + private String mobile; + /** + * 电话 + */ + @Telephone + private String telephone; + /** + * 网址 + */ + private String website; + /** + * QQ + */ + private String qq; + /** + * wechat + */ + private String wechat; + + /** + * 邮箱 + */ + @Email(message = "邮箱格式不正确") + private String email; + + /** + * 客户描述 + */ + @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符") + private String description; + /** + * 备注 + */ + private String remark; + /** + * 负责人的用户编号 + * + * 关联 AdminUserDO 的 id 字段 + */ + private Long ownerUserId; + /** + * 所在地 + * + * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段 + */ + private Integer areaId; + /** + * 详细地址 + */ + private String detailAddress; + + /** + * 最后跟进时间 + */ + private LocalDateTime contactLastTime; + /** + * 最后跟进内容 + */ + private String contactLastContent; + /** + * 下次联系时间 + */ + private LocalDateTime contactNextTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java index aedab18691..d2a7cb8f98 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java @@ -4,8 +4,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO; +import jakarta.validation.Valid; -import javax.validation.Valid; +import java.util.Collection; +import java.util.List; /** * 跟进记录 Service 接口 @@ -22,6 +25,13 @@ public interface CrmFollowUpRecordService { */ Long createFollowUpRecord(@Valid CrmFollowUpRecordSaveReqVO createReqVO); + /** + * 创建更进 + * + * @param list 请求 + */ + void createFollowUpRecordBatch(List list); + /** * 删除跟进记录 (数据权限基于 bizType、 bizId) * @@ -30,6 +40,14 @@ public interface CrmFollowUpRecordService { */ void deleteFollowUpRecord(Long id, Long userId); + /** + * 删除跟进 + * + * @param bizType 模块类型 + * @param bizId 模块数据编号 + */ + void deleteFollowUpRecordByBiz(Integer bizType, Long bizId); + /** * 获得跟进记录 * @@ -46,4 +64,13 @@ public interface CrmFollowUpRecordService { */ PageResult getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO); + /** + * 获取跟进记录 + * + * @param bizType 模块类型 + * @param bizIds 模块数据编号 + * @return 跟进列表 + */ + List getFollowUpRecordByBiz(Integer bizType, Collection bizIds); + } \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java index 7a1ca73b63..88f0b887cb 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java @@ -7,25 +7,29 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; -import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; import cn.iocoder.yudao.module.crm.dal.mysql.followup.CrmFollowUpRecordMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; -import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.clue.CrmClueService; import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; -import cn.iocoder.yudao.module.crm.service.contact.bo.CrmContactUpdateFollowUpReqBO; -import cn.iocoder.yudao.module.crm.service.followup.handle.CrmFollowUpHandler; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; import java.time.LocalDateTime; +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.framework.common.util.collection.CollectionUtils.anyMatch; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_DELETE_DENIED; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS; @@ -43,13 +47,23 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { private CrmFollowUpRecordMapper crmFollowUpRecordMapper; @Resource + @Lazy private CrmPermissionService permissionService; @Resource - private List followUpHandlers; - @Resource + @Lazy private CrmBusinessService businessService; @Resource + @Lazy + private CrmClueService clueService; + @Resource + @Lazy private CrmContactService contactService; + @Resource + @Lazy + private CrmContractService contractService; + @Resource + @Lazy + private CrmCustomerService customerService; @Override @CrmPermission(bizTypeValue = "#createReqVO.bizType", bizId = "#createReqVO.bizId", level = CrmPermissionLevelEnum.WRITE) @@ -59,40 +73,52 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { crmFollowUpRecordMapper.insert(followUpRecord); LocalDateTime now = LocalDateTime.now(); + CrmUpdateFollowUpReqBO updateFollowUpReqBO = new CrmUpdateFollowUpReqBO().setBizId(followUpRecord.getBizId()) + .setContactLastTime(now).setContactNextTime(followUpRecord.getNextTime()).setContactLastContent(followUpRecord.getContent()); // 2. 更新 bizId 对应的记录; - followUpHandlers.forEach(handler -> handler.execute(followUpRecord, now)); - // 3.1 更新 contactIds 对应的记录 - if (CollUtil.isNotEmpty(createReqVO.getContactIds())) { - // TODO @puhui999:可以用链式设置哈 - contactService.updateContactFollowUpBatch(convertList(createReqVO.getContactIds(), contactId -> { - CrmContactUpdateFollowUpReqBO crmContactUpdateFollowUpReqBO = new CrmContactUpdateFollowUpReqBO(); - crmContactUpdateFollowUpReqBO.setId(contactId).setContactNextTime(followUpRecord.getNextTime()) - .setContactLastTime(now).setContactLastContent(followUpRecord.getContent()); - return crmContactUpdateFollowUpReqBO; - })); + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_BUSINESS.getType(), followUpRecord.getBizType())) { // 更新商机跟进信息 + businessService.updateBusinessFollowUpBatch(Collections.singletonList(updateFollowUpReqBO)); } - // 3.2 需要更新 businessIds、contactIds 对应的记录 + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_LEADS.getType(), followUpRecord.getBizType())) { // 更新线索跟进信息 + clueService.updateClueFollowUp(updateFollowUpReqBO); + } + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTACT.getType(), followUpRecord.getBizType())) { // 更新联系人跟进信息 + contactService.updateContactFollowUpBatch(Collections.singletonList(updateFollowUpReqBO)); + } + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTRACT.getType(), followUpRecord.getBizType())) { // 更新合同跟进信息 + contractService.updateContractFollowUp(updateFollowUpReqBO); + } + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CUSTOMER.getType(), followUpRecord.getBizType())) { // 更新客户跟进信息 + customerService.updateCustomerFollowUp(updateFollowUpReqBO); + } + + // 3.1 更新 contactIds 对应的记录,不更新 lastTime 和 lastContent + if (CollUtil.isNotEmpty(createReqVO.getContactIds())) { + contactService.updateContactFollowUpBatch(convertList(createReqVO.getContactIds(), + contactId -> updateFollowUpReqBO.setBizId(contactId).setContactLastTime(null).setContactLastContent(null))); + } + // 3.2 需要更新 businessIds 对应的记录,不更新 lastTime 和 lastContent if (CollUtil.isNotEmpty(createReqVO.getBusinessIds())) { - businessService.updateContactFollowUpBatch(convertList(createReqVO.getBusinessIds(), businessId -> { - CrmBusinessUpdateFollowUpReqBO crmBusinessUpdateFollowUpReqBO = new CrmBusinessUpdateFollowUpReqBO(); - crmBusinessUpdateFollowUpReqBO.setId(businessId).setContactNextTime(followUpRecord.getNextTime()) - .setContactLastTime(now).setContactLastContent(followUpRecord.getContent()); - return crmBusinessUpdateFollowUpReqBO; - })); + businessService.updateBusinessFollowUpBatch(convertList(createReqVO.getBusinessIds(), + businessId -> updateFollowUpReqBO.setBizId(businessId).setContactLastTime(null).setContactLastContent(null))); } return followUpRecord.getId(); } + @Override + public void createFollowUpRecordBatch(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + crmFollowUpRecordMapper.insertBatch(BeanUtils.toBean(list, CrmFollowUpRecordDO.class)); + } + @Override public void deleteFollowUpRecord(Long id, Long userId) { // 校验存在 CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id); // 校验权限 - List permissionList = permissionService.getPermissionListByBiz( - followUpRecord.getBizType(), followUpRecord.getBizId()); - boolean condition = anyMatch(permissionList, permission -> - ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), CrmPermissionLevelEnum.OWNER.getLevel())); - if (!condition) { + if (!permissionService.hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(), userId, CrmPermissionLevelEnum.OWNER)) { throw exception(FOLLOW_UP_RECORD_DELETE_DENIED); } @@ -100,6 +126,11 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { crmFollowUpRecordMapper.deleteById(id); } + @Override + public void deleteFollowUpRecordByBiz(Integer bizType, Long bizId) { + crmFollowUpRecordMapper.deleteByBiz(bizType, bizId); + } + private CrmFollowUpRecordDO validateFollowUpRecordExists(Long id) { CrmFollowUpRecordDO followUpRecord = crmFollowUpRecordMapper.selectById(id); if (followUpRecord == null) { @@ -113,11 +144,15 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { return crmFollowUpRecordMapper.selectById(id); } - @Override @CrmPermission(bizTypeValue = "#pageReqVO.bizType", bizId = "#pageReqVO.bizId", level = CrmPermissionLevelEnum.READ) public PageResult getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO) { return crmFollowUpRecordMapper.selectPage(pageReqVO); } + @Override + public List getFollowUpRecordByBiz(Integer bizType, Collection bizIds) { + return crmFollowUpRecordMapper.selectListByBiz(bizType, bizIds); + } + } \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java new file mode 100644 index 0000000000..dec219e294 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.crm.service.followup.bo; + +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 跟进信息 Create Req BO + * + * @author HUIHUI + */ +@Data +public class CrmFollowUpCreateReqBO { + + /** + * 数据类型 + * + * 枚举 {@link CrmBizTypeEnum} + */ + @NotNull(message = "数据类型不能为空") + private Integer bizType; + /** + * 数据编号 + * + * 关联 {@link CrmBizTypeEnum} 对应模块 DO 的 id 字段 + */ + @NotNull(message = "数据编号不能为空") + private Long bizId; + + /** + * 跟进类型 + * + * 关联 {@link DictTypeConstants#CRM_FOLLOW_UP_TYPE} 字典 + */ + @NotNull(message = "跟进类型不能为空") + private Integer type; + /** + * 跟进内容 + */ + @NotEmpty(message = "跟进内容不能为空") + private String content; + /** + * 下次联系时间 + */ + @NotNull(message = "下次联系时间不能为空") + private LocalDateTime nextTime; + + /** + * 图片 + */ + private List picUrls; + /** + * 附件 + */ + private List fileUrls; + + /** + * 关联的商机编号数组 + * + * 关联 {@link CrmBusinessDO#getId()} + */ + private List businessIds; + + /** + * 关联的联系人编号数组 + * + * 关联 {@link CrmContactDO#getId()} + */ + private List contactIds; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerUpdateFollowUpReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java similarity index 70% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerUpdateFollowUpReqBO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java index 2ba7fbb05a..38e43b5ab1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerUpdateFollowUpReqBO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java @@ -1,22 +1,23 @@ -package cn.iocoder.yudao.module.crm.service.customer.bo; +package cn.iocoder.yudao.module.crm.service.followup.bo; import com.mzt.logapi.starter.annotation.DiffLogField; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import javax.validation.constraints.NotNull; import java.time.LocalDateTime; -// TODO @puhui999:是不是搞个通用的 ReqBO 就好了 /** * 跟进信息 Update Req BO * * @author HUIHUI */ @Data -public class CrmCustomerUpdateFollowUpReqBO { +public class CrmUpdateFollowUpReqBO { - @Schema(description = "主键", example = "3167") - private Long id; + @Schema(description = "数据编号", example = "3167") + @NotNull(message = "数据编号不能为空") + private Long bizId; @Schema(description = "最后跟进时间") @DiffLogField(name = "最后跟进时间") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmBusinessFollowUpHandler.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmBusinessFollowUpHandler.java deleted file mode 100644 index 6d2f14fc52..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmBusinessFollowUpHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.followup.handle; - -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; -import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; -import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; -import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateFollowUpReqBO; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Collections; - -/** - * CRM 商机的 {@link CrmFollowUpHandler} 实现类 - * - * @author HUIHUI - */ -@Component -public class CrmBusinessFollowUpHandler implements CrmFollowUpHandler { - - @Resource - private CrmBusinessService businessService; - - @Override - public void execute(CrmFollowUpRecordDO followUpRecord, LocalDateTime now) { - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_BUSINESS.getType(), followUpRecord.getBizType())) { - return; - } - - // 更新商机跟进信息 - CrmBusinessUpdateFollowUpReqBO businessUpdateFollowUpReqBO = new CrmBusinessUpdateFollowUpReqBO(); - businessUpdateFollowUpReqBO.setId(followUpRecord.getBizId()).setContactNextTime(followUpRecord.getNextTime()) - .setContactLastTime(now).setContactLastContent(followUpRecord.getContent()); - businessService.updateContactFollowUpBatch(Collections.singletonList(businessUpdateFollowUpReqBO)); - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmClueFollowUpHandler.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmClueFollowUpHandler.java deleted file mode 100644 index ba91f25979..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmClueFollowUpHandler.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.followup.handle; - -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; -import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; -import cn.iocoder.yudao.module.crm.service.clue.CrmClueService; -import cn.iocoder.yudao.module.crm.service.clue.bo.CrmClueUpdateFollowUpReqBO; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.time.LocalDateTime; - -/** - * CRM 线索的 {@link CrmFollowUpHandler} 实现类 - * - * @author HUIHUI - */ -@Component -public class CrmClueFollowUpHandler implements CrmFollowUpHandler { - - @Resource - private CrmClueService clueService; - - @Override - public void execute(CrmFollowUpRecordDO followUpRecord, LocalDateTime now) { - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_LEADS.getType(), followUpRecord.getBizType())) { - return; - } - - // 更新线索跟进信息 - CrmClueUpdateFollowUpReqBO clueUpdateFollowUpReqBO = new CrmClueUpdateFollowUpReqBO(); - clueUpdateFollowUpReqBO.setId(followUpRecord.getBizId()).setContactNextTime(followUpRecord.getNextTime()) - .setContactLastTime(now).setContactLastContent(followUpRecord.getContent()); - clueService.updateClueFollowUp(clueUpdateFollowUpReqBO); - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmContactFollowUpHandler.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmContactFollowUpHandler.java deleted file mode 100644 index 29412587b6..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmContactFollowUpHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.followup.handle; - -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; -import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; -import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; -import cn.iocoder.yudao.module.crm.service.contact.bo.CrmContactUpdateFollowUpReqBO; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Collections; - -/** - * CRM 联系人的 {@link CrmFollowUpHandler} 实现类 - * - * @author HUIHUI - */ -@Component -public class CrmContactFollowUpHandler implements CrmFollowUpHandler { - - @Resource - private CrmContactService contactService; - - @Override - public void execute(CrmFollowUpRecordDO followUpRecord, LocalDateTime now) { - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTACT.getType(), followUpRecord.getBizType())) { - return; - } - - // 更新联系人跟进信息 - CrmContactUpdateFollowUpReqBO contactUpdateFollowUpReqBO = new CrmContactUpdateFollowUpReqBO(); - contactUpdateFollowUpReqBO.setId(followUpRecord.getBizId()).setContactNextTime(followUpRecord.getNextTime()) - .setContactLastTime(now).setContactLastContent(followUpRecord.getContent()); - contactService.updateContactFollowUpBatch(Collections.singletonList(contactUpdateFollowUpReqBO)); - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmContractFollowUpHandler.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmContractFollowUpHandler.java deleted file mode 100644 index 5357499fa8..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmContractFollowUpHandler.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.followup.handle; - -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; -import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; -import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; -import cn.iocoder.yudao.module.crm.service.contract.bo.CrmContractUpdateFollowUpReqBO; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.time.LocalDateTime; - -/** - * CRM 合同的 {@link CrmFollowUpHandler} 实现类 - * - * @author HUIHUI - */ -@Component -public class CrmContractFollowUpHandler implements CrmFollowUpHandler { - - @Resource - private CrmContractService contractService; - - @Override - public void execute(CrmFollowUpRecordDO followUpRecord, LocalDateTime now) { - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTRACT.getType(), followUpRecord.getBizType())) { - return; - } - - // 更新合同跟进信息 - CrmContractUpdateFollowUpReqBO contractUpdateFollowUpReqBO = new CrmContractUpdateFollowUpReqBO(); - contractUpdateFollowUpReqBO.setId(followUpRecord.getBizId()).setContactNextTime(followUpRecord.getNextTime()) - .setContactLastTime(now).setContactLastContent(followUpRecord.getContent()); - contractService.updateContractFollowUp(contractUpdateFollowUpReqBO); - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmCustomerFollowUpHandler.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmCustomerFollowUpHandler.java deleted file mode 100644 index 294d8d9740..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmCustomerFollowUpHandler.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.followup.handle; - -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; -import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; -import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; -import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerUpdateFollowUpReqBO; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.time.LocalDateTime; - -/** - * CRM 客户的 {@link CrmFollowUpHandler} 实现类 - * - * @author HUIHUI - */ -@Component -public class CrmCustomerFollowUpHandler implements CrmFollowUpHandler { - - @Resource - private CrmCustomerService customerService; - - @Override - public void execute(CrmFollowUpRecordDO followUpRecord, LocalDateTime now) { - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CUSTOMER.getType(), followUpRecord.getBizType())) { - return; - } - - // 更新客户跟进信息 - CrmCustomerUpdateFollowUpReqBO customerUpdateFollowUpReqBO = new CrmCustomerUpdateFollowUpReqBO(); - customerUpdateFollowUpReqBO.setId(followUpRecord.getBizId()).setContactNextTime(followUpRecord.getNextTime()) - .setContactLastTime(now).setContactLastContent(followUpRecord.getContent()); - customerService.updateCustomerFollowUp(customerUpdateFollowUpReqBO); - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmFollowUpHandler.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmFollowUpHandler.java deleted file mode 100644 index 82bcec8358..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/handle/CrmFollowUpHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.followup.handle; - -import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; - -import java.time.LocalDateTime; - -/** - * CRM 跟进信息处理器 handler 接口 - * - * @author HUIHUI - */ -public interface CrmFollowUpHandler { - - // TODO @puhui999:需要考虑,下次联系时间为空; - /** - * 执行更新 - * - * @param followUpRecord 跟进记录 - * @param now 跟进时间 - */ - void execute(CrmFollowUpRecordDO followUpRecord, LocalDateTime now); - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmMessageService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogService.java similarity index 60% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmMessageService.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogService.java index ca2e870bf9..e00aa4fce0 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmMessageService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogService.java @@ -1,23 +1,23 @@ package cn.iocoder.yudao.module.crm.service.message; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.crm.controller.admin.message.vo.CrmTodayCustomerPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import javax.validation.Valid; /** - * CRM 代办消息 Service 接口 + * CRM 待办消息 Service 接口 * * @author dhb52 */ -public interface CrmMessageService { +public interface CrmBacklogService { /** - * TODO @dbh52:注释要写下 + * 根据【联系状态】、【场景类型】筛选客户分页 * - * @param pageReqVO - * @return + * @param pageReqVO 分页查询 + * @return 分页数据 */ PageResult getTodayCustomerPage(@Valid CrmTodayCustomerPageReqVO pageReqVO, Long userId); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmMessageServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogServiceImpl.java similarity index 75% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmMessageServiceImpl.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogServiceImpl.java index dc0e338c0b..5c24172667 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmMessageServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogServiceImpl.java @@ -1,18 +1,21 @@ package cn.iocoder.yudao.module.crm.service.message; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.crm.controller.admin.message.vo.CrmTodayCustomerPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; +import jakarta.annotation.Resource; import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; - -// TODO @dbh52:注释要写下 +/** + * 待办消息 Service 实现类 + * + * @author dhb52 + */ @Component @Validated -public class CrmMessageServiceImpl implements CrmMessageService { +public class CrmBacklogServiceImpl implements CrmBacklogService { @Resource private CrmCustomerMapper customerMapper; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java index d63589a7c1..544a5c56a0 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java @@ -108,4 +108,15 @@ public interface CrmPermissionService { */ List getPermissionListByBizTypeAndUserId(Integer bizType, Long userId); + /** + * 校验是否有指定数据的操作权限 + * + * @param bizType 数据类型,关联 {@link CrmBizTypeEnum} + * @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId() + * @param userId 用户编号 + * @param level 权限级别 + * @return 是否有权限 + */ + boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java index 20df43d1b9..f16b2df46a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.service.permission; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; import cn.iocoder.yudao.module.crm.convert.permission.CrmPermissionConvert; import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; @@ -23,6 +24,7 @@ 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.anyMatch; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isOwner; @@ -50,7 +52,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { adminUserApi.validateUserList(Collections.singletonList(createReqBO.getUserId())); // 2. 创建 - CrmPermissionDO permission = CrmPermissionConvert.INSTANCE.convert(createReqBO); + CrmPermissionDO permission = BeanUtils.toBean(createReqBO, CrmPermissionDO.class); permissionMapper.insert(permission); return permission.getId(); } @@ -62,7 +64,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { adminUserApi.validateUserList(convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId)); // 2. 创建 - List permissions = CrmPermissionConvert.INSTANCE.convertList(createReqBOs); + List permissions = BeanUtils.toBean(createReqBOs, CrmPermissionDO.class); permissionMapper.insertBatch(permissions); } @@ -167,7 +169,10 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { throw exception(CRM_PERMISSION_DELETE_FAIL); } // 校验操作人是否为负责人 - CrmPermissionDO permission = permissionMapper.selectByIdAndUserId(permissions.get(0).getBizId(), userId); + CrmPermissionDO permission = permissionMapper.selectByBizIdAndUserId(permissions.getFirst().getBizId(), userId); + if (permission == null) { + throw exception(CRM_PERMISSION_DELETE_DENIED); + } if (!CrmPermissionLevelEnum.isOwner(permission.getLevel())) { throw exception(CRM_PERMISSION_DELETE_DENIED); } @@ -207,4 +212,11 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { return permissionMapper.selectListByBizTypeAndUserId(bizType, userId); } + @Override + public boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level) { + List permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId); + return anyMatch(permissionList, permission -> + ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), level.getLevel())); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionUpdateReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionUpdateReqBO.java deleted file mode 100644 index 2e38b958f8..0000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionUpdateReqBO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.crm.service.permission.bo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -/** - * crm 数据权限 Update Req BO - * - * @author HUIHUI - */ -@Data -public class CrmPermissionUpdateReqBO { - - /** - * 数据权限编号 - */ - @NotNull(message = "数据权限编号不能为空") - private Long id; - - /** - * 当前登录用户编号 - */ - @NotNull(message = "用户编号不能为空") - private Long userId; - - /** - * 权限级别 - */ - @NotNull(message = "权限级别不能为空") - @InEnum(CrmPermissionLevelEnum.class) - private Integer level; - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java index 6c4e5ced00..b22bd6f18f 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java @@ -69,4 +69,13 @@ public interface CrmProductService { * @return 产品 */ CrmProductDO getProductByCategoryId(Long categoryId); + + /** + * 获得产品列表 + * + * @param ids 产品编号 + * @return 产品列表 + */ + List getProductListByIds(Collection ids); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java index 7dc18a5af3..d1b6a62cf5 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java @@ -155,4 +155,12 @@ public class CrmProductServiceImpl implements CrmProductService { return productMapper.selectOne(new LambdaQueryWrapper().eq(CrmProductDO::getCategoryId, categoryId)); } + @Override + public List getProductListByIds(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return productMapper.selectBatchIds(ids); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java index 682b8d6d64..9ac2e7f431 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java @@ -23,10 +23,11 @@ import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqB import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; import java.util.Collection; import java.util.List; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java index 8092ac3d02..effe0d7206 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java @@ -26,10 +26,11 @@ import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqB import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; import java.util.Collection; import java.util.List; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java index dc849622ec..cfd2f8c1b8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java @@ -84,10 +84,9 @@ public class CrmQueryWrapperUtils { if (CrmPermissionUtils.isCrmAdmin()) {// 管理员不需要数据权限 return; } - query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType).in(CrmPermissionDO::getBizId, bizIds) - .in(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId)); + .eq(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId)); } /** diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml new file mode 100644 index 0000000000..10030e0a74 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java index 2dd425eb5f..740bd97732 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java @@ -15,6 +15,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; @@ -45,7 +46,7 @@ public class CrmClueServiceImplTest extends BaseDbUnitTest { CrmClueSaveReqVO reqVO = randomPojo(CrmClueSaveReqVO.class); // 调用 - Long clueId = clueService.createClue(reqVO); + Long clueId = clueService.createClue(reqVO, getLoginUserId()); // 断言 assertNotNull(clueId); // 校验记录的属性是否正确 diff --git a/yudao-module-erp/pom.xml b/yudao-module-erp/pom.xml new file mode 100644 index 0000000000..22fe0eea4d --- /dev/null +++ b/yudao-module-erp/pom.xml @@ -0,0 +1,24 @@ + + + + cn.iocoder.boot + yudao + ${revision} + + + yudao-module-erp-api + yudao-module-erp-biz + + 4.0.0 + yudao-module-erp + pom + + ${project.artifactId} + + erp 包下,企业资源管理(Enterprise Resource Planning)。 + 例如说:采购、销售、库存、财务、产品等等 + + + \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-api/pom.xml b/yudao-module-erp/yudao-module-erp-api/pom.xml new file mode 100644 index 0000000000..961845ce3c --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-api/pom.xml @@ -0,0 +1,33 @@ + + + + cn.iocoder.boot + yudao-module-erp + ${revision} + + 4.0.0 + yudao-module-erp-api + jar + + ${project.artifactId} + + erp 模块 API,暴露给其它模块调用 + + + + + cn.iocoder.boot + yudao-common + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + + \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/package-info.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/package-info.java new file mode 100644 index 0000000000..540f18f0e0 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/package-info.java @@ -0,0 +1,4 @@ +/** + * erp API 包,定义暴露给其它模块的 API + */ +package cn.iocoder.yudao.module.erp.api; diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/DictTypeConstants.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/DictTypeConstants.java new file mode 100644 index 0000000000..36d4df852e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/DictTypeConstants.java @@ -0,0 +1,13 @@ +package cn.iocoder.yudao.module.erp.enums; + +/** + * ERP 字典类型的枚举类 + * + * @author 芋道源码 + */ +public interface DictTypeConstants { + + String AUDIT_STATUS = "erp_audit_status"; // 审核状态 + String STOCK_RECORD_BIZ_TYPE = "erp_stock_record_biz_type"; // 库存明细的业务类型 + +} diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java new file mode 100644 index 0000000000..a10147a704 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.erp.enums; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * ERP 审核状态枚举 + * + * TODO 芋艿:目前只有待审批、已审批两个状态,未来接入工作流后,会丰富下:待提交(草稿)=》已提交(待审核)=》审核通过、审核不通过;另外,工作流需要支持“反审核”,把工作流退回到原点; + * + * @author 芋道源码 + */ +@RequiredArgsConstructor +@Getter +public enum ErpAuditStatus implements IntArrayValuable { + + PROCESS(10, "未审核"), // 审核中 + APPROVE(20, "已审核"); // 审核通过 + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpAuditStatus::getStatus).toArray(); + + /** + * 状态 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java new file mode 100644 index 0000000000..65f64c2f56 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java @@ -0,0 +1,168 @@ +package cn.iocoder.yudao.module.erp.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * ERP 错误码枚举类 + *

+ * erp 系统,使用 1-030-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== ERP 供应商(1-030-100-000) ========== + ErrorCode SUPPLIER_NOT_EXISTS = new ErrorCode(1_030_100_000, "供应商不存在"); + ErrorCode SUPPLIER_NOT_ENABLE = new ErrorCode(1_030_100_000, "供应商({})未启用"); + + // ========== ERP 采购订单(1-030-101-000) ========== + ErrorCode PURCHASE_ORDER_NOT_EXISTS = new ErrorCode(1_030_101_000, "采购订单不存在"); + ErrorCode PURCHASE_ORDER_DELETE_FAIL_APPROVE = new ErrorCode(1_030_101_001, "采购订单({})已审核,无法删除"); + ErrorCode PURCHASE_ORDER_PROCESS_FAIL = new ErrorCode(1_030_101_002, "反审核失败,只有已审核的采购订单才能反审核"); + ErrorCode PURCHASE_ORDER_APPROVE_FAIL = new ErrorCode(1_030_101_003, "审核失败,只有未审核的采购订单才能审核"); + ErrorCode PURCHASE_ORDER_NO_EXISTS = new ErrorCode(1_030_101_004, "生成采购单号失败,请重新提交"); + ErrorCode PURCHASE_ORDER_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_101_005, "采购订单({})已审核,无法修改"); + ErrorCode PURCHASE_ORDER_NOT_APPROVE = new ErrorCode(1_030_101_006, "采购订单未审核,无法操作"); + ErrorCode PURCHASE_ORDER_ITEM_IN_FAIL_PRODUCT_EXCEED = new ErrorCode(1_030_101_007, "采购订单项({})超过最大允许入库数量({})"); + ErrorCode PURCHASE_ORDER_PROCESS_FAIL_EXISTS_IN = new ErrorCode(1_030_101_008, "反审核失败,已存在对应的采购入库单"); +ErrorCode PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED = new ErrorCode(1_030_101_009, "采购订单项({})超过最大允许退货数量({})"); + ErrorCode PURCHASE_ORDER_PROCESS_FAIL_EXISTS_RETURN = new ErrorCode(1_030_101_010, "反审核失败,已存在对应的采购退货单"); + + // ========== ERP 采购入库(1-030-102-000) ========== + ErrorCode PURCHASE_IN_NOT_EXISTS = new ErrorCode(1_030_102_000, "采购入库单不存在"); + ErrorCode PURCHASE_IN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_102_001, "采购入库单({})已审核,无法删除"); + ErrorCode PURCHASE_IN_PROCESS_FAIL = new ErrorCode(1_030_102_002, "反审核失败,只有已审核的入库单才能反审核"); + ErrorCode PURCHASE_IN_APPROVE_FAIL = new ErrorCode(1_030_102_003, "审核失败,只有未审核的入库单才能审核"); + ErrorCode PURCHASE_IN_NO_EXISTS = new ErrorCode(1_030_102_004, "生成入库单失败,请重新提交"); + ErrorCode PURCHASE_IN_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_102_005, "采购入库单({})已审核,无法修改"); + ErrorCode PURCHASE_IN_NOT_APPROVE = new ErrorCode(1_030_102_006, "采购入库单未审核,无法操作"); + ErrorCode PURCHASE_IN_FAIL_PAYMENT_PRICE_EXCEED = new ErrorCode(1_030_102_007, "付款金额({})超过采购入库单总金额({})"); + ErrorCode PURCHASE_IN_PROCESS_FAIL_EXISTS_PAYMENT = new ErrorCode(1_030_102_008, "反审核失败,已存在对应的付款单"); + + // ========== ERP 采购退货(1-030-103-000) ========== + ErrorCode PURCHASE_RETURN_NOT_EXISTS = new ErrorCode(1_030_103_000, "采购退货单不存在"); + ErrorCode PURCHASE_RETURN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_103_001, "采购退货单({})已审核,无法删除"); + ErrorCode PURCHASE_RETURN_PROCESS_FAIL = new ErrorCode(1_030_103_002, "反审核失败,只有已审核的退货单才能反审核"); + ErrorCode PURCHASE_RETURN_APPROVE_FAIL = new ErrorCode(1_030_103_003, "审核失败,只有未审核的退货单才能审核"); + ErrorCode PURCHASE_RETURN_NO_EXISTS = new ErrorCode(1_030_103_004, "生成退货单失败,请重新提交"); + ErrorCode PURCHASE_RETURN_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_103_005, "采购退货单({})已审核,无法修改"); + ErrorCode PURCHASE_RETURN_NOT_APPROVE = new ErrorCode(1_030_103_006, "采购退货单未审核,无法操作"); + ErrorCode PURCHASE_RETURN_FAIL_REFUND_PRICE_EXCEED = new ErrorCode(1_030_103_007, "退款金额({})超过采购退货单总金额({})"); + ErrorCode PURCHASE_RETURN_PROCESS_FAIL_EXISTS_REFUND = new ErrorCode(1_030_103_008, "反审核失败,已存在对应的退款单"); + + // ========== ERP 客户(1-030-200-000)========== + ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_200_000, "客户不存在"); + ErrorCode CUSTOMER_NOT_ENABLE = new ErrorCode(1_020_200_001, "客户({})未启用"); + + // ========== ERP 销售订单(1-030-201-000) ========== + ErrorCode SALE_ORDER_NOT_EXISTS = new ErrorCode(1_020_201_000, "销售订单不存在"); + ErrorCode SALE_ORDER_DELETE_FAIL_APPROVE = new ErrorCode(1_020_201_001, "销售订单({})已审核,无法删除"); + ErrorCode SALE_ORDER_PROCESS_FAIL = new ErrorCode(1_020_201_002, "反审核失败,只有已审核的销售订单才能反审核"); + ErrorCode SALE_ORDER_APPROVE_FAIL = new ErrorCode(1_020_201_003, "审核失败,只有未审核的销售订单才能审核"); + ErrorCode SALE_ORDER_NO_EXISTS = new ErrorCode(1_020_201_004, "生成销售单号失败,请重新提交"); + ErrorCode SALE_ORDER_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_201_005, "销售订单({})已审核,无法修改"); + ErrorCode SALE_ORDER_NOT_APPROVE = new ErrorCode(1_020_201_006, "销售订单未审核,无法操作"); + ErrorCode SALE_ORDER_ITEM_OUT_FAIL_PRODUCT_EXCEED = new ErrorCode(1_020_201_007, "销售订单项({})超过最大允许出库数量({})"); + ErrorCode SALE_ORDER_PROCESS_FAIL_EXISTS_OUT = new ErrorCode(1_020_201_008, "反审核失败,已存在对应的销售出库单"); + ErrorCode SALE_ORDER_ITEM_RETURN_FAIL_OUT_EXCEED = new ErrorCode(1_020_201_009, "销售订单项({})超过最大允许退货数量({})"); + ErrorCode SALE_ORDER_PROCESS_FAIL_EXISTS_RETURN = new ErrorCode(1_020_201_010, "反审核失败,已存在对应的销售退货单"); + + // ========== ERP 销售出库(1-030-202-000) ========== + ErrorCode SALE_OUT_NOT_EXISTS = new ErrorCode(1_020_202_000, "销售出库单不存在"); + ErrorCode SALE_OUT_DELETE_FAIL_APPROVE = new ErrorCode(1_020_202_001, "销售出库单({})已审核,无法删除"); + ErrorCode SALE_OUT_PROCESS_FAIL = new ErrorCode(1_020_202_002, "反审核失败,只有已审核的出库单才能反审核"); + ErrorCode SALE_OUT_APPROVE_FAIL = new ErrorCode(1_020_202_003, "审核失败,只有未审核的出库单才能审核"); + ErrorCode SALE_OUT_NO_EXISTS = new ErrorCode(1_020_202_004, "生成出库单失败,请重新提交"); + ErrorCode SALE_OUT_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_202_005, "销售出库单({})已审核,无法修改"); + ErrorCode SALE_OUT_NOT_APPROVE = new ErrorCode(1_020_202_006, "销售出库单未审核,无法操作"); + ErrorCode SALE_OUT_FAIL_RECEIPT_PRICE_EXCEED = new ErrorCode(1_020_202_007, "收款金额({})超过销售出库单总金额({})"); + ErrorCode SALE_OUT_PROCESS_FAIL_EXISTS_RECEIPT = new ErrorCode(1_020_202_008, "反审核失败,已存在对应的收款单"); + + // ========== ERP 销售退货(1-030-203-000) ========== + ErrorCode SALE_RETURN_NOT_EXISTS = new ErrorCode(1_020_203_000, "销售退货单不存在"); + ErrorCode SALE_RETURN_DELETE_FAIL_APPROVE = new ErrorCode(1_020_203_001, "销售退货单({})已审核,无法删除"); + ErrorCode SALE_RETURN_PROCESS_FAIL = new ErrorCode(1_020_203_002, "反审核失败,只有已审核的退货单才能反审核"); + ErrorCode SALE_RETURN_APPROVE_FAIL = new ErrorCode(1_020_203_003, "审核失败,只有未审核的退货单才能审核"); + ErrorCode SALE_RETURN_NO_EXISTS = new ErrorCode(1_020_203_004, "生成退货单失败,请重新提交"); + ErrorCode SALE_RETURN_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_203_005, "销售退货单({})已审核,无法修改"); + ErrorCode SALE_RETURN_NOT_APPROVE = new ErrorCode(1_020_203_006, "销售退货单未审核,无法操作"); + ErrorCode SALE_RETURN_FAIL_REFUND_PRICE_EXCEED = new ErrorCode(1_020_203_007, "退款金额({})超过销售退货单总金额({})"); + ErrorCode SALE_RETURN_PROCESS_FAIL_EXISTS_REFUND = new ErrorCode(1_020_203_008, "反审核失败,已存在对应的退款单"); + + // ========== ERP 仓库 1-030-400-000 ========== + ErrorCode WAREHOUSE_NOT_EXISTS = new ErrorCode(1_030_400_000, "仓库不存在"); + ErrorCode WAREHOUSE_NOT_ENABLE = new ErrorCode(1_030_400_001, "仓库({})未启用"); + + // ========== ERP 其它入库单 1-030-401-000 ========== + ErrorCode STOCK_IN_NOT_EXISTS = new ErrorCode(1_030_401_000, "其它入库单不存在"); + ErrorCode STOCK_IN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_401_001, "其它入库单({})已审核,无法删除"); + ErrorCode STOCK_IN_PROCESS_FAIL = new ErrorCode(1_030_401_002, "反审核失败,只有已审核的入库单才能反审核"); + ErrorCode STOCK_IN_APPROVE_FAIL = new ErrorCode(1_030_401_003, "审核失败,只有未审核的入库单才能审核"); + ErrorCode STOCK_IN_NO_EXISTS = new ErrorCode(1_030_401_004, "生成入库单失败,请重新提交"); + ErrorCode STOCK_IN_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_401_005, "其它入库单({})已审核,无法修改"); + + // ========== ERP 其它出库单 1-030-402-000 ========== + ErrorCode STOCK_OUT_NOT_EXISTS = new ErrorCode(1_030_402_000, "其它出库单不存在"); + ErrorCode STOCK_OUT_DELETE_FAIL_APPROVE = new ErrorCode(1_030_402_001, "其它出库单({})已审核,无法删除"); + ErrorCode STOCK_OUT_PROCESS_FAIL = new ErrorCode(1_030_402_002, "反审核失败,只有已审核的出库单才能反审核"); + ErrorCode STOCK_OUT_APPROVE_FAIL = new ErrorCode(1_030_402_003, "审核失败,只有未审核的出库单才能审核"); + ErrorCode STOCK_OUT_NO_EXISTS = new ErrorCode(1_030_402_004, "生成出库单失败,请重新提交"); + ErrorCode STOCK_OUT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_402_005, "其它出库单({})已审核,无法修改"); + + // ========== ERP 库存调拨单 1-030-403-000 ========== + ErrorCode STOCK_MOVE_NOT_EXISTS = new ErrorCode(1_030_402_000, "库存调拨单不存在"); + ErrorCode STOCK_MOVE_DELETE_FAIL_APPROVE = new ErrorCode(1_030_402_001, "库存调拨单({})已审核,无法删除"); + ErrorCode STOCK_MOVE_PROCESS_FAIL = new ErrorCode(1_030_402_002, "反审核失败,只有已审核的调拨单才能反审核"); + ErrorCode STOCK_MOVE_APPROVE_FAIL = new ErrorCode(1_030_402_003, "审核失败,只有未审核的调拨单才能审核"); + ErrorCode STOCK_MOVE_NO_EXISTS = new ErrorCode(1_030_402_004, "生成调拨号失败,请重新提交"); + ErrorCode STOCK_MOVE_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_402_005, "库存调拨单({})已审核,无法修改"); + + // ========== ERP 库存盘点单 1-030-403-000 ========== + ErrorCode STOCK_CHECK_NOT_EXISTS = new ErrorCode(1_030_403_000, "库存盘点单不存在"); + ErrorCode STOCK_CHECK_DELETE_FAIL_APPROVE = new ErrorCode(1_030_403_001, "库存盘点单({})已审核,无法删除"); + ErrorCode STOCK_CHECK_PROCESS_FAIL = new ErrorCode(1_030_403_002, "反审核失败,只有已审核的盘点单才能反审核"); + ErrorCode STOCK_CHECK_APPROVE_FAIL = new ErrorCode(1_030_403_003, "审核失败,只有未审核的盘点单才能审核"); + ErrorCode STOCK_CHECK_NO_EXISTS = new ErrorCode(1_030_403_004, "生成盘点号失败,请重新提交"); + ErrorCode STOCK_CHECK_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_403_005, "库存盘点单({})已审核,无法修改"); + + // ========== ERP 产品库存 1-030-404-000 ========== + ErrorCode STOCK_COUNT_NEGATIVE = new ErrorCode(1_030_404_000, "操作失败,产品({})所在仓库({})的库存:{},小于变更数量:{}"); + ErrorCode STOCK_COUNT_NEGATIVE2 = new ErrorCode(1_030_404_001, "操作失败,产品({})所在仓库({})的库存不足"); + + // ========== ERP 产品 1-030-500-000 ========== + ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_030_500_000, "产品不存在"); + ErrorCode PRODUCT_NOT_ENABLE = new ErrorCode(1_030_500_001, "产品({})未启用"); + + // ========== ERP 产品分类 1-030-501-000 ========== + ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_501_000, "产品分类不存在"); + ErrorCode PRODUCT_CATEGORY_EXITS_CHILDREN = new ErrorCode(1_030_501_001, "存在存在子产品分类,无法删除"); + ErrorCode PRODUCT_CATEGORY_PARENT_NOT_EXITS = new ErrorCode(1_030_501_002,"父级产品分类不存在"); + ErrorCode PRODUCT_CATEGORY_PARENT_ERROR = new ErrorCode(1_030_501_003, "不能设置自己为父产品分类"); + ErrorCode PRODUCT_CATEGORY_NAME_DUPLICATE = new ErrorCode(1_030_501_004, "已经存在该分类名称的产品分类"); + ErrorCode PRODUCT_CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_030_501_005, "不能设置自己的子分类为父分类"); + ErrorCode PRODUCT_CATEGORY_EXITS_PRODUCT = new ErrorCode(1_030_502_002, "存在产品使用该分类,无法删除"); + + // ========== ERP 产品单位 1-030-502-000 ========== + ErrorCode PRODUCT_UNIT_NOT_EXISTS = new ErrorCode(1_030_502_000, "产品单位不存在"); + ErrorCode PRODUCT_UNIT_NAME_DUPLICATE = new ErrorCode(1_030_502_001, "已存在该名字的产品单位"); + ErrorCode PRODUCT_UNIT_EXITS_PRODUCT = new ErrorCode(1_030_502_002, "存在产品使用该单位,无法删除"); + + // ========== ERP 结算账户 1-030-600-000 ========== + ErrorCode ACCOUNT_NOT_EXISTS = new ErrorCode(1_030_600_000, "结算账户不存在"); + ErrorCode ACCOUNT_NOT_ENABLE = new ErrorCode(1_030_600_001, "结算账户({})未启用"); + + // ========== ERP 付款单 1-030-601-000 ========== + ErrorCode FINANCE_PAYMENT_NOT_EXISTS = new ErrorCode(1_030_601_000, "付款单不存在"); + ErrorCode FINANCE_PAYMENT_DELETE_FAIL_APPROVE = new ErrorCode(1_030_601_001, "付款单({})已审核,无法删除"); + ErrorCode FINANCE_PAYMENT_PROCESS_FAIL = new ErrorCode(1_030_601_002, "反审核失败,只有已审核的付款单才能反审核"); + ErrorCode FINANCE_PAYMENT_APPROVE_FAIL = new ErrorCode(1_030_601_003, "审核失败,只有未审核的付款单才能审核"); + ErrorCode FINANCE_PAYMENT_NO_EXISTS = new ErrorCode(1_030_601_004, "生成付款单号失败,请重新提交"); + ErrorCode FINANCE_PAYMENT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_601_005, "付款单({})已审核,无法修改"); + + // ========== ERP 收款单 1-030-602-000 ========== + ErrorCode FINANCE_RECEIPT_NOT_EXISTS = new ErrorCode(1_030_602_000, "收款单不存在"); + ErrorCode FINANCE_RECEIPT_DELETE_FAIL_APPROVE = new ErrorCode(1_030_602_001, "收款单({})已审核,无法删除"); + ErrorCode FINANCE_RECEIPT_PROCESS_FAIL = new ErrorCode(1_030_602_002, "反审核失败,只有已审核的收款单才能反审核"); + ErrorCode FINANCE_RECEIPT_APPROVE_FAIL = new ErrorCode(1_030_602_003, "审核失败,只有未审核的收款单才能审核"); + ErrorCode FINANCE_RECEIPT_NO_EXISTS = new ErrorCode(1_030_602_004, "生成收款单号失败,请重新提交"); + ErrorCode FINANCE_RECEIPT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_602_005, "收款单({})已审核,无法修改"); + +} diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/LogRecordConstants.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/LogRecordConstants.java new file mode 100644 index 0000000000..73b72c0a92 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/LogRecordConstants.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.erp.enums; + +/** + * ERP 操作日志枚举 + * 目的:统一管理,也减少 Service 里各种“复杂”字符串 + * + * @author 芋道源码 + */ +public interface LogRecordConstants { + + +} diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java new file mode 100644 index 0000000000..bba2b309b3 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.erp.enums.common; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * ERP 业务类型枚举 + * + * @author HUIHUI + */ +@RequiredArgsConstructor +@Getter +public enum ErpBizTypeEnum implements IntArrayValuable { + + PURCHASE_ORDER(10, "采购订单"), + PURCHASE_IN(11, "采购入库"), + PURCHASE_RETURN(12, "采购退货"), + + SALE_ORDER(20, "销售订单"), + SALE_OUT(21, "销售订单"), + SALE_RETURN(22, "销售退货"), + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpBizTypeEnum::getType).toArray(); + + /** + * 类型 + */ + private final Integer type; + /** + * 名称 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java new file mode 100644 index 0000000000..559bf4ccf7 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.erp.enums.stock; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * ERP 库存明细 - 业务类型枚举 + * + * @author 芋道源码 + */ +@RequiredArgsConstructor +@Getter +public enum ErpStockRecordBizTypeEnum implements IntArrayValuable { + + OTHER_IN(10, "其它入库"), + OTHER_IN_CANCEL(11, "其它入库(作废)"), + + OTHER_OUT(20, "其它出库"), + OTHER_OUT_CANCEL(21, "其它出库(作废)"), + + MOVE_IN(30, "调拨入库"), + MOVE_IN_CANCEL(31, "调拨入库(作废)"), + MOVE_OUT(32, "调拨出库"), + MOVE_OUT_CANCEL(33, "调拨出库(作废)"), + + CHECK_MORE_IN(40, "盘盈入库"), + CHECK_MORE_IN_CANCEL(41, "盘盈入库(作废)"), + CHECK_LESS_OUT(42, "盘亏出库"), + CHECK_LESS_OUT_CANCEL(43, "盘亏出库(作废)"), + + SALE_OUT(50, "销售出库"), + SALE_OUT_CANCEL(51, "销售出库(作废)"), + + SALE_RETURN(60, "销售退货入库"), + SALE_RETURN_CANCEL(61, "销售退货入库(作废)"), + + PURCHASE_IN(70, "采购入库"), + PURCHASE_IN_CANCEL(71, "采购入库(作废)"), + + PURCHASE_RETURN(80, "采购退货出库"), + PURCHASE_RETURN_CANCEL(81, "采购退货出库(作废)"), + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpStockRecordBizTypeEnum::getType).toArray(); + + /** + * 类型 + */ + private final Integer type; + /** + * 名字 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/pom.xml b/yudao-module-erp/yudao-module-erp-biz/pom.xml new file mode 100644 index 0000000000..1d0b441624 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/pom.xml @@ -0,0 +1,77 @@ + + + + cn.iocoder.boot + yudao-module-erp + ${revision} + + 4.0.0 + yudao-module-erp-biz + + ${project.artifactId} + + erp 包下,企业资源管理(Enterprise Resource Planning)。 + 例如说:采购、销售、库存、财务、产品等等 + + + + + cn.iocoder.boot + yudao-module-system-api + ${revision} + + + cn.iocoder.boot + yudao-module-erp-api + ${revision} + + + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-operatelog + + + + + cn.iocoder.boot + yudao-spring-boot-starter-web + + + + cn.iocoder.boot + yudao-spring-boot-starter-security + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mybatis + + + + cn.iocoder.boot + yudao-spring-boot-starter-redis + + + + + cn.iocoder.boot + yudao-spring-boot-starter-excel + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-dict + + + + + cn.iocoder.boot + yudao-spring-boot-starter-test + + + + + \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java new file mode 100644 index 0000000000..4c8c980584 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java @@ -0,0 +1,116 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +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.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 结算账户") +@RestController +@RequestMapping("/erp/account") +@Validated +public class ErpAccountController { + + @Resource + private ErpAccountService accountService; + + @PostMapping("/create") + @Operation(summary = "创建结算账户") + @PreAuthorize("@ss.hasPermission('erp:account:create')") + public CommonResult createAccount(@Valid @RequestBody ErpAccountSaveReqVO createReqVO) { + return success(accountService.createAccount(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新结算账户") + @PreAuthorize("@ss.hasPermission('erp:account:update')") + public CommonResult updateAccount(@Valid @RequestBody ErpAccountSaveReqVO updateReqVO) { + accountService.updateAccount(updateReqVO); + return success(true); + } + + @PutMapping("/update-default-status") + @Operation(summary = "更新结算账户默认状态") + @Parameters({ + @Parameter(name = "id", description = "编号", required = true), + @Parameter(name = "status", description = "状态", required = true) + }) + public CommonResult updateAccountDefaultStatus(@RequestParam("id") Long id, + @RequestParam("defaultStatus") Boolean defaultStatus) { + accountService.updateAccountDefaultStatus(id, defaultStatus); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除结算账户") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('erp:account:delete')") + public CommonResult deleteAccount(@RequestParam("id") Long id) { + accountService.deleteAccount(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得结算账户") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:account:query')") + public CommonResult getAccount(@RequestParam("id") Long id) { + ErpAccountDO account = accountService.getAccount(id); + return success(BeanUtils.toBean(account, ErpAccountRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得结算账户精简列表", description = "只包含被开启的结算账户,主要用于前端的下拉选项") + public CommonResult> getWarehouseSimpleList() { + List list = accountService.getAccountListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, account -> new ErpAccountRespVO().setId(account.getId()) + .setName(account.getName()).setDefaultStatus(account.getDefaultStatus()))); + } + + @GetMapping("/page") + @Operation(summary = "获得结算账户分页") + @PreAuthorize("@ss.hasPermission('erp:account:query')") + public CommonResult> getAccountPage(@Valid ErpAccountPageReqVO pageReqVO) { + PageResult pageResult = accountService.getAccountPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, ErpAccountRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出结算账户 Excel") + @PreAuthorize("@ss.hasPermission('erp:account:export')") + @OperateLog(type = EXPORT) + public void exportAccountExcel(@Valid ErpAccountPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = accountService.getAccountPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "结算账户.xls", "数据", ErpAccountRespVO.class, + BeanUtils.toBean(list, ErpAccountRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java new file mode 100644 index 0000000000..b1f028a282 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java @@ -0,0 +1,153 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance; + +import cn.hutool.core.collection.CollUtil; +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.number.NumberUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; +import cn.iocoder.yudao.module.erp.service.finance.ErpFinancePaymentService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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 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.*; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 付款单") +@RestController +@RequestMapping("/erp/finance-payment") +@Validated +public class ErpFinancePaymentController { + + @Resource + private ErpFinancePaymentService financePaymentService; + @Resource + private ErpSupplierService supplierService; + @Resource + private ErpAccountService accountService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建付款单") + @PreAuthorize("@ss.hasPermission('erp:finance-payment:create')") + public CommonResult createFinancePayment(@Valid @RequestBody ErpFinancePaymentSaveReqVO createReqVO) { + return success(financePaymentService.createFinancePayment(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新付款单") + @PreAuthorize("@ss.hasPermission('erp:finance-payment:update')") + public CommonResult updateFinancePayment(@Valid @RequestBody ErpFinancePaymentSaveReqVO updateReqVO) { + financePaymentService.updateFinancePayment(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新付款单的状态") + @PreAuthorize("@ss.hasPermission('erp:finance-payment:update-status')") + public CommonResult updateFinancePaymentStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + financePaymentService.updateFinancePaymentStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除付款单") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:finance-payment:delete')") + public CommonResult deleteFinancePayment(@RequestParam("ids") List ids) { + financePaymentService.deleteFinancePayment(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得付款单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:finance-payment:query')") + public CommonResult getFinancePayment(@RequestParam("id") Long id) { + ErpFinancePaymentDO payment = financePaymentService.getFinancePayment(id); + if (payment == null) { + return success(null); + } + List paymentItemList = financePaymentService.getFinancePaymentItemListByPaymentId(id); + return success(BeanUtils.toBean(payment, ErpFinancePaymentRespVO.class, financePaymentVO -> + financePaymentVO.setItems(BeanUtils.toBean(paymentItemList, ErpFinancePaymentRespVO.Item.class)))); + } + + @GetMapping("/page") + @Operation(summary = "获得付款单分页") + @PreAuthorize("@ss.hasPermission('erp:finance-payment:query')") + public CommonResult> getFinancePaymentPage(@Valid ErpFinancePaymentPageReqVO pageReqVO) { + PageResult pageResult = financePaymentService.getFinancePaymentPage(pageReqVO); + return success(buildFinancePaymentVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出付款单 Excel") + @PreAuthorize("@ss.hasPermission('erp:finance-payment:export')") + @OperateLog(type = EXPORT) + public void exportFinancePaymentExcel(@Valid ErpFinancePaymentPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildFinancePaymentVOPageResult(financePaymentService.getFinancePaymentPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "付款单.xls", "数据", ErpFinancePaymentRespVO.class, list); + } + + private PageResult buildFinancePaymentVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 付款项 + List paymentItemList = financePaymentService.getFinancePaymentItemListByPaymentIds( + convertSet(pageResult.getList(), ErpFinancePaymentDO::getId)); + Map> financePaymentItemMap = convertMultiMap(paymentItemList, ErpFinancePaymentItemDO::getPaymentId); + // 1.2 供应商信息 + Map supplierMap = supplierService.getSupplierMap( + convertSet(pageResult.getList(), ErpFinancePaymentDO::getSupplierId)); + // 1.3 结算账户信息 + Map accountMap = accountService.getAccountMap( + convertSet(pageResult.getList(), ErpFinancePaymentDO::getAccountId)); + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(), + contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getFinanceUserId()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpFinancePaymentRespVO.class, payment -> { + payment.setItems(BeanUtils.toBean(financePaymentItemMap.get(payment.getId()), ErpFinancePaymentRespVO.Item.class)); + MapUtils.findAndThen(supplierMap, payment.getSupplierId(), supplier -> payment.setSupplierName(supplier.getName())); + MapUtils.findAndThen(accountMap, payment.getAccountId(), account -> payment.setAccountName(account.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(payment.getCreator()), user -> payment.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, payment.getFinanceUserId(), user -> payment.setFinanceUserName(user.getNickname())); + }); + } + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinanceReceiptController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinanceReceiptController.java new file mode 100644 index 0000000000..d36ab39900 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinanceReceiptController.java @@ -0,0 +1,153 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance; + +import cn.hutool.core.collection.CollUtil; +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.number.NumberUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; +import cn.iocoder.yudao.module.erp.service.finance.ErpFinanceReceiptService; +import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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 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.*; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 收款单") +@RestController +@RequestMapping("/erp/finance-receipt") +@Validated +public class ErpFinanceReceiptController { + + @Resource + private ErpFinanceReceiptService financeReceiptService; + @Resource + private ErpCustomerService customerService; + @Resource + private ErpAccountService accountService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建收款单") + @PreAuthorize("@ss.hasPermission('erp:finance-receipt:create')") + public CommonResult createFinanceReceipt(@Valid @RequestBody ErpFinanceReceiptSaveReqVO createReqVO) { + return success(financeReceiptService.createFinanceReceipt(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新收款单") + @PreAuthorize("@ss.hasPermission('erp:finance-receipt:update')") + public CommonResult updateFinanceReceipt(@Valid @RequestBody ErpFinanceReceiptSaveReqVO updateReqVO) { + financeReceiptService.updateFinanceReceipt(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新收款单的状态") + @PreAuthorize("@ss.hasPermission('erp:finance-receipt:update-status')") + public CommonResult updateFinanceReceiptStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + financeReceiptService.updateFinanceReceiptStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除收款单") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:finance-receipt:delete')") + public CommonResult deleteFinanceReceipt(@RequestParam("ids") List ids) { + financeReceiptService.deleteFinanceReceipt(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得收款单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:finance-receipt:query')") + public CommonResult getFinanceReceipt(@RequestParam("id") Long id) { + ErpFinanceReceiptDO receipt = financeReceiptService.getFinanceReceipt(id); + if (receipt == null) { + return success(null); + } + List receiptItemList = financeReceiptService.getFinanceReceiptItemListByReceiptId(id); + return success(BeanUtils.toBean(receipt, ErpFinanceReceiptRespVO.class, financeReceiptVO -> + financeReceiptVO.setItems(BeanUtils.toBean(receiptItemList, ErpFinanceReceiptRespVO.Item.class)))); + } + + @GetMapping("/page") + @Operation(summary = "获得收款单分页") + @PreAuthorize("@ss.hasPermission('erp:finance-receipt:query')") + public CommonResult> getFinanceReceiptPage(@Valid ErpFinanceReceiptPageReqVO pageReqVO) { + PageResult pageResult = financeReceiptService.getFinanceReceiptPage(pageReqVO); + return success(buildFinanceReceiptVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出收款单 Excel") + @PreAuthorize("@ss.hasPermission('erp:finance-receipt:export')") + @OperateLog(type = EXPORT) + public void exportFinanceReceiptExcel(@Valid ErpFinanceReceiptPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildFinanceReceiptVOPageResult(financeReceiptService.getFinanceReceiptPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "收款单.xls", "数据", ErpFinanceReceiptRespVO.class, list); + } + + private PageResult buildFinanceReceiptVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 收款项 + List receiptItemList = financeReceiptService.getFinanceReceiptItemListByReceiptIds( + convertSet(pageResult.getList(), ErpFinanceReceiptDO::getId)); + Map> financeReceiptItemMap = convertMultiMap(receiptItemList, ErpFinanceReceiptItemDO::getReceiptId); + // 1.2 客户信息 + Map customerMap = customerService.getCustomerMap( + convertSet(pageResult.getList(), ErpFinanceReceiptDO::getCustomerId)); + // 1.3 结算账户信息 + Map accountMap = accountService.getAccountMap( + convertSet(pageResult.getList(), ErpFinanceReceiptDO::getAccountId)); + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(), + contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getFinanceUserId()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpFinanceReceiptRespVO.class, receipt -> { + receipt.setItems(BeanUtils.toBean(financeReceiptItemMap.get(receipt.getId()), ErpFinanceReceiptRespVO.Item.class)); + MapUtils.findAndThen(customerMap, receipt.getCustomerId(), customer -> receipt.setCustomerName(customer.getName())); + MapUtils.findAndThen(accountMap, receipt.getAccountId(), account -> receipt.setAccountName(account.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(receipt.getCreator()), user -> receipt.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, receipt.getFinanceUserId(), user -> receipt.setFinanceUserName(user.getNickname())); + }); + } + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountPageReqVO.java new file mode 100644 index 0000000000..3e1fa72f4b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountPageReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account; + +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 = "管理后台 - ERP 结算账户分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpAccountPageReqVO extends PageParam { + + @Schema(description = "账户编码", example = "A88") + private String no; + + @Schema(description = "账户名称", example = "张三") + private String name; + + @Schema(description = "备注", example = "随便") + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountRespVO.java new file mode 100644 index 0000000000..a1c2e954db --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountRespVO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - ERP 结算账户 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpAccountRespVO { + + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28684") + @ExcelProperty("结算账户编号") + private Long id; + + @Schema(description = "账户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @ExcelProperty("账户名称") + private String name; + + @Schema(description = "账户编码", example = "A88") + @ExcelProperty("账户编码") + private String no; + + @Schema(description = "备注", example = "随便") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("开启状态") + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("排序") + private Integer sort; + + @Schema(description = "是否默认", example = "1") + @ExcelProperty("是否默认") + private Boolean defaultStatus; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountSaveReqVO.java new file mode 100644 index 0000000000..6f35565309 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountSaveReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account; + +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 lombok.Data; + +@Schema(description = "管理后台 - ERP 结算账户新增/修改 Request VO") +@Data +public class ErpAccountSaveReqVO { + + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28684") + private Long id; + + @Schema(description = "账户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @NotEmpty(message = "账户名称不能为空") + private String name; + + @Schema(description = "账户编码", example = "A88") + private String no; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "开启状态不能为空") + @InEnum(value = CommonStatusEnum.class) + private Integer status; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "排序不能为空") + private Integer sort; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentPageReqVO.java new file mode 100644 index 0000000000..b5618cadc9 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentPageReqVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment; + +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 = "管理后台 - ERP 付款单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpFinancePaymentPageReqVO extends PageParam { + + @Schema(description = "付款单编号", example = "XS001") + private String no; + + @Schema(description = "付款时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] paymentTime; + + @Schema(description = "供应商编号", example = "1724") + private Long supplierId; + + @Schema(description = "创建者", example = "666") + private String creator; + + @Schema(description = "财务人员编号", example = "888") + private String financeUserId; + + @Schema(description = "结算账户编号", example = "31189") + private Long accountId; + + @Schema(description = "付款状态", example = "2") + private Integer status; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "业务编号", example = "123") + private String bizNo; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java new file mode 100644 index 0000000000..43820a7d27 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 付款单 Response VO") +@Data +public class ErpFinancePaymentRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23752") + private Long id; + + @Schema(description = "付款单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "FKD888") + private String no; + + @Schema(description = "付款状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "付款时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime paymentTime; + + @Schema(description = "财务人员编号", example = "19690") + private Long financeUserId; + @Schema(description = "财务人员名称", example = "张三") + private String financeUserName; + + @Schema(description = "供应商编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29399") + private Long supplierId; + @Schema(description = "供应商名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小番茄公司") + private String supplierName; + + @Schema(description = "付款账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28989") + private Long accountId; + @Schema(description = "付款账户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + private String accountName; + + @Schema(description = "合计价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "13832") + private BigDecimal totalPrice; + + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "11600") + private BigDecimal discountPrice; + + @Schema(description = "实际价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + private BigDecimal paymentPrice; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "付款项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Data + public static class Item { + + @Schema(description = "付款项编号", example = "11756") + private Long id; + + @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer bizType; + + @Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long bizId; + + @Schema(description = "业务单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private String bizNo; + + @Schema(description = "应付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + private BigDecimal totalPrice; + + @Schema(description = "已付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + private BigDecimal paidPrice; + + @Schema(description = "本次付款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + @NotNull(message = "本次付款不能为空") + private BigDecimal paymentPrice; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentSaveReqVO.java new file mode 100644 index 0000000000..e50577b258 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentSaveReqVO.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 付款单新增/修改 Request VO") +@Data +public class ErpFinancePaymentSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23752") + private Long id; + + @Schema(description = "付款时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "付款时间不能为空") + private LocalDateTime paymentTime; + + @Schema(description = "财务人员编号", example = "19690") + private Long financeUserId; + + @Schema(description = "供应商编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29399") + @NotNull(message = "供应商编号不能为空") + private Long supplierId; + + @Schema(description = "付款账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28989") + @NotNull(message = "付款账户编号不能为空") + private Long accountId; + + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "11600") + @NotNull(message = "优惠金额不能为空") + private BigDecimal discountPrice; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "付款项列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "付款项列表不能为空") + @Valid + private List items; + + @Data + public static class Item { + + @Schema(description = "付款项编号", example = "11756") + private Long id; + + @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "业务类型不能为空") + private Integer bizType; + + @Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @NotNull(message = "业务编号不能为空") + private Long bizId; + + @Schema(description = "已付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + @NotNull(message = "已付金额不能为空") + private BigDecimal paidPrice; + + @Schema(description = "本次付款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + @NotNull(message = "本次付款不能为空") + private BigDecimal paymentPrice; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptPageReqVO.java new file mode 100644 index 0000000000..d3e938c66d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptPageReqVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt; + +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 = "管理后台 - ERP 收款单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpFinanceReceiptPageReqVO extends PageParam { + + @Schema(description = "收款单编号", example = "XS001") + private String no; + + @Schema(description = "收款时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] receiptTime; + + @Schema(description = "客户编号", example = "1724") + private Long customerId; + + @Schema(description = "创建者", example = "666") + private String creator; + + @Schema(description = "财务人员编号", example = "888") + private String financeUserId; + + @Schema(description = "收款账户编号", example = "31189") + private Long accountId; + + @Schema(description = "收款状态", example = "2") + private Integer status; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "业务编号", example = "123") + private String bizNo; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptRespVO.java new file mode 100644 index 0000000000..5c7133fe68 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptRespVO.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 收款单 Response VO") +@Data +public class ErpFinanceReceiptRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23752") + private Long id; + + @Schema(description = "收款单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "FKD888") + private String no; + + @Schema(description = "收款状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "收款时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime receiptTime; + + @Schema(description = "财务人员编号", example = "19690") + private Long financeUserId; + @Schema(description = "财务人员名称", example = "张三") + private String financeUserName; + + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29399") + private Long customerId; + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小番茄公司") + private String customerName; + + @Schema(description = "收款账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28989") + private Long accountId; + @Schema(description = "收款账户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + private String accountName; + + @Schema(description = "合计价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "13832") + private BigDecimal totalPrice; + + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "11600") + private BigDecimal discountPrice; + + @Schema(description = "实际价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + private BigDecimal receiptPrice; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "收款项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Data + public static class Item { + + @Schema(description = "收款项编号", example = "11756") + private Long id; + + @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer bizType; + + @Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long bizId; + + @Schema(description = "业务单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private String bizNo; + + @Schema(description = "应收金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + private BigDecimal totalPrice; + + @Schema(description = "已收金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + private BigDecimal receiptedPrice; + + @Schema(description = "本次收款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + @NotNull(message = "本次收款不能为空") + private BigDecimal receiptPrice; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptSaveReqVO.java new file mode 100644 index 0000000000..126edf805d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptSaveReqVO.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 收款单新增/修改 Request VO") +@Data +public class ErpFinanceReceiptSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23752") + private Long id; + + @Schema(description = "收款时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "收款时间不能为空") + private LocalDateTime receiptTime; + + @Schema(description = "财务人员编号", example = "19690") + private Long financeUserId; + + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29399") + @NotNull(message = "客户编号不能为空") + private Long customerId; + + @Schema(description = "收款账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28989") + @NotNull(message = "收款账户编号不能为空") + private Long accountId; + + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "11600") + @NotNull(message = "优惠金额不能为空") + private BigDecimal discountPrice; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "收款项列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "收款项列表不能为空") + @Valid + private List items; + + @Data + public static class Item { + + @Schema(description = "收款项编号", example = "11756") + private Long id; + + @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "业务类型不能为空") + private Integer bizType; + + @Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @NotNull(message = "业务编号不能为空") + private Long bizId; + + @Schema(description = "已收金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + @NotNull(message = "已收金额不能为空") + private BigDecimal receiptedPrice; + + @Schema(description = "本次收款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") + @NotNull(message = "本次收款不能为空") + private BigDecimal receiptPrice; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java new file mode 100644 index 0000000000..85f51c1c60 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java @@ -0,0 +1,101 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategorySaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductCategoryService; +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.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 产品分类") +@RestController +@RequestMapping("/erp/product-category") +@Validated +public class ErpProductCategoryController { + + @Resource + private ErpProductCategoryService productCategoryService; + + @PostMapping("/create") + @Operation(summary = "创建产品分类") + @PreAuthorize("@ss.hasPermission('erp:product-category:create')") + public CommonResult createProductCategory(@Valid @RequestBody ErpProductCategorySaveReqVO createReqVO) { + return success(productCategoryService.createProductCategory(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产品分类") + @PreAuthorize("@ss.hasPermission('erp:product-category:update')") + public CommonResult updateProductCategory(@Valid @RequestBody ErpProductCategorySaveReqVO updateReqVO) { + productCategoryService.updateProductCategory(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产品分类") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('erp:product-category:delete')") + public CommonResult deleteProductCategory(@RequestParam("id") Long id) { + productCategoryService.deleteProductCategory(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产品分类") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:product-category:query')") + public CommonResult getProductCategory(@RequestParam("id") Long id) { + ErpProductCategoryDO category = productCategoryService.getProductCategory(id); + return success(BeanUtils.toBean(category, ErpProductCategoryRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得产品分类列表") + @PreAuthorize("@ss.hasPermission('erp:product-category:query')") + public CommonResult> getProductCategoryList(@Valid ErpProductCategoryListReqVO listReqVO) { + List list = productCategoryService.getProductCategoryList(listReqVO); + return success(BeanUtils.toBean(list, ErpProductCategoryRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得产品分类精简列表", description = "只包含被开启的分类,主要用于前端的下拉选项") + public CommonResult> getProductCategorySimpleList() { + List list = productCategoryService.getProductCategoryList( + new ErpProductCategoryListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus())); + return success(convertList(list, category -> new ErpProductCategoryRespVO() + .setId(category.getId()).setName(category.getName()).setParentId(category.getParentId()))); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出产品分类 Excel") + @PreAuthorize("@ss.hasPermission('erp:product-category:export')") + @OperateLog(type = EXPORT) + public void exportProductCategoryExcel(@Valid ErpProductCategoryListReqVO listReqVO, + HttpServletResponse response) throws IOException { + List list = productCategoryService.getProductCategoryList(listReqVO); + // 导出 Excel + ExcelUtils.write(response, "产品分类.xls", "数据", ErpProductCategoryRespVO.class, + BeanUtils.toBean(list, ErpProductCategoryRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java new file mode 100644 index 0000000000..cde7bd704f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java @@ -0,0 +1,105 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +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.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 产品") +@RestController +@RequestMapping("/erp/product") +@Validated +public class ErpProductController { + + @Resource + private ErpProductService productService; + + @PostMapping("/create") + @Operation(summary = "创建产品") + @PreAuthorize("@ss.hasPermission('erp:product:create')") + public CommonResult createProduct(@Valid @RequestBody ProductSaveReqVO createReqVO) { + return success(productService.createProduct(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产品") + @PreAuthorize("@ss.hasPermission('erp:product:update')") + public CommonResult updateProduct(@Valid @RequestBody ProductSaveReqVO updateReqVO) { + productService.updateProduct(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产品") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('erp:product:delete')") + public CommonResult deleteProduct(@RequestParam("id") Long id) { + productService.deleteProduct(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产品") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:product:query')") + public CommonResult getProduct(@RequestParam("id") Long id) { + ErpProductDO product = productService.getProduct(id); + return success(BeanUtils.toBean(product, ErpProductRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得产品分页") + @PreAuthorize("@ss.hasPermission('erp:product:query')") + public CommonResult> getProductPage(@Valid ErpProductPageReqVO pageReqVO) { + return success(productService.getProductVOPage(pageReqVO)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得产品精简列表", description = "只包含被开启的产品,主要用于前端的下拉选项") + public CommonResult> getProductSimpleList() { + List list = productService.getProductVOListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, product -> new ErpProductRespVO().setId(product.getId()) + .setName(product.getName()).setBarCode(product.getBarCode()) + .setCategoryId(product.getCategoryId()).setCategoryName(product.getCategoryName()) + .setUnitId(product.getUnitId()).setUnitName(product.getUnitName()) + .setPurchasePrice(product.getPurchasePrice()).setSalePrice(product.getSalePrice()).setMinPrice(product.getMinPrice()))); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出产品 Excel") + @PreAuthorize("@ss.hasPermission('erp:product:export')") + @OperateLog(type = EXPORT) + public void exportProductExcel(@Valid ErpProductPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + PageResult pageResult = productService.getProductVOPage(pageReqVO); + // 导出 Excel + ExcelUtils.write(response, "产品.xls", "数据", ErpProductRespVO.class, + pageResult.getList()); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductUnitController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductUnitController.java new file mode 100644 index 0000000000..0be3db01c8 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductUnitController.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductUnitService; +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.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 产品单位") +@RestController +@RequestMapping("/erp/product-unit") +@Validated +public class ErpProductUnitController { + + @Resource + private ErpProductUnitService productUnitService; + + @PostMapping("/create") + @Operation(summary = "创建产品单位") + @PreAuthorize("@ss.hasPermission('erp:product-unit:create')") + public CommonResult createProductUnit(@Valid @RequestBody ErpProductUnitSaveReqVO createReqVO) { + return success(productUnitService.createProductUnit(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产品单位") + @PreAuthorize("@ss.hasPermission('erp:product-unit:update')") + public CommonResult updateProductUnit(@Valid @RequestBody ErpProductUnitSaveReqVO updateReqVO) { + productUnitService.updateProductUnit(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产品单位") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('erp:product-unit:delete')") + public CommonResult deleteProductUnit(@RequestParam("id") Long id) { + productUnitService.deleteProductUnit(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产品单位") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:product-unit:query')") + public CommonResult getProductUnit(@RequestParam("id") Long id) { + ErpProductUnitDO productUnit = productUnitService.getProductUnit(id); + return success(BeanUtils.toBean(productUnit, ErpProductUnitRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得产品单位分页") + @PreAuthorize("@ss.hasPermission('erp:product-unit:query')") + public CommonResult> getProductUnitPage(@Valid ErpProductUnitPageReqVO pageReqVO) { + PageResult pageResult = productUnitService.getProductUnitPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, ErpProductUnitRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得产品单位精简列表", description = "只包含被开启的单位,主要用于前端的下拉选项") + public CommonResult> getProductUnitSimpleList() { + List list = productUnitService.getProductUnitListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, unit -> new ErpProductUnitRespVO().setId(unit.getId()).setName(unit.getName()))); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出产品单位 Excel") + @PreAuthorize("@ss.hasPermission('erp:product-unit:export')") + @OperateLog(type = EXPORT) + public void exportProductUnitExcel(@Valid ErpProductUnitPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = productUnitService.getProductUnitPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "产品单位.xls", "数据", ErpProductUnitRespVO.class, + BeanUtils.toBean(list, ErpProductUnitRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryListReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryListReqVO.java new file mode 100644 index 0000000000..b9b530e7d9 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryListReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.category; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - ERP 产品分类列表 Request VO") +@Data +public class ErpProductCategoryListReqVO { + + @Schema(description = "分类名称", example = "芋艿") + private String name; + + @Schema(description = "开启状态", example = "1") + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryRespVO.java new file mode 100644 index 0000000000..23d7d9e8ff --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryRespVO.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.category; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - ERP 产品分类 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpProductCategoryRespVO { + + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5860") + @ExcelProperty("分类编号") + private Long id; + + @Schema(description = "父分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21829") + @ExcelProperty("父分类编号") + private Long parentId; + + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @ExcelProperty("分类名称") + private String name; + + @Schema(description = "分类编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "S110") + @ExcelProperty("分类编码") + private String code; + + @Schema(description = "分类排序", example = "10") + @ExcelProperty("分类排序") + private Integer sort; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "开启状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategorySaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategorySaveReqVO.java new file mode 100644 index 0000000000..a3c82dc937 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategorySaveReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.category; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - ERP 产品分类新增/修改 Request VO") +@Data +public class ErpProductCategorySaveReqVO { + + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5860") + private Long id; + + @Schema(description = "父分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21829") + @NotNull(message = "父分类编号不能为空") + private Long parentId; + + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @NotEmpty(message = "分类名称不能为空") + private String name; + + @Schema(description = "分类编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "S110") + @NotEmpty(message = "分类编码不能为空") + private String code; + + @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "分类排序不能为空") + private Integer sort; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "开启状态不能为空") + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductPageReqVO.java new file mode 100644 index 0000000000..de4f8142a6 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product; + +import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +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 = "管理后台 - ERP 产品分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpProductPageReqVO extends PageParam { + + @Schema(description = "产品名称", example = "李四") + private String name; + + @Schema(description = "产品分类编号", example = "11161") + private Long categoryId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductRespVO.java new file mode 100644 index 0000000000..9be9bc2559 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductRespVO.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - ERP 产品 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpProductRespVO { + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15672") + @ExcelProperty("产品编号") + private Long id; + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @ExcelProperty("产品名称") + private String name; + + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "X110") + @ExcelProperty("产品条码") + private String barCode; + + @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11161") + private Long categoryId; + @Schema(description = "产品分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "水果") + @ExcelProperty("产品分类") + private String categoryName; + + @Schema(description = "单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8869") + private Long unitId; + @Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "个") + @ExcelProperty("单位") + private String unitName; + + @Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("产品状态") + private Integer status; + + @Schema(description = "产品规格", example = "红色") + @ExcelProperty("产品规格") + private String standard; + + @Schema(description = "产品备注", example = "你猜") + @ExcelProperty("产品备注") + private String remark; + + @Schema(description = "保质期天数", example = "10") + @ExcelProperty("保质期天数") + private Integer expiryDay; + + @Schema(description = "基础重量(kg)", example = "1.00") + @ExcelProperty("基础重量(kg)") + private BigDecimal weight; + + @Schema(description = "采购价格,单位:元", example = "10.30") + @ExcelProperty("采购价格,单位:元") + private BigDecimal purchasePrice; + + @Schema(description = "销售价格,单位:元", example = "74.32") + @ExcelProperty("销售价格,单位:元") + private BigDecimal salePrice; + + @Schema(description = "最低价格,单位:元", example = "161.87") + @ExcelProperty("最低价格,单位:元") + private BigDecimal minPrice; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ProductSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ProductSaveReqVO.java new file mode 100644 index 0000000000..6cf806e3e1 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ProductSaveReqVO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - ERP 产品新增/修改 Request VO") +@Data +public class ProductSaveReqVO { + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15672") + private Long id; + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @NotEmpty(message = "产品名称不能为空") + private String name; + + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "X110") + @NotEmpty(message = "产品条码不能为空") + private String barCode; + + @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11161") + @NotNull(message = "产品分类编号不能为空") + private Long categoryId; + + @Schema(description = "单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8869") + @NotNull(message = "单位编号不能为空") + private Long unitId; + + @Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "产品状态不能为空") + private Integer status; + + @Schema(description = "产品规格", example = "红色") + private String standard; + + @Schema(description = "产品备注", example = "你猜") + private String remark; + + @Schema(description = "保质期天数", example = "10") + private Integer expiryDay; + + @Schema(description = "基础重量(kg)", example = "1.00") + private BigDecimal weight; + + @Schema(description = "采购价格,单位:元", example = "10.30") + private BigDecimal purchasePrice; + + @Schema(description = "销售价格,单位:元", example = "74.32") + private BigDecimal salePrice; + + @Schema(description = "最低价格,单位:元", example = "161.87") + private BigDecimal minPrice; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitPageReqVO.java new file mode 100644 index 0000000000..87119c1264 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitPageReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit; + +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 = "管理后台 - ERP 产品单位分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpProductUnitPageReqVO extends PageParam { + + @Schema(description = "单位名字", example = "芋艿") + private String name; + + @Schema(description = "单位状态", example = "1") + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitRespVO.java new file mode 100644 index 0000000000..06f604920c --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitRespVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - ERP 产品单位 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpProductUnitRespVO { + + @Schema(description = "单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31254") + @ExcelProperty("单位编号") + private Long id; + + @Schema(description = "单位名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @ExcelProperty("单位名字") + private String name; + + @Schema(description = "单位状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("单位状态") + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitSaveReqVO.java new file mode 100644 index 0000000000..e413ec1bf0 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitSaveReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit; + +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 lombok.Data; + +@Schema(description = "管理后台 - ERP 产品单位新增/修改 Request VO") +@Data +public class ErpProductUnitSaveReqVO { + + @Schema(description = "单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31254") + private Long id; + + @Schema(description = "单位名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @NotEmpty(message = "单位名字不能为空") + private String name; + + @Schema(description = "单位状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "单位状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java new file mode 100644 index 0000000000..d33c7ae4d6 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java @@ -0,0 +1,165 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.math.BigDecimal; +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.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 采购入库") +@RestController +@RequestMapping("/erp/purchase-in") +@Validated +public class ErpPurchaseInController { + + @Resource + private ErpPurchaseInService purchaseInService; + @Resource + private ErpStockService stockService; + @Resource + private ErpProductService productService; + @Resource + private ErpSupplierService supplierService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建采购入库") + @PreAuthorize("@ss.hasPermission('erp:purchase-in:create')") + public CommonResult createPurchaseIn(@Valid @RequestBody ErpPurchaseInSaveReqVO createReqVO) { + return success(purchaseInService.createPurchaseIn(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新采购入库") + @PreAuthorize("@ss.hasPermission('erp:purchase-in:update')") + public CommonResult updatePurchaseIn(@Valid @RequestBody ErpPurchaseInSaveReqVO updateReqVO) { + purchaseInService.updatePurchaseIn(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新采购入库的状态") + @PreAuthorize("@ss.hasPermission('erp:purchase-in:update-status')") + public CommonResult updatePurchaseInStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + purchaseInService.updatePurchaseInStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除采购入库") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:purchase-in:delete')") + public CommonResult deletePurchaseIn(@RequestParam("ids") List ids) { + purchaseInService.deletePurchaseIn(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得采购入库") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:purchase-in:query')") + public CommonResult getPurchaseIn(@RequestParam("id") Long id) { + ErpPurchaseInDO purchaseIn = purchaseInService.getPurchaseIn(id); + if (purchaseIn == null) { + return success(null); + } + List purchaseInItemList = purchaseInService.getPurchaseInItemListByInId(id); + Map productMap = productService.getProductVOMap( + convertSet(purchaseInItemList, ErpPurchaseInItemDO::getProductId)); + return success(BeanUtils.toBean(purchaseIn, ErpPurchaseInRespVO.class, purchaseInVO -> + purchaseInVO.setItems(BeanUtils.toBean(purchaseInItemList, ErpPurchaseInRespVO.Item.class, item -> { + ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId()); + item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO); + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); + })))); + } + + @GetMapping("/page") + @Operation(summary = "获得采购入库分页") + @PreAuthorize("@ss.hasPermission('erp:purchase-in:query')") + public CommonResult> getPurchaseInPage(@Valid ErpPurchaseInPageReqVO pageReqVO) { + PageResult pageResult = purchaseInService.getPurchaseInPage(pageReqVO); + return success(buildPurchaseInVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出采购入库 Excel") + @PreAuthorize("@ss.hasPermission('erp:purchase-in:export')") + @OperateLog(type = EXPORT) + public void exportPurchaseInExcel(@Valid ErpPurchaseInPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildPurchaseInVOPageResult(purchaseInService.getPurchaseInPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "采购入库.xls", "数据", ErpPurchaseInRespVO.class, list); + } + + private PageResult buildPurchaseInVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 入库项 + List purchaseInItemList = purchaseInService.getPurchaseInItemListByInIds( + convertSet(pageResult.getList(), ErpPurchaseInDO::getId)); + Map> purchaseInItemMap = convertMultiMap(purchaseInItemList, ErpPurchaseInItemDO::getInId); + // 1.2 产品信息 + Map productMap = productService.getProductVOMap( + convertSet(purchaseInItemList, ErpPurchaseInItemDO::getProductId)); + // 1.3 供应商信息 + Map supplierMap = supplierService.getSupplierMap( + convertSet(pageResult.getList(), ErpPurchaseInDO::getSupplierId)); + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), purchaseIn -> Long.parseLong(purchaseIn.getCreator()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpPurchaseInRespVO.class, purchaseIn -> { + purchaseIn.setItems(BeanUtils.toBean(purchaseInItemMap.get(purchaseIn.getId()), ErpPurchaseInRespVO.Item.class, + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); + purchaseIn.setProductNames(CollUtil.join(purchaseIn.getItems(), ",", ErpPurchaseInRespVO.Item::getProductName)); + MapUtils.findAndThen(supplierMap, purchaseIn.getSupplierId(), supplier -> purchaseIn.setSupplierName(supplier.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(purchaseIn.getCreator()), user -> purchaseIn.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java new file mode 100644 index 0000000000..203d2fec0b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java @@ -0,0 +1,164 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseOrderService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.math.BigDecimal; +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.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 采购订单") +@RestController +@RequestMapping("/erp/purchase-order") +@Validated +public class ErpPurchaseOrderController { + + @Resource + private ErpPurchaseOrderService purchaseOrderService; + @Resource + private ErpStockService stockService; + @Resource + private ErpProductService productService; + @Resource + private ErpSupplierService supplierService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建采购订单") + @PreAuthorize("@ss.hasPermission('erp:purchase-create:create')") + public CommonResult createPurchaseOrder(@Valid @RequestBody ErpPurchaseOrderSaveReqVO createReqVO) { + return success(purchaseOrderService.createPurchaseOrder(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新采购订单") + @PreAuthorize("@ss.hasPermission('erp:purchase-create:update')") + public CommonResult updatePurchaseOrder(@Valid @RequestBody ErpPurchaseOrderSaveReqVO updateReqVO) { + purchaseOrderService.updatePurchaseOrder(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新采购订单的状态") + @PreAuthorize("@ss.hasPermission('erp:purchase-create:update-status')") + public CommonResult updatePurchaseOrderStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + purchaseOrderService.updatePurchaseOrderStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除采购订单") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:purchase-create:delete')") + public CommonResult deletePurchaseOrder(@RequestParam("ids") List ids) { + purchaseOrderService.deletePurchaseOrder(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得采购订单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:purchase-create:query')") + public CommonResult getPurchaseOrder(@RequestParam("id") Long id) { + ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.getPurchaseOrder(id); + if (purchaseOrder == null) { + return success(null); + } + List purchaseOrderItemList = purchaseOrderService.getPurchaseOrderItemListByOrderId(id); + Map productMap = productService.getProductVOMap( + convertSet(purchaseOrderItemList, ErpPurchaseOrderItemDO::getProductId)); + return success(BeanUtils.toBean(purchaseOrder, ErpPurchaseOrderRespVO.class, purchaseOrderVO -> + purchaseOrderVO.setItems(BeanUtils.toBean(purchaseOrderItemList, ErpPurchaseOrderRespVO.Item.class, item -> { + BigDecimal purchaseCount = stockService.getStockCount(item.getProductId()); + item.setStockCount(purchaseCount != null ? purchaseCount : BigDecimal.ZERO); + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); + })))); + } + + @GetMapping("/page") + @Operation(summary = "获得采购订单分页") + @PreAuthorize("@ss.hasPermission('erp:purchase-create:query')") + public CommonResult> getPurchaseOrderPage(@Valid ErpPurchaseOrderPageReqVO pageReqVO) { + PageResult pageResult = purchaseOrderService.getPurchaseOrderPage(pageReqVO); + return success(buildPurchaseOrderVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出采购订单 Excel") + @PreAuthorize("@ss.hasPermission('erp:purchase-create:export')") + @OperateLog(type = EXPORT) + public void exportPurchaseOrderExcel(@Valid ErpPurchaseOrderPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildPurchaseOrderVOPageResult(purchaseOrderService.getPurchaseOrderPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "采购订单.xls", "数据", ErpPurchaseOrderRespVO.class, list); + } + + private PageResult buildPurchaseOrderVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 订单项 + List purchaseOrderItemList = purchaseOrderService.getPurchaseOrderItemListByOrderIds( + convertSet(pageResult.getList(), ErpPurchaseOrderDO::getId)); + Map> purchaseOrderItemMap = convertMultiMap(purchaseOrderItemList, ErpPurchaseOrderItemDO::getOrderId); + // 1.2 产品信息 + Map productMap = productService.getProductVOMap( + convertSet(purchaseOrderItemList, ErpPurchaseOrderItemDO::getProductId)); + // 1.3 供应商信息 + Map supplierMap = supplierService.getSupplierMap( + convertSet(pageResult.getList(), ErpPurchaseOrderDO::getSupplierId)); + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), purchaseOrder -> Long.parseLong(purchaseOrder.getCreator()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpPurchaseOrderRespVO.class, purchaseOrder -> { + purchaseOrder.setItems(BeanUtils.toBean(purchaseOrderItemMap.get(purchaseOrder.getId()), ErpPurchaseOrderRespVO.Item.class, + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); + purchaseOrder.setProductNames(CollUtil.join(purchaseOrder.getItems(), ",", ErpPurchaseOrderRespVO.Item::getProductName)); + MapUtils.findAndThen(supplierMap, purchaseOrder.getSupplierId(), supplier -> purchaseOrder.setSupplierName(supplier.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(purchaseOrder.getCreator()), user -> purchaseOrder.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseReturnController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseReturnController.java new file mode 100644 index 0000000000..0df31bcf19 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseReturnController.java @@ -0,0 +1,165 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseReturnService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.math.BigDecimal; +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.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 采购退货") +@RestController +@RequestMapping("/erp/purchase-return") +@Validated +public class ErpPurchaseReturnController { + + @Resource + private ErpPurchaseReturnService purchaseReturnService; + @Resource + private ErpStockService stockService; + @Resource + private ErpProductService productService; + @Resource + private ErpSupplierService supplierService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建采购退货") + @PreAuthorize("@ss.hasPermission('erp:purchase-return:create')") + public CommonResult createPurchaseReturn(@Valid @RequestBody ErpPurchaseReturnSaveReqVO createReqVO) { + return success(purchaseReturnService.createPurchaseReturn(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新采购退货") + @PreAuthorize("@ss.hasPermission('erp:purchase-return:update')") + public CommonResult updatePurchaseReturn(@Valid @RequestBody ErpPurchaseReturnSaveReqVO updateReqVO) { + purchaseReturnService.updatePurchaseReturn(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新采购退货的状态") + @PreAuthorize("@ss.hasPermission('erp:purchase-return:update-status')") + public CommonResult updatePurchaseReturnStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + purchaseReturnService.updatePurchaseReturnStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除采购退货") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:purchase-return:delete')") + public CommonResult deletePurchaseReturn(@RequestParam("ids") List ids) { + purchaseReturnService.deletePurchaseReturn(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得采购退货") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:purchase-return:query')") + public CommonResult getPurchaseReturn(@RequestParam("id") Long id) { + ErpPurchaseReturnDO purchaseReturn = purchaseReturnService.getPurchaseReturn(id); + if (purchaseReturn == null) { + return success(null); + } + List purchaseReturnItemList = purchaseReturnService.getPurchaseReturnItemListByReturnId(id); + Map productMap = productService.getProductVOMap( + convertSet(purchaseReturnItemList, ErpPurchaseReturnItemDO::getProductId)); + return success(BeanUtils.toBean(purchaseReturn, ErpPurchaseReturnRespVO.class, purchaseReturnVO -> + purchaseReturnVO.setItems(BeanUtils.toBean(purchaseReturnItemList, ErpPurchaseReturnRespVO.Item.class, item -> { + ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId()); + item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO); + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); + })))); + } + + @GetMapping("/page") + @Operation(summary = "获得采购退货分页") + @PreAuthorize("@ss.hasPermission('erp:purchase-return:query')") + public CommonResult> getPurchaseReturnPage(@Valid ErpPurchaseReturnPageReqVO pageReqVO) { + PageResult pageResult = purchaseReturnService.getPurchaseReturnPage(pageReqVO); + return success(buildPurchaseReturnVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出采购退货 Excel") + @PreAuthorize("@ss.hasPermission('erp:purchase-return:export')") + @OperateLog(type = EXPORT) + public void exportPurchaseReturnExcel(@Valid ErpPurchaseReturnPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildPurchaseReturnVOPageResult(purchaseReturnService.getPurchaseReturnPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "采购退货.xls", "数据", ErpPurchaseReturnRespVO.class, list); + } + + private PageResult buildPurchaseReturnVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 退货项 + List purchaseReturnItemList = purchaseReturnService.getPurchaseReturnItemListByReturnIds( + convertSet(pageResult.getList(), ErpPurchaseReturnDO::getId)); + Map> purchaseReturnItemMap = convertMultiMap(purchaseReturnItemList, ErpPurchaseReturnItemDO::getReturnId); + // 1.2 产品信息 + Map productMap = productService.getProductVOMap( + convertSet(purchaseReturnItemList, ErpPurchaseReturnItemDO::getProductId)); + // 1.3 供应商信息 + Map supplierMap = supplierService.getSupplierMap( + convertSet(pageResult.getList(), ErpPurchaseReturnDO::getSupplierId)); + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), purchaseReturn -> Long.parseLong(purchaseReturn.getCreator()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpPurchaseReturnRespVO.class, purchaseReturn -> { + purchaseReturn.setItems(BeanUtils.toBean(purchaseReturnItemMap.get(purchaseReturn.getId()), ErpPurchaseReturnRespVO.Item.class, + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); + purchaseReturn.setProductNames(CollUtil.join(purchaseReturn.getItems(), ",", ErpPurchaseReturnRespVO.Item::getProductName)); + MapUtils.findAndThen(supplierMap, purchaseReturn.getSupplierId(), supplier -> purchaseReturn.setSupplierName(supplier.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(purchaseReturn.getCreator()), user -> purchaseReturn.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java new file mode 100644 index 0000000000..88253286db --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService; +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.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 供应商") +@RestController +@RequestMapping("/erp/supplier") +@Validated +public class ErpSupplierController { + + @Resource + private ErpSupplierService supplierService; + + @PostMapping("/create") + @Operation(summary = "创建供应商") + @PreAuthorize("@ss.hasPermission('erp:supplier:create')") + public CommonResult createSupplier(@Valid @RequestBody ErpSupplierSaveReqVO createReqVO) { + return success(supplierService.createSupplier(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新供应商") + @PreAuthorize("@ss.hasPermission('erp:supplier:update')") + public CommonResult updateSupplier(@Valid @RequestBody ErpSupplierSaveReqVO updateReqVO) { + supplierService.updateSupplier(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除供应商") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('erp:supplier:delete')") + public CommonResult deleteSupplier(@RequestParam("id") Long id) { + supplierService.deleteSupplier(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得供应商") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:supplier:query')") + public CommonResult getSupplier(@RequestParam("id") Long id) { + ErpSupplierDO supplier = supplierService.getSupplier(id); + return success(BeanUtils.toBean(supplier, ErpSupplierRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得供应商分页") + @PreAuthorize("@ss.hasPermission('erp:supplier:query')") + public CommonResult> getSupplierPage(@Valid ErpSupplierPageReqVO pageReqVO) { + PageResult pageResult = supplierService.getSupplierPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, ErpSupplierRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得供应商精简列表", description = "只包含被开启的供应商,主要用于前端的下拉选项") + public CommonResult> getSupplierSimpleList() { + List list = supplierService.getSupplierListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, supplier -> new ErpSupplierRespVO().setId(supplier.getId()).setName(supplier.getName()))); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出供应商 Excel") + @PreAuthorize("@ss.hasPermission('erp:supplier:export')") + @OperateLog(type = EXPORT) + public void exportSupplierExcel(@Valid ErpSupplierPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = supplierService.getSupplierPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "供应商.xls", "数据", ErpSupplierRespVO.class, + BeanUtils.toBean(list, ErpSupplierRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInPageReqVO.java new file mode 100644 index 0000000000..e84607ce4e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInPageReqVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in; + +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 = "管理后台 - ERP 采购入库分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpPurchaseInPageReqVO extends PageParam { + + public static final Integer PAYMENT_STATUS_NONE = 0; + public static final Integer PAYMENT_STATUS_PART = 1; + public static final Integer PAYMENT_STATUS_ALL = 2; + + @Schema(description = "采购单编号", example = "XS001") + private String no; + + @Schema(description = "供应商编号", example = "1724") + private Long supplierId; + + @Schema(description = "入库时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] inTime; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "入库状态", example = "2") + private Integer status; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + + @Schema(description = "仓库编号", example = "1") + private Long warehouseId; + + @Schema(description = "结算账号编号", example = "1") + private Long accountId; + + @Schema(description = "付款状态", example = "1") + private Integer paymentStatus; + + @Schema(description = "是否可付款", example = "true") + private Boolean paymentEnable; // 对应 paymentStatus = [0, 1] + + @Schema(description = "采购单号", example = "1") + private String orderNo; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInRespVO.java new file mode 100644 index 0000000000..beeeab8692 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInRespVO.java @@ -0,0 +1,145 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 采购入库 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpPurchaseInRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "入库单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") + @ExcelProperty("入库单编号") + private String no; + + @Schema(description = "入库状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("入库状态") + private Integer status; + + @Schema(description = "供应商编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724") + private Long supplierId; + @Schema(description = "供应商名称", example = "芋道") + @ExcelProperty("供应商名称") + private String supplierName; + + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "311.89") + @ExcelProperty("结算账户编号") + private Long accountId; + + @Schema(description = "入库时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("入库时间") + private LocalDateTime inTime; + + @Schema(description = "采购订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + private Long orderId; + @Schema(description = "采购订单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") + private String orderNo; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + @Schema(description = "最终合计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("最终合计价格") + private BigDecimal totalPrice; + @Schema(description = "已付款金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal paymentPrice; + + @Schema(description = "合计产品价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalProductPrice; + + @Schema(description = "合计税额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalTaxPrice; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal discountPrice; + + @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal otherPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + @ExcelProperty("附件地址") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "入库项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + @Data + public static class Item { + + @Schema(description = "入库项编号", example = "11756") + private Long id; + + @Schema(description = "采购订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long orderItemId; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "税额,单位:元", example = "100.00") + private BigDecimal taxPrice; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 关联字段 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") + private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") + private String productUnitName; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用 + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInSaveReqVO.java new file mode 100644 index 0000000000..80edeec6d9 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInSaveReqVO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 采购入库新增/修改 Request VO") +@Data +public class ErpPurchaseInSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + private Long id; + + @Schema(description = "结算账户编号", example = "31189") + private Long accountId; + + @Schema(description = "入库时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "入库时间不能为空") + private LocalDateTime inTime; + + @Schema(description = "采购订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + @NotNull(message = "采购订单编号不能为空") + private Long orderId; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "其它金额,单位:元", example = "7127") + private BigDecimal otherPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "入库清单列表") + private List items; + + @Data + public static class Item { + + @Schema(description = "入库项编号", example = "11756") + private Long id; + + @Schema(description = "采购订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @NotNull(message = "采购订单项编号不能为空") + private Long orderItemId; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品单位单位不能为空") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderPageReqVO.java new file mode 100644 index 0000000000..8bf70d4275 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderPageReqVO.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order; + +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 = "管理后台 - ERP 采购订单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpPurchaseOrderPageReqVO extends PageParam { + + /** + * 入库状态 - 无 + */ + public static final Integer IN_STATUS_NONE = 0; + /** + * 入库状态 - 部分 + */ + public static final Integer IN_STATUS_PART = 1; + /** + * 入库状态 - 全部 + */ + public static final Integer IN_STATUS_ALL = 2; + + /** + * 退货状态 - 无 + */ + public static final Integer RETURN_STATUS_NONE = 0; + /** + * 退货状态 - 部分 + */ + public static final Integer RETURN_STATUS_PART = 1; + /** + * 退货状态 - 全部 + */ + public static final Integer RETURN_STATUS_ALL = 2; + + @Schema(description = "采购单编号", example = "XS001") + private String no; + + @Schema(description = "供应商编号", example = "1724") + private Long supplierId; + + @Schema(description = "采购时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] orderTime; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "采购状态", example = "2") + private Integer status; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + + @Schema(description = "入库状态", example = "2") + private Integer inStatus; + + @Schema(description = "退货状态", example = "2") + private Integer returnStatus; + + @Schema(description = "是否可入库", example = "true") + private Boolean inEnable; + + @Schema(description = "是否可退货", example = "true") + private Boolean returnEnable; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java new file mode 100644 index 0000000000..bc76720eed --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java @@ -0,0 +1,152 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 采购订单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpPurchaseOrderRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "采购单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") + @ExcelProperty("采购单编号") + private String no; + + @Schema(description = "采购状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("采购状态") + private Integer status; + + @Schema(description = "供应商编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724") + private Long supplierId; + @Schema(description = "供应商名称", example = "芋道") + @ExcelProperty("供应商名称") + private String supplierName; + + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "311.89") + @ExcelProperty("结算账户编号") + private Long accountId; + + @Schema(description = "采购时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("采购时间") + private LocalDateTime orderTime; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + @Schema(description = "最终合计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("最终合计价格") + private BigDecimal totalPrice; + + @Schema(description = "合计产品价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalProductPrice; + + @Schema(description = "合计税额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalTaxPrice; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal discountPrice; + + @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal depositPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + @ExcelProperty("附件地址") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "订单项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + // ========== 采购入库 ========== + + @Schema(description = "采购入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal inCount; + + // ========== 采购退货(出库)) ========== + + @Schema(description = "采购退货数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal returnCount; + + @Data + public static class Item { + + @Schema(description = "订单项编号", example = "11756") + private Long id; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "税额,单位:元", example = "100.00") + private BigDecimal taxPrice; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 采购入库 ========== + + @Schema(description = "采购入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal inCount; + + // ========== 采购退货(入库)) ========== + + @Schema(description = "采购退货数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal returnCount; + + // ========== 关联字段 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") + private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") + private String productUnitName; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用 + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java new file mode 100644 index 0000000000..061ed9dcc2 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 采购订单新增/修改 Request VO") +@Data +public class ErpPurchaseOrderSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + private Long id; + + @Schema(description = "供应商编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724") + @NotNull(message = "供应商编号不能为空") + private Long supplierId; + + @Schema(description = "结算账户编号", example = "31189") + private Long accountId; + + @Schema(description = "采购时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "采购时间不能为空") + private LocalDateTime orderTime; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "定金金额,单位:元", example = "7127") + private BigDecimal depositPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "订单清单列表") + private List items; + + @Data + public static class Item { + + @Schema(description = "订单项编号", example = "11756") + private Long id; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品单位单位不能为空") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnPageReqVO.java new file mode 100644 index 0000000000..a534d2e3e1 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnPageReqVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns; + +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 = "管理后台 - ERP 采购退货分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpPurchaseReturnPageReqVO extends PageParam { + + public static final Integer REFUND_STATUS_NONE = 0; + public static final Integer REFUND_STATUS_PART = 1; + public static final Integer REFUND_STATUS_ALL = 2; + + @Schema(description = "采购单编号", example = "XS001") + private String no; + + @Schema(description = "供应商编号", example = "1724") + private Long supplierId; + + @Schema(description = "退货时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] returnTime; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "退货状态", example = "2") + private Integer status; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + + @Schema(description = "仓库编号", example = "1") + private Long warehouseId; + + @Schema(description = "结算账号编号", example = "1") + private Long accountId; + + @Schema(description = "采购单号", example = "1") + private String orderNo; + + @Schema(description = "退款状态", example = "1") + private Integer refundStatus; + + @Schema(description = "是否可退款", example = "true") + private Boolean refundEnable; // 对应 refundStatus = [0, 1] + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnRespVO.java new file mode 100644 index 0000000000..223b9327e9 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnRespVO.java @@ -0,0 +1,145 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 采购退货 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpPurchaseReturnRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "退货单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") + @ExcelProperty("退货单编号") + private String no; + + @Schema(description = "退货状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("退货状态") + private Integer status; + + @Schema(description = "供应商编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724") + private Long supplierId; + @Schema(description = "供应商名称", example = "芋道") + @ExcelProperty("供应商名称") + private String supplierName; + + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "311.89") + @ExcelProperty("结算账户编号") + private Long accountId; + + @Schema(description = "退货时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("退货时间") + private LocalDateTime returnTime; + + @Schema(description = "采购订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + private Long orderId; + @Schema(description = "采购订单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") + private String orderNo; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + @Schema(description = "最终合计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("最终合计价格") + private BigDecimal totalPrice; + @Schema(description = "已退款金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal refundPrice; + + @Schema(description = "合计产品价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalProductPrice; + + @Schema(description = "合计税额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalTaxPrice; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal discountPrice; + + @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal otherPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + @ExcelProperty("附件地址") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "退货项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + @Data + public static class Item { + + @Schema(description = "退货项编号", example = "11756") + private Long id; + + @Schema(description = "采购订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long orderItemId; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "税额,单位:元", example = "100.00") + private BigDecimal taxPrice; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 关联字段 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") + private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") + private String productUnitName; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用 + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnSaveReqVO.java new file mode 100644 index 0000000000..9254bd5834 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnSaveReqVO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 采购退货新增/修改 Request VO") +@Data +public class ErpPurchaseReturnSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + private Long id; + + @Schema(description = "结算账户编号", example = "31189") + private Long accountId; + + @Schema(description = "退货时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "退货时间不能为空") + private LocalDateTime returnTime; + + @Schema(description = "采购订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + @NotNull(message = "采购订单编号不能为空") + private Long orderId; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "其它金额,单位:元", example = "7127") + private BigDecimal otherPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "退货清单列表") + private List items; + + @Data + public static class Item { + + @Schema(description = "退货项编号", example = "11756") + private Long id; + + @Schema(description = "采购订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @NotNull(message = "采购订单项编号不能为空") + private Long orderItemId; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品单位单位不能为空") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierPageReqVO.java new file mode 100644 index 0000000000..229ab63d9b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierPageReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier; + +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 = "管理后台 - ERP 供应商分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpSupplierPageReqVO extends PageParam { + + @Schema(description = "供应商名称", example = "芋道源码") + private String name; + + @Schema(description = "手机号码", example = "15601691300") + private String mobile; + + @Schema(description = "联系电话", example = "18818288888") + private String telephone; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierRespVO.java new file mode 100644 index 0000000000..5ba5892c1b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierRespVO.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - ERP 供应商 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpSupplierRespVO { + + @Schema(description = "供应商编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17791") + @ExcelProperty("供应商编号") + private Long id; + + @Schema(description = "供应商名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + @ExcelProperty("供应商名称") + private String name; + + @Schema(description = "联系人", example = "芋艿") + @ExcelProperty("联系人") + private String contact; + + @Schema(description = "手机号码", example = "15601691300") + @ExcelProperty("手机号码") + private String mobile; + + @Schema(description = "联系电话", example = "18818288888") + @ExcelProperty("联系电话") + private String telephone; + + @Schema(description = "电子邮箱", example = "76853@qq.com") + @ExcelProperty("电子邮箱") + private String email; + + @Schema(description = "传真", example = "20 7123 4567") + @ExcelProperty("传真") + private String fax; + + @Schema(description = "备注", example = "你猜") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "开启状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty("排序") + private Integer sort; + + @Schema(description = "纳税人识别号", example = "91130803MA098BY05W") + @ExcelProperty("纳税人识别号") + private String taxNo; + + @Schema(description = "税率", example = "10") + @ExcelProperty("税率") + private BigDecimal taxPercent; + + @Schema(description = "开户行", example = "张三") + @ExcelProperty("开户行") + private String bankName; + + @Schema(description = "开户账号", example = "622908212277228617") + @ExcelProperty("开户账号") + private String bankAccount; + + @Schema(description = "开户地址", example = "兴业银行浦东支行") + @ExcelProperty("开户地址") + private String bankAddress; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierSaveReqVO.java new file mode 100644 index 0000000000..2291de050a --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierSaveReqVO.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.framework.common.validation.Telephone; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - ERP 供应商新增/修改 Request VO") +@Data +public class ErpSupplierSaveReqVO { + + @Schema(description = "供应商编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17791") + private Long id; + + @Schema(description = "供应商名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + @NotEmpty(message = "供应商名称不能为空") + private String name; + + @Schema(description = "联系人", example = "芋艿") + private String contact; + + @Schema(description = "手机号码", example = "15601691300") + @Mobile + private String mobile; + + @Schema(description = "联系电话", example = "18818288888") + @Telephone + private String telephone; + + @Schema(description = "电子邮箱", example = "76853@qq.com") + @Email + private String email; + + @Schema(description = "传真", example = "20 7123 4567") + private String fax; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "开启状态不能为空") + @InEnum(value = CommonStatusEnum.class) + private Integer status; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "排序不能为空") + private Integer sort; + + @Schema(description = "纳税人识别号", example = "91130803MA098BY05W") + private String taxNo; + + @Schema(description = "税率", example = "10") + private BigDecimal taxPercent; + + @Schema(description = "开户行", example = "张三") + private String bankName; + + @Schema(description = "开户账号", example = "622908212277228617") + private String bankAccount; + + @Schema(description = "开户地址", example = "兴业银行浦东支行") + private String bankAddress; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpCustomerController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpCustomerController.java new file mode 100644 index 0000000000..2c2886460f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpCustomerController.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO; +import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService; +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.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 客户") +@RestController +@RequestMapping("/erp/customer") +@Validated +public class ErpCustomerController { + + @Resource + private ErpCustomerService customerService; + + @PostMapping("/create") + @Operation(summary = "创建客户") + @PreAuthorize("@ss.hasPermission('erp:customer:create')") + public CommonResult createCustomer(@Valid @RequestBody ErpCustomerSaveReqVO createReqVO) { + return success(customerService.createCustomer(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新客户") + @PreAuthorize("@ss.hasPermission('erp:customer:update')") + public CommonResult updateCustomer(@Valid @RequestBody ErpCustomerSaveReqVO updateReqVO) { + customerService.updateCustomer(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除客户") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('erp:customer:delete')") + public CommonResult deleteCustomer(@RequestParam("id") Long id) { + customerService.deleteCustomer(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得客户") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:customer:query')") + public CommonResult getCustomer(@RequestParam("id") Long id) { + ErpCustomerDO customer = customerService.getCustomer(id); + return success(BeanUtils.toBean(customer, ErpCustomerRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得客户分页") + @PreAuthorize("@ss.hasPermission('erp:customer:query')") + public CommonResult> getCustomerPage(@Valid ErpCustomerPageReqVO pageReqVO) { + PageResult pageResult = customerService.getCustomerPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, ErpCustomerRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得客户精简列表", description = "只包含被开启的客户,主要用于前端的下拉选项") + public CommonResult> getCustomerSimpleList() { + List list = customerService.getCustomerListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, customer -> new ErpCustomerRespVO().setId(customer.getId()).setName(customer.getName()))); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出客户 Excel") + @PreAuthorize("@ss.hasPermission('erp:customer:export')") + @OperateLog(type = EXPORT) + public void exportCustomerExcel(@Valid ErpCustomerPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = customerService.getCustomerPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "客户.xls", "数据", ErpCustomerRespVO.class, + BeanUtils.toBean(list, ErpCustomerRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java new file mode 100644 index 0000000000..0ca56a45eb --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java @@ -0,0 +1,165 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService; +import cn.iocoder.yudao.module.erp.service.sale.ErpSaleOrderService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.math.BigDecimal; +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.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + + +@Tag(name = "管理后台 - ERP 销售订单") +@RestController +@RequestMapping("/erp/sale-order") +@Validated +public class ErpSaleOrderController { + + @Resource + private ErpSaleOrderService saleOrderService; + @Resource + private ErpStockService stockService; + @Resource + private ErpProductService productService; + @Resource + private ErpCustomerService customerService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建销售订单") + @PreAuthorize("@ss.hasPermission('erp:sale-out:create')") + public CommonResult createSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO createReqVO) { + return success(saleOrderService.createSaleOrder(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新销售订单") + @PreAuthorize("@ss.hasPermission('erp:sale-out:update')") + public CommonResult updateSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO updateReqVO) { + saleOrderService.updateSaleOrder(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新销售订单的状态") + @PreAuthorize("@ss.hasPermission('erp:sale-out:update-status')") + public CommonResult updateSaleOrderStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + saleOrderService.updateSaleOrderStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除销售订单") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:sale-out:delete')") + public CommonResult deleteSaleOrder(@RequestParam("ids") List ids) { + saleOrderService.deleteSaleOrder(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得销售订单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:sale-out:query')") + public CommonResult getSaleOrder(@RequestParam("id") Long id) { + ErpSaleOrderDO saleOrder = saleOrderService.getSaleOrder(id); + if (saleOrder == null) { + return success(null); + } + List saleOrderItemList = saleOrderService.getSaleOrderItemListByOrderId(id); + Map productMap = productService.getProductVOMap( + convertSet(saleOrderItemList, ErpSaleOrderItemDO::getProductId)); + return success(BeanUtils.toBean(saleOrder, ErpSaleOrderRespVO.class, saleOrderVO -> + saleOrderVO.setItems(BeanUtils.toBean(saleOrderItemList, ErpSaleOrderRespVO.Item.class, item -> { + BigDecimal stockCount = stockService.getStockCount(item.getProductId()); + item.setStockCount(stockCount != null ? stockCount : BigDecimal.ZERO); + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); + })))); + } + + @GetMapping("/page") + @Operation(summary = "获得销售订单分页") + @PreAuthorize("@ss.hasPermission('erp:sale-out:query')") + public CommonResult> getSaleOrderPage(@Valid ErpSaleOrderPageReqVO pageReqVO) { + PageResult pageResult = saleOrderService.getSaleOrderPage(pageReqVO); + return success(buildSaleOrderVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出销售订单 Excel") + @PreAuthorize("@ss.hasPermission('erp:sale-out:export')") + @OperateLog(type = EXPORT) + public void exportSaleOrderExcel(@Valid ErpSaleOrderPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildSaleOrderVOPageResult(saleOrderService.getSaleOrderPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "销售订单.xls", "数据", ErpSaleOrderRespVO.class, list); + } + + private PageResult buildSaleOrderVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 订单项 + List saleOrderItemList = saleOrderService.getSaleOrderItemListByOrderIds( + convertSet(pageResult.getList(), ErpSaleOrderDO::getId)); + Map> saleOrderItemMap = convertMultiMap(saleOrderItemList, ErpSaleOrderItemDO::getOrderId); + // 1.2 产品信息 + Map productMap = productService.getProductVOMap( + convertSet(saleOrderItemList, ErpSaleOrderItemDO::getProductId)); + // 1.3 客户信息 + Map customerMap = customerService.getCustomerMap( + convertSet(pageResult.getList(), ErpSaleOrderDO::getCustomerId)); + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), saleOrder -> Long.parseLong(saleOrder.getCreator()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpSaleOrderRespVO.class, saleOrder -> { + saleOrder.setItems(BeanUtils.toBean(saleOrderItemMap.get(saleOrder.getId()), ErpSaleOrderRespVO.Item.class, + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); + saleOrder.setProductNames(CollUtil.join(saleOrder.getItems(), ",", ErpSaleOrderRespVO.Item::getProductName)); + MapUtils.findAndThen(customerMap, saleOrder.getCustomerId(), supplier -> saleOrder.setCustomerName(supplier.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(saleOrder.getCreator()), user -> saleOrder.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java new file mode 100644 index 0000000000..5875ea39f9 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java @@ -0,0 +1,165 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService; +import cn.iocoder.yudao.module.erp.service.sale.ErpSaleOutService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.math.BigDecimal; +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.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 销售出库") +@RestController +@RequestMapping("/erp/sale-out") +@Validated +public class ErpSaleOutController { + + @Resource + private ErpSaleOutService saleOutService; + @Resource + private ErpStockService stockService; + @Resource + private ErpProductService productService; + @Resource + private ErpCustomerService customerService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建销售出库") + @PreAuthorize("@ss.hasPermission('erp:sale-out:create')") + public CommonResult createSaleOut(@Valid @RequestBody ErpSaleOutSaveReqVO createReqVO) { + return success(saleOutService.createSaleOut(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新销售出库") + @PreAuthorize("@ss.hasPermission('erp:sale-out:update')") + public CommonResult updateSaleOut(@Valid @RequestBody ErpSaleOutSaveReqVO updateReqVO) { + saleOutService.updateSaleOut(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新销售出库的状态") + @PreAuthorize("@ss.hasPermission('erp:sale-out:update-status')") + public CommonResult updateSaleOutStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + saleOutService.updateSaleOutStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除销售出库") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:sale-out:delete')") + public CommonResult deleteSaleOut(@RequestParam("ids") List ids) { + saleOutService.deleteSaleOut(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得销售出库") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:sale-out:query')") + public CommonResult getSaleOut(@RequestParam("id") Long id) { + ErpSaleOutDO saleOut = saleOutService.getSaleOut(id); + if (saleOut == null) { + return success(null); + } + List saleOutItemList = saleOutService.getSaleOutItemListByOutId(id); + Map productMap = productService.getProductVOMap( + convertSet(saleOutItemList, ErpSaleOutItemDO::getProductId)); + return success(BeanUtils.toBean(saleOut, ErpSaleOutRespVO.class, saleOutVO -> + saleOutVO.setItems(BeanUtils.toBean(saleOutItemList, ErpSaleOutRespVO.Item.class, item -> { + ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId()); + item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO); + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); + })))); + } + + @GetMapping("/page") + @Operation(summary = "获得销售出库分页") + @PreAuthorize("@ss.hasPermission('erp:sale-out:query')") + public CommonResult> getSaleOutPage(@Valid ErpSaleOutPageReqVO pageReqVO) { + PageResult pageResult = saleOutService.getSaleOutPage(pageReqVO); + return success(buildSaleOutVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出销售出库 Excel") + @PreAuthorize("@ss.hasPermission('erp:sale-out:export')") + @OperateLog(type = EXPORT) + public void exportSaleOutExcel(@Valid ErpSaleOutPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildSaleOutVOPageResult(saleOutService.getSaleOutPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "销售出库.xls", "数据", ErpSaleOutRespVO.class, list); + } + + private PageResult buildSaleOutVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 出库项 + List saleOutItemList = saleOutService.getSaleOutItemListByOutIds( + convertSet(pageResult.getList(), ErpSaleOutDO::getId)); + Map> saleOutItemMap = convertMultiMap(saleOutItemList, ErpSaleOutItemDO::getOutId); + // 1.2 产品信息 + Map productMap = productService.getProductVOMap( + convertSet(saleOutItemList, ErpSaleOutItemDO::getProductId)); + // 1.3 客户信息 + Map customerMap = customerService.getCustomerMap( + convertSet(pageResult.getList(), ErpSaleOutDO::getCustomerId)); + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), stockOut -> Long.parseLong(stockOut.getCreator()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpSaleOutRespVO.class, saleOut -> { + saleOut.setItems(BeanUtils.toBean(saleOutItemMap.get(saleOut.getId()), ErpSaleOutRespVO.Item.class, + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); + saleOut.setProductNames(CollUtil.join(saleOut.getItems(), ",", ErpSaleOutRespVO.Item::getProductName)); + MapUtils.findAndThen(customerMap, saleOut.getCustomerId(), supplier -> saleOut.setCustomerName(supplier.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(saleOut.getCreator()), user -> saleOut.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleReturnController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleReturnController.java new file mode 100644 index 0000000000..0dfba67e98 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleReturnController.java @@ -0,0 +1,165 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService; +import cn.iocoder.yudao.module.erp.service.sale.ErpSaleReturnService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.math.BigDecimal; +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.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 销售退货") +@RestController +@RequestMapping("/erp/sale-return") +@Validated +public class ErpSaleReturnController { + + @Resource + private ErpSaleReturnService saleReturnService; + @Resource + private ErpStockService stockService; + @Resource + private ErpProductService productService; + @Resource + private ErpCustomerService customerService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建销售退货") + @PreAuthorize("@ss.hasPermission('erp:sale-return:create')") + public CommonResult createSaleReturn(@Valid @RequestBody ErpSaleReturnSaveReqVO createReqVO) { + return success(saleReturnService.createSaleReturn(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新销售退货") + @PreAuthorize("@ss.hasPermission('erp:sale-return:update')") + public CommonResult updateSaleReturn(@Valid @RequestBody ErpSaleReturnSaveReqVO updateReqVO) { + saleReturnService.updateSaleReturn(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新销售退货的状态") + @PreAuthorize("@ss.hasPermission('erp:sale-return:update-status')") + public CommonResult updateSaleReturnStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + saleReturnService.updateSaleReturnStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除销售退货") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:sale-return:delete')") + public CommonResult deleteSaleReturn(@RequestParam("ids") List ids) { + saleReturnService.deleteSaleReturn(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得销售退货") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:sale-return:query')") + public CommonResult getSaleReturn(@RequestParam("id") Long id) { + ErpSaleReturnDO saleReturn = saleReturnService.getSaleReturn(id); + if (saleReturn == null) { + return success(null); + } + List saleReturnItemList = saleReturnService.getSaleReturnItemListByReturnId(id); + Map productMap = productService.getProductVOMap( + convertSet(saleReturnItemList, ErpSaleReturnItemDO::getProductId)); + return success(BeanUtils.toBean(saleReturn, ErpSaleReturnRespVO.class, saleReturnVO -> + saleReturnVO.setItems(BeanUtils.toBean(saleReturnItemList, ErpSaleReturnRespVO.Item.class, item -> { + ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId()); + item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO); + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); + })))); + } + + @GetMapping("/page") + @Operation(summary = "获得销售退货分页") + @PreAuthorize("@ss.hasPermission('erp:sale-return:query')") + public CommonResult> getSaleReturnPage(@Valid ErpSaleReturnPageReqVO pageReqVO) { + PageResult pageResult = saleReturnService.getSaleReturnPage(pageReqVO); + return success(buildSaleReturnVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出销售退货 Excel") + @PreAuthorize("@ss.hasPermission('erp:sale-return:export')") + @OperateLog(type = EXPORT) + public void exportSaleReturnExcel(@Valid ErpSaleReturnPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildSaleReturnVOPageResult(saleReturnService.getSaleReturnPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "销售退货.xls", "数据", ErpSaleReturnRespVO.class, list); + } + + private PageResult buildSaleReturnVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 退货项 + List saleReturnItemList = saleReturnService.getSaleReturnItemListByReturnIds( + convertSet(pageResult.getList(), ErpSaleReturnDO::getId)); + Map> saleReturnItemMap = convertMultiMap(saleReturnItemList, ErpSaleReturnItemDO::getReturnId); + // 1.2 产品信息 + Map productMap = productService.getProductVOMap( + convertSet(saleReturnItemList, ErpSaleReturnItemDO::getProductId)); + // 1.3 客户信息 + Map customerMap = customerService.getCustomerMap( + convertSet(pageResult.getList(), ErpSaleReturnDO::getCustomerId)); + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), saleReturn -> Long.parseLong(saleReturn.getCreator()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpSaleReturnRespVO.class, saleReturn -> { + saleReturn.setItems(BeanUtils.toBean(saleReturnItemMap.get(saleReturn.getId()), ErpSaleReturnRespVO.Item.class, + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); + saleReturn.setProductNames(CollUtil.join(saleReturn.getItems(), ",", ErpSaleReturnRespVO.Item::getProductName)); + MapUtils.findAndThen(customerMap, saleReturn.getCustomerId(), supplier -> saleReturn.setCustomerName(supplier.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(saleReturn.getCreator()), user -> saleReturn.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerPageReqVO.java new file mode 100644 index 0000000000..e790cb9581 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerPageReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import java.math.BigDecimal; +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 = "管理后台 - ERP 客户分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpCustomerPageReqVO extends PageParam { + + @Schema(description = "客户名称", example = "张三") + private String name; + + @Schema(description = "手机号码", example = "15601691300") + private String mobile; + + @Schema(description = "联系电话", example = "15601691300") + private String telephone; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerRespVO.java new file mode 100644 index 0000000000..f1a58a03d5 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerRespVO.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import java.math.BigDecimal; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; + +@Schema(description = "管理后台 - ERP 客户 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpCustomerRespVO { + + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "27520") + @ExcelProperty("客户编号") + private Long id; + + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @ExcelProperty("客户名称") + private String name; + + @Schema(description = "联系人", example = "老王") + @ExcelProperty("联系人") + private String contact; + + @Schema(description = "手机号码", example = "15601691300") + @ExcelProperty("手机号码") + private String mobile; + + @Schema(description = "联系电话", example = "15601691300") + @ExcelProperty("联系电话") + private String telephone; + + @Schema(description = "电子邮箱", example = "7685323@qq.com") + @ExcelProperty("电子邮箱") + private String email; + + @Schema(description = "传真", example = "20 7123 4567") + @ExcelProperty("传真") + private String fax; + + @Schema(description = "备注", example = "你猜") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "开启状态", converter = DictConvert.class) + @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer status; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty("排序") + private Integer sort; + + @Schema(description = "纳税人识别号", example = "91130803MA098BY05W") + @ExcelProperty("纳税人识别号") + private String taxNo; + + @Schema(description = "税率", example = "10") + @ExcelProperty("税率") + private BigDecimal taxPercent; + + @Schema(description = "开户行", example = "芋艿") + @ExcelProperty("开户行") + private String bankName; + + @Schema(description = "开户账号", example = "622908212277228617") + @ExcelProperty("开户账号") + private String bankAccount; + + @Schema(description = "开户地址", example = "兴业银行浦东支行") + @ExcelProperty("开户地址") + private String bankAddress; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerSaveReqVO.java new file mode 100644 index 0000000000..aef0b2df1d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerSaveReqVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import jakarta.validation.constraints.*; +import java.math.BigDecimal; + +@Schema(description = "管理后台 - ERP 客户新增/修改 Request VO") +@Data +public class ErpCustomerSaveReqVO { + + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "27520") + private Long id; + + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @NotEmpty(message = "客户名称不能为空") + private String name; + + @Schema(description = "联系人", example = "老王") + private String contact; + + @Schema(description = "手机号码", example = "15601691300") + private String mobile; + + @Schema(description = "联系电话", example = "15601691300") + private String telephone; + + @Schema(description = "电子邮箱", example = "7685323@qq.com") + private String email; + + @Schema(description = "传真", example = "20 7123 4567") + private String fax; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "开启状态不能为空") + private Integer status; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "排序不能为空") + private Integer sort; + + @Schema(description = "纳税人识别号", example = "91130803MA098BY05W") + private String taxNo; + + @Schema(description = "税率", example = "10") + private BigDecimal taxPercent; + + @Schema(description = "开户行", example = "芋艿") + private String bankName; + + @Schema(description = "开户账号", example = "622908212277228617") + private String bankAccount; + + @Schema(description = "开户地址", example = "兴业银行浦东支行") + private String bankAddress; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderPageReqVO.java new file mode 100644 index 0000000000..84d92fb673 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderPageReqVO.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order; + +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 = "管理后台 - ERP 销售订单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpSaleOrderPageReqVO extends PageParam { + + /** + * 出库状态 - 无 + */ + public static final Integer OUT_STATUS_NONE = 0; + /** + * 出库状态 - 部分 + */ + public static final Integer OUT_STATUS_PART = 1; + /** + * 出库状态 - 全部 + */ + public static final Integer OUT_STATUS_ALL = 2; + + /** + * 退货状态 - 无 + */ + public static final Integer RETURN_STATUS_NONE = 0; + /** + * 退货状态 - 部分 + */ + public static final Integer RETURN_STATUS_PART = 1; + /** + * 退货状态 - 全部 + */ + public static final Integer RETURN_STATUS_ALL = 2; + + @Schema(description = "销售单编号", example = "XS001") + private String no; + + @Schema(description = "客户编号", example = "1724") + private Long customerId; + + @Schema(description = "下单时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] orderTime; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "销售状态", example = "2") + private Integer status; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + + @Schema(description = "出库状态", example = "2") + private Integer outStatus; + + @Schema(description = "退货状态", example = "2") + private Integer returnStatus; + + @Schema(description = "是否可出库", example = "true") + private Boolean outEnable; + + @Schema(description = "是否可退货", example = "true") + private Boolean returnEnable; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java new file mode 100644 index 0000000000..e5958a841f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java @@ -0,0 +1,155 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 销售订单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpSaleOrderRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "销售单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") + @ExcelProperty("销售单编号") + private String no; + + @Schema(description = "销售状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("销售状态") + private Integer status; + + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724") + private Long customerId; + @Schema(description = "客户名称", example = "芋道") + @ExcelProperty("客户名称") + private String customerName; + + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "311.89") + @ExcelProperty("结算账户编号") + private Long accountId; + + @Schema(description = "销售员编号", example = "1888") + private Long saleUserId; + + @Schema(description = "下单时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("下单时间") + private LocalDateTime orderTime; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + @Schema(description = "最终合计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("最终合计价格") + private BigDecimal totalPrice; + + @Schema(description = "合计产品价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalProductPrice; + + @Schema(description = "合计税额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalTaxPrice; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal discountPrice; + + @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal depositPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + @ExcelProperty("附件地址") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "订单项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + // ========== 销售出库 ========== + + @Schema(description = "销售出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal outCount; + + // ========== 销售退货(出库)) ========== + + @Schema(description = "销售退货数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal returnCount; + + @Data + public static class Item { + + @Schema(description = "订单项编号", example = "11756") + private Long id; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "税额,单位:元", example = "100.00") + private BigDecimal taxPrice; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 销售出库 ========== + + @Schema(description = "销售出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal outCount; + + // ========== 销售退货(入库)) ========== + + @Schema(description = "销售退货数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal returnCount; + + // ========== 关联字段 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") + private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") + private String productUnitName; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用 + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java new file mode 100644 index 0000000000..e23a1fab31 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 销售订单新增/修改 Request VO") +@Data +public class ErpSaleOrderSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + private Long id; + + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724") + @NotNull(message = "客户编号不能为空") + private Long customerId; + + @Schema(description = "下单时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "下单时间不能为空") + private LocalDateTime orderTime; + + @Schema(description = "销售员编号", example = "1888") + private Long saleUserId; + + @Schema(description = "结算账户编号", example = "31189") + private Long accountId; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "定金金额,单位:元", example = "7127") + private BigDecimal depositPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "订单清单列表") + private List items; + + @Data + public static class Item { + + @Schema(description = "订单项编号", example = "11756") + private Long id; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品单位单位不能为空") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutPageReqVO.java new file mode 100644 index 0000000000..5afeeea843 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutPageReqVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out; + +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 = "管理后台 - ERP 销售出库分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpSaleOutPageReqVO extends PageParam { + + public static final Integer RECEIPT_STATUS_NONE = 0; + public static final Integer RECEIPT_STATUS_PART = 1; + public static final Integer RECEIPT_STATUS_ALL = 2; + + @Schema(description = "销售单编号", example = "XS001") + private String no; + + @Schema(description = "客户编号", example = "1724") + private Long customerId; + + @Schema(description = "出库时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] outTime; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "出库状态", example = "2") + private Integer status; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + + @Schema(description = "仓库编号", example = "1") + private Long warehouseId; + + @Schema(description = "结算账号编号", example = "1") + private Long accountId; + + @Schema(description = "收款状态", example = "1") + private Integer receiptStatus; + + @Schema(description = "是否可收款", example = "true") + private Boolean receiptEnable; // 对应 receiptStatus = [0, 1] + + @Schema(description = "销售单号", example = "1") + private String orderNo; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutRespVO.java new file mode 100644 index 0000000000..bc15a13398 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutRespVO.java @@ -0,0 +1,148 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 销售出库 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpSaleOutRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "出库单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") + @ExcelProperty("出库单编号") + private String no; + + @Schema(description = "出库状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("出库状态") + private Integer status; + + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724") + private Long customerId; + @Schema(description = "客户名称", example = "芋道") + @ExcelProperty("客户名称") + private String customerName; + + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "311.89") + @ExcelProperty("结算账户编号") + private Long accountId; + + @Schema(description = "出库员编号", example = "1888") + private Long saleUserId; + + @Schema(description = "出库时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("出库时间") + private LocalDateTime outTime; + + @Schema(description = "销售订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + private Long orderId; + @Schema(description = "销售订单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") + private String orderNo; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + @Schema(description = "最终合计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("最终合计价格") + private BigDecimal totalPrice; + @Schema(description = "已收款金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal receiptPrice; + + @Schema(description = "合计产品价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalProductPrice; + + @Schema(description = "合计税额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalTaxPrice; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal discountPrice; + + @Schema(description = "其它金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal otherPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + @ExcelProperty("附件地址") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "出库项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + @Data + public static class Item { + + @Schema(description = "出库项编号", example = "11756") + private Long id; + + @Schema(description = "销售订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long orderItemId; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "税额,单位:元", example = "100.00") + private BigDecimal taxPrice; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 关联字段 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") + private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") + private String productUnitName; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用 + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutSaveReqVO.java new file mode 100644 index 0000000000..3a2216a33e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutSaveReqVO.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 销售出库新增/修改 Request VO") +@Data +public class ErpSaleOutSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + private Long id; + + @Schema(description = "结算账户编号", example = "31189") + private Long accountId; + + @Schema(description = "销售员编号", example = "1888") + private Long saleUserId; + + @Schema(description = "出库时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出库时间不能为空") + private LocalDateTime outTime; + + @Schema(description = "销售订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + @NotNull(message = "销售订单编号不能为空") + private Long orderId; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "其它金额,单位:元", example = "7127") + private BigDecimal otherPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "出库清单列表") + private List items; + + @Data + public static class Item { + + @Schema(description = "出库项编号", example = "11756") + private Long id; + + @Schema(description = "销售订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @NotNull(message = "销售订单项编号不能为空") + private Long orderItemId; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品单位单位不能为空") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnPageReqVO.java new file mode 100644 index 0000000000..a9be73beb6 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnPageReqVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns; + +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 = "管理后台 - ERP 销售退货分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpSaleReturnPageReqVO extends PageParam { + + public static final Integer REFUND_STATUS_NONE = 0; + public static final Integer REFUND_STATUS_PART = 1; + public static final Integer REFUND_STATUS_ALL = 2; + + @Schema(description = "销售单编号", example = "XS001") + private String no; + + @Schema(description = "客户编号", example = "1724") + private Long customerId; + + @Schema(description = "退货时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] returnTime; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "退货状态", example = "2") + private Integer status; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + + @Schema(description = "仓库编号", example = "1") + private Long warehouseId; + + @Schema(description = "结算账号编号", example = "1") + private Long accountId; + + @Schema(description = "销售单号", example = "1") + private String orderNo; + + @Schema(description = "退款状态", example = "1") + private Integer refundStatus; + + @Schema(description = "是否可退款", example = "true") + private Boolean refundEnable; // 对应 refundStatus = [0, 1] + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnRespVO.java new file mode 100644 index 0000000000..ba52f4f80d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnRespVO.java @@ -0,0 +1,148 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 销售退货 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpSaleReturnRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "退货单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") + @ExcelProperty("退货单编号") + private String no; + + @Schema(description = "退货状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("退货状态") + private Integer status; + + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724") + private Long customerId; + @Schema(description = "客户名称", example = "芋道") + @ExcelProperty("客户名称") + private String customerName; + + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "311.89") + @ExcelProperty("结算账户编号") + private Long accountId; + + @Schema(description = "退货员编号", example = "1888") + private Long saleUserId; + + @Schema(description = "退货时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("退货时间") + private LocalDateTime returnTime; + + @Schema(description = "销售订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + private Long orderId; + @Schema(description = "销售订单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") + private String orderNo; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + @Schema(description = "最终合计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("最终合计价格") + private BigDecimal totalPrice; + @Schema(description = "已退款金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal refundPrice; + + @Schema(description = "合计产品价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalProductPrice; + + @Schema(description = "合计税额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal totalTaxPrice; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal discountPrice; + + @Schema(description = "其它金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") + private BigDecimal otherPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + @ExcelProperty("附件地址") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "退货项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + @Data + public static class Item { + + @Schema(description = "退货项编号", example = "11756") + private Long id; + + @Schema(description = "销售订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long orderItemId; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "税额,单位:元", example = "100.00") + private BigDecimal taxPrice; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 关联字段 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") + private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") + private String productUnitName; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用 + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnSaveReqVO.java new file mode 100644 index 0000000000..81c046f7d8 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnSaveReqVO.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 销售退货新增/修改 Request VO") +@Data +public class ErpSaleReturnSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + private Long id; + + @Schema(description = "结算账户编号", example = "31189") + private Long accountId; + + @Schema(description = "销售员编号", example = "1888") + private Long saleUserId; + + @Schema(description = "退货时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "退货时间不能为空") + private LocalDateTime returnTime; + + @Schema(description = "销售订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") + @NotNull(message = "销售订单编号不能为空") + private Long orderId; + + @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") + private BigDecimal discountPercent; + + @Schema(description = "其它金额,单位:元", example = "7127") + private BigDecimal otherPrice; + + @Schema(description = "附件地址", example = "https://www.iocoder.cn") + private String fileUrl; + + @Schema(description = "备注", example = "你猜") + private String remark; + + @Schema(description = "退货清单列表") + private List items; + + @Data + public static class Item { + + @Schema(description = "退货项编号", example = "11756") + private Long id; + + @Schema(description = "销售订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @NotNull(message = "销售订单项编号不能为空") + private Long orderItemId; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品单位单位不能为空") + private Long productUnitId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "税率,百分比", example = "99.88") + private BigDecimal taxPercent; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java new file mode 100644 index 0000000000..298ed54fa7 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java @@ -0,0 +1,149 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockCheckService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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 java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 库存调拨单") +@RestController +@RequestMapping("/erp/stock-check") +@Validated +public class ErpStockCheckController { + + @Resource + private ErpStockCheckService stockCheckService; + @Resource + private ErpProductService productService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建库存调拨单") + @PreAuthorize("@ss.hasPermission('erp:stock-check:create')") + public CommonResult createStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO createReqVO) { + return success(stockCheckService.createStockCheck(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新库存调拨单") + @PreAuthorize("@ss.hasPermission('erp:stock-check:update')") + public CommonResult updateStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO updateReqVO) { + stockCheckService.updateStockCheck(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新库存调拨单的状态") + @PreAuthorize("@ss.hasPermission('erp:stock-check:update-status')") + public CommonResult updateStockCheckStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + stockCheckService.updateStockCheckStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除库存调拨单") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:stock-check:delete')") + public CommonResult deleteStockCheck(@RequestParam("ids") List ids) { + stockCheckService.deleteStockCheck(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得库存调拨单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:stock-check:query')") + public CommonResult getStockCheck(@RequestParam("id") Long id) { + ErpStockCheckDO stockCheck = stockCheckService.getStockCheck(id); + if (stockCheck == null) { + return success(null); + } + List stockCheckItemList = stockCheckService.getStockCheckItemListByCheckId(id); + Map productMap = productService.getProductVOMap( + convertSet(stockCheckItemList, ErpStockCheckItemDO::getProductId)); + return success(BeanUtils.toBean(stockCheck, ErpStockCheckRespVO.class, stockCheckVO -> + stockCheckVO.setItems(BeanUtils.toBean(stockCheckItemList, ErpStockCheckRespVO.Item.class, item -> + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))))); + } + + @GetMapping("/page") + @Operation(summary = "获得库存调拨单分页") + @PreAuthorize("@ss.hasPermission('erp:stock-check:query')") + public CommonResult> getStockCheckPage(@Valid ErpStockCheckPageReqVO pageReqVO) { + PageResult pageResult = stockCheckService.getStockCheckPage(pageReqVO); + return success(buildStockCheckVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出库存调拨单 Excel") + @PreAuthorize("@ss.hasPermission('erp:stock-check:export')") + @OperateLog(type = EXPORT) + public void exportStockCheckExcel(@Valid ErpStockCheckPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildStockCheckVOPageResult(stockCheckService.getStockCheckPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "库存调拨单.xls", "数据", ErpStockCheckRespVO.class, list); + } + + private PageResult buildStockCheckVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 盘点项 + List stockCheckItemList = stockCheckService.getStockCheckItemListByCheckIds( + convertSet(pageResult.getList(), ErpStockCheckDO::getId)); + Map> stockCheckItemMap = convertMultiMap(stockCheckItemList, ErpStockCheckItemDO::getCheckId); + // 1.2 产品信息 + Map productMap = productService.getProductVOMap( + convertSet(stockCheckItemList, ErpStockCheckItemDO::getProductId)); + // 1.3 管理员信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), stockCheck -> Long.parseLong(stockCheck.getCreator()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpStockCheckRespVO.class, stockCheck -> { + stockCheck.setItems(BeanUtils.toBean(stockCheckItemMap.get(stockCheck.getId()), ErpStockCheckRespVO.Item.class, + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); + stockCheck.setProductNames(CollUtil.join(stockCheck.getItems(), ",", ErpStockCheckRespVO.Item::getProductName)); + MapUtils.findAndThen(userMap, Long.parseLong(stockCheck.getCreator()), user -> stockCheck.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockController.java new file mode 100644 index 0000000000..912f59731a --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockController.java @@ -0,0 +1,112 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockRespVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +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.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.math.BigDecimal; +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; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 产品库存") +@RestController +@RequestMapping("/erp/stock") +@Validated +public class ErpStockController { + + @Resource + private ErpStockService stockService; + @Resource + private ErpProductService productService; + @Resource + private ErpWarehouseService warehouseService; + + @GetMapping("/get") + @Operation(summary = "获得产品库存") + @Parameters({ + @Parameter(name = "id", description = "编号", example = "1"), // 方案一:传递 id + @Parameter(name = "productId", description = "产品编号", example = "10"), // 方案二:传递 productId + warehouseId + @Parameter(name = "warehouseId", description = "仓库编号", example = "2") + }) + @PreAuthorize("@ss.hasPermission('erp:stock:query')") + public CommonResult getStock(@RequestParam(value = "id", required = false) Long id, + @RequestParam(value = "productId", required = false) Long productId, + @RequestParam(value = "warehouseId", required = false) Long warehouseId) { + ErpStockDO stock = id != null ? stockService.getStock(id) : stockService.getStock(productId, warehouseId); + return success(BeanUtils.toBean(stock, ErpStockRespVO.class)); + } + + @GetMapping("/get-count") + @Operation(summary = "获得产品库存数量") + @Parameter(name = "productId", description = "产品编号", example = "10") + public CommonResult getStockCount(@RequestParam("productId") Long productId) { + return success(stockService.getStockCount(productId)); + } + + @GetMapping("/page") + @Operation(summary = "获得产品库存分页") + @PreAuthorize("@ss.hasPermission('erp:stock:query')") + public CommonResult> getStockPage(@Valid ErpStockPageReqVO pageReqVO) { + PageResult pageResult = stockService.getStockPage(pageReqVO); + return success(buildStockVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出产品库存 Excel") + @PreAuthorize("@ss.hasPermission('erp:stock:export')") + @OperateLog(type = EXPORT) + public void exportStockExcel(@Valid ErpStockPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildStockVOPageResult(stockService.getStockPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "产品库存.xls", "数据", ErpStockRespVO.class, list); + } + + private PageResult buildStockVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + Map productMap = productService.getProductVOMap( + convertSet(pageResult.getList(), ErpStockDO::getProductId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(pageResult.getList(), ErpStockDO::getWarehouseId)); + return BeanUtils.toBean(pageResult, ErpStockRespVO.class, stock -> { + MapUtils.findAndThen(productMap, stock.getProductId(), product -> stock.setProductName(product.getName()) + .setCategoryName(product.getCategoryName()).setUnitName(product.getUnitName())); + MapUtils.findAndThen(warehouseMap, stock.getWarehouseId(), warehouse -> stock.setWarehouseName(warehouse.getName())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java new file mode 100644 index 0000000000..8813da89a0 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java @@ -0,0 +1,165 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockInService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.math.BigDecimal; +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.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 其它入库单") +@RestController +@RequestMapping("/erp/stock-in") +@Validated +public class ErpStockInController { + + @Resource + private ErpStockInService stockInService; + @Resource + private ErpStockService stockService; + @Resource + private ErpProductService productService; + @Resource + private ErpSupplierService supplierService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建其它入库单") + @PreAuthorize("@ss.hasPermission('erp:stock-in:create')") + public CommonResult createStockIn(@Valid @RequestBody ErpStockInSaveReqVO createReqVO) { + return success(stockInService.createStockIn(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新其它入库单") + @PreAuthorize("@ss.hasPermission('erp:stock-in:update')") + public CommonResult updateStockIn(@Valid @RequestBody ErpStockInSaveReqVO updateReqVO) { + stockInService.updateStockIn(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新其它入库单的状态") + @PreAuthorize("@ss.hasPermission('erp:stock-in:update-status')") + public CommonResult updateStockInStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + stockInService.updateStockInStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除其它入库单") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:stock-in:delete')") + public CommonResult deleteStockIn(@RequestParam("ids") List ids) { + stockInService.deleteStockIn(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得其它入库单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:stock-in:query')") + public CommonResult getStockIn(@RequestParam("id") Long id) { + ErpStockInDO stockIn = stockInService.getStockIn(id); + if (stockIn == null) { + return success(null); + } + List stockInItemList = stockInService.getStockInItemListByInId(id); + Map productMap = productService.getProductVOMap( + convertSet(stockInItemList, ErpStockInItemDO::getProductId)); + return success(BeanUtils.toBean(stockIn, ErpStockInRespVO.class, stockInVO -> + stockInVO.setItems(BeanUtils.toBean(stockInItemList, ErpStockInRespVO.Item.class, item -> { + ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId()); + item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO); + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); + })))); + } + + @GetMapping("/page") + @Operation(summary = "获得其它入库单分页") + @PreAuthorize("@ss.hasPermission('erp:stock-in:query')") + public CommonResult> getStockInPage(@Valid ErpStockInPageReqVO pageReqVO) { + PageResult pageResult = stockInService.getStockInPage(pageReqVO); + return success(buildStockInVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出其它入库单 Excel") + @PreAuthorize("@ss.hasPermission('erp:stock-in:export')") + @OperateLog(type = EXPORT) + public void exportStockInExcel(@Valid ErpStockInPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildStockInVOPageResult(stockInService.getStockInPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "其它入库单.xls", "数据", ErpStockInRespVO.class, list); + } + + private PageResult buildStockInVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 入库项 + List stockInItemList = stockInService.getStockInItemListByInIds( + convertSet(pageResult.getList(), ErpStockInDO::getId)); + Map> stockInItemMap = convertMultiMap(stockInItemList, ErpStockInItemDO::getInId); + // 1.2 产品信息 + Map productMap = productService.getProductVOMap( + convertSet(stockInItemList, ErpStockInItemDO::getProductId)); + // 1.3 供应商信息 + Map supplierMap = supplierService.getSupplierMap( + convertSet(pageResult.getList(), ErpStockInDO::getSupplierId)); + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), stockIn -> Long.parseLong(stockIn.getCreator()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpStockInRespVO.class, stockIn -> { + stockIn.setItems(BeanUtils.toBean(stockInItemMap.get(stockIn.getId()), ErpStockInRespVO.Item.class, + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); + stockIn.setProductNames(CollUtil.join(stockIn.getItems(), ",", ErpStockInRespVO.Item::getProductName)); + MapUtils.findAndThen(supplierMap, stockIn.getSupplierId(), supplier -> stockIn.setSupplierName(supplier.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(stockIn.getCreator()), user -> stockIn.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java new file mode 100644 index 0000000000..1df3fd7fca --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java @@ -0,0 +1,160 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMovePageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMoveRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMoveSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockMoveService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.math.BigDecimal; +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.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 库存调拨单") +@RestController +@RequestMapping("/erp/stock-move") +@Validated +public class ErpStockMoveController { + + @Resource + private ErpStockMoveService stockMoveService; + @Resource + private ErpStockService stockService; + @Resource + private ErpProductService productService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建库存调拨单") + @PreAuthorize("@ss.hasPermission('erp:stock-move:create')") + public CommonResult createStockMove(@Valid @RequestBody ErpStockMoveSaveReqVO createReqVO) { + return success(stockMoveService.createStockMove(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新库存调拨单") + @PreAuthorize("@ss.hasPermission('erp:stock-move:update')") + public CommonResult updateStockMove(@Valid @RequestBody ErpStockMoveSaveReqVO updateReqVO) { + stockMoveService.updateStockMove(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新库存调拨单的状态") + @PreAuthorize("@ss.hasPermission('erp:stock-move:update-status')") + public CommonResult updateStockMoveStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + stockMoveService.updateStockMoveStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除库存调拨单") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:stock-move:delete')") + public CommonResult deleteStockMove(@RequestParam("ids") List ids) { + stockMoveService.deleteStockMove(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得库存调拨单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:stock-move:query')") + public CommonResult getStockMove(@RequestParam("id") Long id) { + ErpStockMoveDO stockMove = stockMoveService.getStockMove(id); + if (stockMove == null) { + return success(null); + } + List stockMoveItemList = stockMoveService.getStockMoveItemListByMoveId(id); + Map productMap = productService.getProductVOMap( + convertSet(stockMoveItemList, ErpStockMoveItemDO::getProductId)); + return success(BeanUtils.toBean(stockMove, ErpStockMoveRespVO.class, stockMoveVO -> + stockMoveVO.setItems(BeanUtils.toBean(stockMoveItemList, ErpStockMoveRespVO.Item.class, item -> { + ErpStockDO stock = stockService.getStock(item.getProductId(), item.getFromWarehouseId()); + item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO); + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); + })))); + } + + @GetMapping("/page") + @Operation(summary = "获得库存调拨单分页") + @PreAuthorize("@ss.hasPermission('erp:stock-move:query')") + public CommonResult> getStockMovePage(@Valid ErpStockMovePageReqVO pageReqVO) { + PageResult pageResult = stockMoveService.getStockMovePage(pageReqVO); + return success(buildStockMoveVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出库存调拨单 Excel") + @PreAuthorize("@ss.hasPermission('erp:stock-move:export')") + @OperateLog(type = EXPORT) + public void exportStockMoveExcel(@Valid ErpStockMovePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildStockMoveVOPageResult(stockMoveService.getStockMovePage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "库存调拨单.xls", "数据", ErpStockMoveRespVO.class, list); + } + + private PageResult buildStockMoveVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 调拨项 + List stockMoveItemList = stockMoveService.getStockMoveItemListByMoveIds( + convertSet(pageResult.getList(), ErpStockMoveDO::getId)); + Map> stockMoveItemMap = convertMultiMap(stockMoveItemList, ErpStockMoveItemDO::getMoveId); + // 1.2 产品信息 + Map productMap = productService.getProductVOMap( + convertSet(stockMoveItemList, ErpStockMoveItemDO::getProductId)); + // 1.3 TODO 芋艿:搞仓库信息 + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), stockMove -> Long.parseLong(stockMove.getCreator()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpStockMoveRespVO.class, stockMove -> { + stockMove.setItems(BeanUtils.toBean(stockMoveItemMap.get(stockMove.getId()), ErpStockMoveRespVO.Item.class, + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); + stockMove.setProductNames(CollUtil.join(stockMove.getItems(), ",", ErpStockMoveRespVO.Item::getProductName)); + // TODO 芋艿: +// MapUtils.findAndThen(customerMap, stockMove.getCustomerId(), supplier -> stockMove.setCustomerName(supplier.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(stockMove.getCreator()), user -> stockMove.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockOutController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockOutController.java new file mode 100644 index 0000000000..9ad592f1ad --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockOutController.java @@ -0,0 +1,165 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockOutService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.math.BigDecimal; +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.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 其它出库单") +@RestController +@RequestMapping("/erp/stock-out") +@Validated +public class ErpStockOutController { + + @Resource + private ErpStockOutService stockOutService; + @Resource + private ErpStockService stockService; + @Resource + private ErpProductService productService; + @Resource + private ErpCustomerService customerService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建其它出库单") + @PreAuthorize("@ss.hasPermission('erp:stock-out:create')") + public CommonResult createStockOut(@Valid @RequestBody ErpStockOutSaveReqVO createReqVO) { + return success(stockOutService.createStockOut(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新其它出库单") + @PreAuthorize("@ss.hasPermission('erp:stock-out:update')") + public CommonResult updateStockOut(@Valid @RequestBody ErpStockOutSaveReqVO updateReqVO) { + stockOutService.updateStockOut(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新其它出库单的状态") + @PreAuthorize("@ss.hasPermission('erp:stock-out:update-status')") + public CommonResult updateStockOutStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + stockOutService.updateStockOutStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除其它出库单") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:stock-out:delete')") + public CommonResult deleteStockOut(@RequestParam("ids") List ids) { + stockOutService.deleteStockOut(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得其它出库单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:stock-out:query')") + public CommonResult getStockOut(@RequestParam("id") Long id) { + ErpStockOutDO stockOut = stockOutService.getStockOut(id); + if (stockOut == null) { + return success(null); + } + List stockOutItemList = stockOutService.getStockOutItemListByOutId(id); + Map productMap = productService.getProductVOMap( + convertSet(stockOutItemList, ErpStockOutItemDO::getProductId)); + return success(BeanUtils.toBean(stockOut, ErpStockOutRespVO.class, stockOutVO -> + stockOutVO.setItems(BeanUtils.toBean(stockOutItemList, ErpStockOutRespVO.Item.class, item -> { + ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId()); + item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO); + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); + })))); + } + + @GetMapping("/page") + @Operation(summary = "获得其它出库单分页") + @PreAuthorize("@ss.hasPermission('erp:stock-out:query')") + public CommonResult> getStockOutPage(@Valid ErpStockOutPageReqVO pageReqVO) { + PageResult pageResult = stockOutService.getStockOutPage(pageReqVO); + return success(buildStockOutVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出其它出库单 Excel") + @PreAuthorize("@ss.hasPermission('erp:stock-out:export')") + @OperateLog(type = EXPORT) + public void exportStockOutExcel(@Valid ErpStockOutPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildStockOutVOPageResult(stockOutService.getStockOutPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "其它出库单.xls", "数据", ErpStockOutRespVO.class, list); + } + + private PageResult buildStockOutVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1.1 出库项 + List stockOutItemList = stockOutService.getStockOutItemListByOutIds( + convertSet(pageResult.getList(), ErpStockOutDO::getId)); + Map> stockOutItemMap = convertMultiMap(stockOutItemList, ErpStockOutItemDO::getOutId); + // 1.2 产品信息 + Map productMap = productService.getProductVOMap( + convertSet(stockOutItemList, ErpStockOutItemDO::getProductId)); + // 1.3 客户信息 + Map customerMap = customerService.getCustomerMap( + convertSet(pageResult.getList(), ErpStockOutDO::getCustomerId)); + // 1.4 管理员信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), stockOut -> Long.parseLong(stockOut.getCreator()))); + // 2. 开始拼接 + return BeanUtils.toBean(pageResult, ErpStockOutRespVO.class, stockOut -> { + stockOut.setItems(BeanUtils.toBean(stockOutItemMap.get(stockOut.getId()), ErpStockOutRespVO.Item.class, + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); + stockOut.setProductNames(CollUtil.join(stockOut.getItems(), ",", ErpStockOutRespVO.Item::getProductName)); + MapUtils.findAndThen(customerMap, stockOut.getCustomerId(), supplier -> stockOut.setCustomerName(supplier.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(stockOut.getCreator()), user -> stockOut.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockRecordController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockRecordController.java new file mode 100644 index 0000000000..6ed4538945 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockRecordController.java @@ -0,0 +1,105 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock; + +import cn.hutool.core.collection.CollUtil; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordRespVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService; +import cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +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; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 产品库存明细") +@RestController +@RequestMapping("/erp/stock-record") +@Validated +public class ErpStockRecordController { + + @Resource + private ErpStockRecordService stockRecordService; + @Resource + private ErpProductService productService; + @Resource + private ErpWarehouseService warehouseService; + + @Resource + private AdminUserApi adminUserApi; + + @GetMapping("/get") + @Operation(summary = "获得产品库存明细") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:stock-record:query')") + public CommonResult getStockRecord(@RequestParam("id") Long id) { + ErpStockRecordDO stockRecord = stockRecordService.getStockRecord(id); + return success(BeanUtils.toBean(stockRecord, ErpStockRecordRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得产品库存明细分页") + @PreAuthorize("@ss.hasPermission('erp:stock-record:query')") + public CommonResult> getStockRecordPage(@Valid ErpStockRecordPageReqVO pageReqVO) { + PageResult pageResult = stockRecordService.getStockRecordPage(pageReqVO); + return success(buildStockRecrodVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出产品库存明细 Excel") + @PreAuthorize("@ss.hasPermission('erp:stock-record:export')") + @OperateLog(type = EXPORT) + public void exportStockRecordExcel(@Valid ErpStockRecordPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = buildStockRecrodVOPageResult(stockRecordService.getStockRecordPage(pageReqVO)).getList(); + // 导出 Excel + ExcelUtils.write(response, "产品库存明细.xls", "数据", ErpStockRecordRespVO.class, list); + } + + private PageResult buildStockRecrodVOPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + Map productMap = productService.getProductVOMap( + convertSet(pageResult.getList(), ErpStockRecordDO::getProductId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(pageResult.getList(), ErpStockRecordDO::getWarehouseId)); + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), record -> Long.parseLong(record.getCreator()))); + return BeanUtils.toBean(pageResult, ErpStockRecordRespVO.class, stock -> { + MapUtils.findAndThen(productMap, stock.getProductId(), product -> stock.setProductName(product.getName()) + .setCategoryName(product.getCategoryName()).setUnitName(product.getUnitName())); + MapUtils.findAndThen(warehouseMap, stock.getWarehouseId(), warehouse -> stock.setWarehouseName(warehouse.getName())); + MapUtils.findAndThen(userMap, Long.parseLong(stock.getCreator()), user -> stock.setCreatorName(user.getNickname())); + }); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java new file mode 100644 index 0000000000..744f439f57 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java @@ -0,0 +1,116 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.ErpWarehouseSaveReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseRespVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +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.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 仓库") +@RestController +@RequestMapping("/erp/warehouse") +@Validated +public class ErpWarehouseController { + + @Resource + private ErpWarehouseService warehouseService; + + @PostMapping("/create") + @Operation(summary = "创建仓库") + @PreAuthorize("@ss.hasPermission('erp:warehouse:create')") + public CommonResult createWarehouse(@Valid @RequestBody ErpWarehouseSaveReqVO createReqVO) { + return success(warehouseService.createWarehouse(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新仓库") + @PreAuthorize("@ss.hasPermission('erp:warehouse:update')") + public CommonResult updateWarehouse(@Valid @RequestBody ErpWarehouseSaveReqVO updateReqVO) { + warehouseService.updateWarehouse(updateReqVO); + return success(true); + } + + @PutMapping("/update-default-status") + @Operation(summary = "更新仓库默认状态") + @Parameters({ + @Parameter(name = "id", description = "编号", required = true), + @Parameter(name = "status", description = "状态", required = true) + }) + public CommonResult updateWarehouseDefaultStatus(@RequestParam("id") Long id, + @RequestParam("defaultStatus") Boolean defaultStatus) { + warehouseService.updateWarehouseDefaultStatus(id, defaultStatus); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除仓库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('erp: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('erp:warehouse:query')") + public CommonResult getWarehouse(@RequestParam("id") Long id) { + ErpWarehouseDO warehouse = warehouseService.getWarehouse(id); + return success(BeanUtils.toBean(warehouse, ErpWarehouseRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得仓库分页") + @PreAuthorize("@ss.hasPermission('erp:warehouse:query')") + public CommonResult> getWarehousePage(@Valid ErpWarehousePageReqVO pageReqVO) { + PageResult pageResult = warehouseService.getWarehousePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, ErpWarehouseRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得仓库精简列表", description = "只包含被开启的仓库,主要用于前端的下拉选项") + public CommonResult> getWarehouseSimpleList() { + List list = warehouseService.getWarehouseListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, warehouse -> new ErpWarehouseRespVO().setId(warehouse.getId()) + .setName(warehouse.getName()).setDefaultStatus(warehouse.getDefaultStatus()))); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出仓库 Excel") + @PreAuthorize("@ss.hasPermission('erp:warehouse:export')") + @OperateLog(type = EXPORT) + public void exportWarehouseExcel(@Valid ErpWarehousePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = warehouseService.getWarehousePage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "仓库.xls", "数据", ErpWarehouseRespVO.class, + BeanUtils.toBean(list, ErpWarehouseRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckPageReqVO.java new file mode 100644 index 0000000000..2bae14c1eb --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckPageReqVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +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 = "管理后台 - ERP 库存盘点单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpStockCheckPageReqVO extends PageParam { + + @Schema(description = "盘点单号", example = "S123") + private String no; + + @Schema(description = "仓库编号", example = "3113") + private Long warehouseId; + + @Schema(description = "盘点时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] checkTime; + + @Schema(description = "状态", example = "10") + @InEnum(ErpAuditStatus.class) + private Integer status; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java new file mode 100644 index 0000000000..af53e3c726 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java @@ -0,0 +1,111 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.module.erp.enums.DictTypeConstants.AUDIT_STATUS; + +@Schema(description = "管理后台 - ERP 库存盘点单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpStockCheckRespVO { + + @Schema(description = "盘点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @ExcelProperty("盘点编号") + private Long id; + + @Schema(description = "盘点单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "S123") + @ExcelProperty("盘点单号") + private String no; + + @Schema(description = "盘点时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("盘点时间") + private LocalDateTime checkTime; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + + @Schema(description = "合计金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("合计金额") + private BigDecimal totalPrice; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty(value = "状态", converter = DictConvert.class) + @DictFormat(AUDIT_STATUS) + private Integer status; + + @Schema(description = "备注", example = "随便") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") + private String fileUrl; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "盘点项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + @Data + public static class Item { + + @Schema(description = "盘点项编号", example = "11756") + private Long id; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "账面数量(当前库存)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "账面数量不能为空") + private BigDecimal stockCount; + + @Schema(description = "实际数量(实际库存)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "实际数量不能为空") + private BigDecimal actualCount; + + @Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "盈亏数量不能为空") + private BigDecimal count; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 关联字段 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") + private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") + private String productUnitName; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java new file mode 100644 index 0000000000..0af223fb50 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 其它出库单新增/修改 Request VO") +@Data +public class ErpStockCheckSaveReqVO { + + @Schema(description = "出库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long id; + + @Schema(description = "出库时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出库时间不能为空") + private LocalDateTime checkTime; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") + private String fileUrl; + + @Schema(description = "出库项列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "出库项列表不能为空") + @Valid + private List items; + + @Data + public static class Item { + + @Schema(description = "出库项编号", example = "11756") + private Long id; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "账面数量(当前库存)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "账面数量不能为空") + private BigDecimal stockCount; + + @Schema(description = "实际数量(实际库存)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "实际数量不能为空") + private BigDecimal actualCount; + + @Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "盈亏数量不能为空") + private BigDecimal count; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInPageReqVO.java new file mode 100644 index 0000000000..02e3db37a9 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInPageReqVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +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 = "管理后台 - ERP 其它入库单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpStockInPageReqVO extends PageParam { + + @Schema(description = "入库单号", example = "S123") + private String no; + + @Schema(description = "供应商编号", example = "3113") + private Long supplierId; + + @Schema(description = "入库时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] inTime; + + @Schema(description = "状态", example = "10") + @InEnum(ErpAuditStatus.class) + private Integer status; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + + @Schema(description = "仓库编号", example = "1") + private Long warehouseId; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java new file mode 100644 index 0000000000..077b9dd1b6 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java @@ -0,0 +1,110 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.module.erp.enums.DictTypeConstants.AUDIT_STATUS; + +@Schema(description = "管理后台 - ERP 其它入库单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpStockInRespVO { + + @Schema(description = "入库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @ExcelProperty("入库编号") + private Long id; + + @Schema(description = "入库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "S123") + @ExcelProperty("入库单号") + private String no; + + @Schema(description = "供应商编号", example = "3113") + private Long supplierId; + @Schema(description = "供应商名称", example = "芋道") + @ExcelProperty("供应商名称") + private String supplierName; + + @Schema(description = "入库时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("入库时间") + private LocalDateTime inTime; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + + @Schema(description = "合计金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("合计金额") + private BigDecimal totalPrice; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty(value = "状态", converter = DictConvert.class) + @DictFormat(AUDIT_STATUS) + private Integer status; + + @Schema(description = "备注", example = "随便") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") + private String fileUrl; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "入库项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + @Data + public static class Item { + + @Schema(description = "入库项编号", example = "11756") + private Long id; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal count; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 关联字段 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") + private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") + private String productUnitName; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用 + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInSaveReqVO.java new file mode 100644 index 0000000000..0187872c83 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInSaveReqVO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 其它入库单新增/修改 Request VO") +@Data +public class ErpStockInSaveReqVO { + + @Schema(description = "入库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long id; + + @Schema(description = "供应商编号", example = "3113") + private Long supplierId; + + @Schema(description = "入库时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "入库时间不能为空") + private LocalDateTime inTime; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") + private String fileUrl; + + @Schema(description = "入库项列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "入库项列表不能为空") + @Valid + private List items; + + @Data + public static class Item { + + @Schema(description = "入库项编号", example = "11756") + private Long id; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMovePageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMovePageReqVO.java new file mode 100644 index 0000000000..98a1fe95e9 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMovePageReqVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +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 = "管理后台 - ERP 库存调拨单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpStockMovePageReqVO extends PageParam { + + @Schema(description = "调拨单号", example = "S123") + private String no; + + @Schema(description = "调拨时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] moveTime; + + @Schema(description = "状态", example = "10") + @InEnum(ErpAuditStatus.class) + private Integer status; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + + @Schema(description = "调出仓库编号", example = "1") + private Long fromWarehouseId; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveRespVO.java new file mode 100644 index 0000000000..799ddc3f16 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveRespVO.java @@ -0,0 +1,107 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.module.erp.enums.DictTypeConstants.AUDIT_STATUS; + +@Schema(description = "管理后台 - ERP 库存调拨单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpStockMoveRespVO { + + @Schema(description = "调拨编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @ExcelProperty("调拨编号") + private Long id; + + @Schema(description = "调拨单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "S123") + @ExcelProperty("调拨单号") + private String no; + + @Schema(description = "调拨时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("调拨时间") + private LocalDateTime moveTime; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + + @Schema(description = "合计金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("合计金额") + private BigDecimal totalPrice; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty(value = "状态", converter = DictConvert.class) + @DictFormat(AUDIT_STATUS) + private Integer status; + + @Schema(description = "备注", example = "随便") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") + private String fileUrl; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "调拨项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + @Data + public static class Item { + + @Schema(description = "调拨项编号", example = "11756") + private Long id; + + @Schema(description = "调出仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long fromWarehouseId; + + @Schema(description = "调入仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888") + private Long toWarehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal count; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 关联字段 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") + private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") + private String productUnitName; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用 + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveSaveReqVO.java new file mode 100644 index 0000000000..17a431561b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveSaveReqVO.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move; + +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 库存调拨单新增/修改 Request VO") +@Data +public class ErpStockMoveSaveReqVO { + + @Schema(description = "调拨编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long id; + + @Schema(description = "客户编号", example = "3113") + private Long customerId; + + @Schema(description = "调拨时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "调拨时间不能为空") + private LocalDateTime moveTime; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") + private String fileUrl; + + @Schema(description = "调拨项列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "调拨项列表不能为空") + @Valid + private List items; + + @Data + public static class Item { + + @Schema(description = "调拨项编号", example = "11756") + private Long id; + + @Schema(description = "调出仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "调出仓库编号不能为空") + private Long fromWarehouseId; + + @Schema(description = "调入仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888") + @NotNull(message = "调入仓库编号不能为空") + private Long toWarehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "备注", example = "随便") + private String remark; + + @AssertTrue(message = "调出、调仓仓库不能相同") + @JsonIgnore + public boolean isWarehouseValid() { + return ObjectUtil.notEqual(fromWarehouseId, toWarehouseId); + } + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutPageReqVO.java new file mode 100644 index 0000000000..5f6558b19e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutPageReqVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +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 = "管理后台 - ERP 其它出库单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpStockOutPageReqVO extends PageParam { + + @Schema(description = "出库单号", example = "S123") + private String no; + + @Schema(description = "客户编号", example = "3113") + private Long customerId; + + @Schema(description = "出库时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] outTime; + + @Schema(description = "状态", example = "10") + @InEnum(ErpAuditStatus.class) + private Integer status; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + + @Schema(description = "仓库编号", example = "1") + private Long warehouseId; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutRespVO.java new file mode 100644 index 0000000000..22a88e7c93 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutRespVO.java @@ -0,0 +1,110 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.module.erp.enums.DictTypeConstants.AUDIT_STATUS; + +@Schema(description = "管理后台 - ERP 其它出库单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpStockOutRespVO { + + @Schema(description = "出库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @ExcelProperty("出库编号") + private Long id; + + @Schema(description = "出库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "S123") + @ExcelProperty("出库单号") + private String no; + + @Schema(description = "客户编号", example = "3113") + private Long customerId; + @Schema(description = "客户名称", example = "芋道") + @ExcelProperty("客户名称") + private String customerName; + + @Schema(description = "出库时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("出库时间") + private LocalDateTime outTime; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + + @Schema(description = "合计金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("合计金额") + private BigDecimal totalPrice; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty(value = "状态", converter = DictConvert.class) + @DictFormat(AUDIT_STATUS) + private Integer status; + + @Schema(description = "备注", example = "随便") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") + private String fileUrl; + + @Schema(description = "创建人", example = "芋道") + private String creator; + @Schema(description = "创建人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "出库项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + @Data + public static class Item { + + @Schema(description = "出库项编号", example = "11756") + private Long id; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal count; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 关联字段 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") + private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") + private String productUnitName; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用 + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutSaveReqVO.java new file mode 100644 index 0000000000..5a903798e3 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutSaveReqVO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 其它出库单新增/修改 Request VO") +@Data +public class ErpStockOutSaveReqVO { + + @Schema(description = "出库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long id; + + @Schema(description = "客户编号", example = "3113") + private Long customerId; + + @Schema(description = "出库时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出库时间不能为空") + private LocalDateTime outTime; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") + private String fileUrl; + + @Schema(description = "出库项列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "出库项列表不能为空") + @Valid + private List items; + + @Data + public static class Item { + + @Schema(description = "出库项编号", example = "11756") + private Long id; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "产品数量不能为空") + private BigDecimal count; + + @Schema(description = "备注", example = "随便") + private String remark; + + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordPageReqVO.java new file mode 100644 index 0000000000..c478e4fef2 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordPageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record; + +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 = "管理后台 - ERP 产品库存明细分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpStockRecordPageReqVO extends PageParam { + + @Schema(description = "产品编号", example = "10625") + private Long productId; + + @Schema(description = "仓库编号", example = "32407") + private Long warehouseId; + + @Schema(description = "业务类型", example = "10") + private Integer bizType; + + @Schema(description = "业务单号", example = "Z110") + private String bizNo; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordRespVO.java new file mode 100644 index 0000000000..ff4b3e12a1 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordRespVO.java @@ -0,0 +1,87 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.erp.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - ERP 产品库存明细 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpStockRecordRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18909") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10625") + private Long productId; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32407") + private Long warehouseId; + + @Schema(description = "出入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "11084") + @ExcelProperty("出入库数量") + private BigDecimal count; + + @Schema(description = "总库存量", requiredMode = Schema.RequiredMode.REQUIRED, example = "4307") + @ExcelProperty("总库存量") + private BigDecimal totalCount; + + @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty(value = "业务类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.STOCK_RECORD_BIZ_TYPE) + private Integer bizType; + + @Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "27093") + @ExcelProperty("业务编号") + private Long bizId; + + @Schema(description = "业务项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23516") + @ExcelProperty("业务项编号") + private Long bizItemId; + + @Schema(description = "业务单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "Z110") + @ExcelProperty("业务单号") + private String bizNo; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "25682") + private String creator; + + // ========== 产品信息 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "苹果") + @ExcelProperty("产品名称") + private String productName; + + @Schema(description = "产品分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "水果") + @ExcelProperty("产品分类") + private String categoryName; + + @Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "个") + @ExcelProperty("单位") + private String unitName; + + // ========== 仓库信息 ========== + + @Schema(description = "仓库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @ExcelProperty("仓库名称") + private String warehouseName; + + // ========== 用户信息 ========== + + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @ExcelProperty("创建人") + private String creatorName; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockPageReqVO.java new file mode 100644 index 0000000000..f7f3fa3439 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockPageReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock; + +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 = "管理后台 - ERP 库存分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpStockPageReqVO extends PageParam { + + @Schema(description = "产品编号", example = "19614") + private Long productId; + + @Schema(description = "仓库编号", example = "2802") + private Long warehouseId; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockRespVO.java new file mode 100644 index 0000000000..06366a0dd4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockRespVO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - ERP 库存 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpStockRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17086") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19614") + private Long productId; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2802") + private Long warehouseId; + + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "21935") + @ExcelProperty("库存数量") + private BigDecimal count; + + // ========== 产品信息 ========== + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "苹果") + @ExcelProperty("产品名称") + private String productName; + + @Schema(description = "产品分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "水果") + @ExcelProperty("产品分类") + private String categoryName; + + @Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "个") + @ExcelProperty("单位") + private String unitName; + + // ========== 仓库信息 ========== + + @Schema(description = "仓库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @ExcelProperty("仓库名称") + private String warehouseName; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehousePageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehousePageReqVO.java new file mode 100644 index 0000000000..2accf9a0ff --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehousePageReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - ERP 仓库分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpWarehousePageReqVO extends PageParam { + + @Schema(description = "仓库名称", example = "李四") + private String name; + + @Schema(description = "开启状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseRespVO.java new file mode 100644 index 0000000000..188d426997 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseRespVO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - ERP 仓库 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpWarehouseRespVO { + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11614") + @ExcelProperty("仓库编号") + private Long id; + + @Schema(description = "仓库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @ExcelProperty("仓库名称") + private String name; + + @Schema(description = "仓库地址", example = "上海陆家嘴") + @ExcelProperty("仓库地址") + private String address; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty("排序") + private Long sort; + + @Schema(description = "备注", example = "随便") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "负责人", example = "芋头") + @ExcelProperty("负责人") + private String principal; + + @Schema(description = "仓储费,单位:元", example = "13973") + @ExcelProperty("仓储费,单位:元") + private BigDecimal warehousePrice; + + @Schema(description = "搬运费,单位:元", example = "9903") + @ExcelProperty("搬运费,单位:元") + private BigDecimal truckagePrice; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "开启状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "是否默认", example = "1") + @ExcelProperty("是否默认") + private Boolean defaultStatus; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseSaveReqVO.java new file mode 100644 index 0000000000..8a6f1ce78c --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseSaveReqVO.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo; + +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 lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - ERP 仓库新增/修改 Request VO") +@Data +public class ErpWarehouseSaveReqVO { + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11614") + private Long id; + + @Schema(description = "仓库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @NotEmpty(message = "仓库名称不能为空") + private String name; + + @Schema(description = "仓库地址", example = "上海陆家嘴") + private String address; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "排序不能为空") + private Long sort; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "负责人", example = "芋头") + private String principal; + + @Schema(description = "仓储费,单位:元", example = "13973") + private BigDecimal warehousePrice; + + @Schema(description = "搬运费,单位:元", example = "9903") + private BigDecimal truckagePrice; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "开启状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/package-info.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/package-info.java new file mode 100644 index 0000000000..ef0e5accfc --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/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.erp.controller; diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpAccountDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpAccountDO.java new file mode 100644 index 0000000000..fe01cc228b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpAccountDO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.finance; + +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.*; + +/** + * ERP 结算账户 DO + * + * @author 芋道源码 + */ +@TableName("erp_account") +@KeySequence("erp_account_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpAccountDO extends BaseDO { + + /** + * 结算账户编号 + */ + @TableId + private Long id; + /** + * 账户名称 + */ + private String name; + /** + * 账户编码 + */ + private String no; + /** + * 备注 + */ + private String remark; + /** + * 开启状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 排序 + */ + private Integer sort; + /** + * 是否默认 + */ + private Boolean defaultStatus; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java new file mode 100644 index 0000000000..edb55edbfc --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.finance; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +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; + +/** + * ERP 付款单 DO + * + * @author 芋道源码 + */ +@TableName("erp_finance_payment") +@KeySequence("erp_finance_payment_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpFinancePaymentDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 付款单号 + */ + private String no; + /** + * 付款状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 付款时间 + */ + private LocalDateTime paymentTime; + /** + * 财务人员编号 + * + * 关联 AdminUserDO 的 id 字段 + */ + private Long financeUserId; + /** + * 供应商编号 + * + * 关联 {@link ErpSupplierDO#getId()} + */ + private Long supplierId; + /** + * 付款账户编号 + * + * 关联 {@link ErpAccountDO#getId()} + */ + private Long accountId; + + /** + * 合计价格,单位:元 + */ + private BigDecimal totalPrice; + /** + * 优惠金额,单位:元 + */ + private BigDecimal discountPrice; + /** + * 实付金额,单位:分 + * + * paymentPrice = totalPrice - discountPrice + */ + private BigDecimal paymentPrice; + + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentItemDO.java new file mode 100644 index 0000000000..dd5bc5f69b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentItemDO.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.finance; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 付款项 DO + * + * @author 芋道源码 + */ +@TableName("erp_finance_payment_item") +@KeySequence("erp_finance_payment_item_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpFinancePaymentItemDO extends BaseDO { + + /** + * 入库项编号 + */ + @TableId + private Long id; + /** + * 付款单编号 + * + * 关联 {@link ErpFinancePaymentDO#getId()} + */ + private Long paymentId; + + /** + * 业务类型 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum} 的采购入库、退货 + */ + private Integer bizType; + /** + * 业务编号 + * + * 例如说:{@link ErpPurchaseInDO#getId()} + */ + private Long bizId; + /** + * 业务单号 + * + * 例如说:{@link ErpPurchaseInDO#getNo()} + */ + private String bizNo; + + /** + * 应付金额,单位:分 + */ + private BigDecimal totalPrice; + /** + * 已付金额,单位:分 + */ + private BigDecimal paidPrice; + /** + * 本次付款,单位:分 + */ + private BigDecimal paymentPrice; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinanceReceiptDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinanceReceiptDO.java new file mode 100644 index 0000000000..46a5595054 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinanceReceiptDO.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.finance; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO; +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; + +/** + * ERP 收款单 DO + * + * @author 芋道源码 + */ +@TableName("erp_finance_receipt") +@KeySequence("erp_finance_receipt_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpFinanceReceiptDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 收款单号 + */ + private String no; + /** + * 收款状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 收款时间 + */ + private LocalDateTime receiptTime; + /** + * 财务人员编号 + * + * 关联 AdminUserDO 的 id 字段 + */ + private Long financeUserId; + /** + * 客户编号 + * + * 关联 {@link ErpCustomerDO#getId()} + */ + private Long customerId; + /** + * 收款账户编号 + * + * 关联 {@link ErpAccountDO#getId()} + */ + private Long accountId; + + /** + * 合计价格,单位:元 + */ + private BigDecimal totalPrice; + /** + * 优惠金额,单位:元 + */ + private BigDecimal discountPrice; + /** + * 实付金额,单位:分 + * + * receiptPrice = totalPrice - discountPrice + */ + private BigDecimal receiptPrice; + + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinanceReceiptItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinanceReceiptItemDO.java new file mode 100644 index 0000000000..87f051d5ce --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinanceReceiptItemDO.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.finance; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 收款项 DO + * + * @author 芋道源码 + */ +@TableName("erp_finance_receipt_item") +@KeySequence("erp_finance_receipt_item_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpFinanceReceiptItemDO extends BaseDO { + + /** + * 入库项编号 + */ + @TableId + private Long id; + /** + * 收款单编号 + * + * 关联 {@link ErpFinanceReceiptDO#getId()} + */ + private Long receiptId; + + /** + * 业务类型 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum} 的销售出库、退货 + */ + private Integer bizType; + /** + * 业务编号 + * + * 例如说:{@link ErpSaleOutDO#getId()} + */ + private Long bizId; + /** + * 业务单号 + * + * 例如说:{@link ErpSaleOutDO#getNo()} + */ + private String bizNo; + + /** + * 应收金额,单位:分 + */ + private BigDecimal totalPrice; + /** + * 已收金额,单位:分 + */ + private BigDecimal receiptedPrice; + /** + * 本次收款,单位:分 + */ + private BigDecimal receiptPrice; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductCategoryDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductCategoryDO.java new file mode 100644 index 0000000000..4c6225158b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductCategoryDO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.product; + +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.*; + +/** + * ERP 产品分类 DO + * + * @author 芋道源码 + */ +@TableName("erp_product_category") +@KeySequence("erp_product_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpProductCategoryDO extends BaseDO { + + public static final Long PARENT_ID_ROOT = 0L; + + /** + * 分类编号 + */ + @TableId + private Long id; + /** + * 父分类编号 + */ + private Long parentId; + /** + * 分类名称 + */ + private String name; + /** + * 分类编码 + */ + private String code; + /** + * 分类排序 + */ + private Integer sort; + /** + * 开启状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductDO.java new file mode 100644 index 0000000000..31e4aa2d9a --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductDO.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.product; + +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; + +/** + * ERP 产品 DO + * + * @author 芋道源码 + */ +@TableName("erp_product") +@KeySequence("erp_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpProductDO extends BaseDO { + + /** + * 产品编号 + */ + @TableId + private Long id; + /** + * 产品名称 + */ + private String name; + /** + * 产品条码 + */ + private String barCode; + /** + * 产品分类编号 + * + * 关联 {@link ErpProductCategoryDO#getId()} + */ + private Long categoryId; + /** + * 单位编号 + * + * 关联 {@link ErpProductUnitDO#getId()} + */ + private Long unitId; + /** + * 产品状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 产品规格 + */ + private String standard; + /** + * 产品备注 + */ + private String remark; + /** + * 保质期天数 + */ + private Integer expiryDay; + /** + * 基础重量(kg) + */ + private BigDecimal weight; + /** + * 采购价格,单位:元 + */ + private BigDecimal purchasePrice; + /** + * 销售价格,单位:元 + */ + private BigDecimal salePrice; + /** + * 最低价格,单位:元 + */ + private BigDecimal minPrice; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductUnitDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductUnitDO.java new file mode 100644 index 0000000000..73c96c2901 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductUnitDO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.product; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * ERP 产品单位 DO + * + * @author 芋道源码 + */ +@TableName("erp_product_unit") +@KeySequence("erp_product_unit_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpProductUnitDO extends BaseDO { + + /** + * 单位编号 + */ + @TableId + private Long id; + /** + * 单位名字 + */ + private String name; + /** + * 单位状态 + */ + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInDO.java new file mode 100644 index 0000000000..b8186f509f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInDO.java @@ -0,0 +1,122 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.purchase; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +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; + +/** + * ERP 采购入库 DO + * + * @author 芋道源码 + */ +@TableName(value = "erp_purchase_in") +@KeySequence("erp_purchase_in_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpPurchaseInDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 采购入库单号 + */ + private String no; + /** + * 入库状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 供应商编号 + * + * 关联 {@link ErpSupplierDO#getId()} + */ + private Long supplierId; + /** + * 结算账户编号 + * + * 关联 {@link ErpAccountDO#getId()} + */ + private Long accountId; + /** + * 入库时间 + */ + private LocalDateTime inTime; + + /** + * 采购订单编号 + * + * 关联 {@link ErpPurchaseOrderDO#getId()} + */ + private Long orderId; + /** + * 采购订单号 + * + * 冗余 {@link ErpPurchaseOrderDO#getNo()} + */ + private String orderNo; + + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 最终合计价格,单位:元 + * + * totalPrice = totalProductPrice + totalTaxPrice - discountPrice + otherPrice + */ + private BigDecimal totalPrice; + /** + * 已支付金额,单位:元 + * + * 目的:和 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO} 结合,记录已支付金额 + */ + private BigDecimal paymentPrice; + + /** + * 合计产品价格,单位:元 + */ + private BigDecimal totalProductPrice; + /** + * 合计税额,单位:元 + */ + private BigDecimal totalTaxPrice; + /** + * 优惠率,百分比 + */ + private BigDecimal discountPercent; + /** + * 优惠金额,单位:元 + * + * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent + */ + private BigDecimal discountPrice; + /** + * 其它金额,单位:元 + */ + private BigDecimal otherPrice; + + /** + * 附件地址 + */ + private String fileUrl; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInItemDO.java new file mode 100644 index 0000000000..1597bc10bb --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInItemDO.java @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.purchase; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 采购入库项 DO + * + * @author 芋道源码 + */ +@TableName("erp_purchase_in_items") +@KeySequence("erp_purchase_in_items_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpPurchaseInItemDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 采购入库编号 + * + * 关联 {@link ErpPurchaseInDO##getId()} + */ + private Long inId; + /** + * 采购订单项编号 + * + * 关联 {@link ErpPurchaseOrderItemDO#getId()} + * 目的:方便更新关联的采购订单项的入库数量 + */ + private Long orderItemId; + /** + * 仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位单位 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + + /** + * 产品单位单价,单位:元 + */ + private BigDecimal productPrice; + /** + * 数量 + */ + private BigDecimal count; + /** + * 总价,单位:元 + * + * totalPrice = productPrice * count + */ + private BigDecimal totalPrice; + /** + * 税率,百分比 + */ + private BigDecimal taxPercent; + /** + * 税额,单位:元 + * + * taxPrice = totalPrice * taxPercent + */ + private BigDecimal taxPrice; + + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java new file mode 100644 index 0000000000..bba1542e3e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java @@ -0,0 +1,115 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.purchase; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +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; + +/** + * ERP 采购订单 DO + * + * @author 芋道源码 + */ +@TableName(value = "erp_purchase_order") +@KeySequence("erp_purchase_order_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpPurchaseOrderDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 采购订单号 + */ + private String no; + /** + * 采购状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 供应商编号 + * + * 关联 {@link ErpSupplierDO#getId()} + */ + private Long supplierId; + /** + * 结算账户编号 + * + * 关联 {@link ErpAccountDO#getId()} + */ + private Long accountId; + /** + * 下单时间 + */ + private LocalDateTime orderTime; + + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 最终合计价格,单位:元 + * + * totalPrice = totalProductPrice + totalTaxPrice - discountPrice + */ + private BigDecimal totalPrice; + + /** + * 合计产品价格,单位:元 + */ + private BigDecimal totalProductPrice; + /** + * 合计税额,单位:元 + */ + private BigDecimal totalTaxPrice; + /** + * 优惠率,百分比 + */ + private BigDecimal discountPercent; + /** + * 优惠金额,单位:元 + * + * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent + */ + private BigDecimal discountPrice; + /** + * 定金金额,单位:元 + */ + private BigDecimal depositPrice; + + /** + * 附件地址 + */ + private String fileUrl; + /** + * 备注 + */ + private String remark; + + // ========== 采购入库 ========== + /** + * 采购入库数量 + */ + private BigDecimal inCount; + + // ========== 采购退货(出库)) ========== + /** + * 采购退货数量 + */ + private BigDecimal returnCount; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderItemDO.java new file mode 100644 index 0000000000..aa54d336b4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderItemDO.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.purchase; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 采购订单项 DO + * + * @author 芋道源码 + */ +@TableName("erp_purchase_order_items") +@KeySequence("erp_purchase_order_items_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpPurchaseOrderItemDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 采购订单编号 + * + * 关联 {@link ErpPurchaseOrderDO#getId()} + */ + private Long orderId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位单位 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + + /** + * 产品单位单价,单位:元 + */ + private BigDecimal productPrice; + /** + * 数量 + */ + private BigDecimal count; + /** + * 总价,单位:元 + * + * totalPrice = productPrice * count + */ + private BigDecimal totalPrice; + /** + * 税率,百分比 + */ + private BigDecimal taxPercent; + /** + * 税额,单位:元 + * + * taxPrice = totalPrice * taxPercent + */ + private BigDecimal taxPrice; + + /** + * 备注 + */ + private String remark; + + // ========== 采购入库 ========== + /** + * 采购入库数量 + */ + private BigDecimal inCount; + + // ========== 采购退货(出库)) ========== + /** + * 采购退货数量 + */ + private BigDecimal returnCount; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseReturnDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseReturnDO.java new file mode 100644 index 0000000000..4189e0adc5 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseReturnDO.java @@ -0,0 +1,122 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.purchase; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +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; + +/** + * ERP 采购退货 DO + * + * @author 芋道源码 + */ +@TableName(value = "erp_purchase_return") +@KeySequence("erp_purchase_return_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpPurchaseReturnDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 采购退货单号 + */ + private String no; + /** + * 退货状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 供应商编号 + * + * 关联 {@link ErpSupplierDO#getId()} + */ + private Long supplierId; + /** + * 结算账户编号 + * + * 关联 {@link ErpAccountDO#getId()} + */ + private Long accountId; + /** + * 退货时间 + */ + private LocalDateTime returnTime; + + /** + * 采购订单编号 + * + * 关联 {@link ErpPurchaseOrderDO#getId()} + */ + private Long orderId; + /** + * 采购订单号 + * + * 冗余 {@link ErpPurchaseOrderDO#getNo()} + */ + private String orderNo; + + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 最终合计价格,单位:元 + * + * totalPrice = totalProductPrice + totalTaxPrice - discountPrice + otherPrice + */ + private BigDecimal totalPrice; + /** + * 已退款金额,单位:元 + * + * 目的:和 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO} 结合,记录已支付金额 + */ + private BigDecimal refundPrice; + + /** + * 合计产品价格,单位:元 + */ + private BigDecimal totalProductPrice; + /** + * 合计税额,单位:元 + */ + private BigDecimal totalTaxPrice; + /** + * 优惠率,百分比 + */ + private BigDecimal discountPercent; + /** + * 优惠金额,单位:元 + * + * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent + */ + private BigDecimal discountPrice; + /** + * 其它金额,单位:元 + */ + private BigDecimal otherPrice; + + /** + * 附件地址 + */ + private String fileUrl; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseReturnItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseReturnItemDO.java new file mode 100644 index 0000000000..1e17132775 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseReturnItemDO.java @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.purchase; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 采购退货项 DO + * + * @author 芋道源码 + */ +@TableName("erp_purchase_return_items") +@KeySequence("erp_purchase_return_items_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpPurchaseReturnItemDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 采购退货编号 + * + * 关联 {@link ErpPurchaseReturnDO##getId()} + */ + private Long returnId; + /** + * 采购订单项编号 + * + * 关联 {@link ErpPurchaseOrderItemDO#getId()} + * 目的:方便更新关联的采购订单项的退货数量 + */ + private Long orderItemId; + /** + * 仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位单位 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + + /** + * 产品单位单价,单位:元 + */ + private BigDecimal productPrice; + /** + * 数量 + */ + private BigDecimal count; + /** + * 总价,单位:元 + * + * totalPrice = productPrice * count + */ + private BigDecimal totalPrice; + /** + * 税率,百分比 + */ + private BigDecimal taxPercent; + /** + * 税额,单位:元 + * + * taxPrice = totalPrice * taxPercent + */ + private BigDecimal taxPrice; + + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpSupplierDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpSupplierDO.java new file mode 100644 index 0000000000..6e94c66690 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpSupplierDO.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.purchase; + +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; + +/** + * ERP 供应商 DO + * + * @author 芋道源码 + */ +@TableName("erp_supplier") +@KeySequence("erp_supplier_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpSupplierDO extends BaseDO { + + /** + * 供应商编号 + */ + @TableId + private Long id; + /** + * 供应商名称 + */ + private String name; + /** + * 联系人 + */ + private String contact; + /** + * 手机号码 + */ + private String mobile; + /** + * 联系电话 + */ + private String telephone; + /** + * 电子邮箱 + */ + private String email; + /** + * 传真 + */ + private String fax; + /** + * 备注 + */ + private String remark; + /** + * 开启状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 排序 + */ + private Integer sort; + /** + * 纳税人识别号 + */ + private String taxNo; + /** + * 税率 + */ + private BigDecimal taxPercent; + /** + * 开户行 + */ + private String bankName; + /** + * 开户账号 + */ + private String bankAccount; + /** + * 开户地址 + */ + private String bankAddress; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpCustomerDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpCustomerDO.java new file mode 100644 index 0000000000..7bffcc17cc --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpCustomerDO.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.sale; + +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; + +/** + * ERP 客户 DO + * + * @author 芋道源码 + */ +@TableName("erp_customer") +@KeySequence("erp_customer_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpCustomerDO extends BaseDO { + + /** + * 客户编号 + */ + @TableId + private Long id; + /** + * 客户名称 + */ + private String name; + /** + * 联系人 + */ + private String contact; + /** + * 手机号码 + */ + private String mobile; + /** + * 联系电话 + */ + private String telephone; + /** + * 电子邮箱 + */ + private String email; + /** + * 传真 + */ + private String fax; + /** + * 备注 + */ + private String remark; + /** + * 开启状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 排序 + */ + private Integer sort; + /** + * 纳税人识别号 + */ + private String taxNo; + /** + * 税率 + */ + private BigDecimal taxPercent; + /** + * 开户行 + */ + private String bankName; + /** + * 开户账号 + */ + private String bankAccount; + /** + * 开户地址 + */ + private String bankAddress; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java new file mode 100644 index 0000000000..5cdd4344e5 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java @@ -0,0 +1,121 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.sale; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +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; + +/** + * ERP 销售订单 DO + * + * @author 芋道源码 + */ +@TableName(value = "erp_sale_order") +@KeySequence("erp_sale_order_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpSaleOrderDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 销售订单号 + */ + private String no; + /** + * 销售状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 客户编号 + * + * 关联 {@link ErpCustomerDO#getId()} + */ + private Long customerId; + /** + * 结算账户编号 + * + * 关联 {@link ErpAccountDO#getId()} + */ + private Long accountId; + /** + * 销售员编号 + * + * 关联 AdminUserDO 的 id 字段 + */ + private Long saleUserId; + /** + * 下单时间 + */ + private LocalDateTime orderTime; + + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 最终合计价格,单位:元 + * + * totalPrice = totalProductPrice + totalTaxPrice - discountPrice + */ + private BigDecimal totalPrice; + + /** + * 合计产品价格,单位:元 + */ + private BigDecimal totalProductPrice; + /** + * 合计税额,单位:元 + */ + private BigDecimal totalTaxPrice; + /** + * 优惠率,百分比 + */ + private BigDecimal discountPercent; + /** + * 优惠金额,单位:元 + * + * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent + */ + private BigDecimal discountPrice; + /** + * 定金金额,单位:元 + */ + private BigDecimal depositPrice; + + /** + * 附件地址 + */ + private String fileUrl; + /** + * 备注 + */ + private String remark; + + // ========== 销售出库 ========== + /** + * 销售出库数量 + */ + private BigDecimal outCount; + + // ========== 销售退货(入库)) ========== + /** + * 销售退货数量 + */ + private BigDecimal returnCount; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderItemDO.java new file mode 100644 index 0000000000..4c829765b4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderItemDO.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.sale; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 销售订单项 DO + * + * @author 芋道源码 + */ +@TableName("erp_sale_order_items") +@KeySequence("erp_sale_order_items_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpSaleOrderItemDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 销售订单编号 + * + * 关联 {@link ErpSaleOrderDO#getId()} + */ + private Long orderId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位单位 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + + /** + * 产品单位单价,单位:元 + */ + private BigDecimal productPrice; + /** + * 数量 + */ + private BigDecimal count; + /** + * 总价,单位:元 + * + * totalPrice = productPrice * count + */ + private BigDecimal totalPrice; + /** + * 税率,百分比 + */ + private BigDecimal taxPercent; + /** + * 税额,单位:元 + * + * taxPrice = totalPrice * taxPercent + */ + private BigDecimal taxPrice; + + /** + * 备注 + */ + private String remark; + + // ========== 销售出库 ========== + /** + * 销售出库数量 + */ + private BigDecimal outCount; + + // ========== 销售退货(入库)) ========== + /** + * 销售退货数量 + */ + private BigDecimal returnCount; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutDO.java new file mode 100644 index 0000000000..65b9d941fd --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutDO.java @@ -0,0 +1,128 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.sale; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +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; + +/** + * ERP 销售出库 DO + * + * @author 芋道源码 + */ +@TableName(value = "erp_sale_out") +@KeySequence("erp_sale_out_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpSaleOutDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 销售出库单号 + */ + private String no; + /** + * 出库状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 客户编号 + * + * 关联 {@link ErpCustomerDO#getId()} + */ + private Long customerId; + /** + * 结算账户编号 + * + * 关联 {@link ErpAccountDO#getId()} + */ + private Long accountId; + /** + * 销售员编号 + * + * 关联 AdminUserDO 的 id 字段 + */ + private Long saleUserId; + /** + * 出库时间 + */ + private LocalDateTime outTime; + + /** + * 销售订单编号 + * + * 关联 {@link ErpSaleOrderDO#getId()} + */ + private Long orderId; + /** + * 销售订单号 + * + * 冗余 {@link ErpSaleOrderDO#getNo()} + */ + private String orderNo; + + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 最终合计价格,单位:元 + * + * totalPrice = totalProductPrice + totalTaxPrice - discountPrice + otherPrice + */ + private BigDecimal totalPrice; + /** + * 已收款金额,单位:元 + * + * 目的:和 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO} 结合,记录已收款金额 + */ + private BigDecimal receiptPrice; + + /** + * 合计产品价格,单位:元 + */ + private BigDecimal totalProductPrice; + /** + * 合计税额,单位:元 + */ + private BigDecimal totalTaxPrice; + /** + * 优惠率,百分比 + */ + private BigDecimal discountPercent; + /** + * 优惠金额,单位:元 + * + * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent + */ + private BigDecimal discountPrice; + /** + * 其它金额,单位:元 + */ + private BigDecimal otherPrice; + + /** + * 附件地址 + */ + private String fileUrl; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutItemDO.java new file mode 100644 index 0000000000..b9b406413e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutItemDO.java @@ -0,0 +1,96 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.sale; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 销售出库项 DO + * + * @author 芋道源码 + */ +@TableName("erp_sale_out_items") +@KeySequence("erp_sale_out_items_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpSaleOutItemDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 销售出库编号 + * + * 关联 {@link ErpStockOutDO##getId()} + */ + private Long outId; + /** + * 销售订单项编号 + * + * 关联 {@link ErpSaleOrderItemDO#getId()} + * 目的:方便更新关联的销售订单项的出库数量 + */ + private Long orderItemId; + /** + * 仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位单位 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + + /** + * 产品单位单价,单位:元 + */ + private BigDecimal productPrice; + /** + * 数量 + */ + private BigDecimal count; + /** + * 总价,单位:元 + * + * totalPrice = productPrice * count + */ + private BigDecimal totalPrice; + /** + * 税率,百分比 + */ + private BigDecimal taxPercent; + /** + * 税额,单位:元 + * + * taxPrice = totalPrice * taxPercent + */ + private BigDecimal taxPrice; + + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleReturnDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleReturnDO.java new file mode 100644 index 0000000000..ba41ac91b4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleReturnDO.java @@ -0,0 +1,128 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.sale; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +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; + +/** + * ERP 销售退货 DO + * + * @author 芋道源码 + */ +@TableName(value = "erp_sale_return") +@KeySequence("erp_sale_return_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpSaleReturnDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 销售退货单号 + */ + private String no; + /** + * 退货状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 客户编号 + * + * 关联 {@link ErpCustomerDO#getId()} + */ + private Long customerId; + /** + * 结算账户编号 + * + * 关联 {@link ErpAccountDO#getId()} + */ + private Long accountId; + /** + * 销售员编号 + * + * 关联 AdminUserDO 的 id 字段 + */ + private Long saleUserId; + /** + * 退货时间 + */ + private LocalDateTime returnTime; + + /** + * 销售订单编号 + * + * 关联 {@link ErpSaleOrderDO#getId()} + */ + private Long orderId; + /** + * 销售订单号 + * + * 冗余 {@link ErpSaleOrderDO#getNo()} + */ + private String orderNo; + + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 最终合计价格,单位:元 + * + * totalPrice = totalProductPrice + totalTaxPrice - discountPrice + otherPrice + */ + private BigDecimal totalPrice; + /** + * 已退款金额,单位:元 + * + * 目的:和 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO} 结合,记录已退款金额 + */ + private BigDecimal refundPrice; + + /** + * 合计产品价格,单位:元 + */ + private BigDecimal totalProductPrice; + /** + * 合计税额,单位:元 + */ + private BigDecimal totalTaxPrice; + /** + * 优惠率,百分比 + */ + private BigDecimal discountPercent; + /** + * 优惠金额,单位:元 + * + * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent + */ + private BigDecimal discountPrice; + /** + * 其它金额,单位:元 + */ + private BigDecimal otherPrice; + + /** + * 附件地址 + */ + private String fileUrl; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleReturnItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleReturnItemDO.java new file mode 100644 index 0000000000..8851d157c5 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleReturnItemDO.java @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.sale; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 销售退货项 DO + * + * @author 芋道源码 + */ +@TableName("erp_sale_return_items") +@KeySequence("erp_sale_return_items_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpSaleReturnItemDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 销售退货编号 + * + * 关联 {@link ErpSaleReturnDO##getId()} + */ + private Long returnId; + /** + * 销售订单项编号 + * + * 关联 {@link ErpSaleOrderItemDO#getId()} + * 目的:方便更新关联的销售订单项的退货数量 + */ + private Long orderItemId; + /** + * 仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位单位 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + + /** + * 产品单位单价,单位:元 + */ + private BigDecimal productPrice; + /** + * 数量 + */ + private BigDecimal count; + /** + * 总价,单位:元 + * + * totalPrice = productPrice * count + */ + private BigDecimal totalPrice; + /** + * 税率,百分比 + */ + private BigDecimal taxPercent; + /** + * 税额,单位:元 + * + * taxPrice = totalPrice * taxPercent + */ + private BigDecimal taxPrice; + + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java new file mode 100644 index 0000000000..e9168275ff --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +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; +import java.time.LocalDateTime; + +/** + * ERP 库存盘点单 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_check") +@KeySequence("erp_stock_check_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockCheckDO extends BaseDO { + + /** + * 盘点编号 + */ + @TableId + private Long id; + /** + * 盘点单号 + */ + private String no; + /** + * 盘点时间 + */ + private LocalDateTime checkTime; + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 合计金额,单位:元 + */ + private BigDecimal totalPrice; + /** + * 状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 附件 URL + */ + private String fileUrl; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java new file mode 100644 index 0000000000..c3c4dbf991 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java @@ -0,0 +1,83 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 库存盘点单项 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_check_item") +@KeySequence("erp_stock_check_item_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockCheckItemDO extends BaseDO { + + /** + * 盘点项编号 + */ + @TableId + private Long id; + /** + * 盘点编号 + * + * 关联 {@link ErpStockCheckDO#getId()} + */ + private Long checkId; + /** + * 仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位编号 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + /** + * 产品单价 + */ + private BigDecimal productPrice; + /** + * 账面数量(当前库存) + */ + private BigDecimal stockCount; + /** + * 实际数量(实际库存) + */ + private BigDecimal actualCount; + /** + * 盈亏数量 + * + * count = stockCount - actualCount + */ + private BigDecimal count; + /** + * 合计金额,单位:元 + */ + private BigDecimal totalPrice; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockDO.java new file mode 100644 index 0000000000..558c6c6e5a --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockDO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 产品库存 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock") +@KeySequence("erp_stock_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 库存数量 + */ + private BigDecimal count; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockInDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockInDO.java new file mode 100644 index 0000000000..ee2512ab63 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockInDO.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +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; + +/** + * ERP 其它入库单 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_in") +@KeySequence("erp_stock_in_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockInDO extends BaseDO { + + /** + * 入库编号 + */ + @TableId + private Long id; + /** + * 入库单号 + */ + private String no; + /** + * 供应商编号 + * + * 关联 {@link ErpSupplierDO#getId()} + */ + private Long supplierId; + /** + * 入库时间 + */ + private LocalDateTime inTime; + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 合计金额,单位:元 + */ + private BigDecimal totalPrice; + /** + * 状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 附件 URL + */ + private String fileUrl; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockInItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockInItemDO.java new file mode 100644 index 0000000000..3e3ca9c5ca --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockInItemDO.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 其它入库单项 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_in_item") +@KeySequence("erp_stock_in_item_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockInItemDO extends BaseDO { + + /** + * 入库项编号 + */ + @TableId + private Long id; + /** + * 入库编号 + * + * 关联 {@link ErpStockInDO#getId()} + */ + private Long inId; + /** + * 仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位编号 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + /** + * 产品单价 + */ + private BigDecimal productPrice; + /** + * 产品数量 + */ + private BigDecimal count; + /** + * 合计金额,单位:元 + */ + private BigDecimal totalPrice; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockMoveDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockMoveDO.java new file mode 100644 index 0000000000..682b33104a --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockMoveDO.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +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; +import java.time.LocalDateTime; + +/** + * ERP 库存调拨单 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_move") +@KeySequence("erp_stock_move_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockMoveDO extends BaseDO { + + /** + * 调拨编号 + */ + @TableId + private Long id; + /** + * 调拨单号 + */ + private String no; + /** + * 调拨时间 + */ + private LocalDateTime moveTime; + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 合计金额,单位:元 + */ + private BigDecimal totalPrice; + /** + * 状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 附件 URL + */ + private String fileUrl; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockMoveItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockMoveItemDO.java new file mode 100644 index 0000000000..aee2036707 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockMoveItemDO.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 库存调拨单项 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_move_item") +@KeySequence("erp_stock_move_item_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockMoveItemDO extends BaseDO { + + /** + * 调拨项编号 + */ + @TableId + private Long id; + /** + * 调拨编号 + * + * 关联 {@link ErpStockMoveDO#getId()} + */ + private Long moveId; + /** + * 调出仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long fromWarehouseId; + /** + * 调入仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long toWarehouseId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位编号 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + /** + * 产品单价 + */ + private BigDecimal productPrice; + /** + * 产品数量 + */ + private BigDecimal count; + /** + * 合计金额,单位:元 + */ + private BigDecimal totalPrice; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockOutDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockOutDO.java new file mode 100644 index 0000000000..e0b337adbc --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockOutDO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +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; +import java.time.LocalDateTime; + +/** + * ERP 其它出库单 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_out") +@KeySequence("erp_stock_out_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockOutDO extends BaseDO { + + /** + * 出库编号 + */ + @TableId + private Long id; + /** + * 出库单号 + */ + private String no; + /** + * 客户编号 + * + * TODO 芋艿:待关联 + */ + private Long customerId; + /** + * 出库时间 + */ + private LocalDateTime outTime; + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 合计金额,单位:元 + */ + private BigDecimal totalPrice; + /** + * 状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 附件 URL + */ + private String fileUrl; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockOutItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockOutItemDO.java new file mode 100644 index 0000000000..065c5255a4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockOutItemDO.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 其它出库单项 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_out_item") +@KeySequence("erp_stock_out_item_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockOutItemDO extends BaseDO { + + /** + * 出库项编号 + */ + @TableId + private Long id; + /** + * 出库编号 + * + * 关联 {@link ErpStockOutDO#getId()} + */ + private Long outId; + /** + * 仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位编号 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + /** + * 产品单价 + */ + private BigDecimal productPrice; + /** + * 产品数量 + */ + private BigDecimal count; + /** + * 合计金额,单位:元 + */ + private BigDecimal totalPrice; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockRecordDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockRecordDO.java new file mode 100644 index 0000000000..7bc5e5a01e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockRecordDO.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 产品库存明细 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_record") +@KeySequence("erp_stock_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockRecordDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 出入库数量 + * + * 正数,表示入库;负数,表示出库 + */ + private BigDecimal count; + /** + * 总库存量 + * + * 出入库之后,目前的库存量 + */ + private BigDecimal totalCount; + /** + * 业务类型 + * + * 枚举 {@link ErpStockRecordBizTypeEnum} + */ + private Integer bizType; + /** + * 业务编号 + * + * 例如说:{@link ErpStockInDO#getId()} + */ + private Long bizId; + /** + * 业务项编号 + * + * 例如说:{@link ErpStockInItemDO#getId()} + */ + private Long bizItemId; + /** + * 业务单号 + * + * 例如说:{@link ErpStockInDO#getNo()} + */ + private String bizNo; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpWarehouseDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpWarehouseDO.java new file mode 100644 index 0000000000..4f206173fc --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpWarehouseDO.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +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; + +/** + * ERP 仓库 DO + * + * @author 芋道源码 + */ +@TableName("erp_warehouse") +@KeySequence("erp_warehouse_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpWarehouseDO extends BaseDO { + + /** + * 仓库编号 + */ + @TableId + private Long id; + /** + * 仓库名称 + */ + private String name; + /** + * 仓库地址 + */ + private String address; + /** + * 排序 + */ + private Long sort; + /** + * 备注 + */ + private String remark; + /** + * 负责人 + */ + private String principal; + /** + * 仓储费,单位:元 + */ + private BigDecimal warehousePrice; + /** + * 搬运费,单位:元 + */ + private BigDecimal truckagePrice; + /** + * 开启状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 是否默认 + */ + private Boolean defaultStatus; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpAccountMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpAccountMapper.java new file mode 100644 index 0000000000..2f98147e20 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpAccountMapper.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.finance; + +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.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * ERP 结算账户 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpAccountMapper extends BaseMapperX { + + default PageResult selectPage(ErpAccountPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ErpAccountDO::getName, reqVO.getName()) + .likeIfPresent(ErpAccountDO::getNo, reqVO.getNo()) + .eqIfPresent(ErpAccountDO::getRemark, reqVO.getRemark()) + .orderByDesc(ErpAccountDO::getId)); + } + + default ErpAccountDO selectByDefaultStatus() { + return selectOne(ErpAccountDO::getDefaultStatus, true); + } + + default List selectListByStatus(Integer status) { + return selectList(ErpAccountDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinancePaymentItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinancePaymentItemMapper.java new file mode 100644 index 0000000000..7787e8d709 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinancePaymentItemMapper.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.finance; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * ERP 付款单项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpFinancePaymentItemMapper extends BaseMapperX { + + default List selectListByPaymentId(Long paymentId) { + return selectList(ErpFinancePaymentItemDO::getPaymentId, paymentId); + } + + default List selectListByPaymentIds(Collection paymentIds) { + return selectList(ErpFinancePaymentItemDO::getPaymentId, paymentIds); + } + + default BigDecimal selectPaymentPriceSumByBizIdAndBizType(Long bizId, Integer bizType) { + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("SUM(payment_price) AS paymentPriceSum") + .eq("biz_id", bizId) + .eq("biz_type", bizType)); + // 获得数量 + if (CollUtil.isEmpty(result)) { + return BigDecimal.ZERO; + } + return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "paymentPriceSum", 0D)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinancePaymentMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinancePaymentMapper.java new file mode 100644 index 0000000000..5ad0cccfd2 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinancePaymentMapper.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.finance; + +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.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * ERP 付款单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpFinancePaymentMapper extends BaseMapperX { + + default PageResult selectPage(ErpFinancePaymentPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpFinancePaymentDO::getNo, reqVO.getNo()) + .betweenIfPresent(ErpFinancePaymentDO::getPaymentTime, reqVO.getPaymentTime()) + .eqIfPresent(ErpFinancePaymentDO::getSupplierId, reqVO.getSupplierId()) + .eqIfPresent(ErpFinancePaymentDO::getCreator, reqVO.getCreator()) + .eqIfPresent(ErpFinancePaymentDO::getFinanceUserId, reqVO.getFinanceUserId()) + .eqIfPresent(ErpFinancePaymentDO::getAccountId, reqVO.getAccountId()) + .eqIfPresent(ErpFinancePaymentDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpFinancePaymentDO::getRemark, reqVO.getRemark()) + .orderByDesc(ErpFinancePaymentDO::getId); + if (reqVO.getBizNo() != null) { + query.leftJoin(ErpFinancePaymentItemDO.class, ErpFinancePaymentItemDO::getPaymentId, ErpFinancePaymentDO::getId) + .eq(reqVO.getBizNo() != null, ErpFinancePaymentItemDO::getBizNo, reqVO.getBizNo()) + .groupBy(ErpFinancePaymentDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpFinancePaymentDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpFinancePaymentDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpFinancePaymentDO::getId, id).eq(ErpFinancePaymentDO::getStatus, status)); + } + + default ErpFinancePaymentDO selectByNo(String no) { + return selectOne(ErpFinancePaymentDO::getNo, no); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceReceiptItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceReceiptItemMapper.java new file mode 100644 index 0000000000..cb6082b0e4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceReceiptItemMapper.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.finance; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * ERP 收款单项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpFinanceReceiptItemMapper extends BaseMapperX { + + default List selectListByReceiptId(Long receiptId) { + return selectList(ErpFinanceReceiptItemDO::getReceiptId, receiptId); + } + + default List selectListByReceiptIds(Collection receiptIds) { + return selectList(ErpFinanceReceiptItemDO::getReceiptId, receiptIds); + } + + default BigDecimal selectReceiptPriceSumByBizIdAndBizType(Long bizId, Integer bizType) { + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("SUM(receipt_price) AS receiptPriceSum") + .eq("biz_id", bizId) + .eq("biz_type", bizType)); + // 获得数量 + if (CollUtil.isEmpty(result)) { + return BigDecimal.ZERO; + } + return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "receiptPriceSum", 0D)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceReceiptMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceReceiptMapper.java new file mode 100644 index 0000000000..d895adea41 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceReceiptMapper.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.finance; + +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.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * ERP 收款单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpFinanceReceiptMapper extends BaseMapperX { + + default PageResult selectPage(ErpFinanceReceiptPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpFinanceReceiptDO::getNo, reqVO.getNo()) + .betweenIfPresent(ErpFinanceReceiptDO::getReceiptTime, reqVO.getReceiptTime()) + .eqIfPresent(ErpFinanceReceiptDO::getCustomerId, reqVO.getCustomerId()) + .eqIfPresent(ErpFinanceReceiptDO::getCreator, reqVO.getCreator()) + .eqIfPresent(ErpFinanceReceiptDO::getFinanceUserId, reqVO.getFinanceUserId()) + .eqIfPresent(ErpFinanceReceiptDO::getAccountId, reqVO.getAccountId()) + .eqIfPresent(ErpFinanceReceiptDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpFinanceReceiptDO::getRemark, reqVO.getRemark()) + .orderByDesc(ErpFinanceReceiptDO::getId); + if (reqVO.getBizNo() != null) { + query.leftJoin(ErpFinanceReceiptItemDO.class, ErpFinanceReceiptItemDO::getReceiptId, ErpFinanceReceiptDO::getId) + .eq(reqVO.getBizNo() != null, ErpFinanceReceiptItemDO::getBizNo, reqVO.getBizNo()) + .groupBy(ErpFinanceReceiptDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpFinanceReceiptDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpFinanceReceiptDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpFinanceReceiptDO::getId, id).eq(ErpFinanceReceiptDO::getStatus, status)); + } + + default ErpFinanceReceiptDO selectByNo(String no) { + return selectOne(ErpFinanceReceiptDO::getNo, no); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductCategoryMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductCategoryMapper.java new file mode 100644 index 0000000000..70bbc64299 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductCategoryMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.product; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * ERP 产品分类 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpProductCategoryMapper extends BaseMapperX { + + default List selectList(ErpProductCategoryListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(ErpProductCategoryDO::getName, reqVO.getName()) + .eqIfPresent(ErpProductCategoryDO::getStatus, reqVO.getStatus()) + .orderByDesc(ErpProductCategoryDO::getId)); + } + + default ErpProductCategoryDO selectByParentIdAndName(Long parentId, String name) { + return selectOne(ErpProductCategoryDO::getParentId, parentId, ErpProductCategoryDO::getName, name); + } + + default Long selectCountByParentId(Long parentId) { + return selectCount(ErpProductCategoryDO::getParentId, parentId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductMapper.java new file mode 100644 index 0000000000..ff491fe22f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductMapper.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * ERP 产品 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpProductMapper extends BaseMapperX { + + default PageResult selectPage(ErpProductPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ErpProductDO::getName, reqVO.getName()) + .eqIfPresent(ErpProductDO::getCategoryId, reqVO.getCategoryId()) + .betweenIfPresent(ErpProductDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(ErpProductDO::getId)); + } + + default Long selectCountByCategoryId(Long categoryId) { + return selectCount(ErpProductDO::getCategoryId, categoryId); + } + + default Long selectCountByUnitId(Long unitId) { + return selectCount(ErpProductDO::getUnitId, unitId); + } + + default List selectListByStatus(Integer status) { + return selectList(ErpProductDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductUnitMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductUnitMapper.java new file mode 100644 index 0000000000..45db6a8292 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductUnitMapper.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * ERP 产品单位 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpProductUnitMapper extends BaseMapperX { + + default PageResult selectPage(ErpProductUnitPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ErpProductUnitDO::getName, reqVO.getName()) + .eqIfPresent(ErpProductUnitDO::getStatus, reqVO.getStatus()) + .orderByDesc(ErpProductUnitDO::getId)); + } + + default ErpProductUnitDO selectByName(String name) { + return selectOne(ErpProductUnitDO::getName, name); + } + + default List selectListByStatus(Integer status) { + return selectList(ErpProductUnitDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInItemMapper.java new file mode 100644 index 0000000000..9140f9548f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInItemMapper.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.purchase; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * ERP 采购入库项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpPurchaseInItemMapper extends BaseMapperX { + + default List selectListByInId(Long inId) { + return selectList(ErpPurchaseInItemDO::getInId, inId); + } + + default List selectListByInIds(Collection inIds) { + return selectList(ErpPurchaseInItemDO::getInId, inIds); + } + + default int deleteByInId(Long inId) { + return delete(ErpPurchaseInItemDO::getInId, inId); + } + + /** + * 基于采购订单编号,查询每个采购订单项的入库数量之和 + * + * @param inIds 入库订单项编号数组 + * @return key:采购订单项编号;value:入库数量之和 + */ + default Map selectOrderItemCountSumMapByInIds(Collection inIds) { + if (CollUtil.isEmpty(inIds)) { + return Collections.emptyMap(); + } + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("order_item_id, SUM(count) AS sumCount") + .groupBy("order_item_id") + .in("in_id", inIds)); + // 获得数量 + return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sumCount")); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInMapper.java new file mode 100644 index 0000000000..c155d8cbed --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInMapper.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.purchase; + + +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.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Objects; + +/** + * ERP 采购入库 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpPurchaseInMapper extends BaseMapperX { + + default PageResult selectPage(ErpPurchaseInPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpPurchaseInDO::getNo, reqVO.getNo()) + .eqIfPresent(ErpPurchaseInDO::getSupplierId, reqVO.getSupplierId()) + .betweenIfPresent(ErpPurchaseInDO::getInTime, reqVO.getInTime()) + .eqIfPresent(ErpPurchaseInDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpPurchaseInDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpPurchaseInDO::getCreator, reqVO.getCreator()) + .eqIfPresent(ErpPurchaseInDO::getAccountId, reqVO.getAccountId()) + .likeIfPresent(ErpPurchaseInDO::getOrderNo, reqVO.getOrderNo()) + .orderByDesc(ErpPurchaseInDO::getId); + // 付款状态。为什么需要 t. 的原因,是因为联表查询时,需要指定表名,不然会报字段不存在的错误 + if (Objects.equals(reqVO.getPaymentStatus(), ErpPurchaseInPageReqVO.PAYMENT_STATUS_NONE)) { + query.eq(ErpPurchaseInDO::getPaymentPrice, 0); + } else if (Objects.equals(reqVO.getPaymentStatus(), ErpPurchaseInPageReqVO.PAYMENT_STATUS_PART)) { + query.gt(ErpPurchaseInDO::getPaymentPrice, 0).apply("t.payment_price < t.total_price"); + } else if (Objects.equals(reqVO.getPaymentStatus(), ErpPurchaseInPageReqVO.PAYMENT_STATUS_ALL)) { + query.apply("t.payment_price = t.total_price"); + } + if (Boolean.TRUE.equals(reqVO.getPaymentEnable())) { + query.eq(ErpPurchaseInDO::getStatus, ErpAuditStatus.APPROVE.getStatus()) + .apply("t.payment_price < t.total_price"); + } + if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) { + query.leftJoin(ErpPurchaseInItemDO.class, ErpPurchaseInItemDO::getInId, ErpPurchaseInDO::getId) + .eq(reqVO.getWarehouseId() != null, ErpPurchaseInItemDO::getWarehouseId, reqVO.getWarehouseId()) + .eq(reqVO.getProductId() != null, ErpPurchaseInItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpPurchaseInDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpPurchaseInDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpPurchaseInDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpPurchaseInDO::getId, id).eq(ErpPurchaseInDO::getStatus, status)); + } + + default ErpPurchaseInDO selectByNo(String no) { + return selectOne(ErpPurchaseInDO::getNo, no); + } + + default List selectListByOrderId(Long orderId) { + return selectList(ErpPurchaseInDO::getOrderId, orderId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderItemMapper.java new file mode 100644 index 0000000000..17f1fe290a --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderItemMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.purchase; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 采购订单明项目 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpPurchaseOrderItemMapper extends BaseMapperX { + + default List selectListByOrderId(Long orderId) { + return selectList(ErpPurchaseOrderItemDO::getOrderId, orderId); + } + + default List selectListByOrderIds(Collection orderIds) { + return selectList(ErpPurchaseOrderItemDO::getOrderId, orderIds); + } + + default int deleteByOrderId(Long orderId) { + return delete(ErpPurchaseOrderItemDO::getOrderId, orderId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java new file mode 100644 index 0000000000..01f0303f97 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.purchase; + +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.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Objects; + +/** + * ERP 采购订单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpPurchaseOrderMapper extends BaseMapperX { + + default PageResult selectPage(ErpPurchaseOrderPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpPurchaseOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(ErpPurchaseOrderDO::getSupplierId, reqVO.getSupplierId()) + .betweenIfPresent(ErpPurchaseOrderDO::getOrderTime, reqVO.getOrderTime()) + .eqIfPresent(ErpPurchaseOrderDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpPurchaseOrderDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpPurchaseOrderDO::getCreator, reqVO.getCreator()) + .orderByDesc(ErpPurchaseOrderDO::getId); + // 入库状态。为什么需要 t. 的原因,是因为联表查询时,需要指定表名,不然会报 in_count 错误 + if (Objects.equals(reqVO.getInStatus(), ErpPurchaseOrderPageReqVO.IN_STATUS_NONE)) { + query.eq(ErpPurchaseOrderDO::getInCount, 0); + } else if (Objects.equals(reqVO.getInStatus(), ErpPurchaseOrderPageReqVO.IN_STATUS_PART)) { + query.gt(ErpPurchaseOrderDO::getInCount, 0).apply("t.in_count < t.total_count"); + } else if (Objects.equals(reqVO.getInStatus(), ErpPurchaseOrderPageReqVO.IN_STATUS_ALL)) { + query.apply("t.in_count = t.total_count"); + } + // 退货状态 + if (Objects.equals(reqVO.getReturnStatus(), ErpPurchaseOrderPageReqVO.RETURN_STATUS_NONE)) { + query.eq(ErpPurchaseOrderDO::getReturnCount, 0); + } else if (Objects.equals(reqVO.getReturnStatus(), ErpPurchaseOrderPageReqVO.RETURN_STATUS_PART)) { + query.gt(ErpPurchaseOrderDO::getReturnCount, 0).apply("t.return_count < t.total_count"); + } else if (Objects.equals(reqVO.getReturnStatus(), ErpPurchaseOrderPageReqVO.RETURN_STATUS_ALL)) { + query.apply("t.return_count = t.total_count"); + } + // 可采购入库 + if (Boolean.TRUE.equals(reqVO.getInEnable())) { + query.eq(ErpPurchaseOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus()) + .apply("t.in_count < t.total_count"); + } + // 可采购退货 + if (Boolean.TRUE.equals(reqVO.getReturnEnable())) { + query.eq(ErpPurchaseOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus()) + .apply("t.return_count < t.in_count"); + } + if (reqVO.getProductId() != null) { + query.leftJoin(ErpPurchaseOrderItemDO.class, ErpPurchaseOrderItemDO::getOrderId, ErpPurchaseOrderDO::getId) + .eq(reqVO.getProductId() != null, ErpPurchaseOrderItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpPurchaseOrderDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpPurchaseOrderDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpPurchaseOrderDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpPurchaseOrderDO::getId, id).eq(ErpPurchaseOrderDO::getStatus, status)); + } + + default ErpPurchaseOrderDO selectByNo(String no) { + return selectOne(ErpPurchaseOrderDO::getNo, no); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseReturnItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseReturnItemMapper.java new file mode 100644 index 0000000000..2a8011900c --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseReturnItemMapper.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.purchase; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * ERP 采购退货项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpPurchaseReturnItemMapper extends BaseMapperX { + + default List selectListByReturnId(Long returnId) { + return selectList(ErpPurchaseReturnItemDO::getReturnId, returnId); + } + + default List selectListByReturnIds(Collection returnIds) { + return selectList(ErpPurchaseReturnItemDO::getReturnId, returnIds); + } + + default int deleteByReturnId(Long returnId) { + return delete(ErpPurchaseReturnItemDO::getReturnId, returnId); + } + + /** + * 基于采购订单编号,查询每个采购订单项的退货数量之和 + * + * @param returnIds 入库订单项编号数组 + * @return key:采购订单项编号;value:退货数量之和 + */ + default Map selectOrderItemCountSumMapByReturnIds(Collection returnIds) { + if (CollUtil.isEmpty(returnIds)) { + return Collections.emptyMap(); + } + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("order_item_id, SUM(count) AS sumCount") + .groupBy("order_item_id") + .in("return_id", returnIds)); + // 获得数量 + return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sumCount")); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseReturnMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseReturnMapper.java new file mode 100644 index 0000000000..689a55dfd3 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseReturnMapper.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.purchase; + +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.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Objects; + +/** + * ERP 采购退货 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpPurchaseReturnMapper extends BaseMapperX { + + default PageResult selectPage(ErpPurchaseReturnPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpPurchaseReturnDO::getNo, reqVO.getNo()) + .eqIfPresent(ErpPurchaseReturnDO::getSupplierId, reqVO.getSupplierId()) + .betweenIfPresent(ErpPurchaseReturnDO::getReturnTime, reqVO.getReturnTime()) + .eqIfPresent(ErpPurchaseReturnDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpPurchaseReturnDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpPurchaseReturnDO::getCreator, reqVO.getCreator()) + .eqIfPresent(ErpPurchaseReturnDO::getAccountId, reqVO.getAccountId()) + .likeIfPresent(ErpPurchaseReturnDO::getOrderNo, reqVO.getOrderNo()) + .orderByDesc(ErpPurchaseReturnDO::getId); + // 退款状态。为什么需要 t. 的原因,是因为联表查询时,需要指定表名,不然会报字段不存在的错误 + if (Objects.equals(reqVO.getRefundStatus(), ErpPurchaseReturnPageReqVO.REFUND_STATUS_NONE)) { + query.eq(ErpPurchaseReturnDO::getRefundPrice, 0); + } else if (Objects.equals(reqVO.getRefundStatus(), ErpPurchaseReturnPageReqVO.REFUND_STATUS_PART)) { + query.gt(ErpPurchaseReturnDO::getRefundPrice, 0).apply("t.refund_price < t.total_price"); + } else if (Objects.equals(reqVO.getRefundStatus(), ErpPurchaseReturnPageReqVO.REFUND_STATUS_ALL)) { + query.apply("t.refund_price = t.total_price"); + } + if (Boolean.TRUE.equals(reqVO.getRefundEnable())) { + query.eq(ErpPurchaseInDO::getStatus, ErpAuditStatus.APPROVE.getStatus()) + .apply("t.refund_price < t.total_price"); + } + if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) { + query.leftJoin(ErpPurchaseReturnItemDO.class, ErpPurchaseReturnItemDO::getReturnId, ErpPurchaseReturnDO::getId) + .eq(reqVO.getWarehouseId() != null, ErpPurchaseReturnItemDO::getWarehouseId, reqVO.getWarehouseId()) + .eq(reqVO.getProductId() != null, ErpPurchaseReturnItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpPurchaseReturnDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpPurchaseReturnDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpPurchaseReturnDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpPurchaseReturnDO::getId, id).eq(ErpPurchaseReturnDO::getStatus, status)); + } + + default ErpPurchaseReturnDO selectByNo(String no) { + return selectOne(ErpPurchaseReturnDO::getNo, no); + } + + default List selectListByOrderId(Long orderId) { + return selectList(ErpPurchaseReturnDO::getOrderId, orderId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpSupplierMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpSupplierMapper.java new file mode 100644 index 0000000000..c74f1e6f80 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpSupplierMapper.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.purchase; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * ERP 供应商 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpSupplierMapper extends BaseMapperX { + + default PageResult selectPage(ErpSupplierPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ErpSupplierDO::getName, reqVO.getName()) + .likeIfPresent(ErpSupplierDO::getMobile, reqVO.getMobile()) + .likeIfPresent(ErpSupplierDO::getTelephone, reqVO.getTelephone()) + .orderByDesc(ErpSupplierDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(ErpSupplierDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpCustomerMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpCustomerMapper.java new file mode 100644 index 0000000000..4970f9ad52 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpCustomerMapper.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.sale; + +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.erp.controller.admin.sale.vo.customer.ErpCustomerPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * ERP 客户 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpCustomerMapper extends BaseMapperX { + + default PageResult selectPage(ErpCustomerPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ErpCustomerDO::getName, reqVO.getName()) + .eqIfPresent(ErpCustomerDO::getMobile, reqVO.getMobile()) + .eqIfPresent(ErpCustomerDO::getTelephone, reqVO.getTelephone()) + .orderByDesc(ErpCustomerDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(ErpCustomerDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderItemMapper.java new file mode 100644 index 0000000000..d2825e5635 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderItemMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.sale; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 销售订单明项目 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpSaleOrderItemMapper extends BaseMapperX { + + default List selectListByOrderId(Long orderId) { + return selectList(ErpSaleOrderItemDO::getOrderId, orderId); + } + + default List selectListByOrderIds(Collection orderIds) { + return selectList(ErpSaleOrderItemDO::getOrderId, orderIds); + } + + default int deleteByOrderId(Long orderId) { + return delete(ErpSaleOrderItemDO::getOrderId, orderId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderMapper.java new file mode 100644 index 0000000000..8ed3b6fcd5 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderMapper.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.sale; + + +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.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Objects; + +/** + * ERP 销售订单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpSaleOrderMapper extends BaseMapperX { + + default PageResult selectPage(ErpSaleOrderPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpSaleOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(ErpSaleOrderDO::getCustomerId, reqVO.getCustomerId()) + .betweenIfPresent(ErpSaleOrderDO::getOrderTime, reqVO.getOrderTime()) + .eqIfPresent(ErpSaleOrderDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpSaleOrderDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpSaleOrderDO::getCreator, reqVO.getCreator()) + .orderByDesc(ErpSaleOrderDO::getId); + // 入库状态。为什么需要 t. 的原因,是因为联表查询时,需要指定表名,不然会报 out_count 错误 + if (Objects.equals(reqVO.getOutStatus(), ErpSaleOrderPageReqVO.OUT_STATUS_NONE)) { + query.eq(ErpSaleOrderDO::getOutCount, 0); + } else if (Objects.equals(reqVO.getOutStatus(), ErpSaleOrderPageReqVO.OUT_STATUS_PART)) { + query.gt(ErpSaleOrderDO::getOutCount, 0).apply("t.out_count < t.total_count"); + } else if (Objects.equals(reqVO.getOutStatus(), ErpSaleOrderPageReqVO.OUT_STATUS_ALL)) { + query.apply("t.out_count = t.total_count"); + } + // 退货状态 + if (Objects.equals(reqVO.getReturnStatus(), ErpSaleOrderPageReqVO.RETURN_STATUS_NONE)) { + query.eq(ErpSaleOrderDO::getReturnCount, 0); + } else if (Objects.equals(reqVO.getReturnStatus(), ErpSaleOrderPageReqVO.RETURN_STATUS_PART)) { + query.gt(ErpSaleOrderDO::getReturnCount, 0).apply("t.return_count < t.total_count"); + } else if (Objects.equals(reqVO.getReturnStatus(), ErpSaleOrderPageReqVO.RETURN_STATUS_ALL)) { + query.apply("t.return_count = t.total_count"); + } + // 可销售出库 + if (Boolean.TRUE.equals(reqVO.getOutEnable())) { + query.eq(ErpSaleOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus()) + .apply("t.out_count < t.total_count"); + } + // 可销售退货 + if (Boolean.TRUE.equals(reqVO.getReturnEnable())) { + query.eq(ErpSaleOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus()) + .apply("t.return_count < t.out_count"); + } + if (reqVO.getProductId() != null) { + query.leftJoin(ErpSaleOrderItemDO.class, ErpSaleOrderItemDO::getOrderId, ErpSaleOrderDO::getId) + .eq(reqVO.getProductId() != null, ErpSaleOrderItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpSaleOrderDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpSaleOrderDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpSaleOrderDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpSaleOrderDO::getId, id).eq(ErpSaleOrderDO::getStatus, status)); + } + + default ErpSaleOrderDO selectByNo(String no) { + return selectOne(ErpSaleOrderDO::getNo, no); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOutItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOutItemMapper.java new file mode 100644 index 0000000000..9cd5dede0d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOutItemMapper.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.sale; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * ERP 销售出库项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpSaleOutItemMapper extends BaseMapperX { + + default List selectListByOutId(Long outId) { + return selectList(ErpSaleOutItemDO::getOutId, outId); + } + + default List selectListByOutIds(Collection outIds) { + return selectList(ErpSaleOutItemDO::getOutId, outIds); + } + + default int deleteByOutId(Long outId) { + return delete(ErpSaleOutItemDO::getOutId, outId); + } + + /** + * 基于销售订单编号,查询每个销售订单项的出库数量之和 + * + * @param outIds 出库订单项编号数组 + * @return key:销售订单项编号;value:出库数量之和 + */ + default Map selectOrderItemCountSumMapByOutIds(Collection outIds) { + if (CollUtil.isEmpty(outIds)) { + return Collections.emptyMap(); + } + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("order_item_id, SUM(count) AS sumCount") + .groupBy("order_item_id") + .in("out_id", outIds)); + // 获得数量 + return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sumCount")); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOutMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOutMapper.java new file mode 100644 index 0000000000..128913fcd2 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOutMapper.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.sale; + + +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.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Objects; + +/** + * ERP 销售出库 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpSaleOutMapper extends BaseMapperX { + + default PageResult selectPage(ErpSaleOutPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpSaleOutDO::getNo, reqVO.getNo()) + .eqIfPresent(ErpSaleOutDO::getCustomerId, reqVO.getCustomerId()) + .betweenIfPresent(ErpSaleOutDO::getOutTime, reqVO.getOutTime()) + .eqIfPresent(ErpSaleOutDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpSaleOutDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpSaleOutDO::getCreator, reqVO.getCreator()) + .eqIfPresent(ErpSaleOutDO::getAccountId, reqVO.getAccountId()) + .likeIfPresent(ErpSaleOutDO::getOrderNo, reqVO.getOrderNo()) + .orderByDesc(ErpSaleOutDO::getId); + // 收款状态。为什么需要 t. 的原因,是因为联表查询时,需要指定表名,不然会报字段不存在的错误 + if (Objects.equals(reqVO.getReceiptStatus(), ErpSaleOutPageReqVO.RECEIPT_STATUS_NONE)) { + query.eq(ErpSaleOutDO::getReceiptPrice, 0); + } else if (Objects.equals(reqVO.getReceiptStatus(), ErpSaleOutPageReqVO.RECEIPT_STATUS_PART)) { + query.gt(ErpSaleOutDO::getReceiptPrice, 0).apply("t.receipt_price < t.total_price"); + } else if (Objects.equals(reqVO.getReceiptStatus(), ErpSaleOutPageReqVO.RECEIPT_STATUS_ALL)) { + query.apply("t.receipt_price = t.total_price"); + } + if (Boolean.TRUE.equals(reqVO.getReceiptEnable())) { + query.eq(ErpSaleOutDO::getStatus, ErpAuditStatus.APPROVE.getStatus()) + .apply("t.receipt_price < t.total_price"); + } + if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) { + query.leftJoin(ErpSaleOutItemDO.class, ErpSaleOutItemDO::getOutId, ErpSaleOutDO::getId) + .eq(reqVO.getWarehouseId() != null, ErpSaleOutItemDO::getWarehouseId, reqVO.getWarehouseId()) + .eq(reqVO.getProductId() != null, ErpSaleOutItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpSaleOutDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpSaleOutDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpSaleOutDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpSaleOutDO::getId, id).eq(ErpSaleOutDO::getStatus, status)); + } + + default ErpSaleOutDO selectByNo(String no) { + return selectOne(ErpSaleOutDO::getNo, no); + } + + default List selectListByOrderId(Long orderId) { + return selectList(ErpSaleOutDO::getOrderId, orderId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleReturnItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleReturnItemMapper.java new file mode 100644 index 0000000000..fdc5729643 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleReturnItemMapper.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.sale; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * ERP 销售退货项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpSaleReturnItemMapper extends BaseMapperX { + + default List selectListByReturnId(Long returnId) { + return selectList(ErpSaleReturnItemDO::getReturnId, returnId); + } + + default List selectListByReturnIds(Collection returnIds) { + return selectList(ErpSaleReturnItemDO::getReturnId, returnIds); + } + + default int deleteByReturnId(Long returnId) { + return delete(ErpSaleReturnItemDO::getReturnId, returnId); + } + + /** + * 基于销售订单编号,查询每个销售订单项的退货数量之和 + * + * @param returnIds 出库订单项编号数组 + * @return key:销售订单项编号;value:退货数量之和 + */ + default Map selectOrderItemCountSumMapByReturnIds(Collection returnIds) { + if (CollUtil.isEmpty(returnIds)) { + return Collections.emptyMap(); + } + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("order_item_id, SUM(count) AS sumCount") + .groupBy("order_item_id") + .in("return_id", returnIds)); + // 获得数量 + return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sumCount")); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleReturnMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleReturnMapper.java new file mode 100644 index 0000000000..867b454998 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleReturnMapper.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.sale; + + +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.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Objects; + +/** + * ERP 销售退货 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpSaleReturnMapper extends BaseMapperX { + + default PageResult selectPage(ErpSaleReturnPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpSaleReturnDO::getNo, reqVO.getNo()) + .eqIfPresent(ErpSaleReturnDO::getCustomerId, reqVO.getCustomerId()) + .betweenIfPresent(ErpSaleReturnDO::getReturnTime, reqVO.getReturnTime()) + .eqIfPresent(ErpSaleReturnDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpSaleReturnDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpSaleReturnDO::getCreator, reqVO.getCreator()) + .eqIfPresent(ErpSaleReturnDO::getAccountId, reqVO.getAccountId()) + .likeIfPresent(ErpSaleReturnDO::getOrderNo, reqVO.getOrderNo()) + .orderByDesc(ErpSaleReturnDO::getId); + // 退款状态。为什么需要 t. 的原因,是因为联表查询时,需要指定表名,不然会报字段不存在的错误 + if (Objects.equals(reqVO.getRefundStatus(), ErpSaleReturnPageReqVO.REFUND_STATUS_NONE)) { + query.eq(ErpSaleReturnDO::getRefundPrice, 0); + } else if (Objects.equals(reqVO.getRefundStatus(), ErpSaleReturnPageReqVO.REFUND_STATUS_PART)) { + query.gt(ErpSaleReturnDO::getRefundPrice, 0).apply("t.refund_price < t.total_price"); + } else if (Objects.equals(reqVO.getRefundStatus(), ErpSaleReturnPageReqVO.REFUND_STATUS_ALL)) { + query.apply("t.refund_price = t.total_price"); + } + if (Boolean.TRUE.equals(reqVO.getRefundEnable())) { + query.eq(ErpSaleOutDO::getStatus, ErpAuditStatus.APPROVE.getStatus()) + .apply("t.refund_price < t.total_price"); + } + if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) { + query.leftJoin(ErpSaleReturnItemDO.class, ErpSaleReturnItemDO::getReturnId, ErpSaleReturnDO::getId) + .eq(reqVO.getWarehouseId() != null, ErpSaleReturnItemDO::getWarehouseId, reqVO.getWarehouseId()) + .eq(reqVO.getProductId() != null, ErpSaleReturnItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpSaleReturnDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpSaleReturnDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpSaleReturnDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpSaleReturnDO::getId, id).eq(ErpSaleReturnDO::getStatus, status)); + } + + default ErpSaleReturnDO selectByNo(String no) { + return selectOne(ErpSaleReturnDO::getNo, no); + } + + default List selectListByOrderId(Long orderId) { + return selectList(ErpSaleReturnDO::getOrderId, orderId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckItemMapper.java new file mode 100644 index 0000000000..ae13f9f96d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckItemMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 库存盘点单项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockCheckItemMapper extends BaseMapperX { + + default List selectListByCheckId(Long checkId) { + return selectList(ErpStockCheckItemDO::getCheckId, checkId); + } + + default List selectListByCheckIds(Collection checkIds) { + return selectList(ErpStockCheckItemDO::getCheckId, checkIds); + } + + default int deleteByCheckId(Long checkId) { + return delete(ErpStockCheckItemDO::getCheckId, checkId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckMapper.java new file mode 100644 index 0000000000..dd976df3db --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckMapper.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +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.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * ERP 库存调拨单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockCheckMapper extends BaseMapperX { + + default PageResult selectPage(ErpStockCheckPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpStockCheckDO::getNo, reqVO.getNo()) + .betweenIfPresent(ErpStockCheckDO::getCheckTime, reqVO.getCheckTime()) + .eqIfPresent(ErpStockCheckDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpStockCheckDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpStockCheckDO::getCreator, reqVO.getCreator()) + .orderByDesc(ErpStockCheckDO::getId); + if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) { + query.leftJoin(ErpStockCheckItemDO.class, ErpStockCheckItemDO::getCheckId, ErpStockCheckDO::getId) + .eq(reqVO.getWarehouseId() != null, ErpStockCheckItemDO::getWarehouseId, reqVO.getWarehouseId()) + .eq(reqVO.getProductId() != null, ErpStockCheckItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpStockCheckDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpStockCheckDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpStockCheckDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpStockCheckDO::getId, id).eq(ErpStockCheckDO::getStatus, status)); + } + + default ErpStockCheckDO selectByNo(String no) { + return selectOne(ErpStockCheckDO::getNo, no); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInItemMapper.java new file mode 100644 index 0000000000..2731aa7bbb --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInItemMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 其它入库单项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockInItemMapper extends BaseMapperX { + + default List selectListByInId(Long inId) { + return selectList(ErpStockInItemDO::getInId, inId); + } + + default List selectListByInIds(Collection inIds) { + return selectList(ErpStockInItemDO::getInId, inIds); + } + + default int deleteByInId(Long inId) { + return delete(ErpStockInItemDO::getInId, inId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInMapper.java new file mode 100644 index 0000000000..e815583aca --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInMapper.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +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.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * ERP 其它入库单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockInMapper extends BaseMapperX { + + default PageResult selectPage(ErpStockInPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpStockInDO::getNo, reqVO.getNo()) + .eqIfPresent(ErpStockInDO::getSupplierId, reqVO.getSupplierId()) + .betweenIfPresent(ErpStockInDO::getInTime, reqVO.getInTime()) + .eqIfPresent(ErpStockInDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpStockInDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpStockInDO::getCreator, reqVO.getCreator()) + .orderByDesc(ErpStockInDO::getId); + if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) { + query.leftJoin(ErpStockInItemDO.class, ErpStockInItemDO::getInId, ErpStockInDO::getId) + .eq(reqVO.getWarehouseId() != null, ErpStockInItemDO::getWarehouseId, reqVO.getWarehouseId()) + .eq(reqVO.getProductId() != null, ErpStockInItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpStockInDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpStockInDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpStockInDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpStockInDO::getId, id).eq(ErpStockInDO::getStatus, status)); + } + + default ErpStockInDO selectByNo(String no) { + return selectOne(ErpStockInDO::getNo, no); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java new file mode 100644 index 0000000000..0ebc985979 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +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.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * ERP 产品库存 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockMapper extends BaseMapperX { + + default PageResult selectPage(ErpStockPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(ErpStockDO::getProductId, reqVO.getProductId()) + .eqIfPresent(ErpStockDO::getWarehouseId, reqVO.getWarehouseId()) + .orderByDesc(ErpStockDO::getId)); + } + + default ErpStockDO selectByProductIdAndWarehouseId(Long productId, Long warehouseId) { + return selectOne(ErpStockDO::getProductId, productId, + ErpStockDO::getWarehouseId, warehouseId); + } + + default int updateCountIncrement(Long id, BigDecimal count, boolean negativeEnable) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper() + .eq(ErpStockDO::getId, id); + if (count.compareTo(BigDecimal.ZERO) > 0) { + updateWrapper.setSql("count = count + " + count); + } else if (count.compareTo(BigDecimal.ZERO) < 0) { + if (!negativeEnable) { + updateWrapper.ge(ErpStockDO::getCount, count.abs()); + } + updateWrapper.setSql("count = count - " + count.abs()); + } + return update(null, updateWrapper); + } + + default BigDecimal selectSumByProductId(Long productId) { + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("SUM(count) AS sumCount") + .eq("product_id", productId)); + // 获得数量 + if (CollUtil.isEmpty(result)) { + return BigDecimal.ZERO; + } + return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "sumCount", 0D)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMoveItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMoveItemMapper.java new file mode 100644 index 0000000000..21a267029d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMoveItemMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 库存调拨单项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockMoveItemMapper extends BaseMapperX { + + default List selectListByMoveId(Long moveId) { + return selectList(ErpStockMoveItemDO::getMoveId, moveId); + } + + default List selectListByMoveIds(Collection moveIds) { + return selectList(ErpStockMoveItemDO::getMoveId, moveIds); + } + + default int deleteByMoveId(Long moveId) { + return delete(ErpStockMoveItemDO::getMoveId, moveId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMoveMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMoveMapper.java new file mode 100644 index 0000000000..9a8ce0b64d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMoveMapper.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +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.erp.controller.admin.stock.vo.move.ErpStockMovePageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * ERP 库存调拨单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockMoveMapper extends BaseMapperX { + + default PageResult selectPage(ErpStockMovePageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpStockMoveDO::getNo, reqVO.getNo()) + .betweenIfPresent(ErpStockMoveDO::getMoveTime, reqVO.getMoveTime()) + .eqIfPresent(ErpStockMoveDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpStockMoveDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpStockMoveDO::getCreator, reqVO.getCreator()) + .orderByDesc(ErpStockMoveDO::getId); + if (reqVO.getFromWarehouseId() != null || reqVO.getProductId() != null) { + query.leftJoin(ErpStockMoveItemDO.class, ErpStockMoveItemDO::getMoveId, ErpStockMoveDO::getId) + .eq(reqVO.getFromWarehouseId() != null, ErpStockMoveItemDO::getFromWarehouseId, reqVO.getFromWarehouseId()) + .eq(reqVO.getProductId() != null, ErpStockMoveItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpStockMoveDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpStockMoveDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpStockMoveDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpStockMoveDO::getId, id).eq(ErpStockMoveDO::getStatus, status)); + } + + default ErpStockMoveDO selectByNo(String no) { + return selectOne(ErpStockMoveDO::getNo, no); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockOutItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockOutItemMapper.java new file mode 100644 index 0000000000..3b27cd3dc4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockOutItemMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 其它出库单项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockOutItemMapper extends BaseMapperX { + + default List selectListByOutId(Long outId) { + return selectList(ErpStockOutItemDO::getOutId, outId); + } + + default List selectListByOutIds(Collection outIds) { + return selectList(ErpStockOutItemDO::getOutId, outIds); + } + + default int deleteByOutId(Long outId) { + return delete(ErpStockOutItemDO::getOutId, outId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockOutMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockOutMapper.java new file mode 100644 index 0000000000..a73dd3ccff --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockOutMapper.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +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.erp.controller.admin.stock.vo.out.ErpStockOutPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * ERP 其它出库单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockOutMapper extends BaseMapperX { + + default PageResult selectPage(ErpStockOutPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .likeIfPresent(ErpStockOutDO::getNo, reqVO.getNo()) + .eqIfPresent(ErpStockOutDO::getCustomerId, reqVO.getCustomerId()) + .betweenIfPresent(ErpStockOutDO::getOutTime, reqVO.getOutTime()) + .eqIfPresent(ErpStockOutDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpStockOutDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpStockOutDO::getCreator, reqVO.getCreator()) + .orderByDesc(ErpStockOutDO::getId); + if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) { + query.leftJoin(ErpStockOutItemDO.class, ErpStockOutItemDO::getOutId, ErpStockOutDO::getId) + .eq(reqVO.getWarehouseId() != null, ErpStockOutItemDO::getWarehouseId, reqVO.getWarehouseId()) + .eq(reqVO.getProductId() != null, ErpStockOutItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpStockOutDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpStockOutDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpStockOutDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpStockOutDO::getId, id).eq(ErpStockOutDO::getStatus, status)); + } + + default ErpStockOutDO selectByNo(String no) { + return selectOne(ErpStockOutDO::getNo, no); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockRecordMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockRecordMapper.java new file mode 100644 index 0000000000..bfd8b6751a --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockRecordMapper.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +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.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * ERP 产品库存明细 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockRecordMapper extends BaseMapperX { + + default PageResult selectPage(ErpStockRecordPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(ErpStockRecordDO::getProductId, reqVO.getProductId()) + .eqIfPresent(ErpStockRecordDO::getWarehouseId, reqVO.getWarehouseId()) + .eqIfPresent(ErpStockRecordDO::getBizType, reqVO.getBizType()) + .likeIfPresent(ErpStockRecordDO::getBizNo, reqVO.getBizNo()) + .betweenIfPresent(ErpStockRecordDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(ErpStockRecordDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpWarehouseMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpWarehouseMapper.java new file mode 100644 index 0000000000..0442011f21 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpWarehouseMapper.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * ERP 仓库 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpWarehouseMapper extends BaseMapperX { + + default PageResult selectPage(ErpWarehousePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ErpWarehouseDO::getName, reqVO.getName()) + .eqIfPresent(ErpWarehouseDO::getStatus, reqVO.getStatus()) + .orderByDesc(ErpWarehouseDO::getId)); + } + + default ErpWarehouseDO selectByDefaultStatus() { + return selectOne(ErpWarehouseDO::getDefaultStatus, true); + } + + default List selectListByStatus(Integer status) { + return selectList(ErpWarehouseDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/RedisKeyConstants.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/RedisKeyConstants.java new file mode 100644 index 0000000000..f0ba46807c --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/RedisKeyConstants.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.erp.dal.redis; + +/** + * ERP Redis Key 枚举类 + * + * @author 芋道源码 + */ +public interface RedisKeyConstants { + + /** + * 序号的缓存 + * + * KEY 格式:trade_no:{prefix} + * VALUE 数据格式:编号自增 + */ + String NO = "seq_no:"; + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java new file mode 100644 index 0000000000..98fb8e8ffb --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java @@ -0,0 +1,96 @@ +package cn.iocoder.yudao.module.erp.dal.redis.no; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.iocoder.yudao.module.erp.dal.redis.RedisKeyConstants; +import jakarta.annotation.Resource; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import java.time.Duration; +import java.time.LocalDateTime; + + +/** + * 订单序号的 Redis DAO + * + * @author HUIHUI + */ +@Repository +public class ErpNoRedisDAO { + + /** + * 其它入库 {@link cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO} + */ + public static final String STOCK_IN_NO_PREFIX = "QTRK"; + /** + * 其它出库 {@link cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO} + */ + public static final String STOCK_OUT_NO_PREFIX = "QCKD"; + + /** + * 库存调拨 {@link cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO} + */ + public static final String STOCK_MOVE_NO_PREFIX = "QCDB"; + + /** + * 库存盘点 {@link cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO} + */ + public static final String STOCK_CHECK_NO_PREFIX = "QCPD"; + + /** + * 销售订单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO} + */ + public static final String SALE_ORDER_NO_PREFIX = "XSDD"; + /** + * 销售出库 {@link cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO} + */ + public static final String SALE_OUT_NO_PREFIX = "XSCK"; + /** + * 销售退货 {@link cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO} + */ + public static final String SALE_RETURN_NO_PREFIX = "XSTH"; + + /** + * 采购订单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO} + */ + public static final String PURCHASE_ORDER_NO_PREFIX = "CGDD"; + /** + * 采购入库 {@link cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO} + */ + public static final String PURCHASE_IN_NO_PREFIX = "CGRK"; + /** + * 采购退货 {@link cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO} + */ + public static final String PURCHASE_RETURN_NO_PREFIX = "CGTH"; + + /** + * 付款单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO} + */ + public static final String FINANCE_PAYMENT_NO_PREFIX = "FKD"; + /** + * 收款单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO} + */ + public static final String FINANCE_RECEIPT_NO_PREFIX = "SKD"; + + @Resource + private StringRedisTemplate stringRedisTemplate; + + /** + * 生成序号,使用当前日期,格式为 {PREFIX} + yyyyMMdd + 6 位自增 + * 例如说:QTRK 202109 000001 (没有中间空格) + * + * @param prefix 前缀 + * @return 序号 + */ + public String generate(String prefix) { + // 递增序号 + String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN); + String key = RedisKeyConstants.NO + noPrefix; + Long no = stringRedisTemplate.opsForValue().increment(key); + // 设置过期时间 + stringRedisTemplate.expire(key, Duration.ofDays(1L)); + return noPrefix + String.format("%06d", no); + } + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/package-info.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/package-info.java new file mode 100644 index 0000000000..af7bc123ca --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/package-info.java @@ -0,0 +1,6 @@ +/** + * 属于 erp 模块的 framework 封装 + * + * @author 芋道源码 + */ +package cn.iocoder.yudao.module.erp.framework; diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/web/config/ErpWebConfiguration.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/web/config/ErpWebConfiguration.java new file mode 100644 index 0000000000..f87e04047e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/web/config/ErpWebConfiguration.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.erp.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; + +/** + * erp 模块的 web 组件的 Configuration + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +public class ErpWebConfiguration { + + /** + * erp 模块的 API 分组 + */ + @Bean + public GroupedOpenApi tradeGroupedOpenApi() { + return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("erp"); + } + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/web/package-info.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/web/package-info.java new file mode 100644 index 0000000000..70f70c035c --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/web/package-info.java @@ -0,0 +1,4 @@ +/** + * trade 模块的 web 配置 + */ +package cn.iocoder.yudao.module.erp.framework.web; diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/package-info.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/package-info.java new file mode 100644 index 0000000000..2608a98895 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/package-info.java @@ -0,0 +1,10 @@ +/** + * erp 包下,企业资源管理(Enterprise Resource Planning)。 + * 例如说:采购、销售、库存、财务、产品等等 + * + * 1. Controller URL:以 /erp/ 开头,避免和其它 Module 冲突 + * 2. DataObject 表名:以 erp_ 开头,方便在数据库中区分 + * + * 注意,由于 Erp 模块下,容易和其它模块重名,所以类名都加载 Erp 的前缀~ + */ +package cn.iocoder.yudao.module.erp; diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountService.java new file mode 100644 index 0000000000..502423fb29 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountService.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.erp.service.finance; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +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; + +/** + * ERP 结算账户 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpAccountService { + + /** + * 创建结算账户 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createAccount(@Valid ErpAccountSaveReqVO createReqVO); + + /** + * 更新ERP 结算账户 + * + * @param updateReqVO 更新信息 + */ + void updateAccount(@Valid ErpAccountSaveReqVO updateReqVO); + + /** + * 更新结算账户默认状态 + * + * @param id 编号 + * @param defaultStatus 默认状态 + */ + void updateAccountDefaultStatus(Long id, Boolean defaultStatus); + + /** + * 删除结算账户 + * + * @param id 编号 + */ + void deleteAccount(Long id); + + /** + * 获得结算账户 + * + * @param id 编号 + * @return 结算账户 + */ + ErpAccountDO getAccount(Long id); + + /** + * 校验结算账户 + * + * @param id 编号 + * @return 结算账户 + */ + ErpAccountDO validateAccount(Long id); + + /** + * 获得指定状态的结算账户列表 + * + * @param status 状态 + * @return 结算账户 + */ + List getAccountListByStatus(Integer status); + + /** + * 获得结算账户列表 + * + * @param ids 编号数组 + * @return 结算账户列表 + */ + List getAccountList(Collection ids); + + /** + * 获得结算账户 Map + * + * @param ids 编号数组 + * @return 结算账户 Map + */ + default Map getAccountMap(Collection ids) { + return convertMap(getAccountList(ids), ErpAccountDO::getId); + } + + /** + * 获得结算账户分页 + * + * @param pageReqVO 分页查询 + * @return 结算账户分页 + */ + PageResult getAccountPage(ErpAccountPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImpl.java new file mode 100644 index 0000000000..9a8e858288 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImpl.java @@ -0,0 +1,112 @@ +package cn.iocoder.yudao.module.erp.service.finance; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpAccountMapper; +import jakarta.annotation.Resource; +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.erp.enums.ErrorCodeConstants.*; + +/** + * ERP 结算账户 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpAccountServiceImpl implements ErpAccountService { + + @Resource + private ErpAccountMapper accountMapper; + + @Override + public Long createAccount(ErpAccountSaveReqVO createReqVO) { + // 插入 + ErpAccountDO account = BeanUtils.toBean(createReqVO, ErpAccountDO.class); + accountMapper.insert(account); + // 返回 + return account.getId(); + } + + @Override + public void updateAccount(ErpAccountSaveReqVO updateReqVO) { + // 校验存在 + validateAccountExists(updateReqVO.getId()); + // 更新 + ErpAccountDO updateObj = BeanUtils.toBean(updateReqVO, ErpAccountDO.class); + accountMapper.updateById(updateObj); + } + + @Override + public void updateAccountDefaultStatus(Long id, Boolean defaultStatus) { + // 1. 校验存在 + validateAccountExists(id); + + // 2.1 如果开启,则需要关闭所有其它的默认 + if (defaultStatus) { + ErpAccountDO account = accountMapper.selectByDefaultStatus(); + if (account != null) { + accountMapper.updateById(new ErpAccountDO().setId(account.getId()).setDefaultStatus(false)); + } + } + // 2.2 更新对应的默认状态 + accountMapper.updateById(new ErpAccountDO().setId(id).setDefaultStatus(defaultStatus)); + } + + @Override + public void deleteAccount(Long id) { + // 校验存在 + validateAccountExists(id); + // 删除 + accountMapper.deleteById(id); + } + + private void validateAccountExists(Long id) { + if (accountMapper.selectById(id) == null) { + throw exception(ACCOUNT_NOT_EXISTS); + } + } + + @Override + public ErpAccountDO getAccount(Long id) { + return accountMapper.selectById(id); + } + + @Override + public ErpAccountDO validateAccount(Long id) { + ErpAccountDO account = accountMapper.selectById(id); + if (account == null) { + throw exception(ACCOUNT_NOT_EXISTS); + } + if (CommonStatusEnum.isDisable(account.getStatus())) { + throw exception(ACCOUNT_NOT_ENABLE, account.getName()); + } + return account; + } + + @Override + public List getAccountListByStatus(Integer status) { + return accountMapper.selectListByStatus(status); + } + + @Override + public List getAccountList(Collection ids) { + return accountMapper.selectBatchIds(ids); + } + + @Override + public PageResult getAccountPage(ErpAccountPageReqVO pageReqVO) { + return accountMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java new file mode 100644 index 0000000000..b2f27917de --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.erp.service.finance; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 付款单 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpFinancePaymentService { + + /** + * 创建付款单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createFinancePayment(@Valid ErpFinancePaymentSaveReqVO createReqVO); + + /** + * 更新付款单 + * + * @param updateReqVO 更新信息 + */ + void updateFinancePayment(@Valid ErpFinancePaymentSaveReqVO updateReqVO); + + /** + * 更新付款单的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateFinancePaymentStatus(Long id, Integer status); + + /** + * 删除付款单 + * + * @param ids 编号数组 + */ + void deleteFinancePayment(List ids); + + /** + * 获得付款单 + * + * @param id 编号 + * @return 付款单 + */ + ErpFinancePaymentDO getFinancePayment(Long id); + + /** + * 获得付款单分页 + * + * @param pageReqVO 分页查询 + * @return 付款单分页 + */ + PageResult getFinancePaymentPage(ErpFinancePaymentPageReqVO pageReqVO); + + // ==================== 付款单项 ==================== + + /** + * 获得付款单项列表 + * + * @param paymentId 付款单编号 + * @return 付款单项列表 + */ + List getFinancePaymentItemListByPaymentId(Long paymentId); + + /** + * 获得付款单项 List + * + * @param paymentIds 付款单编号数组 + * @return 付款单项 List + */ + List getFinancePaymentItemListByPaymentIds(Collection paymentIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java new file mode 100644 index 0000000000..ab94dad538 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java @@ -0,0 +1,273 @@ +package cn.iocoder.yudao.module.erp.service.finance; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO; +import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinancePaymentItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinancePaymentMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum; +import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseReturnService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +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.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.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 付款单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpFinancePaymentServiceImpl implements ErpFinancePaymentService { + + @Resource + private ErpFinancePaymentMapper financePaymentMapper; + @Resource + private ErpFinancePaymentItemMapper financePaymentItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpSupplierService supplierService; + @Resource + private ErpAccountService accountService; + @Resource + private ErpPurchaseInService purchaseInService; + @Resource + private ErpPurchaseReturnService purchaseReturnService; + + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createFinancePayment(ErpFinancePaymentSaveReqVO createReqVO) { + // 1.1 校验订单项的有效性 + List paymentItems = validateFinancePaymentItems( + createReqVO.getSupplierId(), createReqVO.getItems()); + // 1.2 校验供应商 + supplierService.validateSupplier(createReqVO.getSupplierId()); + // 1.3 校验结算账户 + if (createReqVO.getAccountId() != null) { + accountService.validateAccount(createReqVO.getAccountId()); + } + // 1.4 校验财务人员 + if (createReqVO.getFinanceUserId() != null) { + adminUserApi.validateUser(createReqVO.getFinanceUserId()); + } + // 1.5 生成付款单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.FINANCE_PAYMENT_NO_PREFIX); + if (financePaymentMapper.selectByNo(no) != null) { + throw exception(FINANCE_PAYMENT_NO_EXISTS); + } + + // 2.1 插入付款单 + ErpFinancePaymentDO payment = BeanUtils.toBean(createReqVO, ErpFinancePaymentDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())); + calculateTotalPrice(payment, paymentItems); + financePaymentMapper.insert(payment); + // 2.2 插入付款单项 + paymentItems.forEach(o -> o.setPaymentId(payment.getId())); + financePaymentItemMapper.insertBatch(paymentItems); + + // 3. 更新采购入库、退货的付款金额情况 + updatePurchasePrice(paymentItems); + return payment.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateFinancePayment(ErpFinancePaymentSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpFinancePaymentDO payment = validateFinancePaymentExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(payment.getStatus())) { + throw exception(FINANCE_PAYMENT_UPDATE_FAIL_APPROVE, payment.getNo()); + } + // 1.2 校验供应商 + supplierService.validateSupplier(updateReqVO.getSupplierId()); + // 1.3 校验结算账户 + if (updateReqVO.getAccountId() != null) { + accountService.validateAccount(updateReqVO.getAccountId()); + } + // 1.4 校验财务人员 + if (updateReqVO.getFinanceUserId() != null) { + adminUserApi.validateUser(updateReqVO.getFinanceUserId()); + } + // 1.5 校验付款单项的有效性 + List paymentItems = validateFinancePaymentItems( + updateReqVO.getSupplierId(), updateReqVO.getItems()); + + // 2.1 更新付款单 + ErpFinancePaymentDO updateObj = BeanUtils.toBean(updateReqVO, ErpFinancePaymentDO.class); + calculateTotalPrice(updateObj, paymentItems); + financePaymentMapper.updateById(updateObj); + // 2.2 更新付款单项 + updateFinancePaymentItemList(updateReqVO.getId(), paymentItems); + } + + private void calculateTotalPrice(ErpFinancePaymentDO payment, List paymentItems) { + payment.setTotalPrice(getSumValue(paymentItems, ErpFinancePaymentItemDO::getPaymentPrice, BigDecimal::add, BigDecimal.ZERO)); + payment.setPaymentPrice(payment.getTotalPrice().subtract(payment.getDiscountPrice())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateFinancePaymentStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpFinancePaymentDO payment = validateFinancePaymentExists(id); + // 1.2 校验状态 + if (payment.getStatus().equals(status)) { + throw exception(approve ? FINANCE_PAYMENT_APPROVE_FAIL : FINANCE_PAYMENT_PROCESS_FAIL); + } + + // 2. 更新状态 + int updateCount = financePaymentMapper.updateByIdAndStatus(id, payment.getStatus(), + new ErpFinancePaymentDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? FINANCE_PAYMENT_APPROVE_FAIL : FINANCE_PAYMENT_PROCESS_FAIL); + } + } + + private List validateFinancePaymentItems( + Long supplierId, + List list) { + return convertList(list, o -> BeanUtils.toBean(o, ErpFinancePaymentItemDO.class, item -> { + if (ObjectUtil.equal(item.getBizType(), ErpBizTypeEnum.PURCHASE_IN.getType())) { + ErpPurchaseInDO purchaseIn = purchaseInService.validatePurchaseIn(item.getBizId()); + Assert.equals(purchaseIn.getSupplierId(), supplierId, "供应商必须相同"); + item.setTotalPrice(purchaseIn.getTotalPrice()).setBizNo(purchaseIn.getNo()); + } else if (ObjectUtil.equal(item.getBizType(), ErpBizTypeEnum.PURCHASE_RETURN.getType())) { + ErpPurchaseReturnDO purchaseReturn = purchaseReturnService.validatePurchaseReturn(item.getBizId()); + Assert.equals(purchaseReturn.getSupplierId(), supplierId, "供应商必须相同"); + item.setTotalPrice(purchaseReturn.getTotalPrice().negate()).setBizNo(purchaseReturn.getNo()); + } else { + throw new IllegalArgumentException("业务类型不正确:" + item.getBizType()); + } + })); + } + + private void updateFinancePaymentItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = financePaymentItemMapper.selectListByPaymentId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setPaymentId(id)); + financePaymentItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + financePaymentItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + financePaymentItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpFinancePaymentItemDO::getId)); + } + + // 第三步,更新采购入库、退货的付款金额情况 + updatePurchasePrice(CollectionUtils.newArrayList(diffList)); + } + + private void updatePurchasePrice(List paymentItems) { + paymentItems.forEach(paymentItem -> { + BigDecimal totalPaymentPrice = financePaymentItemMapper.selectPaymentPriceSumByBizIdAndBizType( + paymentItem.getBizId(), paymentItem.getBizType()); + if (ErpBizTypeEnum.PURCHASE_IN.getType().equals(paymentItem.getBizType())) { + purchaseInService.updatePurchaseInPaymentPrice(paymentItem.getBizId(), totalPaymentPrice); + } else if (ErpBizTypeEnum.PURCHASE_RETURN.getType().equals(paymentItem.getBizType())) { + purchaseReturnService.updatePurchaseReturnRefundPrice(paymentItem.getBizId(), totalPaymentPrice.negate()); + } else { + throw new IllegalArgumentException("业务类型不正确:" + paymentItem.getBizType()); + } + }); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteFinancePayment(List ids) { + // 1. 校验不处于已审批 + List payments = financePaymentMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(payments)) { + return; + } + payments.forEach(payment -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(payment.getStatus())) { + throw exception(FINANCE_PAYMENT_DELETE_FAIL_APPROVE, payment.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + payments.forEach(payment -> { + // 2.1 删除付款单 + financePaymentMapper.deleteById(payment.getId()); + // 2.2 删除付款单项 + List paymentItems = financePaymentItemMapper.selectListByPaymentId(payment.getId()); + financePaymentItemMapper.deleteBatchIds(convertSet(paymentItems, ErpFinancePaymentItemDO::getId)); + + // 2.3 更新采购入库、退货的付款金额情况 + updatePurchasePrice(paymentItems); + }); + } + + private ErpFinancePaymentDO validateFinancePaymentExists(Long id) { + ErpFinancePaymentDO payment = financePaymentMapper.selectById(id); + if (payment == null) { + throw exception(FINANCE_PAYMENT_NOT_EXISTS); + } + return payment; + } + + @Override + public ErpFinancePaymentDO getFinancePayment(Long id) { + return financePaymentMapper.selectById(id); + } + + @Override + public PageResult getFinancePaymentPage(ErpFinancePaymentPageReqVO pageReqVO) { + return financePaymentMapper.selectPage(pageReqVO); + } + + // ==================== 付款单项 ==================== + + @Override + public List getFinancePaymentItemListByPaymentId(Long paymentId) { + return financePaymentItemMapper.selectListByPaymentId(paymentId); + } + + @Override + public List getFinancePaymentItemListByPaymentIds(Collection paymentIds) { + if (CollUtil.isEmpty(paymentIds)) { + return Collections.emptyList(); + } + return financePaymentItemMapper.selectListByPaymentIds(paymentIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinanceReceiptService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinanceReceiptService.java new file mode 100644 index 0000000000..1796481952 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinanceReceiptService.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.erp.service.finance; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 收款单 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpFinanceReceiptService { + + /** + * 创建收款单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createFinanceReceipt(@Valid ErpFinanceReceiptSaveReqVO createReqVO); + + /** + * 更新收款单 + * + * @param updateReqVO 更新信息 + */ + void updateFinanceReceipt(@Valid ErpFinanceReceiptSaveReqVO updateReqVO); + + /** + * 更新收款单的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateFinanceReceiptStatus(Long id, Integer status); + + /** + * 删除收款单 + * + * @param ids 编号数组 + */ + void deleteFinanceReceipt(List ids); + + /** + * 获得收款单 + * + * @param id 编号 + * @return 收款单 + */ + ErpFinanceReceiptDO getFinanceReceipt(Long id); + + /** + * 获得收款单分页 + * + * @param pageReqVO 分页查询 + * @return 收款单分页 + */ + PageResult getFinanceReceiptPage(ErpFinanceReceiptPageReqVO pageReqVO); + + // ==================== 收款单项 ==================== + + /** + * 获得收款单项列表 + * + * @param receiptId 收款单编号 + * @return 收款单项列表 + */ + List getFinanceReceiptItemListByReceiptId(Long receiptId); + + /** + * 获得收款单项 List + * + * @param receiptIds 收款单编号数组 + * @return 收款单项 List + */ + List getFinanceReceiptItemListByReceiptIds(Collection receiptIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinanceReceiptServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinanceReceiptServiceImpl.java new file mode 100644 index 0000000000..42b81d8764 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinanceReceiptServiceImpl.java @@ -0,0 +1,273 @@ +package cn.iocoder.yudao.module.erp.service.finance; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO; +import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinanceReceiptItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinanceReceiptMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum; +import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService; +import cn.iocoder.yudao.module.erp.service.sale.ErpSaleOutService; +import cn.iocoder.yudao.module.erp.service.sale.ErpSaleReturnService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +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.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.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 收款单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpFinanceReceiptServiceImpl implements ErpFinanceReceiptService { + + @Resource + private ErpFinanceReceiptMapper financeReceiptMapper; + @Resource + private ErpFinanceReceiptItemMapper financeReceiptItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpCustomerService customerService; + @Resource + private ErpAccountService accountService; + @Resource + private ErpSaleOutService saleOutService; + @Resource + private ErpSaleReturnService saleReturnService; + + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createFinanceReceipt(ErpFinanceReceiptSaveReqVO createReqVO) { + // 1.1 校验订单项的有效性 + List receiptItems = validateFinanceReceiptItems( + createReqVO.getCustomerId(), createReqVO.getItems()); + // 1.2 校验客户 + customerService.validateCustomer(createReqVO.getCustomerId()); + // 1.3 校验结算账户 + if (createReqVO.getAccountId() != null) { + accountService.validateAccount(createReqVO.getAccountId()); + } + // 1.4 校验财务人员 + if (createReqVO.getFinanceUserId() != null) { + adminUserApi.validateUser(createReqVO.getFinanceUserId()); + } + // 1.5 生成收款单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.FINANCE_RECEIPT_NO_PREFIX); + if (financeReceiptMapper.selectByNo(no) != null) { + throw exception(FINANCE_RECEIPT_NO_EXISTS); + } + + // 2.1 插入收款单 + ErpFinanceReceiptDO receipt = BeanUtils.toBean(createReqVO, ErpFinanceReceiptDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())); + calculateTotalPrice(receipt, receiptItems); + financeReceiptMapper.insert(receipt); + // 2.2 插入收款单项 + receiptItems.forEach(o -> o.setReceiptId(receipt.getId())); + financeReceiptItemMapper.insertBatch(receiptItems); + + // 3. 更新销售出库、退货的收款金额情况 + updateSalePrice(receiptItems); + return receipt.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateFinanceReceipt(ErpFinanceReceiptSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpFinanceReceiptDO receipt = validateFinanceReceiptExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(receipt.getStatus())) { + throw exception(FINANCE_RECEIPT_UPDATE_FAIL_APPROVE, receipt.getNo()); + } + // 1.2 校验客户 + customerService.validateCustomer(updateReqVO.getCustomerId()); + // 1.3 校验结算账户 + if (updateReqVO.getAccountId() != null) { + accountService.validateAccount(updateReqVO.getAccountId()); + } + // 1.4 校验财务人员 + if (updateReqVO.getFinanceUserId() != null) { + adminUserApi.validateUser(updateReqVO.getFinanceUserId()); + } + // 1.5 校验收款单项的有效性 + List receiptItems = validateFinanceReceiptItems( + updateReqVO.getCustomerId(), updateReqVO.getItems()); + + // 2.1 更新收款单 + ErpFinanceReceiptDO updateObj = BeanUtils.toBean(updateReqVO, ErpFinanceReceiptDO.class); + calculateTotalPrice(updateObj, receiptItems); + financeReceiptMapper.updateById(updateObj); + // 2.2 更新收款单项 + updateFinanceReceiptItemList(updateReqVO.getId(), receiptItems); + } + + private void calculateTotalPrice(ErpFinanceReceiptDO receipt, List receiptItems) { + receipt.setTotalPrice(getSumValue(receiptItems, ErpFinanceReceiptItemDO::getReceiptPrice, BigDecimal::add, BigDecimal.ZERO)); + receipt.setReceiptPrice(receipt.getTotalPrice().subtract(receipt.getDiscountPrice())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateFinanceReceiptStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpFinanceReceiptDO receipt = validateFinanceReceiptExists(id); + // 1.2 校验状态 + if (receipt.getStatus().equals(status)) { + throw exception(approve ? FINANCE_RECEIPT_APPROVE_FAIL : FINANCE_RECEIPT_PROCESS_FAIL); + } + + // 2. 更新状态 + int updateCount = financeReceiptMapper.updateByIdAndStatus(id, receipt.getStatus(), + new ErpFinanceReceiptDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? FINANCE_RECEIPT_APPROVE_FAIL : FINANCE_RECEIPT_PROCESS_FAIL); + } + } + + private List validateFinanceReceiptItems( + Long customerId, + List list) { + return convertList(list, o -> BeanUtils.toBean(o, ErpFinanceReceiptItemDO.class, item -> { + if (ObjectUtil.equal(item.getBizType(), ErpBizTypeEnum.SALE_OUT.getType())) { + ErpSaleOutDO saleOut = saleOutService.validateSaleOut(item.getBizId()); + Assert.equals(saleOut.getCustomerId(), customerId, "客户必须相同"); + item.setTotalPrice(saleOut.getTotalPrice()).setBizNo(saleOut.getNo()); + } else if (ObjectUtil.equal(item.getBizType(), ErpBizTypeEnum.SALE_RETURN.getType())) { + ErpSaleReturnDO saleReturn = saleReturnService.validateSaleReturn(item.getBizId()); + Assert.equals(saleReturn.getCustomerId(), customerId, "客户必须相同"); + item.setTotalPrice(saleReturn.getTotalPrice().negate()).setBizNo(saleReturn.getNo()); + } else { + throw new IllegalArgumentException("业务类型不正确:" + item.getBizType()); + } + })); + } + + private void updateFinanceReceiptItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = financeReceiptItemMapper.selectListByReceiptId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setReceiptId(id)); + financeReceiptItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + financeReceiptItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + financeReceiptItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpFinanceReceiptItemDO::getId)); + } + + // 第三步,更新销售出库、退货的收款金额情况 + updateSalePrice(CollectionUtils.newArrayList(diffList)); + } + + private void updateSalePrice(List receiptItems) { + receiptItems.forEach(receiptItem -> { + BigDecimal totalReceiptPrice = financeReceiptItemMapper.selectReceiptPriceSumByBizIdAndBizType( + receiptItem.getBizId(), receiptItem.getBizType()); + if (ErpBizTypeEnum.SALE_OUT.getType().equals(receiptItem.getBizType())) { + saleOutService.updateSaleInReceiptPrice(receiptItem.getBizId(), totalReceiptPrice); + } else if (ErpBizTypeEnum.SALE_RETURN.getType().equals(receiptItem.getBizType())) { + saleReturnService.updateSaleReturnRefundPrice(receiptItem.getBizId(), totalReceiptPrice.negate()); + } else { + throw new IllegalArgumentException("业务类型不正确:" + receiptItem.getBizType()); + } + }); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteFinanceReceipt(List ids) { + // 1. 校验不处于已审批 + List receipts = financeReceiptMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(receipts)) { + return; + } + receipts.forEach(receipt -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(receipt.getStatus())) { + throw exception(FINANCE_RECEIPT_DELETE_FAIL_APPROVE, receipt.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + receipts.forEach(receipt -> { + // 2.1 删除收款单 + financeReceiptMapper.deleteById(receipt.getId()); + // 2.2 删除收款单项 + List receiptItems = financeReceiptItemMapper.selectListByReceiptId(receipt.getId()); + financeReceiptItemMapper.deleteBatchIds(convertSet(receiptItems, ErpFinanceReceiptItemDO::getId)); + + // 2.3 更新销售出库、退货的收款金额情况 + updateSalePrice(receiptItems); + }); + } + + private ErpFinanceReceiptDO validateFinanceReceiptExists(Long id) { + ErpFinanceReceiptDO receipt = financeReceiptMapper.selectById(id); + if (receipt == null) { + throw exception(FINANCE_RECEIPT_NOT_EXISTS); + } + return receipt; + } + + @Override + public ErpFinanceReceiptDO getFinanceReceipt(Long id) { + return financeReceiptMapper.selectById(id); + } + + @Override + public PageResult getFinanceReceiptPage(ErpFinanceReceiptPageReqVO pageReqVO) { + return financeReceiptMapper.selectPage(pageReqVO); + } + + // ==================== 收款单项 ==================== + + @Override + public List getFinanceReceiptItemListByReceiptId(Long receiptId) { + return financeReceiptItemMapper.selectListByReceiptId(receiptId); + } + + @Override + public List getFinanceReceiptItemListByReceiptIds(Collection receiptIds) { + if (CollUtil.isEmpty(receiptIds)) { + return Collections.emptyList(); + } + return financeReceiptItemMapper.selectListByReceiptIds(receiptIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryService.java new file mode 100644 index 0000000000..2b11faf618 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryService.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.erp.service.product; + +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategorySaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO; +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; + +/** + * ERP 产品分类 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpProductCategoryService { + + /** + * 创建产品分类 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProductCategory(@Valid ErpProductCategorySaveReqVO createReqVO); + + /** + * 更新产品分类 + * + * @param updateReqVO 更新信息 + */ + void updateProductCategory(@Valid ErpProductCategorySaveReqVO updateReqVO); + + /** + * 删除产品分类 + * + * @param id 编号 + */ + void deleteProductCategory(Long id); + + /** + * 获得产品分类 + * + * @param id 编号 + * @return 产品分类 + */ + ErpProductCategoryDO getProductCategory(Long id); + + /** + * 获得产品分类列表 + * + * @param listReqVO 查询条件 + * @return 产品分类列表 + */ + List getProductCategoryList(ErpProductCategoryListReqVO listReqVO); + + /** + * 获得产品分类列表 + * + * @param ids 编号数组 + * @return 产品分类列表 + */ + List getProductCategoryList(Collection ids); + + /** + * 获得产品分类 Map + * + * @param ids 编号数组 + * @return 产品分类 Map + */ + default Map getProductCategoryMap(Collection ids) { + return convertMap(getProductCategoryList(ids), ErpProductCategoryDO::getId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryServiceImpl.java new file mode 100644 index 0000000000..bad19a0ee6 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryServiceImpl.java @@ -0,0 +1,149 @@ +package cn.iocoder.yudao.module.erp.service.product; + +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategorySaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO; +import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductCategoryMapper; +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 java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +/** + * ERP 产品分类 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpProductCategoryServiceImpl implements ErpProductCategoryService { + + @Resource + private ErpProductCategoryMapper productCategoryMapper; + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private ErpProductService productService; + + @Override + public Long createProductCategory(ErpProductCategorySaveReqVO createReqVO) { + // 校验父分类编号的有效性 + validateParentProductCategory(null, createReqVO.getParentId()); + // 校验分类名称的唯一性 + validateProductCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName()); + + // 插入 + ErpProductCategoryDO category = BeanUtils.toBean(createReqVO, ErpProductCategoryDO.class); + productCategoryMapper.insert(category); + // 返回 + return category.getId(); + } + + @Override + public void updateProductCategory(ErpProductCategorySaveReqVO updateReqVO) { + // 校验存在 + validateProductCategoryExists(updateReqVO.getId()); + // 校验父分类编号的有效性 + validateParentProductCategory(updateReqVO.getId(), updateReqVO.getParentId()); + // 校验分类名称的唯一性 + validateProductCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); + + // 更新 + ErpProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, ErpProductCategoryDO.class); + productCategoryMapper.updateById(updateObj); + } + + @Override + public void deleteProductCategory(Long id) { + // 1.1 校验存在 + validateProductCategoryExists(id); + // 1.2 校验是否有子产品分类 + if (productCategoryMapper.selectCountByParentId(id) > 0) { + throw exception(PRODUCT_CATEGORY_EXITS_CHILDREN); + } + // 1.3 校验是否有产品 + if (productService.getProductCountByCategoryId(id) > 0) { + throw exception(PRODUCT_CATEGORY_EXITS_PRODUCT); + } + // 2. 删除 + productCategoryMapper.deleteById(id); + } + + private void validateProductCategoryExists(Long id) { + if (productCategoryMapper.selectById(id) == null) { + throw exception(PRODUCT_CATEGORY_NOT_EXISTS); + } + } + + private void validateParentProductCategory(Long id, Long parentId) { + if (parentId == null || ErpProductCategoryDO.PARENT_ID_ROOT.equals(parentId)) { + return; + } + // 1. 不能设置自己为父产品分类 + if (Objects.equals(id, parentId)) { + throw exception(PRODUCT_CATEGORY_PARENT_ERROR); + } + // 2. 父产品分类不存在 + ErpProductCategoryDO parentCategory = productCategoryMapper.selectById(parentId); + if (parentCategory == null) { + throw exception(PRODUCT_CATEGORY_PARENT_NOT_EXITS); + } + // 3. 递归校验父产品分类,如果父产品分类是自己的子产品分类,则报错,避免形成环路 + if (id == null) { // id 为空,说明新增,不需要考虑环路 + return; + } + for (int i = 0; i < Short.MAX_VALUE; i++) { + // 3.1 校验环路 + parentId = parentCategory.getParentId(); + if (Objects.equals(id, parentId)) { + throw exception(PRODUCT_CATEGORY_PARENT_IS_CHILD); + } + // 3.2 继续递归下一级父产品分类 + if (parentId == null || ErpProductCategoryDO.PARENT_ID_ROOT.equals(parentId)) { + break; + } + parentCategory = productCategoryMapper.selectById(parentId); + if (parentCategory == null) { + break; + } + } + } + + private void validateProductCategoryNameUnique(Long id, Long parentId, String name) { + ErpProductCategoryDO productCategory = productCategoryMapper.selectByParentIdAndName(parentId, name); + if (productCategory == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的产品分类 + if (id == null) { + throw exception(PRODUCT_CATEGORY_NAME_DUPLICATE); + } + if (!Objects.equals(productCategory.getId(), id)) { + throw exception(PRODUCT_CATEGORY_NAME_DUPLICATE); + } + } + + @Override + public ErpProductCategoryDO getProductCategory(Long id) { + return productCategoryMapper.selectById(id); + } + + @Override + public List getProductCategoryList(ErpProductCategoryListReqVO listReqVO) { + return productCategoryMapper.selectList(listReqVO); + } + + @Override + public List getProductCategoryList(Collection ids) { + return productCategoryMapper.selectBatchIds(ids); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductService.java new file mode 100644 index 0000000000..778c7f29a4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductService.java @@ -0,0 +1,111 @@ +package cn.iocoder.yudao.module.erp.service.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +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; + +/** + * ERP 产品 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpProductService { + + /** + * 创建产品 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProduct(@Valid ProductSaveReqVO createReqVO); + + /** + * 更新产品 + * + * @param updateReqVO 更新信息 + */ + void updateProduct(@Valid ProductSaveReqVO updateReqVO); + + /** + * 删除产品 + * + * @param id 编号 + */ + void deleteProduct(Long id); + + /** + * 校验产品们的有效性 + * + * @param ids 编号数组 + * @return 产品列表 + */ + List validProductList(Collection ids); + + /** + * 获得产品 + * + * @param id 编号 + * @return 产品 + */ + ErpProductDO getProduct(Long id); + + /** + * 获得指定状态的产品 VO 列表 + * + * @param status 状态 + * @return 产品 VO 列表 + */ + List getProductVOListByStatus(Integer status); + + /** + * 获得产品 VO 列表 + * + * @param ids 编号数组 + * @return 产品 VO 列表 + */ + List getProductVOList(Collection ids); + + /** + * 获得产品 VO Map + * + * @param ids 编号数组 + * @return 产品 VO Map + */ + default Map getProductVOMap(Collection ids) { + return convertMap(getProductVOList(ids), ErpProductRespVO::getId); + } + + /** + * 获得产品 VO 分页 + * + * @param pageReqVO 分页查询 + * @return 产品分页 + */ + PageResult getProductVOPage(ErpProductPageReqVO pageReqVO); + + /** + * 基于产品分类编号,获得产品数量 + * + * @param categoryId 产品分类编号 + * @return 产品数量 + */ + Long getProductCountByCategoryId(Long categoryId); + + /** + * 基于产品单位编号,获得产品数量 + * + * @param unitId 产品单位编号 + * @return 产品数量 + */ + Long getProductCountByUnitId(Long unitId); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java new file mode 100644 index 0000000000..cbe689c6ac --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java @@ -0,0 +1,147 @@ +package cn.iocoder.yudao.module.erp.service.product; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.erp.controller.admin.product.vo.product.ErpProductPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO; +import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +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.erp.enums.ErrorCodeConstants.*; + +/** + * ERP 产品 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpProductServiceImpl implements ErpProductService { + + @Resource + private ErpProductMapper productMapper; + + @Resource + private ErpProductCategoryService productCategoryService; + @Resource + private ErpProductUnitService productUnitService; + + @Override + public Long createProduct(ProductSaveReqVO createReqVO) { + // TODO 芋艿:校验分类 + // 插入 + ErpProductDO product = BeanUtils.toBean(createReqVO, ErpProductDO.class); + productMapper.insert(product); + // 返回 + return product.getId(); + } + + @Override + public void updateProduct(ProductSaveReqVO updateReqVO) { + // TODO 芋艿:校验分类 + // 校验存在 + validateProductExists(updateReqVO.getId()); + // 更新 + ErpProductDO updateObj = BeanUtils.toBean(updateReqVO, ErpProductDO.class); + productMapper.updateById(updateObj); + } + + @Override + public void deleteProduct(Long id) { + // 校验存在 + validateProductExists(id); + // 删除 + productMapper.deleteById(id); + } + + @Override + public List validProductList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + List list = productMapper.selectBatchIds(ids); + Map productMap = convertMap(list, ErpProductDO::getId); + for (Long id : ids) { + ErpProductDO product = productMap.get(id); + if (productMap.get(id) == null) { + throw exception(PRODUCT_NOT_EXISTS); + } + if (CommonStatusEnum.isDisable(product.getStatus())) { + throw exception(PRODUCT_NOT_ENABLE, product.getName()); + } + } + return list; + } + + private void validateProductExists(Long id) { + if (productMapper.selectById(id) == null) { + throw exception(PRODUCT_NOT_EXISTS); + } + } + + @Override + public ErpProductDO getProduct(Long id) { + return productMapper.selectById(id); + } + + @Override + public List getProductVOListByStatus(Integer status) { + List list = productMapper.selectListByStatus(status); + return buildProductVOList(list); + } + + @Override + public List getProductVOList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + List list = productMapper.selectBatchIds(ids); + return buildProductVOList(list); + } + + @Override + public PageResult getProductVOPage(ErpProductPageReqVO pageReqVO) { + PageResult pageResult = productMapper.selectPage(pageReqVO); + return new PageResult<>(buildProductVOList(pageResult.getList()), pageResult.getTotal()); + } + + private List buildProductVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + Map categoryMap = productCategoryService.getProductCategoryMap( + convertSet(list, ErpProductDO::getCategoryId)); + Map unitMap = productUnitService.getProductUnitMap( + convertSet(list, ErpProductDO::getUnitId)); + return BeanUtils.toBean(list, ErpProductRespVO.class, product -> { + MapUtils.findAndThen(categoryMap, product.getCategoryId(), + category -> product.setCategoryName(category.getName())); + MapUtils.findAndThen(unitMap, product.getUnitId(), + unit -> product.setUnitName(unit.getName())); + }); + } + + @Override + public Long getProductCountByCategoryId(Long categoryId) { + return productMapper.selectCountByCategoryId(categoryId); + } + + @Override + public Long getProductCountByUnitId(Long unitId) { + return productMapper.selectCountByUnitId(unitId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitService.java new file mode 100644 index 0000000000..340278ab9b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitService.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.erp.service.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO; +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; + +/** + * ERP 产品单位 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpProductUnitService { + + /** + * 创建产品单位 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProductUnit(@Valid ErpProductUnitSaveReqVO createReqVO); + + /** + * 更新产品单位 + * + * @param updateReqVO 更新信息 + */ + void updateProductUnit(@Valid ErpProductUnitSaveReqVO updateReqVO); + + /** + * 删除产品单位 + * + * @param id 编号 + */ + void deleteProductUnit(Long id); + + /** + * 获得产品单位 + * + * @param id 编号 + * @return 产品单位 + */ + ErpProductUnitDO getProductUnit(Long id); + + /** + * 获得产品单位分页 + * + * @param pageReqVO 分页查询 + * @return 产品单位分页 + */ + PageResult getProductUnitPage(ErpProductUnitPageReqVO pageReqVO); + + /** + * 获得指定状态的产品单位列表 + * + * @param status 状态 + * @return 产品单位列表 + */ + List getProductUnitListByStatus(Integer status); + + /** + * 获得产品单位列表 + * + * @param ids 编号数组 + * @return 产品单位列表 + */ + List getProductUnitList(Collection ids); + + /** + * 获得产品单位 Map + * + * @param ids 编号数组 + * @return 产品单位 Map + */ + default Map getProductUnitMap(Collection ids) { + return convertMap(getProductUnitList(ids), ErpProductUnitDO::getId); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitServiceImpl.java new file mode 100644 index 0000000000..89ba6b08e3 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitServiceImpl.java @@ -0,0 +1,111 @@ +package cn.iocoder.yudao.module.erp.service.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO; +import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductUnitMapper; +import com.google.common.annotations.VisibleForTesting; +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.erp.enums.ErrorCodeConstants.*; + +/** + * ERP 产品单位 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpProductUnitServiceImpl implements ErpProductUnitService { + + @Resource + private ErpProductUnitMapper productUnitMapper; + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private ErpProductService productService; + + @Override + public Long createProductUnit(ErpProductUnitSaveReqVO createReqVO) { + // 1. 校验名字唯一 + validateProductUnitNameUnique(null, createReqVO.getName()); + // 2. 插入 + ErpProductUnitDO unit = BeanUtils.toBean(createReqVO, ErpProductUnitDO.class); + productUnitMapper.insert(unit); + return unit.getId(); + } + + @Override + public void updateProductUnit(ErpProductUnitSaveReqVO updateReqVO) { + // 1.1 校验存在 + validateProductUnitExists(updateReqVO.getId()); + // 1.2 校验名字唯一 + validateProductUnitNameUnique(updateReqVO.getId(), updateReqVO.getName()); + // 2. 更新 + ErpProductUnitDO updateObj = BeanUtils.toBean(updateReqVO, ErpProductUnitDO.class); + productUnitMapper.updateById(updateObj); + } + + @VisibleForTesting + void validateProductUnitNameUnique(Long id, String name) { + ErpProductUnitDO unit = productUnitMapper.selectByName(name); + if (unit == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 + if (id == null) { + throw exception(PRODUCT_UNIT_NAME_DUPLICATE); + } + if (!unit.getId().equals(id)) { + throw exception(PRODUCT_UNIT_NAME_DUPLICATE); + } + } + + @Override + public void deleteProductUnit(Long id) { + // 1.1 校验存在 + validateProductUnitExists(id); + // 1.2 校验产品是否使用 + if (productService.getProductCountByUnitId(id) > 0) { + throw exception(PRODUCT_UNIT_EXITS_PRODUCT); + } + // 2. 删除 + productUnitMapper.deleteById(id); + } + + private void validateProductUnitExists(Long id) { + if (productUnitMapper.selectById(id) == null) { + throw exception(PRODUCT_UNIT_NOT_EXISTS); + } + } + + @Override + public ErpProductUnitDO getProductUnit(Long id) { + return productUnitMapper.selectById(id); + } + + @Override + public PageResult getProductUnitPage(ErpProductUnitPageReqVO pageReqVO) { + return productUnitMapper.selectPage(pageReqVO); + } + + @Override + public List getProductUnitListByStatus(Integer status) { + return productUnitMapper.selectListByStatus(status); + } + + @Override + public List getProductUnitList(Collection ids) { + return productUnitMapper.selectBatchIds(ids); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInService.java new file mode 100644 index 0000000000..37aa452cdc --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInService.java @@ -0,0 +1,101 @@ +package cn.iocoder.yudao.module.erp.service.purchase; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO; +import jakarta.validation.Valid; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; + +/** + * ERP 采购入库 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpPurchaseInService { + + /** + * 创建采购入库 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createPurchaseIn(@Valid ErpPurchaseInSaveReqVO createReqVO); + + /** + * 更新采购入库 + * + * @param updateReqVO 更新信息 + */ + void updatePurchaseIn(@Valid ErpPurchaseInSaveReqVO updateReqVO); + + /** + * 更新采购入库的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updatePurchaseInStatus(Long id, Integer status); + + /** + * 更新采购入库的付款金额 + * + * @param id 编号 + * @param paymentPrice 付款金额 + */ + void updatePurchaseInPaymentPrice(Long id, BigDecimal paymentPrice); + + /** + * 删除采购入库 + * + * @param ids 编号数组 + */ + void deletePurchaseIn(List ids); + + /** + * 获得采购入库 + * + * @param id 编号 + * @return 采购入库 + */ + ErpPurchaseInDO getPurchaseIn(Long id); + + /** + * 校验采购入库,已经审核通过 + * + * @param id 编号 + * @return 采购入库 + */ + ErpPurchaseInDO validatePurchaseIn(Long id); + + /** + * 获得采购入库分页 + * + * @param pageReqVO 分页查询 + * @return 采购入库分页 + */ + PageResult getPurchaseInPage(ErpPurchaseInPageReqVO pageReqVO); + + // ==================== 采购入库项 ==================== + + /** + * 获得采购入库项列表 + * + * @param inId 采购入库编号 + * @return 采购入库项列表 + */ + List getPurchaseInItemListByInId(Long inId); + + /** + * 获得采购入库项 List + * + * @param inIds 采购入库编号数组 + * @return 采购入库项 List + */ + List getPurchaseInItemListByInIds(Collection inIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInServiceImpl.java new file mode 100644 index 0000000000..5d6ad6230c --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInServiceImpl.java @@ -0,0 +1,308 @@ +package cn.iocoder.yudao.module.erp.service.purchase; + +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.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; +import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseInItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseInMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService; +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +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.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +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.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 采购入库 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpPurchaseInServiceImpl implements ErpPurchaseInService { + + @Resource + private ErpPurchaseInMapper purchaseInMapper; + @Resource + private ErpPurchaseInItemMapper purchaseInItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private ErpPurchaseOrderService purchaseOrderService; + @Resource + private ErpAccountService accountService; + @Resource + private ErpStockRecordService stockRecordService; + + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createPurchaseIn(ErpPurchaseInSaveReqVO createReqVO) { + // 1.1 校验采购订单已审核 + ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(createReqVO.getOrderId()); + // 1.2 校验入库项的有效性 + List purchaseInItems = validatePurchaseInItems(createReqVO.getItems()); + // 1.3 校验结算账户 + accountService.validateAccount(createReqVO.getAccountId()); + // 1.4 生成入库单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_IN_NO_PREFIX); + if (purchaseInMapper.selectByNo(no) != null) { + throw exception(PURCHASE_IN_NO_EXISTS); + } + + // 2.1 插入入库 + ErpPurchaseInDO purchaseIn = BeanUtils.toBean(createReqVO, ErpPurchaseInDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())) + .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId()); + calculateTotalPrice(purchaseIn, purchaseInItems); + purchaseInMapper.insert(purchaseIn); + // 2.2 插入入库项 + purchaseInItems.forEach(o -> o.setInId(purchaseIn.getId())); + purchaseInItemMapper.insertBatch(purchaseInItems); + + // 3. 更新采购订单的入库数量 + updatePurchaseOrderInCount(createReqVO.getOrderId()); + return purchaseIn.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePurchaseIn(ErpPurchaseInSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpPurchaseInDO purchaseIn = validatePurchaseInExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseIn.getStatus())) { + throw exception(PURCHASE_IN_UPDATE_FAIL_APPROVE, purchaseIn.getNo()); + } + // 1.2 校验采购订单已审核 + ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(updateReqVO.getOrderId()); + // 1.3 校验结算账户 + accountService.validateAccount(updateReqVO.getAccountId()); + // 1.4 校验订单项的有效性 + List purchaseInItems = validatePurchaseInItems(updateReqVO.getItems()); + + // 2.1 更新入库 + ErpPurchaseInDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseInDO.class) + .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId()); + calculateTotalPrice(updateObj, purchaseInItems); + purchaseInMapper.updateById(updateObj); + // 2.2 更新入库项 + updatePurchaseInItemList(updateReqVO.getId(), purchaseInItems); + + // 3.1 更新采购订单的入库数量 + updatePurchaseOrderInCount(updateObj.getOrderId()); + // 3.2 注意:如果采购订单编号变更了,需要更新“老”采购订单的入库数量 + if (ObjectUtil.notEqual(purchaseIn.getOrderId(), updateObj.getOrderId())) { + updatePurchaseOrderInCount(purchaseIn.getOrderId()); + } + } + + private void calculateTotalPrice(ErpPurchaseInDO purchaseIn, List purchaseInItems) { + purchaseIn.setTotalCount(getSumValue(purchaseInItems, ErpPurchaseInItemDO::getCount, BigDecimal::add)); + purchaseIn.setTotalProductPrice(getSumValue(purchaseInItems, ErpPurchaseInItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); + purchaseIn.setTotalTaxPrice(getSumValue(purchaseInItems, ErpPurchaseInItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO)); + purchaseIn.setTotalPrice(purchaseIn.getTotalProductPrice().add(purchaseIn.getTotalTaxPrice())); + // 计算优惠价格 + if (purchaseIn.getDiscountPercent() == null) { + purchaseIn.setDiscountPercent(BigDecimal.ZERO); + } + purchaseIn.setDiscountPrice(MoneyUtils.priceMultiplyPercent(purchaseIn.getTotalPrice(), purchaseIn.getDiscountPercent())); + purchaseIn.setTotalPrice(purchaseIn.getTotalPrice().subtract(purchaseIn.getDiscountPrice().add(purchaseIn.getOtherPrice()))); + } + + private void updatePurchaseOrderInCount(Long orderId) { + // 1.1 查询采购订单对应的采购入库单列表 + List purchaseIns = purchaseInMapper.selectListByOrderId(orderId); + // 1.2 查询对应的采购订单项的入库数量 + Map returnCountMap = purchaseInItemMapper.selectOrderItemCountSumMapByInIds( + convertList(purchaseIns, ErpPurchaseInDO::getId)); + // 2. 更新采购订单的入库数量 + purchaseOrderService.updatePurchaseOrderInCount(orderId, returnCountMap); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePurchaseInStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpPurchaseInDO purchaseIn = validatePurchaseInExists(id); + // 1.2 校验状态 + if (purchaseIn.getStatus().equals(status)) { + throw exception(approve ? PURCHASE_IN_APPROVE_FAIL : PURCHASE_IN_PROCESS_FAIL); + } + // 1.3 校验已付款 + if (approve && purchaseIn.getPaymentPrice().compareTo(BigDecimal.ZERO) > 0) { + throw exception(PURCHASE_IN_PROCESS_FAIL_EXISTS_PAYMENT); + } + + // 2. 更新状态 + int updateCount = purchaseInMapper.updateByIdAndStatus(id, purchaseIn.getStatus(), + new ErpPurchaseInDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? PURCHASE_IN_APPROVE_FAIL : PURCHASE_IN_PROCESS_FAIL); + } + + // 3. 变更库存 + List purchaseInItems = purchaseInItemMapper.selectListByInId(id); + Integer bizType = approve ? ErpStockRecordBizTypeEnum.PURCHASE_IN.getType() + : ErpStockRecordBizTypeEnum.PURCHASE_IN_CANCEL.getType(); + purchaseInItems.forEach(purchaseInItem -> { + BigDecimal count = approve ? purchaseInItem.getCount() : purchaseInItem.getCount().negate(); + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + purchaseInItem.getProductId(), purchaseInItem.getWarehouseId(), count, + bizType, purchaseInItem.getInId(), purchaseInItem.getId(), purchaseIn.getNo())); + }); + } + + @Override + public void updatePurchaseInPaymentPrice(Long id, BigDecimal paymentPrice) { + ErpPurchaseInDO purchaseIn = purchaseInMapper.selectById(id); + if (purchaseIn.getPaymentPrice().equals(paymentPrice)) { + return; + } + if (paymentPrice.compareTo(purchaseIn.getTotalPrice()) > 0) { + throw exception(PURCHASE_IN_FAIL_PAYMENT_PRICE_EXCEED, paymentPrice, purchaseIn.getTotalPrice()); + } + purchaseInMapper.updateById(new ErpPurchaseInDO().setId(id).setPaymentPrice(paymentPrice)); + } + + private List validatePurchaseInItems(List list) { + // 1. 校验产品存在 + List productList = productService.validProductList( + convertSet(list, ErpPurchaseInSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + // 2. 转化为 ErpPurchaseInItemDO 列表 + return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseInItemDO.class, item -> { + item.setProductUnitId(productMap.get(item.getProductId()).getUnitId()); + item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())); + if (item.getTotalPrice() == null) { + return; + } + if (item.getTaxPercent() != null) { + item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent())); + } + })); + } + + private void updatePurchaseInItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = purchaseInItemMapper.selectListByInId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setInId(id)); + purchaseInItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + purchaseInItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + purchaseInItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpPurchaseInItemDO::getId)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deletePurchaseIn(List ids) { + // 1. 校验不处于已审批 + List purchaseIns = purchaseInMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(purchaseIns)) { + return; + } + purchaseIns.forEach(purchaseIn -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseIn.getStatus())) { + throw exception(PURCHASE_IN_DELETE_FAIL_APPROVE, purchaseIn.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + purchaseIns.forEach(purchaseIn -> { + // 2.1 删除订单 + purchaseInMapper.deleteById(purchaseIn.getId()); + // 2.2 删除订单项 + purchaseInItemMapper.deleteByInId(purchaseIn.getId()); + + // 2.3 更新采购订单的入库数量 + updatePurchaseOrderInCount(purchaseIn.getOrderId()); + }); + + } + + private ErpPurchaseInDO validatePurchaseInExists(Long id) { + ErpPurchaseInDO purchaseIn = purchaseInMapper.selectById(id); + if (purchaseIn == null) { + throw exception(PURCHASE_IN_NOT_EXISTS); + } + return purchaseIn; + } + + @Override + public ErpPurchaseInDO getPurchaseIn(Long id) { + return purchaseInMapper.selectById(id); + } + + @Override + public ErpPurchaseInDO validatePurchaseIn(Long id) { + ErpPurchaseInDO purchaseIn = validatePurchaseInExists(id); + if (ObjectUtil.notEqual(purchaseIn.getStatus(), ErpAuditStatus.APPROVE.getStatus())) { + throw exception(PURCHASE_IN_NOT_APPROVE); + } + return purchaseIn; + } + + @Override + public PageResult getPurchaseInPage(ErpPurchaseInPageReqVO pageReqVO) { + return purchaseInMapper.selectPage(pageReqVO); + } + + // ==================== 采购入库项 ==================== + + @Override + public List getPurchaseInItemListByInId(Long inId) { + return purchaseInItemMapper.selectListByInId(inId); + } + + @Override + public List getPurchaseInItemListByInIds(Collection inIds) { + if (CollUtil.isEmpty(inIds)) { + return Collections.emptyList(); + } + return purchaseInItemMapper.selectListByInIds(inIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java new file mode 100644 index 0000000000..bfea076bb4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java @@ -0,0 +1,110 @@ +package cn.iocoder.yudao.module.erp.service.purchase; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO; +import jakarta.validation.Valid; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * ERP 采购订单 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpPurchaseOrderService { + + /** + * 创建采购订单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createPurchaseOrder(@Valid ErpPurchaseOrderSaveReqVO createReqVO); + + /** + * 更新采购订单 + * + * @param updateReqVO 更新信息 + */ + void updatePurchaseOrder(@Valid ErpPurchaseOrderSaveReqVO updateReqVO); + + /** + * 更新采购订单的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updatePurchaseOrderStatus(Long id, Integer status); + + /** + * 更新采购订单的入库数量 + * + * @param id 编号 + * @param inCountMap 入库数量 Map:key 采购订单项编号;value 入库数量 + */ + void updatePurchaseOrderInCount(Long id, Map inCountMap); + + /** + * 更新采购订单的退货数量 + * + * @param orderId 编号 + * @param returnCountMap 退货数量 Map:key 采购订单项编号;value 退货数量 + */ + void updatePurchaseOrderReturnCount(Long orderId, Map returnCountMap); + + /** + * 删除采购订单 + * + * @param ids 编号数组 + */ + void deletePurchaseOrder(List ids); + + /** + * 获得采购订单 + * + * @param id 编号 + * @return 采购订单 + */ + ErpPurchaseOrderDO getPurchaseOrder(Long id); + + /** + * 校验采购订单,已经审核通过 + * + * @param id 编号 + * @return 采购订单 + */ + ErpPurchaseOrderDO validatePurchaseOrder(Long id); + + /** + * 获得采购订单分页 + * + * @param pageReqVO 分页查询 + * @return 采购订单分页 + */ + PageResult getPurchaseOrderPage(ErpPurchaseOrderPageReqVO pageReqVO); + + // ==================== 采购订单项 ==================== + + /** + * 获得采购订单项列表 + * + * @param orderId 采购订单编号 + * @return 采购订单项列表 + */ + List getPurchaseOrderItemListByOrderId(Long orderId); + + /** + * 获得采购订单项 List + * + * @param orderIds 采购订单编号数组 + * @return 采购订单项 List + */ + List getPurchaseOrderItemListByOrderIds(Collection orderIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java new file mode 100644 index 0000000000..74b5b18d22 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java @@ -0,0 +1,295 @@ +package cn.iocoder.yudao.module.erp.service.purchase; + +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.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO; +import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseOrderItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseOrderMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +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.Collection; +import java.util.Collections; +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.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 采购订单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpPurchaseOrderServiceImpl implements ErpPurchaseOrderService { + + @Resource + private ErpPurchaseOrderMapper purchaseOrderMapper; + @Resource + private ErpPurchaseOrderItemMapper purchaseOrderItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + private ErpSupplierService supplierService; + @Resource + private ErpAccountService accountService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createPurchaseOrder(ErpPurchaseOrderSaveReqVO createReqVO) { + // 1.1 校验订单项的有效性 + List purchaseOrderItems = validatePurchaseOrderItems(createReqVO.getItems()); + // 1.2 校验供应商 + supplierService.validateSupplier(createReqVO.getSupplierId()); + // 1.3 校验结算账户 + if (createReqVO.getAccountId() != null) { + accountService.validateAccount(createReqVO.getAccountId()); + } + // 1.4 生成订单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_ORDER_NO_PREFIX); + if (purchaseOrderMapper.selectByNo(no) != null) { + throw exception(PURCHASE_ORDER_NO_EXISTS); + } + + // 2.1 插入订单 + ErpPurchaseOrderDO purchaseOrder = BeanUtils.toBean(createReqVO, ErpPurchaseOrderDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())); + calculateTotalPrice(purchaseOrder, purchaseOrderItems); + purchaseOrderMapper.insert(purchaseOrder); + // 2.2 插入订单项 + purchaseOrderItems.forEach(o -> o.setOrderId(purchaseOrder.getId())); + purchaseOrderItemMapper.insertBatch(purchaseOrderItems); + return purchaseOrder.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePurchaseOrder(ErpPurchaseOrderSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseOrder.getStatus())) { + throw exception(PURCHASE_ORDER_UPDATE_FAIL_APPROVE, purchaseOrder.getNo()); + } + // 1.2 校验供应商 + supplierService.validateSupplier(updateReqVO.getSupplierId()); + // 1.3 校验结算账户 + if (updateReqVO.getAccountId() != null) { + accountService.validateAccount(updateReqVO.getAccountId()); + } + // 1.4 校验订单项的有效性 + List purchaseOrderItems = validatePurchaseOrderItems(updateReqVO.getItems()); + + // 2.1 更新订单 + ErpPurchaseOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseOrderDO.class); + calculateTotalPrice(updateObj, purchaseOrderItems); + purchaseOrderMapper.updateById(updateObj); + // 2.2 更新订单项 + updatePurchaseOrderItemList(updateReqVO.getId(), purchaseOrderItems); + } + + private void calculateTotalPrice(ErpPurchaseOrderDO purchaseOrder, List purchaseOrderItems) { + purchaseOrder.setTotalCount(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getCount, BigDecimal::add)); + purchaseOrder.setTotalProductPrice(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); + purchaseOrder.setTotalTaxPrice(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO)); + purchaseOrder.setTotalPrice(purchaseOrder.getTotalProductPrice().add(purchaseOrder.getTotalTaxPrice())); + // 计算优惠价格 + if (purchaseOrder.getDiscountPercent() == null) { + purchaseOrder.setDiscountPercent(BigDecimal.ZERO); + } + purchaseOrder.setDiscountPrice(MoneyUtils.priceMultiplyPercent(purchaseOrder.getTotalPrice(), purchaseOrder.getDiscountPercent())); + purchaseOrder.setTotalPrice(purchaseOrder.getTotalPrice().subtract(purchaseOrder.getDiscountPrice())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePurchaseOrderStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id); + // 1.2 校验状态 + if (purchaseOrder.getStatus().equals(status)) { + throw exception(approve ? PURCHASE_ORDER_APPROVE_FAIL : PURCHASE_ORDER_PROCESS_FAIL); + } + // 1.3 存在采购入单,无法反审核 + if (!approve && purchaseOrder.getInCount().compareTo(BigDecimal.ZERO) > 0) { + throw exception(PURCHASE_ORDER_PROCESS_FAIL_EXISTS_IN); + } + // 1.4 存在采购退货单,无法反审核 + if (!approve && purchaseOrder.getReturnCount().compareTo(BigDecimal.ZERO) > 0) { + throw exception(PURCHASE_ORDER_PROCESS_FAIL_EXISTS_RETURN); + } + + // 2. 更新状态 + int updateCount = purchaseOrderMapper.updateByIdAndStatus(id, purchaseOrder.getStatus(), + new ErpPurchaseOrderDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? PURCHASE_ORDER_APPROVE_FAIL : PURCHASE_ORDER_PROCESS_FAIL); + } + } + + private List validatePurchaseOrderItems(List list) { + // 1. 校验产品存在 + List productList = productService.validProductList( + convertSet(list, ErpPurchaseOrderSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + // 2. 转化为 ErpPurchaseOrderItemDO 列表 + return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseOrderItemDO.class, item -> { + item.setProductUnitId(productMap.get(item.getProductId()).getUnitId()); + item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())); + if (item.getTotalPrice() == null) { + return; + } + if (item.getTaxPercent() != null) { + item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent())); + } + })); + } + + private void updatePurchaseOrderItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = purchaseOrderItemMapper.selectListByOrderId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setOrderId(id)); + purchaseOrderItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + purchaseOrderItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + purchaseOrderItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpPurchaseOrderItemDO::getId)); + } + } + + @Override + public void updatePurchaseOrderInCount(Long id, Map inCountMap) { + List orderItems = purchaseOrderItemMapper.selectListByOrderId(id); + // 1. 更新每个采购订单项 + orderItems.forEach(item -> { + BigDecimal inCount = inCountMap.getOrDefault(item.getId(), BigDecimal.ZERO); + if (item.getInCount().equals(inCount)) { + return; + } + if (inCount.compareTo(item.getCount()) > 0) { + throw exception(PURCHASE_ORDER_ITEM_IN_FAIL_PRODUCT_EXCEED, + productService.getProduct(item.getProductId()).getName(), item.getCount()); + } + purchaseOrderItemMapper.updateById(new ErpPurchaseOrderItemDO().setId(item.getId()).setInCount(inCount)); + }); + // 2. 更新采购订单 + BigDecimal totalInCount = getSumValue(inCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO); + purchaseOrderMapper.updateById(new ErpPurchaseOrderDO().setId(id).setInCount(totalInCount)); + } + + @Override + public void updatePurchaseOrderReturnCount(Long orderId, Map returnCountMap) { + List orderItems = purchaseOrderItemMapper.selectListByOrderId(orderId); + // 1. 更新每个采购订单项 + orderItems.forEach(item -> { + BigDecimal returnCount = returnCountMap.getOrDefault(item.getId(), BigDecimal.ZERO); + if (item.getReturnCount().equals(returnCount)) { + return; + } + if (returnCount.compareTo(item.getInCount()) > 0) { + throw exception(PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED, + productService.getProduct(item.getProductId()).getName(), item.getInCount()); + } + purchaseOrderItemMapper.updateById(new ErpPurchaseOrderItemDO().setId(item.getId()).setReturnCount(returnCount)); + }); + // 2. 更新采购订单 + BigDecimal totalReturnCount = getSumValue(returnCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO); + purchaseOrderMapper.updateById(new ErpPurchaseOrderDO().setId(orderId).setReturnCount(totalReturnCount)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deletePurchaseOrder(List ids) { + // 1. 校验不处于已审批 + List purchaseOrders = purchaseOrderMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(purchaseOrders)) { + return; + } + purchaseOrders.forEach(purchaseOrder -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseOrder.getStatus())) { + throw exception(PURCHASE_ORDER_DELETE_FAIL_APPROVE, purchaseOrder.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + purchaseOrders.forEach(purchaseOrder -> { + // 2.1 删除订单 + purchaseOrderMapper.deleteById(purchaseOrder.getId()); + // 2.2 删除订单项 + purchaseOrderItemMapper.deleteByOrderId(purchaseOrder.getId()); + }); + } + + private ErpPurchaseOrderDO validatePurchaseOrderExists(Long id) { + ErpPurchaseOrderDO purchaseOrder = purchaseOrderMapper.selectById(id); + if (purchaseOrder == null) { + throw exception(PURCHASE_ORDER_NOT_EXISTS); + } + return purchaseOrder; + } + + @Override + public ErpPurchaseOrderDO getPurchaseOrder(Long id) { + return purchaseOrderMapper.selectById(id); + } + + @Override + public ErpPurchaseOrderDO validatePurchaseOrder(Long id) { + ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id); + if (ObjectUtil.notEqual(purchaseOrder.getStatus(), ErpAuditStatus.APPROVE.getStatus())) { + throw exception(PURCHASE_ORDER_NOT_APPROVE); + } + return purchaseOrder; + } + + @Override + public PageResult getPurchaseOrderPage(ErpPurchaseOrderPageReqVO pageReqVO) { + return purchaseOrderMapper.selectPage(pageReqVO); + } + + // ==================== 订单项 ==================== + + @Override + public List getPurchaseOrderItemListByOrderId(Long orderId) { + return purchaseOrderItemMapper.selectListByOrderId(orderId); + } + + @Override + public List getPurchaseOrderItemListByOrderIds(Collection orderIds) { + if (CollUtil.isEmpty(orderIds)) { + return Collections.emptyList(); + } + return purchaseOrderItemMapper.selectListByOrderIds(orderIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnService.java new file mode 100644 index 0000000000..b6826fa889 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnService.java @@ -0,0 +1,101 @@ +package cn.iocoder.yudao.module.erp.service.purchase; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO; +import jakarta.validation.Valid; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; + +/** + * ERP 采购退货 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpPurchaseReturnService { + + /** + * 创建采购退货 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createPurchaseReturn(@Valid ErpPurchaseReturnSaveReqVO createReqVO); + + /** + * 更新采购退货 + * + * @param updateReqVO 更新信息 + */ + void updatePurchaseReturn(@Valid ErpPurchaseReturnSaveReqVO updateReqVO); + + /** + * 更新采购退货的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updatePurchaseReturnStatus(Long id, Integer status); + + /** + * 更新采购退货的退款金额 + * + * @param id 编号 + * @param refundPrice 退款金额 + */ + void updatePurchaseReturnRefundPrice(Long id, BigDecimal refundPrice); + + /** + * 删除采购退货 + * + * @param ids 编号数组 + */ + void deletePurchaseReturn(List ids); + + /** + * 获得采购退货 + * + * @param id 编号 + * @return 采购退货 + */ + ErpPurchaseReturnDO getPurchaseReturn(Long id); + + /** + * 校验采购退货,已经审核通过 + * + * @param id 编号 + * @return 采购退货 + */ + ErpPurchaseReturnDO validatePurchaseReturn(Long id); + + /** + * 获得采购退货分页 + * + * @param pageReqVO 分页查询 + * @return 采购退货分页 + */ + PageResult getPurchaseReturnPage(ErpPurchaseReturnPageReqVO pageReqVO); + + // ==================== 采购退货项 ==================== + + /** + * 获得采购退货项列表 + * + * @param returnId 采购退货编号 + * @return 采购退货项列表 + */ + List getPurchaseReturnItemListByReturnId(Long returnId); + + /** + * 获得采购退货项 List + * + * @param returnIds 采购退货编号数组 + * @return 采购退货项 List + */ + List getPurchaseReturnItemListByReturnIds(Collection returnIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnServiceImpl.java new file mode 100644 index 0000000000..05960e896a --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnServiceImpl.java @@ -0,0 +1,304 @@ +package cn.iocoder.yudao.module.erp.service.purchase; + +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.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO; +import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseReturnItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseReturnMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService; +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO; +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.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +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.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 采购退货 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpPurchaseReturnServiceImpl implements ErpPurchaseReturnService { + + @Resource + private ErpPurchaseReturnMapper purchaseReturnMapper; + @Resource + private ErpPurchaseReturnItemMapper purchaseReturnItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private ErpPurchaseOrderService purchaseOrderService; + @Resource + private ErpAccountService accountService; + @Resource + private ErpStockRecordService stockRecordService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createPurchaseReturn(ErpPurchaseReturnSaveReqVO createReqVO) { + // 1.1 校验采购订单已审核 + ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(createReqVO.getOrderId()); + // 1.2 校验退货项的有效性 + List purchaseReturnItems = validatePurchaseReturnItems(createReqVO.getItems()); + // 1.3 校验结算账户 + accountService.validateAccount(createReqVO.getAccountId()); + // 1.4 生成退货单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_RETURN_NO_PREFIX); + if (purchaseReturnMapper.selectByNo(no) != null) { + throw exception(PURCHASE_RETURN_NO_EXISTS); + } + + // 2.1 插入退货 + ErpPurchaseReturnDO purchaseReturn = BeanUtils.toBean(createReqVO, ErpPurchaseReturnDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())) + .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId()); + calculateTotalPrice(purchaseReturn, purchaseReturnItems); + purchaseReturnMapper.insert(purchaseReturn); + // 2.2 插入退货项 + purchaseReturnItems.forEach(o -> o.setReturnId(purchaseReturn.getId())); + purchaseReturnItemMapper.insertBatch(purchaseReturnItems); + + // 3. 更新采购订单的退货数量 + updatePurchaseOrderReturnCount(createReqVO.getOrderId()); + return purchaseReturn.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePurchaseReturn(ErpPurchaseReturnSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpPurchaseReturnDO purchaseReturn = validatePurchaseReturnExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseReturn.getStatus())) { + throw exception(PURCHASE_RETURN_UPDATE_FAIL_APPROVE, purchaseReturn.getNo()); + } + // 1.2 校验采购订单已审核 + ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(updateReqVO.getOrderId()); + // 1.3 校验结算账户 + accountService.validateAccount(updateReqVO.getAccountId()); + // 1.4 校验订单项的有效性 + List purchaseReturnItems = validatePurchaseReturnItems(updateReqVO.getItems()); + + // 2.1 更新退货 + ErpPurchaseReturnDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseReturnDO.class) + .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId()); + calculateTotalPrice(updateObj, purchaseReturnItems); + purchaseReturnMapper.updateById(updateObj); + // 2.2 更新退货项 + updatePurchaseReturnItemList(updateReqVO.getId(), purchaseReturnItems); + + // 3.1 更新采购订单的出库数量 + updatePurchaseOrderReturnCount(updateObj.getOrderId()); + // 3.2 注意:如果采购订单编号变更了,需要更新“老”采购订单的出库数量 + if (ObjectUtil.notEqual(purchaseReturn.getOrderId(), updateObj.getOrderId())) { + updatePurchaseOrderReturnCount(purchaseReturn.getOrderId()); + } + } + + private void calculateTotalPrice(ErpPurchaseReturnDO purchaseReturn, List purchaseReturnItems) { + purchaseReturn.setTotalCount(getSumValue(purchaseReturnItems, ErpPurchaseReturnItemDO::getCount, BigDecimal::add)); + purchaseReturn.setTotalProductPrice(getSumValue(purchaseReturnItems, ErpPurchaseReturnItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); + purchaseReturn.setTotalTaxPrice(getSumValue(purchaseReturnItems, ErpPurchaseReturnItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO)); + purchaseReturn.setTotalPrice(purchaseReturn.getTotalProductPrice().add(purchaseReturn.getTotalTaxPrice())); + // 计算优惠价格 + if (purchaseReturn.getDiscountPercent() == null) { + purchaseReturn.setDiscountPercent(BigDecimal.ZERO); + } + purchaseReturn.setDiscountPrice(MoneyUtils.priceMultiplyPercent(purchaseReturn.getTotalPrice(), purchaseReturn.getDiscountPercent())); + purchaseReturn.setTotalPrice(purchaseReturn.getTotalPrice().subtract(purchaseReturn.getDiscountPrice().add(purchaseReturn.getOtherPrice()))); + } + + private void updatePurchaseOrderReturnCount(Long orderId) { + // 1.1 查询采购订单对应的采购出库单列表 + List purchaseReturns = purchaseReturnMapper.selectListByOrderId(orderId); + // 1.2 查询对应的采购订单项的退货数量 + Map returnCountMap = purchaseReturnItemMapper.selectOrderItemCountSumMapByReturnIds( + convertList(purchaseReturns, ErpPurchaseReturnDO::getId)); + // 2. 更新采购订单的出库数量 + purchaseOrderService.updatePurchaseOrderReturnCount(orderId, returnCountMap); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePurchaseReturnStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpPurchaseReturnDO purchaseReturn = validatePurchaseReturnExists(id); + // 1.2 校验状态 + if (purchaseReturn.getStatus().equals(status)) { + throw exception(approve ? PURCHASE_RETURN_APPROVE_FAIL : PURCHASE_RETURN_PROCESS_FAIL); + } + // 1.3 校验已退款 + if (approve && purchaseReturn.getRefundPrice().compareTo(BigDecimal.ZERO) > 0) { + throw exception(PURCHASE_RETURN_PROCESS_FAIL_EXISTS_REFUND); + } + + // 2. 更新状态 + int updateCount = purchaseReturnMapper.updateByIdAndStatus(id, purchaseReturn.getStatus(), + new ErpPurchaseReturnDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? PURCHASE_RETURN_APPROVE_FAIL : PURCHASE_RETURN_PROCESS_FAIL); + } + + // 3. 变更库存 + List purchaseReturnItems = purchaseReturnItemMapper.selectListByReturnId(id); + Integer bizType = approve ? ErpStockRecordBizTypeEnum.PURCHASE_RETURN.getType() + : ErpStockRecordBizTypeEnum.PURCHASE_RETURN_CANCEL.getType(); + purchaseReturnItems.forEach(purchaseReturnItem -> { + BigDecimal count = approve ? purchaseReturnItem.getCount().negate() : purchaseReturnItem.getCount(); + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + purchaseReturnItem.getProductId(), purchaseReturnItem.getWarehouseId(), count, + bizType, purchaseReturnItem.getReturnId(), purchaseReturnItem.getId(), purchaseReturn.getNo())); + }); + } + + @Override + public void updatePurchaseReturnRefundPrice(Long id, BigDecimal refundPrice) { + ErpPurchaseReturnDO purchaseReturn = purchaseReturnMapper.selectById(id); + if (purchaseReturn.getRefundPrice().equals(refundPrice)) { + return; + } + if (refundPrice.compareTo(purchaseReturn.getTotalPrice()) > 0) { + throw exception(PURCHASE_RETURN_FAIL_REFUND_PRICE_EXCEED, refundPrice, purchaseReturn.getTotalPrice()); + } + purchaseReturnMapper.updateById(new ErpPurchaseReturnDO().setId(id).setRefundPrice(refundPrice)); + } + + private List validatePurchaseReturnItems(List list) { + // 1. 校验产品存在 + List productList = productService.validProductList( + convertSet(list, ErpPurchaseReturnSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + // 2. 转化为 ErpPurchaseReturnItemDO 列表 + return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseReturnItemDO.class, item -> { + item.setProductUnitId(productMap.get(item.getProductId()).getUnitId()); + item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())); + if (item.getTotalPrice() == null) { + return; + } + if (item.getTaxPercent() != null) { + item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent())); + } + })); + } + + private void updatePurchaseReturnItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = purchaseReturnItemMapper.selectListByReturnId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setReturnId(id)); + purchaseReturnItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + purchaseReturnItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + purchaseReturnItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpPurchaseReturnItemDO::getId)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deletePurchaseReturn(List ids) { + // 1. 校验不处于已审批 + List purchaseReturns = purchaseReturnMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(purchaseReturns)) { + return; + } + purchaseReturns.forEach(purchaseReturn -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseReturn.getStatus())) { + throw exception(PURCHASE_RETURN_DELETE_FAIL_APPROVE, purchaseReturn.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + purchaseReturns.forEach(purchaseReturn -> { + // 2.1 删除订单 + purchaseReturnMapper.deleteById(purchaseReturn.getId()); + // 2.2 删除订单项 + purchaseReturnItemMapper.deleteByReturnId(purchaseReturn.getId()); + + // 2.3 更新采购订单的出库数量 + updatePurchaseOrderReturnCount(purchaseReturn.getOrderId()); + }); + + } + + private ErpPurchaseReturnDO validatePurchaseReturnExists(Long id) { + ErpPurchaseReturnDO purchaseReturn = purchaseReturnMapper.selectById(id); + if (purchaseReturn == null) { + throw exception(PURCHASE_RETURN_NOT_EXISTS); + } + return purchaseReturn; + } + + @Override + public ErpPurchaseReturnDO getPurchaseReturn(Long id) { + return purchaseReturnMapper.selectById(id); + } + + @Override + public ErpPurchaseReturnDO validatePurchaseReturn(Long id) { + ErpPurchaseReturnDO purchaseReturn = getPurchaseReturn(id); + if (ObjectUtil.notEqual(purchaseReturn.getStatus(), ErpAuditStatus.APPROVE.getStatus())) { + throw exception(PURCHASE_RETURN_NOT_APPROVE); + } + return purchaseReturn; + } + + @Override + public PageResult getPurchaseReturnPage(ErpPurchaseReturnPageReqVO pageReqVO) { + return purchaseReturnMapper.selectPage(pageReqVO); + } + + // ==================== 采购退货项 ==================== + + @Override + public List getPurchaseReturnItemListByReturnId(Long returnId) { + return purchaseReturnItemMapper.selectListByReturnId(returnId); + } + + @Override + public List getPurchaseReturnItemListByReturnIds(Collection returnIds) { + if (CollUtil.isEmpty(returnIds)) { + return Collections.emptyList(); + } + return purchaseReturnItemMapper.selectListByReturnIds(returnIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpSupplierService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpSupplierService.java new file mode 100644 index 0000000000..495474d138 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpSupplierService.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.erp.service.purchase; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +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; + +/** + * ERP 供应商 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpSupplierService { + + /** + * 创建供应商 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSupplier(@Valid ErpSupplierSaveReqVO createReqVO); + + /** + * 更新供应商 + * + * @param updateReqVO 更新信息 + */ + void updateSupplier(@Valid ErpSupplierSaveReqVO updateReqVO); + + /** + * 删除供应商 + * + * @param id 编号 + */ + void deleteSupplier(Long id); + + /** + * 获得供应商 + * + * @param id 编号 + * @return 供应商 + */ + ErpSupplierDO getSupplier(Long id); + + /** + * 校验供应商 + * + * @param id 编号 + * @return 供应商 + */ + ErpSupplierDO validateSupplier(Long id); + + /** + * 获得供应商列表 + * + * @param ids 编号列表 + * @return 供应商列表 + */ + List getSupplierList(Collection ids); + + /** + * 获得供应商 Map + * + * @param ids 编号列表 + * @return 供应商 Map + */ + default Map getSupplierMap(Collection ids) { + return convertMap(getSupplierList(ids), ErpSupplierDO::getId); + } + + /** + * 获得供应商分页 + * + * @param pageReqVO 分页查询 + * @return 供应商分页 + */ + PageResult getSupplierPage(ErpSupplierPageReqVO pageReqVO); + + /** + * 获得指定状态的供应商列表 + * + * @param status 状态 + * @return 供应商列表 + */ + List getSupplierListByStatus(Integer status); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpSupplierServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpSupplierServiceImpl.java new file mode 100644 index 0000000000..1c90ff3f6f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpSupplierServiceImpl.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.erp.service.purchase; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; +import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpSupplierMapper; +import jakarta.annotation.Resource; +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.erp.enums.ErrorCodeConstants.*; + +/** + * ERP 供应商 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpSupplierServiceImpl implements ErpSupplierService { + + @Resource + private ErpSupplierMapper supplierMapper; + + @Override + public Long createSupplier(ErpSupplierSaveReqVO createReqVO) { + ErpSupplierDO supplier = BeanUtils.toBean(createReqVO, ErpSupplierDO.class); + supplierMapper.insert(supplier); + return supplier.getId(); + } + + @Override + public void updateSupplier(ErpSupplierSaveReqVO updateReqVO) { + // 校验存在 + validateSupplierExists(updateReqVO.getId()); + // 更新 + ErpSupplierDO updateObj = BeanUtils.toBean(updateReqVO, ErpSupplierDO.class); + supplierMapper.updateById(updateObj); + } + + @Override + public void deleteSupplier(Long id) { + // 校验存在 + validateSupplierExists(id); + // 删除 + supplierMapper.deleteById(id); + } + + private void validateSupplierExists(Long id) { + if (supplierMapper.selectById(id) == null) { + throw exception(SUPPLIER_NOT_EXISTS); + } + } + + @Override + public ErpSupplierDO getSupplier(Long id) { + return supplierMapper.selectById(id); + } + + @Override + public ErpSupplierDO validateSupplier(Long id) { + ErpSupplierDO supplier = supplierMapper.selectById(id); + if (supplier == null) { + throw exception(SUPPLIER_NOT_EXISTS); + } + if (CommonStatusEnum.isDisable(supplier.getStatus())) { + throw exception(SUPPLIER_NOT_ENABLE, supplier.getName()); + } + return supplier; + } + + @Override + public List getSupplierList(Collection ids) { + return supplierMapper.selectBatchIds(ids); + } + + @Override + public PageResult getSupplierPage(ErpSupplierPageReqVO pageReqVO) { + return supplierMapper.selectPage(pageReqVO); + } + + @Override + public List getSupplierListByStatus(Integer status) { + return supplierMapper.selectListByStatus(status); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpCustomerService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpCustomerService.java new file mode 100644 index 0000000000..d047300e8f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpCustomerService.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.erp.service.sale; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO; +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; + +/** + * ERP 客户 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpCustomerService { + + /** + * 创建客户 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createCustomer(@Valid ErpCustomerSaveReqVO createReqVO); + + /** + * 更新客户 + * + * @param updateReqVO 更新信息 + */ + void updateCustomer(@Valid ErpCustomerSaveReqVO updateReqVO); + + /** + * 删除客户 + * + * @param id 编号 + */ + void deleteCustomer(Long id); + + /** + * 获得客户 + * + * @param id 编号 + * @return 客户 + */ + ErpCustomerDO getCustomer(Long id); + + /** + * 校验客户 + * + * @param id 编号 + * @return 客户 + */ + ErpCustomerDO validateCustomer(Long id); + + /** + * 获得客户列表 + * + * @param ids 编号列表 + * @return 客户列表 + */ + List getCustomerList(Collection ids); + + /** + * 获得客户 Map + * + * @param ids 编号列表 + * @return 客户 Map + */ + default Map getCustomerMap(Collection ids) { + return convertMap(getCustomerList(ids), ErpCustomerDO::getId); + } + + /** + * 获得客户分页 + * + * @param pageReqVO 分页查询 + * @return 客户分页 + */ + PageResult getCustomerPage(ErpCustomerPageReqVO pageReqVO); + + /** + * 获得指定状态的客户列表 + * + * @param status 状态 + * @return 客户列表 + */ + List getCustomerListByStatus(Integer status); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpCustomerServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpCustomerServiceImpl.java new file mode 100644 index 0000000000..73d2aa9d57 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpCustomerServiceImpl.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.erp.service.sale; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO; +import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpCustomerMapper; +import jakarta.annotation.Resource; +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.erp.enums.ErrorCodeConstants.CUSTOMER_NOT_ENABLE; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS; + +/** + * ERP 客户 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpCustomerServiceImpl implements ErpCustomerService { + + @Resource + private ErpCustomerMapper customerMapper; + + @Override + public Long createCustomer(ErpCustomerSaveReqVO createReqVO) { + // 插入 + ErpCustomerDO customer = BeanUtils.toBean(createReqVO, ErpCustomerDO.class); + customerMapper.insert(customer); + // 返回 + return customer.getId(); + } + + @Override + public void updateCustomer(ErpCustomerSaveReqVO updateReqVO) { + // 校验存在 + validateCustomerExists(updateReqVO.getId()); + // 更新 + ErpCustomerDO updateObj = BeanUtils.toBean(updateReqVO, ErpCustomerDO.class); + customerMapper.updateById(updateObj); + } + + @Override + public void deleteCustomer(Long id) { + // 校验存在 + validateCustomerExists(id); + // 删除 + customerMapper.deleteById(id); + } + + private void validateCustomerExists(Long id) { + if (customerMapper.selectById(id) == null) { + throw exception(CUSTOMER_NOT_EXISTS); + } + } + + @Override + public ErpCustomerDO getCustomer(Long id) { + return customerMapper.selectById(id); + } + + @Override + public ErpCustomerDO validateCustomer(Long id) { + ErpCustomerDO customer = customerMapper.selectById(id); + if (customer == null) { + throw exception(CUSTOMER_NOT_EXISTS); + } + if (CommonStatusEnum.isDisable(customer.getStatus())) { + throw exception(CUSTOMER_NOT_ENABLE, customer.getName()); + } + return customer; + } + + @Override + public List getCustomerList(Collection ids) { + return customerMapper.selectBatchIds(ids); + } + + @Override + public PageResult getCustomerPage(ErpCustomerPageReqVO pageReqVO) { + return customerMapper.selectPage(pageReqVO); + } + + @Override + public List getCustomerListByStatus(Integer status) { + return customerMapper.selectListByStatus(status); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java new file mode 100644 index 0000000000..c75f201f34 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java @@ -0,0 +1,110 @@ +package cn.iocoder.yudao.module.erp.service.sale; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO; +import jakarta.validation.Valid; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * ERP 销售订单 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpSaleOrderService { + + /** + * 创建销售订单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSaleOrder(@Valid ErpSaleOrderSaveReqVO createReqVO); + + /** + * 更新销售订单 + * + * @param updateReqVO 更新信息 + */ + void updateSaleOrder(@Valid ErpSaleOrderSaveReqVO updateReqVO); + + /** + * 更新销售订单的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateSaleOrderStatus(Long id, Integer status); + + /** + * 更新销售订单的出库数量 + * + * @param id 编号 + * @param outCountMap 出库数量 Map:key 销售订单项编号;value 出库数量 + */ + void updateSaleOrderOutCount(Long id, Map outCountMap); + + /** + * 更新销售订单的退货数量 + * + * @param orderId 编号 + * @param returnCountMap 退货数量 Map:key 销售订单项编号;value 退货数量 + */ + void updateSaleOrderReturnCount(Long orderId, Map returnCountMap); + + /** + * 删除销售订单 + * + * @param ids 编号数组 + */ + void deleteSaleOrder(List ids); + + /** + * 获得销售订单 + * + * @param id 编号 + * @return 销售订单 + */ + ErpSaleOrderDO getSaleOrder(Long id); + + /** + * 校验销售订单,已经审核通过 + * + * @param id 编号 + * @return 销售订单 + */ + ErpSaleOrderDO validateSaleOrder(Long id); + + /** + * 获得销售订单分页 + * + * @param pageReqVO 分页查询 + * @return 销售订单分页 + */ + PageResult getSaleOrderPage(ErpSaleOrderPageReqVO pageReqVO); + + // ==================== 销售订单项 ==================== + + /** + * 获得销售订单项列表 + * + * @param orderId 销售订单编号 + * @return 销售订单项列表 + */ + List getSaleOrderItemListByOrderId(Long orderId); + + /** + * 获得销售订单项 List + * + * @param orderIds 销售订单编号数组 + * @return 销售订单项 List + */ + List getSaleOrderItemListByOrderIds(Collection orderIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java new file mode 100644 index 0000000000..592fe5fe76 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java @@ -0,0 +1,307 @@ +package cn.iocoder.yudao.module.erp.service.sale; + +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.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO; +import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +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.Collection; +import java.util.Collections; +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.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 销售订单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { + + @Resource + private ErpSaleOrderMapper saleOrderMapper; + @Resource + private ErpSaleOrderItemMapper saleOrderItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + private ErpCustomerService customerService; + @Resource + private ErpAccountService accountService; + + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createSaleOrder(ErpSaleOrderSaveReqVO createReqVO) { + // 1.1 校验订单项的有效性 + List saleOrderItems = validateSaleOrderItems(createReqVO.getItems()); + // 1.2 校验客户 + customerService.validateCustomer(createReqVO.getCustomerId()); + // 1.3 校验结算账户 + if (createReqVO.getAccountId() != null) { + accountService.validateAccount(createReqVO.getAccountId()); + } + // 1.4 校验销售人员 + if (createReqVO.getSaleUserId() != null) { + adminUserApi.validateUser(createReqVO.getSaleUserId()); + } + // 1.5 生成订单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_ORDER_NO_PREFIX); + if (saleOrderMapper.selectByNo(no) != null) { + throw exception(SALE_ORDER_NO_EXISTS); + } + + // 2.1 插入订单 + ErpSaleOrderDO saleOrder = BeanUtils.toBean(createReqVO, ErpSaleOrderDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())); + calculateTotalPrice(saleOrder, saleOrderItems); + saleOrderMapper.insert(saleOrder); + // 2.2 插入订单项 + saleOrderItems.forEach(o -> o.setOrderId(saleOrder.getId())); + saleOrderItemMapper.insertBatch(saleOrderItems); + return saleOrder.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateSaleOrder(ErpSaleOrderSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpSaleOrderDO saleOrder = validateSaleOrderExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) { + throw exception(SALE_ORDER_UPDATE_FAIL_APPROVE, saleOrder.getNo()); + } + // 1.2 校验客户 + customerService.validateCustomer(updateReqVO.getCustomerId()); + // 1.3 校验结算账户 + if (updateReqVO.getAccountId() != null) { + accountService.validateAccount(updateReqVO.getAccountId()); + } + // 1.4 校验销售人员 + if (updateReqVO.getSaleUserId() != null) { + adminUserApi.validateUser(updateReqVO.getSaleUserId()); + } + // 1.5 校验订单项的有效性 + List saleOrderItems = validateSaleOrderItems(updateReqVO.getItems()); + + // 2.1 更新订单 + ErpSaleOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleOrderDO.class); + calculateTotalPrice(updateObj, saleOrderItems); + saleOrderMapper.updateById(updateObj); + // 2.2 更新订单项 + updateSaleOrderItemList(updateReqVO.getId(), saleOrderItems); + } + + private void calculateTotalPrice(ErpSaleOrderDO saleOrder, List saleOrderItems) { + saleOrder.setTotalCount(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getCount, BigDecimal::add)); + saleOrder.setTotalProductPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); + saleOrder.setTotalTaxPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO)); + saleOrder.setTotalPrice(saleOrder.getTotalProductPrice().add(saleOrder.getTotalTaxPrice())); + // 计算优惠价格 + if (saleOrder.getDiscountPercent() == null) { + saleOrder.setDiscountPercent(BigDecimal.ZERO); + } + saleOrder.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleOrder.getTotalPrice(), saleOrder.getDiscountPercent())); + saleOrder.setTotalPrice(saleOrder.getTotalPrice().subtract(saleOrder.getDiscountPrice())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateSaleOrderStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpSaleOrderDO saleOrder = validateSaleOrderExists(id); + // 1.2 校验状态 + if (saleOrder.getStatus().equals(status)) { + throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL); + } + // 1.3 存在销售出库单,无法反审核 + if (!approve && saleOrder.getOutCount().compareTo(BigDecimal.ZERO) > 0) { + throw exception(SALE_ORDER_PROCESS_FAIL_EXISTS_OUT); + } + // 1.4 存在销售退货单,无法反审核 + if (!approve && saleOrder.getReturnCount().compareTo(BigDecimal.ZERO) > 0) { + throw exception(SALE_ORDER_PROCESS_FAIL_EXISTS_RETURN); + } + + // 2. 更新状态 + int updateCount = saleOrderMapper.updateByIdAndStatus(id, saleOrder.getStatus(), + new ErpSaleOrderDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL); + } + } + + private List validateSaleOrderItems(List list) { + // 1. 校验产品存在 + List productList = productService.validProductList( + convertSet(list, ErpSaleOrderSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + // 2. 转化为 ErpSaleOrderItemDO 列表 + return convertList(list, o -> BeanUtils.toBean(o, ErpSaleOrderItemDO.class, item -> { + item.setProductUnitId(productMap.get(item.getProductId()).getUnitId()); + item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())); + if (item.getTotalPrice() == null) { + return; + } + if (item.getTaxPercent() != null) { + item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent())); + } + })); + } + + private void updateSaleOrderItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = saleOrderItemMapper.selectListByOrderId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setOrderId(id)); + saleOrderItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + saleOrderItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + saleOrderItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpSaleOrderItemDO::getId)); + } + } + + @Override + public void updateSaleOrderOutCount(Long id, Map outCountMap) { + List orderItems = saleOrderItemMapper.selectListByOrderId(id); + // 1. 更新每个销售订单项 + orderItems.forEach(item -> { + BigDecimal outCount = outCountMap.getOrDefault(item.getId(), BigDecimal.ZERO); + if (item.getOutCount().equals(outCount)) { + return; + } + if (outCount.compareTo(item.getCount()) > 0) { + throw exception(SALE_ORDER_ITEM_OUT_FAIL_PRODUCT_EXCEED, + productService.getProduct(item.getProductId()).getName(), item.getCount()); + } + saleOrderItemMapper.updateById(new ErpSaleOrderItemDO().setId(item.getId()).setOutCount(outCount)); + }); + // 2. 更新销售订单 + BigDecimal totalOutCount = getSumValue(outCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO); + saleOrderMapper.updateById(new ErpSaleOrderDO().setId(id).setOutCount(totalOutCount)); + } + + @Override + public void updateSaleOrderReturnCount(Long orderId, Map returnCountMap) { + List orderItems = saleOrderItemMapper.selectListByOrderId(orderId); + // 1. 更新每个销售订单项 + orderItems.forEach(item -> { + BigDecimal returnCount = returnCountMap.getOrDefault(item.getId(), BigDecimal.ZERO); + if (item.getReturnCount().equals(returnCount)) { + return; + } + if (returnCount.compareTo(item.getOutCount()) > 0) { + throw exception(SALE_ORDER_ITEM_RETURN_FAIL_OUT_EXCEED, + productService.getProduct(item.getProductId()).getName(), item.getOutCount()); + } + saleOrderItemMapper.updateById(new ErpSaleOrderItemDO().setId(item.getId()).setReturnCount(returnCount)); + }); + // 2. 更新销售订单 + BigDecimal totalReturnCount = getSumValue(returnCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO); + saleOrderMapper.updateById(new ErpSaleOrderDO().setId(orderId).setReturnCount(totalReturnCount)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteSaleOrder(List ids) { + // 1. 校验不处于已审批 + List saleOrders = saleOrderMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(saleOrders)) { + return; + } + saleOrders.forEach(saleOrder -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) { + throw exception(SALE_ORDER_DELETE_FAIL_APPROVE, saleOrder.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + saleOrders.forEach(saleOrder -> { + // 2.1 删除订单 + saleOrderMapper.deleteById(saleOrder.getId()); + // 2.2 删除订单项 + saleOrderItemMapper.deleteByOrderId(saleOrder.getId()); + }); + } + + private ErpSaleOrderDO validateSaleOrderExists(Long id) { + ErpSaleOrderDO saleOrder = saleOrderMapper.selectById(id); + if (saleOrder == null) { + throw exception(SALE_ORDER_NOT_EXISTS); + } + return saleOrder; + } + + @Override + public ErpSaleOrderDO getSaleOrder(Long id) { + return saleOrderMapper.selectById(id); + } + + @Override + public ErpSaleOrderDO validateSaleOrder(Long id) { + ErpSaleOrderDO saleOrder = validateSaleOrderExists(id); + if (ObjectUtil.notEqual(saleOrder.getStatus(), ErpAuditStatus.APPROVE.getStatus())) { + throw exception(SALE_ORDER_NOT_APPROVE); + } + return saleOrder; + } + + @Override + public PageResult getSaleOrderPage(ErpSaleOrderPageReqVO pageReqVO) { + return saleOrderMapper.selectPage(pageReqVO); + } + + // ==================== 订单项 ==================== + + @Override + public List getSaleOrderItemListByOrderId(Long orderId) { + return saleOrderItemMapper.selectListByOrderId(orderId); + } + + @Override + public List getSaleOrderItemListByOrderIds(Collection orderIds) { + if (CollUtil.isEmpty(orderIds)) { + return Collections.emptyList(); + } + return saleOrderItemMapper.selectListByOrderIds(orderIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutService.java new file mode 100644 index 0000000000..616f77837f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutService.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.erp.service.sale; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO; +import jakarta.validation.Valid; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; + +/** + * ERP 销售出库 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpSaleOutService { + + /** + * 创建销售出库 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSaleOut(@Valid ErpSaleOutSaveReqVO createReqVO); + + /** + * 更新销售出库 + * + * @param updateReqVO 更新信息 + */ + void updateSaleOut(@Valid ErpSaleOutSaveReqVO updateReqVO); + + /** + * 更新销售出库的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateSaleOutStatus(Long id, Integer status); + + /** + * 更新销售出库的收款金额 + * + * @param id 编号 + * @param receiptPrice 收款金额 + */ + void updateSaleInReceiptPrice(Long id, BigDecimal receiptPrice); + + /** + * 删除销售出库 + * + * @param ids 编号数组 + */ + void deleteSaleOut(List ids); + + /** + * 获得销售出库 + * + * @param id 编号 + * @return 销售出库 + */ + ErpSaleOutDO getSaleOut(Long id); + + /** + * 校验销售出库,已经审核通过 + * + * @param id 编号 + * @return 销售出库 + */ + ErpSaleOutDO validateSaleOut(Long id); + + /** + * 获得销售出库分页 + * + * @param pageReqVO 分页查询 + * @return 销售出库分页 + */ + PageResult getSaleOutPage(ErpSaleOutPageReqVO pageReqVO); + + // ==================== 销售出库项 ==================== + + /** + * 获得销售出库项列表 + * + * @param outId 销售出库编号 + * @return 销售出库项列表 + */ + List getSaleOutItemListByOutId(Long outId); + + /** + * 获得销售出库项 List + * + * @param outIds 销售出库编号数组 + * @return 销售出库项 List + */ + List getSaleOutItemListByOutIds(Collection outIds); + + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutServiceImpl.java new file mode 100644 index 0000000000..d930c16bf4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutServiceImpl.java @@ -0,0 +1,316 @@ +package cn.iocoder.yudao.module.erp.service.sale; + +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.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO; +import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOutItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOutMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService; +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +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.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +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.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 销售出库 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpSaleOutServiceImpl implements ErpSaleOutService { + + @Resource + private ErpSaleOutMapper saleOutMapper; + @Resource + private ErpSaleOutItemMapper saleOutItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private ErpSaleOrderService saleOrderService; + @Resource + private ErpAccountService accountService; + @Resource + private ErpStockRecordService stockRecordService; + + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createSaleOut(ErpSaleOutSaveReqVO createReqVO) { + // 1.1 校验销售订单已审核 + ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(createReqVO.getOrderId()); + // 1.2 校验出库项的有效性 + List saleOutItems = validateSaleOutItems(createReqVO.getItems()); + // 1.3 校验结算账户 + accountService.validateAccount(createReqVO.getAccountId()); + // 1.4 校验销售人员 + if (createReqVO.getSaleUserId() != null) { + adminUserApi.validateUser(createReqVO.getSaleUserId()); + } + // 1.5 生成出库单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_OUT_NO_PREFIX); + if (saleOutMapper.selectByNo(no) != null) { + throw exception(SALE_OUT_NO_EXISTS); + } + + // 2.1 插入出库 + ErpSaleOutDO saleOut = BeanUtils.toBean(createReqVO, ErpSaleOutDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())) + .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId()); + calculateTotalPrice(saleOut, saleOutItems); + saleOutMapper.insert(saleOut); + // 2.2 插入出库项 + saleOutItems.forEach(o -> o.setOutId(saleOut.getId())); + saleOutItemMapper.insertBatch(saleOutItems); + + // 3. 更新销售订单的出库数量 + updateSaleOrderOutCount(createReqVO.getOrderId()); + return saleOut.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateSaleOut(ErpSaleOutSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpSaleOutDO saleOut = validateSaleOutExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(saleOut.getStatus())) { + throw exception(SALE_OUT_UPDATE_FAIL_APPROVE, saleOut.getNo()); + } + // 1.2 校验销售订单已审核 + ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(updateReqVO.getOrderId()); + // 1.3 校验结算账户 + accountService.validateAccount(updateReqVO.getAccountId()); + // 1.4 校验销售人员 + if (updateReqVO.getSaleUserId() != null) { + adminUserApi.validateUser(updateReqVO.getSaleUserId()); + } + // 1.5 校验订单项的有效性 + List saleOutItems = validateSaleOutItems(updateReqVO.getItems()); + + // 2.1 更新出库 + ErpSaleOutDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleOutDO.class) + .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId()); + calculateTotalPrice(updateObj, saleOutItems); + saleOutMapper.updateById(updateObj); + // 2.2 更新出库项 + updateSaleOutItemList(updateReqVO.getId(), saleOutItems); + + // 3.1 更新销售订单的出库数量 + updateSaleOrderOutCount(updateObj.getOrderId()); + // 3.2 注意:如果销售订单编号变更了,需要更新“老”销售订单的出库数量 + if (ObjectUtil.notEqual(saleOut.getOrderId(), updateObj.getOrderId())) { + updateSaleOrderOutCount(saleOut.getOrderId()); + } + } + + private void calculateTotalPrice(ErpSaleOutDO saleOut, List saleOutItems) { + saleOut.setTotalCount(getSumValue(saleOutItems, ErpSaleOutItemDO::getCount, BigDecimal::add)); + saleOut.setTotalProductPrice(getSumValue(saleOutItems, ErpSaleOutItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); + saleOut.setTotalTaxPrice(getSumValue(saleOutItems, ErpSaleOutItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO)); + saleOut.setTotalPrice(saleOut.getTotalProductPrice().add(saleOut.getTotalTaxPrice())); + // 计算优惠价格 + if (saleOut.getDiscountPercent() == null) { + saleOut.setDiscountPercent(BigDecimal.ZERO); + } + saleOut.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleOut.getTotalPrice(), saleOut.getDiscountPercent())); + saleOut.setTotalPrice(saleOut.getTotalPrice().subtract(saleOut.getDiscountPrice().add(saleOut.getOtherPrice()))); + } + + private void updateSaleOrderOutCount(Long orderId) { + // 1.1 查询销售订单对应的销售出库单列表 + List saleOuts = saleOutMapper.selectListByOrderId(orderId); + // 1.2 查询对应的销售订单项的出库数量 + Map returnCountMap = saleOutItemMapper.selectOrderItemCountSumMapByOutIds( + convertList(saleOuts, ErpSaleOutDO::getId)); + // 2. 更新销售订单的出库数量 + saleOrderService.updateSaleOrderOutCount(orderId, returnCountMap); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateSaleOutStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpSaleOutDO saleOut = validateSaleOutExists(id); + // 1.2 校验状态 + if (saleOut.getStatus().equals(status)) { + throw exception(approve ? SALE_OUT_APPROVE_FAIL : SALE_OUT_PROCESS_FAIL); + } + // 1.3 校验已退款 + if (approve && saleOut.getReceiptPrice().compareTo(BigDecimal.ZERO) > 0) { + throw exception(SALE_OUT_PROCESS_FAIL_EXISTS_RECEIPT); + } + + // 2. 更新状态 + int updateCount = saleOutMapper.updateByIdAndStatus(id, saleOut.getStatus(), + new ErpSaleOutDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? SALE_OUT_APPROVE_FAIL : SALE_OUT_PROCESS_FAIL); + } + + // 3. 变更库存 + List saleOutItems = saleOutItemMapper.selectListByOutId(id); + Integer bizType = approve ? ErpStockRecordBizTypeEnum.SALE_OUT.getType() + : ErpStockRecordBizTypeEnum.SALE_OUT_CANCEL.getType(); + saleOutItems.forEach(saleOutItem -> { + BigDecimal count = approve ? saleOutItem.getCount().negate() : saleOutItem.getCount(); + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + saleOutItem.getProductId(), saleOutItem.getWarehouseId(), count, + bizType, saleOutItem.getOutId(), saleOutItem.getId(), saleOut.getNo())); + }); + } + + @Override + public void updateSaleInReceiptPrice(Long id, BigDecimal receiptPrice) { + ErpSaleOutDO saleOut = saleOutMapper.selectById(id); + if (saleOut.getReceiptPrice().equals(receiptPrice)) { + return; + } + if (receiptPrice.compareTo(saleOut.getTotalPrice()) > 0) { + throw exception(SALE_OUT_FAIL_RECEIPT_PRICE_EXCEED, receiptPrice, saleOut.getTotalPrice()); + } + saleOutMapper.updateById(new ErpSaleOutDO().setId(id).setReceiptPrice(receiptPrice)); + } + + private List validateSaleOutItems(List list) { + // 1. 校验产品存在 + List productList = productService.validProductList( + convertSet(list, ErpSaleOutSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + // 2. 转化为 ErpSaleOutItemDO 列表 + return convertList(list, o -> BeanUtils.toBean(o, ErpSaleOutItemDO.class, item -> { + item.setProductUnitId(productMap.get(item.getProductId()).getUnitId()); + item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())); + if (item.getTotalPrice() == null) { + return; + } + if (item.getTaxPercent() != null) { + item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent())); + } + })); + } + + private void updateSaleOutItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = saleOutItemMapper.selectListByOutId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setOutId(id)); + saleOutItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + saleOutItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + saleOutItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpSaleOutItemDO::getId)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteSaleOut(List ids) { + // 1. 校验不处于已审批 + List saleOuts = saleOutMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(saleOuts)) { + return; + } + saleOuts.forEach(saleOut -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(saleOut.getStatus())) { + throw exception(SALE_OUT_DELETE_FAIL_APPROVE, saleOut.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + saleOuts.forEach(saleOut -> { + // 2.1 删除订单 + saleOutMapper.deleteById(saleOut.getId()); + // 2.2 删除订单项 + saleOutItemMapper.deleteByOutId(saleOut.getId()); + + // 2.3 更新销售订单的出库数量 + updateSaleOrderOutCount(saleOut.getOrderId()); + }); + + } + + private ErpSaleOutDO validateSaleOutExists(Long id) { + ErpSaleOutDO saleOut = saleOutMapper.selectById(id); + if (saleOut == null) { + throw exception(SALE_OUT_NOT_EXISTS); + } + return saleOut; + } + + @Override + public ErpSaleOutDO getSaleOut(Long id) { + return saleOutMapper.selectById(id); + } + + @Override + public ErpSaleOutDO validateSaleOut(Long id) { + ErpSaleOutDO saleOut = validateSaleOutExists(id); + if (ObjectUtil.notEqual(saleOut.getStatus(), ErpAuditStatus.APPROVE.getStatus())) { + throw exception(SALE_OUT_NOT_APPROVE); + } + return saleOut; + } + + @Override + public PageResult getSaleOutPage(ErpSaleOutPageReqVO pageReqVO) { + return saleOutMapper.selectPage(pageReqVO); + } + + // ==================== 销售出库项 ==================== + + @Override + public List getSaleOutItemListByOutId(Long outId) { + return saleOutItemMapper.selectListByOutId(outId); + } + + @Override + public List getSaleOutItemListByOutIds(Collection outIds) { + if (CollUtil.isEmpty(outIds)) { + return Collections.emptyList(); + } + return saleOutItemMapper.selectListByOutIds(outIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnService.java new file mode 100644 index 0000000000..bc192a17ce --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnService.java @@ -0,0 +1,101 @@ +package cn.iocoder.yudao.module.erp.service.sale; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO; +import jakarta.validation.Valid; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; + +/** + * ERP 销售退货 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpSaleReturnService { + + /** + * 创建销售退货 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSaleReturn(@Valid ErpSaleReturnSaveReqVO createReqVO); + + /** + * 更新销售退货 + * + * @param updateReqVO 更新信息 + */ + void updateSaleReturn(@Valid ErpSaleReturnSaveReqVO updateReqVO); + + /** + * 更新销售退货的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateSaleReturnStatus(Long id, Integer status); + + /** + * 更新销售退货的退款金额 + * + * @param id 编号 + * @param refundPrice 退款金额 + */ + void updateSaleReturnRefundPrice(Long id, BigDecimal refundPrice); + + /** + * 删除销售退货 + * + * @param ids 编号数组 + */ + void deleteSaleReturn(List ids); + + /** + * 获得销售退货 + * + * @param id 编号 + * @return 销售退货 + */ + ErpSaleReturnDO getSaleReturn(Long id); + + /** + * 校验销售退货,已经审核通过 + * + * @param id 编号 + * @return 销售退货 + */ + ErpSaleReturnDO validateSaleReturn(Long id); + + /** + * 获得销售退货分页 + * + * @param pageReqVO 分页查询 + * @return 销售退货分页 + */ + PageResult getSaleReturnPage(ErpSaleReturnPageReqVO pageReqVO); + + // ==================== 销售退货项 ==================== + + /** + * 获得销售退货项列表 + * + * @param returnId 销售退货编号 + * @return 销售退货项列表 + */ + List getSaleReturnItemListByReturnId(Long returnId); + + /** + * 获得销售退货项 List + * + * @param returnIds 销售退货编号数组 + * @return 销售退货项 List + */ + List getSaleReturnItemListByReturnIds(Collection returnIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnServiceImpl.java new file mode 100644 index 0000000000..85d798e728 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnServiceImpl.java @@ -0,0 +1,316 @@ +package cn.iocoder.yudao.module.erp.service.sale; + +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.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO; +import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleReturnItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleReturnMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockRecordService; +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +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.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +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.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 销售退货 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpSaleReturnServiceImpl implements ErpSaleReturnService { + + @Resource + private ErpSaleReturnMapper saleReturnMapper; + @Resource + private ErpSaleReturnItemMapper saleReturnItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private ErpSaleOrderService saleOrderService; + @Resource + private ErpAccountService accountService; + @Resource + private ErpStockRecordService stockRecordService; + + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createSaleReturn(ErpSaleReturnSaveReqVO createReqVO) { + // 1.1 校验销售订单已审核 + ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(createReqVO.getOrderId()); + // 1.2 校验退货项的有效性 + List saleReturnItems = validateSaleReturnItems(createReqVO.getItems()); + // 1.3 校验结算账户 + accountService.validateAccount(createReqVO.getAccountId()); + // 1.4 校验销售人员 + if (createReqVO.getSaleUserId() != null) { + adminUserApi.validateUser(createReqVO.getSaleUserId()); + } + // 1.5 生成退货单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_RETURN_NO_PREFIX); + if (saleReturnMapper.selectByNo(no) != null) { + throw exception(SALE_RETURN_NO_EXISTS); + } + + // 2.1 插入退货 + ErpSaleReturnDO saleReturn = BeanUtils.toBean(createReqVO, ErpSaleReturnDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())) + .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId()); + calculateTotalPrice(saleReturn, saleReturnItems); + saleReturnMapper.insert(saleReturn); + // 2.2 插入退货项 + saleReturnItems.forEach(o -> o.setReturnId(saleReturn.getId())); + saleReturnItemMapper.insertBatch(saleReturnItems); + + // 3. 更新销售订单的退货数量 + updateSaleOrderReturnCount(createReqVO.getOrderId()); + return saleReturn.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateSaleReturn(ErpSaleReturnSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpSaleReturnDO saleReturn = validateSaleReturnExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(saleReturn.getStatus())) { + throw exception(SALE_RETURN_UPDATE_FAIL_APPROVE, saleReturn.getNo()); + } + // 1.2 校验销售订单已审核 + ErpSaleOrderDO saleOrder = saleOrderService.validateSaleOrder(updateReqVO.getOrderId()); + // 1.3 校验结算账户 + accountService.validateAccount(updateReqVO.getAccountId()); + // 1.4 校验销售人员 + if (updateReqVO.getSaleUserId() != null) { + adminUserApi.validateUser(updateReqVO.getSaleUserId()); + } + // 1.5 校验订单项的有效性 + List saleReturnItems = validateSaleReturnItems(updateReqVO.getItems()); + + // 2.1 更新退货 + ErpSaleReturnDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleReturnDO.class) + .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId()); + calculateTotalPrice(updateObj, saleReturnItems); + saleReturnMapper.updateById(updateObj); + // 2.2 更新退货项 + updateSaleReturnItemList(updateReqVO.getId(), saleReturnItems); + + // 3.1 更新销售订单的出库数量 + updateSaleOrderReturnCount(updateObj.getOrderId()); + // 3.2 注意:如果销售订单编号变更了,需要更新“老”销售订单的出库数量 + if (ObjectUtil.notEqual(saleReturn.getOrderId(), updateObj.getOrderId())) { + updateSaleOrderReturnCount(saleReturn.getOrderId()); + } + } + + private void calculateTotalPrice(ErpSaleReturnDO saleReturn, List saleReturnItems) { + saleReturn.setTotalCount(getSumValue(saleReturnItems, ErpSaleReturnItemDO::getCount, BigDecimal::add)); + saleReturn.setTotalProductPrice(getSumValue(saleReturnItems, ErpSaleReturnItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); + saleReturn.setTotalTaxPrice(getSumValue(saleReturnItems, ErpSaleReturnItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO)); + saleReturn.setTotalPrice(saleReturn.getTotalProductPrice().add(saleReturn.getTotalTaxPrice())); + // 计算优惠价格 + if (saleReturn.getDiscountPercent() == null) { + saleReturn.setDiscountPercent(BigDecimal.ZERO); + } + saleReturn.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleReturn.getTotalPrice(), saleReturn.getDiscountPercent())); + saleReturn.setTotalPrice(saleReturn.getTotalPrice().subtract(saleReturn.getDiscountPrice().add(saleReturn.getOtherPrice()))); + } + + private void updateSaleOrderReturnCount(Long orderId) { + // 1.1 查询销售订单对应的销售出库单列表 + List saleReturns = saleReturnMapper.selectListByOrderId(orderId); + // 1.2 查询对应的销售订单项的退货数量 + Map returnCountMap = saleReturnItemMapper.selectOrderItemCountSumMapByReturnIds( + convertList(saleReturns, ErpSaleReturnDO::getId)); + // 2. 更新销售订单的出库数量 + saleOrderService.updateSaleOrderReturnCount(orderId, returnCountMap); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateSaleReturnStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpSaleReturnDO saleReturn = validateSaleReturnExists(id); + // 1.2 校验状态 + if (saleReturn.getStatus().equals(status)) { + throw exception(approve ? SALE_RETURN_APPROVE_FAIL : SALE_RETURN_PROCESS_FAIL); + } + // 1.3 校验已退款 + if (approve && saleReturn.getRefundPrice().compareTo(BigDecimal.ZERO) > 0) { + throw exception(SALE_RETURN_PROCESS_FAIL_EXISTS_REFUND); + } + + // 2. 更新状态 + int updateCount = saleReturnMapper.updateByIdAndStatus(id, saleReturn.getStatus(), + new ErpSaleReturnDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? SALE_RETURN_APPROVE_FAIL : SALE_RETURN_PROCESS_FAIL); + } + + // 3. 变更库存 + List saleReturnItems = saleReturnItemMapper.selectListByReturnId(id); + Integer bizType = approve ? ErpStockRecordBizTypeEnum.SALE_RETURN.getType() + : ErpStockRecordBizTypeEnum.SALE_RETURN_CANCEL.getType(); + saleReturnItems.forEach(saleReturnItem -> { + BigDecimal count = approve ? saleReturnItem.getCount() : saleReturnItem.getCount().negate(); + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + saleReturnItem.getProductId(), saleReturnItem.getWarehouseId(), count, + bizType, saleReturnItem.getReturnId(), saleReturnItem.getId(), saleReturn.getNo())); + }); + } + + @Override + public void updateSaleReturnRefundPrice(Long id, BigDecimal refundPrice) { + ErpSaleReturnDO saleReturn = saleReturnMapper.selectById(id); + if (saleReturn.getRefundPrice().equals(refundPrice)) { + return; + } + if (refundPrice.compareTo(saleReturn.getTotalPrice()) > 0) { + throw exception(SALE_RETURN_FAIL_REFUND_PRICE_EXCEED, refundPrice, saleReturn.getTotalPrice()); + } + saleReturnMapper.updateById(new ErpSaleReturnDO().setId(id).setRefundPrice(refundPrice)); + } + + private List validateSaleReturnItems(List list) { + // 1. 校验产品存在 + List productList = productService.validProductList( + convertSet(list, ErpSaleReturnSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + // 2. 转化为 ErpSaleReturnItemDO 列表 + return convertList(list, o -> BeanUtils.toBean(o, ErpSaleReturnItemDO.class, item -> { + item.setProductUnitId(productMap.get(item.getProductId()).getUnitId()); + item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())); + if (item.getTotalPrice() == null) { + return; + } + if (item.getTaxPercent() != null) { + item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent())); + } + })); + } + + private void updateSaleReturnItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = saleReturnItemMapper.selectListByReturnId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setReturnId(id)); + saleReturnItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + saleReturnItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + saleReturnItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpSaleReturnItemDO::getId)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteSaleReturn(List ids) { + // 1. 校验不处于已审批 + List saleReturns = saleReturnMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(saleReturns)) { + return; + } + saleReturns.forEach(saleReturn -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(saleReturn.getStatus())) { + throw exception(SALE_RETURN_DELETE_FAIL_APPROVE, saleReturn.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + saleReturns.forEach(saleReturn -> { + // 2.1 删除订单 + saleReturnMapper.deleteById(saleReturn.getId()); + // 2.2 删除订单项 + saleReturnItemMapper.deleteByReturnId(saleReturn.getId()); + + // 2.3 更新销售订单的出库数量 + updateSaleOrderReturnCount(saleReturn.getOrderId()); + }); + + } + + private ErpSaleReturnDO validateSaleReturnExists(Long id) { + ErpSaleReturnDO saleReturn = saleReturnMapper.selectById(id); + if (saleReturn == null) { + throw exception(SALE_RETURN_NOT_EXISTS); + } + return saleReturn; + } + + @Override + public ErpSaleReturnDO getSaleReturn(Long id) { + return saleReturnMapper.selectById(id); + } + + @Override + public ErpSaleReturnDO validateSaleReturn(Long id) { + ErpSaleReturnDO saleReturn = validateSaleReturnExists(id); + if (ObjectUtil.notEqual(saleReturn.getStatus(), ErpAuditStatus.APPROVE.getStatus())) { + throw exception(SALE_RETURN_NOT_APPROVE); + } + return saleReturn; + } + + @Override + public PageResult getSaleReturnPage(ErpSaleReturnPageReqVO pageReqVO) { + return saleReturnMapper.selectPage(pageReqVO); + } + + // ==================== 销售退货项 ==================== + + @Override + public List getSaleReturnItemListByReturnId(Long returnId) { + return saleReturnItemMapper.selectListByReturnId(returnId); + } + + @Override + public List getSaleReturnItemListByReturnIds(Collection returnIds) { + if (CollUtil.isEmpty(returnIds)) { + return Collections.emptyList(); + } + return saleReturnItemMapper.selectListByReturnIds(returnIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java new file mode 100644 index 0000000000..2f8a0f9eef --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 库存盘点单 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpStockCheckService { + + /** + * 创建库存盘点单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStockCheck(@Valid ErpStockCheckSaveReqVO createReqVO); + + /** + * 更新库存盘点单 + * + * @param updateReqVO 更新信息 + */ + void updateStockCheck(@Valid ErpStockCheckSaveReqVO updateReqVO); + + /** + * 更新库存盘点单的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateStockCheckStatus(Long id, Integer status); + + /** + * 删除库存盘点单 + * + * @param ids 编号数组 + */ + void deleteStockCheck(List ids); + + /** + * 获得库存盘点单 + * + * @param id 编号 + * @return 库存盘点单 + */ + ErpStockCheckDO getStockCheck(Long id); + + /** + * 获得库存盘点单分页 + * + * @param pageReqVO 分页查询 + * @return 库存盘点单分页 + */ + PageResult getStockCheckPage(ErpStockCheckPageReqVO pageReqVO); + + // ==================== 盘点项 ==================== + + /** + * 获得库存盘点单项列表 + * + * @param checkId 盘点编号 + * @return 库存盘点单项列表 + */ + List getStockCheckItemListByCheckId(Long checkId); + + /** + * 获得库存盘点单项 List + * + * @param checkIds 盘点编号数组 + * @return 库存盘点单项 List + */ + List getStockCheckItemListByCheckIds(Collection checkIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java new file mode 100644 index 0000000000..49aca94d38 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java @@ -0,0 +1,232 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.hutool.core.collection.CollUtil; +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.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockCheckItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockCheckMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO; +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.Collection; +import java.util.Collections; +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.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 库存盘点单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpStockCheckServiceImpl implements ErpStockCheckService { + + @Resource + private ErpStockCheckMapper stockCheckMapper; + @Resource + private ErpStockCheckItemMapper stockCheckItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + private ErpWarehouseService warehouseService; + @Resource + private ErpStockRecordService stockRecordService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createStockCheck(ErpStockCheckSaveReqVO createReqVO) { + // 1.1 校验盘点项的有效性 + List stockCheckItems = validateStockCheckItems(createReqVO.getItems()); + // 1.2 生成盘点单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_CHECK_NO_PREFIX); + if (stockCheckMapper.selectByNo(no) != null) { + throw exception(STOCK_CHECK_NO_EXISTS); + } + + // 2.1 插入盘点单 + ErpStockCheckDO stockCheck = BeanUtils.toBean(createReqVO, ErpStockCheckDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()) + .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add)) + .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO))); + stockCheckMapper.insert(stockCheck); + // 2.2 插入盘点单项 + stockCheckItems.forEach(o -> o.setCheckId(stockCheck.getId())); + stockCheckItemMapper.insertBatch(stockCheckItems); + return stockCheck.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStockCheck(ErpStockCheckSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpStockCheckDO stockCheck = validateStockCheckExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) { + throw exception(STOCK_CHECK_UPDATE_FAIL_APPROVE, stockCheck.getNo()); + } + // 1.2 校验盘点项的有效性 + List stockCheckItems = validateStockCheckItems(updateReqVO.getItems()); + + // 2.1 更新盘点单 + ErpStockCheckDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockCheckDO.class, in -> in + .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add)) + .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add))); + stockCheckMapper.updateById(updateObj); + // 2.2 更新盘点单项 + updateStockCheckItemList(updateReqVO.getId(), stockCheckItems); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStockCheckStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpStockCheckDO stockCheck = validateStockCheckExists(id); + // 1.2 校验状态 + if (stockCheck.getStatus().equals(status)) { + throw exception(approve ? STOCK_CHECK_APPROVE_FAIL : STOCK_CHECK_PROCESS_FAIL); + } + + // 2. 更新状态 + int updateCount = stockCheckMapper.updateByIdAndStatus(id, stockCheck.getStatus(), + new ErpStockCheckDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? STOCK_CHECK_APPROVE_FAIL : STOCK_CHECK_PROCESS_FAIL); + } + + // 3. 变更库存 + List stockCheckItems = stockCheckItemMapper.selectListByCheckId(id); + stockCheckItems.forEach(stockCheckItem -> { + // 没有盈亏,不用出入库 + if (stockCheckItem.getCount().compareTo(BigDecimal.ZERO) == 0) { + return; + } + // 10;12;-2() + BigDecimal count = approve ? stockCheckItem.getCount(): stockCheckItem.getCount().negate(); + Integer bizType; + if (approve) { + bizType = count.compareTo(BigDecimal.ZERO) > 0 ? ErpStockRecordBizTypeEnum.CHECK_MORE_IN.getType() + : ErpStockRecordBizTypeEnum.CHECK_LESS_OUT.getType(); + } else { + bizType = count.compareTo(BigDecimal.ZERO) > 0 ? ErpStockRecordBizTypeEnum.CHECK_MORE_IN_CANCEL.getType() + : ErpStockRecordBizTypeEnum.CHECK_LESS_OUT_CANCEL.getType(); + } + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + stockCheckItem.getProductId(), stockCheckItem.getWarehouseId(), count, + bizType, stockCheckItem.getCheckId(), stockCheckItem.getId(), stockCheck.getNo())); + }); + } + + private List validateStockCheckItems(List list) { + // 1.1 校验产品存在 + List productList = productService.validProductList( + convertSet(list, ErpStockCheckSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + // 1.2 校验仓库存在 + warehouseService.validWarehouseList(convertSet(list, ErpStockCheckSaveReqVO.Item::getWarehouseId)); + // 2. 转化为 ErpStockCheckItemDO 列表 + return convertList(list, o -> BeanUtils.toBean(o, ErpStockCheckItemDO.class, item -> item + .setProductUnitId(productMap.get(item.getProductId()).getUnitId()) + .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())))); + } + + private void updateStockCheckItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = stockCheckItemMapper.selectListByCheckId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setCheckId(id)); + stockCheckItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + stockCheckItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + stockCheckItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpStockCheckItemDO::getId)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStockCheck(List ids) { + // 1. 校验不处于已审批 + List stockChecks = stockCheckMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(stockChecks)) { + return; + } + stockChecks.forEach(stockCheck -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) { + throw exception(STOCK_CHECK_DELETE_FAIL_APPROVE, stockCheck.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + stockChecks.forEach(stockCheck -> { + // 2.1 删除盘点单 + stockCheckMapper.deleteById(stockCheck.getId()); + // 2.2 删除盘点单项 + stockCheckItemMapper.deleteByCheckId(stockCheck.getId()); + }); + } + + private ErpStockCheckDO validateStockCheckExists(Long id) { + ErpStockCheckDO stockCheck = stockCheckMapper.selectById(id); + if (stockCheck == null) { + throw exception(STOCK_CHECK_NOT_EXISTS); + } + return stockCheck; + } + + @Override + public ErpStockCheckDO getStockCheck(Long id) { + return stockCheckMapper.selectById(id); + } + + @Override + public PageResult getStockCheckPage(ErpStockCheckPageReqVO pageReqVO) { + return stockCheckMapper.selectPage(pageReqVO); + } + + // ==================== 盘点项 ==================== + + @Override + public List getStockCheckItemListByCheckId(Long checkId) { + return stockCheckItemMapper.selectListByCheckId(checkId); + } + + @Override + public List getStockCheckItemListByCheckIds(Collection checkIds) { + if (CollUtil.isEmpty(checkIds)) { + return Collections.emptyList(); + } + return stockCheckItemMapper.selectListByCheckIds(checkIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInService.java new file mode 100644 index 0000000000..4e51545e4b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInService.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 其它入库单 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpStockInService { + + /** + * 创建其它入库单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStockIn(@Valid ErpStockInSaveReqVO createReqVO); + + /** + * 更新其它入库单 + * + * @param updateReqVO 更新信息 + */ + void updateStockIn(@Valid ErpStockInSaveReqVO updateReqVO); + + /** + * 更新其它入库单的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateStockInStatus(Long id, Integer status); + + /** + * 删除其它入库单 + * + * @param ids 编号数组 + */ + void deleteStockIn(List ids); + + /** + * 获得其它入库单 + * + * @param id 编号 + * @return 其它入库单 + */ + ErpStockInDO getStockIn(Long id); + + /** + * 获得其它入库单分页 + * + * @param pageReqVO 分页查询 + * @return 其它入库单分页 + */ + PageResult getStockInPage(ErpStockInPageReqVO pageReqVO); + + // ==================== 入库项 ==================== + + /** + * 获得其它入库单项列表 + * + * @param inId 入库编号 + * @return 其它入库单项列表 + */ + List getStockInItemListByInId(Long inId); + + /** + * 获得其它入库单项 List + * + * @param inIds 入库编号数组 + * @return 其它入库单项 List + */ + List getStockInItemListByInIds(Collection inIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java new file mode 100644 index 0000000000..213fca8b8f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java @@ -0,0 +1,228 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.hutool.core.collection.CollUtil; +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.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService; +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO; +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.Collection; +import java.util.Collections; +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.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 +/** + * ERP 其它入库单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpStockInServiceImpl implements ErpStockInService { + + @Resource + private ErpStockInMapper stockInMapper; + @Resource + private ErpStockInItemMapper stockInItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + private ErpWarehouseService warehouseService; + @Resource + private ErpSupplierService supplierService; + @Resource + private ErpStockRecordService stockRecordService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createStockIn(ErpStockInSaveReqVO createReqVO) { + // 1.1 校验入库项的有效性 + List stockInItems = validateStockInItems(createReqVO.getItems()); + // 1.2 校验供应商 + supplierService.validateSupplier(createReqVO.getSupplierId()); + // 1.3 生成入库单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_IN_NO_PREFIX); + if (stockInMapper.selectByNo(no) != null) { + throw exception(STOCK_IN_NO_EXISTS); + } + + // 2.1 插入入库单 + ErpStockInDO stockIn = BeanUtils.toBean(createReqVO, ErpStockInDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()) + .setTotalCount(getSumValue(stockInItems, ErpStockInItemDO::getCount, BigDecimal::add)) + .setTotalPrice(getSumValue(stockInItems, ErpStockInItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO))); + stockInMapper.insert(stockIn); + // 2.2 插入入库单项 + stockInItems.forEach(o -> o.setInId(stockIn.getId())); + stockInItemMapper.insertBatch(stockInItems); + return stockIn.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStockIn(ErpStockInSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpStockInDO stockIn = validateStockInExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(stockIn.getStatus())) { + throw exception(STOCK_IN_UPDATE_FAIL_APPROVE, stockIn.getNo()); + } + // 1.2 校验供应商 + supplierService.validateSupplier(updateReqVO.getSupplierId()); + // 1.3 校验入库项的有效性 + List stockInItems = validateStockInItems(updateReqVO.getItems()); + + // 2.1 更新入库单 + ErpStockInDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockInDO.class, in -> in + .setTotalCount(getSumValue(stockInItems, ErpStockInItemDO::getCount, BigDecimal::add)) + .setTotalPrice(getSumValue(stockInItems, ErpStockInItemDO::getTotalPrice, BigDecimal::add))); + stockInMapper.updateById(updateObj); + // 2.2 更新入库单项 + updateStockInItemList(updateReqVO.getId(), stockInItems); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStockInStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpStockInDO stockIn = validateStockInExists(id); + // 1.2 校验状态 + if (stockIn.getStatus().equals(status)) { + throw exception(approve ? STOCK_IN_APPROVE_FAIL : STOCK_IN_PROCESS_FAIL); + } + + // 2. 更新状态 + int updateCount = stockInMapper.updateByIdAndStatus(id, stockIn.getStatus(), + new ErpStockInDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? STOCK_IN_APPROVE_FAIL : STOCK_IN_PROCESS_FAIL); + } + + // 3. 变更库存 + List stockInItems = stockInItemMapper.selectListByInId(id); + Integer bizType = approve ? ErpStockRecordBizTypeEnum.OTHER_IN.getType() + : ErpStockRecordBizTypeEnum.OTHER_IN_CANCEL.getType(); + stockInItems.forEach(stockInItem -> { + BigDecimal count = approve ? stockInItem.getCount() : stockInItem.getCount().negate(); + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + stockInItem.getProductId(), stockInItem.getWarehouseId(), count, + bizType, stockInItem.getInId(), stockInItem.getId(), stockIn.getNo())); + }); + } + + private List validateStockInItems(List list) { + // 1.1 校验产品存在 + List productList = productService.validProductList( + convertSet(list, ErpStockInSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + // 1.2 校验仓库存在 + warehouseService.validWarehouseList(convertSet( + list, ErpStockInSaveReqVO.Item::getWarehouseId)); + // 2. 转化为 ErpStockInItemDO 列表 + return convertList(list, o -> BeanUtils.toBean(o, ErpStockInItemDO.class, item -> item + .setProductUnitId(productMap.get(item.getProductId()).getUnitId()) + .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())))); + } + + private void updateStockInItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = stockInItemMapper.selectListByInId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setInId(id)); + stockInItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + stockInItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + stockInItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpStockInItemDO::getId)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStockIn(List ids) { + // 1. 校验不处于已审批 + List stockIns = stockInMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(stockIns)) { + return; + } + stockIns.forEach(stockIn -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(stockIn.getStatus())) { + throw exception(STOCK_IN_DELETE_FAIL_APPROVE, stockIn.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + stockIns.forEach(stockIn -> { + // 2.1 删除入库单 + stockInMapper.deleteById(stockIn.getId()); + // 2.2 删除入库单项 + stockInItemMapper.deleteByInId(stockIn.getId()); + }); + } + + private ErpStockInDO validateStockInExists(Long id) { + ErpStockInDO stockIn = stockInMapper.selectById(id); + if (stockIn == null) { + throw exception(STOCK_IN_NOT_EXISTS); + } + return stockIn; + } + + @Override + public ErpStockInDO getStockIn(Long id) { + return stockInMapper.selectById(id); + } + + @Override + public PageResult getStockInPage(ErpStockInPageReqVO pageReqVO) { + return stockInMapper.selectPage(pageReqVO); + } + + // ==================== 入库项 ==================== + + @Override + public List getStockInItemListByInId(Long inId) { + return stockInItemMapper.selectListByInId(inId); + } + + @Override + public List getStockInItemListByInIds(Collection inIds) { + if (CollUtil.isEmpty(inIds)) { + return Collections.emptyList(); + } + return stockInItemMapper.selectListByInIds(inIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveService.java new file mode 100644 index 0000000000..8af13a6391 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveService.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMovePageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMoveSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 库存调拨单 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpStockMoveService { + + /** + * 创建库存调拨单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStockMove(@Valid ErpStockMoveSaveReqVO createReqVO); + + /** + * 更新库存调拨单 + * + * @param updateReqVO 更新信息 + */ + void updateStockMove(@Valid ErpStockMoveSaveReqVO updateReqVO); + + /** + * 更新库存调拨单的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateStockMoveStatus(Long id, Integer status); + + /** + * 删除库存调拨单 + * + * @param ids 编号数组 + */ + void deleteStockMove(List ids); + + /** + * 获得库存调拨单 + * + * @param id 编号 + * @return 库存调拨单 + */ + ErpStockMoveDO getStockMove(Long id); + + /** + * 获得库存调拨单分页 + * + * @param pageReqVO 分页查询 + * @return 库存调拨单分页 + */ + PageResult getStockMovePage(ErpStockMovePageReqVO pageReqVO); + + // ==================== 调拨项 ==================== + + /** + * 获得库存调拨单项列表 + * + * @param moveId 调拨编号 + * @return 库存调拨单项列表 + */ + List getStockMoveItemListByMoveId(Long moveId); + + /** + * 获得库存调拨单项 List + * + * @param moveIds 调拨编号数组 + * @return 库存调拨单项 List + */ + List getStockMoveItemListByMoveIds(Collection moveIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java new file mode 100644 index 0000000000..a0d8f79d4e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java @@ -0,0 +1,229 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.hutool.core.collection.CollUtil; +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.erp.controller.admin.stock.vo.move.ErpStockMovePageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMoveSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMoveItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMoveMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO; +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.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +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.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 库存调拨单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpStockMoveServiceImpl implements ErpStockMoveService { + + @Resource + private ErpStockMoveMapper stockMoveMapper; + @Resource + private ErpStockMoveItemMapper stockMoveItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + private ErpWarehouseService warehouseService; + @Resource + private ErpStockRecordService stockRecordService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createStockMove(ErpStockMoveSaveReqVO createReqVO) { + // 1.1 校验出库项的有效性 + List stockMoveItems = validateStockMoveItems(createReqVO.getItems()); + // 1.2 生成调拨单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_MOVE_NO_PREFIX); + if (stockMoveMapper.selectByNo(no) != null) { + throw exception(STOCK_MOVE_NO_EXISTS); + } + + // 2.1 插入出库单 + ErpStockMoveDO stockMove = BeanUtils.toBean(createReqVO, ErpStockMoveDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()) + .setTotalCount(getSumValue(stockMoveItems, ErpStockMoveItemDO::getCount, BigDecimal::add)) + .setTotalPrice(getSumValue(stockMoveItems, ErpStockMoveItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO))); + stockMoveMapper.insert(stockMove); + // 2.2 插入出库单项 + stockMoveItems.forEach(o -> o.setMoveId(stockMove.getId())); + stockMoveItemMapper.insertBatch(stockMoveItems); + return stockMove.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStockMove(ErpStockMoveSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpStockMoveDO stockMove = validateStockMoveExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(stockMove.getStatus())) { + throw exception(STOCK_MOVE_UPDATE_FAIL_APPROVE, stockMove.getNo()); + } + // 1.2 校验出库项的有效性 + List stockMoveItems = validateStockMoveItems(updateReqVO.getItems()); + + // 2.1 更新出库单 + ErpStockMoveDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockMoveDO.class, in -> in + .setTotalCount(getSumValue(stockMoveItems, ErpStockMoveItemDO::getCount, BigDecimal::add)) + .setTotalPrice(getSumValue(stockMoveItems, ErpStockMoveItemDO::getTotalPrice, BigDecimal::add))); + stockMoveMapper.updateById(updateObj); + // 2.2 更新出库单项 + updateStockMoveItemList(updateReqVO.getId(), stockMoveItems); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStockMoveStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpStockMoveDO stockMove = validateStockMoveExists(id); + // 1.2 校验状态 + if (stockMove.getStatus().equals(status)) { + throw exception(approve ? STOCK_MOVE_APPROVE_FAIL : STOCK_MOVE_PROCESS_FAIL); + } + + // 2. 更新状态 + int updateCount = stockMoveMapper.updateByIdAndStatus(id, stockMove.getStatus(), + new ErpStockMoveDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? STOCK_MOVE_APPROVE_FAIL : STOCK_MOVE_PROCESS_FAIL); + } + + // 3. 变更库存 + List stockMoveItems = stockMoveItemMapper.selectListByMoveId(id); + Integer fromBizType = approve ? ErpStockRecordBizTypeEnum.MOVE_OUT.getType() + : ErpStockRecordBizTypeEnum.MOVE_OUT_CANCEL.getType(); + Integer toBizType = approve ? ErpStockRecordBizTypeEnum.MOVE_IN.getType() + : ErpStockRecordBizTypeEnum.MOVE_IN_CANCEL.getType(); + stockMoveItems.forEach(stockMoveItem -> { + BigDecimal fromCount = approve ? stockMoveItem.getCount().negate() : stockMoveItem.getCount(); + BigDecimal toCount = approve ? stockMoveItem.getCount() : stockMoveItem.getCount().negate(); + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + stockMoveItem.getProductId(), stockMoveItem.getFromWarehouseId(), fromCount, + fromBizType, stockMoveItem.getMoveId(), stockMoveItem.getId(), stockMove.getNo())); + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + stockMoveItem.getProductId(), stockMoveItem.getToWarehouseId(), toCount, + toBizType, stockMoveItem.getMoveId(), stockMoveItem.getId(), stockMove.getNo())); + }); + } + + private List validateStockMoveItems(List list) { + // 1.1 校验产品存在 + List productList = productService.validProductList( + convertSet(list, ErpStockMoveSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + // 1.2 校验仓库存在 + warehouseService.validWarehouseList(convertSetByFlatMap(list, + item -> Stream.of(item.getFromWarehouseId(), item.getToWarehouseId()))); + // 2. 转化为 ErpStockMoveItemDO 列表 + return convertList(list, o -> BeanUtils.toBean(o, ErpStockMoveItemDO.class, item -> item + .setProductUnitId(productMap.get(item.getProductId()).getUnitId()) + .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())))); + } + + private void updateStockMoveItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = stockMoveItemMapper.selectListByMoveId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setMoveId(id)); + stockMoveItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + stockMoveItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + stockMoveItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpStockMoveItemDO::getId)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStockMove(List ids) { + // 1. 校验不处于已审批 + List stockMoves = stockMoveMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(stockMoves)) { + return; + } + stockMoves.forEach(stockMove -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(stockMove.getStatus())) { + throw exception(STOCK_MOVE_DELETE_FAIL_APPROVE, stockMove.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + stockMoves.forEach(stockMove -> { + // 2.1 删除出库单 + stockMoveMapper.deleteById(stockMove.getId()); + // 2.2 删除出库单项 + stockMoveItemMapper.deleteByMoveId(stockMove.getId()); + }); + } + + private ErpStockMoveDO validateStockMoveExists(Long id) { + ErpStockMoveDO stockMove = stockMoveMapper.selectById(id); + if (stockMove == null) { + throw exception(STOCK_MOVE_NOT_EXISTS); + } + return stockMove; + } + + @Override + public ErpStockMoveDO getStockMove(Long id) { + return stockMoveMapper.selectById(id); + } + + @Override + public PageResult getStockMovePage(ErpStockMovePageReqVO pageReqVO) { + return stockMoveMapper.selectPage(pageReqVO); + } + + // ==================== 出库项 ==================== + + @Override + public List getStockMoveItemListByMoveId(Long moveId) { + return stockMoveItemMapper.selectListByMoveId(moveId); + } + + @Override + public List getStockMoveItemListByMoveIds(Collection moveIds) { + if (CollUtil.isEmpty(moveIds)) { + return Collections.emptyList(); + } + return stockMoveItemMapper.selectListByMoveIds(moveIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutService.java new file mode 100644 index 0000000000..558fb69488 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutService.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 其它出库单 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpStockOutService { + + /** + * 创建其它出库单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStockOut(@Valid ErpStockOutSaveReqVO createReqVO); + + /** + * 更新其它出库单 + * + * @param updateReqVO 更新信息 + */ + void updateStockOut(@Valid ErpStockOutSaveReqVO updateReqVO); + + /** + * 更新其它出库单的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateStockOutStatus(Long id, Integer status); + + /** + * 删除其它出库单 + * + * @param ids 编号数组 + */ + void deleteStockOut(List ids); + + /** + * 获得其它出库单 + * + * @param id 编号 + * @return 其它出库单 + */ + ErpStockOutDO getStockOut(Long id); + + /** + * 获得其它出库单分页 + * + * @param pageReqVO 分页查询 + * @return 其它出库单分页 + */ + PageResult getStockOutPage(ErpStockOutPageReqVO pageReqVO); + + // ==================== 出库项 ==================== + + /** + * 获得其它出库单项列表 + * + * @param outId 出库编号 + * @return 其它出库单项列表 + */ + List getStockOutItemListByOutId(Long outId); + + /** + * 获得其它出库单项 List + * + * @param outIds 出库编号数组 + * @return 其它出库单项 List + */ + List getStockOutItemListByOutIds(Collection outIds); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutServiceImpl.java new file mode 100644 index 0000000000..9fa1924656 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutServiceImpl.java @@ -0,0 +1,228 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.hutool.core.collection.CollUtil; +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.erp.controller.admin.stock.vo.out.ErpStockOutPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockOutItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockOutMapper; +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService; +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO; +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.Collection; +import java.util.Collections; +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.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 其它出库单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpStockOutServiceImpl implements ErpStockOutService { + + @Resource + private ErpStockOutMapper stockOutMapper; + @Resource + private ErpStockOutItemMapper stockOutItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + private ErpWarehouseService warehouseService; + @Resource + private ErpCustomerService customerService; + @Resource + private ErpStockRecordService stockRecordService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createStockOut(ErpStockOutSaveReqVO createReqVO) { + // 1.1 校验出库项的有效性 + List stockOutItems = validateStockOutItems(createReqVO.getItems()); + // 1.2 校验客户 + customerService.validateCustomer(createReqVO.getCustomerId()); + // 1.3 生成出库单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_OUT_NO_PREFIX); + if (stockOutMapper.selectByNo(no) != null) { + throw exception(STOCK_OUT_NO_EXISTS); + } + + // 2.1 插入出库单 + ErpStockOutDO stockOut = BeanUtils.toBean(createReqVO, ErpStockOutDO.class, in -> in + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()) + .setTotalCount(getSumValue(stockOutItems, ErpStockOutItemDO::getCount, BigDecimal::add)) + .setTotalPrice(getSumValue(stockOutItems, ErpStockOutItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO))); + stockOutMapper.insert(stockOut); + // 2.2 插入出库单项 + stockOutItems.forEach(o -> o.setOutId(stockOut.getId())); + stockOutItemMapper.insertBatch(stockOutItems); + return stockOut.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStockOut(ErpStockOutSaveReqVO updateReqVO) { + // 1.1 校验存在 + ErpStockOutDO stockOut = validateStockOutExists(updateReqVO.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(stockOut.getStatus())) { + throw exception(STOCK_OUT_UPDATE_FAIL_APPROVE, stockOut.getNo()); + } + // 1.2 校验客户 + customerService.validateCustomer(updateReqVO.getCustomerId()); + // 1.3 校验出库项的有效性 + List stockOutItems = validateStockOutItems(updateReqVO.getItems()); + + // 2.1 更新出库单 + ErpStockOutDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockOutDO.class, in -> in + .setTotalCount(getSumValue(stockOutItems, ErpStockOutItemDO::getCount, BigDecimal::add)) + .setTotalPrice(getSumValue(stockOutItems, ErpStockOutItemDO::getTotalPrice, BigDecimal::add))); + stockOutMapper.updateById(updateObj); + // 2.2 更新出库单项 + updateStockOutItemList(updateReqVO.getId(), stockOutItems); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStockOutStatus(Long id, Integer status) { + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + // 1.1 校验存在 + ErpStockOutDO stockOut = validateStockOutExists(id); + // 1.2 校验状态 + if (stockOut.getStatus().equals(status)) { + throw exception(approve ? STOCK_OUT_APPROVE_FAIL : STOCK_OUT_PROCESS_FAIL); + } + + // 2. 更新状态 + int updateCount = stockOutMapper.updateByIdAndStatus(id, stockOut.getStatus(), + new ErpStockOutDO().setStatus(status)); + if (updateCount == 0) { + throw exception(approve ? STOCK_OUT_APPROVE_FAIL : STOCK_OUT_PROCESS_FAIL); + } + + // 3. 变更库存 + List stockOutItems = stockOutItemMapper.selectListByOutId(id); + Integer bizType = approve ? ErpStockRecordBizTypeEnum.OTHER_OUT.getType() + : ErpStockRecordBizTypeEnum.OTHER_OUT_CANCEL.getType(); + stockOutItems.forEach(stockOutItem -> { + BigDecimal count = approve ? stockOutItem.getCount().negate() : stockOutItem.getCount(); + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + stockOutItem.getProductId(), stockOutItem.getWarehouseId(), count, + bizType, stockOutItem.getOutId(), stockOutItem.getId(), stockOut.getNo())); + }); + } + + private List validateStockOutItems(List list) { + // 1.1 校验产品存在 + List productList = productService.validProductList( + convertSet(list, ErpStockOutSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + // 1.2 校验仓库存在 + warehouseService.validWarehouseList(convertSet(list, ErpStockOutSaveReqVO.Item::getWarehouseId)); + // 2. 转化为 ErpStockOutItemDO 列表 + return convertList(list, o -> BeanUtils.toBean(o, ErpStockOutItemDO.class, item -> item + .setProductUnitId(productMap.get(item.getProductId()).getUnitId()) + .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())))); + } + + private void updateStockOutItemList(Long id, List newList) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = stockOutItemMapper.selectListByOutId(id); + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setOutId(id)); + stockOutItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + stockOutItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + stockOutItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpStockOutItemDO::getId)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStockOut(List ids) { + // 1. 校验不处于已审批 + List stockOuts = stockOutMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(stockOuts)) { + return; + } + stockOuts.forEach(stockOut -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(stockOut.getStatus())) { + throw exception(STOCK_OUT_DELETE_FAIL_APPROVE, stockOut.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + stockOuts.forEach(stockOut -> { + // 2.1 删除出库单 + stockOutMapper.deleteById(stockOut.getId()); + // 2.2 删除出库单项 + stockOutItemMapper.deleteByOutId(stockOut.getId()); + }); + } + + private ErpStockOutDO validateStockOutExists(Long id) { + ErpStockOutDO stockOut = stockOutMapper.selectById(id); + if (stockOut == null) { + throw exception(STOCK_OUT_NOT_EXISTS); + } + return stockOut; + } + + @Override + public ErpStockOutDO getStockOut(Long id) { + return stockOutMapper.selectById(id); + } + + @Override + public PageResult getStockOutPage(ErpStockOutPageReqVO pageReqVO) { + return stockOutMapper.selectPage(pageReqVO); + } + + // ==================== 出库项 ==================== + + @Override + public List getStockOutItemListByOutId(Long outId) { + return stockOutItemMapper.selectListByOutId(outId); + } + + @Override + public List getStockOutItemListByOutIds(Collection outIds) { + if (CollUtil.isEmpty(outIds)) { + return Collections.emptyList(); + } + return stockOutItemMapper.selectListByOutIds(outIds); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordService.java new file mode 100644 index 0000000000..506b992a46 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordService.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO; +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO; +import jakarta.validation.Valid; + +/** + * ERP 产品库存明细 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpStockRecordService { + + /** + * 获得产品库存明细 + * + * @param id 编号 + * @return 产品库存明细 + */ + ErpStockRecordDO getStockRecord(Long id); + + /** + * 获得产品库存明细分页 + * + * @param pageReqVO 分页查询 + * @return 产品库存明细分页 + */ + PageResult getStockRecordPage(ErpStockRecordPageReqVO pageReqVO); + + /** + * 创建库存明细 + * + * @param createReqBO 创建库存明细 BO + */ + void createStockRecord(@Valid ErpStockRecordCreateReqBO createReqBO); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordServiceImpl.java new file mode 100644 index 0000000000..d84951c281 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordServiceImpl.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockRecordMapper; +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO; +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; + +/** + * ERP 产品库存明细 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpStockRecordServiceImpl implements ErpStockRecordService { + + @Resource + private ErpStockRecordMapper stockRecordMapper; + + @Resource + private ErpStockService stockService; + + @Override + public ErpStockRecordDO getStockRecord(Long id) { + return stockRecordMapper.selectById(id); + } + + @Override + public PageResult getStockRecordPage(ErpStockRecordPageReqVO pageReqVO) { + return stockRecordMapper.selectPage(pageReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void createStockRecord(ErpStockRecordCreateReqBO createReqBO) { + // 1. 更新库存 + BigDecimal totalCount = stockService.updateStockCountIncrement( + createReqBO.getProductId(), createReqBO.getWarehouseId(), createReqBO.getCount()); + // 2. 创建库存明细 + ErpStockRecordDO stockRecord = BeanUtils.toBean(createReqBO, ErpStockRecordDO.class) + .setTotalCount(totalCount); + stockRecordMapper.insert(stockRecord); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockService.java new file mode 100644 index 0000000000..63ad5fefa3 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockService.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; + +import java.math.BigDecimal; + +/** + * ERP 产品库存 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpStockService { + + /** + * 获得产品库存 + * + * @param id 编号 + * @return 库存 + */ + ErpStockDO getStock(Long id); + + /** + * 基于产品 + 仓库,获得产品库存 + * + * @param productId 产品编号 + * @param warehouseId 仓库编号 + * @return 产品库存 + */ + ErpStockDO getStock(Long productId, Long warehouseId); + + /** + * 获得产品库存数量 + * + * 如果不存在库存记录,则返回 0 + * + * @param productId 产品编号 + * @return 产品库存数量 + */ + BigDecimal getStockCount(Long productId); + + /** + * 获得产品库存分页 + * + * @param pageReqVO 分页查询 + * @return 库存分页 + */ + PageResult getStockPage(ErpStockPageReqVO pageReqVO); + + /** + * 增量更新产品库存数量 + * + * @param productId 产品编号 + * @param warehouseId 仓库编号 + * @param count 增量数量:正数,表示增加;负数,表示减少 + * @return 更新后的库存 + */ + BigDecimal updateStockCountIncrement(Long productId, Long warehouseId, BigDecimal count); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java new file mode 100644 index 0000000000..6541170471 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMapper; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE2; + +/** + * ERP 产品库存 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpStockServiceImpl implements ErpStockService { + + /** + * 允许库存为负数 + * + * TODO 芋艿:后续做成 db 配置 + */ + private static final Boolean NEGATIVE_STOCK_COUNT_ENABLE = false; + + @Resource + private ErpProductService productService; + @Resource + private ErpWarehouseService warehouseService; + + @Resource + private ErpStockMapper stockMapper; + + @Override + public ErpStockDO getStock(Long id) { + return stockMapper.selectById(id); + } + + @Override + public ErpStockDO getStock(Long productId, Long warehouseId) { + return stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId); + } + + @Override + public BigDecimal getStockCount(Long productId) { + BigDecimal count = stockMapper.selectSumByProductId(productId); + return count != null ? count : BigDecimal.ZERO; + } + + @Override + public PageResult getStockPage(ErpStockPageReqVO pageReqVO) { + return stockMapper.selectPage(pageReqVO); + } + + @Override + public BigDecimal updateStockCountIncrement(Long productId, Long warehouseId, BigDecimal count) { + // 1.1 查询当前库存 + ErpStockDO stock = stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId); + if (stock == null) { + stock = new ErpStockDO().setProductId(productId).setWarehouseId(warehouseId).setCount(BigDecimal.ZERO); + stockMapper.insert(stock); + } + // 1.2 校验库存是否充足 + if (!NEGATIVE_STOCK_COUNT_ENABLE && stock.getCount().add(count).compareTo(BigDecimal.ZERO) < 0) { + throw exception(STOCK_COUNT_NEGATIVE, productService.getProduct(productId).getName(), + warehouseService.getWarehouse(warehouseId).getName(), stock.getCount(), count); + } + + // 2. 库存变更 + int updateCount = stockMapper.updateCountIncrement(stock.getId(), count, NEGATIVE_STOCK_COUNT_ENABLE); + if (updateCount == 0) { + // 此时不好去查询最新库存,所以直接抛出该提示,不提供具体库存数字 + throw exception(STOCK_COUNT_NEGATIVE2, productService.getProduct(productId).getName(), + warehouseService.getWarehouse(warehouseId).getName()); + } + + // 3. 返回最新库存 + return stock.getCount().add(count); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseService.java new file mode 100644 index 0000000000..872a698de8 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseService.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.ErpWarehouseSaveReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +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; + +/** + * ERP 仓库 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpWarehouseService { + + /** + * 创建仓库 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createWarehouse(@Valid ErpWarehouseSaveReqVO createReqVO); + + /** + * 更新ERP 仓库 + * + * @param updateReqVO 更新信息 + */ + void updateWarehouse(@Valid ErpWarehouseSaveReqVO updateReqVO); + + /** + * 更新仓库默认状态 + * + * @param id 编号 + * @param defaultStatus 默认状态 + */ + void updateWarehouseDefaultStatus(Long id, Boolean defaultStatus); + + /** + * 删除仓库 + * + * @param id 编号 + */ + void deleteWarehouse(Long id); + + /** + * 获得仓库 + * + * @param id 编号 + * @return 仓库 + */ + ErpWarehouseDO getWarehouse(Long id); + + /** + * 校验仓库列表的有效性 + * + * @param ids 编号数组 + * @return 仓库列表 + */ + List validWarehouseList(Collection ids); + + /** + * 获得指定状态的仓库列表 + * + * @param status 状态 + * @return 仓库列表 + */ + List getWarehouseListByStatus(Integer status); + + /** + * 获得仓库列表 + * + * @param ids 编号数组 + * @return 仓库列表 + */ + List getWarehouseList(Collection ids); + + /** + * 获得仓库 Map + * + * @param ids 编号数组 + * @return 仓库 Map + */ + default Map getWarehouseMap(Collection ids) { + return convertMap(getWarehouseList(ids), ErpWarehouseDO::getId); + } + + /** + * 获得仓库分页 + * + * @param pageReqVO 分页查询 + * @return 仓库分页 + */ + PageResult getWarehousePage(ErpWarehousePageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseServiceImpl.java new file mode 100644 index 0000000000..fb4829146b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseServiceImpl.java @@ -0,0 +1,126 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.ErpWarehouseSaveReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpWarehouseMapper; +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.Collections; +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.convertMap; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +/** + * ERP 仓库 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpWarehouseServiceImpl implements ErpWarehouseService { + + @Resource + private ErpWarehouseMapper warehouseMapper; + + @Override + public Long createWarehouse(ErpWarehouseSaveReqVO createReqVO) { + // 插入 + ErpWarehouseDO warehouse = BeanUtils.toBean(createReqVO, ErpWarehouseDO.class); + warehouseMapper.insert(warehouse); + // 返回 + return warehouse.getId(); + } + + @Override + public void updateWarehouse(ErpWarehouseSaveReqVO updateReqVO) { + // 校验存在 + validateWarehouseExists(updateReqVO.getId()); + // 更新 + ErpWarehouseDO updateObj = BeanUtils.toBean(updateReqVO, ErpWarehouseDO.class); + warehouseMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateWarehouseDefaultStatus(Long id, Boolean defaultStatus) { + // 1. 校验存在 + validateWarehouseExists(id); + + // 2.1 如果开启,则需要关闭所有其它的默认 + if (defaultStatus) { + ErpWarehouseDO warehouse = warehouseMapper.selectByDefaultStatus(); + if (warehouse != null) { + warehouseMapper.updateById(new ErpWarehouseDO().setId(warehouse.getId()).setDefaultStatus(false)); + } + } + // 2.2 更新对应的默认状态 + warehouseMapper.updateById(new ErpWarehouseDO().setId(id).setDefaultStatus(defaultStatus)); + } + + @Override + public void deleteWarehouse(Long id) { + // 校验存在 + validateWarehouseExists(id); + // 删除 + warehouseMapper.deleteById(id); + } + + private void validateWarehouseExists(Long id) { + if (warehouseMapper.selectById(id) == null) { + throw exception(WAREHOUSE_NOT_EXISTS); + } + } + + @Override + public ErpWarehouseDO getWarehouse(Long id) { + return warehouseMapper.selectById(id); + } + + @Override + public List validWarehouseList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + List list = warehouseMapper.selectBatchIds(ids); + Map warehouseMap = convertMap(list, ErpWarehouseDO::getId); + for (Long id : ids) { + ErpWarehouseDO warehouse = warehouseMap.get(id); + if (warehouseMap.get(id) == null) { + throw exception(WAREHOUSE_NOT_EXISTS); + } + if (CommonStatusEnum.isDisable(warehouse.getStatus())) { + throw exception(WAREHOUSE_NOT_ENABLE, warehouse.getName()); + } + } + return list; + } + + @Override + public List getWarehouseListByStatus(Integer status) { + return warehouseMapper.selectListByStatus(status); + } + + @Override + public List getWarehouseList(Collection ids) { + return warehouseMapper.selectBatchIds(ids); + } + + @Override + public PageResult getWarehousePage(ErpWarehousePageReqVO pageReqVO) { + return warehouseMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/bo/ErpStockRecordCreateReqBO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/bo/ErpStockRecordCreateReqBO.java new file mode 100644 index 0000000000..7006f10b5f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/bo/ErpStockRecordCreateReqBO.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.erp.service.stock.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * 库存明细的创建 Request BO + * + * @author 芋道源码 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockRecordCreateReqBO { + + /** + * 产品编号 + */ + @NotNull(message = "产品编号不能为空") + private Long productId; + /** + * 仓库编号 + */ + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + /** + * 出入库数量 + * + * 正数,表示入库;负数,表示出库 + */ + @NotNull(message = "出入库数量不能为空") + private BigDecimal count; + + /** + * 业务类型 + */ + @NotNull(message = "业务类型不能为空") + private Integer bizType; + /** + * 业务编号 + */ + @NotNull(message = "业务编号不能为空") + private Long bizId; + /** + * 业务项编号 + */ + @NotNull(message = "业务项编号不能为空") + private Long bizItemId; + /** + * 业务单号 + */ + @NotNull(message = "业务单号不能为空") + private String bizNo; + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/finance/ErpAccountMapper.xml b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/finance/ErpAccountMapper.xml new file mode 100644 index 0000000000..a3cf6877b6 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/finance/ErpAccountMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/product/ErpProductUnitMapper.xml b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/product/ErpProductUnitMapper.xml new file mode 100644 index 0000000000..4666e48d42 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/product/ErpProductUnitMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/sale/ErpCustomerMapper.xml b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/sale/ErpCustomerMapper.xml new file mode 100644 index 0000000000..c012f4467c --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/sale/ErpCustomerMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/stock/ErpWarehouseMapper.xml b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/stock/ErpWarehouseMapper.xml new file mode 100644 index 0000000000..be2bc61cb6 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/stock/ErpWarehouseMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/supplier/ErpSupplierMapper.xml b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/supplier/ErpSupplierMapper.xml new file mode 100644 index 0000000000..54849af3f2 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/supplier/ErpSupplierMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java index ceb6a8d5c5..7e1dea2e89 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java @@ -8,14 +8,17 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; -import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileRespVO; -import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileUploadReqVO; +import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.*; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO; import cn.iocoder.yudao.module.infra.service.file.FileService; 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.annotation.security.PermitAll; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; @@ -23,12 +26,6 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import javax.annotation.Resource; -import javax.annotation.security.PermitAll; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; - import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Tag(name = "管理后台 - 文件存储") @@ -42,7 +39,7 @@ public class FileController { private FileService fileService; @PostMapping("/upload") - @Operation(summary = "上传文件") + @Operation(summary = "上传文件", description = "模式一:后端上传文件") @OperateLog(logArgs = false) // 上传文件,没有记录操作日志的必要 public CommonResult uploadFile(FileUploadReqVO uploadReqVO) throws Exception { MultipartFile file = uploadReqVO.getFile(); @@ -50,6 +47,18 @@ public class FileController { return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream()))); } + @GetMapping("/presigned-url") + @Operation(summary = "获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器") + public CommonResult getFilePresignedUrl(@RequestParam("path") String path) throws Exception { + return success(fileService.getFilePresignedUrl(path)); + } + + @PostMapping("/create") + @Operation(summary = "创建文件", description = "模式二:前端上传文件:配合 presigned-url 接口,记录上传了上传的文件") + public CommonResult createFile(@Valid @RequestBody FileCreateReqVO createReqVO) { + return success(fileService.createFile(createReqVO)); + } + @DeleteMapping("/delete") @Operation(summary = "删除文件") @Parameter(name = "id", description = "编号", required = true) @@ -62,7 +71,7 @@ public class FileController { @GetMapping("/{configId}/get/**") @PermitAll @Operation(summary = "下载文件") - @Parameter(name = "configId", description = "配置编号", required = true) + @Parameter(name = "configId", description = "配置编号", required = true) public void getFileContent(HttpServletRequest request, HttpServletResponse response, @PathVariable("configId") Long configId) throws Exception { diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileCreateReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileCreateReqVO.java new file mode 100644 index 0000000000..5daa3972e9 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileCreateReqVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 文件创建 Request VO") +@Data +public class FileCreateReqVO { + + @NotNull(message = "文件配置编号不能为空") + @Schema(description = "文件配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11") + private Long configId; + + @NotNull(message = "文件路径不能为空") + @Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao.jpg") + private String path; + + @NotNull(message = "原文件名不能为空") + @Schema(description = "原文件名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao.jpg") + private String name; + + @NotNull(message = "文件 URL不能为空") + @Schema(description = "文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg") + private String url; + + @Schema(description = "文件 MIME 类型", example = "application/octet-stream") + private String type; + + @Schema(description = "文件大小", example = "2048", requiredMode = Schema.RequiredMode.REQUIRED) + private Integer size; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java new file mode 100644 index 0000000000..926133ebce --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "管理后台 - 文件预签名地址 Response VO") +@Data +public class FilePresignedUrlRespVO { + + @Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11") + private Long configId; + + @Schema(description = "文件上传 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://s3.cn-south-1.qiniucs.com/ruoyi-vue-pro/758d3a5387507358c7236de4c8f96de1c7f5097ff6a7722b34772fb7b76b140f.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS%2F20240217%2Fcn-south-1%2Fs3%2Faws4_request&X-Amz-Date=20240217T123222Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=a29f33770ab79bf523ccd4034d0752ac545f3c2a3b17baa1eb4e280cfdccfda5") + private String uploadUrl; + + /** + * 为什么要返回 url 字段? + * + * 前端上传完文件后,需要使用该 URL 进行访问 + */ + @Schema(description = "文件访问 URL", requiredMode = Schema.RequiredMode.REQUIRED, + example = "https://test.yudao.iocoder.cn/758d3a5387507358c7236de4c8f96de1c7f5097ff6a7722b34772fb7b76b140f.png") + private String url; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java index 24baf4218c..3ca9a24198 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.infra.service.file; -import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO; +import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; +import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO; /** @@ -22,13 +24,21 @@ public interface FileService { /** * 保存文件,并返回文件的访问路径 * - * @param name 文件名称 - * @param path 文件路径 + * @param name 文件名称 + * @param path 文件路径 * @param content 文件内容 * @return 文件路径 */ String createFile(String name, String path, byte[] content); + /** + * 创建文件 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createFile(FileCreateReqVO createReqVO); + /** * 删除文件 * @@ -40,9 +50,17 @@ public interface FileService { * 获得文件内容 * * @param configId 配置编号 - * @param path 文件路径 + * @param path 文件路径 * @return 文件内容 */ byte[] getFileContent(Long configId, String path) throws Exception; + /** + * 生成文件预签名地址信息 + * + * @param path 文件路径 + * @return 预签名地址信息 + */ + FilePresignedUrlRespVO getFilePresignedUrl(String path) throws Exception; + } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java index 0732765d86..f7c4b0b8ea 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java @@ -4,16 +4,19 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.io.FileUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.file.core.client.FileClient; +import cn.iocoder.yudao.framework.file.core.client.s3.FilePresignedUrlRespDTO; import cn.iocoder.yudao.framework.file.core.utils.FileTypeUtils; +import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; +import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO; import cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper; +import jakarta.annotation.Resource; import lombok.SneakyThrows; import org.springframework.stereotype.Service; -import javax.annotation.Resource; - import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS; @@ -66,6 +69,13 @@ public class FileServiceImpl implements FileService { return url; } + @Override + public Long createFile(FileCreateReqVO createReqVO) { + FileDO file = BeanUtils.toBean(createReqVO, FileDO.class); + fileMapper.insert(file); + return file.getId(); + } + @Override public void deleteFile(Long id) throws Exception { // 校验存在 @@ -95,4 +105,12 @@ public class FileServiceImpl implements FileService { return client.getContent(path); } + @Override + public FilePresignedUrlRespVO getFilePresignedUrl(String path) throws Exception { + FileClient fileClient = fileConfigService.getMasterFileClient(); + FilePresignedUrlRespDTO presignedObjectUrl = fileClient.getPresignedObjectUrl(path); + return BeanUtils.toBean(presignedObjectUrl, FilePresignedUrlRespVO.class, + object -> object.setConfigId(fileClient.getId())); + } + } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm index 7c2ab277b0..c3044fb872 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm @@ -5,13 +5,12 @@ import request from '@/config/axios' export interface ${simpleClassName}VO { #foreach ($column in $columns) #if ($column.createOperation || $column.updateOperation) - // ${column.columnComment} #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal") - ${column.javaField}: number + ${column.javaField}: number // ${column.columnComment} #elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime") - ${column.javaField}: Date + ${column.javaField}: Date // ${column.columnComment} #else - ${column.javaField}: ${column.javaType.toLowerCase()} + ${column.javaField}: ${column.javaType.toLowerCase()} // ${column.columnComment} #end #end #end diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm index 14a72d2642..8e3596b4f6 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm @@ -244,7 +244,7 @@ const submitForm = async () => { // 提交请求 formLoading.value = true try { - const data = formData.value as unknown as ${simpleClassName}Api.${simpleClassName}VO + const data = formData.value as unknown as ${simpleClassName}VO ## 特殊:主子表专属逻辑 #if ( $table.templateType == 10 || $table.templateType == 12 ) #if ( $subTables && $subTables.size() > 0 ) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm index a76dbaa824..361d379fa5 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm @@ -259,8 +259,7 @@ const loading = ref(true) // 列表的加载中 const list = ref<${simpleClassName}VO[]>([]) // 列表的数据 ## 特殊:树表专属逻辑(树不需要分页接口) #if ( $table.templateType != 2 ) -// 列表的总页数 -const total = ref(0) +const total = ref(0) // 列表的总页数 #end const queryParams = reactive({ ## 特殊:树表专属逻辑(树不需要分页接口) diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java index 33516056e5..1b92e21f24 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java @@ -2,22 +2,30 @@ package cn.iocoder.yudao.module.product.service.comment; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.RandomUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO; import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentReplyReqVO; +import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentRespVO; import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO; +import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper; +import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum; import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; +import jakarta.annotation.Resource; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Lazy; -import javax.annotation.Resource; +import java.time.LocalDateTime; import java.util.Date; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -65,6 +73,88 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest { assertPojoEquals(productComment, comment); } + @Test + public void testGetCommentPage_success() { + // 准备参数 + ProductCommentDO productComment = randomPojo(ProductCommentDO.class, o -> { + o.setUserNickname("王二狗"); + o.setSpuName("感冒药"); + o.setScores(ProductCommentScoresEnum.FOUR.getScores()); + o.setReplyStatus(Boolean.TRUE); + o.setVisible(Boolean.TRUE); + o.setId(generateId()); + o.setUserId(generateId()); + o.setAnonymous(Boolean.TRUE); + o.setOrderId(generateId()); + o.setOrderItemId(generateId()); + o.setSpuId(generateId()); + o.setSkuId(generateId()); + o.setDescriptionScores(ProductCommentScoresEnum.FOUR.getScores()); + o.setBenefitScores(ProductCommentScoresEnum.FOUR.getScores()); + o.setContent("真好吃"); + o.setReplyUserId(generateId()); + o.setReplyContent("确实"); + o.setReplyTime(LocalDateTime.now()); + o.setCreateTime(LocalDateTime.now()); + o.setUpdateTime(LocalDateTime.now()); + }); + productCommentMapper.insert(productComment); + + Long orderId = productComment.getOrderId(); + Long spuId = productComment.getSpuId(); + + // 测试 userNickname 不匹配 + productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setUserNickname("王三").setScores(ProductCommentScoresEnum.ONE.getScores()))); + // 测试 orderId 不匹配 + productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setOrderId(generateId()))); + // 测试 spuId 不匹配 + productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setSpuId(generateId()))); + // 测试 spuName 不匹配 + productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setSpuName("感康"))); + // 测试 scores 不匹配 + productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setScores(ProductCommentScoresEnum.ONE.getScores()))); + // 测试 replied 不匹配 + productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setReplyStatus(Boolean.FALSE))); + // 测试 visible 不匹配 + productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setVisible(Boolean.FALSE))); + + // 调用 + ProductCommentPageReqVO productCommentPageReqVO = new ProductCommentPageReqVO(); + productCommentPageReqVO.setUserNickname("王二"); + productCommentPageReqVO.setOrderId(orderId); + productCommentPageReqVO.setSpuId(spuId); + productCommentPageReqVO.setSpuName("感冒药"); + productCommentPageReqVO.setScores(ProductCommentScoresEnum.FOUR.getScores()); + productCommentPageReqVO.setReplyStatus(Boolean.TRUE); + + PageResult commentPage = productCommentService.getCommentPage(productCommentPageReqVO); + PageResult result = BeanUtils.toBean(productCommentMapper.selectPage(productCommentPageReqVO), + ProductCommentRespVO.class); + assertEquals(result.getTotal(), commentPage.getTotal()); + + PageResult all = productCommentService.getCommentPage(new ProductCommentPageReqVO()); + assertEquals(8, all.getTotal()); + + // 测试获取所有商品分页评论数据 + PageResult result1 = productCommentService.getCommentPage(new AppCommentPageReqVO(), Boolean.TRUE); + assertEquals(7, result1.getTotal()); + + // 测试获取所有商品分页中评数据 + PageResult result2 = productCommentService.getCommentPage(new AppCommentPageReqVO().setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE); + assertEquals(2, result2.getTotal()); + + // 测试获取指定 spuId 商品分页中评数据 + PageResult result3 = productCommentService.getCommentPage(new AppCommentPageReqVO().setSpuId(spuId).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE); + assertEquals(2, result3.getTotal()); + + // 测试分页 tab count + //AppCommentStatisticsRespVO tabsCount = productCommentService.getCommentStatistics(spuId, Boolean.TRUE); + //assertEquals(4, tabsCount.getGoodCount()); + //assertEquals(2, tabsCount.getMediocreCount()); + //assertEquals(0, tabsCount.getNegativeCount()); + + } + @Test public void testUpdateCommentVisible_success() { // mock 测试 diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java index b986150936..31cef2e781 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java @@ -48,8 +48,11 @@ public class CouponDO extends BaseDO { * * 枚举 {@link CouponStatusEnum} */ + // TODO 芋艿:已作废? private Integer status; + // TODO 芋艿:发放 adminid? + // ========== 基本信息 END ========== // ========== 领取情况 BEGIN ========== diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java index 6cab9c58c3..ad4ebab9b5 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java @@ -45,6 +45,7 @@ public class CouponTemplateDO extends BaseDO { * * 枚举 {@link CommonStatusEnum} */ + // TODO 芋艿:要不要改成 3 个状态?? private Integer status; // ========== 基本信息 END ========== @@ -159,4 +160,7 @@ public class CouponTemplateDO extends BaseDO { private Integer useCount; // ========== 统计信息 END ========== + // TODO 芋艿:领取开始时间、领取结束时间 + + // TODO 芋艿:要不要加描述 } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java index f8059fbb7d..bdc3ba59bc 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java @@ -50,4 +50,12 @@ public interface DeptApi { return CollectionUtils.convertMap(list, DeptRespDTO::getId); } + /** + * 获得指定部门的所有子部门 + * + * @param id 部门编号 + * @return 子部门列表 + */ + List getChildDeptList(Long id); + } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java index b6cab30305..cea3c3663b 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -65,6 +66,17 @@ public interface AdminUserApi { return CollectionUtils.convertMap(users, AdminUserRespDTO::getId); } + /** + * 校验用户是否有效。如下情况,视为无效: + * 1. 用户编号不存在 + * 2. 用户被禁用 + * + * @param id 用户编号 + */ + default void validateUser(Long id) { + validateUserList(Collections.singleton(id)); + } + /** * 校验用户们是否有效。如下情况,视为无效: * 1. 用户编号不存在 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java index 8d11bdcf42..b040a75c77 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.service.dept.DeptService; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -38,4 +39,10 @@ public class DeptApiImpl implements DeptApi { deptService.validateDeptList(ids); } + @Override + public List getChildDeptList(Long id) { + List childDeptList = deptService.getChildDeptList(id); + return BeanUtils.toBean(childDeptList, DeptRespDTO.class); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java index 54aaa4dc7c..c26acb8ca1 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java @@ -44,6 +44,9 @@ public class AuthPermissionInfoRespVO { @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.jpg") private String avatar; + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long deptId; + } @Schema(description = "管理后台 - 登录用户的菜单信息 Response VO") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeSaveReqVO.java index 1618498a7d..78794ed712 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeSaveReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeSaveReqVO.java @@ -3,16 +3,15 @@ package cn.iocoder.yudao.module.system.controller.admin.notice.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; @Schema(description = "管理后台 - 通知公告创建/修改 Request VO") @Data public class NoticeSaveReqVO { - @Schema(description = "岗位公告编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") -// @NotNull(message = "岗位公告编号不能为空") + @Schema(description = "岗位公告编号", example = "1024") private Long id; @Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java index 8343547d9e..18463e7f66 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.system.convert.auth; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; @@ -29,7 +30,7 @@ public interface AuthConvert { default AuthPermissionInfoRespVO convert(AdminUserDO user, List roleList, List menuList) { return AuthPermissionInfoRespVO.builder() - .user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()).avatar(user.getAvatar()).build()) + .user(BeanUtils.toBean(user, AuthPermissionInfoRespVO.UserVO.class)) .roles(convertSet(roleList, RoleDO::getCode)) // 权限标识信息 .permissions(convertSet(menuList, MenuDO::getPermission)) diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index 25e684e6c3..bc850b5902 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -92,6 +92,13 @@ + + + + + + + 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 ab626b1e75..7aba89997e 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 @@ -35,6 +35,12 @@ public class DefaultController { "[商城系统 yudao-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]"); } + @RequestMapping("/admin-api/erp/**") + public CommonResult erp404() { + return CommonResult.error(NOT_IMPLEMENTED.getCode(), + "[ERP 模块 yudao-module-erp - 已禁用][参考 https://doc.iocoder.cn/erp/build/ 开启]"); + } + @RequestMapping(value = {"/admin-api/report/**"}) public CommonResult report404() { return CommonResult.error(NOT_IMPLEMENTED.getCode(), diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 311cd4eb85..5996782b0d 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -188,6 +188,7 @@ logging: cn.iocoder.yudao.module.promotion.dal.mysql: debug cn.iocoder.yudao.module.statistics.dal.mysql: debug cn.iocoder.yudao.module.crm.dal.mysql: debug + cn.iocoder.yudao.module.erp.dal.mysql: debug org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 debug: false