Commit d055b5e13f0beb0e006b28973fc7a1975b61e851
1 parent
2030a58d
fix: update development environment API configuration and enhance reimbursement export functionality
- Updated VUE_APP_BASE_API in the development environment to point to localhost for local testing. - Added a new endpoint to export approved reimbursement details for the current month, including associated purchase records. - Enhanced SQL queries for better performance and clarity in the reimbursement statistics service. - Improved documentation and comments for better understanding of the code logic.
Showing
42 changed files
with
6145 additions
and
31 deletions
Excel导入格式说明.md
0 → 100644
| 1 | +# Excel导入格式说明 | |
| 2 | + | |
| 3 | +## 📋 合作成本表导入格式 | |
| 4 | + | |
| 5 | +### 文件要求 | |
| 6 | +- 文件格式:`.xlsx` 或 `.xls` | |
| 7 | +- 第一行为标题行(必须) | |
| 8 | +- 从第二行开始为数据行 | |
| 9 | + | |
| 10 | +### Excel列格式 | |
| 11 | + | |
| 12 | +| 列序号 | 列名 | 是否必填 | 数据类型 | 说明 | 示例 | | |
| 13 | +|--------|------|---------|---------|------|------| | |
| 14 | +| A | 门店ID | ✅ 必填 | 文本 | 门店ID(关联lq_mdxx.F_Id) | `1649328471923847168` | | |
| 15 | +| B | 门店名称 | ⚪ 可选 | 文本 | 门店名称(如果为空,系统会根据门店ID自动查询) | `川师店` | | |
| 16 | +| C | 年份 | ✅ 必填 | 数字 | 年份(4位数字) | `2025` | | |
| 17 | +| D | 月份 | ✅ 必填 | 文本 | 月份(YYYYMM格式,6位) | `202501` | | |
| 18 | +| E | 合计金额 | ✅ 必填 | 数字 | 合计金额(支持小数) | `5000.00` | | |
| 19 | +| F | 备注说明 | ⚪ 可选 | 文本 | 备注说明 | `1月合作项目成本` | | |
| 20 | + | |
| 21 | +### Excel示例 | |
| 22 | + | |
| 23 | +``` | |
| 24 | +门店ID | 门店名称 | 年份 | 月份 | 合计金额 | 备注说明 | |
| 25 | +---------------------------|---------|------|--------|----------|------------------ | |
| 26 | +1649328471923847168 | 川师店 | 2025 | 202501 | 5000.00 | 1月合作项目成本 | |
| 27 | +1649328471923847169 | 春熙店 | 2025 | 202501 | 3000.00 | 1月合作项目成本 | |
| 28 | +``` | |
| 29 | + | |
| 30 | +### 注意事项 | |
| 31 | +1. **门店ID**:必须存在系统中,否则导入会失败 | |
| 32 | +2. **年份**:必须是4位数字,如:2025 | |
| 33 | +3. **月份**:必须是YYYYMM格式的6位字符串,如:202501(表示2025年1月) | |
| 34 | +4. **合计金额**:必须是数字,支持小数,如:5000.00 | |
| 35 | +5. **重复检查**:系统会检查相同门店、年份、月份是否已存在记录,如果存在会跳过并提示 | |
| 36 | +6. **空行处理**:如果门店ID和门店名称都为空,该行会被跳过 | |
| 37 | + | |
| 38 | +--- | |
| 39 | + | |
| 40 | +## 📋 店内支出表导入格式 | |
| 41 | + | |
| 42 | +### 文件要求 | |
| 43 | +- 文件格式:`.xlsx` 或 `.xls` | |
| 44 | +- 第一行为标题行(必须) | |
| 45 | +- 从第二行开始为数据行 | |
| 46 | + | |
| 47 | +### Excel列格式 | |
| 48 | + | |
| 49 | +| 列序号 | 列名 | 是否必填 | 数据类型 | 说明 | 示例 | | |
| 50 | +|--------|------|---------|---------|------|------| | |
| 51 | +| A | 门店ID | ✅ 必填 | 文本 | 门店ID(关联lq_mdxx.F_Id) | `1649328471923847168` | | |
| 52 | +| B | 门店名称 | ⚪ 可选 | 文本 | 门店名称(如果为空,系统会根据门店ID自动查询) | `川师店` | | |
| 53 | +| C | 支出分类ID | ⚪ 可选 | 文本 | 支出分类ID(关联lq_reimbursement_category.F_Id) | `xxx` | | |
| 54 | +| D | 支出分类名称 | ⚪ 可选 | 文本 | 支出分类名称 | `办公用品` | | |
| 55 | +| E | 支出日期 | ✅ 必填 | 日期 | 支出日期(格式:YYYY-MM-DD) | `2025-01-15` | | |
| 56 | +| F | 单价 | ⚪ 可选 | 数字 | 单价(支持小数) | `10.50` | | |
| 57 | +| G | 数量 | ⚪ 可选 | 数字 | 数量(整数) | `5` | | |
| 58 | +| H | 金额 | ✅ 必填 | 数字 | 金额(支持小数) | `52.50` | | |
| 59 | +| I | 备注说明 | ⚪ 可选 | 文本 | 备注说明 | `购买办公用品` | | |
| 60 | +| J | 关联报销申请ID | ⚪ 可选 | 文本 | 关联的报销申请ID | `xxx` | | |
| 61 | +| K | 关联购买记录ID | ⚪ 可选 | 文本 | 关联的购买记录ID | `xxx` | | |
| 62 | + | |
| 63 | +### Excel示例 | |
| 64 | + | |
| 65 | +``` | |
| 66 | +门店ID | 门店名称 | 支出分类ID | 支出分类名称 | 支出日期 | 单价 | 数量 | 金额 | 备注说明 | 关联报销申请ID | 关联购买记录ID | |
| 67 | +---------------------------|---------|-----------|------------|-----------|-------|------|--------|-------------|--------------|--------------- | |
| 68 | +1649328471923847168 | 川师店 | xxx | 办公用品 | 2025-01-15 | 10.50 | 5 | 52.50 | 购买办公用品 | xxx | xxx | |
| 69 | +1649328471923847168 | 川师店 | xxx | 水电费 | 2025-01-20 | 0.00 | 0 | 500.00 | 1月水电费 | | | |
| 70 | +``` | |
| 71 | + | |
| 72 | +### 注意事项 | |
| 73 | +1. **门店ID**:必须存在系统中,否则导入会失败 | |
| 74 | +2. **支出日期**:必须是日期格式,推荐格式:`YYYY-MM-DD`,如:`2025-01-15` | |
| 75 | +3. **金额**:必须是数字,支持小数,如:52.50 | |
| 76 | +4. **单价和数量**:如果为空,默认为0 | |
| 77 | +5. **空行处理**:如果门店ID和门店名称都为空,该行会被跳过 | |
| 78 | + | |
| 79 | +--- | |
| 80 | + | |
| 81 | +## 🔗 导入接口说明 | |
| 82 | + | |
| 83 | +### 合作成本表导入接口 | |
| 84 | +``` | |
| 85 | +POST /api/Extend/LqCooperationCost/Actions/Import | |
| 86 | +Content-Type: multipart/form-data | |
| 87 | + | |
| 88 | +参数: | |
| 89 | +- file: Excel文件(必填) | |
| 90 | +``` | |
| 91 | + | |
| 92 | +### 店内支出表导入接口 | |
| 93 | +``` | |
| 94 | +POST /api/Extend/LqStoreExpense/Actions/Import | |
| 95 | +Content-Type: multipart/form-data | |
| 96 | + | |
| 97 | +参数: | |
| 98 | +- file: Excel文件(必填) | |
| 99 | +``` | |
| 100 | + | |
| 101 | +### 返回结果 | |
| 102 | +```json | |
| 103 | +{ | |
| 104 | + "success": true, | |
| 105 | + "message": "导入完成:成功2条,失败0条", | |
| 106 | + "successCount": 2, | |
| 107 | + "failCount": 0, | |
| 108 | + "failMessages": [] | |
| 109 | +} | |
| 110 | +``` | |
| 111 | + | |
| 112 | +--- | |
| 113 | + | |
| 114 | +## 📝 测试数据准备 | |
| 115 | + | |
| 116 | +### 合作成本表测试数据(示例) | |
| 117 | + | |
| 118 | +请创建一个Excel文件,包含以下测试数据: | |
| 119 | + | |
| 120 | +| 门店ID | 门店名称 | 年份 | 月份 | 合计金额 | 备注说明 | | |
| 121 | +|--------|---------|------|------|----------|----------| | |
| 122 | +| (请填写实际门店ID) | 测试门店1 | 2025 | 202501 | 5000.00 | 测试数据1 | | |
| 123 | +| (请填写实际门店ID) | 测试门店2 | 2025 | 202501 | 3000.00 | 测试数据2 | | |
| 124 | +| (请填写实际门店ID) | 测试门店1 | 2025 | 202502 | 6000.00 | 测试数据3 | | |
| 125 | + | |
| 126 | +### 店内支出表测试数据(示例) | |
| 127 | + | |
| 128 | +请创建一个Excel文件,包含以下测试数据: | |
| 129 | + | |
| 130 | +| 门店ID | 门店名称 | 支出分类ID | 支出分类名称 | 支出日期 | 单价 | 数量 | 金额 | 备注说明 | 关联报销申请ID | 关联购买记录ID | | |
| 131 | +|--------|---------|-----------|------------|---------|------|------|------|----------|--------------|--------------| | |
| 132 | +| (请填写实际门店ID) | 测试门店1 | | 办公用品 | 2025-01-15 | 10.50 | 5 | 52.50 | 测试支出1 | | | | |
| 133 | +| (请填写实际门店ID) | 测试门店1 | | 水电费 | 2025-01-20 | 0 | 0 | 500.00 | 测试支出2 | | | | |
| 134 | +| (请填写实际门店ID) | 测试门店2 | | 交通费 | 2025-01-25 | 50.00 | 2 | 100.00 | 测试支出3 | | | | |
| 135 | + | |
| 136 | +--- | |
| 137 | + | |
| 138 | +## ⚠️ 重要提示 | |
| 139 | + | |
| 140 | +1. **门店ID获取**:可以通过门店列表接口获取实际的门店ID | |
| 141 | +2. **日期格式**:支出日期必须使用标准日期格式,推荐使用 `YYYY-MM-DD` | |
| 142 | +3. **月份格式**:合作成本表的月份必须是YYYYMM格式(6位字符串),如:202501 | |
| 143 | +4. **金额精度**:所有金额字段支持2位小数 | |
| 144 | +5. **重复数据**:合作成本表会检查相同门店、年份、月份是否已存在,如果存在会跳过 | |
| 145 | +6. **错误处理**:导入过程中如果有错误,会在返回结果中列出所有错误信息 | |
| 146 | + | ... | ... |
antis-ncc-admin/.env.development
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | VUE_CLI_BABEL_TRANSPILE_MODULES = true |
| 4 | 4 | # VUE_APP_BASE_API = 'https://erp.lvqianmeiye.com' |
| 5 | -VUE_APP_BASE_API = 'http://erp_test.lvqianmeiye.com' | |
| 6 | -# VUE_APP_BASE_API = 'http://localhost:2011' | |
| 5 | +# VUE_APP_BASE_API = 'http://erp_test.lvqianmeiye.com' | |
| 6 | +VUE_APP_BASE_API = 'http://localhost:2011' | |
| 7 | 7 | # VUE_APP_BASE_API = 'http://localhost:2011' |
| 8 | 8 | VUE_APP_BASE_WSS = 'ws://192.168.110.45:2011/websocket' | ... | ... |
excel/合作成本表.xlsx
0 → 100644
No preview for this file type
excel/店内支出表.xlsx
0 → 100644
No preview for this file type
excel/考勤统计导入模板_11月.xlsx
0 → 100644
No preview for this file type
netcore/src/Application/NCC.API/Files/TemporaryFile/合作成本表.xls
0 → 100644
No preview for this file type
netcore/src/Application/NCC.API/Files/TemporaryFile/店内支出表.xls
0 → 100644
No preview for this file type
netcore/src/Application/NCC.API/Files/TemporaryFile/报销表明细_2025年01月.xls
0 → 100644
No preview for this file type
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqCooperationCost/LqCooperationCostCrInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqCooperationCost | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 合作成本表创建输入参数 | |
| 7 | + /// </summary> | |
| 8 | + public class LqCooperationCostCrInput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 门店ID(关联lq_mdxx.F_Id) | |
| 12 | + /// </summary> | |
| 13 | + public string storeId { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 门店名称 | |
| 17 | + /// </summary> | |
| 18 | + public string storeName { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 年份 | |
| 22 | + /// </summary> | |
| 23 | + public int year { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 月份(YYYYMM格式) | |
| 27 | + /// </summary> | |
| 28 | + public string month { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 合计金额 | |
| 32 | + /// </summary> | |
| 33 | + public decimal totalAmount { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 备注说明 | |
| 37 | + /// </summary> | |
| 38 | + public string remarks { get; set; } | |
| 39 | + } | |
| 40 | +} | |
| 41 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqCooperationCost/LqCooperationCostInfoOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqCooperationCost | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 合作成本表详情输出参数 | |
| 7 | + /// </summary> | |
| 8 | + public class LqCooperationCostInfoOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 主键ID | |
| 12 | + /// </summary> | |
| 13 | + public string id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 门店ID | |
| 17 | + /// </summary> | |
| 18 | + public string storeId { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 门店名称 | |
| 22 | + /// </summary> | |
| 23 | + public string storeName { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 年份 | |
| 27 | + /// </summary> | |
| 28 | + public int year { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 月份(YYYYMM格式) | |
| 32 | + /// </summary> | |
| 33 | + public string month { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 合计金额 | |
| 37 | + /// </summary> | |
| 38 | + public decimal totalAmount { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 备注说明 | |
| 42 | + /// </summary> | |
| 43 | + public string remarks { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 创建人ID | |
| 47 | + /// </summary> | |
| 48 | + public string createUser { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 创建时间 | |
| 52 | + /// </summary> | |
| 53 | + public DateTime? createTime { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 更新人ID | |
| 57 | + /// </summary> | |
| 58 | + public string updateUser { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 更新时间 | |
| 62 | + /// </summary> | |
| 63 | + public DateTime? updateTime { get; set; } | |
| 64 | + } | |
| 65 | +} | |
| 66 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqCooperationCost/LqCooperationCostListOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqCooperationCost | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 合作成本表列表输出参数 | |
| 7 | + /// </summary> | |
| 8 | + public class LqCooperationCostListOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 主键ID | |
| 12 | + /// </summary> | |
| 13 | + public string id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 门店ID | |
| 17 | + /// </summary> | |
| 18 | + public string storeId { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 门店名称 | |
| 22 | + /// </summary> | |
| 23 | + public string storeName { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 年份 | |
| 27 | + /// </summary> | |
| 28 | + public int year { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 月份(YYYYMM格式) | |
| 32 | + /// </summary> | |
| 33 | + public string month { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 合计金额 | |
| 37 | + /// </summary> | |
| 38 | + public decimal totalAmount { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 备注说明 | |
| 42 | + /// </summary> | |
| 43 | + public string remarks { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 创建人ID | |
| 47 | + /// </summary> | |
| 48 | + public string createUser { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 创建时间 | |
| 52 | + /// </summary> | |
| 53 | + public DateTime? createTime { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 更新人ID | |
| 57 | + /// </summary> | |
| 58 | + public string updateUser { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 更新时间 | |
| 62 | + /// </summary> | |
| 63 | + public DateTime? updateTime { get; set; } | |
| 64 | + } | |
| 65 | +} | |
| 66 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqCooperationCost/LqCooperationCostListQueryInput.cs
0 → 100644
| 1 | +using NCC.Common.Filter; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqCooperationCost | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 合作成本表列表查询输入参数 | |
| 7 | + /// </summary> | |
| 8 | + public class LqCooperationCostListQueryInput : PageInputBase | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 选择导出数据key | |
| 12 | + /// </summary> | |
| 13 | + public string selectKey { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 数据类型(0:分页 1:全部) | |
| 17 | + /// </summary> | |
| 18 | + public int dataType { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 门店ID | |
| 22 | + /// </summary> | |
| 23 | + public string storeId { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 门店名称 | |
| 27 | + /// </summary> | |
| 28 | + public string storeName { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 年份 | |
| 32 | + /// </summary> | |
| 33 | + public int? year { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 月份(YYYYMM格式) | |
| 37 | + /// </summary> | |
| 38 | + public string month { get; set; } | |
| 39 | + } | |
| 40 | +} | |
| 41 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqCooperationCost/LqCooperationCostUpInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqCooperationCost | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 合作成本表更新输入参数 | |
| 7 | + /// </summary> | |
| 8 | + public class LqCooperationCostUpInput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 主键ID | |
| 12 | + /// </summary> | |
| 13 | + public string id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 门店ID(关联lq_mdxx.F_Id) | |
| 17 | + /// </summary> | |
| 18 | + public string storeId { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 门店名称 | |
| 22 | + /// </summary> | |
| 23 | + public string storeName { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 年份 | |
| 27 | + /// </summary> | |
| 28 | + public int year { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 月份(YYYYMM格式) | |
| 32 | + /// </summary> | |
| 33 | + public string month { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 合计金额 | |
| 37 | + /// </summary> | |
| 38 | + public decimal totalAmount { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 备注说明 | |
| 42 | + /// </summary> | |
| 43 | + public string remarks { get; set; } | |
| 44 | + } | |
| 45 | +} | |
| 46 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreExpense/LqStoreExpenseCrInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | +using NCC.Common.Model; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.Dto.LqStoreExpense | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 店内支出表创建输入参数 | |
| 9 | + /// </summary> | |
| 10 | + public class LqStoreExpenseCrInput | |
| 11 | + { | |
| 12 | + /// <summary> | |
| 13 | + /// 门店ID(关联lq_mdxx.F_Id) | |
| 14 | + /// </summary> | |
| 15 | + public string storeId { get; set; } | |
| 16 | + | |
| 17 | + /// <summary> | |
| 18 | + /// 门店名称 | |
| 19 | + /// </summary> | |
| 20 | + public string storeName { get; set; } | |
| 21 | + | |
| 22 | + /// <summary> | |
| 23 | + /// 支出分类ID(关联lq_reimbursement_category.F_Id) | |
| 24 | + /// </summary> | |
| 25 | + public string expenseCategoryId { get; set; } | |
| 26 | + | |
| 27 | + /// <summary> | |
| 28 | + /// 支出分类名称 | |
| 29 | + /// </summary> | |
| 30 | + public string expenseCategoryName { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 支出日期 | |
| 34 | + /// </summary> | |
| 35 | + public DateTime expenseDate { get; set; } | |
| 36 | + | |
| 37 | + /// <summary> | |
| 38 | + /// 单价 | |
| 39 | + /// </summary> | |
| 40 | + public decimal unitPrice { get; set; } | |
| 41 | + | |
| 42 | + /// <summary> | |
| 43 | + /// 数量 | |
| 44 | + /// </summary> | |
| 45 | + public int quantity { get; set; } | |
| 46 | + | |
| 47 | + /// <summary> | |
| 48 | + /// 金额 | |
| 49 | + /// </summary> | |
| 50 | + public decimal amount { get; set; } | |
| 51 | + | |
| 52 | + /// <summary> | |
| 53 | + /// 备注说明 | |
| 54 | + /// </summary> | |
| 55 | + public string memo { get; set; } | |
| 56 | + | |
| 57 | + /// <summary> | |
| 58 | + /// 附件 | |
| 59 | + /// </summary> | |
| 60 | + public List<FileControlsModel> attachment { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 关联报销申请ID(可选) | |
| 64 | + /// </summary> | |
| 65 | + public string relatedReimbursementId { get; set; } | |
| 66 | + | |
| 67 | + /// <summary> | |
| 68 | + /// 关联购买记录ID(可选) | |
| 69 | + /// </summary> | |
| 70 | + public string relatedPurchaseRecordId { get; set; } | |
| 71 | + } | |
| 72 | +} | |
| 73 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreExpense/LqStoreExpenseInfoOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | +using NCC.Common.Model; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.Dto.LqStoreExpense | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 店内支出表详情输出参数 | |
| 9 | + /// </summary> | |
| 10 | + public class LqStoreExpenseInfoOutput | |
| 11 | + { | |
| 12 | + /// <summary> | |
| 13 | + /// 主键ID | |
| 14 | + /// </summary> | |
| 15 | + public string id { get; set; } | |
| 16 | + | |
| 17 | + /// <summary> | |
| 18 | + /// 门店ID | |
| 19 | + /// </summary> | |
| 20 | + public string storeId { get; set; } | |
| 21 | + | |
| 22 | + /// <summary> | |
| 23 | + /// 门店名称 | |
| 24 | + /// </summary> | |
| 25 | + public string storeName { get; set; } | |
| 26 | + | |
| 27 | + /// <summary> | |
| 28 | + /// 支出分类ID | |
| 29 | + /// </summary> | |
| 30 | + public string expenseCategoryId { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 支出分类名称 | |
| 34 | + /// </summary> | |
| 35 | + public string expenseCategoryName { get; set; } | |
| 36 | + | |
| 37 | + /// <summary> | |
| 38 | + /// 支出日期 | |
| 39 | + /// </summary> | |
| 40 | + public DateTime expenseDate { get; set; } | |
| 41 | + | |
| 42 | + /// <summary> | |
| 43 | + /// 单价 | |
| 44 | + /// </summary> | |
| 45 | + public decimal unitPrice { get; set; } | |
| 46 | + | |
| 47 | + /// <summary> | |
| 48 | + /// 数量 | |
| 49 | + /// </summary> | |
| 50 | + public int quantity { get; set; } | |
| 51 | + | |
| 52 | + /// <summary> | |
| 53 | + /// 金额 | |
| 54 | + /// </summary> | |
| 55 | + public decimal amount { get; set; } | |
| 56 | + | |
| 57 | + /// <summary> | |
| 58 | + /// 备注说明 | |
| 59 | + /// </summary> | |
| 60 | + public string memo { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 附件 | |
| 64 | + /// </summary> | |
| 65 | + public List<FileControlsModel> attachment { get; set; } | |
| 66 | + | |
| 67 | + /// <summary> | |
| 68 | + /// 关联报销申请ID | |
| 69 | + /// </summary> | |
| 70 | + public string relatedReimbursementId { get; set; } | |
| 71 | + | |
| 72 | + /// <summary> | |
| 73 | + /// 关联购买记录ID | |
| 74 | + /// </summary> | |
| 75 | + public string relatedPurchaseRecordId { get; set; } | |
| 76 | + | |
| 77 | + /// <summary> | |
| 78 | + /// 创建人ID | |
| 79 | + /// </summary> | |
| 80 | + public string createUser { get; set; } | |
| 81 | + | |
| 82 | + /// <summary> | |
| 83 | + /// 创建时间 | |
| 84 | + /// </summary> | |
| 85 | + public DateTime? createTime { get; set; } | |
| 86 | + | |
| 87 | + /// <summary> | |
| 88 | + /// 更新人ID | |
| 89 | + /// </summary> | |
| 90 | + public string updateUser { get; set; } | |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 更新时间 | |
| 94 | + /// </summary> | |
| 95 | + public DateTime? updateTime { get; set; } | |
| 96 | + } | |
| 97 | +} | |
| 98 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreExpense/LqStoreExpenseListOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqStoreExpense | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 店内支出表列表输出参数 | |
| 7 | + /// </summary> | |
| 8 | + public class LqStoreExpenseListOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 主键ID | |
| 12 | + /// </summary> | |
| 13 | + public string id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 门店ID | |
| 17 | + /// </summary> | |
| 18 | + public string storeId { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 门店名称 | |
| 22 | + /// </summary> | |
| 23 | + public string storeName { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 支出分类ID | |
| 27 | + /// </summary> | |
| 28 | + public string expenseCategoryId { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 支出分类名称 | |
| 32 | + /// </summary> | |
| 33 | + public string expenseCategoryName { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 支出日期 | |
| 37 | + /// </summary> | |
| 38 | + public DateTime expenseDate { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 单价 | |
| 42 | + /// </summary> | |
| 43 | + public decimal unitPrice { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 数量 | |
| 47 | + /// </summary> | |
| 48 | + public int quantity { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 金额 | |
| 52 | + /// </summary> | |
| 53 | + public decimal amount { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 备注说明 | |
| 57 | + /// </summary> | |
| 58 | + public string memo { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 关联报销申请ID | |
| 62 | + /// </summary> | |
| 63 | + public string relatedReimbursementId { get; set; } | |
| 64 | + | |
| 65 | + /// <summary> | |
| 66 | + /// 关联购买记录ID | |
| 67 | + /// </summary> | |
| 68 | + public string relatedPurchaseRecordId { get; set; } | |
| 69 | + | |
| 70 | + /// <summary> | |
| 71 | + /// 创建人ID | |
| 72 | + /// </summary> | |
| 73 | + public string createUser { get; set; } | |
| 74 | + | |
| 75 | + /// <summary> | |
| 76 | + /// 创建时间 | |
| 77 | + /// </summary> | |
| 78 | + public DateTime? createTime { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 更新人ID | |
| 82 | + /// </summary> | |
| 83 | + public string updateUser { get; set; } | |
| 84 | + | |
| 85 | + /// <summary> | |
| 86 | + /// 更新时间 | |
| 87 | + /// </summary> | |
| 88 | + public DateTime? updateTime { get; set; } | |
| 89 | + } | |
| 90 | +} | |
| 91 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreExpense/LqStoreExpenseListQueryInput.cs
0 → 100644
| 1 | +using NCC.Common.Filter; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqStoreExpense | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 店内支出表列表查询输入参数 | |
| 7 | + /// </summary> | |
| 8 | + public class LqStoreExpenseListQueryInput : PageInputBase | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 选择导出数据key | |
| 12 | + /// </summary> | |
| 13 | + public string selectKey { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 数据类型(0:分页 1:全部) | |
| 17 | + /// </summary> | |
| 18 | + public int dataType { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 门店ID | |
| 22 | + /// </summary> | |
| 23 | + public string storeId { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 门店名称 | |
| 27 | + /// </summary> | |
| 28 | + public string storeName { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 支出分类ID | |
| 32 | + /// </summary> | |
| 33 | + public string expenseCategoryId { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 支出日期(开始) | |
| 37 | + /// </summary> | |
| 38 | + public string expenseDateStart { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 支出日期(结束) | |
| 42 | + /// </summary> | |
| 43 | + public string expenseDateEnd { get; set; } | |
| 44 | + } | |
| 45 | +} | |
| 46 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreExpense/LqStoreExpenseUpInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | +using NCC.Common.Model; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.Dto.LqStoreExpense | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 店内支出表更新输入参数 | |
| 9 | + /// </summary> | |
| 10 | + public class LqStoreExpenseUpInput | |
| 11 | + { | |
| 12 | + /// <summary> | |
| 13 | + /// 主键ID | |
| 14 | + /// </summary> | |
| 15 | + public string id { get; set; } | |
| 16 | + | |
| 17 | + /// <summary> | |
| 18 | + /// 门店ID(关联lq_mdxx.F_Id) | |
| 19 | + /// </summary> | |
| 20 | + public string storeId { get; set; } | |
| 21 | + | |
| 22 | + /// <summary> | |
| 23 | + /// 门店名称 | |
| 24 | + /// </summary> | |
| 25 | + public string storeName { get; set; } | |
| 26 | + | |
| 27 | + /// <summary> | |
| 28 | + /// 支出分类ID(关联lq_reimbursement_category.F_Id) | |
| 29 | + /// </summary> | |
| 30 | + public string expenseCategoryId { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 支出分类名称 | |
| 34 | + /// </summary> | |
| 35 | + public string expenseCategoryName { get; set; } | |
| 36 | + | |
| 37 | + /// <summary> | |
| 38 | + /// 支出日期 | |
| 39 | + /// </summary> | |
| 40 | + public DateTime expenseDate { get; set; } | |
| 41 | + | |
| 42 | + /// <summary> | |
| 43 | + /// 单价 | |
| 44 | + /// </summary> | |
| 45 | + public decimal unitPrice { get; set; } | |
| 46 | + | |
| 47 | + /// <summary> | |
| 48 | + /// 数量 | |
| 49 | + /// </summary> | |
| 50 | + public int quantity { get; set; } | |
| 51 | + | |
| 52 | + /// <summary> | |
| 53 | + /// 金额 | |
| 54 | + /// </summary> | |
| 55 | + public decimal amount { get; set; } | |
| 56 | + | |
| 57 | + /// <summary> | |
| 58 | + /// 备注说明 | |
| 59 | + /// </summary> | |
| 60 | + public string memo { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 附件 | |
| 64 | + /// </summary> | |
| 65 | + public List<FileControlsModel> attachment { get; set; } | |
| 66 | + | |
| 67 | + /// <summary> | |
| 68 | + /// 关联报销申请ID(可选) | |
| 69 | + /// </summary> | |
| 70 | + public string relatedReimbursementId { get; set; } | |
| 71 | + | |
| 72 | + /// <summary> | |
| 73 | + /// 关联购买记录ID(可选) | |
| 74 | + /// </summary> | |
| 75 | + public string relatedPurchaseRecordId { get; set; } | |
| 76 | + } | |
| 77 | +} | |
| 78 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreManagerSalary/StoreManagerSalaryInput.cs
0 → 100644
| 1 | +using NCC.Common.Filter; | |
| 2 | +using System; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.LqStoreManagerSalary | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 店长工资查询参数 | |
| 8 | + /// </summary> | |
| 9 | + public class StoreManagerSalaryInput : PageInputBase | |
| 10 | + { | |
| 11 | + /// <summary> | |
| 12 | + /// 年份 | |
| 13 | + /// </summary> | |
| 14 | + public int Year { get; set; } | |
| 15 | + | |
| 16 | + /// <summary> | |
| 17 | + /// 月份 | |
| 18 | + /// </summary> | |
| 19 | + public int Month { get; set; } | |
| 20 | + | |
| 21 | + /// <summary> | |
| 22 | + /// 门店ID(可选,用于筛选特定门店) | |
| 23 | + /// </summary> | |
| 24 | + public string StoreId { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 员工姓名/账号(可选,用于模糊搜索) | |
| 28 | + /// </summary> | |
| 29 | + public string Keyword { get; set; } | |
| 30 | + } | |
| 31 | +} | |
| 32 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStoreManagerSalary/StoreManagerSalaryOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqStoreManagerSalary | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 店长工资输出 | |
| 7 | + /// </summary> | |
| 8 | + public class StoreManagerSalaryOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 主键ID | |
| 12 | + /// </summary> | |
| 13 | + public string Id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 门店名称 | |
| 17 | + /// </summary> | |
| 18 | + public string StoreName { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 员工姓名 | |
| 22 | + /// </summary> | |
| 23 | + public string EmployeeName { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 岗位 | |
| 27 | + /// </summary> | |
| 28 | + public string Position { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 门店总业绩 | |
| 32 | + /// </summary> | |
| 33 | + public decimal StoreTotalPerformance { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 门店开单业绩 | |
| 37 | + /// </summary> | |
| 38 | + public decimal StoreBillingPerformance { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 门店退卡业绩 | |
| 42 | + /// </summary> | |
| 43 | + public decimal StoreRefundPerformance { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 门店生命线 | |
| 47 | + /// </summary> | |
| 48 | + public decimal StoreLifeline { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 业绩完成率 | |
| 52 | + /// </summary> | |
| 53 | + public decimal PerformanceCompletionRate { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 业绩是否达标 | |
| 57 | + /// </summary> | |
| 58 | + public string PerformanceReached { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 人头是否达标 | |
| 62 | + /// </summary> | |
| 63 | + public string HeadCountReached { get; set; } | |
| 64 | + | |
| 65 | + /// <summary> | |
| 66 | + /// 消耗是否达标 | |
| 67 | + /// </summary> | |
| 68 | + public string ConsumeReached { get; set; } | |
| 69 | + | |
| 70 | + /// <summary> | |
| 71 | + /// 考核扣款金额 | |
| 72 | + /// </summary> | |
| 73 | + public decimal AssessmentDeduction { get; set; } | |
| 74 | + | |
| 75 | + /// <summary> | |
| 76 | + /// 未达标指标数量 | |
| 77 | + /// </summary> | |
| 78 | + public int UnreachedIndicatorCount { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 进店消耗人数 | |
| 82 | + /// </summary> | |
| 83 | + public int HeadCount { get; set; } | |
| 84 | + | |
| 85 | + /// <summary> | |
| 86 | + /// 目标人头数 | |
| 87 | + /// </summary> | |
| 88 | + public decimal TargetHeadCount { get; set; } | |
| 89 | + | |
| 90 | + /// <summary> | |
| 91 | + /// 门店消耗金额 | |
| 92 | + /// </summary> | |
| 93 | + public decimal StoreConsume { get; set; } | |
| 94 | + | |
| 95 | + /// <summary> | |
| 96 | + /// 目标消耗金额 | |
| 97 | + /// </summary> | |
| 98 | + public decimal TargetConsume { get; set; } | |
| 99 | + | |
| 100 | + /// <summary> | |
| 101 | + /// 销售业绩(开单业绩-退款业绩) | |
| 102 | + /// </summary> | |
| 103 | + public decimal SalesPerformance { get; set; } | |
| 104 | + | |
| 105 | + /// <summary> | |
| 106 | + /// 产品物料(仓库领用金额) | |
| 107 | + /// </summary> | |
| 108 | + public decimal ProductMaterial { get; set; } | |
| 109 | + | |
| 110 | + /// <summary> | |
| 111 | + /// 合作项目成本 | |
| 112 | + /// </summary> | |
| 113 | + public decimal CooperationCost { get; set; } | |
| 114 | + | |
| 115 | + /// <summary> | |
| 116 | + /// 店内支出 | |
| 117 | + /// </summary> | |
| 118 | + public decimal StoreExpense { get; set; } | |
| 119 | + | |
| 120 | + /// <summary> | |
| 121 | + /// 洗毛巾费用 | |
| 122 | + /// </summary> | |
| 123 | + public decimal LaundryCost { get; set; } | |
| 124 | + | |
| 125 | + /// <summary> | |
| 126 | + /// 毛利(销售业绩-产品物料-合作项目成本-店内支出-洗毛巾) | |
| 127 | + /// </summary> | |
| 128 | + public decimal GrossProfit { get; set; } | |
| 129 | + | |
| 130 | + /// <summary> | |
| 131 | + /// 提成比例 | |
| 132 | + /// </summary> | |
| 133 | + public decimal CommissionRate { get; set; } | |
| 134 | + | |
| 135 | + /// <summary> | |
| 136 | + /// 提成金额(基于毛利) | |
| 137 | + /// </summary> | |
| 138 | + public decimal CommissionAmount { get; set; } | |
| 139 | + | |
| 140 | + /// <summary> | |
| 141 | + /// 底薪金额(固定4000元) | |
| 142 | + /// </summary> | |
| 143 | + public decimal BaseSalary { get; set; } | |
| 144 | + | |
| 145 | + /// <summary> | |
| 146 | + /// 旗舰店负奖励(800元) | |
| 147 | + /// </summary> | |
| 148 | + public decimal FlagshipStoreDeduction { get; set; } | |
| 149 | + | |
| 150 | + /// <summary> | |
| 151 | + /// 实际底薪(底薪-考核扣款-旗舰店负奖励) | |
| 152 | + /// </summary> | |
| 153 | + public decimal ActualBaseSalary { get; set; } | |
| 154 | + | |
| 155 | + /// <summary> | |
| 156 | + /// 在店天数 | |
| 157 | + /// </summary> | |
| 158 | + public int WorkingDays { get; set; } | |
| 159 | + | |
| 160 | + /// <summary> | |
| 161 | + /// 请假天数 | |
| 162 | + /// </summary> | |
| 163 | + public int LeaveDays { get; set; } | |
| 164 | + | |
| 165 | + /// <summary> | |
| 166 | + /// 应发工资(实际底薪+提成) | |
| 167 | + /// </summary> | |
| 168 | + public decimal GrossSalary { get; set; } | |
| 169 | + | |
| 170 | + /// <summary> | |
| 171 | + /// 实发工资(应发工资-扣款合计+补贴合计+奖金) | |
| 172 | + /// </summary> | |
| 173 | + public decimal ActualSalary { get; set; } | |
| 174 | + | |
| 175 | + /// <summary> | |
| 176 | + /// 扣款合计 | |
| 177 | + /// </summary> | |
| 178 | + public decimal TotalDeduction { get; set; } | |
| 179 | + | |
| 180 | + /// <summary> | |
| 181 | + /// 补贴合计 | |
| 182 | + /// </summary> | |
| 183 | + public decimal TotalSubsidy { get; set; } | |
| 184 | + | |
| 185 | + /// <summary> | |
| 186 | + /// 发奖金 | |
| 187 | + /// </summary> | |
| 188 | + public decimal Bonus { get; set; } | |
| 189 | + | |
| 190 | + /// <summary> | |
| 191 | + /// 当月是否发放 | |
| 192 | + /// </summary> | |
| 193 | + public string MonthlyPaymentStatus { get; set; } | |
| 194 | + | |
| 195 | + /// <summary> | |
| 196 | + /// 支付金额 | |
| 197 | + /// </summary> | |
| 198 | + public decimal PaidAmount { get; set; } | |
| 199 | + | |
| 200 | + /// <summary> | |
| 201 | + /// 待支付金额 | |
| 202 | + /// </summary> | |
| 203 | + public decimal PendingAmount { get; set; } | |
| 204 | + | |
| 205 | + /// <summary> | |
| 206 | + /// 是否锁定(0未锁定,1已锁定) | |
| 207 | + /// </summary> | |
| 208 | + public int IsLocked { get; set; } | |
| 209 | + | |
| 210 | + /// <summary> | |
| 211 | + /// 门店类型 | |
| 212 | + /// </summary> | |
| 213 | + public int? StoreType { get; set; } | |
| 214 | + | |
| 215 | + /// <summary> | |
| 216 | + /// 门店类别 | |
| 217 | + /// </summary> | |
| 218 | + public int? StoreCategory { get; set; } | |
| 219 | + | |
| 220 | + /// <summary> | |
| 221 | + /// 是否新店 | |
| 222 | + /// </summary> | |
| 223 | + public string IsNewStore { get; set; } | |
| 224 | + | |
| 225 | + /// <summary> | |
| 226 | + /// 新店保护阶段 | |
| 227 | + /// </summary> | |
| 228 | + public int NewStoreProtectionStage { get; set; } | |
| 229 | + | |
| 230 | + /// <summary> | |
| 231 | + /// 更新时间 | |
| 232 | + /// </summary> | |
| 233 | + public DateTime UpdateTime { get; set; } | |
| 234 | + } | |
| 235 | +} | |
| 236 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_cooperation_cost/LqCooperationCostEntity.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using NCC.Common.Const; | |
| 3 | +using SqlSugar; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.lq_cooperation_cost | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 合作成本表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("lq_cooperation_cost")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class LqCooperationCostEntity | |
| 13 | + { | |
| 14 | + /// <summary> | |
| 15 | + /// 主键ID | |
| 16 | + /// </summary> | |
| 17 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 18 | + public string Id { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 门店ID(关联lq_mdxx.F_Id) | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "F_StoreId")] | |
| 24 | + public string StoreId { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 门店名称 | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "F_StoreName")] | |
| 30 | + public string StoreName { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 年份 | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "F_Year")] | |
| 36 | + public int Year { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 月份(YYYYMM格式) | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "F_Month")] | |
| 42 | + public string Month { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 合计金额 | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "F_TotalAmount", DecimalDigits = 2)] | |
| 48 | + public decimal TotalAmount { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 备注说明 | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "F_Remarks")] | |
| 54 | + public string Remarks { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 是否有效(1:有效 0:无效) | |
| 58 | + /// </summary> | |
| 59 | + [SugarColumn(ColumnName = "F_IsEffective")] | |
| 60 | + public int IsEffective { get; set; } = 1; | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 创建人ID | |
| 64 | + /// </summary> | |
| 65 | + [SugarColumn(ColumnName = "F_CreateUser")] | |
| 66 | + public string CreateUser { get; set; } | |
| 67 | + | |
| 68 | + /// <summary> | |
| 69 | + /// 创建时间 | |
| 70 | + /// </summary> | |
| 71 | + [SugarColumn(ColumnName = "F_CreateTime")] | |
| 72 | + public DateTime? CreateTime { get; set; } | |
| 73 | + | |
| 74 | + /// <summary> | |
| 75 | + /// 更新人ID | |
| 76 | + /// </summary> | |
| 77 | + [SugarColumn(ColumnName = "F_UpdateUser")] | |
| 78 | + public string UpdateUser { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 更新时间 | |
| 82 | + /// </summary> | |
| 83 | + [SugarColumn(ColumnName = "F_UpdateTime")] | |
| 84 | + public DateTime? UpdateTime { get; set; } | |
| 85 | + } | |
| 86 | +} | |
| 87 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application/LqInventoryUsageApplicationEntity.cs
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_store_expense/LqStoreExpenseEntity.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using NCC.Common.Const; | |
| 3 | +using SqlSugar; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.lq_store_expense | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 店内支出表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("lq_store_expense")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class LqStoreExpenseEntity | |
| 13 | + { | |
| 14 | + /// <summary> | |
| 15 | + /// 主键ID | |
| 16 | + /// </summary> | |
| 17 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 18 | + public string Id { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 门店ID(关联lq_mdxx.F_Id) | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "F_StoreId")] | |
| 24 | + public string StoreId { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 门店名称 | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "F_StoreName")] | |
| 30 | + public string StoreName { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 支出分类ID(关联lq_reimbursement_category.F_Id) | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "F_ExpenseCategoryId")] | |
| 36 | + public string ExpenseCategoryId { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 支出分类名称 | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "F_ExpenseCategoryName")] | |
| 42 | + public string ExpenseCategoryName { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 支出日期 | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "F_ExpenseDate")] | |
| 48 | + public DateTime ExpenseDate { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 单价 | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "F_UnitPrice", DecimalDigits = 2)] | |
| 54 | + public decimal UnitPrice { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 数量 | |
| 58 | + /// </summary> | |
| 59 | + [SugarColumn(ColumnName = "F_Quantity")] | |
| 60 | + public int Quantity { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 金额 | |
| 64 | + /// </summary> | |
| 65 | + [SugarColumn(ColumnName = "F_Amount", DecimalDigits = 2)] | |
| 66 | + public decimal Amount { get; set; } | |
| 67 | + | |
| 68 | + /// <summary> | |
| 69 | + /// 备注说明 | |
| 70 | + /// </summary> | |
| 71 | + [SugarColumn(ColumnName = "F_Memo")] | |
| 72 | + public string Memo { get; set; } | |
| 73 | + | |
| 74 | + /// <summary> | |
| 75 | + /// 附件(JSON格式) | |
| 76 | + /// </summary> | |
| 77 | + [SugarColumn(ColumnName = "F_Attachment", ColumnDataType = "TEXT")] | |
| 78 | + public string Attachment { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 关联报销申请ID(可选) | |
| 82 | + /// </summary> | |
| 83 | + [SugarColumn(ColumnName = "F_RelatedReimbursementId")] | |
| 84 | + public string RelatedReimbursementId { get; set; } | |
| 85 | + | |
| 86 | + /// <summary> | |
| 87 | + /// 关联购买记录ID(可选) | |
| 88 | + /// </summary> | |
| 89 | + [SugarColumn(ColumnName = "F_RelatedPurchaseRecordId")] | |
| 90 | + public string RelatedPurchaseRecordId { get; set; } | |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 是否有效(1:有效 0:无效) | |
| 94 | + /// </summary> | |
| 95 | + [SugarColumn(ColumnName = "F_IsEffective")] | |
| 96 | + public int IsEffective { get; set; } = 1; | |
| 97 | + | |
| 98 | + /// <summary> | |
| 99 | + /// 创建人ID | |
| 100 | + /// </summary> | |
| 101 | + [SugarColumn(ColumnName = "F_CreateUser")] | |
| 102 | + public string CreateUser { get; set; } | |
| 103 | + | |
| 104 | + /// <summary> | |
| 105 | + /// 创建时间 | |
| 106 | + /// </summary> | |
| 107 | + [SugarColumn(ColumnName = "F_CreateTime")] | |
| 108 | + public DateTime? CreateTime { get; set; } | |
| 109 | + | |
| 110 | + /// <summary> | |
| 111 | + /// 更新人ID | |
| 112 | + /// </summary> | |
| 113 | + [SugarColumn(ColumnName = "F_UpdateUser")] | |
| 114 | + public string UpdateUser { get; set; } | |
| 115 | + | |
| 116 | + /// <summary> | |
| 117 | + /// 更新时间 | |
| 118 | + /// </summary> | |
| 119 | + [SugarColumn(ColumnName = "F_UpdateTime")] | |
| 120 | + public DateTime? UpdateTime { get; set; } | |
| 121 | + } | |
| 122 | +} | |
| 123 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_store_manager_salary_statistics/LqStoreManagerSalaryStatisticsEntity.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using NCC.Common.Const; | |
| 3 | +using SqlSugar; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.lq_store_manager_salary_statistics | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 店长工资统计表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("lq_store_manager_salary_statistics")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class LqStoreManagerSalaryStatisticsEntity | |
| 13 | + { | |
| 14 | + /// <summary> | |
| 15 | + /// 主键ID | |
| 16 | + /// </summary> | |
| 17 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 18 | + public string Id { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 门店ID | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "F_StoreId")] | |
| 24 | + public string StoreId { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 门店名称 | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "F_StoreName")] | |
| 30 | + public string StoreName { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 核算岗位 | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "F_Position")] | |
| 36 | + public string Position { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 员工ID | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "F_EmployeeId")] | |
| 42 | + public string EmployeeId { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 员工姓名 | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "F_EmployeeName")] | |
| 48 | + public string EmployeeName { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 统计月份(YYYYMM) | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "F_StatisticsMonth")] | |
| 54 | + public string StatisticsMonth { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 门店类型 | |
| 58 | + /// </summary> | |
| 59 | + [SugarColumn(ColumnName = "F_StoreType")] | |
| 60 | + public int? StoreType { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 门店类别 | |
| 64 | + /// </summary> | |
| 65 | + [SugarColumn(ColumnName = "F_StoreCategory")] | |
| 66 | + public int? StoreCategory { get; set; } | |
| 67 | + | |
| 68 | + /// <summary> | |
| 69 | + /// 是否新店 | |
| 70 | + /// </summary> | |
| 71 | + [SugarColumn(ColumnName = "F_IsNewStore")] | |
| 72 | + public string IsNewStore { get; set; } | |
| 73 | + | |
| 74 | + /// <summary> | |
| 75 | + /// 新店保护阶段 | |
| 76 | + /// </summary> | |
| 77 | + [SugarColumn(ColumnName = "F_NewStoreProtectionStage")] | |
| 78 | + public int NewStoreProtectionStage { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 门店总业绩 | |
| 82 | + /// </summary> | |
| 83 | + [SugarColumn(ColumnName = "F_StoreTotalPerformance")] | |
| 84 | + public decimal StoreTotalPerformance { get; set; } | |
| 85 | + | |
| 86 | + /// <summary> | |
| 87 | + /// 门店开单业绩 | |
| 88 | + /// </summary> | |
| 89 | + [SugarColumn(ColumnName = "F_StoreBillingPerformance")] | |
| 90 | + public decimal StoreBillingPerformance { get; set; } | |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 门店退卡业绩 | |
| 94 | + /// </summary> | |
| 95 | + [SugarColumn(ColumnName = "F_StoreRefundPerformance")] | |
| 96 | + public decimal StoreRefundPerformance { get; set; } | |
| 97 | + | |
| 98 | + /// <summary> | |
| 99 | + /// 门店生命线 | |
| 100 | + /// </summary> | |
| 101 | + [SugarColumn(ColumnName = "F_StoreLifeline")] | |
| 102 | + public decimal StoreLifeline { get; set; } | |
| 103 | + | |
| 104 | + /// <summary> | |
| 105 | + /// 业绩完成率 | |
| 106 | + /// </summary> | |
| 107 | + [SugarColumn(ColumnName = "F_PerformanceCompletionRate")] | |
| 108 | + public decimal PerformanceCompletionRate { get; set; } | |
| 109 | + | |
| 110 | + /// <summary> | |
| 111 | + /// 业绩是否达标 | |
| 112 | + /// </summary> | |
| 113 | + [SugarColumn(ColumnName = "F_PerformanceReached")] | |
| 114 | + public string PerformanceReached { get; set; } | |
| 115 | + | |
| 116 | + /// <summary> | |
| 117 | + /// 进店消耗人数 | |
| 118 | + /// </summary> | |
| 119 | + [SugarColumn(ColumnName = "F_HeadCount")] | |
| 120 | + public int HeadCount { get; set; } | |
| 121 | + | |
| 122 | + /// <summary> | |
| 123 | + /// 目标人头数 | |
| 124 | + /// </summary> | |
| 125 | + [SugarColumn(ColumnName = "F_TargetHeadCount")] | |
| 126 | + public decimal TargetHeadCount { get; set; } | |
| 127 | + | |
| 128 | + /// <summary> | |
| 129 | + /// 人头是否达标 | |
| 130 | + /// </summary> | |
| 131 | + [SugarColumn(ColumnName = "F_HeadCountReached")] | |
| 132 | + public string HeadCountReached { get; set; } | |
| 133 | + | |
| 134 | + /// <summary> | |
| 135 | + /// 门店消耗金额 | |
| 136 | + /// </summary> | |
| 137 | + [SugarColumn(ColumnName = "F_StoreConsume")] | |
| 138 | + public decimal StoreConsume { get; set; } | |
| 139 | + | |
| 140 | + /// <summary> | |
| 141 | + /// 目标消耗金额 | |
| 142 | + /// </summary> | |
| 143 | + [SugarColumn(ColumnName = "F_TargetConsume")] | |
| 144 | + public decimal TargetConsume { get; set; } | |
| 145 | + | |
| 146 | + /// <summary> | |
| 147 | + /// 消耗是否达标 | |
| 148 | + /// </summary> | |
| 149 | + [SugarColumn(ColumnName = "F_ConsumeReached")] | |
| 150 | + public string ConsumeReached { get; set; } | |
| 151 | + | |
| 152 | + /// <summary> | |
| 153 | + /// 未达标指标数量 | |
| 154 | + /// </summary> | |
| 155 | + [SugarColumn(ColumnName = "F_UnreachedIndicatorCount")] | |
| 156 | + public int UnreachedIndicatorCount { get; set; } | |
| 157 | + | |
| 158 | + /// <summary> | |
| 159 | + /// 考核扣款金额 | |
| 160 | + /// </summary> | |
| 161 | + [SugarColumn(ColumnName = "F_AssessmentDeduction")] | |
| 162 | + public decimal AssessmentDeduction { get; set; } | |
| 163 | + | |
| 164 | + /// <summary> | |
| 165 | + /// 销售业绩(开单业绩-退款业绩) | |
| 166 | + /// </summary> | |
| 167 | + [SugarColumn(ColumnName = "F_SalesPerformance")] | |
| 168 | + public decimal SalesPerformance { get; set; } | |
| 169 | + | |
| 170 | + /// <summary> | |
| 171 | + /// 产品物料(仓库领用金额) | |
| 172 | + /// </summary> | |
| 173 | + [SugarColumn(ColumnName = "F_ProductMaterial")] | |
| 174 | + public decimal ProductMaterial { get; set; } | |
| 175 | + | |
| 176 | + /// <summary> | |
| 177 | + /// 合作项目成本 | |
| 178 | + /// </summary> | |
| 179 | + [SugarColumn(ColumnName = "F_CooperationCost")] | |
| 180 | + public decimal CooperationCost { get; set; } | |
| 181 | + | |
| 182 | + /// <summary> | |
| 183 | + /// 店内支出 | |
| 184 | + /// </summary> | |
| 185 | + [SugarColumn(ColumnName = "F_StoreExpense")] | |
| 186 | + public decimal StoreExpense { get; set; } | |
| 187 | + | |
| 188 | + /// <summary> | |
| 189 | + /// 洗毛巾费用 | |
| 190 | + /// </summary> | |
| 191 | + [SugarColumn(ColumnName = "F_LaundryCost")] | |
| 192 | + public decimal LaundryCost { get; set; } | |
| 193 | + | |
| 194 | + /// <summary> | |
| 195 | + /// 毛利(销售业绩-产品物料-合作项目成本-店内支出-洗毛巾) | |
| 196 | + /// </summary> | |
| 197 | + [SugarColumn(ColumnName = "F_GrossProfit")] | |
| 198 | + public decimal GrossProfit { get; set; } | |
| 199 | + | |
| 200 | + /// <summary> | |
| 201 | + /// 提成比例 | |
| 202 | + /// </summary> | |
| 203 | + [SugarColumn(ColumnName = "F_CommissionRate")] | |
| 204 | + public decimal CommissionRate { get; set; } | |
| 205 | + | |
| 206 | + /// <summary> | |
| 207 | + /// 提成金额(基于毛利) | |
| 208 | + /// </summary> | |
| 209 | + [SugarColumn(ColumnName = "F_CommissionAmount")] | |
| 210 | + public decimal CommissionAmount { get; set; } | |
| 211 | + | |
| 212 | + /// <summary> | |
| 213 | + /// 底薪金额(固定4000元) | |
| 214 | + /// </summary> | |
| 215 | + [SugarColumn(ColumnName = "F_BaseSalary")] | |
| 216 | + public decimal BaseSalary { get; set; } | |
| 217 | + | |
| 218 | + /// <summary> | |
| 219 | + /// 旗舰店负奖励(800元) | |
| 220 | + /// </summary> | |
| 221 | + [SugarColumn(ColumnName = "F_FlagshipStoreDeduction")] | |
| 222 | + public decimal FlagshipStoreDeduction { get; set; } | |
| 223 | + | |
| 224 | + /// <summary> | |
| 225 | + /// 实际底薪(底薪-考核扣款-旗舰店负奖励) | |
| 226 | + /// </summary> | |
| 227 | + [SugarColumn(ColumnName = "F_ActualBaseSalary")] | |
| 228 | + public decimal ActualBaseSalary { get; set; } | |
| 229 | + | |
| 230 | + /// <summary> | |
| 231 | + /// 在店天数 | |
| 232 | + /// </summary> | |
| 233 | + [SugarColumn(ColumnName = "F_WorkingDays")] | |
| 234 | + public int WorkingDays { get; set; } | |
| 235 | + | |
| 236 | + /// <summary> | |
| 237 | + /// 请假天数 | |
| 238 | + /// </summary> | |
| 239 | + [SugarColumn(ColumnName = "F_LeaveDays")] | |
| 240 | + public int LeaveDays { get; set; } | |
| 241 | + | |
| 242 | + /// <summary> | |
| 243 | + /// 应发工资(实际底薪+提成) | |
| 244 | + /// </summary> | |
| 245 | + [SugarColumn(ColumnName = "F_GrossSalary")] | |
| 246 | + public decimal GrossSalary { get; set; } | |
| 247 | + | |
| 248 | + /// <summary> | |
| 249 | + /// 实发工资(应发工资-扣款合计+补贴合计+奖金) | |
| 250 | + /// </summary> | |
| 251 | + [SugarColumn(ColumnName = "F_ActualSalary")] | |
| 252 | + public decimal ActualSalary { get; set; } | |
| 253 | + | |
| 254 | + /// <summary> | |
| 255 | + /// 缺卡扣款 | |
| 256 | + /// </summary> | |
| 257 | + [SugarColumn(ColumnName = "F_MissingCard")] | |
| 258 | + public decimal MissingCard { get; set; } | |
| 259 | + | |
| 260 | + /// <summary> | |
| 261 | + /// 迟到扣款 | |
| 262 | + /// </summary> | |
| 263 | + [SugarColumn(ColumnName = "F_LateArrival")] | |
| 264 | + public decimal LateArrival { get; set; } | |
| 265 | + | |
| 266 | + /// <summary> | |
| 267 | + /// 请假扣款 | |
| 268 | + /// </summary> | |
| 269 | + [SugarColumn(ColumnName = "F_LeaveDeduction")] | |
| 270 | + public decimal LeaveDeduction { get; set; } | |
| 271 | + | |
| 272 | + /// <summary> | |
| 273 | + /// 扣社保 | |
| 274 | + /// </summary> | |
| 275 | + [SugarColumn(ColumnName = "F_SocialInsuranceDeduction")] | |
| 276 | + public decimal SocialInsuranceDeduction { get; set; } | |
| 277 | + | |
| 278 | + /// <summary> | |
| 279 | + /// 扣除奖励 | |
| 280 | + /// </summary> | |
| 281 | + [SugarColumn(ColumnName = "F_RewardDeduction")] | |
| 282 | + public decimal RewardDeduction { get; set; } | |
| 283 | + | |
| 284 | + /// <summary> | |
| 285 | + /// 扣住宿费 | |
| 286 | + /// </summary> | |
| 287 | + [SugarColumn(ColumnName = "F_AccommodationDeduction")] | |
| 288 | + public decimal AccommodationDeduction { get; set; } | |
| 289 | + | |
| 290 | + /// <summary> | |
| 291 | + /// 扣学习期费用 | |
| 292 | + /// </summary> | |
| 293 | + [SugarColumn(ColumnName = "F_StudyPeriodDeduction")] | |
| 294 | + public decimal StudyPeriodDeduction { get; set; } | |
| 295 | + | |
| 296 | + /// <summary> | |
| 297 | + /// 扣工作服费用 | |
| 298 | + /// </summary> | |
| 299 | + [SugarColumn(ColumnName = "F_WorkClothesDeduction")] | |
| 300 | + public decimal WorkClothesDeduction { get; set; } | |
| 301 | + | |
| 302 | + /// <summary> | |
| 303 | + /// 扣款合计 | |
| 304 | + /// </summary> | |
| 305 | + [SugarColumn(ColumnName = "F_TotalDeduction")] | |
| 306 | + public decimal TotalDeduction { get; set; } | |
| 307 | + | |
| 308 | + /// <summary> | |
| 309 | + /// 当月培训补贴 | |
| 310 | + /// </summary> | |
| 311 | + [SugarColumn(ColumnName = "F_MonthlyTrainingSubsidy")] | |
| 312 | + public decimal MonthlyTrainingSubsidy { get; set; } | |
| 313 | + | |
| 314 | + /// <summary> | |
| 315 | + /// 当月交通补贴 | |
| 316 | + /// </summary> | |
| 317 | + [SugarColumn(ColumnName = "F_MonthlyTransportSubsidy")] | |
| 318 | + public decimal MonthlyTransportSubsidy { get; set; } | |
| 319 | + | |
| 320 | + /// <summary> | |
| 321 | + /// 上月培训补贴 | |
| 322 | + /// </summary> | |
| 323 | + [SugarColumn(ColumnName = "F_LastMonthTrainingSubsidy")] | |
| 324 | + public decimal LastMonthTrainingSubsidy { get; set; } | |
| 325 | + | |
| 326 | + /// <summary> | |
| 327 | + /// 上月交通补贴 | |
| 328 | + /// </summary> | |
| 329 | + [SugarColumn(ColumnName = "F_LastMonthTransportSubsidy")] | |
| 330 | + public decimal LastMonthTransportSubsidy { get; set; } | |
| 331 | + | |
| 332 | + /// <summary> | |
| 333 | + /// 补贴合计 | |
| 334 | + /// </summary> | |
| 335 | + [SugarColumn(ColumnName = "F_TotalSubsidy")] | |
| 336 | + public decimal TotalSubsidy { get; set; } | |
| 337 | + | |
| 338 | + /// <summary> | |
| 339 | + /// 发奖金 | |
| 340 | + /// </summary> | |
| 341 | + [SugarColumn(ColumnName = "F_Bonus")] | |
| 342 | + public decimal Bonus { get; set; } | |
| 343 | + | |
| 344 | + /// <summary> | |
| 345 | + /// 退手机押金 | |
| 346 | + /// </summary> | |
| 347 | + [SugarColumn(ColumnName = "F_ReturnPhoneDeposit")] | |
| 348 | + public decimal ReturnPhoneDeposit { get; set; } | |
| 349 | + | |
| 350 | + /// <summary> | |
| 351 | + /// 退住宿押金 | |
| 352 | + /// </summary> | |
| 353 | + [SugarColumn(ColumnName = "F_ReturnAccommodationDeposit")] | |
| 354 | + public decimal ReturnAccommodationDeposit { get; set; } | |
| 355 | + | |
| 356 | + /// <summary> | |
| 357 | + /// 当月是否发放 | |
| 358 | + /// </summary> | |
| 359 | + [SugarColumn(ColumnName = "F_MonthlyPaymentStatus")] | |
| 360 | + public string MonthlyPaymentStatus { get; set; } | |
| 361 | + | |
| 362 | + /// <summary> | |
| 363 | + /// 支付金额 | |
| 364 | + /// </summary> | |
| 365 | + [SugarColumn(ColumnName = "F_PaidAmount")] | |
| 366 | + public decimal PaidAmount { get; set; } | |
| 367 | + | |
| 368 | + /// <summary> | |
| 369 | + /// 待支付金额 | |
| 370 | + /// </summary> | |
| 371 | + [SugarColumn(ColumnName = "F_PendingAmount")] | |
| 372 | + public decimal PendingAmount { get; set; } | |
| 373 | + | |
| 374 | + /// <summary> | |
| 375 | + /// 补发上月 | |
| 376 | + /// </summary> | |
| 377 | + [SugarColumn(ColumnName = "F_LastMonthSupplement")] | |
| 378 | + public decimal LastMonthSupplement { get; set; } | |
| 379 | + | |
| 380 | + /// <summary> | |
| 381 | + /// 当月支付总额 | |
| 382 | + /// </summary> | |
| 383 | + [SugarColumn(ColumnName = "F_MonthlyTotalPayment")] | |
| 384 | + public decimal MonthlyTotalPayment { get; set; } | |
| 385 | + | |
| 386 | + /// <summary> | |
| 387 | + /// 是否锁定(0未锁定,1已锁定) | |
| 388 | + /// </summary> | |
| 389 | + [SugarColumn(ColumnName = "F_IsLocked")] | |
| 390 | + public int IsLocked { get; set; } | |
| 391 | + | |
| 392 | + /// <summary> | |
| 393 | + /// 创建时间 | |
| 394 | + /// </summary> | |
| 395 | + [SugarColumn(ColumnName = "F_CreateTime")] | |
| 396 | + public DateTime CreateTime { get; set; } | |
| 397 | + | |
| 398 | + /// <summary> | |
| 399 | + /// 更新时间 | |
| 400 | + /// </summary> | |
| 401 | + [SugarColumn(ColumnName = "F_UpdateTime")] | |
| 402 | + public DateTime UpdateTime { get; set; } | |
| 403 | + | |
| 404 | + /// <summary> | |
| 405 | + /// 创建人 | |
| 406 | + /// </summary> | |
| 407 | + [SugarColumn(ColumnName = "F_CreateUser")] | |
| 408 | + public string CreateUser { get; set; } | |
| 409 | + | |
| 410 | + /// <summary> | |
| 411 | + /// 更新人 | |
| 412 | + /// </summary> | |
| 413 | + [SugarColumn(ColumnName = "F_UpdateUser")] | |
| 414 | + public string UpdateUser { get; set; } | |
| 415 | + } | |
| 416 | +} | |
| 417 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Interfaces/ILqCooperationCostService.cs
0 → 100644
netcore/src/Modularity/Extend/NCC.Extend.Interfaces/ILqStoreExpenseService.cs
0 → 100644
netcore/src/Modularity/Extend/NCC.Extend/LqCooperationCostService.cs
0 → 100644
| 1 | +using NCC.Common.Core.Manager; | |
| 2 | +using NCC.Common.Enum; | |
| 3 | +using NCC.Common.Extension; | |
| 4 | +using NCC.Common.Filter; | |
| 5 | +using NCC.Dependency; | |
| 6 | +using NCC.DynamicApiController; | |
| 7 | +using NCC.FriendlyException; | |
| 8 | +using NCC.Extend.Interfaces.LqCooperationCost; | |
| 9 | +using Mapster; | |
| 10 | +using Microsoft.AspNetCore.Mvc; | |
| 11 | +using SqlSugar; | |
| 12 | +using System; | |
| 13 | +using System.Collections.Generic; | |
| 14 | +using System.Linq; | |
| 15 | +using System.Threading.Tasks; | |
| 16 | +using NCC.Extend.Entitys.lq_cooperation_cost; | |
| 17 | +using NCC.Extend.Entitys.Dto.LqCooperationCost; | |
| 18 | +using NCC.Extend.Entitys.lq_mdxx; | |
| 19 | +using Yitter.IdGenerator; | |
| 20 | +using NCC.Common.Helper; | |
| 21 | +using NCC.Common.Model.NPOI; | |
| 22 | +using NCC.Common.Configuration; | |
| 23 | +using NCC.DataEncryption; | |
| 24 | +using NCC.ClayObject; | |
| 25 | +using NCC.Common.Const; | |
| 26 | +using NCC.Extend.Entitys.Enum; | |
| 27 | +using Microsoft.AspNetCore.Http; | |
| 28 | +using System.IO; | |
| 29 | +using System.Data; | |
| 30 | + | |
| 31 | +namespace NCC.Extend.LqCooperationCost | |
| 32 | +{ | |
| 33 | + /// <summary> | |
| 34 | + /// 合作成本表服务 | |
| 35 | + /// </summary> | |
| 36 | + [ApiDescriptionSettings(Tag = "Extend", Name = "LqCooperationCost", Order = 200)] | |
| 37 | + [Route("api/Extend/[controller]")] | |
| 38 | + public class LqCooperationCostService : ILqCooperationCostService, IDynamicApiController, ITransient | |
| 39 | + { | |
| 40 | + private readonly ISqlSugarRepository<LqCooperationCostEntity> _repository; | |
| 41 | + private readonly SqlSugarScope _db; | |
| 42 | + private readonly IUserManager _userManager; | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 初始化一个<see cref="LqCooperationCostService"/>类型的新实例 | |
| 46 | + /// </summary> | |
| 47 | + public LqCooperationCostService( | |
| 48 | + ISqlSugarRepository<LqCooperationCostEntity> repository, | |
| 49 | + IUserManager userManager) | |
| 50 | + { | |
| 51 | + _repository = repository; | |
| 52 | + _db = _repository.Context; | |
| 53 | + _userManager = userManager; | |
| 54 | + } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 获取合作成本表详情 | |
| 58 | + /// </summary> | |
| 59 | + /// <param name="id">主键ID</param> | |
| 60 | + /// <returns></returns> | |
| 61 | + [HttpGet("{id}")] | |
| 62 | + public async Task<dynamic> GetInfo(string id) | |
| 63 | + { | |
| 64 | + var entity = await _db.Queryable<LqCooperationCostEntity>() | |
| 65 | + .Where(x => x.Id == id && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 66 | + .FirstAsync(); | |
| 67 | + _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); | |
| 68 | + var output = entity.Adapt<LqCooperationCostInfoOutput>(); | |
| 69 | + return output; | |
| 70 | + } | |
| 71 | + | |
| 72 | + /// <summary> | |
| 73 | + /// 获取合作成本表列表 | |
| 74 | + /// </summary> | |
| 75 | + /// <param name="input">请求参数</param> | |
| 76 | + /// <returns></returns> | |
| 77 | + [HttpGet("")] | |
| 78 | + public async Task<dynamic> GetList([FromQuery] LqCooperationCostListQueryInput input) | |
| 79 | + { | |
| 80 | + var sidx = input.sidx ?? "CreateTime"; | |
| 81 | + var sortType = input.sort?.ToLower() == "asc" ? OrderByType.Asc : OrderByType.Desc; | |
| 82 | + var query = _db.Queryable<LqCooperationCostEntity>() | |
| 83 | + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 84 | + .WhereIF(!string.IsNullOrEmpty(input.storeId), x => x.StoreId == input.storeId) | |
| 85 | + .WhereIF(!string.IsNullOrEmpty(input.storeName), x => x.StoreName.Contains(input.storeName)) | |
| 86 | + .WhereIF(input.year.HasValue, x => x.Year == input.year.Value) | |
| 87 | + .WhereIF(!string.IsNullOrEmpty(input.month), x => x.Month == input.month); | |
| 88 | + | |
| 89 | + // 处理排序 | |
| 90 | + switch (sidx.ToLower()) | |
| 91 | + { | |
| 92 | + case "id": | |
| 93 | + query = query.OrderBy(x => x.Id, sortType); | |
| 94 | + break; | |
| 95 | + case "createtime": | |
| 96 | + query = query.OrderBy(x => x.CreateTime, sortType); | |
| 97 | + break; | |
| 98 | + case "storename": | |
| 99 | + query = query.OrderBy(x => x.StoreName, sortType); | |
| 100 | + break; | |
| 101 | + case "year": | |
| 102 | + query = query.OrderBy(x => x.Year, sortType); | |
| 103 | + break; | |
| 104 | + case "month": | |
| 105 | + query = query.OrderBy(x => x.Month, sortType); | |
| 106 | + break; | |
| 107 | + default: | |
| 108 | + query = query.OrderBy(x => x.CreateTime, OrderByType.Desc); | |
| 109 | + break; | |
| 110 | + } | |
| 111 | + | |
| 112 | + var total = await query.CountAsync(); | |
| 113 | + var list = await query.ToPageListAsync(input.currentPage, input.pageSize); | |
| 114 | + | |
| 115 | + var result = list.Select(x => new LqCooperationCostListOutput | |
| 116 | + { | |
| 117 | + id = x.Id, | |
| 118 | + storeId = x.StoreId, | |
| 119 | + storeName = x.StoreName, | |
| 120 | + year = x.Year, | |
| 121 | + month = x.Month, | |
| 122 | + totalAmount = x.TotalAmount, | |
| 123 | + remarks = x.Remarks, | |
| 124 | + createUser = x.CreateUser, | |
| 125 | + createTime = x.CreateTime, | |
| 126 | + updateUser = x.UpdateUser, | |
| 127 | + updateTime = x.UpdateTime | |
| 128 | + }).ToList(); | |
| 129 | + | |
| 130 | + return PageResult<LqCooperationCostListOutput>.SqlSugarPageResult( | |
| 131 | + new SqlSugarPagedList<LqCooperationCostListOutput> | |
| 132 | + { | |
| 133 | + list = result, | |
| 134 | + pagination = new PagedModel { PageIndex = input.currentPage, PageSize = input.pageSize, Total = total } | |
| 135 | + }); | |
| 136 | + } | |
| 137 | + | |
| 138 | + /// <summary> | |
| 139 | + /// 获取合作成本表无分页列表 | |
| 140 | + /// </summary> | |
| 141 | + /// <param name="input">请求参数</param> | |
| 142 | + /// <returns></returns> | |
| 143 | + [HttpGet("Actions/GetNoPagingList")] | |
| 144 | + public async Task<List<LqCooperationCostListOutput>> GetNoPagingList([FromQuery] LqCooperationCostListQueryInput input) | |
| 145 | + { | |
| 146 | + var sidx = input.sidx ?? "CreateTime"; | |
| 147 | + var sortType = input.sort?.ToLower() == "asc" ? OrderByType.Asc : OrderByType.Desc; | |
| 148 | + var query = _db.Queryable<LqCooperationCostEntity>() | |
| 149 | + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 150 | + .WhereIF(!string.IsNullOrEmpty(input.storeId), x => x.StoreId == input.storeId) | |
| 151 | + .WhereIF(!string.IsNullOrEmpty(input.storeName), x => x.StoreName.Contains(input.storeName)) | |
| 152 | + .WhereIF(input.year.HasValue, x => x.Year == input.year.Value) | |
| 153 | + .WhereIF(!string.IsNullOrEmpty(input.month), x => x.Month == input.month); | |
| 154 | + | |
| 155 | + // 处理排序 | |
| 156 | + switch (sidx.ToLower()) | |
| 157 | + { | |
| 158 | + case "id": | |
| 159 | + query = query.OrderBy(x => x.Id, sortType); | |
| 160 | + break; | |
| 161 | + case "createtime": | |
| 162 | + query = query.OrderBy(x => x.CreateTime, sortType); | |
| 163 | + break; | |
| 164 | + case "storename": | |
| 165 | + query = query.OrderBy(x => x.StoreName, sortType); | |
| 166 | + break; | |
| 167 | + case "year": | |
| 168 | + query = query.OrderBy(x => x.Year, sortType); | |
| 169 | + break; | |
| 170 | + case "month": | |
| 171 | + query = query.OrderBy(x => x.Month, sortType); | |
| 172 | + break; | |
| 173 | + default: | |
| 174 | + query = query.OrderBy(x => x.CreateTime, OrderByType.Desc); | |
| 175 | + break; | |
| 176 | + } | |
| 177 | + | |
| 178 | + var list = await query | |
| 179 | + .Select(x => new LqCooperationCostListOutput | |
| 180 | + { | |
| 181 | + id = x.Id, | |
| 182 | + storeId = x.StoreId, | |
| 183 | + storeName = x.StoreName, | |
| 184 | + year = x.Year, | |
| 185 | + month = x.Month, | |
| 186 | + totalAmount = x.TotalAmount, | |
| 187 | + remarks = x.Remarks, | |
| 188 | + createUser = x.CreateUser, | |
| 189 | + createTime = x.CreateTime, | |
| 190 | + updateUser = x.UpdateUser, | |
| 191 | + updateTime = x.UpdateTime | |
| 192 | + }) | |
| 193 | + .ToListAsync(); | |
| 194 | + return list; | |
| 195 | + } | |
| 196 | + | |
| 197 | + /// <summary> | |
| 198 | + /// 创建合作成本表 | |
| 199 | + /// </summary> | |
| 200 | + /// <param name="input">参数</param> | |
| 201 | + /// <returns></returns> | |
| 202 | + [HttpPost("")] | |
| 203 | + public async Task Create([FromBody] LqCooperationCostCrInput input) | |
| 204 | + { | |
| 205 | + var userInfo = await _userManager.GetUserInfo(); | |
| 206 | + var entity = input.Adapt<LqCooperationCostEntity>(); | |
| 207 | + entity.Id = YitIdHelper.NextId().ToString(); | |
| 208 | + entity.IsEffective = StatusEnum.有效.GetHashCode(); | |
| 209 | + entity.CreateUser = _userManager.UserId; | |
| 210 | + entity.CreateTime = DateTime.Now; | |
| 211 | + | |
| 212 | + // 如果未提供门店名称,根据门店ID查询 | |
| 213 | + if (string.IsNullOrEmpty(entity.StoreName) && !string.IsNullOrEmpty(entity.StoreId)) | |
| 214 | + { | |
| 215 | + var store = await _db.Queryable<LqMdxxEntity>() | |
| 216 | + .Where(x => x.Id == entity.StoreId) | |
| 217 | + .Select(x => x.Dm) | |
| 218 | + .FirstAsync(); | |
| 219 | + entity.StoreName = store; | |
| 220 | + } | |
| 221 | + | |
| 222 | + var isOk = await _db.Insertable(entity).ExecuteCommandAsync(); | |
| 223 | + if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); | |
| 224 | + } | |
| 225 | + | |
| 226 | + /// <summary> | |
| 227 | + /// 更新合作成本表 | |
| 228 | + /// </summary> | |
| 229 | + /// <param name="id">主键</param> | |
| 230 | + /// <param name="input">参数</param> | |
| 231 | + /// <returns></returns> | |
| 232 | + [HttpPut("{id}")] | |
| 233 | + public async Task Update(string id, [FromBody] LqCooperationCostUpInput input) | |
| 234 | + { | |
| 235 | + var entity = await _db.Queryable<LqCooperationCostEntity>() | |
| 236 | + .Where(x => x.Id == id && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 237 | + .FirstAsync(); | |
| 238 | + _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); | |
| 239 | + | |
| 240 | + entity.StoreId = input.storeId; | |
| 241 | + entity.StoreName = input.storeName; | |
| 242 | + entity.Year = input.year; | |
| 243 | + entity.Month = input.month; | |
| 244 | + entity.TotalAmount = input.totalAmount; | |
| 245 | + entity.Remarks = input.remarks; | |
| 246 | + entity.UpdateUser = _userManager.UserId; | |
| 247 | + entity.UpdateTime = DateTime.Now; | |
| 248 | + | |
| 249 | + // 如果未提供门店名称,根据门店ID查询 | |
| 250 | + if (string.IsNullOrEmpty(entity.StoreName) && !string.IsNullOrEmpty(entity.StoreId)) | |
| 251 | + { | |
| 252 | + var store = await _db.Queryable<LqMdxxEntity>() | |
| 253 | + .Where(x => x.Id == entity.StoreId) | |
| 254 | + .Select(x => x.Dm) | |
| 255 | + .FirstAsync(); | |
| 256 | + entity.StoreName = store; | |
| 257 | + } | |
| 258 | + | |
| 259 | + var isOk = await _db.Updateable(entity).ExecuteCommandAsync(); | |
| 260 | + if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1001); | |
| 261 | + } | |
| 262 | + | |
| 263 | + /// <summary> | |
| 264 | + /// 删除合作成本表 | |
| 265 | + /// </summary> | |
| 266 | + /// <param name="id">主键</param> | |
| 267 | + /// <returns></returns> | |
| 268 | + [HttpDelete("{id}")] | |
| 269 | + public async Task Delete(string id) | |
| 270 | + { | |
| 271 | + var entity = await _db.Queryable<LqCooperationCostEntity>() | |
| 272 | + .Where(x => x.Id == id && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 273 | + .FirstAsync(); | |
| 274 | + _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); | |
| 275 | + | |
| 276 | + // 逻辑删除 | |
| 277 | + entity.IsEffective = StatusEnum.无效.GetHashCode(); | |
| 278 | + entity.UpdateUser = _userManager.UserId; | |
| 279 | + entity.UpdateTime = DateTime.Now; | |
| 280 | + | |
| 281 | + var isOk = await _db.Updateable(entity).ExecuteCommandAsync(); | |
| 282 | + if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1002); | |
| 283 | + } | |
| 284 | + | |
| 285 | + /// <summary> | |
| 286 | + /// 导出合作成本表 | |
| 287 | + /// </summary> | |
| 288 | + /// <param name="input">请求参数</param> | |
| 289 | + /// <returns></returns> | |
| 290 | + [HttpGet("Actions/Export")] | |
| 291 | + public async Task<dynamic> Export([FromQuery] LqCooperationCostListQueryInput input) | |
| 292 | + { | |
| 293 | + var userInfo = await _userManager.GetUserInfo(); | |
| 294 | + var exportData = new List<LqCooperationCostListOutput>(); | |
| 295 | + if (input.dataType == 0) | |
| 296 | + { | |
| 297 | + var data = Clay.Object(await this.GetList(input)); | |
| 298 | + exportData = data.Solidify<PageResult<LqCooperationCostListOutput>>().list; | |
| 299 | + } | |
| 300 | + else | |
| 301 | + { | |
| 302 | + exportData = await this.GetNoPagingList(input); | |
| 303 | + } | |
| 304 | + List<ParamsModel> paramList = "[{\"value\":\"门店ID\",\"field\":\"storeId\"},{\"value\":\"门店名称\",\"field\":\"storeName\"},{\"value\":\"年份\",\"field\":\"year\"},{\"value\":\"月份\",\"field\":\"month\"},{\"value\":\"合计金额\",\"field\":\"totalAmount\"},{\"value\":\"备注说明\",\"field\":\"remarks\"},{\"value\":\"创建人\",\"field\":\"createUser\"},{\"value\":\"创建时间\",\"field\":\"createTime\"},]".ToList<ParamsModel>(); | |
| 305 | + ExcelConfig excelconfig = new ExcelConfig(); | |
| 306 | + excelconfig.FileName = "合作成本表.xls"; | |
| 307 | + excelconfig.HeadFont = "微软雅黑"; | |
| 308 | + excelconfig.HeadPoint = 10; | |
| 309 | + excelconfig.IsAllSizeColumn = true; | |
| 310 | + excelconfig.ColumnModel = new List<ExcelColumnModel>(); | |
| 311 | + List<string> selectKeyList = !string.IsNullOrEmpty(input.selectKey) ? input.selectKey.Split(',').ToList() : paramList.Select(p => p.field).ToList(); | |
| 312 | + foreach (var item in selectKeyList) | |
| 313 | + { | |
| 314 | + var isExist = paramList.Find(p => p.field == item); | |
| 315 | + if (isExist != null) | |
| 316 | + { | |
| 317 | + excelconfig.ColumnModel.Add(new ExcelColumnModel() { Column = isExist.field, ExcelColumn = isExist.value }); | |
| 318 | + } | |
| 319 | + } | |
| 320 | + var addPath = FileVariable.TemporaryFilePath + excelconfig.FileName; | |
| 321 | + ExcelExportHelper<LqCooperationCostListOutput>.Export(exportData, excelconfig, addPath); | |
| 322 | + var fileName = _userManager.UserId + "|" + addPath + "|xls"; | |
| 323 | + var output = new | |
| 324 | + { | |
| 325 | + name = excelconfig.FileName, | |
| 326 | + url = "/api/File/Download?encryption=" + DESCEncryption.Encrypt(fileName, "NCC") | |
| 327 | + }; | |
| 328 | + return output; | |
| 329 | + } | |
| 330 | + | |
| 331 | + /// <summary> | |
| 332 | + /// 导入合作成本数据 | |
| 333 | + /// </summary> | |
| 334 | + /// <remarks> | |
| 335 | + /// 从Excel文件导入合作成本数据 | |
| 336 | + /// | |
| 337 | + /// Excel格式要求: | |
| 338 | + /// 第一行为标题行:门店ID、门店名称、年份、月份、合计金额、备注说明 | |
| 339 | + /// 从第二行开始为数据行 | |
| 340 | + /// | |
| 341 | + /// 示例请求: | |
| 342 | + /// POST /api/Extend/LqCooperationCost/Actions/Import | |
| 343 | + /// Content-Type: multipart/form-data | |
| 344 | + /// </remarks> | |
| 345 | + /// <param name="file">Excel文件</param> | |
| 346 | + /// <returns>导入结果</returns> | |
| 347 | + /// <response code="200">导入成功</response> | |
| 348 | + /// <response code="400">文件格式错误或数据验证失败</response> | |
| 349 | + [HttpPost("Actions/Import")] | |
| 350 | + public async Task<dynamic> Import(IFormFile file) | |
| 351 | + { | |
| 352 | + try | |
| 353 | + { | |
| 354 | + if (file == null || file.Length == 0) | |
| 355 | + { | |
| 356 | + throw NCCException.Oh("请选择要上传的Excel文件"); | |
| 357 | + } | |
| 358 | + | |
| 359 | + // 检查文件格式 | |
| 360 | + var allowedExtensions = new[] { ".xlsx", ".xls" }; | |
| 361 | + var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant(); | |
| 362 | + if (!allowedExtensions.Contains(fileExtension)) | |
| 363 | + { | |
| 364 | + throw NCCException.Oh("只支持.xlsx和.xls格式的Excel文件"); | |
| 365 | + } | |
| 366 | + | |
| 367 | + var successCount = 0; | |
| 368 | + var failCount = 0; | |
| 369 | + var failMessages = new List<string>(); | |
| 370 | + | |
| 371 | + // 保存临时文件 | |
| 372 | + var tempFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + Path.GetExtension(file.FileName)); | |
| 373 | + try | |
| 374 | + { | |
| 375 | + using (var stream = new FileStream(tempFilePath, FileMode.Create)) | |
| 376 | + { | |
| 377 | + await file.CopyToAsync(stream); | |
| 378 | + } | |
| 379 | + | |
| 380 | + // 使用ExcelImportHelper读取Excel文件(第一行为标题行) | |
| 381 | + var dataTable = ExcelImportHelper.ToDataTable(tempFilePath, 0, 0); | |
| 382 | + | |
| 383 | + if (dataTable.Rows.Count <= 1) | |
| 384 | + { | |
| 385 | + throw NCCException.Oh("Excel文件中没有数据行(至少需要标题行和一行数据)"); | |
| 386 | + } | |
| 387 | + | |
| 388 | + // 从第1行开始读取数据(跳过标题行) | |
| 389 | + for (int i = 1; i < dataTable.Rows.Count; i++) | |
| 390 | + { | |
| 391 | + try | |
| 392 | + { | |
| 393 | + var row = dataTable.Rows[i]; | |
| 394 | + var storeId = row[0]?.ToString()?.Trim(); | |
| 395 | + var storeName = row[1]?.ToString()?.Trim(); | |
| 396 | + var yearText = row[2]?.ToString()?.Trim(); | |
| 397 | + var monthText = row[3]?.ToString()?.Trim(); | |
| 398 | + var totalAmountText = row[4]?.ToString()?.Trim(); | |
| 399 | + var remarks = row[5]?.ToString()?.Trim(); | |
| 400 | + | |
| 401 | + // 跳过空行 | |
| 402 | + if (string.IsNullOrEmpty(storeId) && string.IsNullOrEmpty(storeName)) | |
| 403 | + { | |
| 404 | + continue; | |
| 405 | + } | |
| 406 | + | |
| 407 | + // 验证必填字段 | |
| 408 | + if (string.IsNullOrEmpty(storeId)) | |
| 409 | + { | |
| 410 | + failMessages.Add($"第{i + 1}行:门店ID不能为空"); | |
| 411 | + failCount++; | |
| 412 | + continue; | |
| 413 | + } | |
| 414 | + | |
| 415 | + if (string.IsNullOrEmpty(yearText) || !int.TryParse(yearText, out int year)) | |
| 416 | + { | |
| 417 | + failMessages.Add($"第{i + 1}行:年份格式错误(应为数字)"); | |
| 418 | + failCount++; | |
| 419 | + continue; | |
| 420 | + } | |
| 421 | + | |
| 422 | + if (string.IsNullOrEmpty(monthText) || monthText.Length != 6) | |
| 423 | + { | |
| 424 | + failMessages.Add($"第{i + 1}行:月份格式错误(应为YYYYMM格式,如:202501)"); | |
| 425 | + failCount++; | |
| 426 | + continue; | |
| 427 | + } | |
| 428 | + | |
| 429 | + if (string.IsNullOrEmpty(totalAmountText) || !decimal.TryParse(totalAmountText, out decimal totalAmount)) | |
| 430 | + { | |
| 431 | + failMessages.Add($"第{i + 1}行:合计金额格式错误(应为数字)"); | |
| 432 | + failCount++; | |
| 433 | + continue; | |
| 434 | + } | |
| 435 | + | |
| 436 | + // 如果未提供门店名称,根据门店ID查询 | |
| 437 | + if (string.IsNullOrEmpty(storeName)) | |
| 438 | + { | |
| 439 | + var store = await _db.Queryable<LqMdxxEntity>() | |
| 440 | + .Where(x => x.Id == storeId) | |
| 441 | + .Select(x => x.Dm) | |
| 442 | + .FirstAsync(); | |
| 443 | + storeName = store ?? ""; | |
| 444 | + } | |
| 445 | + | |
| 446 | + // 检查是否已存在相同门店、年份、月份的记录 | |
| 447 | + var exists = await _db.Queryable<LqCooperationCostEntity>() | |
| 448 | + .Where(x => x.StoreId == storeId && x.Year == year && x.Month == monthText && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 449 | + .AnyAsync(); | |
| 450 | + | |
| 451 | + if (exists) | |
| 452 | + { | |
| 453 | + failMessages.Add($"第{i + 1}行:该门店{year}年{monthText}月的记录已存在"); | |
| 454 | + failCount++; | |
| 455 | + continue; | |
| 456 | + } | |
| 457 | + | |
| 458 | + // 创建记录 | |
| 459 | + var entity = new LqCooperationCostEntity | |
| 460 | + { | |
| 461 | + Id = YitIdHelper.NextId().ToString(), | |
| 462 | + StoreId = storeId, | |
| 463 | + StoreName = storeName, | |
| 464 | + Year = year, | |
| 465 | + Month = monthText, | |
| 466 | + TotalAmount = totalAmount, | |
| 467 | + Remarks = remarks, | |
| 468 | + IsEffective = StatusEnum.有效.GetHashCode(), | |
| 469 | + CreateUser = _userManager.UserId, | |
| 470 | + CreateTime = DateTime.Now | |
| 471 | + }; | |
| 472 | + | |
| 473 | + var isOk = await _db.Insertable(entity).ExecuteCommandAsync(); | |
| 474 | + if (isOk > 0) | |
| 475 | + { | |
| 476 | + successCount++; | |
| 477 | + } | |
| 478 | + else | |
| 479 | + { | |
| 480 | + failMessages.Add($"第{i + 1}行:保存失败"); | |
| 481 | + failCount++; | |
| 482 | + } | |
| 483 | + } | |
| 484 | + catch (Exception ex) | |
| 485 | + { | |
| 486 | + failMessages.Add($"第{i + 1}行:{ex.Message}"); | |
| 487 | + failCount++; | |
| 488 | + } | |
| 489 | + } | |
| 490 | + } | |
| 491 | + finally | |
| 492 | + { | |
| 493 | + // 清理临时文件 | |
| 494 | + if (File.Exists(tempFilePath)) | |
| 495 | + { | |
| 496 | + File.Delete(tempFilePath); | |
| 497 | + } | |
| 498 | + } | |
| 499 | + | |
| 500 | + return new | |
| 501 | + { | |
| 502 | + success = true, | |
| 503 | + message = $"导入完成:成功{successCount}条,失败{failCount}条", | |
| 504 | + successCount = successCount, | |
| 505 | + failCount = failCount, | |
| 506 | + failMessages = failMessages | |
| 507 | + }; | |
| 508 | + } | |
| 509 | + catch (Exception ex) | |
| 510 | + { | |
| 511 | + throw NCCException.Oh($"导入失败:{ex.Message}"); | |
| 512 | + } | |
| 513 | + } | |
| 514 | + } | |
| 515 | +} | |
| 516 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqReimbursementApplicationService.cs
| ... | ... | @@ -1470,5 +1470,233 @@ namespace NCC.Extend.LqReimbursementApplication |
| 1470 | 1470 | |
| 1471 | 1471 | return new List<object>(); |
| 1472 | 1472 | } |
| 1473 | + | |
| 1474 | + /// <summary> | |
| 1475 | + /// 导出本月已审核通过的报销表明细 | |
| 1476 | + /// </summary> | |
| 1477 | + /// <remarks> | |
| 1478 | + /// 导出本月已审核通过的报销申请及其关联的购买记录明细 | |
| 1479 | + /// 用于线下整理后导入到店内支出表 | |
| 1480 | + /// | |
| 1481 | + /// 示例请求: | |
| 1482 | + /// GET /api/Extend/LqReimbursementApplication/Actions/ExportApprovedDetails?year=2025&month=01 | |
| 1483 | + /// </remarks> | |
| 1484 | + /// <param name="year">年份(可选,默认当前年份)</param> | |
| 1485 | + /// <param name="month">月份(可选,默认当前月份,格式:01-12)</param> | |
| 1486 | + /// <returns>导出文件信息</returns> | |
| 1487 | + /// <response code="200">导出成功</response> | |
| 1488 | + /// <response code="500">服务器错误</response> | |
| 1489 | + [HttpGet("Actions/ExportApprovedDetails")] | |
| 1490 | + public async Task<dynamic> ExportApprovedDetails([FromQuery] int? year = null, [FromQuery] string month = null) | |
| 1491 | + { | |
| 1492 | + try | |
| 1493 | + { | |
| 1494 | + var userInfo = await _userManager.GetUserInfo(); | |
| 1495 | + var now = DateTime.Now; | |
| 1496 | + var queryYear = year ?? now.Year; | |
| 1497 | + var queryMonth = !string.IsNullOrEmpty(month) ? month : now.ToString("MM"); | |
| 1498 | + | |
| 1499 | + // 构建月份字符串(YYYYMM格式) | |
| 1500 | + var monthStr = $"{queryYear}{queryMonth}"; | |
| 1501 | + | |
| 1502 | + // 计算月份的开始和结束日期 | |
| 1503 | + var startDate = new DateTime(queryYear, int.Parse(queryMonth), 1); | |
| 1504 | + var endDate = startDate.AddMonths(1).AddDays(-1); | |
| 1505 | + | |
| 1506 | + // 查询本月已审核通过的报销申请 | |
| 1507 | + var applications = await _db.Queryable<LqReimbursementApplicationEntity>() | |
| 1508 | + .Where(x => (x.ApprovalStatus ?? x.ApproveStatus) == "已通过") | |
| 1509 | + .Where(x => x.ApplicationTime.HasValue && | |
| 1510 | + x.ApplicationTime.Value.Year == queryYear && | |
| 1511 | + x.ApplicationTime.Value.Month == int.Parse(queryMonth)) | |
| 1512 | + .ToListAsync(); | |
| 1513 | + | |
| 1514 | + // 获取所有关联的购买记录 | |
| 1515 | + var applicationIds = applications.Select(x => x.Id).ToList(); | |
| 1516 | + var purchaseRecords = new List<LqPurchaseRecordsEntity>(); | |
| 1517 | + if (applicationIds.Any()) | |
| 1518 | + { | |
| 1519 | + purchaseRecords = await _db.Queryable<LqPurchaseRecordsEntity>() | |
| 1520 | + .Where(x => applicationIds.Contains(x.ApplicationId)) | |
| 1521 | + .OrderBy(x => x.ApplicationId) | |
| 1522 | + .OrderBy(x => x.CreateTime) | |
| 1523 | + .ToListAsync(); | |
| 1524 | + } | |
| 1525 | + | |
| 1526 | + // 获取门店信息 | |
| 1527 | + var storeIds = applications.Where(x => !string.IsNullOrEmpty(x.ApplicationStoreId)) | |
| 1528 | + .Select(x => x.ApplicationStoreId) | |
| 1529 | + .Distinct() | |
| 1530 | + .ToList(); | |
| 1531 | + var stores = new Dictionary<string, string>(); | |
| 1532 | + if (storeIds.Any()) | |
| 1533 | + { | |
| 1534 | + var storeList = await _db.Queryable<LqMdxxEntity>() | |
| 1535 | + .Where(x => storeIds.Contains(x.Id)) | |
| 1536 | + .Select(x => new { x.Id, x.Dm }) | |
| 1537 | + .ToListAsync(); | |
| 1538 | + stores = storeList.ToDictionary(x => x.Id, x => x.Dm ?? ""); | |
| 1539 | + } | |
| 1540 | + | |
| 1541 | + // 组装导出数据(包含报销申请和购买记录明细) | |
| 1542 | + var exportData = new List<ReimbursementDetailExportOutput>(); | |
| 1543 | + foreach (var app in applications) | |
| 1544 | + { | |
| 1545 | + var appPurchaseRecords = purchaseRecords.Where(x => x.ApplicationId == app.Id).ToList(); | |
| 1546 | + | |
| 1547 | + if (appPurchaseRecords.Any()) | |
| 1548 | + { | |
| 1549 | + // 每个购买记录作为一行 | |
| 1550 | + foreach (var pr in appPurchaseRecords) | |
| 1551 | + { | |
| 1552 | + exportData.Add(new ReimbursementDetailExportOutput | |
| 1553 | + { | |
| 1554 | + applicationId = app.Id, | |
| 1555 | + applicationUserName = app.ApplicationUserName, | |
| 1556 | + applicationStoreId = app.ApplicationStoreId, | |
| 1557 | + applicationStoreName = !string.IsNullOrEmpty(app.ApplicationStoreId) && stores.ContainsKey(app.ApplicationStoreId) | |
| 1558 | + ? stores[app.ApplicationStoreId] | |
| 1559 | + : "", | |
| 1560 | + applicationTime = app.ApplicationTime, | |
| 1561 | + applicationAmount = !string.IsNullOrEmpty(app.Amount) ? decimal.Parse(app.Amount) : 0m, | |
| 1562 | + purchaseRecordId = pr.Id, | |
| 1563 | + reimbursementCategoryId = pr.ReimbursementCategoryId, | |
| 1564 | + reimbursementCategoryName = pr.ReimbursementCategoryName, | |
| 1565 | + unitPrice = pr.UnitPrice, | |
| 1566 | + quantity = pr.Quantity ?? 0, | |
| 1567 | + amount = pr.Amount, | |
| 1568 | + memo = pr.Memo, | |
| 1569 | + purchaseTime = pr.PurchaseTime | |
| 1570 | + }); | |
| 1571 | + } | |
| 1572 | + } | |
| 1573 | + else | |
| 1574 | + { | |
| 1575 | + // 如果没有购买记录,至少导出报销申请基本信息 | |
| 1576 | + exportData.Add(new ReimbursementDetailExportOutput | |
| 1577 | + { | |
| 1578 | + applicationId = app.Id, | |
| 1579 | + applicationUserName = app.ApplicationUserName, | |
| 1580 | + applicationStoreId = app.ApplicationStoreId, | |
| 1581 | + applicationStoreName = !string.IsNullOrEmpty(app.ApplicationStoreId) && stores.ContainsKey(app.ApplicationStoreId) | |
| 1582 | + ? stores[app.ApplicationStoreId] | |
| 1583 | + : "", | |
| 1584 | + applicationTime = app.ApplicationTime, | |
| 1585 | + applicationAmount = !string.IsNullOrEmpty(app.Amount) ? decimal.Parse(app.Amount) : 0m, | |
| 1586 | + purchaseRecordId = "", | |
| 1587 | + reimbursementCategoryId = "", | |
| 1588 | + reimbursementCategoryName = "", | |
| 1589 | + unitPrice = 0m, | |
| 1590 | + quantity = 0, | |
| 1591 | + amount = 0m, | |
| 1592 | + memo = "", | |
| 1593 | + purchaseTime = null | |
| 1594 | + }); | |
| 1595 | + } | |
| 1596 | + } | |
| 1597 | + | |
| 1598 | + // 导出Excel | |
| 1599 | + List<ParamsModel> paramList = "[{\"value\":\"报销申请ID\",\"field\":\"applicationId\"},{\"value\":\"申请人姓名\",\"field\":\"applicationUserName\"},{\"value\":\"门店ID\",\"field\":\"applicationStoreId\"},{\"value\":\"门店名称\",\"field\":\"applicationStoreName\"},{\"value\":\"申请时间\",\"field\":\"applicationTime\"},{\"value\":\"申请金额\",\"field\":\"applicationAmount\"},{\"value\":\"购买记录ID\",\"field\":\"purchaseRecordId\"},{\"value\":\"支出分类ID\",\"field\":\"reimbursementCategoryId\"},{\"value\":\"支出分类名称\",\"field\":\"reimbursementCategoryName\"},{\"value\":\"单价\",\"field\":\"unitPrice\"},{\"value\":\"数量\",\"field\":\"quantity\"},{\"value\":\"金额\",\"field\":\"amount\"},{\"value\":\"备注说明\",\"field\":\"memo\"},{\"value\":\"购买时间\",\"field\":\"purchaseTime\"},]".ToList<ParamsModel>(); | |
| 1600 | + ExcelConfig excelconfig = new ExcelConfig(); | |
| 1601 | + excelconfig.FileName = $"报销表明细_{queryYear}年{queryMonth}月.xls"; | |
| 1602 | + excelconfig.HeadFont = "微软雅黑"; | |
| 1603 | + excelconfig.HeadPoint = 10; | |
| 1604 | + excelconfig.IsAllSizeColumn = true; | |
| 1605 | + excelconfig.ColumnModel = new List<ExcelColumnModel>(); | |
| 1606 | + foreach (var param in paramList) | |
| 1607 | + { | |
| 1608 | + excelconfig.ColumnModel.Add(new ExcelColumnModel() { Column = param.field, ExcelColumn = param.value }); | |
| 1609 | + } | |
| 1610 | + var addPath = FileVariable.TemporaryFilePath + excelconfig.FileName; | |
| 1611 | + ExcelExportHelper<ReimbursementDetailExportOutput>.Export(exportData, excelconfig, addPath); | |
| 1612 | + var fileName = _userManager.UserId + "|" + addPath + "|xls"; | |
| 1613 | + var output = new | |
| 1614 | + { | |
| 1615 | + name = excelconfig.FileName, | |
| 1616 | + url = "/api/File/Download?encryption=" + DESCEncryption.Encrypt(fileName, "NCC") | |
| 1617 | + }; | |
| 1618 | + return output; | |
| 1619 | + } | |
| 1620 | + catch (Exception ex) | |
| 1621 | + { | |
| 1622 | + throw NCCException.Oh($"导出失败:{ex.Message}"); | |
| 1623 | + } | |
| 1624 | + } | |
| 1625 | + } | |
| 1626 | + | |
| 1627 | + /// <summary> | |
| 1628 | + /// 报销表明细导出输出 | |
| 1629 | + /// </summary> | |
| 1630 | + public class ReimbursementDetailExportOutput | |
| 1631 | + { | |
| 1632 | + /// <summary> | |
| 1633 | + /// 报销申请ID | |
| 1634 | + /// </summary> | |
| 1635 | + public string applicationId { get; set; } | |
| 1636 | + | |
| 1637 | + /// <summary> | |
| 1638 | + /// 申请人姓名 | |
| 1639 | + /// </summary> | |
| 1640 | + public string applicationUserName { get; set; } | |
| 1641 | + | |
| 1642 | + /// <summary> | |
| 1643 | + /// 门店ID | |
| 1644 | + /// </summary> | |
| 1645 | + public string applicationStoreId { get; set; } | |
| 1646 | + | |
| 1647 | + /// <summary> | |
| 1648 | + /// 门店名称 | |
| 1649 | + /// </summary> | |
| 1650 | + public string applicationStoreName { get; set; } | |
| 1651 | + | |
| 1652 | + /// <summary> | |
| 1653 | + /// 申请时间 | |
| 1654 | + /// </summary> | |
| 1655 | + public DateTime? applicationTime { get; set; } | |
| 1656 | + | |
| 1657 | + /// <summary> | |
| 1658 | + /// 申请金额 | |
| 1659 | + /// </summary> | |
| 1660 | + public decimal applicationAmount { get; set; } | |
| 1661 | + | |
| 1662 | + /// <summary> | |
| 1663 | + /// 购买记录ID | |
| 1664 | + /// </summary> | |
| 1665 | + public string purchaseRecordId { get; set; } | |
| 1666 | + | |
| 1667 | + /// <summary> | |
| 1668 | + /// 支出分类ID | |
| 1669 | + /// </summary> | |
| 1670 | + public string reimbursementCategoryId { get; set; } | |
| 1671 | + | |
| 1672 | + /// <summary> | |
| 1673 | + /// 支出分类名称 | |
| 1674 | + /// </summary> | |
| 1675 | + public string reimbursementCategoryName { get; set; } | |
| 1676 | + | |
| 1677 | + /// <summary> | |
| 1678 | + /// 单价 | |
| 1679 | + /// </summary> | |
| 1680 | + public decimal unitPrice { get; set; } | |
| 1681 | + | |
| 1682 | + /// <summary> | |
| 1683 | + /// 数量 | |
| 1684 | + /// </summary> | |
| 1685 | + public int quantity { get; set; } | |
| 1686 | + | |
| 1687 | + /// <summary> | |
| 1688 | + /// 金额 | |
| 1689 | + /// </summary> | |
| 1690 | + public decimal amount { get; set; } | |
| 1691 | + | |
| 1692 | + /// <summary> | |
| 1693 | + /// 备注说明 | |
| 1694 | + /// </summary> | |
| 1695 | + public string memo { get; set; } | |
| 1696 | + | |
| 1697 | + /// <summary> | |
| 1698 | + /// 购买时间 | |
| 1699 | + /// </summary> | |
| 1700 | + public DateTime? purchaseTime { get; set; } | |
| 1473 | 1701 | } |
| 1474 | 1702 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
| ... | ... | @@ -4070,12 +4070,14 @@ namespace NCC.Extend.LqStatistics |
| 4070 | 4070 | var whereClause = whereConditions.Any() ? "WHERE " + string.Join(" AND ", whereConditions) : ""; |
| 4071 | 4071 | |
| 4072 | 4072 | // 使用子查询优化性能,避免复杂的JOIN和GROUP BY |
| 4073 | + // 注意:邀约、预约、消耗、开单表存储的是会员ID(F_MemberId),不是线索池ID(F_Id) | |
| 4074 | + // 需要通过线索池的F_MemberId去关联这些表 | |
| 4073 | 4075 | var sql = $@" |
| 4074 | 4076 | SELECT |
| 4075 | 4077 | tk.F_Id as LeadCustomerId, |
| 4076 | 4078 | tk.F_CustomerName as CustomerName, |
| 4077 | 4079 | tk.F_ExpansionTime as ExpansionTime, |
| 4078 | - -- 是否邀约:通过拓客编号关联 | |
| 4080 | + -- 是否邀约:通过会员ID关联(邀约表的yykh字段存储的是会员ID) | |
| 4079 | 4081 | CASE WHEN yaoy_stats.has_invite = 1 THEN '是' ELSE '否' END as HasInvite, |
| 4080 | 4082 | -- 是否预约:通过邀约ID关联(只统计通过邀约产生的预约) |
| 4081 | 4083 | CASE WHEN yy_stats.has_appointment = 1 THEN '是' ELSE '否' END as HasAppointment, |
| ... | ... | @@ -4096,37 +4098,41 @@ namespace NCC.Extend.LqStatistics |
| 4096 | 4098 | -- 实际开单记录数(不管是否通过预约产生) |
| 4097 | 4099 | COALESCE(kd_actual.count, 0) as ActualBillingCount |
| 4098 | 4100 | FROM lq_tkjlb tk |
| 4099 | - -- 邀约统计子查询 | |
| 4101 | + -- 邀约统计子查询(通过会员ID关联:邀约表的yykh字段存储的是会员ID) | |
| 4100 | 4102 | LEFT JOIN ( |
| 4101 | 4103 | SELECT |
| 4102 | - yaoy.tkbh as tk_id, | |
| 4104 | + yaoy.yykh as member_id, | |
| 4103 | 4105 | 1 as has_invite |
| 4104 | 4106 | FROM lq_yaoyjl yaoy |
| 4105 | - GROUP BY yaoy.tkbh | |
| 4106 | - ) yaoy_stats ON yaoy_stats.tk_id = tk.F_Id | |
| 4107 | + WHERE yaoy.yykh IS NOT NULL | |
| 4108 | + GROUP BY yaoy.yykh | |
| 4109 | + ) yaoy_stats ON yaoy_stats.member_id = tk.F_MemberId | |
| 4107 | 4110 | -- 预约统计子查询(只统计通过邀约产生的预约) |
| 4111 | + -- 通过会员ID关联:线索池 -> 邀约(通过会员ID) -> 预约(通过邀约ID,且会员ID匹配) | |
| 4108 | 4112 | LEFT JOIN ( |
| 4109 | 4113 | SELECT |
| 4110 | 4114 | tk_inner.F_MemberId as member_id, |
| 4111 | 4115 | 1 as has_appointment, |
| 4112 | 4116 | MAX(yy.F_NoDealRemark) as no_billing_reason |
| 4113 | 4117 | FROM lq_tkjlb tk_inner |
| 4114 | - INNER JOIN lq_yaoyjl yaoy ON yaoy.tkbh = tk_inner.F_Id | |
| 4115 | - INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id | |
| 4118 | + INNER JOIN lq_yaoyjl yaoy ON yaoy.yykh = tk_inner.F_MemberId | |
| 4119 | + INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id AND yy.gk = tk_inner.F_MemberId | |
| 4116 | 4120 | GROUP BY tk_inner.F_MemberId |
| 4117 | 4121 | ) yy_stats ON yy_stats.member_id = tk.F_MemberId |
| 4118 | 4122 | -- 消耗统计子查询(只统计通过预约产生的耗卡) |
| 4123 | + -- 通过会员ID关联:线索池 -> 邀约 -> 预约 -> 消耗(通过预约ID,且会员ID匹配) | |
| 4119 | 4124 | LEFT JOIN ( |
| 4120 | 4125 | SELECT |
| 4121 | 4126 | tk_inner.F_MemberId as member_id, |
| 4122 | 4127 | 1 as has_consume |
| 4123 | 4128 | FROM lq_tkjlb tk_inner |
| 4124 | - INNER JOIN lq_yaoyjl yaoy ON yaoy.tkbh = tk_inner.F_Id | |
| 4125 | - INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id | |
| 4126 | - INNER JOIN lq_xh_hyhk xh ON xh.F_AppointmentId = yy.F_Id AND xh.F_IsEffective = 1 | |
| 4129 | + INNER JOIN lq_yaoyjl yaoy ON yaoy.yykh = tk_inner.F_MemberId | |
| 4130 | + INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id AND yy.gk = tk_inner.F_MemberId | |
| 4131 | + INNER JOIN lq_xh_hyhk xh ON xh.F_AppointmentId = yy.F_Id AND xh.hy = tk_inner.F_MemberId AND xh.F_IsEffective = 1 | |
| 4127 | 4132 | GROUP BY tk_inner.F_MemberId |
| 4128 | 4133 | ) xh_stats ON xh_stats.member_id = tk.F_MemberId |
| 4129 | 4134 | -- 开单统计子查询(只统计通过预约产生的开单,包含金额和品项) |
| 4135 | + -- 通过会员ID关联:线索池 -> 邀约 -> 预约 -> 开单(通过预约ID,且会员ID匹配) | |
| 4130 | 4136 | LEFT JOIN ( |
| 4131 | 4137 | SELECT |
| 4132 | 4138 | tk_inner.F_MemberId as member_id, |
| ... | ... | @@ -4134,9 +4140,9 @@ namespace NCC.Extend.LqStatistics |
| 4134 | 4140 | SUM(kd.zdyj) as billing_amount, |
| 4135 | 4141 | GROUP_CONCAT(DISTINCT kdpx.pxmc SEPARATOR '、') as billing_items |
| 4136 | 4142 | FROM lq_tkjlb tk_inner |
| 4137 | - INNER JOIN lq_yaoyjl yaoy ON yaoy.tkbh = tk_inner.F_Id | |
| 4138 | - INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id | |
| 4139 | - INNER JOIN lq_kd_kdjlb kd ON kd.F_AppointmentId = yy.F_Id AND kd.F_IsEffective = 1 | |
| 4143 | + INNER JOIN lq_yaoyjl yaoy ON yaoy.yykh = tk_inner.F_MemberId | |
| 4144 | + INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id AND yy.gk = tk_inner.F_MemberId | |
| 4145 | + INNER JOIN lq_kd_kdjlb kd ON kd.F_AppointmentId = yy.F_Id AND kd.kdhy = tk_inner.F_MemberId AND kd.F_IsEffective = 1 | |
| 4140 | 4146 | LEFT JOIN lq_kd_pxmx kdpx ON kdpx.glkdbh = kd.F_Id AND kdpx.F_IsEffective = 1 |
| 4141 | 4147 | GROUP BY tk_inner.F_MemberId |
| 4142 | 4148 | ) kd_stats ON kd_stats.member_id = tk.F_MemberId |
| ... | ... | @@ -4186,40 +4192,69 @@ namespace NCC.Extend.LqStatistics |
| 4186 | 4192 | var result = await _db.Ado.SqlQueryAsync<LeadCustomerStatisticsListOutput>(sql, parameters); |
| 4187 | 4193 | |
| 4188 | 4194 | // 生成问题分析说明 |
| 4195 | + // 完整链路1:邀约 -> 预约 -> 开单 | |
| 4196 | + // 完整链路2:邀约 -> 预约 -> 消耗 | |
| 4189 | 4197 | foreach (var item in result) |
| 4190 | 4198 | { |
| 4191 | 4199 | var analysisList = new List<string>(); |
| 4200 | + var completeChains = new List<string>(); | |
| 4201 | + | |
| 4202 | + // 判断是否形成完整链路 | |
| 4203 | + if (item.HasInvite == "是" && item.HasAppointment == "是") | |
| 4204 | + { | |
| 4205 | + if (item.HasBilling == "是") | |
| 4206 | + { | |
| 4207 | + completeChains.Add("邀约->预约->开单"); | |
| 4208 | + } | |
| 4209 | + if (item.HasConsume == "是") | |
| 4210 | + { | |
| 4211 | + completeChains.Add("邀约->预约->消耗"); | |
| 4212 | + } | |
| 4213 | + } | |
| 4192 | 4214 | |
| 4215 | + // 如果有完整链路,先说明链路完整 | |
| 4216 | + if (completeChains.Count > 0) | |
| 4217 | + { | |
| 4218 | + analysisList.Add($"✓ 链路完整:{string.Join("、", completeChains)}"); | |
| 4219 | + } | |
| 4220 | + | |
| 4221 | + // 有预约记录,但未通过邀约产生 | |
| 4193 | 4222 | if (item.HasInvite == "否" && item.HasAppointment == "否" && item.ActualAppointmentCount > 0) |
| 4194 | 4223 | { |
| 4195 | 4224 | analysisList.Add($"有{item.ActualAppointmentCount}条预约记录,但未通过邀约产生(F_InviteId为null)"); |
| 4196 | 4225 | } |
| 4197 | 4226 | |
| 4227 | + // 有消耗记录,但未通过完整链路产生(邀约->预约->消耗) | |
| 4198 | 4228 | if (item.HasAppointment == "否" && item.HasConsume == "否" && item.ActualConsumeCount > 0) |
| 4199 | 4229 | { |
| 4200 | - analysisList.Add($"有{item.ActualConsumeCount}条消耗记录,但未通过预约产生(F_AppointmentId为null或预约未通过邀约产生)"); | |
| 4230 | + analysisList.Add($"有{item.ActualConsumeCount}条消耗记录,但未通过完整链路产生(邀约->预约->消耗)"); | |
| 4201 | 4231 | } |
| 4202 | 4232 | |
| 4233 | + // 有开单记录,但未通过完整链路产生(邀约->预约->开单) | |
| 4203 | 4234 | if (item.HasAppointment == "否" && item.HasBilling == "否" && item.ActualBillingCount > 0) |
| 4204 | 4235 | { |
| 4205 | - analysisList.Add($"有{item.ActualBillingCount}条开单记录,但未通过预约产生(F_AppointmentId为null或预约未通过邀约产生)"); | |
| 4236 | + analysisList.Add($"有{item.ActualBillingCount}条开单记录,但未通过完整链路产生(邀约->预约->开单)"); | |
| 4206 | 4237 | } |
| 4207 | 4238 | |
| 4239 | + // 有邀约记录,有预约记录,但预约记录的F_InviteId未关联到邀约记录 | |
| 4208 | 4240 | if (item.HasInvite == "是" && item.HasAppointment == "否" && item.ActualAppointmentCount > 0) |
| 4209 | 4241 | { |
| 4210 | 4242 | analysisList.Add($"有邀约记录,有{item.ActualAppointmentCount}条预约记录,但预约记录的F_InviteId未关联到邀约记录"); |
| 4211 | 4243 | } |
| 4212 | 4244 | |
| 4245 | + // 有预约记录(通过邀约产生),有消耗记录,但消耗记录的F_AppointmentId未关联到预约记录 | |
| 4213 | 4246 | if (item.HasAppointment == "是" && item.HasConsume == "否" && item.ActualConsumeCount > 0) |
| 4214 | 4247 | { |
| 4215 | - analysisList.Add($"有预约记录,有{item.ActualConsumeCount}条消耗记录,但消耗记录的F_AppointmentId未关联到预约记录"); | |
| 4248 | + analysisList.Add($"有预约记录(通过邀约产生),有{item.ActualConsumeCount}条消耗记录,但消耗记录的F_AppointmentId未关联到预约记录,未形成完整链路(邀约->预约->消耗)"); | |
| 4216 | 4249 | } |
| 4217 | 4250 | |
| 4251 | + // 有预约记录(通过邀约产生),有开单记录,但开单记录的F_AppointmentId未关联到预约记录 | |
| 4218 | 4252 | if (item.HasAppointment == "是" && item.HasBilling == "否" && item.ActualBillingCount > 0) |
| 4219 | 4253 | { |
| 4220 | - analysisList.Add($"有预约记录,有{item.ActualBillingCount}条开单记录,但开单记录的F_AppointmentId未关联到预约记录"); | |
| 4254 | + analysisList.Add($"有预约记录(通过邀约产生),有{item.ActualBillingCount}条开单记录,但开单记录的F_AppointmentId未关联到预约记录,未形成完整链路(邀约->预约->开单)"); | |
| 4221 | 4255 | } |
| 4222 | 4256 | |
| 4257 | + // 生成最终分析说明 | |
| 4223 | 4258 | if (analysisList.Count == 0) |
| 4224 | 4259 | { |
| 4225 | 4260 | item.Analysis = "数据正常,符合业务链路:拓客 -> 邀约 -> 预约 -> 开单/消耗"; |
| ... | ... | @@ -4394,37 +4429,41 @@ namespace NCC.Extend.LqStatistics |
| 4394 | 4429 | -- 实际开单记录数(不管是否通过预约产生) |
| 4395 | 4430 | COALESCE(kd_actual.count, 0) as ActualBillingCount |
| 4396 | 4431 | FROM lq_tkjlb tk |
| 4397 | - -- 邀约统计子查询 | |
| 4432 | + -- 邀约统计子查询(通过会员ID关联:邀约表的yykh字段存储的是会员ID) | |
| 4398 | 4433 | LEFT JOIN ( |
| 4399 | 4434 | SELECT |
| 4400 | - yaoy.tkbh as tk_id, | |
| 4435 | + yaoy.yykh as member_id, | |
| 4401 | 4436 | 1 as has_invite |
| 4402 | 4437 | FROM lq_yaoyjl yaoy |
| 4403 | - GROUP BY yaoy.tkbh | |
| 4404 | - ) yaoy_stats ON yaoy_stats.tk_id = tk.F_Id | |
| 4438 | + WHERE yaoy.yykh IS NOT NULL | |
| 4439 | + GROUP BY yaoy.yykh | |
| 4440 | + ) yaoy_stats ON yaoy_stats.member_id = tk.F_MemberId | |
| 4405 | 4441 | -- 预约统计子查询(只统计通过邀约产生的预约) |
| 4442 | + -- 通过会员ID关联:线索池 -> 邀约(通过会员ID) -> 预约(通过邀约ID,且会员ID匹配) | |
| 4406 | 4443 | LEFT JOIN ( |
| 4407 | 4444 | SELECT |
| 4408 | 4445 | tk_inner.F_MemberId as member_id, |
| 4409 | 4446 | 1 as has_appointment, |
| 4410 | 4447 | MAX(yy.F_NoDealRemark) as no_billing_reason |
| 4411 | 4448 | FROM lq_tkjlb tk_inner |
| 4412 | - INNER JOIN lq_yaoyjl yaoy ON yaoy.tkbh = tk_inner.F_Id | |
| 4413 | - INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id | |
| 4449 | + INNER JOIN lq_yaoyjl yaoy ON yaoy.yykh = tk_inner.F_MemberId | |
| 4450 | + INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id AND yy.gk = tk_inner.F_MemberId | |
| 4414 | 4451 | GROUP BY tk_inner.F_MemberId |
| 4415 | 4452 | ) yy_stats ON yy_stats.member_id = tk.F_MemberId |
| 4416 | 4453 | -- 消耗统计子查询(只统计通过预约产生的耗卡) |
| 4454 | + -- 通过会员ID关联:线索池 -> 邀约 -> 预约 -> 消耗(通过预约ID,且会员ID匹配) | |
| 4417 | 4455 | LEFT JOIN ( |
| 4418 | 4456 | SELECT |
| 4419 | 4457 | tk_inner.F_MemberId as member_id, |
| 4420 | 4458 | 1 as has_consume |
| 4421 | 4459 | FROM lq_tkjlb tk_inner |
| 4422 | - INNER JOIN lq_yaoyjl yaoy ON yaoy.tkbh = tk_inner.F_Id | |
| 4423 | - INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id | |
| 4424 | - INNER JOIN lq_xh_hyhk xh ON xh.F_AppointmentId = yy.F_Id AND xh.F_IsEffective = 1 | |
| 4460 | + INNER JOIN lq_yaoyjl yaoy ON yaoy.yykh = tk_inner.F_MemberId | |
| 4461 | + INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id AND yy.gk = tk_inner.F_MemberId | |
| 4462 | + INNER JOIN lq_xh_hyhk xh ON xh.F_AppointmentId = yy.F_Id AND xh.hy = tk_inner.F_MemberId AND xh.F_IsEffective = 1 | |
| 4425 | 4463 | GROUP BY tk_inner.F_MemberId |
| 4426 | 4464 | ) xh_stats ON xh_stats.member_id = tk.F_MemberId |
| 4427 | 4465 | -- 开单统计子查询(只统计通过预约产生的开单,包含金额和品项) |
| 4466 | + -- 通过会员ID关联:线索池 -> 邀约 -> 预约 -> 开单(通过预约ID,且会员ID匹配) | |
| 4428 | 4467 | LEFT JOIN ( |
| 4429 | 4468 | SELECT |
| 4430 | 4469 | tk_inner.F_MemberId as member_id, |
| ... | ... | @@ -4432,9 +4471,9 @@ namespace NCC.Extend.LqStatistics |
| 4432 | 4471 | SUM(kd.zdyj) as billing_amount, |
| 4433 | 4472 | GROUP_CONCAT(DISTINCT kdpx.pxmc SEPARATOR '、') as billing_items |
| 4434 | 4473 | FROM lq_tkjlb tk_inner |
| 4435 | - INNER JOIN lq_yaoyjl yaoy ON yaoy.tkbh = tk_inner.F_Id | |
| 4436 | - INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id | |
| 4437 | - INNER JOIN lq_kd_kdjlb kd ON kd.F_AppointmentId = yy.F_Id AND kd.F_IsEffective = 1 | |
| 4474 | + INNER JOIN lq_yaoyjl yaoy ON yaoy.yykh = tk_inner.F_MemberId | |
| 4475 | + INNER JOIN lq_yyjl yy ON yy.F_InviteId = yaoy.F_Id AND yy.gk = tk_inner.F_MemberId | |
| 4476 | + INNER JOIN lq_kd_kdjlb kd ON kd.F_AppointmentId = yy.F_Id AND kd.kdhy = tk_inner.F_MemberId AND kd.F_IsEffective = 1 | |
| 4438 | 4477 | LEFT JOIN lq_kd_pxmx kdpx ON kdpx.glkdbh = kd.F_Id AND kdpx.F_IsEffective = 1 |
| 4439 | 4478 | GROUP BY tk_inner.F_MemberId |
| 4440 | 4479 | ) kd_stats ON kd_stats.member_id = tk.F_MemberId | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqStoreExpenseService.cs
0 → 100644
| 1 | +using NCC.Common.Core.Manager; | |
| 2 | +using NCC.Common.Enum; | |
| 3 | +using NCC.Common.Extension; | |
| 4 | +using NCC.Common.Filter; | |
| 5 | +using NCC.Dependency; | |
| 6 | +using NCC.DynamicApiController; | |
| 7 | +using NCC.FriendlyException; | |
| 8 | +using NCC.Extend.Interfaces.LqStoreExpense; | |
| 9 | +using Mapster; | |
| 10 | +using Microsoft.AspNetCore.Mvc; | |
| 11 | +using SqlSugar; | |
| 12 | +using System; | |
| 13 | +using System.Collections.Generic; | |
| 14 | +using System.Linq; | |
| 15 | +using System.Threading.Tasks; | |
| 16 | +using NCC.Extend.Entitys.lq_store_expense; | |
| 17 | +using NCC.Extend.Entitys.Dto.LqStoreExpense; | |
| 18 | +using NCC.Extend.Entitys.lq_mdxx; | |
| 19 | +using Yitter.IdGenerator; | |
| 20 | +using NCC.Common.Helper; | |
| 21 | +using NCC.Common.Model.NPOI; | |
| 22 | +using NCC.Common.Configuration; | |
| 23 | +using NCC.DataEncryption; | |
| 24 | +using NCC.ClayObject; | |
| 25 | +using NCC.Common.Const; | |
| 26 | +using NCC.JsonSerialization; | |
| 27 | +using NCC.Extend.Entitys.Enum; | |
| 28 | +using Microsoft.AspNetCore.Http; | |
| 29 | +using System.IO; | |
| 30 | +using System.Data; | |
| 31 | + | |
| 32 | +namespace NCC.Extend.LqStoreExpense | |
| 33 | +{ | |
| 34 | + /// <summary> | |
| 35 | + /// 店内支出表服务 | |
| 36 | + /// </summary> | |
| 37 | + [ApiDescriptionSettings(Tag = "Extend", Name = "LqStoreExpense", Order = 200)] | |
| 38 | + [Route("api/Extend/[controller]")] | |
| 39 | + public class LqStoreExpenseService : ILqStoreExpenseService, IDynamicApiController, ITransient | |
| 40 | + { | |
| 41 | + private readonly ISqlSugarRepository<LqStoreExpenseEntity> _repository; | |
| 42 | + private readonly SqlSugarScope _db; | |
| 43 | + private readonly IUserManager _userManager; | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 初始化一个<see cref="LqStoreExpenseService"/>类型的新实例 | |
| 47 | + /// </summary> | |
| 48 | + public LqStoreExpenseService( | |
| 49 | + ISqlSugarRepository<LqStoreExpenseEntity> repository, | |
| 50 | + IUserManager userManager) | |
| 51 | + { | |
| 52 | + _repository = repository; | |
| 53 | + _db = _repository.Context; | |
| 54 | + _userManager = userManager; | |
| 55 | + } | |
| 56 | + | |
| 57 | + /// <summary> | |
| 58 | + /// 获取店内支出表详情 | |
| 59 | + /// </summary> | |
| 60 | + /// <param name="id">主键ID</param> | |
| 61 | + /// <returns></returns> | |
| 62 | + [HttpGet("{id}")] | |
| 63 | + public async Task<dynamic> GetInfo(string id) | |
| 64 | + { | |
| 65 | + var entity = await _db.Queryable<LqStoreExpenseEntity>() | |
| 66 | + .Where(x => x.Id == id && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 67 | + .FirstAsync(); | |
| 68 | + _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); | |
| 69 | + var output = entity.Adapt<LqStoreExpenseInfoOutput>(); | |
| 70 | + if (!string.IsNullOrEmpty(entity.Attachment)) | |
| 71 | + { | |
| 72 | + output.attachment = entity.Attachment.ToObject<List<NCC.Common.Model.FileControlsModel>>(); | |
| 73 | + } | |
| 74 | + return output; | |
| 75 | + } | |
| 76 | + | |
| 77 | + /// <summary> | |
| 78 | + /// 获取店内支出表列表 | |
| 79 | + /// </summary> | |
| 80 | + /// <param name="input">请求参数</param> | |
| 81 | + /// <returns></returns> | |
| 82 | + [HttpGet("")] | |
| 83 | + public async Task<dynamic> GetList([FromQuery] LqStoreExpenseListQueryInput input) | |
| 84 | + { | |
| 85 | + var sidx = input.sidx ?? "ExpenseDate"; | |
| 86 | + var sortType = input.sort?.ToLower() == "asc" ? OrderByType.Asc : OrderByType.Desc; | |
| 87 | + List<string> queryExpenseDate = input.expenseDateStart != null && input.expenseDateEnd != null | |
| 88 | + ? new List<string> { input.expenseDateStart, input.expenseDateEnd } | |
| 89 | + : null; | |
| 90 | + DateTime? startExpenseDate = queryExpenseDate != null ? Ext.GetDateTime(queryExpenseDate.First()) : null; | |
| 91 | + DateTime? endExpenseDate = queryExpenseDate != null ? Ext.GetDateTime(queryExpenseDate.Last()) : null; | |
| 92 | + | |
| 93 | + var query = _db.Queryable<LqStoreExpenseEntity>() | |
| 94 | + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 95 | + .WhereIF(!string.IsNullOrEmpty(input.storeId), x => x.StoreId == input.storeId) | |
| 96 | + .WhereIF(!string.IsNullOrEmpty(input.storeName), x => x.StoreName.Contains(input.storeName)) | |
| 97 | + .WhereIF(!string.IsNullOrEmpty(input.expenseCategoryId), x => x.ExpenseCategoryId == input.expenseCategoryId) | |
| 98 | + .WhereIF(queryExpenseDate != null, x => x.ExpenseDate >= new DateTime(startExpenseDate.ToDate().Year, startExpenseDate.ToDate().Month, startExpenseDate.ToDate().Day, 0, 0, 0)) | |
| 99 | + .WhereIF(queryExpenseDate != null, x => x.ExpenseDate <= new DateTime(endExpenseDate.ToDate().Year, endExpenseDate.ToDate().Month, endExpenseDate.ToDate().Day, 23, 59, 59)); | |
| 100 | + | |
| 101 | + // 处理排序 | |
| 102 | + switch (sidx.ToLower()) | |
| 103 | + { | |
| 104 | + case "id": | |
| 105 | + query = query.OrderBy(x => x.Id, sortType); | |
| 106 | + break; | |
| 107 | + case "expensedate": | |
| 108 | + query = query.OrderBy(x => x.ExpenseDate, sortType); | |
| 109 | + break; | |
| 110 | + case "storename": | |
| 111 | + query = query.OrderBy(x => x.StoreName, sortType); | |
| 112 | + break; | |
| 113 | + case "amount": | |
| 114 | + query = query.OrderBy(x => x.Amount, sortType); | |
| 115 | + break; | |
| 116 | + case "createtime": | |
| 117 | + query = query.OrderBy(x => x.CreateTime, sortType); | |
| 118 | + break; | |
| 119 | + default: | |
| 120 | + query = query.OrderBy(x => x.ExpenseDate, OrderByType.Desc); | |
| 121 | + break; | |
| 122 | + } | |
| 123 | + | |
| 124 | + var total = await query.CountAsync(); | |
| 125 | + var list = await query.ToPageListAsync(input.currentPage, input.pageSize); | |
| 126 | + | |
| 127 | + var result = list.Select(x => new LqStoreExpenseListOutput | |
| 128 | + { | |
| 129 | + id = x.Id, | |
| 130 | + storeId = x.StoreId, | |
| 131 | + storeName = x.StoreName, | |
| 132 | + expenseCategoryId = x.ExpenseCategoryId, | |
| 133 | + expenseCategoryName = x.ExpenseCategoryName, | |
| 134 | + expenseDate = x.ExpenseDate, | |
| 135 | + unitPrice = x.UnitPrice, | |
| 136 | + quantity = x.Quantity, | |
| 137 | + amount = x.Amount, | |
| 138 | + memo = x.Memo, | |
| 139 | + relatedReimbursementId = x.RelatedReimbursementId, | |
| 140 | + relatedPurchaseRecordId = x.RelatedPurchaseRecordId, | |
| 141 | + createUser = x.CreateUser, | |
| 142 | + createTime = x.CreateTime, | |
| 143 | + updateUser = x.UpdateUser, | |
| 144 | + updateTime = x.UpdateTime | |
| 145 | + }).ToList(); | |
| 146 | + | |
| 147 | + return PageResult<LqStoreExpenseListOutput>.SqlSugarPageResult( | |
| 148 | + new SqlSugarPagedList<LqStoreExpenseListOutput> | |
| 149 | + { | |
| 150 | + list = result, | |
| 151 | + pagination = new PagedModel { PageIndex = input.currentPage, PageSize = input.pageSize, Total = total } | |
| 152 | + }); | |
| 153 | + } | |
| 154 | + | |
| 155 | + /// <summary> | |
| 156 | + /// 获取店内支出表无分页列表 | |
| 157 | + /// </summary> | |
| 158 | + /// <param name="input">请求参数</param> | |
| 159 | + /// <returns></returns> | |
| 160 | + [HttpGet("Actions/GetNoPagingList")] | |
| 161 | + public async Task<List<LqStoreExpenseListOutput>> GetNoPagingList([FromQuery] LqStoreExpenseListQueryInput input) | |
| 162 | + { | |
| 163 | + var sidx = input.sidx ?? "ExpenseDate"; | |
| 164 | + var sortType = input.sort?.ToLower() == "asc" ? OrderByType.Asc : OrderByType.Desc; | |
| 165 | + List<string> queryExpenseDate = input.expenseDateStart != null && input.expenseDateEnd != null | |
| 166 | + ? new List<string> { input.expenseDateStart, input.expenseDateEnd } | |
| 167 | + : null; | |
| 168 | + DateTime? startExpenseDate = queryExpenseDate != null ? Ext.GetDateTime(queryExpenseDate.First()) : null; | |
| 169 | + DateTime? endExpenseDate = queryExpenseDate != null ? Ext.GetDateTime(queryExpenseDate.Last()) : null; | |
| 170 | + | |
| 171 | + var query = _db.Queryable<LqStoreExpenseEntity>() | |
| 172 | + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 173 | + .WhereIF(!string.IsNullOrEmpty(input.storeId), x => x.StoreId == input.storeId) | |
| 174 | + .WhereIF(!string.IsNullOrEmpty(input.storeName), x => x.StoreName.Contains(input.storeName)) | |
| 175 | + .WhereIF(!string.IsNullOrEmpty(input.expenseCategoryId), x => x.ExpenseCategoryId == input.expenseCategoryId) | |
| 176 | + .WhereIF(queryExpenseDate != null, x => x.ExpenseDate >= new DateTime(startExpenseDate.ToDate().Year, startExpenseDate.ToDate().Month, startExpenseDate.ToDate().Day, 0, 0, 0)) | |
| 177 | + .WhereIF(queryExpenseDate != null, x => x.ExpenseDate <= new DateTime(endExpenseDate.ToDate().Year, endExpenseDate.ToDate().Month, endExpenseDate.ToDate().Day, 23, 59, 59)); | |
| 178 | + | |
| 179 | + // 处理排序 | |
| 180 | + switch (sidx.ToLower()) | |
| 181 | + { | |
| 182 | + case "id": | |
| 183 | + query = query.OrderBy(x => x.Id, sortType); | |
| 184 | + break; | |
| 185 | + case "expensedate": | |
| 186 | + query = query.OrderBy(x => x.ExpenseDate, sortType); | |
| 187 | + break; | |
| 188 | + case "storename": | |
| 189 | + query = query.OrderBy(x => x.StoreName, sortType); | |
| 190 | + break; | |
| 191 | + case "amount": | |
| 192 | + query = query.OrderBy(x => x.Amount, sortType); | |
| 193 | + break; | |
| 194 | + case "createtime": | |
| 195 | + query = query.OrderBy(x => x.CreateTime, sortType); | |
| 196 | + break; | |
| 197 | + default: | |
| 198 | + query = query.OrderBy(x => x.ExpenseDate, OrderByType.Desc); | |
| 199 | + break; | |
| 200 | + } | |
| 201 | + | |
| 202 | + var list = await query | |
| 203 | + .Select(x => new LqStoreExpenseListOutput | |
| 204 | + { | |
| 205 | + id = x.Id, | |
| 206 | + storeId = x.StoreId, | |
| 207 | + storeName = x.StoreName, | |
| 208 | + expenseCategoryId = x.ExpenseCategoryId, | |
| 209 | + expenseCategoryName = x.ExpenseCategoryName, | |
| 210 | + expenseDate = x.ExpenseDate, | |
| 211 | + unitPrice = x.UnitPrice, | |
| 212 | + quantity = x.Quantity, | |
| 213 | + amount = x.Amount, | |
| 214 | + memo = x.Memo, | |
| 215 | + relatedReimbursementId = x.RelatedReimbursementId, | |
| 216 | + relatedPurchaseRecordId = x.RelatedPurchaseRecordId, | |
| 217 | + createUser = x.CreateUser, | |
| 218 | + createTime = x.CreateTime, | |
| 219 | + updateUser = x.UpdateUser, | |
| 220 | + updateTime = x.UpdateTime | |
| 221 | + }) | |
| 222 | + .ToListAsync(); | |
| 223 | + return list; | |
| 224 | + } | |
| 225 | + | |
| 226 | + /// <summary> | |
| 227 | + /// 创建店内支出表 | |
| 228 | + /// </summary> | |
| 229 | + /// <param name="input">参数</param> | |
| 230 | + /// <returns></returns> | |
| 231 | + [HttpPost("")] | |
| 232 | + public async Task Create([FromBody] LqStoreExpenseCrInput input) | |
| 233 | + { | |
| 234 | + var userInfo = await _userManager.GetUserInfo(); | |
| 235 | + var entity = input.Adapt<LqStoreExpenseEntity>(); | |
| 236 | + entity.Id = YitIdHelper.NextId().ToString(); | |
| 237 | + entity.IsEffective = StatusEnum.有效.GetHashCode(); | |
| 238 | + entity.CreateUser = _userManager.UserId; | |
| 239 | + entity.CreateTime = DateTime.Now; | |
| 240 | + | |
| 241 | + if (input.attachment != null && input.attachment.Any()) | |
| 242 | + { | |
| 243 | + entity.Attachment = input.attachment.ToJson(); | |
| 244 | + } | |
| 245 | + | |
| 246 | + // 如果未提供门店名称,根据门店ID查询 | |
| 247 | + if (string.IsNullOrEmpty(entity.StoreName) && !string.IsNullOrEmpty(entity.StoreId)) | |
| 248 | + { | |
| 249 | + var store = await _db.Queryable<LqMdxxEntity>() | |
| 250 | + .Where(x => x.Id == entity.StoreId) | |
| 251 | + .Select(x => x.Dm) | |
| 252 | + .FirstAsync(); | |
| 253 | + entity.StoreName = store; | |
| 254 | + } | |
| 255 | + | |
| 256 | + var isOk = await _db.Insertable(entity).ExecuteCommandAsync(); | |
| 257 | + if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); | |
| 258 | + } | |
| 259 | + | |
| 260 | + /// <summary> | |
| 261 | + /// 更新店内支出表 | |
| 262 | + /// </summary> | |
| 263 | + /// <param name="id">主键</param> | |
| 264 | + /// <param name="input">参数</param> | |
| 265 | + /// <returns></returns> | |
| 266 | + [HttpPut("{id}")] | |
| 267 | + public async Task Update(string id, [FromBody] LqStoreExpenseUpInput input) | |
| 268 | + { | |
| 269 | + var entity = await _db.Queryable<LqStoreExpenseEntity>() | |
| 270 | + .Where(x => x.Id == id && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 271 | + .FirstAsync(); | |
| 272 | + _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); | |
| 273 | + | |
| 274 | + entity.StoreId = input.storeId; | |
| 275 | + entity.StoreName = input.storeName; | |
| 276 | + entity.ExpenseCategoryId = input.expenseCategoryId; | |
| 277 | + entity.ExpenseCategoryName = input.expenseCategoryName; | |
| 278 | + entity.ExpenseDate = input.expenseDate; | |
| 279 | + entity.UnitPrice = input.unitPrice; | |
| 280 | + entity.Quantity = input.quantity; | |
| 281 | + entity.Amount = input.amount; | |
| 282 | + entity.Memo = input.memo; | |
| 283 | + entity.RelatedReimbursementId = input.relatedReimbursementId; | |
| 284 | + entity.RelatedPurchaseRecordId = input.relatedPurchaseRecordId; | |
| 285 | + entity.UpdateUser = _userManager.UserId; | |
| 286 | + entity.UpdateTime = DateTime.Now; | |
| 287 | + | |
| 288 | + if (input.attachment != null && input.attachment.Any()) | |
| 289 | + { | |
| 290 | + entity.Attachment = input.attachment.ToJson(); | |
| 291 | + } | |
| 292 | + | |
| 293 | + // 如果未提供门店名称,根据门店ID查询 | |
| 294 | + if (string.IsNullOrEmpty(entity.StoreName) && !string.IsNullOrEmpty(entity.StoreId)) | |
| 295 | + { | |
| 296 | + var store = await _db.Queryable<LqMdxxEntity>() | |
| 297 | + .Where(x => x.Id == entity.StoreId) | |
| 298 | + .Select(x => x.Dm) | |
| 299 | + .FirstAsync(); | |
| 300 | + entity.StoreName = store; | |
| 301 | + } | |
| 302 | + | |
| 303 | + var isOk = await _db.Updateable(entity).ExecuteCommandAsync(); | |
| 304 | + if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1001); | |
| 305 | + } | |
| 306 | + | |
| 307 | + /// <summary> | |
| 308 | + /// 删除店内支出表 | |
| 309 | + /// </summary> | |
| 310 | + /// <param name="id">主键</param> | |
| 311 | + /// <returns></returns> | |
| 312 | + [HttpDelete("{id}")] | |
| 313 | + public async Task Delete(string id) | |
| 314 | + { | |
| 315 | + var entity = await _db.Queryable<LqStoreExpenseEntity>() | |
| 316 | + .Where(x => x.Id == id && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 317 | + .FirstAsync(); | |
| 318 | + _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); | |
| 319 | + | |
| 320 | + // 逻辑删除 | |
| 321 | + entity.IsEffective = StatusEnum.无效.GetHashCode(); | |
| 322 | + entity.UpdateUser = _userManager.UserId; | |
| 323 | + entity.UpdateTime = DateTime.Now; | |
| 324 | + | |
| 325 | + var isOk = await _db.Updateable(entity).ExecuteCommandAsync(); | |
| 326 | + if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1002); | |
| 327 | + } | |
| 328 | + | |
| 329 | + /// <summary> | |
| 330 | + /// 导出店内支出表 | |
| 331 | + /// </summary> | |
| 332 | + /// <param name="input">请求参数</param> | |
| 333 | + /// <returns></returns> | |
| 334 | + [HttpGet("Actions/Export")] | |
| 335 | + public async Task<dynamic> Export([FromQuery] LqStoreExpenseListQueryInput input) | |
| 336 | + { | |
| 337 | + var userInfo = await _userManager.GetUserInfo(); | |
| 338 | + var exportData = new List<LqStoreExpenseListOutput>(); | |
| 339 | + if (input.dataType == 0) | |
| 340 | + { | |
| 341 | + var data = Clay.Object(await this.GetList(input)); | |
| 342 | + exportData = data.Solidify<PageResult<LqStoreExpenseListOutput>>().list; | |
| 343 | + } | |
| 344 | + else | |
| 345 | + { | |
| 346 | + exportData = await this.GetNoPagingList(input); | |
| 347 | + } | |
| 348 | + List<ParamsModel> paramList = "[{\"value\":\"门店ID\",\"field\":\"storeId\"},{\"value\":\"门店名称\",\"field\":\"storeName\"},{\"value\":\"支出分类ID\",\"field\":\"expenseCategoryId\"},{\"value\":\"支出分类名称\",\"field\":\"expenseCategoryName\"},{\"value\":\"支出日期\",\"field\":\"expenseDate\"},{\"value\":\"单价\",\"field\":\"unitPrice\"},{\"value\":\"数量\",\"field\":\"quantity\"},{\"value\":\"金额\",\"field\":\"amount\"},{\"value\":\"备注说明\",\"field\":\"memo\"},{\"value\":\"关联报销申请ID\",\"field\":\"relatedReimbursementId\"},{\"value\":\"关联购买记录ID\",\"field\":\"relatedPurchaseRecordId\"},{\"value\":\"创建时间\",\"field\":\"createTime\"},]".ToList<ParamsModel>(); | |
| 349 | + ExcelConfig excelconfig = new ExcelConfig(); | |
| 350 | + excelconfig.FileName = "店内支出表.xls"; | |
| 351 | + excelconfig.HeadFont = "微软雅黑"; | |
| 352 | + excelconfig.HeadPoint = 10; | |
| 353 | + excelconfig.IsAllSizeColumn = true; | |
| 354 | + excelconfig.ColumnModel = new List<ExcelColumnModel>(); | |
| 355 | + List<string> selectKeyList = !string.IsNullOrEmpty(input.selectKey) ? input.selectKey.Split(',').ToList() : paramList.Select(p => p.field).ToList(); | |
| 356 | + foreach (var item in selectKeyList) | |
| 357 | + { | |
| 358 | + var isExist = paramList.Find(p => p.field == item); | |
| 359 | + if (isExist != null) | |
| 360 | + { | |
| 361 | + excelconfig.ColumnModel.Add(new ExcelColumnModel() { Column = isExist.field, ExcelColumn = isExist.value }); | |
| 362 | + } | |
| 363 | + } | |
| 364 | + var addPath = FileVariable.TemporaryFilePath + excelconfig.FileName; | |
| 365 | + ExcelExportHelper<LqStoreExpenseListOutput>.Export(exportData, excelconfig, addPath); | |
| 366 | + var fileName = _userManager.UserId + "|" + addPath + "|xls"; | |
| 367 | + var output = new | |
| 368 | + { | |
| 369 | + name = excelconfig.FileName, | |
| 370 | + url = "/api/File/Download?encryption=" + DESCEncryption.Encrypt(fileName, "NCC") | |
| 371 | + }; | |
| 372 | + return output; | |
| 373 | + } | |
| 374 | + | |
| 375 | + /// <summary> | |
| 376 | + /// 导入店内支出数据 | |
| 377 | + /// </summary> | |
| 378 | + /// <remarks> | |
| 379 | + /// 从Excel文件导入店内支出数据 | |
| 380 | + /// | |
| 381 | + /// Excel格式要求: | |
| 382 | + /// 第一行为标题行:门店ID、门店名称、支出分类ID、支出分类名称、支出日期、单价、数量、金额、备注说明、关联报销申请ID、关联购买记录ID | |
| 383 | + /// 从第二行开始为数据行 | |
| 384 | + /// | |
| 385 | + /// 示例请求: | |
| 386 | + /// POST /api/Extend/LqStoreExpense/Actions/Import | |
| 387 | + /// Content-Type: multipart/form-data | |
| 388 | + /// </remarks> | |
| 389 | + /// <param name="file">Excel文件</param> | |
| 390 | + /// <returns>导入结果</returns> | |
| 391 | + /// <response code="200">导入成功</response> | |
| 392 | + /// <response code="400">文件格式错误或数据验证失败</response> | |
| 393 | + [HttpPost("Actions/Import")] | |
| 394 | + public async Task<dynamic> Import(IFormFile file) | |
| 395 | + { | |
| 396 | + try | |
| 397 | + { | |
| 398 | + if (file == null || file.Length == 0) | |
| 399 | + { | |
| 400 | + throw NCCException.Oh("请选择要上传的Excel文件"); | |
| 401 | + } | |
| 402 | + | |
| 403 | + // 检查文件格式 | |
| 404 | + var allowedExtensions = new[] { ".xlsx", ".xls" }; | |
| 405 | + var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant(); | |
| 406 | + if (!allowedExtensions.Contains(fileExtension)) | |
| 407 | + { | |
| 408 | + throw NCCException.Oh("只支持.xlsx和.xls格式的Excel文件"); | |
| 409 | + } | |
| 410 | + | |
| 411 | + var successCount = 0; | |
| 412 | + var failCount = 0; | |
| 413 | + var failMessages = new List<string>(); | |
| 414 | + | |
| 415 | + // 保存临时文件 | |
| 416 | + var tempFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + Path.GetExtension(file.FileName)); | |
| 417 | + try | |
| 418 | + { | |
| 419 | + using (var stream = new FileStream(tempFilePath, FileMode.Create)) | |
| 420 | + { | |
| 421 | + await file.CopyToAsync(stream); | |
| 422 | + } | |
| 423 | + | |
| 424 | + // 使用ExcelImportHelper读取Excel文件(第一行为标题行) | |
| 425 | + var dataTable = ExcelImportHelper.ToDataTable(tempFilePath, 0, 0); | |
| 426 | + | |
| 427 | + if (dataTable.Rows.Count <= 1) | |
| 428 | + { | |
| 429 | + throw NCCException.Oh("Excel文件中没有数据行(至少需要标题行和一行数据)"); | |
| 430 | + } | |
| 431 | + | |
| 432 | + // 从第1行开始读取数据(跳过标题行) | |
| 433 | + for (int i = 1; i < dataTable.Rows.Count; i++) | |
| 434 | + { | |
| 435 | + try | |
| 436 | + { | |
| 437 | + var row = dataTable.Rows[i]; | |
| 438 | + var storeId = row[0]?.ToString()?.Trim(); | |
| 439 | + var storeName = row[1]?.ToString()?.Trim(); | |
| 440 | + var expenseCategoryId = row[2]?.ToString()?.Trim(); | |
| 441 | + var expenseCategoryName = row[3]?.ToString()?.Trim(); | |
| 442 | + var expenseDateText = row[4]?.ToString()?.Trim(); | |
| 443 | + var unitPriceText = row[5]?.ToString()?.Trim(); | |
| 444 | + var quantityText = row[6]?.ToString()?.Trim(); | |
| 445 | + var amountText = row[7]?.ToString()?.Trim(); | |
| 446 | + var memo = row[8]?.ToString()?.Trim(); | |
| 447 | + var relatedReimbursementId = row[9]?.ToString()?.Trim(); | |
| 448 | + var relatedPurchaseRecordId = row[10]?.ToString()?.Trim(); | |
| 449 | + | |
| 450 | + // 跳过空行 | |
| 451 | + if (string.IsNullOrEmpty(storeId) && string.IsNullOrEmpty(storeName)) | |
| 452 | + { | |
| 453 | + continue; | |
| 454 | + } | |
| 455 | + | |
| 456 | + // 验证必填字段 | |
| 457 | + if (string.IsNullOrEmpty(storeId)) | |
| 458 | + { | |
| 459 | + failMessages.Add($"第{i + 1}行:门店ID不能为空"); | |
| 460 | + failCount++; | |
| 461 | + continue; | |
| 462 | + } | |
| 463 | + | |
| 464 | + if (string.IsNullOrEmpty(expenseDateText) || !DateTime.TryParse(expenseDateText, out DateTime expenseDate)) | |
| 465 | + { | |
| 466 | + failMessages.Add($"第{i + 1}行:支出日期格式错误(应为日期格式,如:2025-01-15)"); | |
| 467 | + failCount++; | |
| 468 | + continue; | |
| 469 | + } | |
| 470 | + | |
| 471 | + if (string.IsNullOrEmpty(amountText) || !decimal.TryParse(amountText, out decimal amount)) | |
| 472 | + { | |
| 473 | + failMessages.Add($"第{i + 1}行:金额格式错误(应为数字)"); | |
| 474 | + failCount++; | |
| 475 | + continue; | |
| 476 | + } | |
| 477 | + | |
| 478 | + // 解析可选字段 | |
| 479 | + decimal unitPrice = 0m; | |
| 480 | + if (!string.IsNullOrEmpty(unitPriceText) && decimal.TryParse(unitPriceText, out decimal up)) | |
| 481 | + { | |
| 482 | + unitPrice = up; | |
| 483 | + } | |
| 484 | + | |
| 485 | + int quantity = 0; | |
| 486 | + if (!string.IsNullOrEmpty(quantityText) && int.TryParse(quantityText, out int qty)) | |
| 487 | + { | |
| 488 | + quantity = qty; | |
| 489 | + } | |
| 490 | + | |
| 491 | + // 如果未提供门店名称,根据门店ID查询 | |
| 492 | + if (string.IsNullOrEmpty(storeName)) | |
| 493 | + { | |
| 494 | + var store = await _db.Queryable<LqMdxxEntity>() | |
| 495 | + .Where(x => x.Id == storeId) | |
| 496 | + .Select(x => x.Dm) | |
| 497 | + .FirstAsync(); | |
| 498 | + storeName = store ?? ""; | |
| 499 | + } | |
| 500 | + | |
| 501 | + // 创建记录 | |
| 502 | + var entity = new LqStoreExpenseEntity | |
| 503 | + { | |
| 504 | + Id = YitIdHelper.NextId().ToString(), | |
| 505 | + StoreId = storeId, | |
| 506 | + StoreName = storeName, | |
| 507 | + ExpenseCategoryId = expenseCategoryId, | |
| 508 | + ExpenseCategoryName = expenseCategoryName, | |
| 509 | + ExpenseDate = expenseDate, | |
| 510 | + UnitPrice = unitPrice, | |
| 511 | + Quantity = quantity, | |
| 512 | + Amount = amount, | |
| 513 | + Memo = memo, | |
| 514 | + RelatedReimbursementId = relatedReimbursementId, | |
| 515 | + RelatedPurchaseRecordId = relatedPurchaseRecordId, | |
| 516 | + IsEffective = StatusEnum.有效.GetHashCode(), | |
| 517 | + CreateUser = _userManager.UserId, | |
| 518 | + CreateTime = DateTime.Now | |
| 519 | + }; | |
| 520 | + | |
| 521 | + var isOk = await _db.Insertable(entity).ExecuteCommandAsync(); | |
| 522 | + if (isOk > 0) | |
| 523 | + { | |
| 524 | + successCount++; | |
| 525 | + } | |
| 526 | + else | |
| 527 | + { | |
| 528 | + failMessages.Add($"第{i + 1}行:保存失败"); | |
| 529 | + failCount++; | |
| 530 | + } | |
| 531 | + } | |
| 532 | + catch (Exception ex) | |
| 533 | + { | |
| 534 | + failMessages.Add($"第{i + 1}行:{ex.Message}"); | |
| 535 | + failCount++; | |
| 536 | + } | |
| 537 | + } | |
| 538 | + } | |
| 539 | + finally | |
| 540 | + { | |
| 541 | + // 清理临时文件 | |
| 542 | + if (File.Exists(tempFilePath)) | |
| 543 | + { | |
| 544 | + File.Delete(tempFilePath); | |
| 545 | + } | |
| 546 | + } | |
| 547 | + | |
| 548 | + return new | |
| 549 | + { | |
| 550 | + success = true, | |
| 551 | + message = $"导入完成:成功{successCount}条,失败{failCount}条", | |
| 552 | + successCount = successCount, | |
| 553 | + failCount = failCount, | |
| 554 | + failMessages = failMessages | |
| 555 | + }; | |
| 556 | + } | |
| 557 | + catch (Exception ex) | |
| 558 | + { | |
| 559 | + throw NCCException.Oh($"导入失败:{ex.Message}"); | |
| 560 | + } | |
| 561 | + } | |
| 562 | + } | |
| 563 | +} | |
| 564 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqStoreManagerSalaryService.cs
0 → 100644
| 1 | +using Microsoft.AspNetCore.Authorization; | |
| 2 | +using Microsoft.AspNetCore.Mvc; | |
| 3 | +using NCC.Common.Filter; | |
| 4 | +using NCC.Common.Helper; | |
| 5 | +using NCC.Dependency; | |
| 6 | +using NCC.DynamicApiController; | |
| 7 | +using NCC.Extend.Entitys.Dto.LqStoreManagerSalary; | |
| 8 | +using NCC.Extend.Entitys.Enum; | |
| 9 | +using NCC.Extend.Entitys.lq_attendance_summary; | |
| 10 | +using NCC.Extend.Entitys.lq_cooperation_cost; | |
| 11 | +using NCC.Extend.Entitys.lq_hytk_hytk; | |
| 12 | +using NCC.Extend.Entitys.lq_inventory_usage; | |
| 13 | +using NCC.Extend.Entitys.lq_kd_kdjlb; | |
| 14 | +using NCC.Extend.Entitys.lq_laundry_flow; | |
| 15 | +using NCC.Extend.Entitys.lq_md_target; | |
| 16 | +using NCC.Extend.Entitys.lq_md_xdbhsj; | |
| 17 | +using NCC.Extend.Entitys.lq_mdxx; | |
| 18 | +using NCC.Extend.Entitys.lq_store_expense; | |
| 19 | +using NCC.Extend.Entitys.lq_store_manager_salary_statistics; | |
| 20 | +using NCC.Extend.Entitys.lq_xh_hyhk; | |
| 21 | +using NCC.Extend.Entitys.lq_xh_jksyj; | |
| 22 | +using NCC.System.Entitys.Permission; | |
| 23 | +using SqlSugar; | |
| 24 | +using System; | |
| 25 | +using System.Collections.Generic; | |
| 26 | +using System.Linq; | |
| 27 | +using System.Threading.Tasks; | |
| 28 | +using Yitter.IdGenerator; | |
| 29 | + | |
| 30 | +namespace NCC.Extend | |
| 31 | +{ | |
| 32 | + /// <summary> | |
| 33 | + /// 店长薪酬服务 | |
| 34 | + /// </summary> | |
| 35 | + [ApiDescriptionSettings(Tag = "店长薪酬服务", Name = "LqStoreManagerSalary", Order = 303)] | |
| 36 | + [Route("api/Extend/[controller]")] | |
| 37 | + public class LqStoreManagerSalaryService : IDynamicApiController, ITransient | |
| 38 | + { | |
| 39 | + private readonly ISqlSugarClient _db; | |
| 40 | + | |
| 41 | + /// <summary> | |
| 42 | + /// 初始化一个<see cref="LqStoreManagerSalaryService"/>类型的新实例 | |
| 43 | + /// </summary> | |
| 44 | + public LqStoreManagerSalaryService(ISqlSugarClient db) | |
| 45 | + { | |
| 46 | + _db = db; | |
| 47 | + } | |
| 48 | + | |
| 49 | + /// <summary> | |
| 50 | + /// 获取店长工资列表 | |
| 51 | + /// </summary> | |
| 52 | + /// <param name="input">查询参数</param> | |
| 53 | + /// <returns>店长工资分页列表</returns> | |
| 54 | + [HttpGet("store-manager")] | |
| 55 | + public async Task<dynamic> GetStoreManagerSalaryList([FromQuery] StoreManagerSalaryInput input) | |
| 56 | + { | |
| 57 | + var monthStr = $"{input.Year}{input.Month:D2}"; | |
| 58 | + | |
| 59 | + // 1. 检查当月是否已生成工资数据 | |
| 60 | + var exists = await _db.Queryable<LqStoreManagerSalaryStatisticsEntity>() | |
| 61 | + .AnyAsync(x => x.StatisticsMonth == monthStr); | |
| 62 | + | |
| 63 | + // 2. 如果没有数据,则进行计算 | |
| 64 | + if (!exists) | |
| 65 | + { | |
| 66 | + await CalculateStoreManagerSalary(input.Year, input.Month); | |
| 67 | + } | |
| 68 | + | |
| 69 | + // 3. 查询数据 | |
| 70 | + var query = _db.Queryable<LqStoreManagerSalaryStatisticsEntity>() | |
| 71 | + .Where(x => x.StatisticsMonth == monthStr); | |
| 72 | + | |
| 73 | + if (!string.IsNullOrEmpty(input.StoreId)) | |
| 74 | + { | |
| 75 | + query = query.Where(x => x.StoreId == input.StoreId); | |
| 76 | + } | |
| 77 | + | |
| 78 | + if (!string.IsNullOrEmpty(input.Keyword)) | |
| 79 | + { | |
| 80 | + query = query.Where(x => x.EmployeeName.Contains(input.Keyword) || x.EmployeeId.Contains(input.Keyword)); | |
| 81 | + } | |
| 82 | + | |
| 83 | + var list = await query.Select(x => new StoreManagerSalaryOutput | |
| 84 | + { | |
| 85 | + Id = x.Id, | |
| 86 | + StoreName = x.StoreName, | |
| 87 | + EmployeeName = x.EmployeeName, | |
| 88 | + Position = x.Position, | |
| 89 | + StoreTotalPerformance = x.StoreTotalPerformance, | |
| 90 | + StoreBillingPerformance = x.StoreBillingPerformance, | |
| 91 | + StoreRefundPerformance = x.StoreRefundPerformance, | |
| 92 | + StoreLifeline = x.StoreLifeline, | |
| 93 | + PerformanceCompletionRate = x.PerformanceCompletionRate, | |
| 94 | + PerformanceReached = x.PerformanceReached, | |
| 95 | + HeadCountReached = x.HeadCountReached, | |
| 96 | + ConsumeReached = x.ConsumeReached, | |
| 97 | + AssessmentDeduction = x.AssessmentDeduction, | |
| 98 | + UnreachedIndicatorCount = x.UnreachedIndicatorCount, | |
| 99 | + HeadCount = x.HeadCount, | |
| 100 | + TargetHeadCount = x.TargetHeadCount, | |
| 101 | + StoreConsume = x.StoreConsume, | |
| 102 | + TargetConsume = x.TargetConsume, | |
| 103 | + SalesPerformance = x.SalesPerformance, | |
| 104 | + ProductMaterial = x.ProductMaterial, | |
| 105 | + CooperationCost = x.CooperationCost, | |
| 106 | + StoreExpense = x.StoreExpense, | |
| 107 | + LaundryCost = x.LaundryCost, | |
| 108 | + GrossProfit = x.GrossProfit, | |
| 109 | + CommissionRate = x.CommissionRate, | |
| 110 | + CommissionAmount = x.CommissionAmount, | |
| 111 | + BaseSalary = x.BaseSalary, | |
| 112 | + FlagshipStoreDeduction = x.FlagshipStoreDeduction, | |
| 113 | + ActualBaseSalary = x.ActualBaseSalary, | |
| 114 | + WorkingDays = x.WorkingDays, | |
| 115 | + LeaveDays = x.LeaveDays, | |
| 116 | + GrossSalary = x.GrossSalary, | |
| 117 | + ActualSalary = x.ActualSalary, | |
| 118 | + TotalDeduction = x.TotalDeduction, | |
| 119 | + TotalSubsidy = x.TotalSubsidy, | |
| 120 | + Bonus = x.Bonus, | |
| 121 | + MonthlyPaymentStatus = x.MonthlyPaymentStatus, | |
| 122 | + PaidAmount = x.PaidAmount, | |
| 123 | + PendingAmount = x.PendingAmount, | |
| 124 | + IsLocked = x.IsLocked, | |
| 125 | + StoreType = x.StoreType, | |
| 126 | + StoreCategory = x.StoreCategory, | |
| 127 | + IsNewStore = x.IsNewStore, | |
| 128 | + NewStoreProtectionStage = x.NewStoreProtectionStage, | |
| 129 | + UpdateTime = x.UpdateTime | |
| 130 | + }) | |
| 131 | + .ToPagedListAsync(input.currentPage, input.pageSize); | |
| 132 | + | |
| 133 | + return PageResult<StoreManagerSalaryOutput>.SqlSugarPageResult(list); | |
| 134 | + } | |
| 135 | + | |
| 136 | + /// <summary> | |
| 137 | + /// 计算店长工资 | |
| 138 | + /// </summary> | |
| 139 | + /// <param name="year">年份</param> | |
| 140 | + /// <param name="month">月份</param> | |
| 141 | + /// <returns></returns> | |
| 142 | + [HttpPost("calculate/store-manager")] | |
| 143 | + public async Task CalculateStoreManagerSalary(int year, int month) | |
| 144 | + { | |
| 145 | + var startDate = new DateTime(year, month, 1); | |
| 146 | + var endDate = startDate.AddMonths(1).AddDays(-1); | |
| 147 | + var monthStr = $"{year}{month:D2}"; | |
| 148 | + | |
| 149 | + // 1. 获取基础数据 | |
| 150 | + | |
| 151 | + // 1.1 获取店长员工列表(从BASE_USER表,岗位为"店长") | |
| 152 | + var storeManagerUserList = await _db.Queryable<UserEntity>() | |
| 153 | + .Where(x => x.Gw == "店长" && x.DeleteMark == null && x.EnabledMark == 1) | |
| 154 | + .Select(x => new { x.Id, x.RealName, x.Mdid, x.Gw }) | |
| 155 | + .ToListAsync(); | |
| 156 | + | |
| 157 | + if (!storeManagerUserList.Any()) | |
| 158 | + { | |
| 159 | + // 如果没有店长员工,直接返回 | |
| 160 | + return; | |
| 161 | + } | |
| 162 | + | |
| 163 | + // 1.2 门店信息 (lq_mdxx) | |
| 164 | + var storeList = await _db.Queryable<LqMdxxEntity>().ToListAsync(); | |
| 165 | + var storeDict = storeList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x); | |
| 166 | + | |
| 167 | + // 1.3 门店目标信息 (lq_md_target) | |
| 168 | + var storeTargets = await _db.Queryable<LqMdTargetEntity>() | |
| 169 | + .Where(x => x.Month == monthStr) | |
| 170 | + .ToListAsync(); | |
| 171 | + var storeTargetDict = storeTargets.Where(x => !string.IsNullOrEmpty(x.StoreId)) | |
| 172 | + .ToDictionary(x => x.StoreId, x => x); | |
| 173 | + | |
| 174 | + // 1.4 门店新店保护信息 (lq_md_xdbhsj) | |
| 175 | + var newStoreProtectionList = await _db.Queryable<LqMdXdbhsjEntity>() | |
| 176 | + .Where(x => x.Sfqy == 1) | |
| 177 | + .ToListAsync(); | |
| 178 | + var newStoreProtectionDict = newStoreProtectionList | |
| 179 | + .Where(x => x.Bhkssj <= startDate && x.Bhjssj >= startDate) | |
| 180 | + .GroupBy(x => x.Mdid) | |
| 181 | + .ToDictionary(g => g.Key, g => g.First()); | |
| 182 | + | |
| 183 | + // 1.5 门店总业绩计算 (开单实付 - 退卡金额) | |
| 184 | + // 开单实付 | |
| 185 | + var storeBillingList = await _db.Queryable<LqKdKdjlbEntity>() | |
| 186 | + .Where(x => x.Kdrq >= startDate && x.Kdrq <= endDate.AddDays(1) && x.IsEffective == 1) | |
| 187 | + .Select(x => new { x.Djmd, x.Sfyj }) | |
| 188 | + .ToListAsync(); | |
| 189 | + var storeBillingDict = storeBillingList | |
| 190 | + .Where(x => !string.IsNullOrEmpty(x.Djmd)) | |
| 191 | + .GroupBy(x => x.Djmd) | |
| 192 | + .ToDictionary(g => g.Key, g => g.Sum(x => x.Sfyj)); | |
| 193 | + | |
| 194 | + // 退卡金额(使用F_ActualRefundAmount,如果没有则使用tkje) | |
| 195 | + var storeRefundList = await _db.Queryable<LqHytkHytkEntity>() | |
| 196 | + .Where(x => x.Tksj >= startDate && x.Tksj <= endDate.AddDays(1) && x.IsEffective == 1) | |
| 197 | + .Select(x => new { x.Md, x.ActualRefundAmount, x.Tkje }) | |
| 198 | + .ToListAsync(); | |
| 199 | + var storeRefundDict = storeRefundList | |
| 200 | + .Where(x => !string.IsNullOrEmpty(x.Md)) | |
| 201 | + .GroupBy(x => x.Md) | |
| 202 | + .ToDictionary(g => g.Key, g => g.Sum(x => x.ActualRefundAmount ?? x.Tkje ?? 0)); | |
| 203 | + | |
| 204 | + // 1.6 门店消耗金额统计(按门店统计当月总消耗) | |
| 205 | + var storeConsumeSql = $@" | |
| 206 | + SELECT | |
| 207 | + hyhk.md as StoreId, | |
| 208 | + COALESCE(SUM(jksyj.jksyj), 0) as ConsumeAmount | |
| 209 | + FROM lq_xh_jksyj jksyj | |
| 210 | + INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id | |
| 211 | + WHERE jksyj.F_IsEffective = 1 | |
| 212 | + AND hyhk.F_IsEffective = 1 | |
| 213 | + AND hyhk.hksj >= @startDate | |
| 214 | + AND hyhk.hksj <= @endDate | |
| 215 | + GROUP BY hyhk.md"; | |
| 216 | + | |
| 217 | + var storeConsumeData = await _db.Ado.SqlQueryAsync<dynamic>(storeConsumeSql, new { startDate, endDate = endDate.AddDays(1) }); | |
| 218 | + var storeConsumeDict = storeConsumeData | |
| 219 | + .Where(x => x.StoreId != null) | |
| 220 | + .ToDictionary(x => x.StoreId.ToString(), x => Convert.ToDecimal(x.ConsumeAmount ?? 0)); | |
| 221 | + | |
| 222 | + // 1.7 进店消耗人数统计(有消费金额的,按门店按月去重客户数) | |
| 223 | + var headcountSql = $@" | |
| 224 | + SELECT | |
| 225 | + hyhk.md as StoreId, | |
| 226 | + COUNT(DISTINCT hyhk.hy) as HeadCount | |
| 227 | + FROM lq_xh_hyhk hyhk | |
| 228 | + WHERE hyhk.F_IsEffective = 1 | |
| 229 | + AND DATE_FORMAT(hyhk.hksj, '%Y%m') = @monthStr | |
| 230 | + AND EXISTS ( | |
| 231 | + SELECT 1 | |
| 232 | + FROM lq_xh_jksyj jksyj | |
| 233 | + WHERE jksyj.glkdbh = hyhk.F_Id | |
| 234 | + AND jksyj.F_IsEffective = 1 | |
| 235 | + AND jksyj.jksyj > 0 | |
| 236 | + ) | |
| 237 | + GROUP BY hyhk.md"; | |
| 238 | + | |
| 239 | + var headcountData = await _db.Ado.SqlQueryAsync<dynamic>(headcountSql, new { monthStr }); | |
| 240 | + var headcountDict = headcountData | |
| 241 | + .Where(x => x.StoreId != null) | |
| 242 | + .ToDictionary(x => x.StoreId.ToString(), x => Convert.ToInt32(x.HeadCount)); | |
| 243 | + | |
| 244 | + // 1.8 考勤数据 (lq_attendance_summary) | |
| 245 | + var attendanceList = await _db.Queryable<LqAttendanceSummaryEntity>() | |
| 246 | + .Where(x => x.Year == year && x.Month == month && x.IsEffective == 1) | |
| 247 | + .ToListAsync(); | |
| 248 | + var attendanceDict = attendanceList.ToDictionary(x => x.UserId, x => x); | |
| 249 | + | |
| 250 | + // 1.9 产品物料统计(仓库领用金额,注意11月特殊规则) | |
| 251 | + var queryMonth = monthStr; | |
| 252 | + if (month == 11) | |
| 253 | + { | |
| 254 | + // 11月工资算10月数据 | |
| 255 | + queryMonth = $"{year}10"; | |
| 256 | + } | |
| 257 | + var productMaterialSql = $@" | |
| 258 | + SELECT | |
| 259 | + F_StoreId as StoreId, | |
| 260 | + COALESCE(SUM(F_TotalAmount), 0) as MaterialAmount | |
| 261 | + FROM lq_inventory_usage | |
| 262 | + WHERE F_IsEffective = 1 | |
| 263 | + AND DATE_FORMAT(F_UsageTime, '%Y%m') = @queryMonth | |
| 264 | + GROUP BY F_StoreId"; | |
| 265 | + | |
| 266 | + var productMaterialData = await _db.Ado.SqlQueryAsync<dynamic>(productMaterialSql, new { queryMonth }); | |
| 267 | + var productMaterialDict = productMaterialData | |
| 268 | + .Where(x => x.StoreId != null) | |
| 269 | + .ToDictionary(x => x.StoreId.ToString(), x => Convert.ToDecimal(x.MaterialAmount ?? 0)); | |
| 270 | + | |
| 271 | + // 1.10 合作项目成本统计 | |
| 272 | + var cooperationCostList = await _db.Queryable<LqCooperationCostEntity>() | |
| 273 | + .Where(x => x.Year == year && x.Month == monthStr && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 274 | + .Select(x => new { x.StoreId, x.TotalAmount }) | |
| 275 | + .ToListAsync(); | |
| 276 | + var cooperationCostDict = cooperationCostList | |
| 277 | + .Where(x => !string.IsNullOrEmpty(x.StoreId)) | |
| 278 | + .GroupBy(x => x.StoreId) | |
| 279 | + .ToDictionary(g => g.Key, g => g.Sum(x => x.TotalAmount)); | |
| 280 | + | |
| 281 | + // 1.11 店内支出统计 | |
| 282 | + var storeExpenseSql = $@" | |
| 283 | + SELECT | |
| 284 | + F_StoreId as StoreId, | |
| 285 | + COALESCE(SUM(F_Amount), 0) as ExpenseAmount | |
| 286 | + FROM lq_store_expense | |
| 287 | + WHERE F_IsEffective = 1 | |
| 288 | + AND DATE_FORMAT(F_ExpenseDate, '%Y%m') = @monthStr | |
| 289 | + GROUP BY F_StoreId"; | |
| 290 | + | |
| 291 | + var storeExpenseData = await _db.Ado.SqlQueryAsync<dynamic>(storeExpenseSql, new { monthStr }); | |
| 292 | + var storeExpenseDict = storeExpenseData | |
| 293 | + .Where(x => x.StoreId != null) | |
| 294 | + .ToDictionary(x => x.StoreId.ToString(), x => Convert.ToDecimal(x.ExpenseAmount ?? 0)); | |
| 295 | + | |
| 296 | + // 1.12 洗毛巾费用统计(只统计送出的记录,F_FlowType = 0) | |
| 297 | + var laundryCostSql = $@" | |
| 298 | + SELECT | |
| 299 | + F_StoreId as StoreId, | |
| 300 | + COALESCE(SUM(F_TotalPrice), 0) as LaundryAmount | |
| 301 | + FROM lq_laundry_flow | |
| 302 | + WHERE F_IsEffective = 1 | |
| 303 | + AND F_FlowType = 0 | |
| 304 | + AND DATE_FORMAT(F_CreateTime, '%Y%m') = @monthStr | |
| 305 | + GROUP BY F_StoreId"; | |
| 306 | + | |
| 307 | + var laundryCostData = await _db.Ado.SqlQueryAsync<dynamic>(laundryCostSql, new { monthStr }); | |
| 308 | + var laundryCostDict = laundryCostData | |
| 309 | + .Where(x => x.StoreId != null) | |
| 310 | + .ToDictionary(x => x.StoreId.ToString(), x => Convert.ToDecimal(x.LaundryAmount ?? 0)); | |
| 311 | + | |
| 312 | + // 2. 计算每个店长的工资 | |
| 313 | + var storeManagerSalaryList = new List<LqStoreManagerSalaryStatisticsEntity>(); | |
| 314 | + | |
| 315 | + foreach (var storeManagerUser in storeManagerUserList) | |
| 316 | + { | |
| 317 | + var salary = new LqStoreManagerSalaryStatisticsEntity | |
| 318 | + { | |
| 319 | + Id = YitIdHelper.NextId().ToString(), | |
| 320 | + EmployeeId = storeManagerUser.Id, | |
| 321 | + EmployeeName = storeManagerUser.RealName, | |
| 322 | + StatisticsMonth = monthStr, | |
| 323 | + Position = "店长", | |
| 324 | + CreateTime = DateTime.Now, | |
| 325 | + UpdateTime = DateTime.Now, | |
| 326 | + IsLocked = 0, | |
| 327 | + MonthlyPaymentStatus = "未发放" | |
| 328 | + }; | |
| 329 | + | |
| 330 | + // 2.1 填充门店信息 | |
| 331 | + string storeId = storeManagerUser.Mdid; | |
| 332 | + if (string.IsNullOrEmpty(storeId)) | |
| 333 | + { | |
| 334 | + // 如果用户没有门店ID,跳过 | |
| 335 | + continue; | |
| 336 | + } | |
| 337 | + | |
| 338 | + salary.StoreId = storeId; | |
| 339 | + | |
| 340 | + if (storeDict.ContainsKey(storeId)) | |
| 341 | + { | |
| 342 | + var store = storeDict[storeId]; | |
| 343 | + salary.StoreName = store.Dm; | |
| 344 | + salary.StoreType = store.StoreType; | |
| 345 | + salary.StoreCategory = store.StoreCategory; | |
| 346 | + } | |
| 347 | + else | |
| 348 | + { | |
| 349 | + // 如果门店不存在,跳过 | |
| 350 | + continue; | |
| 351 | + } | |
| 352 | + | |
| 353 | + // 2.2 填充新店保护信息 | |
| 354 | + bool isNewStore = false; | |
| 355 | + if (newStoreProtectionDict.ContainsKey(storeId)) | |
| 356 | + { | |
| 357 | + var protection = newStoreProtectionDict[storeId]; | |
| 358 | + salary.IsNewStore = "是"; | |
| 359 | + salary.NewStoreProtectionStage = protection.Stage; | |
| 360 | + isNewStore = true; | |
| 361 | + } | |
| 362 | + else | |
| 363 | + { | |
| 364 | + salary.IsNewStore = "否"; | |
| 365 | + salary.NewStoreProtectionStage = 0; | |
| 366 | + } | |
| 367 | + | |
| 368 | + // 2.2.1 数据校验:只有新店才需要校验门店分类和门店类型 | |
| 369 | + if (isNewStore) | |
| 370 | + { | |
| 371 | + if (!salary.StoreCategory.HasValue) | |
| 372 | + { | |
| 373 | + // 新店如果没有设置门店分类,默认设置为B类门店 | |
| 374 | + salary.StoreCategory = (int)StoreCategoryEnum.B类门店; | |
| 375 | + } | |
| 376 | + if (!salary.StoreType.HasValue) | |
| 377 | + { | |
| 378 | + // 新店如果没有设置门店类型,默认设置为200平门店 | |
| 379 | + salary.StoreType = (int)StoreTypeEnum.门店200平; | |
| 380 | + } | |
| 381 | + } | |
| 382 | + | |
| 383 | + // 2.3 获取门店目标信息(门店生命线、目标人头、目标消耗) | |
| 384 | + if (!storeTargetDict.ContainsKey(storeId)) | |
| 385 | + { | |
| 386 | + throw new Exception($"门店【{salary.StoreName ?? storeId}】在门店目标表中未配置{monthStr}月份的目标数据,无法计算店长工资"); | |
| 387 | + } | |
| 388 | + | |
| 389 | + var storeTarget = storeTargetDict[storeId]; | |
| 390 | + salary.StoreLifeline = storeTarget.StoreLifeline; | |
| 391 | + salary.TargetHeadCount = storeTarget.StoreHeadcountTarget; | |
| 392 | + salary.TargetConsume = storeTarget.StoreConsumeTarget; | |
| 393 | + | |
| 394 | + // 数据校验:门店生命线、目标人头必须设置 | |
| 395 | + if (salary.StoreLifeline <= 0) | |
| 396 | + { | |
| 397 | + throw new Exception($"门店【{salary.StoreName ?? storeId}】的门店生命线未设置,无法计算店长工资"); | |
| 398 | + } | |
| 399 | + if (salary.TargetHeadCount <= 0) | |
| 400 | + { | |
| 401 | + throw new Exception($"门店【{salary.StoreName ?? storeId}】的目标人头数未设置,无法计算店长工资"); | |
| 402 | + } | |
| 403 | + if (!isNewStore && salary.TargetConsume <= 0) | |
| 404 | + { | |
| 405 | + throw new Exception($"门店【{salary.StoreName ?? storeId}】的目标消耗未设置,无法计算店长工资(老店需要考核消耗)"); | |
| 406 | + } | |
| 407 | + | |
| 408 | + // 2.4 计算门店业绩 | |
| 409 | + decimal billing = storeBillingDict.ContainsKey(storeId) ? storeBillingDict[storeId] : 0; | |
| 410 | + decimal refund = storeRefundDict.ContainsKey(storeId) ? storeRefundDict[storeId] : 0; | |
| 411 | + salary.StoreBillingPerformance = billing; | |
| 412 | + salary.StoreRefundPerformance = refund; | |
| 413 | + salary.StoreTotalPerformance = billing - refund; | |
| 414 | + | |
| 415 | + // 计算业绩完成率 | |
| 416 | + if (salary.StoreLifeline > 0) | |
| 417 | + { | |
| 418 | + salary.PerformanceCompletionRate = salary.StoreTotalPerformance / salary.StoreLifeline; | |
| 419 | + } | |
| 420 | + else | |
| 421 | + { | |
| 422 | + salary.PerformanceCompletionRate = 0; | |
| 423 | + } | |
| 424 | + | |
| 425 | + // 2.5 统计门店消耗金额 | |
| 426 | + salary.StoreConsume = storeConsumeDict.ContainsKey(storeId) ? storeConsumeDict[storeId] : 0; | |
| 427 | + | |
| 428 | + // 2.6 统计进店消耗人数 | |
| 429 | + salary.HeadCount = headcountDict.ContainsKey(storeId) ? headcountDict[storeId] : 0; | |
| 430 | + | |
| 431 | + // 2.7 计算考核指标(业绩、人头、消耗是否达标) | |
| 432 | + bool performanceReached = salary.StoreTotalPerformance >= salary.StoreLifeline; | |
| 433 | + bool headCountReached = salary.HeadCount >= salary.TargetHeadCount; | |
| 434 | + bool consumeReached = salary.StoreConsume >= salary.TargetConsume; | |
| 435 | + | |
| 436 | + salary.PerformanceReached = performanceReached ? "是" : "否"; | |
| 437 | + salary.HeadCountReached = headCountReached ? "是" : "否"; | |
| 438 | + salary.ConsumeReached = consumeReached ? "是" : "否"; | |
| 439 | + | |
| 440 | + // 计算未达标指标数量 | |
| 441 | + int unreachedCount = 0; | |
| 442 | + if (!performanceReached) unreachedCount++; | |
| 443 | + if (!headCountReached) unreachedCount++; | |
| 444 | + // 新店不考核消耗 | |
| 445 | + if (!isNewStore && !consumeReached) unreachedCount++; | |
| 446 | + | |
| 447 | + salary.UnreachedIndicatorCount = unreachedCount; | |
| 448 | + | |
| 449 | + // 2.8 计算底薪 | |
| 450 | + salary.BaseSalary = 4000m; // 固定底薪4000元 | |
| 451 | + | |
| 452 | + // 考核扣款:老店每个指标500元,新店每个指标800元 | |
| 453 | + if (isNewStore) | |
| 454 | + { | |
| 455 | + salary.AssessmentDeduction = unreachedCount * 800m; | |
| 456 | + } | |
| 457 | + else | |
| 458 | + { | |
| 459 | + salary.AssessmentDeduction = unreachedCount * 500m; | |
| 460 | + } | |
| 461 | + | |
| 462 | + // 旗舰店负奖励(800元) | |
| 463 | + bool isFlagshipStore = salary.StoreType.HasValue && salary.StoreType.Value == (int)StoreTypeEnum.旗舰店; | |
| 464 | + salary.FlagshipStoreDeduction = isFlagshipStore ? 800m : 0m; | |
| 465 | + | |
| 466 | + // 实际底薪 = 底薪 - 考核扣款 - 旗舰店负奖励 | |
| 467 | + salary.ActualBaseSalary = salary.BaseSalary - salary.AssessmentDeduction - salary.FlagshipStoreDeduction; | |
| 468 | + | |
| 469 | + // 2.9 计算毛利 | |
| 470 | + // 销售业绩 = 开单业绩 - 退款业绩 | |
| 471 | + salary.SalesPerformance = salary.StoreTotalPerformance; | |
| 472 | + | |
| 473 | + // 产品物料(注意11月特殊规则已在查询时处理) | |
| 474 | + salary.ProductMaterial = productMaterialDict.ContainsKey(storeId) ? productMaterialDict[storeId] : 0; | |
| 475 | + | |
| 476 | + // 合作项目成本 | |
| 477 | + salary.CooperationCost = cooperationCostDict.ContainsKey(storeId) ? cooperationCostDict[storeId] : 0; | |
| 478 | + | |
| 479 | + // 店内支出 | |
| 480 | + salary.StoreExpense = storeExpenseDict.ContainsKey(storeId) ? storeExpenseDict[storeId] : 0; | |
| 481 | + | |
| 482 | + // 洗毛巾费用 | |
| 483 | + salary.LaundryCost = laundryCostDict.ContainsKey(storeId) ? laundryCostDict[storeId] : 0; | |
| 484 | + | |
| 485 | + // 毛利 = 销售业绩 - 产品物料 - 合作项目成本 - 店内支出 - 洗毛巾 | |
| 486 | + salary.GrossProfit = salary.SalesPerformance - salary.ProductMaterial - salary.CooperationCost - salary.StoreExpense - salary.LaundryCost; | |
| 487 | + | |
| 488 | + // 2.10 计算提成(基于毛利) | |
| 489 | + CalculateCommission(salary, isNewStore, performanceReached); | |
| 490 | + | |
| 491 | + // 2.11 考勤数据 | |
| 492 | + if (attendanceDict.ContainsKey(storeManagerUser.Id)) | |
| 493 | + { | |
| 494 | + var attendance = attendanceDict[storeManagerUser.Id]; | |
| 495 | + salary.WorkingDays = (int)attendance.WorkDays; | |
| 496 | + salary.LeaveDays = (int)attendance.LeaveDays; | |
| 497 | + } | |
| 498 | + else | |
| 499 | + { | |
| 500 | + salary.WorkingDays = 0; | |
| 501 | + salary.LeaveDays = 0; | |
| 502 | + } | |
| 503 | + | |
| 504 | + // 2.12 计算应发工资 | |
| 505 | + salary.GrossSalary = salary.ActualBaseSalary + salary.CommissionAmount; | |
| 506 | + | |
| 507 | + // 2.13 初始化扣款、补贴、奖金字段(默认值为0) | |
| 508 | + salary.MissingCard = 0; | |
| 509 | + salary.LateArrival = 0; | |
| 510 | + salary.LeaveDeduction = 0; | |
| 511 | + salary.SocialInsuranceDeduction = 0; | |
| 512 | + salary.RewardDeduction = 0; | |
| 513 | + salary.AccommodationDeduction = 0; | |
| 514 | + salary.StudyPeriodDeduction = 0; | |
| 515 | + salary.WorkClothesDeduction = 0; | |
| 516 | + salary.TotalDeduction = 0; | |
| 517 | + | |
| 518 | + salary.MonthlyTrainingSubsidy = 0; | |
| 519 | + salary.MonthlyTransportSubsidy = 0; | |
| 520 | + salary.LastMonthTrainingSubsidy = 0; | |
| 521 | + salary.LastMonthTransportSubsidy = 0; | |
| 522 | + salary.TotalSubsidy = 0; | |
| 523 | + | |
| 524 | + salary.Bonus = 0; | |
| 525 | + salary.ReturnPhoneDeposit = 0; | |
| 526 | + salary.ReturnAccommodationDeposit = 0; | |
| 527 | + | |
| 528 | + // 2.14 计算实发工资 | |
| 529 | + salary.ActualSalary = salary.GrossSalary - salary.TotalDeduction + salary.TotalSubsidy + salary.Bonus; | |
| 530 | + | |
| 531 | + // 2.15 初始化支付相关字段 | |
| 532 | + salary.PaidAmount = 0; | |
| 533 | + salary.PendingAmount = salary.ActualSalary; | |
| 534 | + salary.LastMonthSupplement = 0; | |
| 535 | + salary.MonthlyTotalPayment = 0; | |
| 536 | + | |
| 537 | + storeManagerSalaryList.Add(salary); | |
| 538 | + } | |
| 539 | + | |
| 540 | + // 3. 保存数据 | |
| 541 | + if (storeManagerSalaryList.Any()) | |
| 542 | + { | |
| 543 | + // 先删除当月旧数据 (防止重复) | |
| 544 | + await _db.Deleteable<LqStoreManagerSalaryStatisticsEntity>() | |
| 545 | + .Where(x => x.StatisticsMonth == monthStr) | |
| 546 | + .ExecuteCommandAsync(); | |
| 547 | + | |
| 548 | + await _db.Insertable(storeManagerSalaryList).ExecuteCommandAsync(); | |
| 549 | + } | |
| 550 | + } | |
| 551 | + | |
| 552 | + /// <summary> | |
| 553 | + /// 计算提成(基于毛利) | |
| 554 | + /// </summary> | |
| 555 | + /// <param name="salary">工资实体</param> | |
| 556 | + /// <param name="isNewStore">是否新店</param> | |
| 557 | + /// <param name="performanceReached">业绩是否达标</param> | |
| 558 | + private void CalculateCommission(LqStoreManagerSalaryStatisticsEntity salary, bool isNewStore, bool performanceReached) | |
| 559 | + { | |
| 560 | + decimal grossProfit = salary.GrossProfit; | |
| 561 | + | |
| 562 | + // 确定提成比例 | |
| 563 | + decimal commissionRate; | |
| 564 | + | |
| 565 | + if (isNewStore) | |
| 566 | + { | |
| 567 | + // 新店:根据门店分类和门店类型确定提成比例 | |
| 568 | + int? storeCategory = salary.StoreCategory; | |
| 569 | + int? storeType = salary.StoreType; | |
| 570 | + | |
| 571 | + if (!storeCategory.HasValue) | |
| 572 | + { | |
| 573 | + throw new Exception($"新店【{salary.StoreName}】的门店分类未设置,无法计算提成"); | |
| 574 | + } | |
| 575 | + if (!storeType.HasValue) | |
| 576 | + { | |
| 577 | + throw new Exception($"新店【{salary.StoreName}】的门店类型未设置,无法计算提成"); | |
| 578 | + } | |
| 579 | + | |
| 580 | + // 新店根据门店分类和门店类型确定提成比例 | |
| 581 | + // 这里需要根据实际业务规则确定新店的提成比例 | |
| 582 | + // 暂时使用统一的提成比例,后续可以根据业务规则调整 | |
| 583 | + if (performanceReached) | |
| 584 | + { | |
| 585 | + commissionRate = 0.035m; // 3.5%(业绩达标) | |
| 586 | + } | |
| 587 | + else | |
| 588 | + { | |
| 589 | + commissionRate = 0.03m; // 3%(业绩未达标) | |
| 590 | + } | |
| 591 | + } | |
| 592 | + else | |
| 593 | + { | |
| 594 | + // 老店:不区分门店分类和门店类型,使用统一比例 | |
| 595 | + if (performanceReached) | |
| 596 | + { | |
| 597 | + commissionRate = 0.04m; // 4%(业绩达标) | |
| 598 | + } | |
| 599 | + else | |
| 600 | + { | |
| 601 | + commissionRate = 0.035m; // 3.5%(业绩未达标) | |
| 602 | + } | |
| 603 | + } | |
| 604 | + | |
| 605 | + salary.CommissionRate = commissionRate; | |
| 606 | + salary.CommissionAmount = grossProfit * commissionRate; | |
| 607 | + } | |
| 608 | + } | |
| 609 | +} | |
| 610 | + | ... | ... |
sql/创建合作成本表和店内支出表.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 创建合作成本表和店内支出表 | |
| 3 | +-- ============================================ | |
| 4 | +-- 说明:用于店长工资计算中的毛利计算 | |
| 5 | +-- 执行时间:2025年 | |
| 6 | +-- ============================================ | |
| 7 | + | |
| 8 | +-- ============================================ | |
| 9 | +-- 1. 创建合作成本表 (lq_cooperation_cost) | |
| 10 | +-- ============================================ | |
| 11 | +CREATE TABLE IF NOT EXISTS `lq_cooperation_cost` ( | |
| 12 | + `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID', | |
| 13 | + `F_StoreId` VARCHAR(50) NOT NULL COMMENT '门店ID(关联lq_mdxx.F_Id)', | |
| 14 | + `F_StoreName` VARCHAR(200) NULL COMMENT '门店名称', | |
| 15 | + `F_Year` INT NOT NULL COMMENT '年份', | |
| 16 | + `F_Month` VARCHAR(6) NOT NULL COMMENT '月份(YYYYMM格式)', | |
| 17 | + `F_TotalAmount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '合计金额', | |
| 18 | + `F_Remarks` VARCHAR(1000) NULL COMMENT '备注说明', | |
| 19 | + `F_IsEffective` INT DEFAULT 1 COMMENT '是否有效(1:有效 0:无效)', | |
| 20 | + `F_CreateUser` VARCHAR(50) NULL COMMENT '创建人ID', | |
| 21 | + `F_CreateTime` DATETIME NULL COMMENT '创建时间', | |
| 22 | + `F_UpdateUser` VARCHAR(50) NULL COMMENT '更新人ID', | |
| 23 | + `F_UpdateTime` DATETIME NULL COMMENT '更新时间', | |
| 24 | + PRIMARY KEY (`F_Id`), | |
| 25 | + KEY `idx_store_month` (`F_StoreId`, `F_Year`, `F_Month`), | |
| 26 | + KEY `idx_month` (`F_Year`, `F_Month`), | |
| 27 | + KEY `idx_store_id` (`F_StoreId`) | |
| 28 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='合作成本表'; | |
| 29 | + | |
| 30 | +-- ============================================ | |
| 31 | +-- 2. 创建店内支出表 (lq_store_expense) | |
| 32 | +-- ============================================ | |
| 33 | +CREATE TABLE IF NOT EXISTS `lq_store_expense` ( | |
| 34 | + `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID', | |
| 35 | + `F_StoreId` VARCHAR(50) NOT NULL COMMENT '门店ID(关联lq_mdxx.F_Id)', | |
| 36 | + `F_StoreName` VARCHAR(200) NULL COMMENT '门店名称', | |
| 37 | + `F_ExpenseCategoryId` VARCHAR(50) NULL COMMENT '支出分类ID(关联lq_reimbursement_category.F_Id)', | |
| 38 | + `F_ExpenseCategoryName` VARCHAR(200) NULL COMMENT '支出分类名称', | |
| 39 | + `F_ExpenseDate` DATETIME NOT NULL COMMENT '支出日期', | |
| 40 | + `F_UnitPrice` DECIMAL(18,2) DEFAULT 0.00 COMMENT '单价', | |
| 41 | + `F_Quantity` INT DEFAULT 0 COMMENT '数量', | |
| 42 | + `F_Amount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '金额', | |
| 43 | + `F_Memo` VARCHAR(1000) NULL COMMENT '备注说明', | |
| 44 | + `F_Attachment` TEXT NULL COMMENT '附件(JSON格式)', | |
| 45 | + `F_RelatedReimbursementId` VARCHAR(50) NULL COMMENT '关联报销申请ID(可选)', | |
| 46 | + `F_RelatedPurchaseRecordId` VARCHAR(50) NULL COMMENT '关联购买记录ID(可选)', | |
| 47 | + `F_IsEffective` INT DEFAULT 1 COMMENT '是否有效(1:有效 0:无效)', | |
| 48 | + `F_CreateUser` VARCHAR(50) NULL COMMENT '创建人ID', | |
| 49 | + `F_CreateTime` DATETIME NULL COMMENT '创建时间', | |
| 50 | + `F_UpdateUser` VARCHAR(50) NULL COMMENT '更新人ID', | |
| 51 | + `F_UpdateTime` DATETIME NULL COMMENT '更新时间', | |
| 52 | + PRIMARY KEY (`F_Id`), | |
| 53 | + KEY `idx_store_date` (`F_StoreId`, `F_ExpenseDate`), | |
| 54 | + KEY `idx_expense_date` (`F_ExpenseDate`), | |
| 55 | + KEY `idx_category` (`F_ExpenseCategoryId`), | |
| 56 | + KEY `idx_store_id` (`F_StoreId`), | |
| 57 | + KEY `idx_related_reimbursement` (`F_RelatedReimbursementId`), | |
| 58 | + KEY `idx_related_purchase` (`F_RelatedPurchaseRecordId`) | |
| 59 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='店内支出表'; | |
| 60 | + | ... | ... |
sql/创建库存使用申请审批流程表.sql
sql/创建店长工资统计表.sql
0 → 100644
| 1 | +-- 创建店长工资统计表 | |
| 2 | +-- 表名:lq_store_manager_salary_statistics | |
| 3 | + | |
| 4 | +CREATE TABLE IF NOT EXISTS `lq_store_manager_salary_statistics` ( | |
| 5 | + `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID', | |
| 6 | + | |
| 7 | + -- 基础信息 | |
| 8 | + `F_StoreId` VARCHAR(50) NOT NULL COMMENT '门店ID(关联lq_mdxx.F_Id)', | |
| 9 | + `F_StoreName` VARCHAR(200) NULL COMMENT '门店名称', | |
| 10 | + `F_Position` VARCHAR(50) NULL COMMENT '核算岗位(店长)', | |
| 11 | + `F_EmployeeId` VARCHAR(50) NOT NULL COMMENT '员工ID(关联BASE_USER.F_Id)', | |
| 12 | + `F_EmployeeName` VARCHAR(100) NULL COMMENT '员工姓名', | |
| 13 | + `F_StatisticsMonth` VARCHAR(6) NOT NULL COMMENT '统计月份(YYYYMM格式)', | |
| 14 | + | |
| 15 | + -- 门店信息 | |
| 16 | + `F_StoreType` INT NULL COMMENT '门店类型(判断旗舰店)', | |
| 17 | + `F_StoreCategory` INT NULL COMMENT '门店类别(A/B/C类)', | |
| 18 | + `F_IsNewStore` VARCHAR(10) NULL COMMENT '是否新店(是/否)', | |
| 19 | + `F_NewStoreProtectionStage` INT DEFAULT 0 COMMENT '新店保护阶段', | |
| 20 | + | |
| 21 | + -- 业绩相关 | |
| 22 | + `F_StoreTotalPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '门店总业绩(开单业绩-退卡业绩)', | |
| 23 | + `F_StoreBillingPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '门店开单业绩', | |
| 24 | + `F_StoreRefundPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '门店退卡业绩', | |
| 25 | + `F_StoreLifeline` DECIMAL(18,2) DEFAULT 0.00 COMMENT '门店生命线', | |
| 26 | + `F_PerformanceCompletionRate` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '业绩完成率', | |
| 27 | + `F_PerformanceReached` VARCHAR(10) NULL COMMENT '业绩是否达标(是/否)', | |
| 28 | + | |
| 29 | + -- 考核相关 | |
| 30 | + `F_HeadCount` INT DEFAULT 0 COMMENT '进店消耗人数', | |
| 31 | + `F_TargetHeadCount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '目标人头数', | |
| 32 | + `F_HeadCountReached` VARCHAR(10) NULL COMMENT '人头是否达标(是/否)', | |
| 33 | + `F_StoreConsume` DECIMAL(18,2) DEFAULT 0.00 COMMENT '门店消耗金额', | |
| 34 | + `F_TargetConsume` DECIMAL(18,2) DEFAULT 0.00 COMMENT '目标消耗金额', | |
| 35 | + `F_ConsumeReached` VARCHAR(10) NULL COMMENT '消耗是否达标(是/否)', | |
| 36 | + `F_UnreachedIndicatorCount` INT DEFAULT 0 COMMENT '未达标指标数量', | |
| 37 | + `F_AssessmentDeduction` DECIMAL(18,2) DEFAULT 0.00 COMMENT '考核扣款金额', | |
| 38 | + | |
| 39 | + -- 毛利相关 | |
| 40 | + `F_SalesPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '销售业绩(开单业绩-退款业绩)', | |
| 41 | + `F_ProductMaterial` DECIMAL(18,2) DEFAULT 0.00 COMMENT '产品物料(仓库领用金额)', | |
| 42 | + `F_CooperationCost` DECIMAL(18,2) DEFAULT 0.00 COMMENT '合作项目成本', | |
| 43 | + `F_StoreExpense` DECIMAL(18,2) DEFAULT 0.00 COMMENT '店内支出', | |
| 44 | + `F_LaundryCost` DECIMAL(18,2) DEFAULT 0.00 COMMENT '洗毛巾费用', | |
| 45 | + `F_GrossProfit` DECIMAL(18,2) DEFAULT 0.00 COMMENT '毛利(销售业绩-产品物料-合作项目成本-店内支出-洗毛巾)', | |
| 46 | + | |
| 47 | + -- 提成相关 | |
| 48 | + `F_CommissionRate` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '提成比例', | |
| 49 | + `F_CommissionAmount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '提成金额(基于毛利)', | |
| 50 | + | |
| 51 | + -- 底薪相关 | |
| 52 | + `F_BaseSalary` DECIMAL(18,2) DEFAULT 4000.00 COMMENT '底薪金额(固定4000元)', | |
| 53 | + `F_FlagshipStoreDeduction` DECIMAL(18,2) DEFAULT 0.00 COMMENT '旗舰店负奖励(800元)', | |
| 54 | + `F_ActualBaseSalary` DECIMAL(18,2) DEFAULT 0.00 COMMENT '实际底薪(底薪-考核扣款-旗舰店负奖励)', | |
| 55 | + | |
| 56 | + -- 考勤相关 | |
| 57 | + `F_WorkingDays` INT DEFAULT 0 COMMENT '在店天数', | |
| 58 | + `F_LeaveDays` INT DEFAULT 0 COMMENT '请假天数', | |
| 59 | + | |
| 60 | + -- 工资相关 | |
| 61 | + `F_GrossSalary` DECIMAL(18,2) DEFAULT 0.00 COMMENT '应发工资(实际底薪+提成)', | |
| 62 | + `F_ActualSalary` DECIMAL(18,2) DEFAULT 0.00 COMMENT '实发工资(应发工资-扣款合计+补贴合计+奖金)', | |
| 63 | + | |
| 64 | + -- 扣款相关 | |
| 65 | + `F_MissingCard` DECIMAL(18,2) DEFAULT 0.00 COMMENT '缺卡扣款', | |
| 66 | + `F_LateArrival` DECIMAL(18,2) DEFAULT 0.00 COMMENT '迟到扣款', | |
| 67 | + `F_LeaveDeduction` DECIMAL(18,2) DEFAULT 0.00 COMMENT '请假扣款', | |
| 68 | + `F_SocialInsuranceDeduction` DECIMAL(18,2) DEFAULT 0.00 COMMENT '扣社保', | |
| 69 | + `F_RewardDeduction` DECIMAL(18,2) DEFAULT 0.00 COMMENT '扣除奖励', | |
| 70 | + `F_AccommodationDeduction` DECIMAL(18,2) DEFAULT 0.00 COMMENT '扣住宿费', | |
| 71 | + `F_StudyPeriodDeduction` DECIMAL(18,2) DEFAULT 0.00 COMMENT '扣学习期费用', | |
| 72 | + `F_WorkClothesDeduction` DECIMAL(18,2) DEFAULT 0.00 COMMENT '扣工作服费用', | |
| 73 | + `F_TotalDeduction` DECIMAL(18,2) DEFAULT 0.00 COMMENT '扣款合计', | |
| 74 | + | |
| 75 | + -- 补贴相关 | |
| 76 | + `F_MonthlyTrainingSubsidy` DECIMAL(18,2) DEFAULT 0.00 COMMENT '当月培训补贴', | |
| 77 | + `F_MonthlyTransportSubsidy` DECIMAL(18,2) DEFAULT 0.00 COMMENT '当月交通补贴', | |
| 78 | + `F_LastMonthTrainingSubsidy` DECIMAL(18,2) DEFAULT 0.00 COMMENT '上月培训补贴', | |
| 79 | + `F_LastMonthTransportSubsidy` DECIMAL(18,2) DEFAULT 0.00 COMMENT '上月交通补贴', | |
| 80 | + `F_TotalSubsidy` DECIMAL(18,2) DEFAULT 0.00 COMMENT '补贴合计', | |
| 81 | + | |
| 82 | + -- 奖金相关 | |
| 83 | + `F_Bonus` DECIMAL(18,2) DEFAULT 0.00 COMMENT '发奖金', | |
| 84 | + `F_ReturnPhoneDeposit` DECIMAL(18,2) DEFAULT 0.00 COMMENT '退手机押金', | |
| 85 | + `F_ReturnAccommodationDeposit` DECIMAL(18,2) DEFAULT 0.00 COMMENT '退住宿押金', | |
| 86 | + | |
| 87 | + -- 支付相关 | |
| 88 | + `F_MonthlyPaymentStatus` VARCHAR(50) NULL COMMENT '当月是否发放', | |
| 89 | + `F_PaidAmount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '支付金额', | |
| 90 | + `F_PendingAmount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '待支付金额', | |
| 91 | + `F_LastMonthSupplement` DECIMAL(18,2) DEFAULT 0.00 COMMENT '补发上月', | |
| 92 | + `F_MonthlyTotalPayment` DECIMAL(18,2) DEFAULT 0.00 COMMENT '当月支付总额', | |
| 93 | + | |
| 94 | + -- 其他 | |
| 95 | + `F_IsLocked` INT DEFAULT 0 COMMENT '是否锁定(0未锁定,1已锁定)', | |
| 96 | + `F_CreateTime` DATETIME NULL COMMENT '创建时间', | |
| 97 | + `F_UpdateTime` DATETIME NULL COMMENT '更新时间', | |
| 98 | + `F_CreateUser` VARCHAR(50) NULL COMMENT '创建人', | |
| 99 | + `F_UpdateUser` VARCHAR(50) NULL COMMENT '更新人', | |
| 100 | + | |
| 101 | + PRIMARY KEY (`F_Id`), | |
| 102 | + KEY `idx_store_month` (`F_StoreId`, `F_StatisticsMonth`), | |
| 103 | + KEY `idx_employee_month` (`F_EmployeeId`, `F_StatisticsMonth`), | |
| 104 | + KEY `idx_statistics_month` (`F_StatisticsMonth`), | |
| 105 | + KEY `idx_store_id` (`F_StoreId`), | |
| 106 | + KEY `idx_employee_id` (`F_EmployeeId`) | |
| 107 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='店长工资统计表'; | |
| 108 | + | ... | ... |
合作成本和店内支出-前端调用说明.md
0 → 100644
| 1 | +# 合作成本和店内支出 - 前端调用说明 | |
| 2 | + | |
| 3 | +## 📋 目录 | |
| 4 | +- [业务流程概述](#业务流程概述) | |
| 5 | +- [合作成本接口列表](#合作成本接口列表) | |
| 6 | +- [店内支出接口列表](#店内支出接口列表) | |
| 7 | +- [完整流程示例](#完整流程示例) | |
| 8 | +- [接口详细说明](#接口详细说明) | |
| 9 | +- [数据验证说明](#数据验证说明) | |
| 10 | +- [注意事项](#注意事项) | |
| 11 | + | |
| 12 | +## 🔄 业务流程概述 | |
| 13 | + | |
| 14 | +### 合作成本管理 | |
| 15 | +合作成本用于记录门店每月的合作项目成本,用于店长工资计算中的毛利计算。 | |
| 16 | + | |
| 17 | +**核心功能:** | |
| 18 | +1. **创建合作成本** - 录入门店某年某月的合作成本金额 | |
| 19 | +2. **查询合作成本** - 支持列表查询和详情查询,可按门店、年份、月份筛选 | |
| 20 | +3. **更新合作成本** - 修改已创建的合作成本记录 | |
| 21 | +4. **删除合作成本** - 删除合作成本记录(逻辑删除) | |
| 22 | +5. **导入合作成本** - 通过Excel批量导入合作成本数据 | |
| 23 | +6. **导出合作成本** - 导出合作成本数据为Excel文件 | |
| 24 | + | |
| 25 | +### 店内支出管理 | |
| 26 | +店内支出用于记录门店每月的各项支出,用于店长工资计算中的毛利计算。 | |
| 27 | + | |
| 28 | +**核心功能:** | |
| 29 | +1. **创建店内支出** - 录入门店的支出明细(支持分类、单价、数量、金额等) | |
| 30 | +2. **查询店内支出** - 支持列表查询和详情查询,可按门店、分类、日期范围筛选 | |
| 31 | +3. **更新店内支出** - 修改已创建的店内支出记录 | |
| 32 | +4. **删除店内支出** - 删除店内支出记录(逻辑删除) | |
| 33 | +5. **导入店内支出** - 通过Excel批量导入店内支出数据 | |
| 34 | +6. **导出店内支出** - 导出店内支出数据为Excel文件 | |
| 35 | + | |
| 36 | +## 📡 合作成本接口列表 | |
| 37 | + | |
| 38 | +| 接口 | 方法 | 路径 | 说明 | | |
| 39 | +|------|------|------|------| | |
| 40 | +| 获取合作成本详情 | GET | `/api/Extend/LqCooperationCost/{id}` | 根据ID获取合作成本详情 | | |
| 41 | +| 获取合作成本列表 | GET | `/api/Extend/LqCooperationCost` | 分页查询合作成本列表,支持多条件筛选 | | |
| 42 | +| 获取全部数据(不分页) | GET | `/api/Extend/LqCooperationCost/Actions/GetNoPagingList` | 获取全部数据,用于导出 | | |
| 43 | +| 创建合作成本 | POST | `/api/Extend/LqCooperationCost` | 创建新的合作成本记录 | | |
| 44 | +| 更新合作成本 | PUT | `/api/Extend/LqCooperationCost/{id}` | 更新合作成本记录 | | |
| 45 | +| 删除合作成本 | DELETE | `/api/Extend/LqCooperationCost/{id}` | 删除合作成本记录(逻辑删除) | | |
| 46 | +| 导出合作成本 | GET | `/api/Extend/LqCooperationCost/Actions/Export` | 导出合作成本数据为Excel | | |
| 47 | +| 导入合作成本 | POST | `/api/Extend/LqCooperationCost/Actions/Import` | 通过Excel导入合作成本数据 | | |
| 48 | + | |
| 49 | +## 📡 店内支出接口列表 | |
| 50 | + | |
| 51 | +| 接口 | 方法 | 路径 | 说明 | | |
| 52 | +|------|------|------|------| | |
| 53 | +| 获取店内支出详情 | GET | `/api/Extend/LqStoreExpense/{id}` | 根据ID获取店内支出详情 | | |
| 54 | +| 获取店内支出列表 | GET | `/api/Extend/LqStoreExpense` | 分页查询店内支出列表,支持多条件筛选 | | |
| 55 | +| 获取全部数据(不分页) | GET | `/api/Extend/LqStoreExpense/Actions/GetNoPagingList` | 获取全部数据,用于导出 | | |
| 56 | +| 创建店内支出 | POST | `/api/Extend/LqStoreExpense` | 创建新的店内支出记录 | | |
| 57 | +| 更新店内支出 | PUT | `/api/Extend/LqStoreExpense/{id}` | 更新店内支出记录 | | |
| 58 | +| 删除店内支出 | DELETE | `/api/Extend/LqStoreExpense/{id}` | 删除店内支出记录(逻辑删除) | | |
| 59 | +| 导出店内支出 | GET | `/api/Extend/LqStoreExpense/Actions/Export` | 导出店内支出数据为Excel | | |
| 60 | +| 导入店内支出 | POST | `/api/Extend/LqStoreExpense/Actions/Import` | 通过Excel导入店内支出数据 | | |
| 61 | + | |
| 62 | +## 🚀 完整流程示例 | |
| 63 | + | |
| 64 | +### 合作成本管理流程 | |
| 65 | + | |
| 66 | +#### 步骤1:创建合作成本 | |
| 67 | + | |
| 68 | +**接口:** `POST /api/Extend/LqCooperationCost` | |
| 69 | + | |
| 70 | +**请求示例:** | |
| 71 | +```javascript | |
| 72 | +const response = await request({ | |
| 73 | + url: '/api/Extend/LqCooperationCost', | |
| 74 | + method: 'POST', | |
| 75 | + data: { | |
| 76 | + storeId: '1649328471923847172', // 门店ID(必填) | |
| 77 | + storeName: '绿纤华润店', // 门店名称(可选,不传则自动查询) | |
| 78 | + year: 2025, // 年份(必填) | |
| 79 | + month: '202511', // 月份(必填,格式:YYYYMM,如202511表示2025年11月) | |
| 80 | + totalAmount: 3000.00, // 合计金额(必填) | |
| 81 | + remarks: '测试数据-合作成本' // 备注说明(可选) | |
| 82 | + } | |
| 83 | +}); | |
| 84 | + | |
| 85 | +// 响应示例 | |
| 86 | +// { | |
| 87 | +// "code": 200, | |
| 88 | +// "msg": "操作成功", | |
| 89 | +// "data": null | |
| 90 | +// } | |
| 91 | +``` | |
| 92 | + | |
| 93 | +**说明:** | |
| 94 | +- 如果未提供门店名称,系统会根据门店ID自动查询并填充 | |
| 95 | +- 月份格式必须为YYYYMM(如:202511表示2025年11月) | |
| 96 | +- 同一门店、同一年份、同一月份只能有一条有效记录(如果已存在会报错) | |
| 97 | + | |
| 98 | +#### 步骤2:查询合作成本列表 | |
| 99 | + | |
| 100 | +**接口:** `GET /api/Extend/LqCooperationCost` | |
| 101 | + | |
| 102 | +**请求示例:** | |
| 103 | +```javascript | |
| 104 | +const response = await request({ | |
| 105 | + url: '/api/Extend/LqCooperationCost', | |
| 106 | + method: 'GET', | |
| 107 | + params: { | |
| 108 | + currentPage: 1, // 当前页码(必填) | |
| 109 | + pageSize: 10, // 每页数量(必填) | |
| 110 | + storeId: '1649328471923847172', // 门店ID(可选) | |
| 111 | + storeName: '绿纤华润店', // 门店名称(可选,模糊查询) | |
| 112 | + year: 2025, // 年份(可选) | |
| 113 | + month: '202511', // 月份(可选,格式:YYYYMM) | |
| 114 | + sidx: 'CreateTime', // 排序字段(可选,默认:CreateTime) | |
| 115 | + sort: 'desc' // 排序方式(可选,asc/desc,默认:desc) | |
| 116 | + } | |
| 117 | +}); | |
| 118 | + | |
| 119 | +// 响应示例 | |
| 120 | +// { | |
| 121 | +// "code": 200, | |
| 122 | +// "msg": "操作成功", | |
| 123 | +// "data": { | |
| 124 | +// "list": [ | |
| 125 | +// { | |
| 126 | +// "id": "768041985045955845", | |
| 127 | +// "storeId": "1649328471923847172", | |
| 128 | +// "storeName": "绿纤华润店", | |
| 129 | +// "year": 2025, | |
| 130 | +// "month": "202511", | |
| 131 | +// "totalAmount": 3000.00, | |
| 132 | +// "remarks": "测试数据-合作成本", | |
| 133 | +// "createUser": "admin", | |
| 134 | +// "createTime": "2025-01-10T10:00:00", | |
| 135 | +// "updateUser": "admin", | |
| 136 | +// "updateTime": "2025-01-10T10:00:00" | |
| 137 | +// } | |
| 138 | +// ], | |
| 139 | +// "pagination": { | |
| 140 | +// "total": 1, | |
| 141 | +// "pageSize": 10, | |
| 142 | +// "currentPage": 1 | |
| 143 | +// } | |
| 144 | +// } | |
| 145 | +// } | |
| 146 | +``` | |
| 147 | + | |
| 148 | +#### 步骤3:更新合作成本 | |
| 149 | + | |
| 150 | +**接口:** `PUT /api/Extend/LqCooperationCost/{id}` | |
| 151 | + | |
| 152 | +**请求示例:** | |
| 153 | +```javascript | |
| 154 | +const response = await request({ | |
| 155 | + url: `/api/Extend/LqCooperationCost/${id}`, | |
| 156 | + method: 'PUT', | |
| 157 | + data: { | |
| 158 | + storeId: '1649328471923847172', | |
| 159 | + storeName: '绿纤华润店', | |
| 160 | + year: 2025, | |
| 161 | + month: '202511', | |
| 162 | + totalAmount: 3500.00, // 修改金额 | |
| 163 | + remarks: '更新后的备注' | |
| 164 | + } | |
| 165 | +}); | |
| 166 | +``` | |
| 167 | + | |
| 168 | +#### 步骤4:删除合作成本 | |
| 169 | + | |
| 170 | +**接口:** `DELETE /api/Extend/LqCooperationCost/{id}` | |
| 171 | + | |
| 172 | +**请求示例:** | |
| 173 | +```javascript | |
| 174 | +const response = await request({ | |
| 175 | + url: `/api/Extend/LqCooperationCost/${id}`, | |
| 176 | + method: 'DELETE' | |
| 177 | +}); | |
| 178 | +``` | |
| 179 | + | |
| 180 | +#### 步骤5:导出合作成本 | |
| 181 | + | |
| 182 | +**接口:** `GET /api/Extend/LqCooperationCost/Actions/Export` | |
| 183 | + | |
| 184 | +**请求示例:** | |
| 185 | +```javascript | |
| 186 | +const response = await request({ | |
| 187 | + url: '/api/Extend/LqCooperationCost/Actions/Export', | |
| 188 | + method: 'GET', | |
| 189 | + params: { | |
| 190 | + storeId: '1649328471923847172', // 可选 | |
| 191 | + year: 2025, // 可选 | |
| 192 | + month: '202511' // 可选 | |
| 193 | + }, | |
| 194 | + responseType: 'blob' // 重要:设置响应类型为blob | |
| 195 | +}); | |
| 196 | + | |
| 197 | +// 下载文件 | |
| 198 | +const blob = new Blob([response.data]); | |
| 199 | +const url = window.URL.createObjectURL(blob); | |
| 200 | +const link = document.createElement('a'); | |
| 201 | +link.href = url; | |
| 202 | +link.setAttribute('download', `合作成本表_${new Date().getTime()}.xlsx`); | |
| 203 | +document.body.appendChild(link); | |
| 204 | +link.click(); | |
| 205 | +document.body.removeChild(link); | |
| 206 | +``` | |
| 207 | + | |
| 208 | +#### 步骤6:导入合作成本 | |
| 209 | + | |
| 210 | +**接口:** `POST /api/Extend/LqCooperationCost/Actions/Import` | |
| 211 | + | |
| 212 | +**请求示例:** | |
| 213 | +```javascript | |
| 214 | +const formData = new FormData(); | |
| 215 | +formData.append('file', file); // file是File对象 | |
| 216 | + | |
| 217 | +const response = await request({ | |
| 218 | + url: '/api/Extend/LqCooperationCost/Actions/Import', | |
| 219 | + method: 'POST', | |
| 220 | + data: formData, | |
| 221 | + headers: { | |
| 222 | + 'Content-Type': 'multipart/form-data' | |
| 223 | + } | |
| 224 | +}); | |
| 225 | + | |
| 226 | +// 响应示例 | |
| 227 | +// { | |
| 228 | +// "code": 200, | |
| 229 | +// "msg": "导入成功", | |
| 230 | +// "data": { | |
| 231 | +// "successCount": 10, | |
| 232 | +// "failCount": 2, | |
| 233 | +// "failMessages": [ | |
| 234 | +// "第3行:门店ID不存在", | |
| 235 | +// "第5行:该门店2025年11月的记录已存在" | |
| 236 | +// ] | |
| 237 | +// } | |
| 238 | +// } | |
| 239 | +``` | |
| 240 | + | |
| 241 | +**Excel格式要求:** | |
| 242 | +- 第一行为表头:门店ID、门店名称、年份、月份、合计金额、备注 | |
| 243 | +- 门店ID和门店名称至少填写一个 | |
| 244 | +- 月份格式:YYYYMM(如:202511) | |
| 245 | +- 合计金额必须为数字 | |
| 246 | + | |
| 247 | +### 店内支出管理流程 | |
| 248 | + | |
| 249 | +#### 步骤1:创建店内支出 | |
| 250 | + | |
| 251 | +**接口:** `POST /api/Extend/LqStoreExpense` | |
| 252 | + | |
| 253 | +**请求示例:** | |
| 254 | +```javascript | |
| 255 | +const response = await request({ | |
| 256 | + url: '/api/Extend/LqStoreExpense', | |
| 257 | + method: 'POST', | |
| 258 | + data: { | |
| 259 | + storeId: '1649328471923847172', // 门店ID(必填) | |
| 260 | + storeName: '绿纤华润店', // 门店名称(可选,不传则自动查询) | |
| 261 | + expenseCategoryId: '11156146041171400', // 支出分类ID(可选) | |
| 262 | + expenseCategoryName: '日常支出', // 支出分类名称(可选) | |
| 263 | + expenseDate: '2025-11-15T00:00:00', // 支出日期(必填) | |
| 264 | + unitPrice: 2000.00, // 单价(可选) | |
| 265 | + quantity: 1, // 数量(可选) | |
| 266 | + amount: 2000.00, // 金额(必填) | |
| 267 | + memo: '测试数据-店内支出', // 备注说明(可选) | |
| 268 | + attachment: [], // 附件(可选,FileControlsModel数组) | |
| 269 | + relatedReimbursementId: '', // 关联报销申请ID(可选) | |
| 270 | + relatedPurchaseRecordId: '' // 关联购买记录ID(可选) | |
| 271 | + } | |
| 272 | +}); | |
| 273 | + | |
| 274 | +// 响应示例 | |
| 275 | +// { | |
| 276 | +// "code": 200, | |
| 277 | +// "msg": "操作成功", | |
| 278 | +// "data": null | |
| 279 | +// } | |
| 280 | +``` | |
| 281 | + | |
| 282 | +**说明:** | |
| 283 | +- 如果未提供门店名称,系统会根据门店ID自动查询并填充 | |
| 284 | +- 金额字段为必填,单价和数量为可选(如果提供了单价和数量,系统会验证:金额 = 单价 × 数量) | |
| 285 | +- 支持附件上传(FileControlsModel格式) | |
| 286 | + | |
| 287 | +#### 步骤2:查询店内支出列表 | |
| 288 | + | |
| 289 | +**接口:** `GET /api/Extend/LqStoreExpense` | |
| 290 | + | |
| 291 | +**请求示例:** | |
| 292 | +```javascript | |
| 293 | +const response = await request({ | |
| 294 | + url: '/api/Extend/LqStoreExpense', | |
| 295 | + method: 'GET', | |
| 296 | + params: { | |
| 297 | + currentPage: 1, // 当前页码(必填) | |
| 298 | + pageSize: 10, // 每页数量(必填) | |
| 299 | + storeId: '1649328471923847172', // 门店ID(可选) | |
| 300 | + storeName: '绿纤华润店', // 门店名称(可选,模糊查询) | |
| 301 | + expenseCategoryId: '11156146041171400', // 支出分类ID(可选) | |
| 302 | + expenseDateStart: '2025-11-01', // 支出日期开始(可选) | |
| 303 | + expenseDateEnd: '2025-11-30', // 支出日期结束(可选) | |
| 304 | + sidx: 'ExpenseDate', // 排序字段(可选,默认:ExpenseDate) | |
| 305 | + sort: 'desc' // 排序方式(可选,asc/desc,默认:desc) | |
| 306 | + } | |
| 307 | +}); | |
| 308 | + | |
| 309 | +// 响应示例 | |
| 310 | +// { | |
| 311 | +// "code": 200, | |
| 312 | +// "msg": "操作成功", | |
| 313 | +// "data": { | |
| 314 | +// "list": [ | |
| 315 | +// { | |
| 316 | +// "id": "768041985045955845", | |
| 317 | +// "storeId": "1649328471923847172", | |
| 318 | +// "storeName": "绿纤华润店", | |
| 319 | +// "expenseCategoryId": "11156146041171400", | |
| 320 | +// "expenseCategoryName": "日常支出", | |
| 321 | +// "expenseDate": "2025-11-15T00:00:00", | |
| 322 | +// "unitPrice": 2000.00, | |
| 323 | +// "quantity": 1, | |
| 324 | +// "amount": 2000.00, | |
| 325 | +// "memo": "测试数据-店内支出", | |
| 326 | +// "attachment": [], | |
| 327 | +// "relatedReimbursementId": "", | |
| 328 | +// "relatedPurchaseRecordId": "", | |
| 329 | +// "createUser": "admin", | |
| 330 | +// "createTime": "2025-01-10T10:00:00", | |
| 331 | +// "updateUser": "admin", | |
| 332 | +// "updateTime": "2025-01-10T10:00:00" | |
| 333 | +// } | |
| 334 | +// ], | |
| 335 | +// "pagination": { | |
| 336 | +// "total": 1, | |
| 337 | +// "pageSize": 10, | |
| 338 | +// "currentPage": 1 | |
| 339 | +// } | |
| 340 | +// } | |
| 341 | +// } | |
| 342 | +``` | |
| 343 | + | |
| 344 | +#### 步骤3:更新店内支出 | |
| 345 | + | |
| 346 | +**接口:** `PUT /api/Extend/LqStoreExpense/{id}` | |
| 347 | + | |
| 348 | +**请求示例:** | |
| 349 | +```javascript | |
| 350 | +const response = await request({ | |
| 351 | + url: `/api/Extend/LqStoreExpense/${id}`, | |
| 352 | + method: 'PUT', | |
| 353 | + data: { | |
| 354 | + storeId: '1649328471923847172', | |
| 355 | + storeName: '绿纤华润店', | |
| 356 | + expenseCategoryId: '11156146041171400', | |
| 357 | + expenseCategoryName: '日常支出', | |
| 358 | + expenseDate: '2025-11-15T00:00:00', | |
| 359 | + unitPrice: 2500.00, // 修改单价 | |
| 360 | + quantity: 1, | |
| 361 | + amount: 2500.00, // 修改金额 | |
| 362 | + memo: '更新后的备注', | |
| 363 | + attachment: [] | |
| 364 | + } | |
| 365 | +}); | |
| 366 | +``` | |
| 367 | + | |
| 368 | +#### 步骤4:删除店内支出 | |
| 369 | + | |
| 370 | +**接口:** `DELETE /api/Extend/LqStoreExpense/{id}` | |
| 371 | + | |
| 372 | +**请求示例:** | |
| 373 | +```javascript | |
| 374 | +const response = await request({ | |
| 375 | + url: `/api/Extend/LqStoreExpense/${id}`, | |
| 376 | + method: 'DELETE' | |
| 377 | +}); | |
| 378 | +``` | |
| 379 | + | |
| 380 | +#### 步骤5:导出店内支出 | |
| 381 | + | |
| 382 | +**接口:** `GET /api/Extend/LqStoreExpense/Actions/Export` | |
| 383 | + | |
| 384 | +**请求示例:** | |
| 385 | +```javascript | |
| 386 | +const response = await request({ | |
| 387 | + url: '/api/Extend/LqStoreExpense/Actions/Export', | |
| 388 | + method: 'GET', | |
| 389 | + params: { | |
| 390 | + storeId: '1649328471923847172', // 可选 | |
| 391 | + expenseCategoryId: '11156146041171400', // 可选 | |
| 392 | + expenseDateStart: '2025-11-01', // 可选 | |
| 393 | + expenseDateEnd: '2025-11-30' // 可选 | |
| 394 | + }, | |
| 395 | + responseType: 'blob' // 重要:设置响应类型为blob | |
| 396 | +}); | |
| 397 | + | |
| 398 | +// 下载文件(同合作成本导出) | |
| 399 | +``` | |
| 400 | + | |
| 401 | +#### 步骤6:导入店内支出 | |
| 402 | + | |
| 403 | +**接口:** `POST /api/Extend/LqStoreExpense/Actions/Import` | |
| 404 | + | |
| 405 | +**请求示例:** | |
| 406 | +```javascript | |
| 407 | +const formData = new FormData(); | |
| 408 | +formData.append('file', file); // file是File对象 | |
| 409 | + | |
| 410 | +const response = await request({ | |
| 411 | + url: '/api/Extend/LqStoreExpense/Actions/Import', | |
| 412 | + method: 'POST', | |
| 413 | + data: formData, | |
| 414 | + headers: { | |
| 415 | + 'Content-Type': 'multipart/form-data' | |
| 416 | + } | |
| 417 | +}); | |
| 418 | + | |
| 419 | +// 响应示例(同合作成本导入) | |
| 420 | +``` | |
| 421 | + | |
| 422 | +**Excel格式要求:** | |
| 423 | +- 第一行为表头:门店ID、门店名称、支出分类ID、支出分类名称、支出日期、单价、数量、金额、备注 | |
| 424 | +- 门店ID和门店名称至少填写一个 | |
| 425 | +- 支出日期格式:YYYY-MM-DD 或 YYYY-MM-DD HH:mm:ss | |
| 426 | +- 金额必须为数字 | |
| 427 | + | |
| 428 | +## 📝 接口详细说明 | |
| 429 | + | |
| 430 | +### 合作成本接口 | |
| 431 | + | |
| 432 | +#### 1. 获取合作成本详情 | |
| 433 | + | |
| 434 | +**接口:** `GET /api/Extend/LqCooperationCost/{id}` | |
| 435 | + | |
| 436 | +**路径参数:** | |
| 437 | +- `id`:合作成本记录ID(必填) | |
| 438 | + | |
| 439 | +**响应示例:** | |
| 440 | +```json | |
| 441 | +{ | |
| 442 | + "code": 200, | |
| 443 | + "msg": "操作成功", | |
| 444 | + "data": { | |
| 445 | + "id": "768041985045955845", | |
| 446 | + "storeId": "1649328471923847172", | |
| 447 | + "storeName": "绿纤华润店", | |
| 448 | + "year": 2025, | |
| 449 | + "month": "202511", | |
| 450 | + "totalAmount": 3000.00, | |
| 451 | + "remarks": "测试数据-合作成本", | |
| 452 | + "createUser": "admin", | |
| 453 | + "createTime": "2025-01-10T10:00:00", | |
| 454 | + "updateUser": "admin", | |
| 455 | + "updateTime": "2025-01-10T10:00:00" | |
| 456 | + } | |
| 457 | +} | |
| 458 | +``` | |
| 459 | + | |
| 460 | +#### 2. 获取合作成本列表 | |
| 461 | + | |
| 462 | +**接口:** `GET /api/Extend/LqCooperationCost` | |
| 463 | + | |
| 464 | +**查询参数:** | |
| 465 | +- `currentPage`:当前页码(必填,默认:1) | |
| 466 | +- `pageSize`:每页数量(必填,默认:10) | |
| 467 | +- `storeId`:门店ID(可选) | |
| 468 | +- `storeName`:门店名称(可选,模糊查询) | |
| 469 | +- `year`:年份(可选) | |
| 470 | +- `month`:月份(可选,格式:YYYYMM) | |
| 471 | +- `sidx`:排序字段(可选,默认:CreateTime) | |
| 472 | +- `sort`:排序方式(可选,asc/desc,默认:desc) | |
| 473 | + | |
| 474 | +**响应格式:** 同列表查询标准格式 | |
| 475 | + | |
| 476 | +#### 3. 创建合作成本 | |
| 477 | + | |
| 478 | +**接口:** `POST /api/Extend/LqCooperationCost` | |
| 479 | + | |
| 480 | +**请求体:** | |
| 481 | +```json | |
| 482 | +{ | |
| 483 | + "storeId": "1649328471923847172", // 门店ID(必填) | |
| 484 | + "storeName": "绿纤华润店", // 门店名称(可选) | |
| 485 | + "year": 2025, // 年份(必填) | |
| 486 | + "month": "202511", // 月份(必填,格式:YYYYMM) | |
| 487 | + "totalAmount": 3000.00, // 合计金额(必填) | |
| 488 | + "remarks": "备注说明" // 备注(可选) | |
| 489 | +} | |
| 490 | +``` | |
| 491 | + | |
| 492 | +**验证规则:** | |
| 493 | +- 门店ID必填 | |
| 494 | +- 年份必填,必须大于0 | |
| 495 | +- 月份必填,格式必须为YYYYMM(6位数字) | |
| 496 | +- 合计金额必填,必须大于等于0 | |
| 497 | +- 同一门店、同一年份、同一月份只能有一条有效记录 | |
| 498 | + | |
| 499 | +#### 4. 更新合作成本 | |
| 500 | + | |
| 501 | +**接口:** `PUT /api/Extend/LqCooperationCost/{id}` | |
| 502 | + | |
| 503 | +**路径参数:** | |
| 504 | +- `id`:合作成本记录ID(必填) | |
| 505 | + | |
| 506 | +**请求体:** 同创建接口 | |
| 507 | + | |
| 508 | +#### 5. 删除合作成本 | |
| 509 | + | |
| 510 | +**接口:** `DELETE /api/Extend/LqCooperationCost/{id}` | |
| 511 | + | |
| 512 | +**路径参数:** | |
| 513 | +- `id`:合作成本记录ID(必填) | |
| 514 | + | |
| 515 | +**说明:** 执行逻辑删除,不会真正删除数据 | |
| 516 | + | |
| 517 | +### 店内支出接口 | |
| 518 | + | |
| 519 | +#### 1. 获取店内支出详情 | |
| 520 | + | |
| 521 | +**接口:** `GET /api/Extend/LqStoreExpense/{id}` | |
| 522 | + | |
| 523 | +**路径参数:** | |
| 524 | +- `id`:店内支出记录ID(必填) | |
| 525 | + | |
| 526 | +**响应示例:** | |
| 527 | +```json | |
| 528 | +{ | |
| 529 | + "code": 200, | |
| 530 | + "msg": "操作成功", | |
| 531 | + "data": { | |
| 532 | + "id": "768041985045955845", | |
| 533 | + "storeId": "1649328471923847172", | |
| 534 | + "storeName": "绿纤华润店", | |
| 535 | + "expenseCategoryId": "11156146041171400", | |
| 536 | + "expenseCategoryName": "日常支出", | |
| 537 | + "expenseDate": "2025-11-15T00:00:00", | |
| 538 | + "unitPrice": 2000.00, | |
| 539 | + "quantity": 1, | |
| 540 | + "amount": 2000.00, | |
| 541 | + "memo": "测试数据-店内支出", | |
| 542 | + "attachment": [], | |
| 543 | + "relatedReimbursementId": "", | |
| 544 | + "relatedPurchaseRecordId": "", | |
| 545 | + "createUser": "admin", | |
| 546 | + "createTime": "2025-01-10T10:00:00", | |
| 547 | + "updateUser": "admin", | |
| 548 | + "updateTime": "2025-01-10T10:00:00" | |
| 549 | + } | |
| 550 | +} | |
| 551 | +``` | |
| 552 | + | |
| 553 | +#### 2. 获取店内支出列表 | |
| 554 | + | |
| 555 | +**接口:** `GET /api/Extend/LqStoreExpense` | |
| 556 | + | |
| 557 | +**查询参数:** | |
| 558 | +- `currentPage`:当前页码(必填,默认:1) | |
| 559 | +- `pageSize`:每页数量(必填,默认:10) | |
| 560 | +- `storeId`:门店ID(可选) | |
| 561 | +- `storeName`:门店名称(可选,模糊查询) | |
| 562 | +- `expenseCategoryId`:支出分类ID(可选) | |
| 563 | +- `expenseDateStart`:支出日期开始(可选,格式:YYYY-MM-DD) | |
| 564 | +- `expenseDateEnd`:支出日期结束(可选,格式:YYYY-MM-DD) | |
| 565 | +- `sidx`:排序字段(可选,默认:ExpenseDate) | |
| 566 | +- `sort`:排序方式(可选,asc/desc,默认:desc) | |
| 567 | + | |
| 568 | +**响应格式:** 同列表查询标准格式 | |
| 569 | + | |
| 570 | +#### 3. 创建店内支出 | |
| 571 | + | |
| 572 | +**接口:** `POST /api/Extend/LqStoreExpense` | |
| 573 | + | |
| 574 | +**请求体:** | |
| 575 | +```json | |
| 576 | +{ | |
| 577 | + "storeId": "1649328471923847172", // 门店ID(必填) | |
| 578 | + "storeName": "绿纤华润店", // 门店名称(可选) | |
| 579 | + "expenseCategoryId": "11156146041171400", // 支出分类ID(可选) | |
| 580 | + "expenseCategoryName": "日常支出", // 支出分类名称(可选) | |
| 581 | + "expenseDate": "2025-11-15T00:00:00", // 支出日期(必填) | |
| 582 | + "unitPrice": 2000.00, // 单价(可选) | |
| 583 | + "quantity": 1, // 数量(可选) | |
| 584 | + "amount": 2000.00, // 金额(必填) | |
| 585 | + "memo": "备注说明", // 备注(可选) | |
| 586 | + "attachment": [], // 附件(可选,FileControlsModel数组) | |
| 587 | + "relatedReimbursementId": "", // 关联报销申请ID(可选) | |
| 588 | + "relatedPurchaseRecordId": "" // 关联购买记录ID(可选) | |
| 589 | +} | |
| 590 | +``` | |
| 591 | + | |
| 592 | +**验证规则:** | |
| 593 | +- 门店ID必填 | |
| 594 | +- 支出日期必填 | |
| 595 | +- 金额必填,必须大于等于0 | |
| 596 | +- 如果提供了单价和数量,系统会验证:金额 = 单价 × 数量 | |
| 597 | + | |
| 598 | +#### 4. 更新店内支出 | |
| 599 | + | |
| 600 | +**接口:** `PUT /api/Extend/LqStoreExpense/{id}` | |
| 601 | + | |
| 602 | +**路径参数:** | |
| 603 | +- `id`:店内支出记录ID(必填) | |
| 604 | + | |
| 605 | +**请求体:** 同创建接口 | |
| 606 | + | |
| 607 | +#### 5. 删除店内支出 | |
| 608 | + | |
| 609 | +**接口:** `DELETE /api/Extend/LqStoreExpense/{id}` | |
| 610 | + | |
| 611 | +**路径参数:** | |
| 612 | +- `id`:店内支出记录ID(必填) | |
| 613 | + | |
| 614 | +**说明:** 执行逻辑删除,不会真正删除数据 | |
| 615 | + | |
| 616 | +## ✅ 数据验证说明 | |
| 617 | + | |
| 618 | +### 合作成本验证规则 | |
| 619 | + | |
| 620 | +1. **门店ID验证** | |
| 621 | + - 必填 | |
| 622 | + - 如果门店ID不存在,会报错 | |
| 623 | + | |
| 624 | +2. **年份验证** | |
| 625 | + - 必填 | |
| 626 | + - 必须为整数,大于0 | |
| 627 | + | |
| 628 | +3. **月份验证** | |
| 629 | + - 必填 | |
| 630 | + - 格式必须为YYYYMM(6位数字,如:202511) | |
| 631 | + - 月份必须在1-12之间 | |
| 632 | + | |
| 633 | +4. **合计金额验证** | |
| 634 | + - 必填 | |
| 635 | + - 必须为数字,大于等于0 | |
| 636 | + | |
| 637 | +5. **唯一性验证** | |
| 638 | + - 同一门店、同一年份、同一月份只能有一条有效记录 | |
| 639 | + - 如果已存在,创建时会报错 | |
| 640 | + | |
| 641 | +### 店内支出验证规则 | |
| 642 | + | |
| 643 | +1. **门店ID验证** | |
| 644 | + - 必填 | |
| 645 | + - 如果门店ID不存在,会报错 | |
| 646 | + | |
| 647 | +2. **支出日期验证** | |
| 648 | + - 必填 | |
| 649 | + - 格式:YYYY-MM-DD 或 YYYY-MM-DD HH:mm:ss | |
| 650 | + | |
| 651 | +3. **金额验证** | |
| 652 | + - 必填 | |
| 653 | + - 必须为数字,大于等于0 | |
| 654 | + | |
| 655 | +4. **单价和数量验证** | |
| 656 | + - 如果同时提供了单价和数量,系统会验证:金额 = 单价 × 数量 | |
| 657 | + - 如果验证失败,会报错 | |
| 658 | + | |
| 659 | +## ⚠️ 注意事项 | |
| 660 | + | |
| 661 | +### 合作成本注意事项 | |
| 662 | + | |
| 663 | +1. **月份格式** | |
| 664 | + - 月份必须使用YYYYMM格式(如:202511表示2025年11月) | |
| 665 | + - 不要使用YYYY-MM格式 | |
| 666 | + | |
| 667 | +2. **唯一性约束** | |
| 668 | + - 同一门店、同一年份、同一月份只能有一条有效记录 | |
| 669 | + - 如果需要修改,请使用更新接口,不要重复创建 | |
| 670 | + | |
| 671 | +3. **门店名称自动填充** | |
| 672 | + - 如果未提供门店名称,系统会根据门店ID自动查询并填充 | |
| 673 | + - 建议前端也自动填充,提升用户体验 | |
| 674 | + | |
| 675 | +4. **导入Excel格式** | |
| 676 | + - 第一行必须是表头 | |
| 677 | + - 门店ID和门店名称至少填写一个 | |
| 678 | + - 月份格式必须为YYYYMM(6位数字) | |
| 679 | + | |
| 680 | +### 店内支出注意事项 | |
| 681 | + | |
| 682 | +1. **金额计算** | |
| 683 | + - 如果同时提供了单价和数量,系统会验证:金额 = 单价 × 数量 | |
| 684 | + - 建议前端在用户输入单价和数量时自动计算金额 | |
| 685 | + | |
| 686 | +2. **附件上传** | |
| 687 | + - 附件使用FileControlsModel格式 | |
| 688 | + - 支持多个附件 | |
| 689 | + | |
| 690 | +3. **关联字段** | |
| 691 | + - `relatedReimbursementId`:关联报销申请ID(可选) | |
| 692 | + - `relatedPurchaseRecordId`:关联购买记录ID(可选) | |
| 693 | + - 这些字段用于数据追溯,建议填写 | |
| 694 | + | |
| 695 | +4. **导入Excel格式** | |
| 696 | + - 第一行必须是表头 | |
| 697 | + - 门店ID和门店名称至少填写一个 | |
| 698 | + - 支出日期格式:YYYY-MM-DD 或 YYYY-MM-DD HH:mm:ss | |
| 699 | + | |
| 700 | +### 通用注意事项 | |
| 701 | + | |
| 702 | +1. **GET请求参数** | |
| 703 | + - 所有GET请求使用`params`传参,不使用`data` | |
| 704 | + - 分页参数`currentPage`和`pageSize`必填 | |
| 705 | + | |
| 706 | +2. **文件下载** | |
| 707 | + - 导出接口返回的是文件流,需要设置`responseType: 'blob'` | |
| 708 | + - 下载文件时需要创建Blob对象并触发下载 | |
| 709 | + | |
| 710 | +3. **文件上传** | |
| 711 | + - 导入接口需要使用`FormData`格式 | |
| 712 | + - 设置`Content-Type: 'multipart/form-data'` | |
| 713 | + | |
| 714 | +4. **错误处理** | |
| 715 | + - 所有接口返回标准格式:`{code, msg, data}` | |
| 716 | + - `code === 200`表示成功,其他表示失败 | |
| 717 | + - 失败时查看`msg`字段获取错误信息 | |
| 718 | + | |
| 719 | +5. **数据删除** | |
| 720 | + - 删除操作执行的是逻辑删除,不会真正删除数据 | |
| 721 | + - 删除后的数据在列表中不会显示,但可以通过数据库查询 | |
| 722 | + | |
| 723 | +6. **店长工资计算** | |
| 724 | + - 合作成本和店内支出数据用于店长工资计算中的毛利计算 | |
| 725 | + - 确保数据的准确性和及时性,避免影响工资计算结果 | |
| 726 | + | ... | ... |
店长工资计算-前置条件检查清单.md
0 → 100644
| 1 | +# 店长工资计算 - 前置条件检查清单 | |
| 2 | + | |
| 3 | +## 📋 检查时间 | |
| 4 | +2025-01-XX | |
| 5 | + | |
| 6 | +--- | |
| 7 | + | |
| 8 | +## ✅ 一、数据表检查 | |
| 9 | + | |
| 10 | +### 1. 核心业务表 | |
| 11 | + | |
| 12 | +| 表名 | 用途 | 状态 | 说明 | | |
| 13 | +|------|------|------|------| | |
| 14 | +| `lq_kd_kdjlb` | 开单记录表(获取开单业绩) | ✅ 已存在 | 字段:`sfyj`(实付业绩)、`Djmd`(门店ID)、`Kdrq`(开单日期) | | |
| 15 | +| `lq_hytk_hytk` | 退卡记录表(获取退款业绩) | ✅ 已存在 | 字段:`F_ActualRefundAmount`(实际退款金额)、`md`(门店ID)、`tksj`(退卡时间) | | |
| 16 | +| `lq_inventory_usage` | 库存使用记录表(获取产品物料) | ✅ 已存在 | 字段:`F_TotalAmount`(合计金额)、`F_StoreId`(门店ID)、`F_UsageTime`(使用时间) | | |
| 17 | +| `lq_cooperation_cost` | 合作成本表 | ✅ 已创建 | 字段:`F_TotalAmount`(合计金额)、`F_StoreId`(门店ID)、`F_Year`(年份)、`F_Month`(月份) | | |
| 18 | +| `lq_store_expense` | 店内支出表 | ✅ 已创建 | 字段:`F_Amount`(金额)、`F_StoreId`(门店ID)、`F_ExpenseDate`(支出日期) | | |
| 19 | +| `lq_laundry_flow` | 清洗流水表(获取洗毛巾费用) | ✅ 已存在 | 字段:`F_TotalPrice`(总费用)、`F_StoreId`(门店ID)、`F_FlowType`(流水类型,0=送出) | | |
| 20 | +| `lq_md_target` | 门店目标表 | ✅ 已存在 | 字段:`F_StoreId`(门店ID)、`F_Month`(月份)、`F_StoreLifeline`(门店生命线)、`F_HeadcountTarget`(目标人头数)、`F_ConsumeTarget`(目标消耗) | | |
| 21 | +| `lq_xh_hyhk` | 耗卡记录表(统计进店消耗人数) | ✅ 已存在 | 用于统计进店消耗人数 | | |
| 22 | +| `lq_md_xdbhsj` | 新店保护时间表 | ⚠️ 需确认 | 用于判断是否为新店 | | |
| 23 | +| `lq_mdxx` | 门店信息表 | ✅ 已存在 | 字段:`F_Id`(门店ID)、`F_StoreType`(门店类型,判断旗舰店)、`F_StoreCategory`(门店分类,A/B/C类) | | |
| 24 | +| `BASE_USER` | 系统用户表 | ✅ 已存在 | 字段:`F_Id`(员工ID)、`F_RealName`(姓名)、`F_GW`(岗位,店长)、`F_MDID`(门店ID) | | |
| 25 | + | |
| 26 | +--- | |
| 27 | + | |
| 28 | +## ✅ 二、实体类检查 | |
| 29 | + | |
| 30 | +### 1. 核心实体类 | |
| 31 | + | |
| 32 | +| 实体类 | 对应表 | 状态 | 说明 | | |
| 33 | +|--------|--------|------|------| | |
| 34 | +| `LqKdKdjlbEntity` | `lq_kd_kdjlb` | ✅ 已存在 | 开单记录实体 | | |
| 35 | +| `LqHytkHytkEntity` | `lq_hytk_hytk` | ✅ 已存在 | 退卡记录实体 | | |
| 36 | +| `LqInventoryUsageEntity` | `lq_inventory_usage` | ✅ 已存在 | 库存使用记录实体 | | |
| 37 | +| `LqCooperationCostEntity` | `lq_cooperation_cost` | ✅ 已创建 | 合作成本实体 | | |
| 38 | +| `LqStoreExpenseEntity` | `lq_store_expense` | ✅ 已创建 | 店内支出实体 | | |
| 39 | +| `LqLaundryFlowEntity` | `lq_laundry_flow` | ✅ 已存在 | 清洗流水实体 | | |
| 40 | +| `LqMdTargetEntity` | `lq_md_target` | ✅ 已存在 | 门店目标实体 | | |
| 41 | +| `LqXhHyhkEntity` | `lq_xh_hyhk` | ✅ 已存在 | 耗卡记录实体 | | |
| 42 | +| `LqMdxxEntity` | `lq_mdxx` | ✅ 已存在 | 门店信息实体 | | |
| 43 | + | |
| 44 | +--- | |
| 45 | + | |
| 46 | +## ✅ 三、服务类检查 | |
| 47 | + | |
| 48 | +### 1. 数据查询服务 | |
| 49 | + | |
| 50 | +| 服务类 | 功能 | 状态 | 说明 | | |
| 51 | +|--------|------|------|------| | |
| 52 | +| `LqCooperationCostService` | 合作成本管理 | ✅ 已实现 | CRUD、导入、导出 | | |
| 53 | +| `LqStoreExpenseService` | 店内支出管理 | ✅ 已实现 | CRUD、导入、导出 | | |
| 54 | +| `LqReimbursementApplicationService` | 报销申请管理 | ✅ 已实现 | 已实现导出本月已审核通过的报销表明细 | | |
| 55 | +| `LqInventoryUsageService` | 库存使用管理 | ✅ 已存在 | 可查询仓库领用金额 | | |
| 56 | +| `LqLaundryFlowService` | 清洗流水管理 | ⚠️ 需确认 | 需确认是否有查询送洗记录的服务 | | |
| 57 | +| `LqKdKdjlbService` | 开单记录管理 | ✅ 已存在 | 可查询开单业绩 | | |
| 58 | +| `LqHytkHytkService` | 退卡记录管理 | ⚠️ 需确认 | 需确认是否有查询退卡记录的服务 | | |
| 59 | +| `LqMdTargetService` | 门店目标管理 | ⚠️ 需确认 | 需确认是否有查询门店目标的服务 | | |
| 60 | +| `LqMdxxService` | 门店信息管理 | ✅ 已存在 | 可查询门店信息、分类、类型 | | |
| 61 | + | |
| 62 | +--- | |
| 63 | + | |
| 64 | +## ⚠️ 四、待实现功能检查 | |
| 65 | + | |
| 66 | +### 1. 毛利计算接口 | |
| 67 | + | |
| 68 | +| 功能 | 状态 | 优先级 | | |
| 69 | +|------|------|--------| | |
| 70 | +| 销售业绩查询(开单业绩 - 退款业绩) | ❌ 未实现 | 高 | | |
| 71 | +| 产品物料查询(仓库领用金额,特殊规则:11月算10月) | ❌ 未实现 | 高 | | |
| 72 | +| 合作项目成本查询 | ✅ 已实现 | - | | |
| 73 | +| 店内支出查询 | ✅ 已实现 | - | | |
| 74 | +| 洗毛巾查询 | ❌ 未实现 | 高 | | |
| 75 | +| 毛利汇总计算接口 | ❌ 未实现 | 高 | | |
| 76 | + | |
| 77 | +### 2. 工资计算接口 | |
| 78 | + | |
| 79 | +| 功能 | 状态 | 优先级 | | |
| 80 | +|------|------|--------| | |
| 81 | +| 底薪计算(老店/新店,旗舰店特殊规则) | ❌ 未实现 | 中 | | |
| 82 | +| 提成计算(基于毛利,老店/新店不同规则) | ❌ 未实现 | 中 | | |
| 83 | +| 考核指标判断(业绩、人头、消耗) | ❌ 未实现 | 中 | | |
| 84 | +| 新店判断逻辑 | ❌ 未实现 | 中 | | |
| 85 | +| 旗舰店判断逻辑 | ⚠️ 需确认 | 中 | | |
| 86 | +| 完整工资计算接口 | ❌ 未实现 | 中 | | |
| 87 | + | |
| 88 | +--- | |
| 89 | + | |
| 90 | +## 📊 五、数据查询能力检查 | |
| 91 | + | |
| 92 | +### 1. 销售业绩查询 | |
| 93 | + | |
| 94 | +**需求**: | |
| 95 | +- 开单业绩:从 `lq_kd_kdjlb` 表查询 `sfyj` 字段,按门店ID和月份统计 | |
| 96 | +- 退款业绩:从 `lq_hytk_hytk` 表查询 `F_ActualRefundAmount` 字段,按门店ID和月份统计 | |
| 97 | +- 销售业绩 = 开单业绩 - 退款业绩 | |
| 98 | + | |
| 99 | +**状态**:❌ 需要实现查询接口 | |
| 100 | + | |
| 101 | +--- | |
| 102 | + | |
| 103 | +### 2. 产品物料查询 | |
| 104 | + | |
| 105 | +**需求**: | |
| 106 | +- 从 `lq_inventory_usage` 表查询 `F_TotalAmount` 字段 | |
| 107 | +- 按门店ID和月份统计 | |
| 108 | +- **特殊规则**:核算11月工资时,算的是10月份的仓库领用 | |
| 109 | + | |
| 110 | +**状态**:❌ 需要实现查询接口(注意11月特殊规则) | |
| 111 | + | |
| 112 | +--- | |
| 113 | + | |
| 114 | +### 3. 合作项目成本查询 | |
| 115 | + | |
| 116 | +**需求**: | |
| 117 | +- 从 `lq_cooperation_cost` 表查询 `F_TotalAmount` 字段 | |
| 118 | +- 按门店ID、年份、月份查询 | |
| 119 | + | |
| 120 | +**状态**:✅ 已实现(`LqCooperationCostService.GetList`) | |
| 121 | + | |
| 122 | +--- | |
| 123 | + | |
| 124 | +### 4. 店内支出查询 | |
| 125 | + | |
| 126 | +**需求**: | |
| 127 | +- 从 `lq_store_expense` 表查询 `F_Amount` 字段 | |
| 128 | +- 按门店ID和月份统计 | |
| 129 | + | |
| 130 | +**状态**:✅ 已实现(`LqStoreExpenseService.GetList`) | |
| 131 | + | |
| 132 | +--- | |
| 133 | + | |
| 134 | +### 5. 洗毛巾查询 | |
| 135 | + | |
| 136 | +**需求**: | |
| 137 | +- 从 `lq_laundry_flow` 表查询 `F_TotalPrice` 字段 | |
| 138 | +- 条件:`F_FlowType = 0`(送出) | |
| 139 | +- 按门店ID和月份统计 | |
| 140 | + | |
| 141 | +**状态**:❌ 需要实现查询接口 | |
| 142 | + | |
| 143 | +--- | |
| 144 | + | |
| 145 | +### 6. 门店目标查询 | |
| 146 | + | |
| 147 | +**需求**: | |
| 148 | +- 从 `lq_md_target` 表查询门店生命线、目标人头数、目标消耗 | |
| 149 | +- 按门店ID和月份查询 | |
| 150 | + | |
| 151 | +**状态**:⚠️ 需确认是否有查询接口 | |
| 152 | + | |
| 153 | +--- | |
| 154 | + | |
| 155 | +### 7. 新店判断 | |
| 156 | + | |
| 157 | +**需求**: | |
| 158 | +- 从 `lq_md_xdbhsj` 表查询新店保护信息 | |
| 159 | +- 判断统计月份的第一天是否在保护期内 | |
| 160 | + | |
| 161 | +**状态**:❌ 需要实现新店判断逻辑 | |
| 162 | + | |
| 163 | +--- | |
| 164 | + | |
| 165 | +### 8. 进店消耗人数统计 | |
| 166 | + | |
| 167 | +**需求**: | |
| 168 | +- 从 `lq_xh_hyhk` 表统计有消费金额的客户数 | |
| 169 | +- 按门店按月去重 | |
| 170 | + | |
| 171 | +**状态**:❌ 需要实现统计接口 | |
| 172 | + | |
| 173 | +--- | |
| 174 | + | |
| 175 | +### 9. 门店消耗统计 | |
| 176 | + | |
| 177 | +**需求**: | |
| 178 | +- 从 `lq_xh_jksyj` 表统计门店当月总消耗金额 | |
| 179 | +- 按门店和月份统计 | |
| 180 | + | |
| 181 | +**状态**:❌ 需要实现统计接口 | |
| 182 | + | |
| 183 | +--- | |
| 184 | + | |
| 185 | +## ✅ 六、已完成功能 | |
| 186 | + | |
| 187 | +### 1. 数据表创建 | |
| 188 | +- ✅ `lq_cooperation_cost` 表已创建 | |
| 189 | +- ✅ `lq_store_expense` 表已创建 | |
| 190 | + | |
| 191 | +### 2. 实体类创建 | |
| 192 | +- ✅ `LqCooperationCostEntity` 已创建 | |
| 193 | +- ✅ `LqStoreExpenseEntity` 已创建 | |
| 194 | + | |
| 195 | +### 3. 服务类实现 | |
| 196 | +- ✅ `LqCooperationCostService` 已实现(CRUD、导入、导出) | |
| 197 | +- ✅ `LqStoreExpenseService` 已实现(CRUD、导入、导出) | |
| 198 | +- ✅ `LqReimbursementApplicationService.ExportApprovedDetails` 已实现(导出报销表明细) | |
| 199 | + | |
| 200 | +--- | |
| 201 | + | |
| 202 | +## ❌ 七、待实现功能 | |
| 203 | + | |
| 204 | +### 1. 毛利计算接口(高优先级) | |
| 205 | + | |
| 206 | +需要实现以下接口: | |
| 207 | + | |
| 208 | +1. **销售业绩查询接口** | |
| 209 | + - 查询开单业绩(`lq_kd_kdjlb`) | |
| 210 | + - 查询退款业绩(`lq_hytk_hytk`) | |
| 211 | + - 计算销售业绩 = 开单业绩 - 退款业绩 | |
| 212 | + | |
| 213 | +2. **产品物料查询接口** | |
| 214 | + - 查询仓库领用金额(`lq_inventory_usage`) | |
| 215 | + - 特殊规则:11月工资算10月数据 | |
| 216 | + | |
| 217 | +3. **洗毛巾查询接口** | |
| 218 | + - 查询送洗记录总费用(`lq_laundry_flow`,`F_FlowType = 0`) | |
| 219 | + | |
| 220 | +4. **毛利汇总计算接口** | |
| 221 | + - 汇总所有组成部分 | |
| 222 | + - 计算:毛利 = 销售业绩 - 产品物料 - 合作项目成本 - 店内支出 - 洗毛巾 | |
| 223 | + | |
| 224 | +### 2. 工资计算接口(中优先级) | |
| 225 | + | |
| 226 | +需要实现以下接口: | |
| 227 | + | |
| 228 | +1. **门店目标查询接口** | |
| 229 | + - 查询门店生命线、目标人头数、目标消耗(`lq_md_target`) | |
| 230 | + | |
| 231 | +2. **新店判断接口** | |
| 232 | + - 判断门店是否为新店(`lq_md_xdbhsj`) | |
| 233 | + | |
| 234 | +3. **进店消耗人数统计接口** | |
| 235 | + - 统计有消费金额的客户数(`lq_xh_hyhk`) | |
| 236 | + | |
| 237 | +4. **门店消耗统计接口** | |
| 238 | + - 统计门店当月总消耗金额(`lq_xh_jksyj`) | |
| 239 | + | |
| 240 | +5. **底薪计算接口** | |
| 241 | + - 老店:3个考核指标(业绩、人头、消耗) | |
| 242 | + - 新店:2个考核指标(业绩、人头) | |
| 243 | + - 旗舰店:额外扣除800元 | |
| 244 | + | |
| 245 | +6. **提成计算接口** | |
| 246 | + - 老店:根据门店分类(A/B/C)和业绩是否达标 | |
| 247 | + - 新店:不区分分类,统一比例 | |
| 248 | + | |
| 249 | +7. **完整工资计算接口** | |
| 250 | + - 整合底薪和提成 | |
| 251 | + - 返回完整工资计算结果 | |
| 252 | + | |
| 253 | +--- | |
| 254 | + | |
| 255 | +## 📝 八、总结 | |
| 256 | + | |
| 257 | +### ✅ 已完成 | |
| 258 | +1. 合作成本表和店内支出表已创建并实现CRUD、导入、导出功能 | |
| 259 | +2. 报销表导出功能已实现 | |
| 260 | +3. 所有核心数据表都已存在 | |
| 261 | +4. 所有核心实体类都已存在 | |
| 262 | + | |
| 263 | +### ❌ 待实现 | |
| 264 | +1. **毛利计算接口**(高优先级) | |
| 265 | + - 销售业绩查询 | |
| 266 | + - 产品物料查询(注意11月特殊规则) | |
| 267 | + - 洗毛巾查询 | |
| 268 | + - 毛利汇总计算 | |
| 269 | + | |
| 270 | +2. **工资计算接口**(中优先级) | |
| 271 | + - 门店目标查询 | |
| 272 | + - 新店判断 | |
| 273 | + - 进店消耗人数统计 | |
| 274 | + - 门店消耗统计 | |
| 275 | + - 底薪计算 | |
| 276 | + - 提成计算 | |
| 277 | + - 完整工资计算 | |
| 278 | + | |
| 279 | +### ⚠️ 需确认 | |
| 280 | +1. 新店保护时间表(`lq_md_xdbhsj`)是否存在 | |
| 281 | +2. 是否有查询门店目标的服务 | |
| 282 | +3. 是否有查询送洗记录的服务 | |
| 283 | +4. 是否有查询退卡记录的服务 | |
| 284 | + | |
| 285 | +--- | |
| 286 | + | |
| 287 | +## 🚀 下一步行动 | |
| 288 | + | |
| 289 | +### 第一步:实现毛利计算接口 | |
| 290 | +1. 实现销售业绩查询接口 | |
| 291 | +2. 实现产品物料查询接口(注意11月特殊规则) | |
| 292 | +3. 实现洗毛巾查询接口 | |
| 293 | +4. 实现毛利汇总计算接口 | |
| 294 | + | |
| 295 | +### 第二步:实现工资计算接口 | |
| 296 | +1. 实现门店目标查询接口 | |
| 297 | +2. 实现新店判断逻辑 | |
| 298 | +3. 实现进店消耗人数统计接口 | |
| 299 | +4. 实现门店消耗统计接口 | |
| 300 | +5. 实现底薪计算接口 | |
| 301 | +6. 实现提成计算接口 | |
| 302 | +7. 实现完整工资计算接口 | |
| 303 | + | |
| 304 | +### 第三步:测试验证 | |
| 305 | +1. 测试毛利计算准确性 | |
| 306 | +2. 测试工资计算准确性 | |
| 307 | +3. 测试各种边界情况(新店、老店、旗舰店、业绩达标/未达标等) | |
| 308 | + | ... | ... |
店长工资计算规则-完整逻辑梳理.md
0 → 100644
| 1 | +# 店长工资计算规则 - 完整逻辑梳理 | |
| 2 | + | |
| 3 | +## 📋 概述 | |
| 4 | + | |
| 5 | +店长工资由以下几个部分组成: | |
| 6 | +1. **底薪**:固定4000元,根据考核指标扣款 | |
| 7 | +2. **提成**:根据门店分类和业绩是否达标,使用不同的提成比例计算(基于毛利) | |
| 8 | + | |
| 9 | +--- | |
| 10 | + | |
| 11 | +## 💰 工资组成规则 | |
| 12 | + | |
| 13 | +### 1. 底薪规则 | |
| 14 | + | |
| 15 | +**固定底薪**:4000元 | |
| 16 | + | |
| 17 | +#### 老店店长底薪考核 | |
| 18 | + | |
| 19 | +**考核指标**(3个): | |
| 20 | +1. **业绩考核**:门店业绩是否达到门店生命线 | |
| 21 | +2. **人头考核**:进店消耗人数是否达到目标人头数 | |
| 22 | +3. **消耗考核**:门店消耗是否达到目标消耗 | |
| 23 | + | |
| 24 | +**扣款规则**: | |
| 25 | +- 每个指标未达到:扣除500元 | |
| 26 | +- 如果3个指标都未达到:扣除1500元(500 × 3) | |
| 27 | + | |
| 28 | +**计算公式**: | |
| 29 | +``` | |
| 30 | +底薪 = 4000 - (未达标指标数 × 500) | |
| 31 | +``` | |
| 32 | + | |
| 33 | +#### 新店店长底薪考核 | |
| 34 | + | |
| 35 | +**重要说明**:新店店长涉及全部阶段,都有负奖励机制 | |
| 36 | + | |
| 37 | +**考核指标**(2个): | |
| 38 | +1. **业绩考核**:门店业绩是否达到门店生命线 | |
| 39 | +2. **人头考核**:进店消耗人数是否达到目标人头数 | |
| 40 | + | |
| 41 | +**扣款规则**: | |
| 42 | +- 每个指标未达到:扣除800元 | |
| 43 | +- 如果2个指标都未达到:扣除1600元(800 × 2) | |
| 44 | + | |
| 45 | +**计算公式**: | |
| 46 | +``` | |
| 47 | +底薪 = 4000 - (未达标指标数 × 800) | |
| 48 | +``` | |
| 49 | + | |
| 50 | +**注意**:新店不考核消耗指标 | |
| 51 | + | |
| 52 | +**旗舰店特殊规则**: | |
| 53 | +- 旗舰店类型门店需要扣除负奖励800元 | |
| 54 | +- 判断方式:`lq_mdxx.F_StoreType = 2`(StoreTypeEnum.旗舰店) | |
| 55 | + | |
| 56 | +--- | |
| 57 | + | |
| 58 | +### 2. 提成规则 | |
| 59 | + | |
| 60 | +**提成计算方式**:根据门店分类和业绩是否达标,使用不同的提成比例 | |
| 61 | + | |
| 62 | +**重要说明**:提成计算基于**毛利**,不是门店业绩 | |
| 63 | + | |
| 64 | +#### 老店店长提成规则 | |
| 65 | + | |
| 66 | +根据门店分类(A、B、C类)和业绩是否达标,使用不同的提成比例: | |
| 67 | + | |
| 68 | +| 门店分类 | 业绩未达标 | 业绩达标 | | |
| 69 | +|---------|----------|---------| | |
| 70 | +| A类门店 | 3% | 3.5% | | |
| 71 | +| B类门店 | 3.5% | 4% | | |
| 72 | +| C类门店 | 4% | 4.5% | | |
| 73 | + | |
| 74 | +**业绩达标判断**: | |
| 75 | +- 业绩达标:门店业绩 ≥ 门店生命线 | |
| 76 | +- 业绩未达标:门店业绩 < 门店生命线 | |
| 77 | + | |
| 78 | +**计算公式**: | |
| 79 | +``` | |
| 80 | +如果 业绩 ≥ 生命线(业绩达标): | |
| 81 | + 提成 = 毛利 × 对应提成比例(业绩达标) | |
| 82 | + | |
| 83 | +如果 业绩 < 生命线(业绩未达标): | |
| 84 | + 提成 = 毛利 × 对应提成比例(业绩未达标) | |
| 85 | +``` | |
| 86 | + | |
| 87 | +#### 新店店长提成规则 | |
| 88 | + | |
| 89 | +**提成比例**(不区分门店分类): | |
| 90 | +- 业绩未达标:3% | |
| 91 | +- 业绩达标:3.5% | |
| 92 | + | |
| 93 | +**计算公式**: | |
| 94 | +``` | |
| 95 | +如果 业绩 ≥ 生命线(业绩达标): | |
| 96 | + 提成 = 毛利 × 3.5% | |
| 97 | + | |
| 98 | +如果 业绩 < 生命线(业绩未达标): | |
| 99 | + 提成 = 毛利 × 3% | |
| 100 | +``` | |
| 101 | + | |
| 102 | +--- | |
| 103 | + | |
| 104 | +## 📊 毛利计算公式 | |
| 105 | + | |
| 106 | +**核心公式**: | |
| 107 | +``` | |
| 108 | +毛利 = 销售业绩 - 产品物料 - 合作项目成本 - 店内支出 - 洗毛巾 | |
| 109 | +``` | |
| 110 | + | |
| 111 | +### 1. 销售业绩 | |
| 112 | + | |
| 113 | +**计算公式**: | |
| 114 | +``` | |
| 115 | +销售业绩 = 开单业绩 - 退款业绩 | |
| 116 | +``` | |
| 117 | + | |
| 118 | +**数据来源**: | |
| 119 | + | |
| 120 | +1. **开单业绩**: | |
| 121 | + - 表:`lq_kd_kdjlb`(开单记录表) | |
| 122 | + - 字段:`sfyj`(实付业绩) | |
| 123 | + - 条件: | |
| 124 | + - `F_IsEffective = 1`(有效记录) | |
| 125 | + - `Djmd = @StoreId`(门店ID) | |
| 126 | + - `DATE_FORMAT(Kdrq, '%Y%m') = @Month`(月份,YYYYMM格式) | |
| 127 | + | |
| 128 | +2. **退款业绩**: | |
| 129 | + - 表:`lq_hytk_hytk`(退卡记录表) | |
| 130 | + - 字段:`F_ActualRefundAmount`(实际退款金额) | |
| 131 | + - 条件: | |
| 132 | + - `F_IsEffective = 1`(有效记录) | |
| 133 | + - `md = @StoreId`(门店ID) | |
| 134 | + - `DATE_FORMAT(tksj, '%Y%m') = @Month`(月份,YYYYMM格式) | |
| 135 | + | |
| 136 | +**SQL示例**: | |
| 137 | +```sql | |
| 138 | +-- 开单业绩 | |
| 139 | +SELECT COALESCE(SUM(sfyj), 0) as BillingPerformance | |
| 140 | +FROM lq_kd_kdjlb | |
| 141 | +WHERE Djmd = @StoreId | |
| 142 | + AND DATE_FORMAT(Kdrq, '%Y%m') = @Month | |
| 143 | + AND F_IsEffective = 1 | |
| 144 | + | |
| 145 | +-- 退款业绩 | |
| 146 | +SELECT COALESCE(SUM(F_ActualRefundAmount), 0) as RefundPerformance | |
| 147 | +FROM lq_hytk_hytk | |
| 148 | +WHERE md = @StoreId | |
| 149 | + AND DATE_FORMAT(tksj, '%Y%m') = @Month | |
| 150 | + AND F_IsEffective = 1 | |
| 151 | + | |
| 152 | +-- 销售业绩 | |
| 153 | +销售业绩 = 开单业绩 - 退款业绩 | |
| 154 | +``` | |
| 155 | + | |
| 156 | +--- | |
| 157 | + | |
| 158 | +### 2. 产品物料 | |
| 159 | + | |
| 160 | +**计算公式**: | |
| 161 | +``` | |
| 162 | +产品物料 = 仓库领用金额合计 | |
| 163 | +``` | |
| 164 | + | |
| 165 | +**数据来源**: | |
| 166 | +- 表:`lq_inventory_usage`(库存使用记录表) | |
| 167 | +- 字段:`F_TotalAmount`(合计金额) | |
| 168 | +- 条件: | |
| 169 | + - `F_IsEffective = 1`(有效记录) | |
| 170 | + - `F_StoreId = @StoreId`(门店ID) | |
| 171 | + - **特殊规则**:核算11月工资时,算的是10月份的仓库领用 | |
| 172 | + - 如果计算月份是11月,则查询10月的数据 | |
| 173 | + - 其他月份正常查询当月数据 | |
| 174 | + | |
| 175 | +**SQL示例**: | |
| 176 | +```sql | |
| 177 | +-- 产品物料(特殊规则:11月工资算10月数据) | |
| 178 | +SET @QueryMonth = @Month; | |
| 179 | +IF @Month = '202411' THEN | |
| 180 | + SET @QueryMonth = '202410'; | |
| 181 | +END IF; | |
| 182 | + | |
| 183 | +SELECT COALESCE(SUM(F_TotalAmount), 0) as MaterialCost | |
| 184 | +FROM lq_inventory_usage | |
| 185 | +WHERE F_StoreId = @StoreId | |
| 186 | + AND DATE_FORMAT(F_UsageTime, '%Y%m') = @QueryMonth | |
| 187 | + AND F_IsEffective = 1 | |
| 188 | +``` | |
| 189 | + | |
| 190 | +--- | |
| 191 | + | |
| 192 | +### 3. 合作项目成本 | |
| 193 | + | |
| 194 | +**计算公式**: | |
| 195 | +``` | |
| 196 | +合作项目成本 = 合作成本表合计金额 | |
| 197 | +``` | |
| 198 | + | |
| 199 | +**数据来源**: | |
| 200 | +- 表:`lq_cooperation_cost`(合作成本表)**需要新建** | |
| 201 | +- 字段:`F_TotalAmount`(合计金额) | |
| 202 | +- 条件: | |
| 203 | + - `F_StoreId = @StoreId`(门店ID) | |
| 204 | + - `F_Year = @Year`(年份) | |
| 205 | + - `F_Month = @Month`(月份,YYYYMM格式) | |
| 206 | + | |
| 207 | +**表结构设计**: | |
| 208 | +```sql | |
| 209 | +CREATE TABLE IF NOT EXISTS `lq_cooperation_cost` ( | |
| 210 | + `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID', | |
| 211 | + `F_StoreId` VARCHAR(50) NOT NULL COMMENT '门店ID(关联lq_mdxx.F_Id)', | |
| 212 | + `F_StoreName` VARCHAR(200) NULL COMMENT '门店名称', | |
| 213 | + `F_Year` INT NOT NULL COMMENT '年份', | |
| 214 | + `F_Month` VARCHAR(6) NOT NULL COMMENT '月份(YYYYMM格式)', | |
| 215 | + `F_TotalAmount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '合计金额', | |
| 216 | + `F_Remarks` VARCHAR(1000) NULL COMMENT '备注说明', | |
| 217 | + `F_IsEffective` INT DEFAULT 1 COMMENT '是否有效(1:有效 0:无效)', | |
| 218 | + `F_CreateUser` VARCHAR(50) NULL COMMENT '创建人ID', | |
| 219 | + `F_CreateTime` DATETIME NULL COMMENT '创建时间', | |
| 220 | + `F_UpdateUser` VARCHAR(50) NULL COMMENT '更新人ID', | |
| 221 | + `F_UpdateTime` DATETIME NULL COMMENT '更新时间', | |
| 222 | + PRIMARY KEY (`F_Id`), | |
| 223 | + KEY `idx_store_month` (`F_StoreId`, `F_Year`, `F_Month`), | |
| 224 | + KEY `idx_month` (`F_Year`, `F_Month`) | |
| 225 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='合作成本表'; | |
| 226 | +``` | |
| 227 | + | |
| 228 | +**功能要求**: | |
| 229 | +- 支持按门店、年份、月份查询 | |
| 230 | +- 支持导入功能(Excel导入) | |
| 231 | +- 支持增删改查操作 | |
| 232 | + | |
| 233 | +--- | |
| 234 | + | |
| 235 | +### 4. 店内支出 | |
| 236 | + | |
| 237 | +**计算公式**: | |
| 238 | +``` | |
| 239 | +店内支出 = 店内支出表合计金额 | |
| 240 | +``` | |
| 241 | + | |
| 242 | +**数据来源**: | |
| 243 | +- 表:`lq_store_expense`(店内支出表)**需要新建** | |
| 244 | +- 字段:`F_Amount`(金额) | |
| 245 | +- 条件: | |
| 246 | + - `F_StoreId = @StoreId`(门店ID) | |
| 247 | + - `DATE_FORMAT(F_ExpenseDate, '%Y%m') = @Month`(月份,YYYYMM格式) | |
| 248 | + - `F_IsEffective = 1`(有效记录) | |
| 249 | + | |
| 250 | +**业务流程**: | |
| 251 | +1. 从报销表导出本月审核通过的报销表明细 | |
| 252 | + - 表:`lq_reimbursement_application`(报销申请表) | |
| 253 | + - 关联表:`lq_purchase_records`(购买记录表) | |
| 254 | + - 条件: | |
| 255 | + - `F_ApprovalStatus = '已通过'`(审批状态) | |
| 256 | + - `F_ApplicationStoreId = @StoreId`(门店ID) | |
| 257 | + - `DATE_FORMAT(F_ApplicationTime, '%Y%m') = @Month`(月份) | |
| 258 | +2. 线下重新整理明细(可能调整金额、分类等) | |
| 259 | +3. 导入到店内支出表 `lq_store_expense` | |
| 260 | + | |
| 261 | +**表结构设计**(参考购买记录表结构): | |
| 262 | +```sql | |
| 263 | +CREATE TABLE IF NOT EXISTS `lq_store_expense` ( | |
| 264 | + `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID', | |
| 265 | + `F_StoreId` VARCHAR(50) NOT NULL COMMENT '门店ID(关联lq_mdxx.F_Id)', | |
| 266 | + `F_StoreName` VARCHAR(200) NULL COMMENT '门店名称', | |
| 267 | + `F_ExpenseCategoryId` VARCHAR(50) NULL COMMENT '支出分类ID(关联lq_reimbursement_category.F_Id)', | |
| 268 | + `F_ExpenseCategoryName` VARCHAR(200) NULL COMMENT '支出分类名称', | |
| 269 | + `F_ExpenseDate` DATETIME NOT NULL COMMENT '支出日期', | |
| 270 | + `F_UnitPrice` DECIMAL(18,2) DEFAULT 0.00 COMMENT '单价', | |
| 271 | + `F_Quantity` INT DEFAULT 0 COMMENT '数量', | |
| 272 | + `F_Amount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '金额', | |
| 273 | + `F_Memo` VARCHAR(1000) NULL COMMENT '备注说明', | |
| 274 | + `F_Attachment` TEXT NULL COMMENT '附件(JSON格式)', | |
| 275 | + `F_RelatedReimbursementId` VARCHAR(50) NULL COMMENT '关联报销申请ID(可选)', | |
| 276 | + `F_RelatedPurchaseRecordId` VARCHAR(50) NULL COMMENT '关联购买记录ID(可选)', | |
| 277 | + `F_IsEffective` INT DEFAULT 1 COMMENT '是否有效(1:有效 0:无效)', | |
| 278 | + `F_CreateUser` VARCHAR(50) NULL COMMENT '创建人ID', | |
| 279 | + `F_CreateTime` DATETIME NULL COMMENT '创建时间', | |
| 280 | + `F_UpdateUser` VARCHAR(50) NULL COMMENT '更新人ID', | |
| 281 | + `F_UpdateTime` DATETIME NULL COMMENT '更新时间', | |
| 282 | + PRIMARY KEY (`F_Id`), | |
| 283 | + KEY `idx_store_date` (`F_StoreId`, `F_ExpenseDate`), | |
| 284 | + KEY `idx_expense_date` (`F_ExpenseDate`), | |
| 285 | + KEY `idx_category` (`F_ExpenseCategoryId`) | |
| 286 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='店内支出表'; | |
| 287 | +``` | |
| 288 | + | |
| 289 | +**功能要求**: | |
| 290 | +- 支持按门店、月份查询 | |
| 291 | +- 支持导出报销表明细(用于线下整理) | |
| 292 | +- 支持导入功能(Excel导入) | |
| 293 | +- 支持增删改查操作 | |
| 294 | + | |
| 295 | +--- | |
| 296 | + | |
| 297 | +### 5. 洗毛巾 | |
| 298 | + | |
| 299 | +**计算公式**: | |
| 300 | +``` | |
| 301 | +洗毛巾 = 送洗记录总费用 | |
| 302 | +``` | |
| 303 | + | |
| 304 | +**数据来源**: | |
| 305 | +- 表:`lq_laundry_flow`(清洗流水表) | |
| 306 | +- 字段:`F_TotalPrice`(总费用) | |
| 307 | +- 条件: | |
| 308 | + - `F_FlowType = 0`(流水类型:送出) | |
| 309 | + - `F_StoreId = @StoreId`(门店ID) | |
| 310 | + - `DATE_FORMAT(F_CreateTime, '%Y%m') = @Month`(月份,YYYYMM格式) | |
| 311 | + - `F_IsEffective = 1`(有效记录) | |
| 312 | + | |
| 313 | +**重要说明**: | |
| 314 | +- 只要送出就产生金额(不管是否送回) | |
| 315 | +- 只统计 `F_FlowType = 0`(送出)的记录 | |
| 316 | + | |
| 317 | +**SQL示例**: | |
| 318 | +```sql | |
| 319 | +SELECT COALESCE(SUM(F_TotalPrice), 0) as LaundryCost | |
| 320 | +FROM lq_laundry_flow | |
| 321 | +WHERE F_StoreId = @StoreId | |
| 322 | + AND F_FlowType = 0 | |
| 323 | + AND DATE_FORMAT(F_CreateTime, '%Y%m') = @Month | |
| 324 | + AND F_IsEffective = 1 | |
| 325 | +``` | |
| 326 | + | |
| 327 | +--- | |
| 328 | + | |
| 329 | +## 🔍 其他重要规则 | |
| 330 | + | |
| 331 | +### 1. 旗舰店判断 | |
| 332 | + | |
| 333 | +**判断方式**: | |
| 334 | +- 表:`lq_mdxx`(门店信息表) | |
| 335 | +- 字段:`F_StoreType`(门店类型) | |
| 336 | +- 枚举值: | |
| 337 | + - `1` = 门店200平(StoreTypeEnum.门店200平) | |
| 338 | + - `2` = 旗舰店(StoreTypeEnum.旗舰店) | |
| 339 | + | |
| 340 | +**代码实现**: | |
| 341 | +```csharp | |
| 342 | +// 判断是否为旗舰店 | |
| 343 | +bool isFlagshipStore = store.StoreType == (int)StoreTypeEnum.旗舰店; | |
| 344 | +``` | |
| 345 | + | |
| 346 | +**特殊规则**: | |
| 347 | +- 旗舰店类型门店需要扣除负奖励800元 | |
| 348 | + | |
| 349 | +--- | |
| 350 | + | |
| 351 | +### 2. 新店店长 | |
| 352 | + | |
| 353 | +**重要说明**: | |
| 354 | +- 新店店长所有阶段都有负奖励 | |
| 355 | +- 新店判断逻辑:需要根据门店开业时间或新店保护期判断(参考其他工资计算服务) | |
| 356 | + | |
| 357 | +--- | |
| 358 | + | |
| 359 | +### 3. 店长岗位名称 | |
| 360 | + | |
| 361 | +**系统标识**: | |
| 362 | +- 岗位名称:`"店长"` | |
| 363 | +- 查询条件:`BASE_USER.F_ZW = "店长"` 或 `BASE_USER.F_GW = "店长"` | |
| 364 | + | |
| 365 | +--- | |
| 366 | + | |
| 367 | +## 📋 数据来源总结 | |
| 368 | + | |
| 369 | +### 核心数据表 | |
| 370 | + | |
| 371 | +1. **门店信息表** (`lq_mdxx`) | |
| 372 | + - `F_Id`:门店ID | |
| 373 | + - `F_StoreType`:门店类型(判断旗舰店) | |
| 374 | + - `F_StoreCategory`:门店分类(A、B、C类) | |
| 375 | + | |
| 376 | +2. **门店目标表** (`lq_md_target`) | |
| 377 | + - `F_StoreId`:门店ID | |
| 378 | + - `F_Month`:月份(YYYYMM格式) | |
| 379 | + - `F_StoreLifeline`:门店生命线 | |
| 380 | + - `F_HeadcountTarget`:目标人头数 | |
| 381 | + - `F_ConsumeTarget`:目标消耗 | |
| 382 | + | |
| 383 | +3. **开单记录表** (`lq_kd_kdjlb`) | |
| 384 | + - `sfyj`:实付业绩(开单业绩) | |
| 385 | + - `Djmd`:门店ID | |
| 386 | + - `Kdrq`:开单日期 | |
| 387 | + | |
| 388 | +4. **退卡记录表** (`lq_hytk_hytk`) | |
| 389 | + - `F_ActualRefundAmount`:实际退款金额(退款业绩) | |
| 390 | + - `md`:门店ID | |
| 391 | + - `tksj`:退卡时间 | |
| 392 | + | |
| 393 | +5. **库存使用记录表** (`lq_inventory_usage`) | |
| 394 | + - `F_TotalAmount`:合计金额(产品物料) | |
| 395 | + - `F_StoreId`:门店ID | |
| 396 | + - `F_UsageTime`:使用时间 | |
| 397 | + | |
| 398 | +6. **合作成本表** (`lq_cooperation_cost`) **需要新建** | |
| 399 | + - `F_TotalAmount`:合计金额 | |
| 400 | + - `F_StoreId`:门店ID | |
| 401 | + - `F_Year`:年份 | |
| 402 | + - `F_Month`:月份 | |
| 403 | + | |
| 404 | +7. **店内支出表** (`lq_store_expense`) **需要新建** | |
| 405 | + - `F_Amount`:金额 | |
| 406 | + - `F_StoreId`:门店ID | |
| 407 | + - `F_ExpenseDate`:支出日期 | |
| 408 | + | |
| 409 | +8. **清洗流水表** (`lq_laundry_flow`) | |
| 410 | + - `F_TotalPrice`:总费用(洗毛巾) | |
| 411 | + - `F_StoreId`:门店ID | |
| 412 | + - `F_FlowType`:流水类型(0=送出) | |
| 413 | + | |
| 414 | +9. **耗卡记录表** (`lq_xh_hyhk`) | |
| 415 | + - 用于统计进店消耗人数 | |
| 416 | + | |
| 417 | +10. **系统用户表** (`BASE_USER`) | |
| 418 | + - `F_REALNAME`:姓名 | |
| 419 | + - `F_ZW`:职位(店长) | |
| 420 | + - `F_MDID`:门店ID | |
| 421 | + | |
| 422 | +--- | |
| 423 | + | |
| 424 | +## 🚀 需要先实现的功能 | |
| 425 | + | |
| 426 | +### 第一阶段:基础数据表 | |
| 427 | + | |
| 428 | +#### 1. 合作成本表 (`lq_cooperation_cost`) | |
| 429 | + | |
| 430 | +**功能清单**: | |
| 431 | +- [ ] 创建表结构(SQL) | |
| 432 | +- [ ] 创建实体类 (`LqCooperationCostEntity`) | |
| 433 | +- [ ] 创建DTO类(输入/输出) | |
| 434 | +- [ ] 创建Service类 (`LqCooperationCostService`) | |
| 435 | +- [ ] 实现CRUD接口 | |
| 436 | +- [ ] 实现导入功能(Excel导入) | |
| 437 | +- [ ] 实现查询接口(按门店、年份、月份) | |
| 438 | + | |
| 439 | +**优先级**:高(毛利计算必需) | |
| 440 | + | |
| 441 | +--- | |
| 442 | + | |
| 443 | +#### 2. 店内支出表 (`lq_store_expense`) | |
| 444 | + | |
| 445 | +**功能清单**: | |
| 446 | +- [ ] 创建表结构(SQL) | |
| 447 | +- [ ] 创建实体类 (`LqStoreExpenseEntity`) | |
| 448 | +- [ ] 创建DTO类(输入/输出) | |
| 449 | +- [ ] 创建Service类 (`LqStoreExpenseService`) | |
| 450 | +- [ ] 实现CRUD接口 | |
| 451 | +- [ ] 实现导出功能(导出报销表明细) | |
| 452 | +- [ ] 实现导入功能(Excel导入) | |
| 453 | +- [ ] 实现查询接口(按门店、月份) | |
| 454 | + | |
| 455 | +**优先级**:高(毛利计算必需) | |
| 456 | + | |
| 457 | +--- | |
| 458 | + | |
| 459 | +### 第二阶段:数据查询接口 | |
| 460 | + | |
| 461 | +#### 3. 毛利计算接口 | |
| 462 | + | |
| 463 | +**功能清单**: | |
| 464 | +- [ ] 实现销售业绩查询(开单业绩 - 退款业绩) | |
| 465 | +- [ ] 实现产品物料查询(仓库领用金额,特殊规则:11月算10月) | |
| 466 | +- [ ] 实现合作项目成本查询 | |
| 467 | +- [ ] 实现店内支出查询 | |
| 468 | +- [ ] 实现洗毛巾查询 | |
| 469 | +- [ ] 实现毛利汇总计算接口 | |
| 470 | + | |
| 471 | +**优先级**:高(工资计算必需) | |
| 472 | + | |
| 473 | +--- | |
| 474 | + | |
| 475 | +### 第三阶段:工资计算 | |
| 476 | + | |
| 477 | +#### 4. 店长工资计算服务 | |
| 478 | + | |
| 479 | +**功能清单**: | |
| 480 | +- [ ] 实现底薪计算(老店/新店,旗舰店特殊规则) | |
| 481 | +- [ ] 实现提成计算(基于毛利,老店/新店不同规则) | |
| 482 | +- [ ] 实现考核指标判断(业绩、人头、消耗) | |
| 483 | +- [ ] 实现新店判断逻辑 | |
| 484 | +- [ ] 实现旗舰店判断逻辑 | |
| 485 | +- [ ] 实现完整工资计算接口 | |
| 486 | + | |
| 487 | +**优先级**:中(依赖第一阶段和第二阶段) | |
| 488 | + | |
| 489 | +--- | |
| 490 | + | |
| 491 | +## 📝 实现顺序建议 | |
| 492 | + | |
| 493 | +### 步骤1:创建合作成本表 | |
| 494 | +1. 编写SQL创建表结构 | |
| 495 | +2. 创建实体类、DTO、Service | |
| 496 | +3. 实现CRUD接口 | |
| 497 | +4. 实现导入功能 | |
| 498 | + | |
| 499 | +### 步骤2:创建店内支出表 | |
| 500 | +1. 编写SQL创建表结构 | |
| 501 | +2. 创建实体类、DTO、Service | |
| 502 | +3. 实现CRUD接口 | |
| 503 | +4. 实现导出报销表明细功能 | |
| 504 | +5. 实现导入功能 | |
| 505 | + | |
| 506 | +### 步骤3:实现毛利计算接口 | |
| 507 | +1. 实现销售业绩查询 | |
| 508 | +2. 实现产品物料查询(注意11月特殊规则) | |
| 509 | +3. 实现合作项目成本查询 | |
| 510 | +4. 实现店内支出查询 | |
| 511 | +5. 实现洗毛巾查询 | |
| 512 | +6. 实现毛利汇总计算 | |
| 513 | + | |
| 514 | +### 步骤4:实现店长工资计算 | |
| 515 | +1. 实现底薪计算逻辑 | |
| 516 | +2. 实现提成计算逻辑 | |
| 517 | +3. 实现考核指标判断 | |
| 518 | +4. 实现完整工资计算接口 | |
| 519 | +5. 编写测试用例验证 | |
| 520 | + | |
| 521 | +--- | |
| 522 | + | |
| 523 | +## ⚠️ 注意事项 | |
| 524 | + | |
| 525 | +1. **产品物料特殊规则**: | |
| 526 | + - 核算11月工资时,算的是10月份的仓库领用 | |
| 527 | + - 需要在查询时特殊处理 | |
| 528 | + | |
| 529 | +2. **店内支出流程**: | |
| 530 | + - 必须先导出报销表明细 | |
| 531 | + - 线下整理后再导入 | |
| 532 | + - 不是直接从报销表计算 | |
| 533 | + | |
| 534 | +3. **洗毛巾计算**: | |
| 535 | + - 只要送出就产生金额 | |
| 536 | + - 只统计 `F_FlowType = 0`(送出)的记录 | |
| 537 | + | |
| 538 | +4. **旗舰店判断**: | |
| 539 | + - 使用 `F_StoreType = 2` 判断 | |
| 540 | + - 需要扣除负奖励800元 | |
| 541 | + | |
| 542 | +5. **新店店长**: | |
| 543 | + - 所有阶段都有负奖励 | |
| 544 | + - 需要确认新店判断逻辑 | |
| 545 | + | |
| 546 | +6. **数据一致性**: | |
| 547 | + - 所有金额计算保留2位小数 | |
| 548 | + - 确保月份格式统一(YYYYMM) | |
| 549 | + | |
| 550 | +--- | |
| 551 | + | |
| 552 | +## 📌 待确认问题 | |
| 553 | + | |
| 554 | +1. **新店判断逻辑**: | |
| 555 | + - 如何判断门店是否为新店? | |
| 556 | + - 是否有新店保护期字段? | |
| 557 | + - 参考其他工资计算服务的实现 | |
| 558 | + | |
| 559 | +2. **合作成本导入格式**: | |
| 560 | + - Excel导入的字段格式要求 | |
| 561 | + - 是否需要校验门店ID、年份、月份 | |
| 562 | + | |
| 563 | +3. **店内支出导入格式**: | |
| 564 | + - Excel导入的字段格式要求 | |
| 565 | + - 是否需要关联报销申请ID | |
| 566 | + | |
| 567 | +4. **毛利计算精度**: | |
| 568 | + - 各组成部分的精度要求 | |
| 569 | + - 最终毛利的精度要求 | |
| 570 | + | |
| 571 | +--- | |
| 572 | + | |
| 573 | +## 🔗 相关代码位置 | |
| 574 | + | |
| 575 | +- **门店信息查询**:`LqMdxxService.cs` | |
| 576 | +- **门店分类枚举**:`StoreCategoryEnum.cs` | |
| 577 | +- **门店类型枚举**:`StoreTypeEnum.cs` | |
| 578 | +- **门店实体类**:`LqMdxxEntity.cs` | |
| 579 | +- **库存使用服务**:`LqInventoryUsageService.cs` | |
| 580 | +- **清洗流水服务**:`LqLaundryFlowService.cs` | |
| 581 | +- **报销申请服务**:`LqReimbursementApplicationService.cs` | |
| 582 | +- **购买记录服务**:`LqPurchaseRecordsService.cs` | |
| 583 | +- **其他工资计算服务**:`LqAssistantSalaryService.cs`、`LqDirectorSalaryService.cs`(参考新店判断逻辑) | |
| 584 | + | ... | ... |
测试脚本-创建店长工资测试数据.sh
0 → 100755
| 1 | +#!/bin/bash | |
| 2 | + | |
| 3 | +# 店长工资计算测试数据创建脚本 | |
| 4 | +# 门店ID:1649328471923847172(绿纤华润店) | |
| 5 | +# 员工ID:13881949169(易春梅) | |
| 6 | + | |
| 7 | +TOKEN="Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOiJhZG1pbiIsIkFjY291bnQiOiJhZG1pbiIsIlVzZXJOYW1lIjoi566h55CG5ZGYIiwiQWRtaW5pc3RyYXRvciI6MSwiVGVuYW50SWQiOiJkYiIsIlRlbmFudERiTmFtZSI6ImxxZXJwX3Rlc3QiLCJpYXQiOjE3NjUzNDA4ODYsIm5iZiI6MTc2NTM0MDg4NiwiZXhwIjoxNzY1Mzk0ODg2LCJpc3MiOiJ5aW5tYWlzb2Z0IiwiYXVkIjoieWlubWFpc29mdCJ9.s6IjmTLVCF_Dmho3rXWiL1FE-wimA4ZTUYt_W8h4URk" | |
| 8 | + | |
| 9 | +STORE_ID="1649328471923847172" | |
| 10 | +STORE_NAME="绿纤华润店" | |
| 11 | +APPROVER_ID="13032810387" | |
| 12 | +LAUNDRY_SUPPLIER_ID="764256388720362757" | |
| 13 | +EXPENSE_CATEGORY_ID="11156146041171400" | |
| 14 | + | |
| 15 | +echo "=== 1. 创建合作成本数据(2025年11月)===" | |
| 16 | +curl -s -X POST "http://localhost:2011/api/Extend/LqCooperationCost" \ | |
| 17 | + -H "Authorization: ${TOKEN}" \ | |
| 18 | + -H "Content-Type: application/json" \ | |
| 19 | + -d "{ | |
| 20 | + \"storeId\": \"${STORE_ID}\", | |
| 21 | + \"storeName\": \"${STORE_NAME}\", | |
| 22 | + \"year\": 2025, | |
| 23 | + \"month\": \"202511\", | |
| 24 | + \"totalAmount\": 3000.00, | |
| 25 | + \"remarks\": \"测试数据-合作成本\" | |
| 26 | + }" | jq . | |
| 27 | + | |
| 28 | +echo "" | |
| 29 | +echo "=== 2. 创建店内支出数据(2025年11月)===" | |
| 30 | +curl -s -X POST "http://localhost:2011/api/Extend/LqStoreExpense" \ | |
| 31 | + -H "Authorization: ${TOKEN}" \ | |
| 32 | + -H "Content-Type: application/json" \ | |
| 33 | + -d "{ | |
| 34 | + \"storeId\": \"${STORE_ID}\", | |
| 35 | + \"storeName\": \"${STORE_NAME}\", | |
| 36 | + \"expenseCategoryId\": \"${EXPENSE_CATEGORY_ID}\", | |
| 37 | + \"expenseCategoryName\": \"日常支出\", | |
| 38 | + \"expenseDate\": \"2025-11-15T00:00:00\", | |
| 39 | + \"unitPrice\": 2000.00, | |
| 40 | + \"quantity\": 1, | |
| 41 | + \"amount\": 2000.00, | |
| 42 | + \"memo\": \"测试数据-店内支出\" | |
| 43 | + }" | jq . | |
| 44 | + | |
| 45 | +echo "" | |
| 46 | +echo "=== 3. 创建洗毛巾费用数据(2025年11月,送出)===" | |
| 47 | +curl -s -X POST "http://localhost:2011/api/Extend/LqLaundryFlow/Send" \ | |
| 48 | + -H "Authorization: ${TOKEN}" \ | |
| 49 | + -H "Content-Type: application/json" \ | |
| 50 | + -d "{ | |
| 51 | + \"storeId\": \"${STORE_ID}\", | |
| 52 | + \"productType\": \"毛巾\", | |
| 53 | + \"laundrySupplierId\": \"${LAUNDRY_SUPPLIER_ID}\", | |
| 54 | + \"quantity\": 100, | |
| 55 | + \"remark\": \"测试数据-洗毛巾\" | |
| 56 | + }" | jq . | |
| 57 | + | |
| 58 | +echo "" | |
| 59 | +echo "=== 4. 创建产品物料数据(2025年10月,用于11月工资计算)===" | |
| 60 | +# 先查询一个产品ID | |
| 61 | +PRODUCT_ID=$(curl -s -X GET "http://localhost:2011/api/Extend/LqProduct?currentPage=1&pageSize=1" \ | |
| 62 | + -H "Authorization: ${TOKEN}" | jq -r '.data.list[0].id // empty') | |
| 63 | + | |
| 64 | +if [ -z "$PRODUCT_ID" ]; then | |
| 65 | + echo "警告:未找到产品,跳过产品物料数据创建" | |
| 66 | +else | |
| 67 | + echo "使用产品ID: ${PRODUCT_ID}" | |
| 68 | + curl -s -X POST "http://localhost:2011/api/Extend/LqInventoryUsage/BatchCreate" \ | |
| 69 | + -H "Authorization: ${TOKEN}" \ | |
| 70 | + -H "Content-Type: application/json" \ | |
| 71 | + -d "{ | |
| 72 | + \"approverId\": \"${APPROVER_ID}\", | |
| 73 | + \"applicationStoreId\": \"${STORE_ID}\", | |
| 74 | + \"remarks\": \"测试数据-产品物料(10月)\", | |
| 75 | + \"usageItems\": [ | |
| 76 | + { | |
| 77 | + \"productId\": \"${PRODUCT_ID}\", | |
| 78 | + \"storeId\": \"${STORE_ID}\", | |
| 79 | + \"usageTime\": \"2025-10-15T00:00:00\", | |
| 80 | + \"usageQuantity\": 10 | |
| 81 | + } | |
| 82 | + ] | |
| 83 | + }" | jq . | |
| 84 | +fi | |
| 85 | + | |
| 86 | +echo "" | |
| 87 | +echo "=== 5. 重新计算2025年11月店长工资 ===" | |
| 88 | +curl -s -X POST "http://localhost:2011/api/Extend/LqStoreManagerSalary/calculate/store-manager?year=2025&month=11" \ | |
| 89 | + -H "Authorization: ${TOKEN}" | jq . | |
| 90 | + | |
| 91 | +echo "" | |
| 92 | +echo "=== 6. 查询计算结果 ===" | |
| 93 | +curl -s -X GET "http://localhost:2011/api/Extend/LqStoreManagerSalary/store-manager?Year=2025&Month=11¤tPage=1&pageSize=5" \ | |
| 94 | + -H "Authorization: ${TOKEN}" | jq '.data.list[0] | { | |
| 95 | + StoreName, | |
| 96 | + EmployeeName, | |
| 97 | + SalesPerformance, | |
| 98 | + ProductMaterial, | |
| 99 | + CooperationCost, | |
| 100 | + StoreExpense, | |
| 101 | + LaundryCost, | |
| 102 | + GrossProfit, | |
| 103 | + BaseSalary, | |
| 104 | + ActualBaseSalary, | |
| 105 | + CommissionRate, | |
| 106 | + CommissionAmount, | |
| 107 | + GrossSalary, | |
| 108 | + IsNewStore, | |
| 109 | + StoreCategory, | |
| 110 | + StoreType | |
| 111 | + }' | |
| 112 | + | ... | ... |
测试脚本-合作成本表和店内支出表.sh
0 → 100755
| 1 | +#!/bin/bash | |
| 2 | + | |
| 3 | +# 测试脚本 - 合作成本表和店内支出表 | |
| 4 | +# 使用方法:./测试脚本-合作成本表和店内支出表.sh <token> <base_url> | |
| 5 | +# 示例:./测试脚本-合作成本表和店内支出表.sh "Bearer xxx" "http://localhost:2011" | |
| 6 | + | |
| 7 | +TOKEN=${1:-"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOiJhZG1pbiIsIkFjY291bnQiOiJhZG1pbiIsIlVzZXJOYW1lIjoi566h55CG5ZGYIiwiQWRtaW5pc3RyYXRvciI6MSwiVGVuYW50SWQiOiJkYiIsIlRlbmFudERiTmFtZSI6ImxxZXJwX3Rlc3QiLCJpYXQiOjE3NjUyOTAwNTQsIm5iZiI6MTc2NTI5MDA1NCwiZXhwIjoxNzY1MzQ0MDU0LCJpc3MiOiJ5aW5tYWlzb2Z0IiwiYXVkIjoieWlubWFpc29mdCJ9.d68JfuAnxusEWSzloJu4uFPd5uK-Tf8_AA2gBPui-k4"} | |
| 8 | +BASE_URL=${2:-"http://localhost:2011"} | |
| 9 | + | |
| 10 | +echo "==========================================" | |
| 11 | +echo "开始测试 - 合作成本表和店内支出表" | |
| 12 | +echo "==========================================" | |
| 13 | +echo "" | |
| 14 | + | |
| 15 | +# 测试门店ID | |
| 16 | +STORE_ID_1="1649328471923847168" # 绿纤总部 | |
| 17 | +STORE_ID_2="1649328471923847169" # 绿纤紫荆店 | |
| 18 | +STORE_ID_3="1649328471923847170" # 绿纤龙湖店 | |
| 19 | + | |
| 20 | +# 颜色输出 | |
| 21 | +GREEN='\033[0;32m' | |
| 22 | +RED='\033[0;31m' | |
| 23 | +YELLOW='\033[1;33m' | |
| 24 | +NC='\033[0m' # No Color | |
| 25 | + | |
| 26 | +# 测试函数 | |
| 27 | +test_api() { | |
| 28 | + local name=$1 | |
| 29 | + local method=$2 | |
| 30 | + local url=$3 | |
| 31 | + local data=$4 | |
| 32 | + | |
| 33 | + echo -e "${YELLOW}测试: ${name}${NC}" | |
| 34 | + echo "请求: ${method} ${url}" | |
| 35 | + if [ -n "$data" ]; then | |
| 36 | + echo "数据: ${data}" | |
| 37 | + response=$(curl -s -w "\n%{http_code}" -X ${method} "${BASE_URL}${url}" \ | |
| 38 | + -H "Authorization: ${TOKEN}" \ | |
| 39 | + -H "Content-Type: application/json" \ | |
| 40 | + -d "${data}") | |
| 41 | + else | |
| 42 | + response=$(curl -s -w "\n%{http_code}" -X ${method} "${BASE_URL}${url}" \ | |
| 43 | + -H "Authorization: ${TOKEN}") | |
| 44 | + fi | |
| 45 | + | |
| 46 | + http_code=$(echo "$response" | tail -n1) | |
| 47 | + body=$(echo "$response" | sed '$d') | |
| 48 | + | |
| 49 | + if [ "$http_code" -eq 200 ] || [ "$http_code" -eq 201 ]; then | |
| 50 | + echo -e "${GREEN}✓ 成功 (HTTP ${http_code})${NC}" | |
| 51 | + echo "响应: ${body}" | jq . 2>/dev/null || echo "响应: ${body}" | |
| 52 | + echo "" | |
| 53 | + echo "$body" | |
| 54 | + else | |
| 55 | + echo -e "${RED}✗ 失败 (HTTP ${http_code})${NC}" | |
| 56 | + echo "响应: ${body}" | |
| 57 | + echo "" | |
| 58 | + echo "" | |
| 59 | + fi | |
| 60 | +} | |
| 61 | + | |
| 62 | +# ========================================== | |
| 63 | +# 第一阶段:合作成本表 CRUD 测试 | |
| 64 | +# ========================================== | |
| 65 | +echo "==========================================" | |
| 66 | +echo "第一阶段:合作成本表 CRUD 测试" | |
| 67 | +echo "==========================================" | |
| 68 | +echo "" | |
| 69 | + | |
| 70 | +# 1.1 创建合作成本记录 | |
| 71 | +echo "1.1 创建合作成本记录" | |
| 72 | +COOPERATION_COST_DATA='{"storeId":"'${STORE_ID_1}'","storeName":"绿纤总部","year":2025,"month":"202501","totalAmount":5000.00,"remarks":"CRUD测试-创建"}' | |
| 73 | +CREATE_RESPONSE=$(test_api "创建合作成本记录" "POST" "/api/Extend/LqCooperationCost" "${COOPERATION_COST_DATA}") | |
| 74 | +COOPERATION_COST_ID=$(echo "$CREATE_RESPONSE" | jq -r '.id // empty' 2>/dev/null) | |
| 75 | + | |
| 76 | +if [ -z "$COOPERATION_COST_ID" ]; then | |
| 77 | + echo "无法获取创建的记录ID,尝试从响应中提取..." | |
| 78 | + COOPERATION_COST_ID=$(echo "$CREATE_RESPONSE" | grep -oP '"id"\s*:\s*"[^"]*"' | head -1 | cut -d'"' -f4) | |
| 79 | +fi | |
| 80 | + | |
| 81 | +echo "创建的记录ID: ${COOPERATION_COST_ID}" | |
| 82 | +echo "" | |
| 83 | + | |
| 84 | +# 1.2 查询合作成本列表 | |
| 85 | +echo "1.2 查询合作成本列表" | |
| 86 | +test_api "查询合作成本列表" "GET" "/api/Extend/LqCooperationCost?currentPage=1&pageSize=10&storeId=${STORE_ID_1}" "" | |
| 87 | +echo "" | |
| 88 | + | |
| 89 | +# 1.3 查询合作成本详情 | |
| 90 | +if [ -n "$COOPERATION_COST_ID" ]; then | |
| 91 | + echo "1.3 查询合作成本详情" | |
| 92 | + test_api "查询合作成本详情" "GET" "/api/Extend/LqCooperationCost/${COOPERATION_COST_ID}" "" | |
| 93 | + echo "" | |
| 94 | + | |
| 95 | + # 1.4 更新合作成本记录 | |
| 96 | + echo "1.4 更新合作成本记录" | |
| 97 | + UPDATE_DATA='{"id":"'${COOPERATION_COST_ID}'","storeId":"'${STORE_ID_1}'","storeName":"绿纤总部","year":2025,"month":"202501","totalAmount":6000.00,"remarks":"CRUD测试-更新"}' | |
| 98 | + test_api "更新合作成本记录" "PUT" "/api/Extend/LqCooperationCost/${COOPERATION_COST_ID}" "${UPDATE_DATA}" | |
| 99 | + echo "" | |
| 100 | +fi | |
| 101 | + | |
| 102 | +# ========================================== | |
| 103 | +# 第二阶段:店内支出表 CRUD 测试 | |
| 104 | +# ========================================== | |
| 105 | +echo "==========================================" | |
| 106 | +echo "第二阶段:店内支出表 CRUD 测试" | |
| 107 | +echo "==========================================" | |
| 108 | +echo "" | |
| 109 | + | |
| 110 | +# 2.1 创建店内支出记录 | |
| 111 | +echo "2.1 创建店内支出记录" | |
| 112 | +STORE_EXPENSE_DATA='{"storeId":"'${STORE_ID_1}'","storeName":"绿纤总部","expenseCategoryId":"","expenseCategoryName":"办公用品","expenseDate":"2025-01-15T00:00:00","unitPrice":10.50,"quantity":5,"amount":52.50,"memo":"CRUD测试-创建","attachment":[],"relatedReimbursementId":"","relatedPurchaseRecordId":""}' | |
| 113 | +CREATE_EXPENSE_RESPONSE=$(test_api "创建店内支出记录" "POST" "/api/Extend/LqStoreExpense" "${STORE_EXPENSE_DATA}") | |
| 114 | +STORE_EXPENSE_ID=$(echo "$CREATE_EXPENSE_RESPONSE" | jq -r '.id // empty' 2>/dev/null) | |
| 115 | + | |
| 116 | +if [ -z "$STORE_EXPENSE_ID" ]; then | |
| 117 | + echo "无法获取创建的记录ID,尝试从响应中提取..." | |
| 118 | + STORE_EXPENSE_ID=$(echo "$CREATE_EXPENSE_RESPONSE" | grep -oP '"id"\s*:\s*"[^"]*"' | head -1 | cut -d'"' -f4) | |
| 119 | +fi | |
| 120 | + | |
| 121 | +echo "创建的记录ID: ${STORE_EXPENSE_ID}" | |
| 122 | +echo "" | |
| 123 | + | |
| 124 | +# 2.2 查询店内支出列表 | |
| 125 | +echo "2.2 查询店内支出列表" | |
| 126 | +test_api "查询店内支出列表" "GET" "/api/Extend/LqStoreExpense?currentPage=1&pageSize=10&storeId=${STORE_ID_1}" "" | |
| 127 | +echo "" | |
| 128 | + | |
| 129 | +# 2.3 查询店内支出详情 | |
| 130 | +if [ -n "$STORE_EXPENSE_ID" ]; then | |
| 131 | + echo "2.3 查询店内支出详情" | |
| 132 | + test_api "查询店内支出详情" "GET" "/api/Extend/LqStoreExpense/${STORE_EXPENSE_ID}" "" | |
| 133 | + echo "" | |
| 134 | + | |
| 135 | + # 2.4 更新店内支出记录 | |
| 136 | + echo "2.4 更新店内支出记录" | |
| 137 | + UPDATE_EXPENSE_DATA='{"id":"'${STORE_EXPENSE_ID}'","storeId":"'${STORE_ID_1}'","storeName":"绿纤总部","expenseCategoryId":"","expenseCategoryName":"水电费","expenseDate":"2025-01-20T00:00:00","unitPrice":0,"quantity":0,"amount":500.00,"memo":"CRUD测试-更新","attachment":[],"relatedReimbursementId":"","relatedPurchaseRecordId":""}' | |
| 138 | + test_api "更新店内支出记录" "PUT" "/api/Extend/LqStoreExpense/${STORE_EXPENSE_ID}" "${UPDATE_EXPENSE_DATA}" | |
| 139 | + echo "" | |
| 140 | +fi | |
| 141 | + | |
| 142 | +# ========================================== | |
| 143 | +# 第三阶段:Excel导入测试 | |
| 144 | +# ========================================== | |
| 145 | +echo "==========================================" | |
| 146 | +echo "第三阶段:Excel导入测试" | |
| 147 | +echo "==========================================" | |
| 148 | +echo "" | |
| 149 | + | |
| 150 | +# 3.1 导入合作成本表 | |
| 151 | +echo "3.1 导入合作成本表" | |
| 152 | +echo "注意:需要手动执行以下命令(需要提供Excel文件路径)" | |
| 153 | +echo "curl -X POST \"${BASE_URL}/api/Extend/LqCooperationCost/Actions/Import\" \\" | |
| 154 | +echo " -H \"Authorization: ${TOKEN}\" \\" | |
| 155 | +echo " -F \"file=@excel/合作成本表.xlsx\"" | |
| 156 | +echo "" | |
| 157 | + | |
| 158 | +# 3.2 导入店内支出表 | |
| 159 | +echo "3.2 导入店内支出表" | |
| 160 | +echo "注意:需要手动执行以下命令(需要提供Excel文件路径)" | |
| 161 | +echo "curl -X POST \"${BASE_URL}/api/Extend/LqStoreExpense/Actions/Import\" \\" | |
| 162 | +echo " -H \"Authorization: ${TOKEN}\" \\" | |
| 163 | +echo " -F \"file=@excel/店内支出表.xlsx\"" | |
| 164 | +echo "" | |
| 165 | + | |
| 166 | +# ========================================== | |
| 167 | +# 第四阶段:Excel导出测试 | |
| 168 | +# ========================================== | |
| 169 | +echo "==========================================" | |
| 170 | +echo "第四阶段:Excel导出测试" | |
| 171 | +echo "==========================================" | |
| 172 | +echo "" | |
| 173 | + | |
| 174 | +# 4.1 导出合作成本表 | |
| 175 | +echo "4.1 导出合作成本表" | |
| 176 | +test_api "导出合作成本表" "GET" "/api/Extend/LqCooperationCost/Actions/Export?dataType=1&storeId=${STORE_ID_1}&year=2025&month=202501" "" | |
| 177 | +echo "" | |
| 178 | + | |
| 179 | +# 4.2 导出店内支出表 | |
| 180 | +echo "4.2 导出店内支出表" | |
| 181 | +test_api "导出店内支出表" "GET" "/api/Extend/LqStoreExpense/Actions/Export?dataType=1&storeId=${STORE_ID_1}" "" | |
| 182 | +echo "" | |
| 183 | + | |
| 184 | +# ========================================== | |
| 185 | +# 第五阶段:报销表导出测试 | |
| 186 | +# ========================================== | |
| 187 | +echo "==========================================" | |
| 188 | +echo "第五阶段:报销表导出测试" | |
| 189 | +echo "==========================================" | |
| 190 | +echo "" | |
| 191 | + | |
| 192 | +# 5.1 导出报销表明细 | |
| 193 | +echo "5.1 导出报销表明细(本月已审核通过的)" | |
| 194 | +test_api "导出报销表明细" "GET" "/api/Extend/LqReimbursementApplication/Actions/ExportApprovedDetails?year=2025&month=01" "" | |
| 195 | +echo "" | |
| 196 | + | |
| 197 | +echo "==========================================" | |
| 198 | +echo "测试完成" | |
| 199 | +echo "==========================================" | |
| 200 | + | ... | ... |
测试计划-合作成本表和店内支出表.md
0 → 100644
| 1 | +# 测试计划 - 合作成本表和店内支出表 | |
| 2 | + | |
| 3 | +## 📋 测试范围 | |
| 4 | + | |
| 5 | +1. **合作成本表** | |
| 6 | + - CRUD操作(创建、查询、更新、删除) | |
| 7 | + - Excel导入功能 | |
| 8 | + - Excel导出功能 | |
| 9 | + | |
| 10 | +2. **店内支出表** | |
| 11 | + - CRUD操作(创建、查询、更新、删除) | |
| 12 | + - Excel导入功能 | |
| 13 | + - Excel导出功能 | |
| 14 | + | |
| 15 | +3. **报销表导出功能** | |
| 16 | + - 导出本月已审核通过的报销表明细 | |
| 17 | + | |
| 18 | +--- | |
| 19 | + | |
| 20 | +## 🧪 测试步骤 | |
| 21 | + | |
| 22 | +### 第一阶段:基础功能测试 | |
| 23 | + | |
| 24 | +#### 1. 合作成本表 - CRUD测试 | |
| 25 | + | |
| 26 | +**1.1 创建合作成本记录** | |
| 27 | +```bash | |
| 28 | +POST /api/Extend/LqCooperationCost | |
| 29 | +Content-Type: application/json | |
| 30 | + | |
| 31 | +{ | |
| 32 | + "storeId": "门店ID", | |
| 33 | + "storeName": "测试门店", | |
| 34 | + "year": 2025, | |
| 35 | + "month": "202501", | |
| 36 | + "totalAmount": 5000.00, | |
| 37 | + "remarks": "测试数据" | |
| 38 | +} | |
| 39 | +``` | |
| 40 | + | |
| 41 | +**预期结果**:创建成功,返回记录ID | |
| 42 | + | |
| 43 | +**1.2 查询合作成本列表** | |
| 44 | +```bash | |
| 45 | +GET /api/Extend/LqCooperationCost?currentPage=1&pageSize=10&storeId=门店ID | |
| 46 | +``` | |
| 47 | + | |
| 48 | +**预期结果**:返回分页列表,包含刚创建的记录 | |
| 49 | + | |
| 50 | +**1.3 查询合作成本详情** | |
| 51 | +```bash | |
| 52 | +GET /api/Extend/LqCooperationCost/{id} | |
| 53 | +``` | |
| 54 | + | |
| 55 | +**预期结果**:返回记录详情 | |
| 56 | + | |
| 57 | +**1.4 更新合作成本记录** | |
| 58 | +```bash | |
| 59 | +PUT /api/Extend/LqCooperationCost/{id} | |
| 60 | +Content-Type: application/json | |
| 61 | + | |
| 62 | +{ | |
| 63 | + "id": "记录ID", | |
| 64 | + "storeId": "门店ID", | |
| 65 | + "storeName": "测试门店", | |
| 66 | + "year": 2025, | |
| 67 | + "month": "202501", | |
| 68 | + "totalAmount": 6000.00, | |
| 69 | + "remarks": "更新后的测试数据" | |
| 70 | +} | |
| 71 | +``` | |
| 72 | + | |
| 73 | +**预期结果**:更新成功 | |
| 74 | + | |
| 75 | +**1.5 删除合作成本记录** | |
| 76 | +```bash | |
| 77 | +DELETE /api/Extend/LqCooperationCost/{id} | |
| 78 | +``` | |
| 79 | + | |
| 80 | +**预期结果**:逻辑删除成功(IsEffective = 0) | |
| 81 | + | |
| 82 | +--- | |
| 83 | + | |
| 84 | +#### 2. 店内支出表 - CRUD测试 | |
| 85 | + | |
| 86 | +**2.1 创建店内支出记录** | |
| 87 | +```bash | |
| 88 | +POST /api/Extend/LqStoreExpense | |
| 89 | +Content-Type: application/json | |
| 90 | + | |
| 91 | +{ | |
| 92 | + "storeId": "门店ID", | |
| 93 | + "storeName": "测试门店", | |
| 94 | + "expenseCategoryId": "", | |
| 95 | + "expenseCategoryName": "办公用品", | |
| 96 | + "expenseDate": "2025-01-15T00:00:00", | |
| 97 | + "unitPrice": 10.50, | |
| 98 | + "quantity": 5, | |
| 99 | + "amount": 52.50, | |
| 100 | + "memo": "测试支出", | |
| 101 | + "attachment": [], | |
| 102 | + "relatedReimbursementId": "", | |
| 103 | + "relatedPurchaseRecordId": "" | |
| 104 | +} | |
| 105 | +``` | |
| 106 | + | |
| 107 | +**预期结果**:创建成功 | |
| 108 | + | |
| 109 | +**2.2 查询店内支出列表** | |
| 110 | +```bash | |
| 111 | +GET /api/Extend/LqStoreExpense?currentPage=1&pageSize=10&storeId=门店ID | |
| 112 | +``` | |
| 113 | + | |
| 114 | +**预期结果**:返回分页列表 | |
| 115 | + | |
| 116 | +**2.3 查询店内支出详情** | |
| 117 | +```bash | |
| 118 | +GET /api/Extend/LqStoreExpense/{id} | |
| 119 | +``` | |
| 120 | + | |
| 121 | +**预期结果**:返回记录详情 | |
| 122 | + | |
| 123 | +**2.4 更新店内支出记录** | |
| 124 | +```bash | |
| 125 | +PUT /api/Extend/LqStoreExpense/{id} | |
| 126 | +Content-Type: application/json | |
| 127 | + | |
| 128 | +{ | |
| 129 | + "id": "记录ID", | |
| 130 | + "storeId": "门店ID", | |
| 131 | + "storeName": "测试门店", | |
| 132 | + "expenseCategoryId": "", | |
| 133 | + "expenseCategoryName": "水电费", | |
| 134 | + "expenseDate": "2025-01-20T00:00:00", | |
| 135 | + "unitPrice": 0, | |
| 136 | + "quantity": 0, | |
| 137 | + "amount": 500.00, | |
| 138 | + "memo": "更新后的测试支出", | |
| 139 | + "attachment": [], | |
| 140 | + "relatedReimbursementId": "", | |
| 141 | + "relatedPurchaseRecordId": "" | |
| 142 | +} | |
| 143 | +``` | |
| 144 | + | |
| 145 | +**预期结果**:更新成功 | |
| 146 | + | |
| 147 | +**2.5 删除店内支出记录** | |
| 148 | +```bash | |
| 149 | +DELETE /api/Extend/LqStoreExpense/{id} | |
| 150 | +``` | |
| 151 | + | |
| 152 | +**预期结果**:逻辑删除成功 | |
| 153 | + | |
| 154 | +--- | |
| 155 | + | |
| 156 | +### 第二阶段:Excel导入测试 | |
| 157 | + | |
| 158 | +#### 3. 合作成本表 - Excel导入测试 | |
| 159 | + | |
| 160 | +**3.1 准备Excel文件** | |
| 161 | +- 按照 `Excel导入格式说明.md` 中的格式创建Excel文件 | |
| 162 | +- 包含至少3条测试数据 | |
| 163 | +- 包含1条错误数据(用于测试错误处理) | |
| 164 | + | |
| 165 | +**3.2 执行导入** | |
| 166 | +```bash | |
| 167 | +POST /api/Extend/LqCooperationCost/Actions/Import | |
| 168 | +Content-Type: multipart/form-data | |
| 169 | + | |
| 170 | +file: Excel文件 | |
| 171 | +``` | |
| 172 | + | |
| 173 | +**预期结果**: | |
| 174 | +- 成功导入有效数据 | |
| 175 | +- 错误数据被跳过并返回错误信息 | |
| 176 | +- 返回导入结果统计 | |
| 177 | + | |
| 178 | +**3.3 验证导入数据** | |
| 179 | +```bash | |
| 180 | +GET /api/Extend/LqCooperationCost?storeId=门店ID&year=2025&month=202501 | |
| 181 | +``` | |
| 182 | + | |
| 183 | +**预期结果**:查询到导入的数据 | |
| 184 | + | |
| 185 | +--- | |
| 186 | + | |
| 187 | +#### 4. 店内支出表 - Excel导入测试 | |
| 188 | + | |
| 189 | +**4.1 准备Excel文件** | |
| 190 | +- 按照 `Excel导入格式说明.md` 中的格式创建Excel文件 | |
| 191 | +- 包含至少3条测试数据 | |
| 192 | +- 包含1条错误数据(用于测试错误处理) | |
| 193 | + | |
| 194 | +**4.2 执行导入** | |
| 195 | +```bash | |
| 196 | +POST /api/Extend/LqStoreExpense/Actions/Import | |
| 197 | +Content-Type: multipart/form-data | |
| 198 | + | |
| 199 | +file: Excel文件 | |
| 200 | +``` | |
| 201 | + | |
| 202 | +**预期结果**: | |
| 203 | +- 成功导入有效数据 | |
| 204 | +- 错误数据被跳过并返回错误信息 | |
| 205 | +- 返回导入结果统计 | |
| 206 | + | |
| 207 | +**4.3 验证导入数据** | |
| 208 | +```bash | |
| 209 | +GET /api/Extend/LqStoreExpense?storeId=门店ID | |
| 210 | +``` | |
| 211 | + | |
| 212 | +**预期结果**:查询到导入的数据 | |
| 213 | + | |
| 214 | +--- | |
| 215 | + | |
| 216 | +### 第三阶段:Excel导出测试 | |
| 217 | + | |
| 218 | +#### 5. 合作成本表 - Excel导出测试 | |
| 219 | + | |
| 220 | +**5.1 执行导出** | |
| 221 | +```bash | |
| 222 | +GET /api/Extend/LqCooperationCost/Actions/Export?dataType=1&storeId=门店ID&year=2025&month=202501 | |
| 223 | +``` | |
| 224 | + | |
| 225 | +**预期结果**: | |
| 226 | +- 返回Excel文件下载链接 | |
| 227 | +- 下载的文件包含所有查询到的数据 | |
| 228 | +- Excel格式正确 | |
| 229 | + | |
| 230 | +--- | |
| 231 | + | |
| 232 | +#### 6. 店内支出表 - Excel导出测试 | |
| 233 | + | |
| 234 | +**6.1 执行导出** | |
| 235 | +```bash | |
| 236 | +GET /api/Extend/LqStoreExpense/Actions/Export?dataType=1&storeId=门店ID | |
| 237 | +``` | |
| 238 | + | |
| 239 | +**预期结果**: | |
| 240 | +- 返回Excel文件下载链接 | |
| 241 | +- 下载的文件包含所有查询到的数据 | |
| 242 | +- Excel格式正确 | |
| 243 | + | |
| 244 | +--- | |
| 245 | + | |
| 246 | +### 第四阶段:报销表导出测试 | |
| 247 | + | |
| 248 | +#### 7. 报销表导出测试 | |
| 249 | + | |
| 250 | +**7.1 准备测试数据** | |
| 251 | +- 确保有至少1条已审核通过的报销申请(本月) | |
| 252 | +- 该报销申请关联了购买记录 | |
| 253 | + | |
| 254 | +**7.2 执行导出** | |
| 255 | +```bash | |
| 256 | +GET /api/Extend/LqReimbursementApplication/Actions/ExportApprovedDetails?year=2025&month=01 | |
| 257 | +``` | |
| 258 | + | |
| 259 | +**预期结果**: | |
| 260 | +- 返回Excel文件下载链接 | |
| 261 | +- 下载的文件包含: | |
| 262 | + - 报销申请基本信息(申请ID、申请人、门店、申请时间、申请金额) | |
| 263 | + - 关联的购买记录明细(每条购买记录一行) | |
| 264 | +- Excel格式正确 | |
| 265 | + | |
| 266 | +--- | |
| 267 | + | |
| 268 | +## ✅ 测试检查清单 | |
| 269 | + | |
| 270 | +### 合作成本表 | |
| 271 | +- [ ] 创建记录成功 | |
| 272 | +- [ ] 查询列表成功(分页) | |
| 273 | +- [ ] 查询详情成功 | |
| 274 | +- [ ] 更新记录成功 | |
| 275 | +- [ ] 删除记录成功(逻辑删除) | |
| 276 | +- [ ] Excel导入成功(有效数据) | |
| 277 | +- [ ] Excel导入错误处理正确(无效数据被跳过) | |
| 278 | +- [ ] Excel导出成功 | |
| 279 | +- [ ] 导出文件格式正确 | |
| 280 | + | |
| 281 | +### 店内支出表 | |
| 282 | +- [ ] 创建记录成功 | |
| 283 | +- [ ] 查询列表成功(分页) | |
| 284 | +- [ ] 查询详情成功 | |
| 285 | +- [ ] 更新记录成功 | |
| 286 | +- [ ] 删除记录成功(逻辑删除) | |
| 287 | +- [ ] Excel导入成功(有效数据) | |
| 288 | +- [ ] Excel导入错误处理正确(无效数据被跳过) | |
| 289 | +- [ ] Excel导出成功 | |
| 290 | +- [ ] 导出文件格式正确 | |
| 291 | + | |
| 292 | +### 报销表导出 | |
| 293 | +- [ ] 导出本月已审核通过的报销申请 | |
| 294 | +- [ ] 包含购买记录明细 | |
| 295 | +- [ ] 包含门店、金额等信息 | |
| 296 | +- [ ] Excel格式正确 | |
| 297 | + | |
| 298 | +--- | |
| 299 | + | |
| 300 | +## 🐛 常见问题处理 | |
| 301 | + | |
| 302 | +### 1. 导入失败 - 门店ID不存在 | |
| 303 | +**问题**:导入时提示门店ID不存在 | |
| 304 | +**解决**:检查门店ID是否正确,可以通过门店列表接口获取正确的门店ID | |
| 305 | + | |
| 306 | +### 2. 导入失败 - 月份格式错误 | |
| 307 | +**问题**:提示月份格式错误 | |
| 308 | +**解决**:月份必须是YYYYMM格式(6位字符串),如:202501 | |
| 309 | + | |
| 310 | +### 3. 导入失败 - 日期格式错误 | |
| 311 | +**问题**:店内支出表导入时提示日期格式错误 | |
| 312 | +**解决**:日期必须使用标准格式,推荐:YYYY-MM-DD,如:2025-01-15 | |
| 313 | + | |
| 314 | +### 4. 导入失败 - 重复数据 | |
| 315 | +**问题**:合作成本表导入时提示记录已存在 | |
| 316 | +**解决**:系统会检查相同门店、年份、月份是否已存在,如果存在会跳过该条记录 | |
| 317 | + | |
| 318 | +--- | |
| 319 | + | |
| 320 | +## 📝 测试数据准备 | |
| 321 | + | |
| 322 | +### 需要准备的数据 | |
| 323 | + | |
| 324 | +1. **门店ID**:至少2个有效的门店ID(用于测试) | |
| 325 | +2. **Excel文件**: | |
| 326 | + - 合作成本表导入文件(至少3条有效数据 + 1条错误数据) | |
| 327 | + - 店内支出表导入文件(至少3条有效数据 + 1条错误数据) | |
| 328 | +3. **报销申请**:至少1条本月已审核通过的报销申请(关联购买记录) | |
| 329 | + | |
| 330 | +### 获取门店ID的方法 | |
| 331 | + | |
| 332 | +```bash | |
| 333 | +GET /api/Extend/LqMdxx/Selector | |
| 334 | +``` | |
| 335 | + | |
| 336 | +返回结果中包含门店ID和名称。 | |
| 337 | + | |
| 338 | +--- | |
| 339 | + | |
| 340 | +## 🚀 开始测试 | |
| 341 | + | |
| 342 | +请按照以下步骤进行: | |
| 343 | + | |
| 344 | +1. **准备Excel文件**:根据 `Excel导入格式说明.md` 创建Excel文件 | |
| 345 | +2. **填写测试数据**:我会在您创建的Excel文件中填写测试数据 | |
| 346 | +3. **执行测试**:按照测试步骤逐一执行测试 | |
| 347 | +4. **验证结果**:检查每个测试点的预期结果 | |
| 348 | + | |
| 349 | +准备好Excel文件后,请告诉我,我会填写测试数据并开始测试。 | |
| 350 | + | ... | ... |