- 2.0.0-snapshot
+ 2.0.1-snapshot
1.5.0
3.2.1
diff --git a/yudao-gateway/src/main/resources/application.yaml b/yudao-gateway/src/main/resources/application.yaml
index 5992bc27bc..87d9a4a9b4 100644
--- a/yudao-gateway/src/main/resources/application.yaml
+++ b/yudao-gateway/src/main/resources/application.yaml
@@ -138,12 +138,13 @@ spring:
- Path=/admin-api/statistics/**
filters:
- RewritePath=/admin-api/statistics/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
- - id: statistics-app-api # 路由的编号
- uri: grayLb://statistics-server
+ ## erp-server 服务
+ - id: erp-admin-api # 路由的编号
+ uri: grayLb://erp-server
predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/app-api/statistics/**
+ - Path=/admin-api/erp/**
filters:
- - RewritePath=/app-api/statistics/v3/api-docs, /v3/api-docs
+ - RewritePath=/admin-api/erp/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
x-forwarded:
prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀
@@ -182,6 +183,9 @@ knife4j:
- name: statistics-server
service-name: statistics-server
url: /admin-api/statistics/v3/api-docs
+ - name: erp-server
+ service-name: erp-server
+ url: /admin-api/erp/v3/api-docs
--- #################### 芋道相关配置 ####################
diff --git a/yudao-module-erp/pom.xml b/yudao-module-erp/pom.xml
new file mode 100644
index 0000000000..bed9d7d513
--- /dev/null
+++ b/yudao-module-erp/pom.xml
@@ -0,0 +1,24 @@
+
+
+
+ cn.iocoder.cloud
+ 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..ef5394f107
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/pom.xml
@@ -0,0 +1,33 @@
+
+
+
+ cn.iocoder.cloud
+ yudao-module-erp
+ ${revision}
+
+ 4.0.0
+ yudao-module-erp-api
+ jar
+
+ ${project.artifactId}
+
+ erp 模块 API,暴露给其它模块调用
+
+
+
+
+ cn.iocoder.cloud
+ 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/ApiConstants.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ApiConstants.java
new file mode 100644
index 0000000000..1241dbc912
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ApiConstants.java
@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.erp.enums;
+
+import cn.iocoder.yudao.framework.common.enums.RpcConstants;
+
+/**
+ * API 相关的枚举
+ *
+ * @author 芋道源码
+ */
+public class ApiConstants {
+
+ /**
+ * 服务名
+ *
+ * 注意,需要保证和 spring.application.name 保持一致
+ */
+ public static final String NAME = "erp-server";
+
+ public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/erp";
+
+ public static final String VERSION = "1.0.0";
+
+}
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..a5b4270061
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-biz/pom.xml
@@ -0,0 +1,132 @@
+
+
+
+ cn.iocoder.cloud
+ yudao-module-erp
+ ${revision}
+
+ 4.0.0
+ yudao-module-erp-biz
+
+ ${project.artifactId}
+
+ erp 包下,企业资源管理(Enterprise Resource Planning)。
+ 例如说:采购、销售、库存、财务、产品等等
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-bootstrap
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-env
+
+
+
+
+ cn.iocoder.cloud
+ yudao-module-system-api
+ ${revision}
+
+
+ cn.iocoder.cloud
+ yudao-module-erp-api
+ ${revision}
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-biz-operatelog
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-biz-tenant
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-security
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-mybatis
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-redis
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-rpc
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-excel
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-biz-dict
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-monitor
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-test
+
+
+
+
+
+
+ ${project.artifactId}
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+
+ repackage
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/ErpServerApplication.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/ErpServerApplication.java
new file mode 100644
index 0000000000..07a3cd6e1c
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/ErpServerApplication.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.erp;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * 项目的启动类
+ *
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ *
+ * @author 芋道源码
+ */
+@SpringBootApplication
+public class ErpServerApplication {
+
+ public static void main(String[] args) {
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+
+ SpringApplication.run(ErpServerApplication.class, args);
+
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ }
+
+}
diff --git a/yudao-module-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