Commit cea16511630744ea545df953b2f8c29b6efd4faa
Merge branch 'master' of http://39.98.150.180/antissoft/lvqianmeiye_ERP
Showing
56 changed files
with
4601 additions
and
130 deletions
PROJECT_RULES.md
| ... | ... | @@ -143,6 +143,44 @@ Id = Guid.NewGuid().ToString() |
| 143 | 143 | - 必须包含所有可能的HTTP状态码响应说明 |
| 144 | 144 | - 复杂接口必须提供完整的请求示例 |
| 145 | 145 | |
| 146 | +### 接口测试规范 | |
| 147 | +- **必须测试**: 所有新开发的接口或修改的接口都必须进行测试 | |
| 148 | +- **测试要求**: | |
| 149 | + - 使用实际数据测试接口功能 | |
| 150 | + - 验证接口返回数据的正确性 | |
| 151 | + - 测试边界情况和异常情况 | |
| 152 | + - 验证接口性能和响应时间 | |
| 153 | + - 确保接口符合业务逻辑要求 | |
| 154 | +- **测试方式**: 可以使用 curl、Postman、Swagger 等工具进行接口测试 | |
| 155 | +- **测试通过**: 只有测试通过的接口才能提交代码 | |
| 156 | +- **测试token获取**: | |
| 157 | + - **接口地址**: `/api/oauth/Login` | |
| 158 | + - **请求方式**: POST | |
| 159 | + - **Content-Type**: `application/x-www-form-urlencoded` | |
| 160 | + - **请求参数**: | |
| 161 | + - `account`: `admin` | |
| 162 | + - `password`: `e10adc3949ba59abbe56e057f20f883e` | |
| 163 | + - **curl示例**: | |
| 164 | + ```bash | |
| 165 | + curl -X POST "http://localhost:2011/api/oauth/Login" \ | |
| 166 | + -H "Content-Type: application/x-www-form-urlencoded" \ | |
| 167 | + -d "account=admin&password=e10adc3949ba59abbe56e057f20f883e" | |
| 168 | + ``` | |
| 169 | + - **返回格式**: | |
| 170 | + ```json | |
| 171 | + { | |
| 172 | + "code": 200, | |
| 173 | + "msg": "操作成功", | |
| 174 | + "data": { | |
| 175 | + "theme": "functional", | |
| 176 | + "token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", | |
| 177 | + "user": null | |
| 178 | + } | |
| 179 | + } | |
| 180 | + ``` | |
| 181 | + - **token使用**: 返回的token已包含"Bearer "前缀,可直接在请求头中使用:`Authorization: {data.token}` | |
| 182 | + | |
| 183 | + | |
| 146 | 184 | ## 📊 数据一致性规范 |
| 147 | 185 | |
| 148 | 186 | ### 统计与列表数据 | ... | ... |
excel/健康师额外数据模板_测试.xlsx
0 → 100644
No preview for this file type
excel/健康师额外数据模板_测试导入.xlsx
0 → 100644
No preview for this file type
netcore/src/Modularity/Extend/NCC.Extend.Entitys.Dto/LqTechTeacherSalary/TechTeacherStatisticsOutput.cs deleted
| 1 | -using System; | |
| 2 | - | |
| 3 | -namespace NCC.Extend.Entitys.Dto.LqTechTeacherSalary | |
| 4 | -{ | |
| 5 | - /// <summary> | |
| 6 | - /// 科技部老师统计数据输出 | |
| 7 | - /// </summary> | |
| 8 | - public class TechTeacherStatisticsOutput | |
| 9 | - { | |
| 10 | - /// <summary> | |
| 11 | - /// 员工ID | |
| 12 | - /// </summary> | |
| 13 | - public string EmployeeId { get; set; } | |
| 14 | - | |
| 15 | - /// <summary> | |
| 16 | - /// 员工姓名 | |
| 17 | - /// </summary> | |
| 18 | - public string EmployeeName { get; set; } | |
| 19 | - | |
| 20 | - /// <summary> | |
| 21 | - /// 开单业绩 | |
| 22 | - /// </summary> | |
| 23 | - public decimal OrderAchievement { get; set; } | |
| 24 | - | |
| 25 | - /// <summary> | |
| 26 | - /// 消耗业绩 | |
| 27 | - /// </summary> | |
| 28 | - public decimal ConsumeAchievement { get; set; } | |
| 29 | - | |
| 30 | - /// <summary> | |
| 31 | - /// 退卡业绩 | |
| 32 | - /// </summary> | |
| 33 | - public decimal RefundAchievement { get; set; } | |
| 34 | - | |
| 35 | - /// <summary> | |
| 36 | - /// 人头(按月份+客户去重统计) | |
| 37 | - /// </summary> | |
| 38 | - public int PersonCount { get; set; } | |
| 39 | - | |
| 40 | - /// <summary> | |
| 41 | - /// 人次(按日期+客户去重统计) | |
| 42 | - /// </summary> | |
| 43 | - public decimal PersonTimes { get; set; } | |
| 44 | - | |
| 45 | - /// <summary> | |
| 46 | - /// 手工费 | |
| 47 | - /// </summary> | |
| 48 | - public decimal LaborCost { get; set; } | |
| 49 | - } | |
| 50 | -} | |
| 51 | - |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs
| ... | ... | @@ -33,6 +33,11 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsage |
| 33 | 33 | public decimal productPrice { get; set; } |
| 34 | 34 | |
| 35 | 35 | /// <summary> |
| 36 | + /// 产品归属仓库 | |
| 37 | + /// </summary> | |
| 38 | + public string productWarehouse { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 36 | 41 | /// 门店ID |
| 37 | 42 | /// </summary> |
| 38 | 43 | public string storeId { get; set; } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingItemDetailListOutput.cs
| ... | ... | @@ -76,6 +76,16 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb |
| 76 | 76 | /// 门店名称 |
| 77 | 77 | /// </summary> |
| 78 | 78 | public string storeName { get; set; } |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 合作机构 | |
| 82 | + /// </summary> | |
| 83 | + public string hgjg { get; set; } | |
| 84 | + | |
| 85 | + /// <summary> | |
| 86 | + /// 付款医院 | |
| 87 | + /// </summary> | |
| 88 | + public string fkyy { get; set; } | |
| 79 | 89 | } |
| 80 | 90 | } |
| 81 | 91 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowListOutput.cs
| ... | ... | @@ -109,6 +109,18 @@ namespace NCC.Extend.Entitys.Dto.LqLaundryFlow |
| 109 | 109 | /// </summary> |
| 110 | 110 | [Display(Name = "创建时间")] |
| 111 | 111 | public DateTime createTime { get; set; } |
| 112 | + | |
| 113 | + /// <summary> | |
| 114 | + /// 送出时间(流水类型为0时使用) | |
| 115 | + /// </summary> | |
| 116 | + [Display(Name = "送出时间")] | |
| 117 | + public DateTime? sendTime { get; set; } | |
| 118 | + | |
| 119 | + /// <summary> | |
| 120 | + /// 送回时间(流水类型为1时使用) | |
| 121 | + /// </summary> | |
| 122 | + [Display(Name = "送回时间")] | |
| 123 | + public DateTime? returnTime { get; set; } | |
| 112 | 124 | } |
| 113 | 125 | } |
| 114 | 126 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqLaundryFlow/LqLaundryFlowUpdateInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.ComponentModel.DataAnnotations; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.LqLaundryFlow | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 清洗流水修改输入 | |
| 8 | + /// </summary> | |
| 9 | + public class LqLaundryFlowUpdateInput | |
| 10 | + { | |
| 11 | + /// <summary> | |
| 12 | + /// 记录ID | |
| 13 | + /// </summary> | |
| 14 | + [Required(ErrorMessage = "记录ID不能为空")] | |
| 15 | + [StringLength(50, ErrorMessage = "记录ID长度不能超过50个字符")] | |
| 16 | + [Display(Name = "记录ID")] | |
| 17 | + public string Id { get; set; } | |
| 18 | + | |
| 19 | + /// <summary> | |
| 20 | + /// 数量 | |
| 21 | + /// </summary> | |
| 22 | + [Range(0, int.MaxValue, ErrorMessage = "数量不能小于0")] | |
| 23 | + [Display(Name = "数量")] | |
| 24 | + public int? Quantity { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 送出时间(流水类型为0时使用) | |
| 28 | + /// </summary> | |
| 29 | + [Display(Name = "送出时间")] | |
| 30 | + public DateTime? SendTime { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 送回时间(流水类型为1时使用) | |
| 34 | + /// </summary> | |
| 35 | + [Display(Name = "送回时间")] | |
| 36 | + public DateTime? ReturnTime { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 备注 | |
| 40 | + /// </summary> | |
| 41 | + [StringLength(1000, ErrorMessage = "备注长度不能超过1000个字符")] | |
| 42 | + [Display(Name = "备注")] | |
| 43 | + public string Remark { get; set; } | |
| 44 | + } | |
| 45 | +} | |
| 46 | + | |
| 47 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqShareStatisticsHq/ShareStatisticsHqGenerateInput.cs
0 → 100644
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqShareStatisticsHq/ShareStatisticsHqOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqShareStatisticsHq | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 总部股份统计输出 | |
| 7 | + /// </summary> | |
| 8 | + public class ShareStatisticsHqOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 主键ID | |
| 12 | + /// </summary> | |
| 13 | + public string Id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 统计月份 | |
| 17 | + /// </summary> | |
| 18 | + public string StatisticsMonth { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 收入-全部 | |
| 22 | + /// </summary> | |
| 23 | + public decimal IncomeGeneral { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 收入-科技部 | |
| 27 | + /// </summary> | |
| 28 | + public decimal IncomeTechDept { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 成本-报销 | |
| 32 | + /// </summary> | |
| 33 | + public decimal CostReimbursement { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 成本-人工 | |
| 37 | + /// </summary> | |
| 38 | + public decimal CostLabor { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 成本-教育部房租 | |
| 42 | + /// </summary> | |
| 43 | + public decimal CostEducationRent { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 成本-仓库房租 | |
| 47 | + /// </summary> | |
| 48 | + public decimal CostWarehouseRent { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 成本-总部房租 | |
| 52 | + /// </summary> | |
| 53 | + public decimal CostHQRent { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 运营利润 | |
| 57 | + /// </summary> | |
| 58 | + public decimal OperationalProfit { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 创建时间 | |
| 62 | + /// </summary> | |
| 63 | + public DateTime CreateTime { get; set; } | |
| 64 | + | |
| 65 | + /// <summary> | |
| 66 | + /// 更新时间 | |
| 67 | + /// </summary> | |
| 68 | + public DateTime? UpdateTime { get; set; } | |
| 69 | + } | |
| 70 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqShareStatisticsHq/ShareStatisticsHqQueryInput.cs
0 → 100644
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqShareStatisticsStore/ShareStatisticsStoreGenerateInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqShareStatisticsStore | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 门店股份统计生成输入 | |
| 7 | + /// </summary> | |
| 8 | + public class ShareStatisticsStoreGenerateInput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 统计月份(YYYYMM) | |
| 12 | + /// </summary> | |
| 13 | + public string StatisticsMonth { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 门店ID(可选,为空则生成所有门店) | |
| 17 | + /// </summary> | |
| 18 | + public string StoreId { get; set; } | |
| 19 | + } | |
| 20 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqShareStatisticsStore/ShareStatisticsStoreOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqShareStatisticsStore | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 门店股份统计输出 | |
| 7 | + /// </summary> | |
| 8 | + public class ShareStatisticsStoreOutput | |
| 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 | + /// 统计月份(YYYYMM) | |
| 27 | + /// </summary> | |
| 28 | + public string StatisticsMonth { get; set; } | |
| 29 | + | |
| 30 | + // 收入部分 | |
| 31 | + /// <summary> | |
| 32 | + /// 主营收入(消耗总业绩, 不扣减退款) | |
| 33 | + /// </summary> | |
| 34 | + public decimal MainIncome { get; set; } | |
| 35 | + | |
| 36 | + /// <summary> | |
| 37 | + /// 生美消耗业绩 | |
| 38 | + /// </summary> | |
| 39 | + public decimal ConsumeLifeBeauty { get; set; } | |
| 40 | + | |
| 41 | + /// <summary> | |
| 42 | + /// 科美消耗业绩 | |
| 43 | + /// </summary> | |
| 44 | + public decimal ConsumeTechBeauty { get; set; } | |
| 45 | + | |
| 46 | + /// <summary> | |
| 47 | + /// 医美消耗业绩 | |
| 48 | + /// </summary> | |
| 49 | + public decimal ConsumeMedicalBeauty { get; set; } | |
| 50 | + | |
| 51 | + /// <summary> | |
| 52 | + /// 合作消费业绩 | |
| 53 | + /// </summary> | |
| 54 | + public decimal ConsumeCooperation { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 产品消耗业绩 | |
| 58 | + /// </summary> | |
| 59 | + public decimal ConsumeProduct { get; set; } | |
| 60 | + | |
| 61 | + /// <summary> | |
| 62 | + /// 其他消耗业绩 | |
| 63 | + /// </summary> | |
| 64 | + public decimal ConsumeOther { get; set; } | |
| 65 | + | |
| 66 | + /// <summary> | |
| 67 | + /// 其他收入(退款差额>0) | |
| 68 | + /// </summary> | |
| 69 | + public decimal OtherIncome { get; set; } | |
| 70 | + | |
| 71 | + /// <summary> | |
| 72 | + /// 预收款(开单实付) | |
| 73 | + /// </summary> | |
| 74 | + public decimal AdvanceReceipt { get; set; } | |
| 75 | + | |
| 76 | + /// <summary> | |
| 77 | + /// 实际退款(实退业绩) | |
| 78 | + /// </summary> | |
| 79 | + public decimal Refund { get; set; } | |
| 80 | + | |
| 81 | + /// <summary> | |
| 82 | + /// 应收(合作医院) | |
| 83 | + /// </summary> | |
| 84 | + public decimal Receivables { get; set; } | |
| 85 | + | |
| 86 | + /// <summary> | |
| 87 | + /// 银行存款(开单实付-应收) | |
| 88 | + /// </summary> | |
| 89 | + public decimal BankDeposit { get; set; } | |
| 90 | + | |
| 91 | + // 成本部分 | |
| 92 | + /// <summary> | |
| 93 | + /// 主营成本-产品(仓库领取) | |
| 94 | + /// </summary> | |
| 95 | + public decimal CostProduct { get; set; } | |
| 96 | + | |
| 97 | + /// <summary> | |
| 98 | + /// 主营成本-福田(福田仓库领取) | |
| 99 | + /// </summary> | |
| 100 | + public decimal CostFutian { get; set; } | |
| 101 | + | |
| 102 | + /// <summary> | |
| 103 | + /// 主营成本-毛巾(清洗送出) | |
| 104 | + /// </summary> | |
| 105 | + public decimal CostTowel { get; set; } | |
| 106 | + | |
| 107 | + /// <summary> | |
| 108 | + /// 主营成本-科技部(科美业绩30%) | |
| 109 | + /// </summary> | |
| 110 | + public decimal CostTechDept { get; set; } | |
| 111 | + | |
| 112 | + /// <summary> | |
| 113 | + /// 主营成本-管理费(总业绩9%) | |
| 114 | + /// </summary> | |
| 115 | + public decimal CostManagementFee { get; set; } | |
| 116 | + | |
| 117 | + /// <summary> | |
| 118 | + /// 主营成本-合作(保留) | |
| 119 | + /// </summary> | |
| 120 | + public decimal CostCooperation { get; set; } | |
| 121 | + | |
| 122 | + /// <summary> | |
| 123 | + /// 主营成本-大项目(保留) | |
| 124 | + /// </summary> | |
| 125 | + public decimal CostMajorProject { get; set; } | |
| 126 | + | |
| 127 | + /// <summary> | |
| 128 | + /// 其他成本(退款差额<0) | |
| 129 | + /// </summary> | |
| 130 | + public decimal CostOther { get; set; } | |
| 131 | + | |
| 132 | + // 人工工资部分 | |
| 133 | + /// <summary> | |
| 134 | + /// 人工工资-健康师底薪 | |
| 135 | + /// </summary> | |
| 136 | + public decimal SalaryBaseHealthCoach { get; set; } | |
| 137 | + | |
| 138 | + /// <summary> | |
| 139 | + /// 人工工资-店助底薪 | |
| 140 | + /// </summary> | |
| 141 | + public decimal SalaryBaseAssistant { get; set; } | |
| 142 | + | |
| 143 | + /// <summary> | |
| 144 | + /// 人工工资-店助主任底薪 | |
| 145 | + /// </summary> | |
| 146 | + public decimal SalaryBaseDirector { get; set; } | |
| 147 | + | |
| 148 | + /// <summary> | |
| 149 | + /// 人工工资-店长底薪 | |
| 150 | + /// </summary> | |
| 151 | + public decimal SalaryBaseStoreManager { get; set; } | |
| 152 | + | |
| 153 | + /// <summary> | |
| 154 | + /// 人工工资-总经理/经理底薪 | |
| 155 | + /// </summary> | |
| 156 | + public decimal SalaryBaseGeneralManager { get; set; } | |
| 157 | + | |
| 158 | + /// <summary> | |
| 159 | + /// 人工工资-健康师提成 | |
| 160 | + /// </summary> | |
| 161 | + public decimal SalaryCommissionHealthCoach { get; set; } | |
| 162 | + | |
| 163 | + /// <summary> | |
| 164 | + /// 人工工资-店助提成 | |
| 165 | + /// </summary> | |
| 166 | + public decimal SalaryCommissionAssistant { get; set; } | |
| 167 | + | |
| 168 | + /// <summary> | |
| 169 | + /// 人工工资-店助主任提成 | |
| 170 | + /// </summary> | |
| 171 | + public decimal SalaryCommissionDirector { get; set; } | |
| 172 | + | |
| 173 | + /// <summary> | |
| 174 | + /// 人工工资-店长提成 | |
| 175 | + /// </summary> | |
| 176 | + public decimal SalaryCommissionStoreManager { get; set; } | |
| 177 | + | |
| 178 | + /// <summary> | |
| 179 | + /// 人工工资-总经理/经理提成 | |
| 180 | + /// </summary> | |
| 181 | + public decimal SalaryCommissionGeneralManager { get; set; } | |
| 182 | + | |
| 183 | + /// <summary> | |
| 184 | + /// 人工工资-手工 | |
| 185 | + /// </summary> | |
| 186 | + public decimal SalaryManual { get; set; } | |
| 187 | + | |
| 188 | + /// <summary> | |
| 189 | + /// 人工工资-出勤(保留) | |
| 190 | + /// </summary> | |
| 191 | + public decimal SalaryAttendance { get; set; } | |
| 192 | + | |
| 193 | + /// <summary> | |
| 194 | + /// 人工工资-岗位-手机保管费 | |
| 195 | + /// </summary> | |
| 196 | + public decimal SalaryPhoneCustody { get; set; } | |
| 197 | + | |
| 198 | + /// <summary> | |
| 199 | + /// 人工工资-岗位-人头 | |
| 200 | + /// </summary> | |
| 201 | + public decimal SalaryHeadcountReward { get; set; } | |
| 202 | + | |
| 203 | + /// <summary> | |
| 204 | + /// 人工工资-门店T区 | |
| 205 | + /// </summary> | |
| 206 | + public decimal SalaryTZone { get; set; } | |
| 207 | + | |
| 208 | + // 费用与其他 | |
| 209 | + /// <summary> | |
| 210 | + /// 社保(保留) | |
| 211 | + /// </summary> | |
| 212 | + public decimal SocialSecurity { get; set; } | |
| 213 | + | |
| 214 | + /// <summary> | |
| 215 | + /// 门店房租 | |
| 216 | + /// </summary> | |
| 217 | + public decimal StoreRent { get; set; } | |
| 218 | + | |
| 219 | + /// <summary> | |
| 220 | + /// 宿舍房租(保留) | |
| 221 | + /// </summary> | |
| 222 | + public decimal DormRent { get; set; } | |
| 223 | + | |
| 224 | + /// <summary> | |
| 225 | + /// 当期费用(报销) | |
| 226 | + /// </summary> | |
| 227 | + public decimal CurrentPeriodExpense { get; set; } | |
| 228 | + | |
| 229 | + /// <summary> | |
| 230 | + /// 当前费用-秦董(保留) | |
| 231 | + /// </summary> | |
| 232 | + public decimal CurrentPeriodExpenseQin { get; set; } | |
| 233 | + | |
| 234 | + /// <summary> | |
| 235 | + /// 财务费用(保留) | |
| 236 | + /// </summary> | |
| 237 | + public decimal FinancialFee { get; set; } | |
| 238 | + | |
| 239 | + // 奖励 | |
| 240 | + /// <summary> | |
| 241 | + /// 奖励-医美(保留) | |
| 242 | + /// </summary> | |
| 243 | + public decimal RewardMedicalBeauty { get; set; } | |
| 244 | + | |
| 245 | + /// <summary> | |
| 246 | + /// 奖励-其他(保留) | |
| 247 | + /// </summary> | |
| 248 | + public decimal RewardOther { get; set; } | |
| 249 | + | |
| 250 | + /// <summary> | |
| 251 | + /// 奖励-大单奖(保留) | |
| 252 | + /// </summary> | |
| 253 | + public decimal RewardLargeOrder { get; set; } | |
| 254 | + | |
| 255 | + /// <summary> | |
| 256 | + /// 奖励-首单奖(保留) | |
| 257 | + /// </summary> | |
| 258 | + public decimal RewardFirstOrder { get; set; } | |
| 259 | + | |
| 260 | + /// <summary> | |
| 261 | + /// 奖励(保留) | |
| 262 | + /// </summary> | |
| 263 | + public decimal RewardGeneral { get; set; } | |
| 264 | + | |
| 265 | + // 其他保留字段 | |
| 266 | + /// <summary> | |
| 267 | + /// 其他1(保留) | |
| 268 | + /// </summary> | |
| 269 | + public decimal Other1 { get; set; } | |
| 270 | + | |
| 271 | + /// <summary> | |
| 272 | + /// 其他2(保留) | |
| 273 | + /// </summary> | |
| 274 | + public decimal Other2 { get; set; } | |
| 275 | + | |
| 276 | + // 利润结果 | |
| 277 | + /// <summary> | |
| 278 | + /// 利润(预收-实退-主营成本-人工工资[分项汇总]-房租-费用-奖励-其他) | |
| 279 | + /// </summary> | |
| 280 | + public decimal Profit { get; set; } | |
| 281 | + | |
| 282 | + /// <summary> | |
| 283 | + /// 财务利润(主营-其他收入-主营成本-人工工资[分项汇总]-房租-费用-奖励-其他) | |
| 284 | + /// </summary> | |
| 285 | + public decimal FinancialProfit { get; set; } | |
| 286 | + | |
| 287 | + // 基础字段 | |
| 288 | + /// <summary> | |
| 289 | + /// 创建时间 | |
| 290 | + /// </summary> | |
| 291 | + public DateTime? CreateTime { get; set; } | |
| 292 | + | |
| 293 | + /// <summary> | |
| 294 | + /// 更新时间 | |
| 295 | + /// </summary> | |
| 296 | + public DateTime? UpdateTime { get; set; } | |
| 297 | + } | |
| 298 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqShareStatisticsStore/ShareStatisticsStoreQueryInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqShareStatisticsStore | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 门店股份统计查询输入 | |
| 7 | + /// </summary> | |
| 8 | + public class ShareStatisticsStoreQueryInput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 统计月份(YYYYMM) | |
| 12 | + /// </summary> | |
| 13 | + public string StatisticsMonth { 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 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqShareStatisticsTechDept/ShareStatisticsTechDeptGenerateInput.cs
0 → 100644
| 1 | +namespace NCC.Extend.Entitys.Dto.LqShareStatisticsTechDept | |
| 2 | +{ | |
| 3 | + /// <summary> | |
| 4 | + /// 科技部股份统计生成输入 | |
| 5 | + /// </summary> | |
| 6 | + public class ShareStatisticsTechDeptGenerateInput | |
| 7 | + { | |
| 8 | + /// <summary> | |
| 9 | + /// 统计月份(YYYYMM) | |
| 10 | + /// </summary> | |
| 11 | + public string StatisticsMonth { get; set; } | |
| 12 | + | |
| 13 | + /// <summary> | |
| 14 | + /// 部门名称(科技一部/科技二部,为空则生成两个部门) | |
| 15 | + /// </summary> | |
| 16 | + public string DepartmentName { get; set; } | |
| 17 | + } | |
| 18 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqShareStatisticsTechDept/ShareStatisticsTechDeptOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqShareStatisticsTechDept | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 科技部股份统计输出 | |
| 7 | + /// </summary> | |
| 8 | + public class ShareStatisticsTechDeptOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 主键ID | |
| 12 | + /// </summary> | |
| 13 | + public string Id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 部门名称 | |
| 17 | + /// </summary> | |
| 18 | + public string DepartmentName { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 统计月份 | |
| 22 | + /// </summary> | |
| 23 | + public string StatisticsMonth { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 收入 | |
| 27 | + /// </summary> | |
| 28 | + public decimal Income { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 成本-报销 | |
| 32 | + /// </summary> | |
| 33 | + public decimal CostReimbursement { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 成本-人工-老师底薪 | |
| 37 | + /// </summary> | |
| 38 | + public decimal CostTeacherBase { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 成本-人工-手工费 | |
| 42 | + /// </summary> | |
| 43 | + public decimal CostTeacherManual { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 成本-人工-开单提成 | |
| 47 | + /// </summary> | |
| 48 | + public decimal CostTeacherBillingComm { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 成本-人工-消耗提成 | |
| 52 | + /// </summary> | |
| 53 | + public decimal CostTeacherConsumeComm { get; set; } | |
| 54 | + | |
| 55 | + /// <summary> | |
| 56 | + /// 成本-人工-专家提成 | |
| 57 | + /// </summary> | |
| 58 | + public decimal CostTeacherExpertComm { get; set; } | |
| 59 | + | |
| 60 | + /// <summary> | |
| 61 | + /// 成本-人工-加班 | |
| 62 | + /// </summary> | |
| 63 | + public decimal CostTeacherOvertime { get; set; } | |
| 64 | + | |
| 65 | + /// <summary> | |
| 66 | + /// 成本-人工-总经理底薪 | |
| 67 | + /// </summary> | |
| 68 | + public decimal CostGMBase { get; set; } | |
| 69 | + | |
| 70 | + /// <summary> | |
| 71 | + /// 成本-人工-总经理提成 | |
| 72 | + /// </summary> | |
| 73 | + public decimal CostGMComm { get; set; } | |
| 74 | + | |
| 75 | + /// <summary> | |
| 76 | + /// 奖励-科技部 | |
| 77 | + /// </summary> | |
| 78 | + public decimal RewardTechDept { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 成本-其他1 | |
| 82 | + /// </summary> | |
| 83 | + public decimal CostOther1 { get; set; } | |
| 84 | + | |
| 85 | + /// <summary> | |
| 86 | + /// 成本-其他2 | |
| 87 | + /// </summary> | |
| 88 | + public decimal CostOther2 { get; set; } | |
| 89 | + | |
| 90 | + /// <summary> | |
| 91 | + /// 利润 | |
| 92 | + /// </summary> | |
| 93 | + public decimal Profit { get; set; } | |
| 94 | + | |
| 95 | + /// <summary> | |
| 96 | + /// 创建时间 | |
| 97 | + /// </summary> | |
| 98 | + public DateTime CreateTime { get; set; } | |
| 99 | + | |
| 100 | + /// <summary> | |
| 101 | + /// 更新时间 | |
| 102 | + /// </summary> | |
| 103 | + public DateTime? UpdateTime { get; set; } | |
| 104 | + } | |
| 105 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqShareStatisticsTechDept/ShareStatisticsTechDeptQueryInput.cs
0 → 100644
| 1 | +namespace NCC.Extend.Entitys.Dto.LqShareStatisticsTechDept | |
| 2 | +{ | |
| 3 | + /// <summary> | |
| 4 | + /// 科技部股份统计查询输入 | |
| 5 | + /// </summary> | |
| 6 | + public class ShareStatisticsTechDeptQueryInput | |
| 7 | + { | |
| 8 | + /// <summary> | |
| 9 | + /// 统计月份(YYYYMM) | |
| 10 | + /// </summary> | |
| 11 | + public string StatisticsMonth { get; set; } | |
| 12 | + | |
| 13 | + /// <summary> | |
| 14 | + /// 部门名称 | |
| 15 | + /// </summary> | |
| 16 | + public string DepartmentName { get; set; } | |
| 17 | + } | |
| 18 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_contract_monthly_cost/LqContractMonthlyCostEntity.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using NCC.Common.Const; | |
| 3 | +using SqlSugar; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.lq_contract_monthly_cost | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 合同成本按月统计表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("lq_contract_monthly_cost")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class LqContractMonthlyCostEntity | |
| 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_contract.F_Id) | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "F_ContractId")] | |
| 24 | + public string ContractId { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 门店ID(关联lq_mdxx.F_Id,冗余字段便于查询) | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "F_StoreId")] | |
| 30 | + public string StoreId { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 店名(冗余字段,便于查询) | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "F_StoreName")] | |
| 36 | + public string StoreName { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 分类(冗余字段,便于按分类统计) | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "F_Category")] | |
| 42 | + public string Category { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 统计月份(格式:YYYY-MM-01,表示该月的第一天) | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "F_Month")] | |
| 48 | + public DateTime Month { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 该月的合同成本(缴租金额 / 交租周期) | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "F_MonthlyCost", DecimalDigits = 2)] | |
| 54 | + public decimal MonthlyCost { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 交租周期(冗余字段,便于查询) | |
| 58 | + /// </summary> | |
| 59 | + [SugarColumn(ColumnName = "F_PaymentCycle")] | |
| 60 | + public int PaymentCycle { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 缴租金额(冗余字段,便于查询) | |
| 64 | + /// </summary> | |
| 65 | + [SugarColumn(ColumnName = "F_PaymentAmount", DecimalDigits = 2)] | |
| 66 | + public decimal PaymentAmount { get; set; } | |
| 67 | + | |
| 68 | + /// <summary> | |
| 69 | + /// 是否有效(1-有效,0-无效) | |
| 70 | + /// </summary> | |
| 71 | + [SugarColumn(ColumnName = "F_IsEffective")] | |
| 72 | + public int IsEffective { get; set; } = 1; | |
| 73 | + | |
| 74 | + /// <summary> | |
| 75 | + /// 创建人ID | |
| 76 | + /// </summary> | |
| 77 | + [SugarColumn(ColumnName = "F_CreateUser")] | |
| 78 | + public string CreateUser { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 创建时间 | |
| 82 | + /// </summary> | |
| 83 | + [SugarColumn(ColumnName = "F_CreateTime")] | |
| 84 | + public DateTime CreateTime { get; set; } | |
| 85 | + | |
| 86 | + /// <summary> | |
| 87 | + /// 更新时间 | |
| 88 | + /// </summary> | |
| 89 | + [SugarColumn(ColumnName = "F_UpdateTime")] | |
| 90 | + public DateTime? UpdateTime { get; set; } | |
| 91 | + } | |
| 92 | +} | |
| 93 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_deductinfo/LqKdDeductinfoEntity.cs
| ... | ... | @@ -36,6 +36,12 @@ namespace NCC.Extend.Entitys.lq_kd_deductinfo |
| 36 | 36 | public string BillingId { get; set; } |
| 37 | 37 | |
| 38 | 38 | /// <summary> |
| 39 | + /// 开单时间(来源:lq_kd_kdjlb.kdrq) | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "F_BillingTime")] | |
| 42 | + public DateTime? BillingTime { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 39 | 45 | /// 合计金额 |
| 40 | 46 | /// </summary> |
| 41 | 47 | [SugarColumn(ColumnName = "F_Amount")] | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_laundry_flow/LqLaundryFlowEntity.cs
| ... | ... | @@ -88,6 +88,18 @@ namespace NCC.Extend.Entitys.lq_laundry_flow |
| 88 | 88 | /// </summary> |
| 89 | 89 | [SugarColumn(ColumnName = "F_CreateTime")] |
| 90 | 90 | public DateTime CreateTime { get; set; } |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 送出时间(流水类型为0时使用) | |
| 94 | + /// </summary> | |
| 95 | + [SugarColumn(ColumnName = "F_SendTime")] | |
| 96 | + public DateTime? SendTime { get; set; } | |
| 97 | + | |
| 98 | + /// <summary> | |
| 99 | + /// 送回时间(流水类型为1时使用) | |
| 100 | + /// </summary> | |
| 101 | + [SugarColumn(ColumnName = "F_ReturnTime")] | |
| 102 | + public DateTime? ReturnTime { get; set; } | |
| 91 | 103 | } |
| 92 | 104 | } |
| 93 | 105 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_share_statistics_hq/LqShareStatisticsHqEntity.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using NCC.Common.Const; | |
| 3 | +using SqlSugar; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.lq_share_statistics_hq | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 总部股份统计表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("lq_share_statistics_hq")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class LqShareStatisticsHqEntity | |
| 13 | + { | |
| 14 | + /// <summary> | |
| 15 | + /// 主键ID | |
| 16 | + /// </summary> | |
| 17 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 18 | + public string Id { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 统计月份(YYYYMM) | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "F_StatisticsMonth")] | |
| 24 | + public string StatisticsMonth { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 收入-全部(开单业绩9%) | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "F_IncomeGeneral")] | |
| 30 | + public decimal IncomeGeneral { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 收入-科技部(科美30%的9%) | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "F_IncomeTechDept")] | |
| 36 | + public decimal IncomeTechDept { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 成本-报销(总部费用) | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "F_CostReimbursement")] | |
| 42 | + public decimal CostReimbursement { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 成本-人工(保留) | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "F_CostLabor")] | |
| 48 | + public decimal CostLabor { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 成本-教育部房租 | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "F_CostEducationRent")] | |
| 54 | + public decimal CostEducationRent { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 成本-仓库房租 | |
| 58 | + /// </summary> | |
| 59 | + [SugarColumn(ColumnName = "F_CostWarehouseRent")] | |
| 60 | + public decimal CostWarehouseRent { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 成本-总部房租 | |
| 64 | + /// </summary> | |
| 65 | + [SugarColumn(ColumnName = "F_CostHQRent")] | |
| 66 | + public decimal CostHQRent { get; set; } | |
| 67 | + | |
| 68 | + /// <summary> | |
| 69 | + /// 总部运营利润 | |
| 70 | + /// </summary> | |
| 71 | + [SugarColumn(ColumnName = "F_OperationalProfit")] | |
| 72 | + public decimal OperationalProfit { get; set; } | |
| 73 | + | |
| 74 | + /// <summary> | |
| 75 | + /// 创建时间 | |
| 76 | + /// </summary> | |
| 77 | + [SugarColumn(ColumnName = "F_CreateTime")] | |
| 78 | + public DateTime CreateTime { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 更新时间 | |
| 82 | + /// </summary> | |
| 83 | + [SugarColumn(ColumnName = "F_UpdateTime")] | |
| 84 | + public DateTime? UpdateTime { get; set; } | |
| 85 | + | |
| 86 | + /// <summary> | |
| 87 | + /// 创建人 | |
| 88 | + /// </summary> | |
| 89 | + [SugarColumn(ColumnName = "F_CreateUser")] | |
| 90 | + public string CreateUser { get; set; } | |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 更新人 | |
| 94 | + /// </summary> | |
| 95 | + [SugarColumn(ColumnName = "F_UpdateUser")] | |
| 96 | + public string UpdateUser { get; set; } | |
| 97 | + | |
| 98 | + /// <summary> | |
| 99 | + /// 是否有效(1:有效 0:无效) | |
| 100 | + /// </summary> | |
| 101 | + [SugarColumn(ColumnName = "F_IsEffective")] | |
| 102 | + public int IsEffective { get; set; } = 1; | |
| 103 | + } | |
| 104 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_share_statistics_store/LqShareStatisticsStoreEntity.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using NCC.Common.Const; | |
| 3 | +using SqlSugar; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.lq_share_statistics_store | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 门店股份统计表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("lq_share_statistics_store")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class LqShareStatisticsStoreEntity | |
| 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 | + /// 统计月份(YYYYMM) | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "F_StatisticsMonth")] | |
| 36 | + public string StatisticsMonth { get; set; } | |
| 37 | + | |
| 38 | + // 收入部分 | |
| 39 | + /// <summary> | |
| 40 | + /// 主营收入(消耗总业绩, 不扣减退款) | |
| 41 | + /// </summary> | |
| 42 | + [SugarColumn(ColumnName = "F_MainIncome")] | |
| 43 | + public decimal MainIncome { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 46 | + /// 生美消耗业绩 | |
| 47 | + /// </summary> | |
| 48 | + [SugarColumn(ColumnName = "F_ConsumeLifeBeauty")] | |
| 49 | + public decimal ConsumeLifeBeauty { get; set; } | |
| 50 | + | |
| 51 | + /// <summary> | |
| 52 | + /// 科美消耗业绩 | |
| 53 | + /// </summary> | |
| 54 | + [SugarColumn(ColumnName = "F_ConsumeTechBeauty")] | |
| 55 | + public decimal ConsumeTechBeauty { get; set; } | |
| 56 | + | |
| 57 | + /// <summary> | |
| 58 | + /// 医美消耗业绩 | |
| 59 | + /// </summary> | |
| 60 | + [SugarColumn(ColumnName = "F_ConsumeMedicalBeauty")] | |
| 61 | + public decimal ConsumeMedicalBeauty { get; set; } | |
| 62 | + | |
| 63 | + /// <summary> | |
| 64 | + /// 合作消费业绩 | |
| 65 | + /// </summary> | |
| 66 | + [SugarColumn(ColumnName = "F_ConsumeCooperation")] | |
| 67 | + public decimal ConsumeCooperation { get; set; } | |
| 68 | + | |
| 69 | + /// <summary> | |
| 70 | + /// 产品消耗业绩 | |
| 71 | + /// </summary> | |
| 72 | + [SugarColumn(ColumnName = "F_ConsumeProduct")] | |
| 73 | + public decimal ConsumeProduct { get; set; } | |
| 74 | + | |
| 75 | + /// <summary> | |
| 76 | + /// 其他消耗业绩 | |
| 77 | + /// </summary> | |
| 78 | + [SugarColumn(ColumnName = "F_ConsumeOther")] | |
| 79 | + public decimal ConsumeOther { get; set; } | |
| 80 | + | |
| 81 | + /// <summary> | |
| 82 | + /// 其他收入(退款差额>0) | |
| 83 | + /// </summary> | |
| 84 | + [SugarColumn(ColumnName = "F_OtherIncome")] | |
| 85 | + public decimal OtherIncome { get; set; } | |
| 86 | + | |
| 87 | + /// <summary> | |
| 88 | + /// 预收款(开单实付) | |
| 89 | + /// </summary> | |
| 90 | + [SugarColumn(ColumnName = "F_AdvanceReceipt")] | |
| 91 | + public decimal AdvanceReceipt { get; set; } | |
| 92 | + | |
| 93 | + /// <summary> | |
| 94 | + /// 实际退款(实退业绩) | |
| 95 | + /// </summary> | |
| 96 | + [SugarColumn(ColumnName = "F_Refund")] | |
| 97 | + public decimal Refund { get; set; } | |
| 98 | + | |
| 99 | + /// <summary> | |
| 100 | + /// 应收(合作医院) | |
| 101 | + /// </summary> | |
| 102 | + [SugarColumn(ColumnName = "F_Receivables")] | |
| 103 | + public decimal Receivables { get; set; } | |
| 104 | + | |
| 105 | + /// <summary> | |
| 106 | + /// 银行存款(开单实付-应收) | |
| 107 | + /// </summary> | |
| 108 | + [SugarColumn(ColumnName = "F_BankDeposit")] | |
| 109 | + public decimal BankDeposit { get; set; } | |
| 110 | + | |
| 111 | + // 成本部分 | |
| 112 | + /// <summary> | |
| 113 | + /// 主营成本-产品(仓库领取) | |
| 114 | + /// </summary> | |
| 115 | + [SugarColumn(ColumnName = "F_CostProduct")] | |
| 116 | + public decimal CostProduct { get; set; } | |
| 117 | + | |
| 118 | + /// <summary> | |
| 119 | + /// 主营成本-福田(福田仓库领取) | |
| 120 | + /// </summary> | |
| 121 | + [SugarColumn(ColumnName = "F_CostFutian")] | |
| 122 | + public decimal CostFutian { get; set; } | |
| 123 | + | |
| 124 | + /// <summary> | |
| 125 | + /// 主营成本-毛巾(清洗送出) | |
| 126 | + /// </summary> | |
| 127 | + [SugarColumn(ColumnName = "F_CostTowel")] | |
| 128 | + public decimal CostTowel { get; set; } | |
| 129 | + | |
| 130 | + /// <summary> | |
| 131 | + /// 主营成本-科技部(科美业绩30%) | |
| 132 | + /// </summary> | |
| 133 | + [SugarColumn(ColumnName = "F_CostTechDept")] | |
| 134 | + public decimal CostTechDept { get; set; } | |
| 135 | + | |
| 136 | + /// <summary> | |
| 137 | + /// 主营成本-管理费(总业绩9%) | |
| 138 | + /// </summary> | |
| 139 | + [SugarColumn(ColumnName = "F_CostManagementFee")] | |
| 140 | + public decimal CostManagementFee { get; set; } | |
| 141 | + | |
| 142 | + /// <summary> | |
| 143 | + /// 主营成本-合作(保留) | |
| 144 | + /// </summary> | |
| 145 | + [SugarColumn(ColumnName = "F_CostCooperation")] | |
| 146 | + public decimal CostCooperation { get; set; } | |
| 147 | + | |
| 148 | + /// <summary> | |
| 149 | + /// 主营成本-大项目(保留) | |
| 150 | + /// </summary> | |
| 151 | + [SugarColumn(ColumnName = "F_CostMajorProject")] | |
| 152 | + public decimal CostMajorProject { get; set; } | |
| 153 | + | |
| 154 | + /// <summary> | |
| 155 | + /// 其他成本(退款差额<0) | |
| 156 | + /// </summary> | |
| 157 | + [SugarColumn(ColumnName = "F_CostOther")] | |
| 158 | + public decimal CostOther { get; set; } | |
| 159 | + | |
| 160 | + // 人工工资部分 | |
| 161 | + /// <summary> | |
| 162 | + /// 人工工资-健康师底薪 | |
| 163 | + /// </summary> | |
| 164 | + [SugarColumn(ColumnName = "F_SalaryBaseHealthCoach")] | |
| 165 | + public decimal SalaryBaseHealthCoach { get; set; } | |
| 166 | + | |
| 167 | + /// <summary> | |
| 168 | + /// 人工工资-店助底薪 | |
| 169 | + /// </summary> | |
| 170 | + [SugarColumn(ColumnName = "F_SalaryBaseAssistant")] | |
| 171 | + public decimal SalaryBaseAssistant { get; set; } | |
| 172 | + | |
| 173 | + /// <summary> | |
| 174 | + /// 人工工资-店助主任底薪 | |
| 175 | + /// </summary> | |
| 176 | + [SugarColumn(ColumnName = "F_SalaryBaseDirector")] | |
| 177 | + public decimal SalaryBaseDirector { get; set; } | |
| 178 | + | |
| 179 | + /// <summary> | |
| 180 | + /// 人工工资-店长底薪 | |
| 181 | + /// </summary> | |
| 182 | + [SugarColumn(ColumnName = "F_SalaryBaseStoreManager")] | |
| 183 | + public decimal SalaryBaseStoreManager { get; set; } | |
| 184 | + | |
| 185 | + /// <summary> | |
| 186 | + /// 人工工资-总经理/经理底薪 | |
| 187 | + /// </summary> | |
| 188 | + [SugarColumn(ColumnName = "F_SalaryBaseGeneralManager")] | |
| 189 | + public decimal SalaryBaseGeneralManager { get; set; } | |
| 190 | + | |
| 191 | + /// <summary> | |
| 192 | + /// 人工工资-健康师提成 | |
| 193 | + /// </summary> | |
| 194 | + [SugarColumn(ColumnName = "F_SalaryCommissionHealthCoach")] | |
| 195 | + public decimal SalaryCommissionHealthCoach { get; set; } | |
| 196 | + | |
| 197 | + /// <summary> | |
| 198 | + /// 人工工资-店助提成 | |
| 199 | + /// </summary> | |
| 200 | + [SugarColumn(ColumnName = "F_SalaryCommissionAssistant")] | |
| 201 | + public decimal SalaryCommissionAssistant { get; set; } | |
| 202 | + | |
| 203 | + /// <summary> | |
| 204 | + /// 人工工资-店助主任提成 | |
| 205 | + /// </summary> | |
| 206 | + [SugarColumn(ColumnName = "F_SalaryCommissionDirector")] | |
| 207 | + public decimal SalaryCommissionDirector { get; set; } | |
| 208 | + | |
| 209 | + /// <summary> | |
| 210 | + /// 人工工资-店长提成 | |
| 211 | + /// </summary> | |
| 212 | + [SugarColumn(ColumnName = "F_SalaryCommissionStoreManager")] | |
| 213 | + public decimal SalaryCommissionStoreManager { get; set; } | |
| 214 | + | |
| 215 | + /// <summary> | |
| 216 | + /// 人工工资-总经理/经理提成 | |
| 217 | + /// </summary> | |
| 218 | + [SugarColumn(ColumnName = "F_SalaryCommissionGeneralManager")] | |
| 219 | + public decimal SalaryCommissionGeneralManager { get; set; } | |
| 220 | + | |
| 221 | + /// <summary> | |
| 222 | + /// 人工工资-手工 | |
| 223 | + /// </summary> | |
| 224 | + [SugarColumn(ColumnName = "F_SalaryManual")] | |
| 225 | + public decimal SalaryManual { get; set; } | |
| 226 | + | |
| 227 | + /// <summary> | |
| 228 | + /// 人工工资-出勤(保留) | |
| 229 | + /// </summary> | |
| 230 | + [SugarColumn(ColumnName = "F_SalaryAttendance")] | |
| 231 | + public decimal SalaryAttendance { get; set; } | |
| 232 | + | |
| 233 | + /// <summary> | |
| 234 | + /// 人工工资-岗位-手机保管费 | |
| 235 | + /// </summary> | |
| 236 | + [SugarColumn(ColumnName = "F_SalaryPhoneCustody")] | |
| 237 | + public decimal SalaryPhoneCustody { get; set; } | |
| 238 | + | |
| 239 | + /// <summary> | |
| 240 | + /// 人工工资-岗位-人头 | |
| 241 | + /// </summary> | |
| 242 | + [SugarColumn(ColumnName = "F_SalaryHeadcountReward")] | |
| 243 | + public decimal SalaryHeadcountReward { get; set; } | |
| 244 | + | |
| 245 | + /// <summary> | |
| 246 | + /// 人工工资-门店T区 | |
| 247 | + /// </summary> | |
| 248 | + [SugarColumn(ColumnName = "F_SalaryTZone")] | |
| 249 | + public decimal SalaryTZone { get; set; } | |
| 250 | + | |
| 251 | + // 费用与其他 | |
| 252 | + /// <summary> | |
| 253 | + /// 社保(保留) | |
| 254 | + /// </summary> | |
| 255 | + [SugarColumn(ColumnName = "F_SocialSecurity")] | |
| 256 | + public decimal SocialSecurity { get; set; } | |
| 257 | + | |
| 258 | + /// <summary> | |
| 259 | + /// 门店房租 | |
| 260 | + /// </summary> | |
| 261 | + [SugarColumn(ColumnName = "F_StoreRent")] | |
| 262 | + public decimal StoreRent { get; set; } | |
| 263 | + | |
| 264 | + /// <summary> | |
| 265 | + /// 宿舍房租(保留) | |
| 266 | + /// </summary> | |
| 267 | + [SugarColumn(ColumnName = "F_DormRent")] | |
| 268 | + public decimal DormRent { get; set; } | |
| 269 | + | |
| 270 | + /// <summary> | |
| 271 | + /// 当期费用(报销) | |
| 272 | + /// </summary> | |
| 273 | + [SugarColumn(ColumnName = "F_CurrentPeriodExpense")] | |
| 274 | + public decimal CurrentPeriodExpense { get; set; } | |
| 275 | + | |
| 276 | + /// <summary> | |
| 277 | + /// 当前费用-秦董(保留) | |
| 278 | + /// </summary> | |
| 279 | + [SugarColumn(ColumnName = "F_CurrentPeriodExpenseQin")] | |
| 280 | + public decimal CurrentPeriodExpenseQin { get; set; } | |
| 281 | + | |
| 282 | + /// <summary> | |
| 283 | + /// 财务费用(保留) | |
| 284 | + /// </summary> | |
| 285 | + [SugarColumn(ColumnName = "F_FinancialFee")] | |
| 286 | + public decimal FinancialFee { get; set; } | |
| 287 | + | |
| 288 | + // 奖励 | |
| 289 | + /// <summary> | |
| 290 | + /// 奖励-医美(保留) | |
| 291 | + /// </summary> | |
| 292 | + [SugarColumn(ColumnName = "F_RewardMedicalBeauty")] | |
| 293 | + public decimal RewardMedicalBeauty { get; set; } | |
| 294 | + | |
| 295 | + /// <summary> | |
| 296 | + /// 奖励-其他(保留) | |
| 297 | + /// </summary> | |
| 298 | + [SugarColumn(ColumnName = "F_RewardOther")] | |
| 299 | + public decimal RewardOther { get; set; } | |
| 300 | + | |
| 301 | + /// <summary> | |
| 302 | + /// 奖励-大单奖(保留) | |
| 303 | + /// </summary> | |
| 304 | + [SugarColumn(ColumnName = "F_RewardLargeOrder")] | |
| 305 | + public decimal RewardLargeOrder { get; set; } | |
| 306 | + | |
| 307 | + /// <summary> | |
| 308 | + /// 奖励-首单奖(保留) | |
| 309 | + /// </summary> | |
| 310 | + [SugarColumn(ColumnName = "F_RewardFirstOrder")] | |
| 311 | + public decimal RewardFirstOrder { get; set; } | |
| 312 | + | |
| 313 | + /// <summary> | |
| 314 | + /// 奖励(保留) | |
| 315 | + /// </summary> | |
| 316 | + [SugarColumn(ColumnName = "F_RewardGeneral")] | |
| 317 | + public decimal RewardGeneral { get; set; } | |
| 318 | + | |
| 319 | + // 其他保留字段 | |
| 320 | + /// <summary> | |
| 321 | + /// 其他1(保留) | |
| 322 | + /// </summary> | |
| 323 | + [SugarColumn(ColumnName = "F_Other1")] | |
| 324 | + public decimal Other1 { get; set; } | |
| 325 | + | |
| 326 | + /// <summary> | |
| 327 | + /// 其他2(保留) | |
| 328 | + /// </summary> | |
| 329 | + [SugarColumn(ColumnName = "F_Other2")] | |
| 330 | + public decimal Other2 { get; set; } | |
| 331 | + | |
| 332 | + // 利润结果 | |
| 333 | + /// <summary> | |
| 334 | + /// 利润(预收-实退-主营成本-人工工资[分项汇总]-房租-费用-奖励-其他) | |
| 335 | + /// </summary> | |
| 336 | + [SugarColumn(ColumnName = "F_Profit")] | |
| 337 | + public decimal Profit { get; set; } | |
| 338 | + | |
| 339 | + /// <summary> | |
| 340 | + /// 财务利润(主营-其他收入-主营成本-人工工资[分项汇总]-房租-费用-奖励-其他) | |
| 341 | + /// </summary> | |
| 342 | + [SugarColumn(ColumnName = "F_FinancialProfit")] | |
| 343 | + public decimal FinancialProfit { get; set; } | |
| 344 | + | |
| 345 | + // 基础字段 | |
| 346 | + /// <summary> | |
| 347 | + /// 创建时间 | |
| 348 | + /// </summary> | |
| 349 | + [SugarColumn(ColumnName = "F_CreateTime")] | |
| 350 | + public DateTime? CreateTime { get; set; } | |
| 351 | + | |
| 352 | + /// <summary> | |
| 353 | + /// 更新时间 | |
| 354 | + /// </summary> | |
| 355 | + [SugarColumn(ColumnName = "F_UpdateTime")] | |
| 356 | + public DateTime? UpdateTime { get; set; } | |
| 357 | + | |
| 358 | + /// <summary> | |
| 359 | + /// 创建人 | |
| 360 | + /// </summary> | |
| 361 | + [SugarColumn(ColumnName = "F_CreateUser")] | |
| 362 | + public string CreateUser { get; set; } | |
| 363 | + | |
| 364 | + /// <summary> | |
| 365 | + /// 更新人 | |
| 366 | + /// </summary> | |
| 367 | + [SugarColumn(ColumnName = "F_UpdateUser")] | |
| 368 | + public string UpdateUser { get; set; } | |
| 369 | + | |
| 370 | + /// <summary> | |
| 371 | + /// 是否有效(1:有效 0:无效) | |
| 372 | + /// </summary> | |
| 373 | + [SugarColumn(ColumnName = "F_IsEffective")] | |
| 374 | + public int IsEffective { get; set; } | |
| 375 | + } | |
| 376 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_share_statistics_tech_dept/LqShareStatisticsTechDeptEntity.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using NCC.Common.Const; | |
| 3 | +using SqlSugar; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys.lq_share_statistics_tech_dept | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 科技部股份统计表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("lq_share_statistics_tech_dept")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class LqShareStatisticsTechDeptEntity | |
| 13 | + { | |
| 14 | + /// <summary> | |
| 15 | + /// 主键ID | |
| 16 | + /// </summary> | |
| 17 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 18 | + public string Id { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 部门名称(科技一部/二部) | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "F_DepartmentName")] | |
| 24 | + public string DepartmentName { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 统计月份(YYYYMM) | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "F_StatisticsMonth")] | |
| 30 | + public string StatisticsMonth { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 收入(门店科美开单30%) | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "F_Income")] | |
| 36 | + public decimal Income { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 成本-报销 | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "F_CostReimbursement")] | |
| 42 | + public decimal CostReimbursement { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 成本-人工-科技部老师底薪 | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "F_CostTeacherBase")] | |
| 48 | + public decimal CostTeacherBase { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 成本-人工-科技部手工费 | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "F_CostTeacherManual")] | |
| 54 | + public decimal CostTeacherManual { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 成本-人工-科技部开单提成 | |
| 58 | + /// </summary> | |
| 59 | + [SugarColumn(ColumnName = "F_CostTeacherBillingComm")] | |
| 60 | + public decimal CostTeacherBillingComm { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 成本-人工-科技部消耗提成 | |
| 64 | + /// </summary> | |
| 65 | + [SugarColumn(ColumnName = "F_CostTeacherConsumeComm")] | |
| 66 | + public decimal CostTeacherConsumeComm { get; set; } | |
| 67 | + | |
| 68 | + /// <summary> | |
| 69 | + /// 成本-人工-科技部专家提成(保留) | |
| 70 | + /// </summary> | |
| 71 | + [SugarColumn(ColumnName = "F_CostTeacherExpertComm")] | |
| 72 | + public decimal CostTeacherExpertComm { get; set; } | |
| 73 | + | |
| 74 | + /// <summary> | |
| 75 | + /// 成本-人工-科技部加班(保留) | |
| 76 | + /// </summary> | |
| 77 | + [SugarColumn(ColumnName = "F_CostTeacherOvertime")] | |
| 78 | + public decimal CostTeacherOvertime { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 成本-人工-科技部总经理底薪 | |
| 82 | + /// </summary> | |
| 83 | + [SugarColumn(ColumnName = "F_CostGMBase")] | |
| 84 | + public decimal CostGMBase { get; set; } | |
| 85 | + | |
| 86 | + /// <summary> | |
| 87 | + /// 成本-人工-科技部总经理提成 | |
| 88 | + /// </summary> | |
| 89 | + [SugarColumn(ColumnName = "F_CostGMComm")] | |
| 90 | + public decimal CostGMComm { get; set; } | |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 奖励-科技部(保留) | |
| 94 | + /// </summary> | |
| 95 | + [SugarColumn(ColumnName = "F_RewardTechDept")] | |
| 96 | + public decimal RewardTechDept { get; set; } | |
| 97 | + | |
| 98 | + /// <summary> | |
| 99 | + /// 成本-其他1(保留) | |
| 100 | + /// </summary> | |
| 101 | + [SugarColumn(ColumnName = "F_CostOther1")] | |
| 102 | + public decimal CostOther1 { get; set; } | |
| 103 | + | |
| 104 | + /// <summary> | |
| 105 | + /// 成本-其他2(保留) | |
| 106 | + /// </summary> | |
| 107 | + [SugarColumn(ColumnName = "F_CostOther2")] | |
| 108 | + public decimal CostOther2 { get; set; } | |
| 109 | + | |
| 110 | + /// <summary> | |
| 111 | + /// 科技部利润 | |
| 112 | + /// </summary> | |
| 113 | + [SugarColumn(ColumnName = "F_Profit")] | |
| 114 | + public decimal Profit { get; set; } | |
| 115 | + | |
| 116 | + /// <summary> | |
| 117 | + /// 创建时间 | |
| 118 | + /// </summary> | |
| 119 | + [SugarColumn(ColumnName = "F_CreateTime")] | |
| 120 | + public DateTime CreateTime { get; set; } | |
| 121 | + | |
| 122 | + /// <summary> | |
| 123 | + /// 更新时间 | |
| 124 | + /// </summary> | |
| 125 | + [SugarColumn(ColumnName = "F_UpdateTime")] | |
| 126 | + public DateTime? UpdateTime { get; set; } | |
| 127 | + | |
| 128 | + /// <summary> | |
| 129 | + /// 创建人 | |
| 130 | + /// </summary> | |
| 131 | + [SugarColumn(ColumnName = "F_CreateUser")] | |
| 132 | + public string CreateUser { get; set; } | |
| 133 | + | |
| 134 | + /// <summary> | |
| 135 | + /// 更新人 | |
| 136 | + /// </summary> | |
| 137 | + [SugarColumn(ColumnName = "F_UpdateUser")] | |
| 138 | + public string UpdateUser { get; set; } | |
| 139 | + | |
| 140 | + /// <summary> | |
| 141 | + /// 是否有效(1:有效 0:无效) | |
| 142 | + /// </summary> | |
| 143 | + [SugarColumn(ColumnName = "F_IsEffective")] | |
| 144 | + public int IsEffective { get; set; } = 1; | |
| 145 | + } | |
| 146 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqContractService.cs
| 1 | 1 | using System; |
| 2 | 2 | using System.Collections.Generic; |
| 3 | +using System.Globalization; | |
| 3 | 4 | using System.Linq; |
| 4 | 5 | using System.Threading.Tasks; |
| 5 | 6 | using Microsoft.AspNetCore.Mvc; |
| ... | ... | @@ -12,6 +13,7 @@ using NCC.Extend.Entitys.Dto.LqContract; |
| 12 | 13 | using NCC.Extend.Entitys.Enum; |
| 13 | 14 | using NCC.Extend.Entitys.lq_contract; |
| 14 | 15 | using NCC.Extend.Entitys.lq_contract_rent_detail; |
| 16 | +using NCC.Extend.Entitys.lq_contract_monthly_cost; | |
| 15 | 17 | using NCC.Extend.Entitys.lq_mdxx; |
| 16 | 18 | using NCC.FriendlyException; |
| 17 | 19 | using NCC.System.Entitys.Permission; |
| ... | ... | @@ -147,6 +149,9 @@ namespace NCC.Extend |
| 147 | 149 | // 自动生成月租明细 |
| 148 | 150 | await GenerateRentDetailsAsync(contractEntity.Id, contractEntity.ContractStartDate, contractEntity.ContractEndDate, contractEntity.PaymentCycle, contractEntity.PaymentAmount); |
| 149 | 151 | |
| 152 | + // 自动生成按月成本记录 | |
| 153 | + await GenerateMonthlyCostAsync(contractEntity.Id, contractEntity.StoreId, contractEntity.StoreName, contractEntity.Category, contractEntity.ContractStartDate, contractEntity.ContractEndDate, contractEntity.PaymentCycle, contractEntity.PaymentAmount); | |
| 154 | + | |
| 150 | 155 | // 计算下次应交时间 |
| 151 | 156 | await CalculateNextPaymentDate(contractEntity.Id); |
| 152 | 157 | |
| ... | ... | @@ -249,6 +254,9 @@ namespace NCC.Extend |
| 249 | 254 | contract.PaymentCycle != input.PaymentCycle || |
| 250 | 255 | contract.PaymentAmount != input.PaymentAmount; |
| 251 | 256 | |
| 257 | + // 判断是否需要更新成本记录的分类(如果只修改了分类,不需要重新生成,只需更新分类字段) | |
| 258 | + bool needUpdateCategory = contract.Category != input.Category; | |
| 259 | + | |
| 252 | 260 | // 更新合同信息 |
| 253 | 261 | contract.StoreId = input.StoreId; |
| 254 | 262 | contract.StoreName = store.Dm ?? ""; |
| ... | ... | @@ -287,8 +295,33 @@ namespace NCC.Extend |
| 287 | 295 | .Where(x => x.ContractId == contract.Id && x.IsEffective == StatusEnum.有效.GetHashCode()) |
| 288 | 296 | .ExecuteCommandAsync(); |
| 289 | 297 | |
| 298 | + // 删除旧的成本记录(逻辑删除) | |
| 299 | + await _db.Updateable<LqContractMonthlyCostEntity>() | |
| 300 | + .SetColumns(x => new LqContractMonthlyCostEntity | |
| 301 | + { | |
| 302 | + IsEffective = StatusEnum.无效.GetHashCode(), | |
| 303 | + UpdateTime = DateTime.Now | |
| 304 | + }) | |
| 305 | + .Where(x => x.ContractId == contract.Id && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 306 | + .ExecuteCommandAsync(); | |
| 307 | + | |
| 290 | 308 | // 生成新的明细 |
| 291 | 309 | await GenerateRentDetailsAsync(contract.Id, contract.ContractStartDate, contract.ContractEndDate, contract.PaymentCycle, contract.PaymentAmount); |
| 310 | + | |
| 311 | + // 生成新的成本记录 | |
| 312 | + await GenerateMonthlyCostAsync(contract.Id, contract.StoreId, contract.StoreName, contract.Category, contract.ContractStartDate, contract.ContractEndDate, contract.PaymentCycle, contract.PaymentAmount); | |
| 313 | + } | |
| 314 | + else if (needUpdateCategory) | |
| 315 | + { | |
| 316 | + // 如果只修改了分类,只需更新成本记录的分类字段 | |
| 317 | + await _db.Updateable<LqContractMonthlyCostEntity>() | |
| 318 | + .SetColumns(x => new LqContractMonthlyCostEntity | |
| 319 | + { | |
| 320 | + Category = contract.Category, | |
| 321 | + UpdateTime = DateTime.Now | |
| 322 | + }) | |
| 323 | + .Where(x => x.ContractId == contract.Id && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 324 | + .ExecuteCommandAsync(); | |
| 292 | 325 | } |
| 293 | 326 | |
| 294 | 327 | // 重新计算下次应交时间 |
| ... | ... | @@ -365,6 +398,16 @@ namespace NCC.Extend |
| 365 | 398 | .Where(x => x.ContractId == id && x.IsEffective == StatusEnum.有效.GetHashCode()) |
| 366 | 399 | .ExecuteCommandAsync(); |
| 367 | 400 | |
| 401 | + // 删除该合同的所有成本记录(逻辑删除) | |
| 402 | + await _db.Updateable<LqContractMonthlyCostEntity>() | |
| 403 | + .SetColumns(x => new LqContractMonthlyCostEntity | |
| 404 | + { | |
| 405 | + IsEffective = StatusEnum.无效.GetHashCode(), | |
| 406 | + UpdateTime = DateTime.Now | |
| 407 | + }) | |
| 408 | + .Where(x => x.ContractId == id && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 409 | + .ExecuteCommandAsync(); | |
| 410 | + | |
| 368 | 411 | // 删除合同(逻辑删除) |
| 369 | 412 | contract.IsEffective = StatusEnum.无效.GetHashCode(); |
| 370 | 413 | contract.UpdateUser = _userManager.UserId; |
| ... | ... | @@ -792,6 +835,35 @@ namespace NCC.Extend |
| 792 | 835 | |
| 793 | 836 | #endregion |
| 794 | 837 | |
| 838 | + #region 根据合同id获取合同成本列表 | |
| 839 | + /// <summary> | |
| 840 | + /// 根据合同id获取合同成本列表 | |
| 841 | + /// </summary> | |
| 842 | + /// <param name="contractId"></param> | |
| 843 | + /// <returns></returns> | |
| 844 | + [HttpGet("GetContractCostList")] | |
| 845 | + public async Task<dynamic> GetContractCostListAsync([FromQuery] string contractId) | |
| 846 | + { | |
| 847 | + try | |
| 848 | + { | |
| 849 | + if (string.IsNullOrWhiteSpace(contractId)) | |
| 850 | + { | |
| 851 | + throw NCCException.Oh("合同ID不能为空"); | |
| 852 | + } | |
| 853 | + | |
| 854 | + var costList = await _db.Queryable<LqContractMonthlyCostEntity>() | |
| 855 | + .Where(x => x.ContractId == contractId && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 856 | + .ToListAsync(); | |
| 857 | + return costList; | |
| 858 | + } | |
| 859 | + catch (Exception ex) | |
| 860 | + { | |
| 861 | + _logger.LogError(ex, "获取合同成本列表失败"); | |
| 862 | + throw NCCException.Oh($"获取合同成本列表失败:{ex.Message}"); | |
| 863 | + } | |
| 864 | + } | |
| 865 | + #endregion | |
| 866 | + | |
| 795 | 867 | #region 私有方法 |
| 796 | 868 | |
| 797 | 869 | /// <summary> |
| ... | ... | @@ -842,6 +914,57 @@ namespace NCC.Extend |
| 842 | 914 | } |
| 843 | 915 | |
| 844 | 916 | /// <summary> |
| 917 | + /// 生成按月成本记录 | |
| 918 | + /// </summary> | |
| 919 | + /// <param name="contractId">合同ID</param> | |
| 920 | + /// <param name="storeId">门店ID</param> | |
| 921 | + /// <param name="storeName">店名</param> | |
| 922 | + /// <param name="category">分类</param> | |
| 923 | + /// <param name="contractStartDate">合同起始日期</param> | |
| 924 | + /// <param name="contractEndDate">合同结束日期</param> | |
| 925 | + /// <param name="paymentCycle">交租周期(月)</param> | |
| 926 | + /// <param name="paymentAmount">缴租金额</param> | |
| 927 | + private async Task GenerateMonthlyCostAsync(string contractId, string storeId, string storeName, string category, DateTime contractStartDate, DateTime contractEndDate, int paymentCycle, decimal paymentAmount) | |
| 928 | + { | |
| 929 | + // 计算每个月成本 = 缴租金额 / 交租周期 | |
| 930 | + var monthlyCost = paymentAmount / paymentCycle; | |
| 931 | + | |
| 932 | + var costRecords = new List<LqContractMonthlyCostEntity>(); | |
| 933 | + var currentMonth = new DateTime(contractStartDate.Year, contractStartDate.Month, 1); | |
| 934 | + | |
| 935 | + // 从合同起始月份开始,逐月生成成本记录,直到合同结束月份 | |
| 936 | + while (currentMonth <= contractEndDate) | |
| 937 | + { | |
| 938 | + var costRecord = new LqContractMonthlyCostEntity | |
| 939 | + { | |
| 940 | + Id = YitIdHelper.NextId().ToString(), | |
| 941 | + ContractId = contractId, | |
| 942 | + StoreId = storeId, | |
| 943 | + StoreName = storeName, | |
| 944 | + Category = category, | |
| 945 | + Month = currentMonth, | |
| 946 | + MonthlyCost = monthlyCost, | |
| 947 | + PaymentCycle = paymentCycle, | |
| 948 | + PaymentAmount = paymentAmount, | |
| 949 | + IsEffective = StatusEnum.有效.GetHashCode(), | |
| 950 | + CreateUser = _userManager.UserId, | |
| 951 | + CreateTime = DateTime.Now | |
| 952 | + }; | |
| 953 | + | |
| 954 | + costRecords.Add(costRecord); | |
| 955 | + | |
| 956 | + // 计算下一个月 | |
| 957 | + currentMonth = currentMonth.AddMonths(1); | |
| 958 | + } | |
| 959 | + | |
| 960 | + // 批量插入 | |
| 961 | + if (costRecords.Any()) | |
| 962 | + { | |
| 963 | + await _db.Insertable(costRecords).ExecuteCommandAsync(); | |
| 964 | + } | |
| 965 | + } | |
| 966 | + | |
| 967 | + /// <summary> | |
| 845 | 968 | /// 计算下次应交时间(匿名方法) |
| 846 | 969 | /// </summary> |
| 847 | 970 | /// <param name="contractId">合同ID</param> |
| ... | ... | @@ -888,6 +1011,152 @@ namespace NCC.Extend |
| 888 | 1011 | |
| 889 | 1012 | #endregion |
| 890 | 1013 | |
| 1014 | + #region 获取合同成本按月统计 | |
| 1015 | + | |
| 1016 | + /// <summary> | |
| 1017 | + /// 获取合同成本按月统计 | |
| 1018 | + /// </summary> | |
| 1019 | + /// <remarks> | |
| 1020 | + /// 根据合同ID、门店ID、月份等条件查询合同成本按月统计数据 | |
| 1021 | + /// | |
| 1022 | + /// 示例请求: | |
| 1023 | + /// ``` | |
| 1024 | + /// GET /api/Extend/LqContract/GetMonthlyCost?contractId=合同ID&storeId=门店ID&startMonth=2025-01&endMonth=2025-12 | |
| 1025 | + /// ``` | |
| 1026 | + /// | |
| 1027 | + /// 参数说明: | |
| 1028 | + /// - contractId: 合同ID(可选) | |
| 1029 | + /// - storeId: 门店ID(可选) | |
| 1030 | + /// - startMonth: 开始月份(格式:YYYY-MM,可选) | |
| 1031 | + /// - endMonth: 结束月份(格式:YYYY-MM,可选) | |
| 1032 | + /// </remarks> | |
| 1033 | + /// <param name="contractId">合同ID(可选)</param> | |
| 1034 | + /// <param name="storeId">门店ID(可选)</param> | |
| 1035 | + /// <param name="category">分类(可选)</param> | |
| 1036 | + /// <param name="startMonth">开始月份(格式:YYYY-MM,可选)</param> | |
| 1037 | + /// <param name="endMonth">结束月份(格式:YYYY-MM,可选)</param> | |
| 1038 | + /// <returns>合同成本按月统计列表</returns> | |
| 1039 | + /// <response code="200">查询成功</response> | |
| 1040 | + /// <response code="500">服务器错误</response> | |
| 1041 | + [HttpGet("GetMonthlyCost")] | |
| 1042 | + public async Task<dynamic> GetMonthlyCostAsync( | |
| 1043 | + [FromQuery] string contractId = null, | |
| 1044 | + [FromQuery] string storeId = null, | |
| 1045 | + [FromQuery] string category = null, | |
| 1046 | + [FromQuery] string startMonth = null, | |
| 1047 | + [FromQuery] string endMonth = null) | |
| 1048 | + { | |
| 1049 | + try | |
| 1050 | + { | |
| 1051 | + var query = _db.Queryable<LqContractMonthlyCostEntity>() | |
| 1052 | + .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 1053 | + .WhereIF(!string.IsNullOrWhiteSpace(contractId), x => x.ContractId == contractId) | |
| 1054 | + .WhereIF(!string.IsNullOrWhiteSpace(storeId), x => x.StoreId == storeId) | |
| 1055 | + .WhereIF(!string.IsNullOrWhiteSpace(category), x => x.Category == category); | |
| 1056 | + | |
| 1057 | + // 处理月份范围筛选 | |
| 1058 | + if (!string.IsNullOrWhiteSpace(startMonth)) | |
| 1059 | + { | |
| 1060 | + if (DateTime.TryParseExact(startMonth, "yyyy-MM", null, DateTimeStyles.None, out DateTime startDate)) | |
| 1061 | + { | |
| 1062 | + var startMonthDate = new DateTime(startDate.Year, startDate.Month, 1); | |
| 1063 | + query = query.Where(x => x.Month >= startMonthDate); | |
| 1064 | + } | |
| 1065 | + } | |
| 1066 | + | |
| 1067 | + if (!string.IsNullOrWhiteSpace(endMonth)) | |
| 1068 | + { | |
| 1069 | + if (DateTime.TryParseExact(endMonth, "yyyy-MM", null, DateTimeStyles.None, out DateTime endDate)) | |
| 1070 | + { | |
| 1071 | + var endMonthDate = new DateTime(endDate.Year, endDate.Month, DateTime.DaysInMonth(endDate.Year, endDate.Month)); | |
| 1072 | + query = query.Where(x => x.Month <= endMonthDate); | |
| 1073 | + } | |
| 1074 | + } | |
| 1075 | + | |
| 1076 | + var data = await query | |
| 1077 | + .OrderBy(x => x.Month) | |
| 1078 | + .Select(x => new | |
| 1079 | + { | |
| 1080 | + id = x.Id, | |
| 1081 | + contractId = x.ContractId, | |
| 1082 | + storeId = x.StoreId, | |
| 1083 | + storeName = x.StoreName, | |
| 1084 | + category = x.Category, | |
| 1085 | + month = x.Month.ToString("yyyy-MM"), | |
| 1086 | + monthlyCost = x.MonthlyCost, | |
| 1087 | + paymentCycle = x.PaymentCycle, | |
| 1088 | + paymentAmount = x.PaymentAmount, | |
| 1089 | + createTime = x.CreateTime | |
| 1090 | + }) | |
| 1091 | + .ToListAsync(); | |
| 1092 | + | |
| 1093 | + return new { code = 200, msg = "查询成功", data = data }; | |
| 1094 | + } | |
| 1095 | + catch (Exception ex) | |
| 1096 | + { | |
| 1097 | + _logger.LogError(ex, "获取合同成本按月统计失败"); | |
| 1098 | + throw NCCException.Oh($"查询失败:{ex.Message}"); | |
| 1099 | + } | |
| 1100 | + } | |
| 1101 | + | |
| 1102 | + /// <summary> | |
| 1103 | + /// 按门店和月份统计合同成本 | |
| 1104 | + /// </summary> | |
| 1105 | + /// <remarks> | |
| 1106 | + /// 根据门店ID和月份统计该门店的合同成本总和 | |
| 1107 | + /// | |
| 1108 | + /// 示例请求: | |
| 1109 | + /// ``` | |
| 1110 | + /// GET /api/Extend/LqContract/GetStoreMonthlyCost?storeId=门店ID&month=2025-11 | |
| 1111 | + /// ``` | |
| 1112 | + /// </remarks> | |
| 1113 | + /// <param name="storeId">门店ID</param> | |
| 1114 | + /// <param name="month">月份(格式:YYYY-MM)</param> | |
| 1115 | + /// <returns>该门店该月的合同成本总和</returns> | |
| 1116 | + /// <response code="200">查询成功</response> | |
| 1117 | + /// <response code="400">参数错误</response> | |
| 1118 | + /// <response code="500">服务器错误</response> | |
| 1119 | + [HttpGet("GetStoreMonthlyCost")] | |
| 1120 | + public async Task<dynamic> GetStoreMonthlyCostAsync( | |
| 1121 | + [FromQuery] string storeId, | |
| 1122 | + [FromQuery] string month) | |
| 1123 | + { | |
| 1124 | + try | |
| 1125 | + { | |
| 1126 | + if (string.IsNullOrWhiteSpace(storeId)) | |
| 1127 | + { | |
| 1128 | + throw NCCException.Oh("门店ID不能为空"); | |
| 1129 | + } | |
| 1130 | + | |
| 1131 | + if (string.IsNullOrWhiteSpace(month)) | |
| 1132 | + { | |
| 1133 | + throw NCCException.Oh("月份不能为空"); | |
| 1134 | + } | |
| 1135 | + | |
| 1136 | + if (!DateTime.TryParseExact(month, "yyyy-MM", null, DateTimeStyles.None, out DateTime monthDate)) | |
| 1137 | + { | |
| 1138 | + throw NCCException.Oh("月份格式错误,应为 YYYY-MM"); | |
| 1139 | + } | |
| 1140 | + | |
| 1141 | + var monthStart = new DateTime(monthDate.Year, monthDate.Month, 1); | |
| 1142 | + | |
| 1143 | + var totalCost = await _db.Queryable<LqContractMonthlyCostEntity>() | |
| 1144 | + .Where(x => x.StoreId == storeId | |
| 1145 | + && x.Month == monthStart | |
| 1146 | + && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 1147 | + .SumAsync(x => x.MonthlyCost); | |
| 1148 | + | |
| 1149 | + return new { code = 200, msg = "查询成功", data = new { storeId = storeId, month = month, totalCost = totalCost } }; | |
| 1150 | + } | |
| 1151 | + catch (Exception ex) | |
| 1152 | + { | |
| 1153 | + _logger.LogError(ex, "按门店和月份统计合同成本失败"); | |
| 1154 | + throw NCCException.Oh($"查询失败:{ex.Message}"); | |
| 1155 | + } | |
| 1156 | + } | |
| 1157 | + | |
| 1158 | + #endregion | |
| 1159 | + | |
| 891 | 1160 | #region 统计门店合同费用 |
| 892 | 1161 | |
| 893 | 1162 | /// <summary> |
| ... | ... | @@ -960,13 +1229,13 @@ namespace NCC.Extend |
| 960 | 1229 | var details = await _db.Queryable<LqContractRentDetailEntity, LqContractEntity>( |
| 961 | 1230 | (detail, contract) => new JoinQueryInfos( |
| 962 | 1231 | JoinType.Inner, detail.ContractId == contract.Id)) |
| 963 | - .Where((detail, contract) => | |
| 1232 | + .Where((detail, contract) => | |
| 964 | 1233 | contract.StoreId == input.StoreId && |
| 965 | 1234 | contract.IsEffective == StatusEnum.有效.GetHashCode() && |
| 966 | 1235 | detail.IsEffective == StatusEnum.有效.GetHashCode() && |
| 967 | 1236 | detail.PaymentMonth >= monthStart && |
| 968 | 1237 | detail.PaymentMonth <= monthEnd) |
| 969 | - .WhereIF(input.Categories != null && input.Categories.Length > 0, | |
| 1238 | + .WhereIF(input.Categories != null && input.Categories.Length > 0, | |
| 970 | 1239 | (detail, contract) => contract.Category != null && input.Categories.Contains(contract.Category)) |
| 971 | 1240 | .Select((detail, contract) => new |
| 972 | 1241 | { | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
| ... | ... | @@ -706,14 +706,15 @@ namespace NCC.Extend |
| 706 | 706 | } |
| 707 | 707 | |
| 708 | 708 | // 2.3.1 查询开单业绩(按品项类型) |
| 709 | + // 使用targetDict来关联,确保每个门店只关联一条目标记录,避免重复统计 | |
| 710 | + // 先按门店统计业绩,再按部门汇总 | |
| 709 | 711 | var billingSql = $@" |
| 710 | 712 | SELECT |
| 711 | - target.{deptField} as TargetDeptId, | |
| 713 | + billing.djmd as StoreId, | |
| 712 | 714 | COALESCE(SUM(pxmx.F_ActualPrice), 0) as StoreAmount |
| 713 | 715 | FROM lq_kd_pxmx pxmx |
| 714 | 716 | INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id |
| 715 | 717 | INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id |
| 716 | - INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId AND target.F_Month = '{month}' | |
| 717 | 718 | WHERE pxmx.F_IsEffective = 1 |
| 718 | 719 | AND billing.F_IsEffective = 1 |
| 719 | 720 | AND item.F_IsEffective = 1 |
| ... | ... | @@ -721,35 +722,48 @@ namespace NCC.Extend |
| 721 | 722 | AND billing.djmd IN ('{storeIdsStr}') |
| 722 | 723 | AND billing.kdrq >= '{startDate.ToString("yyyy-MM-dd")} 00:00:00' |
| 723 | 724 | AND billing.kdrq < '{endDate.AddDays(1).ToString("yyyy-MM-dd")} 00:00:00' |
| 724 | - GROUP BY target.{deptField}"; | |
| 725 | + GROUP BY billing.djmd"; | |
| 725 | 726 | |
| 726 | 727 | var billingData = await _db.Ado.SqlQueryAsync<dynamic>(billingSql); |
| 727 | 728 | |
| 728 | - // 分配开单业绩到对应部门 | |
| 729 | + // 分配开单业绩到对应部门(使用targetDict确保每个门店只关联一条目标记录) | |
| 729 | 730 | foreach (var billing in billingData ?? Enumerable.Empty<dynamic>()) |
| 730 | 731 | { |
| 731 | 732 | var storeAmount = billing?.StoreAmount != null ? Convert.ToDecimal(billing.StoreAmount) : 0m; |
| 732 | - var targetDeptId = billing?.TargetDeptId?.ToString(); | |
| 733 | + var storeId = billing?.StoreId?.ToString(); | |
| 733 | 734 | |
| 734 | - if (storeAmount <= 0 || string.IsNullOrEmpty(targetDeptId)) | |
| 735 | + if (storeAmount <= 0 || string.IsNullOrEmpty(storeId)) | |
| 735 | 736 | continue; |
| 736 | 737 | |
| 737 | - // 只分配给在查询列表中的部门,避免重复统计 | |
| 738 | - if (departmentDict.ContainsKey(targetDeptId)) | |
| 738 | + // 从targetDict中获取门店对应的部门信息 | |
| 739 | + if (targetDict.ContainsKey(storeId)) | |
| 739 | 740 | { |
| 740 | - departmentDict[targetDeptId].BillingPerformance += storeAmount; | |
| 741 | + var target = targetDict[storeId]; | |
| 742 | + // 动态获取部门字段值 | |
| 743 | + string targetDeptId = null; | |
| 744 | + if (deptField == "F_EducationDepartment") | |
| 745 | + targetDeptId = target?.F_EducationDepartment?.ToString(); | |
| 746 | + else if (deptField == "F_TechDepartment") | |
| 747 | + targetDeptId = target?.F_TechDepartment?.ToString(); | |
| 748 | + else if (deptField == "F_MajorProjectDepartment") | |
| 749 | + targetDeptId = target?.F_MajorProjectDepartment?.ToString(); | |
| 750 | + | |
| 751 | + if (!string.IsNullOrEmpty(targetDeptId) && departmentDict.ContainsKey(targetDeptId)) | |
| 752 | + { | |
| 753 | + departmentDict[targetDeptId].BillingPerformance += storeAmount; | |
| 754 | + } | |
| 741 | 755 | } |
| 742 | 756 | } |
| 743 | 757 | |
| 744 | 758 | // 2.3.2 查询退卡业绩(按品项类型) |
| 759 | + // 使用targetDict来关联,确保每个门店只关联一条目标记录,避免重复统计 | |
| 745 | 760 | var refundSql = $@" |
| 746 | 761 | SELECT |
| 747 | - target.{deptField} as TargetDeptId, | |
| 762 | + refund.md as StoreId, | |
| 748 | 763 | COALESCE(SUM(refund_mx.tkje), 0) as StoreRefundAmount |
| 749 | 764 | FROM lq_hytk_mx refund_mx |
| 750 | 765 | INNER JOIN lq_hytk_hytk refund ON refund_mx.F_RefundInfoId = refund.F_Id |
| 751 | 766 | INNER JOIN lq_xmzl item ON refund_mx.px = item.F_Id |
| 752 | - INNER JOIN lq_md_target target ON refund.md = target.F_StoreId AND target.F_Month = '{month}' | |
| 753 | 767 | WHERE refund_mx.F_IsEffective = 1 |
| 754 | 768 | AND refund.F_IsEffective = 1 |
| 755 | 769 | AND item.F_IsEffective = 1 |
| ... | ... | @@ -757,59 +771,86 @@ namespace NCC.Extend |
| 757 | 771 | AND refund.md IN ('{storeIdsStr}') |
| 758 | 772 | AND refund.tksj >= '{startDate.ToString("yyyy-MM-dd")} 00:00:00' |
| 759 | 773 | AND refund.tksj < '{endDate.AddDays(1).ToString("yyyy-MM-dd")} 00:00:00' |
| 760 | - GROUP BY target.{deptField}"; | |
| 774 | + GROUP BY refund.md"; | |
| 761 | 775 | |
| 762 | 776 | var refundData = await _db.Ado.SqlQueryAsync<dynamic>(refundSql); |
| 763 | 777 | |
| 764 | - // 分配退卡业绩到对应部门 | |
| 778 | + // 分配退卡业绩到对应部门(使用targetDict确保每个门店只关联一条目标记录) | |
| 765 | 779 | foreach (var refund in refundData ?? Enumerable.Empty<dynamic>()) |
| 766 | 780 | { |
| 767 | 781 | var storeRefundAmount = refund?.StoreRefundAmount != null ? Convert.ToDecimal(refund.StoreRefundAmount) : 0m; |
| 768 | - var targetDeptId = refund?.TargetDeptId?.ToString(); | |
| 782 | + var storeId = refund?.StoreId?.ToString(); | |
| 769 | 783 | |
| 770 | - if (storeRefundAmount <= 0 || string.IsNullOrEmpty(targetDeptId)) | |
| 784 | + if (storeRefundAmount <= 0 || string.IsNullOrEmpty(storeId)) | |
| 771 | 785 | continue; |
| 772 | 786 | |
| 773 | - // 只分配给在查询列表中的部门,避免重复统计 | |
| 774 | - if (departmentDict.ContainsKey(targetDeptId)) | |
| 787 | + // 从targetDict中获取门店对应的部门信息 | |
| 788 | + if (targetDict.ContainsKey(storeId)) | |
| 775 | 789 | { |
| 776 | - departmentDict[targetDeptId].RefundPerformance += storeRefundAmount; | |
| 790 | + var target = targetDict[storeId]; | |
| 791 | + // 动态获取部门字段值 | |
| 792 | + string targetDeptId = null; | |
| 793 | + if (deptField == "F_EducationDepartment") | |
| 794 | + targetDeptId = target?.F_EducationDepartment?.ToString(); | |
| 795 | + else if (deptField == "F_TechDepartment") | |
| 796 | + targetDeptId = target?.F_TechDepartment?.ToString(); | |
| 797 | + else if (deptField == "F_MajorProjectDepartment") | |
| 798 | + targetDeptId = target?.F_MajorProjectDepartment?.ToString(); | |
| 799 | + | |
| 800 | + if (!string.IsNullOrEmpty(targetDeptId) && departmentDict.ContainsKey(targetDeptId)) | |
| 801 | + { | |
| 802 | + departmentDict[targetDeptId].RefundPerformance += storeRefundAmount; | |
| 803 | + } | |
| 777 | 804 | } |
| 778 | 805 | } |
| 779 | 806 | |
| 780 | 807 | // 2.3.3 查询储扣金额(按品项类型) |
| 808 | + // 使用储扣记录表中的开单时间(F_BillingTime)进行时间过滤,如果为空则使用开单记录表的开单时间(kdrq) | |
| 809 | + // 使用targetDict来关联,确保每个门店只关联一条目标记录,避免重复统计 | |
| 781 | 810 | var deductSql = $@" |
| 782 | 811 | SELECT |
| 783 | - target.{deptField} as TargetDeptId, | |
| 812 | + billing.djmd as StoreId, | |
| 784 | 813 | COALESCE(SUM(deduct.F_Amount), 0) as StoreDeductAmount |
| 785 | 814 | FROM lq_kd_deductinfo deduct |
| 786 | 815 | INNER JOIN lq_kd_kdjlb billing ON deduct.F_BillingId = billing.F_Id |
| 787 | 816 | INNER JOIN lq_xmzl item ON deduct.F_ItemId = item.F_Id |
| 788 | - INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId AND target.F_Month = '{month}' | |
| 789 | 817 | WHERE deduct.F_IsEffective = 1 |
| 790 | 818 | AND billing.F_IsEffective = 1 |
| 791 | 819 | AND item.F_IsEffective = 1 |
| 792 | 820 | AND item.qt2 = '{itemType}' |
| 793 | 821 | AND billing.djmd IN ('{storeIdsStr}') |
| 794 | - AND billing.kdrq >= '{startDate.ToString("yyyy-MM-dd")} 00:00:00' | |
| 795 | - AND billing.kdrq < '{endDate.AddDays(1).ToString("yyyy-MM-dd")} 00:00:00' | |
| 796 | - GROUP BY target.{deptField}"; | |
| 822 | + AND COALESCE(deduct.F_BillingTime, billing.kdrq) >= '{startDate.ToString("yyyy-MM-dd")} 00:00:00' | |
| 823 | + AND COALESCE(deduct.F_BillingTime, billing.kdrq) < '{endDate.AddDays(1).ToString("yyyy-MM-dd")} 00:00:00' | |
| 824 | + GROUP BY billing.djmd"; | |
| 797 | 825 | |
| 798 | 826 | var deductData = await _db.Ado.SqlQueryAsync<dynamic>(deductSql); |
| 799 | 827 | |
| 800 | - // 分配储扣金额到对应部门 | |
| 828 | + // 分配储扣金额到对应部门(使用targetDict确保每个门店只关联一条目标记录) | |
| 801 | 829 | foreach (var deduct in deductData ?? Enumerable.Empty<dynamic>()) |
| 802 | 830 | { |
| 803 | 831 | var storeDeductAmount = deduct?.StoreDeductAmount != null ? Convert.ToDecimal(deduct.StoreDeductAmount) : 0m; |
| 804 | - var targetDeptId = deduct?.TargetDeptId?.ToString(); | |
| 832 | + var storeId = deduct?.StoreId?.ToString(); | |
| 805 | 833 | |
| 806 | - if (storeDeductAmount <= 0 || string.IsNullOrEmpty(targetDeptId)) | |
| 834 | + if (storeDeductAmount <= 0 || string.IsNullOrEmpty(storeId)) | |
| 807 | 835 | continue; |
| 808 | 836 | |
| 809 | - // 只累加到在查询列表中的部门,避免重复统计 | |
| 810 | - if (departmentDict.ContainsKey(targetDeptId)) | |
| 837 | + // 从targetDict中获取门店对应的部门信息 | |
| 838 | + if (targetDict.ContainsKey(storeId)) | |
| 811 | 839 | { |
| 812 | - departmentDict[targetDeptId].DeductAmount += storeDeductAmount; | |
| 840 | + var target = targetDict[storeId]; | |
| 841 | + // 动态获取部门字段值 | |
| 842 | + string targetDeptId = null; | |
| 843 | + if (deptField == "F_EducationDepartment") | |
| 844 | + targetDeptId = target?.F_EducationDepartment?.ToString(); | |
| 845 | + else if (deptField == "F_TechDepartment") | |
| 846 | + targetDeptId = target?.F_TechDepartment?.ToString(); | |
| 847 | + else if (deptField == "F_MajorProjectDepartment") | |
| 848 | + targetDeptId = target?.F_MajorProjectDepartment?.ToString(); | |
| 849 | + | |
| 850 | + if (!string.IsNullOrEmpty(targetDeptId) && departmentDict.ContainsKey(targetDeptId)) | |
| 851 | + { | |
| 852 | + departmentDict[targetDeptId].DeductAmount += storeDeductAmount; | |
| 853 | + } | |
| 813 | 854 | } |
| 814 | 855 | } |
| 815 | 856 | } |
| ... | ... | @@ -1801,6 +1842,7 @@ namespace NCC.Extend |
| 1801 | 1842 | var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime); |
| 1802 | 1843 | |
| 1803 | 1844 | // 统计储扣金额,按品项分类分组 |
| 1845 | + // 使用储扣记录表中的开单时间(F_BillingTime)进行时间过滤,如果为空则使用开单记录表的开单时间(kdrq) | |
| 1804 | 1846 | var sql = $@" |
| 1805 | 1847 | SELECT |
| 1806 | 1848 | COALESCE(SUM(CASE WHEN item.qt2 = '医美' THEN deduct.F_Amount ELSE 0 END), 0) as YiMeiAmount, |
| ... | ... | @@ -1813,8 +1855,8 @@ namespace NCC.Extend |
| 1813 | 1855 | LEFT JOIN lq_xmzl item ON deduct.F_ItemId = item.F_Id AND item.F_IsEffective = 1 |
| 1814 | 1856 | WHERE deduct.F_IsEffective = 1 |
| 1815 | 1857 | AND billing.F_IsEffective = 1 |
| 1816 | - AND billing.kdrq >= '{startDate:yyyy-MM-dd} 00:00:00' | |
| 1817 | - AND billing.kdrq < '{endDate.AddDays(1):yyyy-MM-dd} 00:00:00'"; | |
| 1858 | + AND COALESCE(deduct.F_BillingTime, billing.kdrq) >= '{startDate:yyyy-MM-dd} 00:00:00' | |
| 1859 | + AND COALESCE(deduct.F_BillingTime, billing.kdrq) < '{endDate.AddDays(1):yyyy-MM-dd} 00:00:00'"; | |
| 1818 | 1860 | |
| 1819 | 1861 | var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); |
| 1820 | 1862 | var data = result.FirstOrDefault(); | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqDirectorSalaryService.cs
| ... | ... | @@ -296,6 +296,7 @@ namespace NCC.Extend |
| 296 | 296 | .ToDictionary(x => x.StoreId.ToString(), x => Convert.ToDecimal(x.ExpenseAmount ?? 0)); |
| 297 | 297 | |
| 298 | 298 | // 1.12 洗毛巾费用统计(只统计送出的记录,F_FlowType = 0) |
| 299 | + // 优先使用送出时间(F_SendTime),如果为空则使用创建时间(F_CreateTime) | |
| 299 | 300 | var laundryCostSql = $@" |
| 300 | 301 | SELECT |
| 301 | 302 | F_StoreId as StoreId, |
| ... | ... | @@ -303,7 +304,7 @@ namespace NCC.Extend |
| 303 | 304 | FROM lq_laundry_flow |
| 304 | 305 | WHERE F_IsEffective = 1 |
| 305 | 306 | AND F_FlowType = 0 |
| 306 | - AND DATE_FORMAT(F_CreateTime, '%Y%m') = @monthStr | |
| 307 | + AND DATE_FORMAT(COALESCE(F_SendTime, F_CreateTime), '%Y%m') = @monthStr | |
| 307 | 308 | GROUP BY F_StoreId"; |
| 308 | 309 | |
| 309 | 310 | var laundryCostData = await _db.Ado.SqlQueryAsync<dynamic>(laundryCostSql, new { monthStr }); | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqInventoryService.cs
| ... | ... | @@ -186,8 +186,8 @@ namespace NCC.Extend |
| 186 | 186 | _db.Ado.BeginTran(); |
| 187 | 187 | try |
| 188 | 188 | { |
| 189 | - var isOk = await _db.Insertable(inventoryEntity).ExecuteCommandAsync(); | |
| 190 | - if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); | |
| 189 | + var isOk = await _db.Insertable(inventoryEntity).ExecuteCommandAsync(); | |
| 190 | + if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); | |
| 191 | 191 | |
| 192 | 192 | // 计算并更新产品的平均单价(加权平均成本法) |
| 193 | 193 | // 普通入库和采购入库都需要更新平均单价 |
| ... | ... | @@ -398,8 +398,8 @@ namespace NCC.Extend |
| 398 | 398 | _db.Ado.BeginTran(); |
| 399 | 399 | try |
| 400 | 400 | { |
| 401 | - var isOk = await _db.Updateable(existingInventory).ExecuteCommandAsync(); | |
| 402 | - if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); | |
| 401 | + var isOk = await _db.Updateable(existingInventory).ExecuteCommandAsync(); | |
| 402 | + if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); | |
| 403 | 403 | |
| 404 | 404 | // 如果数量或单价发生变化,需要重新计算平均单价 |
| 405 | 405 | // 注意:更新库存时,如果数量或单价变化,需要重新计算整个产品的平均单价 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs
| ... | ... | @@ -503,6 +503,7 @@ namespace NCC.Extend |
| 503 | 503 | productName = product.ProductName, |
| 504 | 504 | productCategory = product.ProductCategory, |
| 505 | 505 | productPrice = product.Price, |
| 506 | + productWarehouse = product.Warehouse, // 产品归属仓库 | |
| 506 | 507 | storeId = u.StoreId, |
| 507 | 508 | storeName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(store => store.Id == u.StoreId).Select(store => store.Dm), |
| 508 | 509 | usageTime = u.UsageTime, |
| ... | ... | @@ -648,6 +649,7 @@ namespace NCC.Extend |
| 648 | 649 | productName = product.ProductName, |
| 649 | 650 | productCategory = product.ProductCategory, |
| 650 | 651 | productPrice = product.Price, |
| 652 | + productWarehouse = product.Warehouse, // 产品归属仓库 | |
| 651 | 653 | storeId = u.StoreId, |
| 652 | 654 | storeName = SqlFunc.Subqueryable<LqMdxxEntity>().Where(store => store.Id == u.StoreId).Select(store => store.Dm), |
| 653 | 655 | usageTime = u.UsageTime, | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
| ... | ... | @@ -897,6 +897,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 897 | 897 | { |
| 898 | 898 | Id = YitIdHelper.NextId().ToString(), |
| 899 | 899 | BillingId = newEntity.Id, |
| 900 | + BillingTime = newEntity.Kdrq, // 设置开单时间 | |
| 900 | 901 | DeductId = item.DeductId, |
| 901 | 902 | DeductType = item.DeductType, |
| 902 | 903 | Amount = item.Amount, |
| ... | ... | @@ -1243,6 +1244,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 1243 | 1244 | { |
| 1244 | 1245 | Id = YitIdHelper.NextId().ToString(), |
| 1245 | 1246 | BillingId = id, |
| 1247 | + BillingTime = entity.Kdrq, // 设置开单时间 | |
| 1246 | 1248 | DeductId = item.DeductId, |
| 1247 | 1249 | DeductType = item.DeductType, |
| 1248 | 1250 | Amount = item.Amount, |
| ... | ... | @@ -1252,7 +1254,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 1252 | 1254 | ItemId = item.ItemId, |
| 1253 | 1255 | IsEffective = StatusEnum.有效.GetHashCode(), // 设置为有效 |
| 1254 | 1256 | CreateTime = DateTime.Now, // 设置创建时间 |
| 1255 | - ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.DeductId).Select(x => x.Qt2).FirstAsync() | |
| 1257 | + ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.ItemId).Select(x => x.Qt2).FirstAsync() // 修复:使用 ItemId 而不是 DeductId | |
| 1256 | 1258 | }; |
| 1257 | 1259 | allDeductEntities.Add(lqKdDeductEntity); |
| 1258 | 1260 | } |
| ... | ... | @@ -2292,6 +2294,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 2292 | 2294 | Sfyj = input.Sfyj, |
| 2293 | 2295 | DeductAmount = input.DeductAmount, |
| 2294 | 2296 | Qk = input.Qk, |
| 2297 | + Bz = input.Remark, | |
| 2295 | 2298 | UpdateTime = DateTime.Now |
| 2296 | 2299 | }).Where(w => w.Id == input.BillingId).ExecuteCommandAsync(); |
| 2297 | 2300 | |
| ... | ... | @@ -3791,11 +3794,19 @@ namespace NCC.Extend.LqKdKdjlb |
| 3791 | 3794 | } |
| 3792 | 3795 | |
| 3793 | 3796 | // 批量查询开单记录,获取门店ID |
| 3797 | + // 批量查询开单记录,获取门店ID及扩展信息 | |
| 3794 | 3798 | var billingStoreDict = new Dictionary<string, string>(); |
| 3799 | + var billingExtraInfoDict = new Dictionary<string, dynamic>(); | |
| 3800 | + | |
| 3795 | 3801 | if (billingIds.Any()) |
| 3796 | 3802 | { |
| 3797 | - var billings = await _db.Queryable<LqKdKdjlbEntity>().Where(x => billingIds.Contains(x.Id)).Select(x => new { x.Id, x.Djmd }).ToListAsync(); | |
| 3803 | + var billings = await _db.Queryable<LqKdKdjlbEntity>() | |
| 3804 | + .Where(x => billingIds.Contains(x.Id)) | |
| 3805 | + .Select(x => new { x.Id, x.Djmd, x.Hgjg, x.Fkyy }) | |
| 3806 | + .ToListAsync(); | |
| 3807 | + | |
| 3798 | 3808 | billingStoreDict = billings.ToDictionary(x => x.Id, x => x.Djmd ?? ""); |
| 3809 | + billingExtraInfoDict = billings.ToDictionary(x => x.Id, x => (dynamic)new { Hgjg = x.Hgjg, Fkyy = x.Fkyy }); | |
| 3799 | 3810 | } |
| 3800 | 3811 | |
| 3801 | 3812 | // 批量查询门店信息 |
| ... | ... | @@ -3823,7 +3834,9 @@ namespace NCC.Extend.LqKdKdjlb |
| 3823 | 3834 | sourceType = pxmx.SourceType, |
| 3824 | 3835 | remark = pxmx.Remark, |
| 3825 | 3836 | storeId = pxmx.Glkdbh != null && billingStoreDict.ContainsKey(pxmx.Glkdbh) ? billingStoreDict[pxmx.Glkdbh] : "", |
| 3826 | - storeName = pxmx.Glkdbh != null && billingStoreDict.ContainsKey(pxmx.Glkdbh) && !string.IsNullOrEmpty(billingStoreDict[pxmx.Glkdbh]) && storeDict.ContainsKey(billingStoreDict[pxmx.Glkdbh]) ? storeDict[billingStoreDict[pxmx.Glkdbh]] : "" | |
| 3837 | + storeName = pxmx.Glkdbh != null && billingStoreDict.ContainsKey(pxmx.Glkdbh) && !string.IsNullOrEmpty(billingStoreDict[pxmx.Glkdbh]) && storeDict.ContainsKey(billingStoreDict[pxmx.Glkdbh]) ? storeDict[billingStoreDict[pxmx.Glkdbh]] : "", | |
| 3838 | + hgjg = pxmx.Glkdbh != null && billingExtraInfoDict.ContainsKey(pxmx.Glkdbh) ? billingExtraInfoDict[pxmx.Glkdbh].Hgjg : "", | |
| 3839 | + fkyy = pxmx.Glkdbh != null && billingExtraInfoDict.ContainsKey(pxmx.Glkdbh) ? billingExtraInfoDict[pxmx.Glkdbh].Fkyy : "" | |
| 3827 | 3840 | }).ToList(); |
| 3828 | 3841 | |
| 3829 | 3842 | // 6. 返回分页结果 |
| ... | ... | @@ -3915,6 +3928,7 @@ namespace NCC.Extend.LqKdKdjlb |
| 3915 | 3928 | |
| 3916 | 3929 | |
| 3917 | 3930 | // 查询并分页,使用子查询获取开单类型 |
| 3931 | + // 优先使用储扣记录表中的开单时间,如果为空则使用开单记录表中的开单时间 | |
| 3918 | 3932 | var data = await baseQuery.Select((deduct, billing, member, store) => new LqKdDeductinfoListOutput |
| 3919 | 3933 | { |
| 3920 | 3934 | Id = deduct.Id ?? "", |
| ... | ... | @@ -3929,13 +3943,13 @@ namespace NCC.Extend.LqKdKdjlb |
| 3929 | 3943 | CreateTime = deduct.CreateTime, |
| 3930 | 3944 | ProjectNumber = deduct.ProjectNumber, |
| 3931 | 3945 | ItemCategory = deduct.ItemCategory ?? "", |
| 3932 | - BillingDate = billing.Kdrq, | |
| 3946 | + BillingDate = deduct.BillingTime ?? billing.Kdrq, // 优先使用储扣记录表中的开单时间 | |
| 3933 | 3947 | MemberId = billing.Kdhy ?? "", |
| 3934 | 3948 | MemberName = member.Khmc ?? "", |
| 3935 | 3949 | MemberPhone = member.Sjh ?? "", |
| 3936 | 3950 | StoreId = billing.Djmd ?? "", |
| 3937 | 3951 | StoreName = store.Dm ?? "", |
| 3938 | - TimePeriod = billing.Kdrq, | |
| 3952 | + TimePeriod = deduct.BillingTime ?? billing.Kdrq, // 优先使用储扣记录表中的开单时间 | |
| 3939 | 3953 | BillingType = SqlFunc.Subqueryable<LqKdPxmxEntity>() |
| 3940 | 3954 | .Where(pxmx => pxmx.Id == deduct.DeductId && pxmx.Px == deduct.ItemId) |
| 3941 | 3955 | .Select(pxmx => pxmx.SourceType), | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqLaundryFlowService.cs
| ... | ... | @@ -115,7 +115,8 @@ namespace NCC.Extend |
| 115 | 115 | Remark = input.Remark, |
| 116 | 116 | IsEffective = StatusEnum.有效.GetHashCode(), |
| 117 | 117 | CreateUser = _userManager.UserId, |
| 118 | - CreateTime = DateTime.Now | |
| 118 | + CreateTime = DateTime.Now, | |
| 119 | + SendTime = DateTime.Now // 设置送出时间 | |
| 119 | 120 | }; |
| 120 | 121 | |
| 121 | 122 | var isOk = await _db.Insertable(entity).ExecuteCommandAsync(); |
| ... | ... | @@ -212,7 +213,8 @@ namespace NCC.Extend |
| 212 | 213 | Remark = input.Remark, |
| 213 | 214 | IsEffective = StatusEnum.有效.GetHashCode(), |
| 214 | 215 | CreateUser = _userManager.UserId, |
| 215 | - CreateTime = DateTime.Now | |
| 216 | + CreateTime = DateTime.Now, | |
| 217 | + ReturnTime = DateTime.Now // 设置送回时间 | |
| 216 | 218 | }; |
| 217 | 219 | |
| 218 | 220 | var isOk = await _db.Insertable(entity).ExecuteCommandAsync(); |
| ... | ... | @@ -228,6 +230,109 @@ namespace NCC.Extend |
| 228 | 230 | } |
| 229 | 231 | #endregion |
| 230 | 232 | |
| 233 | + #region 修改送出/送回记录 | |
| 234 | + /// <summary> | |
| 235 | + /// 修改送出/送回记录 | |
| 236 | + /// </summary> | |
| 237 | + /// <remarks> | |
| 238 | + /// 修改清洗流水记录的数量、送出时间、送回时间和备注 | |
| 239 | + /// | |
| 240 | + /// 示例请求: | |
| 241 | + /// ```json | |
| 242 | + /// { | |
| 243 | + /// "id": "记录ID", | |
| 244 | + /// "quantity": 95, | |
| 245 | + /// "sendTime": "2025-11-01 10:00:00", | |
| 246 | + /// "returnTime": "2025-11-05 15:00:00", | |
| 247 | + /// "remark": "修改备注" | |
| 248 | + /// } | |
| 249 | + /// ``` | |
| 250 | + /// | |
| 251 | + /// 参数说明: | |
| 252 | + /// - id: 记录ID(必填) | |
| 253 | + /// - quantity: 数量(可选,修改时需重新计算总费用) | |
| 254 | + /// - sendTime: 送出时间(可选,仅流水类型为0时有效) | |
| 255 | + /// - returnTime: 送回时间(可选,仅流水类型为1时有效) | |
| 256 | + /// - remark: 备注(可选) | |
| 257 | + /// </remarks> | |
| 258 | + /// <param name="input">修改输入</param> | |
| 259 | + /// <returns>修改结果</returns> | |
| 260 | + /// <response code="200">修改成功</response> | |
| 261 | + /// <response code="400">记录不存在或参数错误</response> | |
| 262 | + /// <response code="500">服务器错误</response> | |
| 263 | + [HttpPost("Update")] | |
| 264 | + public async Task<dynamic> UpdateAsync([FromBody] LqLaundryFlowUpdateInput input) | |
| 265 | + { | |
| 266 | + try | |
| 267 | + { | |
| 268 | + // 查询记录是否存在 | |
| 269 | + var entity = await _db.Queryable<LqLaundryFlowEntity>() | |
| 270 | + .Where(x => x.Id == input.Id && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 271 | + .FirstAsync(); | |
| 272 | + | |
| 273 | + if (entity == null) | |
| 274 | + { | |
| 275 | + throw NCCException.Oh("记录不存在或已失效"); | |
| 276 | + } | |
| 277 | + | |
| 278 | + // 更新数量(如果提供) | |
| 279 | + if (input.Quantity.HasValue) | |
| 280 | + { | |
| 281 | + entity.Quantity = input.Quantity.Value; | |
| 282 | + // 重新计算总费用 | |
| 283 | + entity.TotalPrice = entity.Quantity * entity.LaundryPrice; | |
| 284 | + } | |
| 285 | + | |
| 286 | + // 更新送出时间(仅流水类型为0时有效) | |
| 287 | + if (input.SendTime.HasValue) | |
| 288 | + { | |
| 289 | + if (entity.FlowType != 0) | |
| 290 | + { | |
| 291 | + throw NCCException.Oh("只有送出记录才能修改送出时间"); | |
| 292 | + } | |
| 293 | + entity.SendTime = input.SendTime.Value; | |
| 294 | + } | |
| 295 | + | |
| 296 | + // 更新送回时间(仅流水类型为1时有效) | |
| 297 | + if (input.ReturnTime.HasValue) | |
| 298 | + { | |
| 299 | + if (entity.FlowType != 1) | |
| 300 | + { | |
| 301 | + throw NCCException.Oh("只有送回记录才能修改送回时间"); | |
| 302 | + } | |
| 303 | + entity.ReturnTime = input.ReturnTime.Value; | |
| 304 | + } | |
| 305 | + | |
| 306 | + // 更新备注(如果提供) | |
| 307 | + if (input.Remark != null) | |
| 308 | + { | |
| 309 | + entity.Remark = input.Remark; | |
| 310 | + } | |
| 311 | + | |
| 312 | + // 执行更新 | |
| 313 | + var isOk = await _db.Updateable(entity) | |
| 314 | + .UpdateColumns(it => new | |
| 315 | + { | |
| 316 | + it.Quantity, | |
| 317 | + it.TotalPrice, | |
| 318 | + it.SendTime, | |
| 319 | + it.ReturnTime, | |
| 320 | + it.Remark | |
| 321 | + }) | |
| 322 | + .ExecuteCommandAsync(); | |
| 323 | + | |
| 324 | + if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); | |
| 325 | + | |
| 326 | + return new { message = "修改成功", totalPrice = entity.TotalPrice }; | |
| 327 | + } | |
| 328 | + catch (Exception ex) | |
| 329 | + { | |
| 330 | + _logger.LogError(ex, "修改清洗流水记录失败"); | |
| 331 | + throw NCCException.Oh($"修改失败:{ex.Message}"); | |
| 332 | + } | |
| 333 | + } | |
| 334 | + #endregion | |
| 335 | + | |
| 231 | 336 | #region 获取清洗流水列表 |
| 232 | 337 | /// <summary> |
| 233 | 338 | /// 获取清洗流水列表 |
| ... | ... | @@ -275,7 +380,9 @@ namespace NCC.Extend |
| 275 | 380 | isEffective = flow.IsEffective, |
| 276 | 381 | createUser = flow.CreateUser, |
| 277 | 382 | createUserName = "", |
| 278 | - createTime = flow.CreateTime | |
| 383 | + createTime = flow.CreateTime, | |
| 384 | + sendTime = flow.SendTime, | |
| 385 | + returnTime = flow.ReturnTime | |
| 279 | 386 | }) |
| 280 | 387 | .MergeTable() |
| 281 | 388 | .OrderBy(sidx + " " + sort) | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqReimbursementApplicationService.cs
| ... | ... | @@ -1515,8 +1515,8 @@ namespace NCC.Extend.LqReimbursementApplication |
| 1515 | 1515 | // 查询本月已审核通过的报销申请 |
| 1516 | 1516 | var applications = await _db.Queryable<LqReimbursementApplicationEntity>() |
| 1517 | 1517 | .Where(x => (x.ApprovalStatus ?? x.ApproveStatus) == "已通过") |
| 1518 | - .Where(x => x.ApplicationTime.HasValue && | |
| 1519 | - x.ApplicationTime.Value.Year == queryYear && | |
| 1518 | + .Where(x => x.ApplicationTime.HasValue && | |
| 1519 | + x.ApplicationTime.Value.Year == queryYear && | |
| 1520 | 1520 | x.ApplicationTime.Value.Month == int.Parse(queryMonth)) |
| 1521 | 1521 | .ToListAsync(); |
| 1522 | 1522 | |
| ... | ... | @@ -1552,7 +1552,7 @@ namespace NCC.Extend.LqReimbursementApplication |
| 1552 | 1552 | foreach (var app in applications) |
| 1553 | 1553 | { |
| 1554 | 1554 | var appPurchaseRecords = purchaseRecords.Where(x => x.ApplicationId == app.Id).ToList(); |
| 1555 | - | |
| 1555 | + | |
| 1556 | 1556 | if (appPurchaseRecords.Any()) |
| 1557 | 1557 | { |
| 1558 | 1558 | // 每个购买记录作为一行 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs
| ... | ... | @@ -211,16 +211,31 @@ namespace NCC.Extend |
| 211 | 211 | throw new Exception($"第{i + 1}行:月份必须在1-12之间"); |
| 212 | 212 | } |
| 213 | 213 | |
| 214 | + // 辅助方法:清理数值字符串(去除千分位分隔符、货币符号等) | |
| 215 | + Func<string, string> CleanNumericString = (str) => | |
| 216 | + { | |
| 217 | + if (string.IsNullOrWhiteSpace(str)) | |
| 218 | + return "0"; | |
| 219 | + // 去除常见的非数字字符(保留小数点和负号) | |
| 220 | + return str.Trim() | |
| 221 | + .Replace(",", "") // 去除千分位分隔符 | |
| 222 | + .Replace(",", "") // 去除中文逗号 | |
| 223 | + .Replace("¥", "") // 去除人民币符号 | |
| 224 | + .Replace("$", "") // 去除美元符号 | |
| 225 | + .Replace("元", "") // 去除"元"字 | |
| 226 | + .Replace(" ", ""); // 去除空格 | |
| 227 | + }; | |
| 228 | + | |
| 214 | 229 | // 解析数值字段(允许为空,默认为0) |
| 215 | - decimal.TryParse(baseRewardPerformanceText, out decimal baseRewardPerformance); | |
| 216 | - decimal.TryParse(cooperationRewardPerformanceText, out decimal cooperationRewardPerformance); | |
| 217 | - decimal.TryParse(newCustomerPerformanceText, out decimal newCustomerPerformance); | |
| 218 | - decimal.TryParse(newCustomerConversionRateText, out decimal newCustomerConversionRate); | |
| 219 | - decimal.TryParse(upgradePerformanceText, out decimal upgradePerformance); | |
| 220 | - decimal.TryParse(upgradeConversionRateText, out decimal upgradeConversionRate); | |
| 221 | - decimal.TryParse(upgradeCustomerCountText, out decimal upgradeCustomerCount); | |
| 222 | - decimal.TryParse(otherPerformanceAddText, out decimal otherPerformanceAdd); | |
| 223 | - decimal.TryParse(otherPerformanceSubtractText, out decimal otherPerformanceSubtract); | |
| 230 | + decimal.TryParse(CleanNumericString(baseRewardPerformanceText), out decimal baseRewardPerformance); | |
| 231 | + decimal.TryParse(CleanNumericString(cooperationRewardPerformanceText), out decimal cooperationRewardPerformance); | |
| 232 | + decimal.TryParse(CleanNumericString(newCustomerPerformanceText), out decimal newCustomerPerformance); | |
| 233 | + decimal.TryParse(CleanNumericString(newCustomerConversionRateText), out decimal newCustomerConversionRate); | |
| 234 | + decimal.TryParse(CleanNumericString(upgradePerformanceText), out decimal upgradePerformance); | |
| 235 | + decimal.TryParse(CleanNumericString(upgradeConversionRateText), out decimal upgradeConversionRate); | |
| 236 | + decimal.TryParse(CleanNumericString(upgradeCustomerCountText), out decimal upgradeCustomerCount); | |
| 237 | + decimal.TryParse(CleanNumericString(otherPerformanceAddText), out decimal otherPerformanceAdd); | |
| 238 | + decimal.TryParse(CleanNumericString(otherPerformanceSubtractText), out decimal otherPerformanceSubtract); | |
| 224 | 239 | |
| 225 | 240 | var item = new SalaryExtraCalculationImportInput |
| 226 | 241 | { |
| ... | ... | @@ -411,7 +426,22 @@ namespace NCC.Extend |
| 411 | 426 | // 批量更新现有记录 |
| 412 | 427 | if (entitiesToUpdate.Any()) |
| 413 | 428 | { |
| 414 | - await _db.Updateable(entitiesToUpdate).ExecuteCommandAsync(); | |
| 429 | + // 明确指定要更新的字段,确保所有字段都被更新(包括升单业绩) | |
| 430 | + // 注意:Updateable接收实体列表时,会自动根据主键更新,不需要Where条件 | |
| 431 | + await _db.Updateable(entitiesToUpdate) | |
| 432 | + .UpdateColumns(it => new | |
| 433 | + { | |
| 434 | + it.BaseRewardPerformance, | |
| 435 | + it.CooperationRewardPerformance, | |
| 436 | + it.NewCustomerPerformance, | |
| 437 | + it.NewCustomerConversionRate, | |
| 438 | + it.UpgradePerformance, | |
| 439 | + it.UpgradeConversionRate, | |
| 440 | + it.UpgradeCustomerCount, | |
| 441 | + it.OtherPerformanceAdd, | |
| 442 | + it.OtherPerformanceSubtract | |
| 443 | + }) | |
| 444 | + .ExecuteCommandAsync(); | |
| 415 | 445 | } |
| 416 | 446 | |
| 417 | 447 | var result = new | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
| ... | ... | @@ -623,8 +623,15 @@ namespace NCC.Extend |
| 623 | 623 | isNewStore); |
| 624 | 624 | |
| 625 | 625 | // 4.2 提成计算 |
| 626 | - // 业绩门槛: 战队成员个人总业绩 <= 6000 无提成 | |
| 627 | - if (!string.IsNullOrEmpty(salary.GoldTriangleId) && salary.TotalPerformance <= 6000) | |
| 626 | + // 业绩门槛: 战队成员个人总业绩 <= 6000 无提成 (需按日均计算) | |
| 627 | + // 规则:战队成员日均业绩 <= 6000 / 当月天数 -> 无提成 | |
| 628 | + decimal memberThreshold = 6000m; | |
| 629 | + if (daysInMonth > 0 && salary.WorkingDays > 0) | |
| 630 | + { | |
| 631 | + memberThreshold = (6000m / daysInMonth) * salary.WorkingDays; | |
| 632 | + } | |
| 633 | + | |
| 634 | + if (!string.IsNullOrEmpty(salary.GoldTriangleId) && salary.TotalPerformance < memberThreshold) // 修正为小于校验 | |
| 628 | 635 | { |
| 629 | 636 | salary.TotalCommission = 0; |
| 630 | 637 | salary.BasePerformanceCommission = 0; |
| ... | ... | @@ -644,13 +651,15 @@ namespace NCC.Extend |
| 644 | 651 | // 获取战队人数 (注意:这里应该是有效战队人数) |
| 645 | 652 | var teamMemberCount = employeeStats.Values.Count(x => x.GoldTriangleId == salary.GoldTriangleId); |
| 646 | 653 | // 注意:提成点按原始基础业绩计算,不是实际基础业绩 |
| 647 | - commissionPoint = GetTeamCommissionPoint(teamMemberCount, salary.TeamPerformance); | |
| 654 | + // 战队成员不按日均考核提成点,只考核个人门槛 | |
| 655 | + commissionPoint = GetTeamCommissionPoint(teamMemberCount, salary.TeamPerformance, daysInMonth, salary.WorkingDays); | |
| 648 | 656 | } |
| 649 | 657 | else |
| 650 | 658 | { |
| 651 | 659 | // 单人 (或被剔除出战队) |
| 652 | 660 | // 注意:提成点按原始总业绩计算 |
| 653 | - commissionPoint = GetTeamCommissionPoint(1, salary.TotalPerformance); | |
| 661 | + // 单人按日均考核提成点 | |
| 662 | + commissionPoint = GetTeamCommissionPoint(1, salary.TotalPerformance, daysInMonth, salary.WorkingDays); | |
| 654 | 663 | } |
| 655 | 664 | |
| 656 | 665 | salary.CommissionPoint = commissionPoint; |
| ... | ... | @@ -697,6 +706,17 @@ namespace NCC.Extend |
| 697 | 706 | if (!string.IsNullOrEmpty(salary.EmployeeName) && salary.EmployeeName.Contains("T区")) |
| 698 | 707 | { |
| 699 | 708 | salary.StoreTZoneCommission = salary.StoreTotalPerformance * 0.05m * 0.05m; |
| 709 | + | |
| 710 | + // T区人员仅核算提成,其他项(底薪、手工、社保等)归零 | |
| 711 | + salary.HealthCoachBaseSalary = 0; | |
| 712 | + salary.HandworkFee = 0; | |
| 713 | + salary.BasePerformanceCommission = 0; | |
| 714 | + salary.CooperationPerformanceCommission = 0; | |
| 715 | + salary.ConsultantCommission = 0; | |
| 716 | + salary.NewCustomerPerformanceCommission = 0; | |
| 717 | + salary.UpgradePerformanceCommission = 0; | |
| 718 | + salary.TotalSubsidy = 0; | |
| 719 | + salary.TotalDeduction = 0; | |
| 700 | 720 | } |
| 701 | 721 | |
| 702 | 722 | salary.TotalCommission = salary.BasePerformanceCommission |
| ... | ... | @@ -801,7 +821,7 @@ namespace NCC.Extend |
| 801 | 821 | /// <summary> |
| 802 | 822 | /// 获取战队提成点 |
| 803 | 823 | /// </summary> |
| 804 | - private decimal GetTeamCommissionPoint(int memberCount, decimal teamPerformance) | |
| 824 | + private decimal GetTeamCommissionPoint(int memberCount, decimal teamPerformance, int daysInMonth, decimal workingDays) | |
| 805 | 825 | { |
| 806 | 826 | if (memberCount >= 3) |
| 807 | 827 | { |
| ... | ... | @@ -820,10 +840,24 @@ namespace NCC.Extend |
| 820 | 840 | } |
| 821 | 841 | else // 1人 |
| 822 | 842 | { |
| 823 | - if (teamPerformance >= 60000) return 0.06m; | |
| 824 | - if (teamPerformance >= 40000) return 0.05m; | |
| 825 | - if (teamPerformance >= 20000) return 0.04m; | |
| 826 | - if (teamPerformance >= 10000) return 0.03m; | |
| 843 | + // 单人按照日均考核 | |
| 844 | + decimal p1 = 60000m; | |
| 845 | + decimal p2 = 40000m; | |
| 846 | + decimal p3 = 20000m; | |
| 847 | + decimal p4 = 10000m; | |
| 848 | + | |
| 849 | + if (daysInMonth > 0 && workingDays > 0) | |
| 850 | + { | |
| 851 | + p1 = (p1 / daysInMonth) * workingDays; | |
| 852 | + p2 = (p2 / daysInMonth) * workingDays; | |
| 853 | + p3 = (p3 / daysInMonth) * workingDays; | |
| 854 | + p4 = (p4 / daysInMonth) * workingDays; | |
| 855 | + } | |
| 856 | + | |
| 857 | + if (teamPerformance >= p1) return 0.06m; | |
| 858 | + if (teamPerformance >= p2) return 0.05m; | |
| 859 | + if (teamPerformance >= p3) return 0.04m; | |
| 860 | + if (teamPerformance >= p4) return 0.03m; | |
| 827 | 861 | } |
| 828 | 862 | return 0; |
| 829 | 863 | } |
| ... | ... | @@ -839,21 +873,23 @@ namespace NCC.Extend |
| 839 | 873 | |
| 840 | 874 | // 注意: |
| 841 | 875 | // 1. "组员业绩"指除顾问外的其他成员业绩总和 |
| 842 | - // 2. 只统计有效战队成员(考勤≥21天,未被剔除的成员) | |
| 876 | + // 2. 只统计有效战队成员(考勤≥20天,未被剔除的成员) | |
| 843 | 877 | // 3. "达到X%以上"指:组员业绩总和 ≥ 团队总业绩 × X% |
| 844 | 878 | // 4. 新店顾问不考核消耗 |
| 879 | + // 5. 消耗达标:高级顾问整组消耗>=6万,普通顾问整组消耗>=4万 | |
| 845 | 880 | |
| 846 | - // 使用传入的 teamMembers 计算总消耗,或者直接使用 TeamTotalConsumption (如果已计算) | |
| 847 | - // 但为了保险起见,这里重新计算或使用已有的逻辑 | |
| 848 | - // 注意:CalculateConsultantCommission 方法签名未变,但逻辑需确认 teamConsumption 来源 | |
| 849 | - // 在调用此方法前,teamMembers 已经有了 Consumption 数据 | |
| 881 | + // 使用传入的 teamMembers 计算总消耗 | |
| 850 | 882 | var teamConsumption = teamMembers.Sum(x => x.Consumption); |
| 851 | 883 | |
| 852 | 884 | // 计算组员(非顾问)业绩总和 |
| 853 | - // teamMembers 已经是过滤后的有效成员列表(GoldTriangleId 相同且未被剔除) | |
| 854 | 885 | var memberPerformance = teamMembers.Where(x => x.Position != "顾问").Sum(x => x.TotalPerformance); |
| 855 | 886 | |
| 856 | - // 高级顾问:业绩≥6万 且 组员业绩≥40% 且 (新店 或 消耗≥6万) | |
| 887 | + // 高级顾问:业绩≥6万 且 组员业绩≥40% 且 (新店(第1,2阶段) 或 消耗≥6万) | |
| 888 | + // 注意:isNewStore 仅代表是否为新店,具体免考核阶段需确认。假设新店前两个阶段免考核,第三阶段需考核。 | |
| 889 | + // 这里暂且沿用 isNewStore 逻辑,如果需要更细粒度控制,应传入 NewStoreProtectionStage | |
| 890 | + // 如果 isNewStore 为 true,则默认免考核消耗(根据原需求描述:新店第3个阶段时,有金三角,但是不考核消耗) | |
| 891 | + // 用户最新指示:新店第3个阶段时,有金三角,但是不考核消耗,默认达标 -> 意味着只要是新店,不管阶段,都不考核消耗 | |
| 892 | + | |
| 857 | 893 | if (teamPerformance >= 60000 && memberPerformance >= teamPerformance * 0.4m) |
| 858 | 894 | { |
| 859 | 895 | if (isNewStore || teamConsumption >= 60000) | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqShareStatisticsHqService.cs
0 → 100644
| 1 | +using Microsoft.AspNetCore.Mvc; | |
| 2 | +using NCC.Common.Filter; | |
| 3 | +using NCC.Dependency; | |
| 4 | +using NCC.DynamicApiController; | |
| 5 | +using NCC.Extend.Entitys.Dto.LqShareStatisticsHq; | |
| 6 | +using NCC.Extend.Entitys.lq_share_statistics_hq; | |
| 7 | +using NCC.Extend.Entitys.lq_kd_kdjlb; | |
| 8 | +using NCC.Extend.Entitys.lq_hytk_hytk; | |
| 9 | +using NCC.Extend.Entitys.lq_kd_pxmx; | |
| 10 | +using NCC.Extend.Entitys.lq_contract_rent_detail; | |
| 11 | +using SqlSugar; | |
| 12 | +using System; | |
| 13 | +using System.Linq; | |
| 14 | +using System.Threading.Tasks; | |
| 15 | +using Yitter.IdGenerator; | |
| 16 | + | |
| 17 | +namespace NCC.Extend | |
| 18 | +{ | |
| 19 | + /// <summary> | |
| 20 | + /// 总部股份统计服务 | |
| 21 | + /// </summary> | |
| 22 | + [ApiDescriptionSettings(Tag = "总部股份统计服务", Name = "LqShareStatisticsHq", Order = 402)] | |
| 23 | + [Route("api/Extend/[controller]")] | |
| 24 | + public class LqShareStatisticsHqService : IDynamicApiController, ITransient | |
| 25 | + { | |
| 26 | + private readonly ISqlSugarClient _db; | |
| 27 | + | |
| 28 | + public LqShareStatisticsHqService(ISqlSugarClient db) | |
| 29 | + { | |
| 30 | + _db = db; | |
| 31 | + } | |
| 32 | + | |
| 33 | + /// <summary> | |
| 34 | + /// 生成总部股份统计数据 | |
| 35 | + /// </summary> | |
| 36 | + /// <param name="input">生成参数</param> | |
| 37 | + /// <returns>生成结果</returns> | |
| 38 | + [HttpPost("generate")] | |
| 39 | + public async Task<dynamic> GenerateStatistics([FromBody] ShareStatisticsHqGenerateInput input) | |
| 40 | + { | |
| 41 | + if (string.IsNullOrEmpty(input.StatisticsMonth) || input.StatisticsMonth.Length != 6) | |
| 42 | + { | |
| 43 | + return new { code = 500, msg = "统计月份格式错误,应为 YYYYMM" }; | |
| 44 | + } | |
| 45 | + | |
| 46 | + var year = int.Parse(input.StatisticsMonth.Substring(0, 4)); | |
| 47 | + var month = int.Parse(input.StatisticsMonth.Substring(4, 2)); | |
| 48 | + var startDate = new DateTime(year, month, 1); | |
| 49 | + var endDate = startDate.AddMonths(1).AddDays(-1); | |
| 50 | + | |
| 51 | + // 检查是否已存在 | |
| 52 | + var existing = await _db.Queryable<LqShareStatisticsHqEntity>() | |
| 53 | + .FirstAsync(x => x.StatisticsMonth == input.StatisticsMonth); | |
| 54 | + | |
| 55 | + var entity = existing ?? new LqShareStatisticsHqEntity | |
| 56 | + { | |
| 57 | + Id = YitIdHelper.NextId().ToString(), | |
| 58 | + StatisticsMonth = input.StatisticsMonth, | |
| 59 | + IsEffective = 1, | |
| 60 | + CreateTime = DateTime.Now, | |
| 61 | + CreateUser = "System" | |
| 62 | + }; | |
| 63 | + | |
| 64 | + // 计算各项数据 | |
| 65 | + await CalculateIncome(entity, startDate, endDate); | |
| 66 | + await CalculateCost(entity, startDate, endDate, input.StatisticsMonth); | |
| 67 | + CalculateProfit(entity); | |
| 68 | + | |
| 69 | + entity.UpdateTime = DateTime.Now; | |
| 70 | + entity.UpdateUser = "System"; | |
| 71 | + | |
| 72 | + if (existing == null) | |
| 73 | + { | |
| 74 | + await _db.Insertable(entity).ExecuteCommandAsync(); | |
| 75 | + return new { code = 200, msg = "生成成功", data = new { generated = true } }; | |
| 76 | + } | |
| 77 | + else | |
| 78 | + { | |
| 79 | + await _db.Updateable(entity).ExecuteCommandAsync(); | |
| 80 | + return new { code = 200, msg = "更新成功", data = new { updated = true } }; | |
| 81 | + } | |
| 82 | + } | |
| 83 | + | |
| 84 | + /// <summary> | |
| 85 | + /// 查询总部股份统计列表 | |
| 86 | + /// </summary> | |
| 87 | + /// <param name="input">查询参数</param> | |
| 88 | + /// <returns>统计列表</returns> | |
| 89 | + [HttpGet("list")] | |
| 90 | + public async Task<dynamic> GetList([FromQuery] ShareStatisticsHqQueryInput input) | |
| 91 | + { | |
| 92 | + var query = _db.Queryable<LqShareStatisticsHqEntity>() | |
| 93 | + .Where(x => x.IsEffective == 1); | |
| 94 | + | |
| 95 | + if (!string.IsNullOrEmpty(input.StatisticsMonth)) | |
| 96 | + { | |
| 97 | + query = query.Where(x => x.StatisticsMonth == input.StatisticsMonth); | |
| 98 | + } | |
| 99 | + | |
| 100 | + var list = await query.OrderBy(x => x.StatisticsMonth, OrderByType.Desc) | |
| 101 | + .Select(x => new ShareStatisticsHqOutput | |
| 102 | + { | |
| 103 | + Id = x.Id, | |
| 104 | + StatisticsMonth = x.StatisticsMonth, | |
| 105 | + IncomeGeneral = x.IncomeGeneral, | |
| 106 | + IncomeTechDept = x.IncomeTechDept, | |
| 107 | + CostReimbursement = x.CostReimbursement, | |
| 108 | + CostLabor = x.CostLabor, | |
| 109 | + CostEducationRent = x.CostEducationRent, | |
| 110 | + CostWarehouseRent = x.CostWarehouseRent, | |
| 111 | + CostHQRent = x.CostHQRent, | |
| 112 | + OperationalProfit = x.OperationalProfit, | |
| 113 | + CreateTime = x.CreateTime, | |
| 114 | + UpdateTime = x.UpdateTime | |
| 115 | + }) | |
| 116 | + .ToListAsync(); | |
| 117 | + | |
| 118 | + return new { code = 200, msg = "查询成功", data = list }; | |
| 119 | + } | |
| 120 | + | |
| 121 | + #region 私有计算方法 | |
| 122 | + | |
| 123 | + /// <summary> | |
| 124 | + /// 计算收入部分 | |
| 125 | + /// </summary> | |
| 126 | + private async Task CalculateIncome(LqShareStatisticsHqEntity entity, DateTime startDate, DateTime endDate) | |
| 127 | + { | |
| 128 | + // 1. 收入-全部 = (所有门店的总开单实付业绩 - 所有门店的总实退金额) * 9% | |
| 129 | + var totalBilling = await _db.Queryable<LqKdKdjlbEntity>() | |
| 130 | + .Where(x => x.Kdrq >= startDate && x.Kdrq <= endDate && x.IsEffective == 1) | |
| 131 | + .SumAsync(x => x.Sfyj); | |
| 132 | + | |
| 133 | + var totalRefund = await _db.Queryable<LqHytkHytkEntity>() | |
| 134 | + .Where(x => x.Tksj >= startDate && x.Tksj <= endDate && x.IsEffective == 1) | |
| 135 | + .SumAsync(x => x.Tkje ?? 0); | |
| 136 | + | |
| 137 | + entity.IncomeGeneral = (totalBilling - totalRefund) * 0.09m; | |
| 138 | + | |
| 139 | + // 2. 收入-科技部 = (总科美业绩 - 总科美退款) * 0.3 * 0.09 | |
| 140 | + var kemeiPerformance = await _db.Queryable<LqKdPxmxEntity>() | |
| 141 | + .Where(x => x.Yjsj >= startDate && x.Yjsj <= endDate && x.IsEffective == 1) | |
| 142 | + .Where(x => x.ItemCategory == "科美") | |
| 143 | + .SumAsync(x => x.TotalPrice); | |
| 144 | + | |
| 145 | + // TODO: 需要确认科美退款的统计方式 | |
| 146 | + entity.IncomeTechDept = kemeiPerformance * 0.3m * 0.09m; | |
| 147 | + } | |
| 148 | + | |
| 149 | + /// <summary> | |
| 150 | + /// 计算成本部分 | |
| 151 | + /// </summary> | |
| 152 | + private async Task CalculateCost(LqShareStatisticsHqEntity entity, DateTime startDate, DateTime endDate, string statisticsMonth) | |
| 153 | + { | |
| 154 | + // 1. 成本-报销 (TODO: 需要确认总部报销的判定方式) | |
| 155 | + entity.CostReimbursement = 0; | |
| 156 | + | |
| 157 | + // 2. 成本-人工 (保留) | |
| 158 | + entity.CostLabor = 0; | |
| 159 | + | |
| 160 | + // 3. 成本-教育部房租 | |
| 161 | + // TODO: 需要确认如何识别教育部合同 | |
| 162 | + entity.CostEducationRent = 0; | |
| 163 | + | |
| 164 | + // 4. 成本-仓库房租 | |
| 165 | + // TODO: 需要确认如何识别仓库合同 | |
| 166 | + entity.CostWarehouseRent = 0; | |
| 167 | + | |
| 168 | + // 5. 成本-总部房租 | |
| 169 | + // TODO: 需要确认如何识别总部合同 | |
| 170 | + entity.CostHQRent = 0; | |
| 171 | + } | |
| 172 | + | |
| 173 | + /// <summary> | |
| 174 | + /// 计算利润 | |
| 175 | + /// </summary> | |
| 176 | + private void CalculateProfit(LqShareStatisticsHqEntity entity) | |
| 177 | + { | |
| 178 | + // 总部运营利润 = 收入(门店9%) + 收入(科技部9%) - 成本(报销) - 成本(人工) - 成本(房租) | |
| 179 | + var totalIncome = entity.IncomeGeneral + entity.IncomeTechDept; | |
| 180 | + var totalCost = entity.CostReimbursement | |
| 181 | + + entity.CostLabor | |
| 182 | + + entity.CostEducationRent | |
| 183 | + + entity.CostWarehouseRent | |
| 184 | + + entity.CostHQRent; | |
| 185 | + | |
| 186 | + entity.OperationalProfit = totalIncome - totalCost; | |
| 187 | + } | |
| 188 | + | |
| 189 | + #endregion | |
| 190 | + } | |
| 191 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqShareStatisticsStoreService.cs
0 → 100644
| 1 | +using Microsoft.AspNetCore.Mvc; | |
| 2 | +using NCC.Common.Filter; | |
| 3 | +using NCC.Dependency; | |
| 4 | +using NCC.DynamicApiController; | |
| 5 | +using NCC.Extend.Entitys.Dto.LqShareStatisticsStore; | |
| 6 | +using NCC.Extend.Entitys.lq_share_statistics_store; | |
| 7 | +using NCC.Extend.Entitys.lq_xh_jksyj; | |
| 8 | +using NCC.Extend.Entitys.lq_kd_kdjlb; | |
| 9 | +using NCC.Extend.Entitys.lq_hytk_hytk; | |
| 10 | +using NCC.Extend.Entitys.lq_hytk_jksyj; | |
| 11 | +using NCC.Extend.Entitys.lq_inventory_usage; | |
| 12 | +using NCC.Extend.Entitys.lq_laundry_flow; | |
| 13 | +using NCC.Extend.Entitys.lq_salary_statistics; | |
| 14 | +using NCC.Extend.Entitys.lq_assistant_salary_statistics; | |
| 15 | +using NCC.Extend.Entitys.lq_director_salary_statistics; | |
| 16 | +using NCC.Extend.Entitys.lq_store_manager_salary_statistics; | |
| 17 | +using NCC.Extend.Entitys.lq_business_unit_manager_salary_statistics; | |
| 18 | +using NCC.Extend.Entitys.lq_contract_rent_detail; | |
| 19 | +using NCC.Extend.Entitys.lq_mdxx; | |
| 20 | +using NCC.Extend.Entitys.lq_kd_pxmx; | |
| 21 | +using NCC.Extend.Entitys.lq_product; | |
| 22 | +using NCC.Extend.Entitys.lq_inventory_usage_application; | |
| 23 | +using NCC.Extend.Entitys; | |
| 24 | +using SqlSugar; | |
| 25 | +using System; | |
| 26 | +using System.Collections.Generic; | |
| 27 | +using System.Linq; | |
| 28 | +using System.Threading.Tasks; | |
| 29 | +using Yitter.IdGenerator; | |
| 30 | + | |
| 31 | +namespace NCC.Extend | |
| 32 | +{ | |
| 33 | + /// <summary> | |
| 34 | + /// 门店股份统计服务 | |
| 35 | + /// </summary> | |
| 36 | + [ApiDescriptionSettings(Tag = "门店股份统计服务", Name = "LqShareStatisticsStore", Order = 400)] | |
| 37 | + [Route("api/Extend/[controller]")] | |
| 38 | + public class LqShareStatisticsStoreService : IDynamicApiController, ITransient | |
| 39 | + { | |
| 40 | + private readonly ISqlSugarClient _db; | |
| 41 | + | |
| 42 | + public LqShareStatisticsStoreService(ISqlSugarClient db) | |
| 43 | + { | |
| 44 | + _db = db; | |
| 45 | + } | |
| 46 | + | |
| 47 | + /// <summary> | |
| 48 | + /// 生成门店股份统计数据 | |
| 49 | + /// </summary> | |
| 50 | + /// <param name="input">生成参数</param> | |
| 51 | + /// <returns>生成结果</returns> | |
| 52 | + [HttpPost("generate")] | |
| 53 | + public async Task<dynamic> GenerateStatistics([FromBody] ShareStatisticsStoreGenerateInput input) | |
| 54 | + { | |
| 55 | + if (string.IsNullOrEmpty(input.StatisticsMonth) || input.StatisticsMonth.Length != 6) | |
| 56 | + { | |
| 57 | + return new { code = 500, msg = "统计月份格式错误,应为 YYYYMM" }; | |
| 58 | + } | |
| 59 | + | |
| 60 | + var year = int.Parse(input.StatisticsMonth.Substring(0, 4)); | |
| 61 | + var month = int.Parse(input.StatisticsMonth.Substring(4, 2)); | |
| 62 | + var startDate = new DateTime(year, month, 1); | |
| 63 | + var endDate = startDate.AddMonths(1).AddDays(-1); | |
| 64 | + | |
| 65 | + // 获取门店列表 | |
| 66 | + var storeQuery = _db.Queryable<LqMdxxEntity>(); | |
| 67 | + if (!string.IsNullOrEmpty(input.StoreId)) | |
| 68 | + { | |
| 69 | + storeQuery = storeQuery.Where(x => x.Id == input.StoreId); | |
| 70 | + } | |
| 71 | + var stores = await storeQuery.ToListAsync(); | |
| 72 | + | |
| 73 | + if (stores.Count == 0) | |
| 74 | + { | |
| 75 | + return new { code = 500, msg = "未找到有效门店" }; | |
| 76 | + } | |
| 77 | + | |
| 78 | + var generatedCount = 0; | |
| 79 | + var updatedCount = 0; | |
| 80 | + | |
| 81 | + foreach (var store in stores) | |
| 82 | + { | |
| 83 | + try | |
| 84 | + { | |
| 85 | + // 检查是否已存在 | |
| 86 | + var existing = await _db.Queryable<LqShareStatisticsStoreEntity>() | |
| 87 | + .FirstAsync(x => x.StoreId == store.Id && x.StatisticsMonth == input.StatisticsMonth); | |
| 88 | + | |
| 89 | + var entity = existing ?? new LqShareStatisticsStoreEntity | |
| 90 | + { | |
| 91 | + Id = YitIdHelper.NextId().ToString(), | |
| 92 | + StoreId = store.Id, | |
| 93 | + StoreName = store.Dm, | |
| 94 | + StatisticsMonth = input.StatisticsMonth, | |
| 95 | + IsEffective = 1, | |
| 96 | + CreateTime = DateTime.Now, | |
| 97 | + CreateUser = "System" | |
| 98 | + }; | |
| 99 | + | |
| 100 | + // 计算各项数据 | |
| 101 | + await CalculateIncome(entity, startDate, endDate); | |
| 102 | + await CalculateCost(entity, startDate, endDate); | |
| 103 | + await CalculateSalary(entity, input.StatisticsMonth); | |
| 104 | + await CalculateExpense(entity, startDate, endDate, input.StatisticsMonth); | |
| 105 | + CalculateProfit(entity); | |
| 106 | + | |
| 107 | + entity.UpdateTime = DateTime.Now; | |
| 108 | + entity.UpdateUser = "System"; | |
| 109 | + | |
| 110 | + if (existing == null) | |
| 111 | + { | |
| 112 | + await _db.Insertable(entity).ExecuteCommandAsync(); | |
| 113 | + generatedCount++; | |
| 114 | + } | |
| 115 | + else | |
| 116 | + { | |
| 117 | + await _db.Updateable(entity).ExecuteCommandAsync(); | |
| 118 | + updatedCount++; | |
| 119 | + } | |
| 120 | + } | |
| 121 | + catch (Exception ex) | |
| 122 | + { | |
| 123 | + return new { code = 500, msg = $"处理门店 {store.Dm} 时出错: {ex.Message}, StackTrace: {ex.StackTrace}" }; | |
| 124 | + } | |
| 125 | + } | |
| 126 | + | |
| 127 | + return new | |
| 128 | + { | |
| 129 | + code = 200, | |
| 130 | + msg = "生成成功", | |
| 131 | + data = new | |
| 132 | + { | |
| 133 | + generatedCount, | |
| 134 | + updatedCount, | |
| 135 | + totalCount = stores.Count | |
| 136 | + } | |
| 137 | + }; | |
| 138 | + } | |
| 139 | + | |
| 140 | + /// <summary> | |
| 141 | + /// 查询门店股份统计列表 | |
| 142 | + /// </summary> | |
| 143 | + /// <param name="input">查询参数</param> | |
| 144 | + /// <returns>统计列表</returns> | |
| 145 | + [HttpGet("list")] | |
| 146 | + public async Task<dynamic> GetList([FromQuery] ShareStatisticsStoreQueryInput input) | |
| 147 | + { | |
| 148 | + var query = _db.Queryable<LqShareStatisticsStoreEntity>() | |
| 149 | + .Where(x => x.IsEffective == 1); | |
| 150 | + | |
| 151 | + if (!string.IsNullOrEmpty(input.StatisticsMonth)) | |
| 152 | + { | |
| 153 | + query = query.Where(x => x.StatisticsMonth == input.StatisticsMonth); | |
| 154 | + } | |
| 155 | + | |
| 156 | + if (!string.IsNullOrEmpty(input.StoreId)) | |
| 157 | + { | |
| 158 | + query = query.Where(x => x.StoreId == input.StoreId); | |
| 159 | + } | |
| 160 | + | |
| 161 | + if (!string.IsNullOrEmpty(input.StoreName)) | |
| 162 | + { | |
| 163 | + query = query.Where(x => x.StoreName.Contains(input.StoreName)); | |
| 164 | + } | |
| 165 | + | |
| 166 | + var list = await query.OrderBy(x => x.StatisticsMonth, OrderByType.Desc) | |
| 167 | + .OrderBy(x => x.StoreName) | |
| 168 | + .Select(x => new ShareStatisticsStoreOutput | |
| 169 | + { | |
| 170 | + Id = x.Id, | |
| 171 | + StoreId = x.StoreId, | |
| 172 | + StoreName = x.StoreName, | |
| 173 | + StatisticsMonth = x.StatisticsMonth, | |
| 174 | + MainIncome = x.MainIncome, | |
| 175 | + ConsumeLifeBeauty = x.ConsumeLifeBeauty, | |
| 176 | + ConsumeTechBeauty = x.ConsumeTechBeauty, | |
| 177 | + ConsumeMedicalBeauty = x.ConsumeMedicalBeauty, | |
| 178 | + ConsumeCooperation = x.ConsumeCooperation, | |
| 179 | + ConsumeProduct = x.ConsumeProduct, | |
| 180 | + ConsumeOther = x.ConsumeOther, | |
| 181 | + OtherIncome = x.OtherIncome, | |
| 182 | + AdvanceReceipt = x.AdvanceReceipt, | |
| 183 | + Refund = x.Refund, | |
| 184 | + Receivables = x.Receivables, | |
| 185 | + BankDeposit = x.BankDeposit, | |
| 186 | + CostProduct = x.CostProduct, | |
| 187 | + CostFutian = x.CostFutian, | |
| 188 | + CostTowel = x.CostTowel, | |
| 189 | + CostTechDept = x.CostTechDept, | |
| 190 | + CostManagementFee = x.CostManagementFee, | |
| 191 | + CostCooperation = x.CostCooperation, | |
| 192 | + CostMajorProject = x.CostMajorProject, | |
| 193 | + CostOther = x.CostOther, | |
| 194 | + SalaryBaseHealthCoach = x.SalaryBaseHealthCoach, | |
| 195 | + SalaryBaseAssistant = x.SalaryBaseAssistant, | |
| 196 | + SalaryBaseDirector = x.SalaryBaseDirector, | |
| 197 | + SalaryBaseStoreManager = x.SalaryBaseStoreManager, | |
| 198 | + SalaryBaseGeneralManager = x.SalaryBaseGeneralManager, | |
| 199 | + SalaryCommissionHealthCoach = x.SalaryCommissionHealthCoach, | |
| 200 | + SalaryCommissionAssistant = x.SalaryCommissionAssistant, | |
| 201 | + SalaryCommissionDirector = x.SalaryCommissionDirector, | |
| 202 | + SalaryCommissionStoreManager = x.SalaryCommissionStoreManager, | |
| 203 | + SalaryCommissionGeneralManager = x.SalaryCommissionGeneralManager, | |
| 204 | + SalaryManual = x.SalaryManual, | |
| 205 | + SalaryAttendance = x.SalaryAttendance, | |
| 206 | + SalaryPhoneCustody = x.SalaryPhoneCustody, | |
| 207 | + SalaryHeadcountReward = x.SalaryHeadcountReward, | |
| 208 | + SalaryTZone = x.SalaryTZone, | |
| 209 | + SocialSecurity = x.SocialSecurity, | |
| 210 | + StoreRent = x.StoreRent, | |
| 211 | + DormRent = x.DormRent, | |
| 212 | + CurrentPeriodExpense = x.CurrentPeriodExpense, | |
| 213 | + CurrentPeriodExpenseQin = x.CurrentPeriodExpenseQin, | |
| 214 | + FinancialFee = x.FinancialFee, | |
| 215 | + RewardMedicalBeauty = x.RewardMedicalBeauty, | |
| 216 | + RewardOther = x.RewardOther, | |
| 217 | + RewardLargeOrder = x.RewardLargeOrder, | |
| 218 | + RewardFirstOrder = x.RewardFirstOrder, | |
| 219 | + RewardGeneral = x.RewardGeneral, | |
| 220 | + Other1 = x.Other1, | |
| 221 | + Other2 = x.Other2, | |
| 222 | + Profit = x.Profit, | |
| 223 | + FinancialProfit = x.FinancialProfit, | |
| 224 | + CreateTime = x.CreateTime, | |
| 225 | + UpdateTime = x.UpdateTime | |
| 226 | + }) | |
| 227 | + .ToListAsync(); | |
| 228 | + | |
| 229 | + return new { code = 200, msg = "查询成功", data = list }; | |
| 230 | + } | |
| 231 | + | |
| 232 | + #region 私有计算方法 | |
| 233 | + | |
| 234 | + /// <summary> | |
| 235 | + /// 计算收入部分 | |
| 236 | + /// </summary> | |
| 237 | + private async Task CalculateIncome(LqShareStatisticsStoreEntity entity, DateTime startDate, DateTime endDate) | |
| 238 | + { | |
| 239 | + // 1. 主营收入 = 消耗总业绩(不扣减退款),并按分类拆分 | |
| 240 | + var consumePerformance = await _db.Queryable<LqXhJksyjEntity>() | |
| 241 | + .Where(x => x.StoreId == entity.StoreId && x.Yjsj >= startDate && x.Yjsj <= endDate && x.IsEffective == 1) | |
| 242 | + .SumAsync(x => x.Jksyj); | |
| 243 | + | |
| 244 | + entity.MainIncome = consumePerformance ?? 0; | |
| 245 | + | |
| 246 | + // 分项消耗业绩 | |
| 247 | + var consumeLifeBeauty = await _db.Queryable<LqXhJksyjEntity>() | |
| 248 | + .Where(x => x.StoreId == entity.StoreId && x.Yjsj >= startDate && x.Yjsj <= endDate && x.IsEffective == 1) | |
| 249 | + .Where(x => x.ItemCategory == "生美") | |
| 250 | + .SumAsync(x => x.Jksyj); | |
| 251 | + | |
| 252 | + var consumeTechBeauty = await _db.Queryable<LqXhJksyjEntity>() | |
| 253 | + .Where(x => x.StoreId == entity.StoreId && x.Yjsj >= startDate && x.Yjsj <= endDate && x.IsEffective == 1) | |
| 254 | + .Where(x => x.ItemCategory == "科美") | |
| 255 | + .SumAsync(x => x.Jksyj); | |
| 256 | + | |
| 257 | + var consumeMedicalBeauty = await _db.Queryable<LqXhJksyjEntity>() | |
| 258 | + .Where(x => x.StoreId == entity.StoreId && x.Yjsj >= startDate && x.Yjsj <= endDate && x.IsEffective == 1) | |
| 259 | + .Where(x => x.ItemCategory == "医美") | |
| 260 | + .SumAsync(x => x.Jksyj); | |
| 261 | + | |
| 262 | + var consumeCooperation = await _db.Queryable<LqXhJksyjEntity>() | |
| 263 | + .Where(x => x.StoreId == entity.StoreId && x.Yjsj >= startDate && x.Yjsj <= endDate && x.IsEffective == 1) | |
| 264 | + .Where(x => x.ItemCategory == "合作") | |
| 265 | + .SumAsync(x => x.Jksyj); | |
| 266 | + | |
| 267 | + var consumeProduct = await _db.Queryable<LqXhJksyjEntity>() | |
| 268 | + .Where(x => x.StoreId == entity.StoreId && x.Yjsj >= startDate && x.Yjsj <= endDate && x.IsEffective == 1) | |
| 269 | + .Where(x => x.ItemCategory == "产品") | |
| 270 | + .SumAsync(x => x.Jksyj); | |
| 271 | + | |
| 272 | + var consumeOther = await _db.Queryable<LqXhJksyjEntity>() | |
| 273 | + .Where(x => x.StoreId == entity.StoreId && x.Yjsj >= startDate && x.Yjsj <= endDate && x.IsEffective == 1) | |
| 274 | + .Where(x => x.ItemCategory == "其他" || x.ItemCategory == null || x.ItemCategory == "") | |
| 275 | + .SumAsync(x => x.Jksyj); | |
| 276 | + | |
| 277 | + entity.ConsumeLifeBeauty = consumeLifeBeauty ?? 0; | |
| 278 | + entity.ConsumeTechBeauty = consumeTechBeauty ?? 0; | |
| 279 | + entity.ConsumeMedicalBeauty = consumeMedicalBeauty ?? 0; | |
| 280 | + entity.ConsumeCooperation = consumeCooperation ?? 0; | |
| 281 | + entity.ConsumeProduct = consumeProduct ?? 0; | |
| 282 | + entity.ConsumeOther = consumeOther ?? 0; | |
| 283 | + | |
| 284 | + // 2. 预收款 = 开单实付 | |
| 285 | + entity.AdvanceReceipt = await _db.Queryable<LqKdKdjlbEntity>() | |
| 286 | + .Where(x => x.Djmd == entity.StoreId && x.Kdrq >= startDate && x.Kdrq <= endDate && x.IsEffective == 1) | |
| 287 | + .SumAsync(x => x.Sfyj); | |
| 288 | + | |
| 289 | + // 3. 实际退款 = 退卡实退金额 | |
| 290 | + entity.Refund = await _db.Queryable<LqHytkHytkEntity>() | |
| 291 | + .Where(x => x.Md == entity.StoreId && x.Tksj >= startDate && x.Tksj <= endDate && x.IsEffective == 1) | |
| 292 | + .SumAsync(x => x.ActualRefundAmount ?? 0); | |
| 293 | + | |
| 294 | + // 4. 应收 = 合作医院开单金额 | |
| 295 | + entity.Receivables = await _db.Queryable<LqKdKdjlbEntity>() | |
| 296 | + .Where(x => x.Djmd == entity.StoreId && x.Kdrq >= startDate && x.Kdrq <= endDate | |
| 297 | + && x.IsEffective == 1 && !string.IsNullOrEmpty(x.Fkyy)) | |
| 298 | + .SumAsync(x => x.Sfyj); | |
| 299 | + | |
| 300 | + // 5. 银行存款 = 开单实付 - 应收 | |
| 301 | + entity.BankDeposit = entity.AdvanceReceipt - entity.Receivables; | |
| 302 | + | |
| 303 | + // 6. 其他收入 = 退款差额 > 0 的部分(按照退款单应退金额-实退金额) | |
| 304 | + // 退款差额 = tkje - ActualRefundAmount | |
| 305 | + var positiveRefundDiff = await _db.Queryable<LqHytkHytkEntity>() | |
| 306 | + .Where(x => x.Md == entity.StoreId && x.Tksj >= startDate && x.Tksj <= endDate && x.IsEffective == 1) | |
| 307 | + .SumAsync(x => SqlFunc.IIF((x.Tkje - (x.ActualRefundAmount ?? 0)) > 0, | |
| 308 | + x.Tkje - (x.ActualRefundAmount ?? 0), | |
| 309 | + 0)); | |
| 310 | + | |
| 311 | + entity.OtherIncome = positiveRefundDiff ?? 0; | |
| 312 | + } | |
| 313 | + | |
| 314 | + /// <summary> | |
| 315 | + /// 计算成本部分 | |
| 316 | + /// </summary> | |
| 317 | + private async Task CalculateCost(LqShareStatisticsStoreEntity entity, DateTime startDate, DateTime endDate) | |
| 318 | + { | |
| 319 | + // 1. 产品成本 = 仓库领用金额 | |
| 320 | + entity.CostProduct = await _db.Queryable<LqInventoryUsageEntity>() | |
| 321 | + .Where(x => x.StoreId == entity.StoreId && x.UsageTime >= startDate && x.UsageTime <= endDate && x.IsEffective == 1) | |
| 322 | + .SumAsync(x => x.TotalAmount); | |
| 323 | + | |
| 324 | + // 2. 福田成本 = 福田仓库领用(上一个月的成本) | |
| 325 | + // 读取产品归属仓库为“福田仓库”的库存领用记录, | |
| 326 | + // 并且仅统计已审批通过且已领取的使用申请(领用完成后才纳入统计) | |
| 327 | + var prevMonthStart = startDate.AddMonths(-1); | |
| 328 | + var prevMonthEnd = startDate.AddDays(-1); | |
| 329 | + | |
| 330 | + entity.CostFutian = await _db.Queryable<LqInventoryUsageEntity, LqProductEntity, LqInventoryUsageApplicationEntity>((u, p, a) => new JoinQueryInfos( | |
| 331 | + JoinType.Inner, u.ProductId == p.Id, | |
| 332 | + JoinType.Inner, u.UsageBatchId == a.UsageBatchId)) | |
| 333 | + .Where((u, p, a) => p.Warehouse == "福田仓库" | |
| 334 | + && u.StoreId == entity.StoreId | |
| 335 | + && u.UsageTime >= prevMonthStart && u.UsageTime <= prevMonthEnd | |
| 336 | + && u.IsEffective == 1 | |
| 337 | + && a.IsEffective == 1 | |
| 338 | + && a.ApprovalStatus == "已通过" | |
| 339 | + && a.IsReceived == 1) | |
| 340 | + .SumAsync((u, p, a) => u.TotalAmount); | |
| 341 | + | |
| 342 | + // 3. 毛巾成本 = 洗毛巾费用 | |
| 343 | + entity.CostTowel = await _db.Queryable<LqLaundryFlowEntity>() | |
| 344 | + .Where(x => x.StoreId == entity.StoreId | |
| 345 | + && x.SendTime >= startDate && x.SendTime <= endDate | |
| 346 | + && x.IsEffective == 1) | |
| 347 | + .SumAsync(x => x.TotalPrice); | |
| 348 | + | |
| 349 | + // 4. 科技部成本 = 科美业绩 * 30% | |
| 350 | + var kemeiPerformance = await _db.Queryable<LqKdPxmxEntity>() | |
| 351 | + .Where(x => x.Glkdbh.StartsWith(entity.StoreId) && x.Yjsj >= startDate && x.Yjsj <= endDate && x.IsEffective == 1) | |
| 352 | + .Where(x => x.ItemCategory == "科美") | |
| 353 | + .SumAsync(x => x.TotalPrice); | |
| 354 | + | |
| 355 | + entity.CostTechDept = kemeiPerformance * 0.3m; | |
| 356 | + | |
| 357 | + // 5. 管理费 = 总业绩 * 9% | |
| 358 | + var totalPerformance = await _db.Queryable<LqKdKdjlbEntity>() | |
| 359 | + .Where(x => x.Djmd == entity.StoreId && x.Kdrq >= startDate && x.Kdrq <= endDate && x.IsEffective == 1) | |
| 360 | + .SumAsync(x => x.Sfyj); | |
| 361 | + | |
| 362 | + entity.CostManagementFee = totalPerformance * 0.09m; | |
| 363 | + | |
| 364 | + // 6. 其他成本 = 退款差额 < 0 的部分(按照退款单应退金额-实退金额) | |
| 365 | + var negativeRefundDiffAbs = await _db.Queryable<LqHytkHytkEntity>() | |
| 366 | + .Where(x => x.Md == entity.StoreId && x.Tksj >= startDate && x.Tksj <= endDate && x.IsEffective == 1) | |
| 367 | + .SumAsync(x => SqlFunc.IIF((x.Tkje - (x.ActualRefundAmount ?? 0)) < 0, | |
| 368 | + (x.ActualRefundAmount ?? 0) - x.Tkje, | |
| 369 | + 0)); | |
| 370 | + | |
| 371 | + entity.CostOther = negativeRefundDiffAbs ?? 0; | |
| 372 | + | |
| 373 | + // 保留字段暂时为0 | |
| 374 | + entity.CostCooperation = 0; | |
| 375 | + entity.CostMajorProject = 0; | |
| 376 | + } | |
| 377 | + | |
| 378 | + /// <summary> | |
| 379 | + /// 计算人工工资部分 | |
| 380 | + /// </summary> | |
| 381 | + private async Task CalculateSalary(LqShareStatisticsStoreEntity entity, string statisticsMonth) | |
| 382 | + { | |
| 383 | + // 1. 健康师底薪和提成 | |
| 384 | + var healthCoachSalary = await _db.Queryable<LqSalaryStatisticsEntity>() | |
| 385 | + .Where(x => x.StoreId == entity.StoreId && x.StatisticsMonth == statisticsMonth) | |
| 386 | + .Select(x => new | |
| 387 | + { | |
| 388 | + BaseSalary = SqlFunc.AggregateSum(x.HealthCoachBaseSalary), | |
| 389 | + Commission = SqlFunc.AggregateSum(x.TotalCommission), | |
| 390 | + Manual = SqlFunc.AggregateSum(x.HandworkFee), | |
| 391 | + TZone = SqlFunc.AggregateSum(x.StoreTZoneCommission) | |
| 392 | + }) | |
| 393 | + .FirstAsync(); | |
| 394 | + | |
| 395 | + entity.SalaryBaseHealthCoach = healthCoachSalary?.BaseSalary ?? 0; | |
| 396 | + entity.SalaryCommissionHealthCoach = healthCoachSalary?.Commission ?? 0; | |
| 397 | + entity.SalaryManual = healthCoachSalary?.Manual ?? 0; | |
| 398 | + entity.SalaryTZone = healthCoachSalary?.TZone ?? 0; | |
| 399 | + | |
| 400 | + // 2. 店助底薪和提成 | |
| 401 | + var assistantSalary = await _db.Queryable<LqAssistantSalaryStatisticsEntity>() | |
| 402 | + .Where(x => x.StoreId == entity.StoreId && x.StatisticsMonth == statisticsMonth && x.Position == "店助") | |
| 403 | + .Select(x => new | |
| 404 | + { | |
| 405 | + BaseSalary = SqlFunc.AggregateSum(x.BaseSalary), | |
| 406 | + Commission = SqlFunc.AggregateSum(x.CommissionAmount), | |
| 407 | + PhoneCustody = SqlFunc.AggregateSum(x.PhoneManagementFee), | |
| 408 | + HeadcountReward = SqlFunc.AggregateSum(x.Stage1Reward + x.Stage2Reward) | |
| 409 | + }) | |
| 410 | + .FirstAsync(); | |
| 411 | + | |
| 412 | + entity.SalaryBaseAssistant = assistantSalary?.BaseSalary ?? 0; | |
| 413 | + entity.SalaryCommissionAssistant = assistantSalary?.Commission ?? 0; | |
| 414 | + entity.SalaryPhoneCustody = assistantSalary?.PhoneCustody ?? 0; | |
| 415 | + entity.SalaryHeadcountReward = assistantSalary?.HeadcountReward ?? 0; | |
| 416 | + | |
| 417 | + // 3. 店助主任底薪和提成 | |
| 418 | + var directorSalary = await _db.Queryable<LqAssistantSalaryStatisticsEntity>() | |
| 419 | + .Where(x => x.StoreId == entity.StoreId && x.StatisticsMonth == statisticsMonth && x.Position == "店助主任") | |
| 420 | + .Select(x => new | |
| 421 | + { | |
| 422 | + BaseSalary = SqlFunc.AggregateSum(x.BaseSalary), | |
| 423 | + Commission = SqlFunc.AggregateSum(x.CommissionAmount), | |
| 424 | + PhoneCustody = SqlFunc.AggregateSum(x.PhoneManagementFee), | |
| 425 | + HeadcountReward = SqlFunc.AggregateSum(x.Stage1Reward + x.Stage2Reward) | |
| 426 | + }) | |
| 427 | + .FirstAsync(); | |
| 428 | + | |
| 429 | + entity.SalaryBaseDirector = directorSalary?.BaseSalary ?? 0; | |
| 430 | + entity.SalaryCommissionDirector = directorSalary?.Commission ?? 0; | |
| 431 | + | |
| 432 | + // 4. 店长底薪和提成 | |
| 433 | + var storeManagerSalary = await _db.Queryable<LqStoreManagerSalaryStatisticsEntity>() | |
| 434 | + .Where(x => x.StoreId == entity.StoreId && x.StatisticsMonth == statisticsMonth) | |
| 435 | + .Select(x => new | |
| 436 | + { | |
| 437 | + BaseSalary = SqlFunc.AggregateSum(x.ActualBaseSalary), | |
| 438 | + Commission = SqlFunc.AggregateSum(x.CommissionAmount) | |
| 439 | + }) | |
| 440 | + .FirstAsync(); | |
| 441 | + | |
| 442 | + entity.SalaryBaseStoreManager = storeManagerSalary?.BaseSalary ?? 0; | |
| 443 | + entity.SalaryCommissionStoreManager = storeManagerSalary?.Commission ?? 0; | |
| 444 | + | |
| 445 | + // 5. 总经理/经理底薪和提成 | |
| 446 | + // 底薪按门店数平均分摊,提成需要从 F_StorePerformanceDetail JSON 中提取该门店的提成 | |
| 447 | + var gmSalary = await _db.Queryable<LqBusinessUnitManagerSalaryStatisticsEntity>() | |
| 448 | + .Where(x => x.StatisticsMonth == statisticsMonth) | |
| 449 | + .Where(x => x.StorePerformanceDetail.Contains(entity.StoreId)) | |
| 450 | + .ToListAsync(); | |
| 451 | + | |
| 452 | + decimal gmBaseSalary = 0; | |
| 453 | + decimal gmCommission = 0; | |
| 454 | + | |
| 455 | + foreach (var gm in gmSalary) | |
| 456 | + { | |
| 457 | + // 底薪平均分摊 | |
| 458 | + if (!string.IsNullOrEmpty(gm.StorePerformanceDetail)) | |
| 459 | + { | |
| 460 | + try | |
| 461 | + { | |
| 462 | + var storeDetails = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(gm.StorePerformanceDetail); | |
| 463 | + var storeCount = storeDetails?.Count ?? 1; | |
| 464 | + gmBaseSalary += gm.BaseSalary / storeCount; | |
| 465 | + | |
| 466 | + // 提取该门店的提成 | |
| 467 | + var storeDetail = storeDetails?.FirstOrDefault(s => s.ContainsKey("StoreId") && s["StoreId"].ToString() == entity.StoreId); | |
| 468 | + if (storeDetail != null && storeDetail.ContainsKey("Commission")) | |
| 469 | + { | |
| 470 | + gmCommission += Convert.ToDecimal(storeDetail["Commission"]); | |
| 471 | + } | |
| 472 | + } | |
| 473 | + catch | |
| 474 | + { | |
| 475 | + // JSON 解析失败,使用默认分摊 | |
| 476 | + gmBaseSalary += gm.BaseSalary; | |
| 477 | + } | |
| 478 | + } | |
| 479 | + } | |
| 480 | + | |
| 481 | + entity.SalaryBaseGeneralManager = gmBaseSalary; | |
| 482 | + entity.SalaryCommissionGeneralManager = gmCommission; | |
| 483 | + | |
| 484 | + // 保留字段 | |
| 485 | + entity.SalaryAttendance = 0; | |
| 486 | + } | |
| 487 | + | |
| 488 | + /// <summary> | |
| 489 | + /// 计算费用部分 | |
| 490 | + /// </summary> | |
| 491 | + private async Task CalculateExpense(LqShareStatisticsStoreEntity entity, DateTime startDate, DateTime endDate, string statisticsMonth) | |
| 492 | + { | |
| 493 | + // 1. 门店房租 - 需要通过合同关联门店 | |
| 494 | + // 从合同表关联租金明细 | |
| 495 | + // 解析统计月份 | |
| 496 | + var year = int.Parse(statisticsMonth.Substring(0, 4)); | |
| 497 | + var month = int.Parse(statisticsMonth.Substring(4, 2)); | |
| 498 | + | |
| 499 | + var rentDetail = await _db.Queryable<LqContractRentDetailEntity, NCC.Extend.Entitys.lq_contract.LqContractEntity>((rd, c) => new JoinQueryInfos( | |
| 500 | + JoinType.Inner, rd.ContractId == c.Id)) | |
| 501 | + .Where((rd, c) => c.StoreId == entity.StoreId | |
| 502 | + && rd.PaymentMonth.Year == year | |
| 503 | + && rd.PaymentMonth.Month == month | |
| 504 | + && rd.IsEffective == 1) | |
| 505 | + .Select((rd, c) => rd.DueAmount) | |
| 506 | + .ToListAsync(); | |
| 507 | + | |
| 508 | + entity.StoreRent = rentDetail.Sum(); | |
| 509 | + | |
| 510 | + // 2. 当期费用 = 报销费用中一级分类为“当期费用”的已审核通过记录 | |
| 511 | + // 只统计:报销申请审批通过 + 申请门店=当前门店 + 申请时间在当月 | |
| 512 | + var currentPeriodExpense = await _db.Queryable<LqReimbursementApplicationEntity, LqPurchaseRecordsEntity, LqReimbursementCategoryEntity>((app, pr, cat) => new JoinQueryInfos( | |
| 513 | + JoinType.Inner, pr.ApplicationId == app.Id, | |
| 514 | + JoinType.Inner, pr.ReimbursementCategoryId == cat.Id)) | |
| 515 | + .Where((app, pr, cat) => app.ApplicationStoreId == entity.StoreId | |
| 516 | + && app.ApplicationTime >= startDate && app.ApplicationTime <= endDate | |
| 517 | + && (app.ApprovalStatus ?? app.ApproveStatus) == "已通过" | |
| 518 | + && cat.Level1Name == "当期费用") | |
| 519 | + .SumAsync((app, pr, cat) => pr.Amount); | |
| 520 | + | |
| 521 | + entity.CurrentPeriodExpense = currentPeriodExpense; | |
| 522 | + | |
| 523 | + // 保留字段 | |
| 524 | + entity.SocialSecurity = 0; | |
| 525 | + entity.DormRent = 0; | |
| 526 | + entity.CurrentPeriodExpenseQin = 0; | |
| 527 | + entity.FinancialFee = 0; | |
| 528 | + entity.RewardMedicalBeauty = 0; | |
| 529 | + entity.RewardOther = 0; | |
| 530 | + entity.RewardLargeOrder = 0; | |
| 531 | + entity.RewardFirstOrder = 0; | |
| 532 | + entity.RewardGeneral = 0; | |
| 533 | + entity.Other1 = 0; | |
| 534 | + entity.Other2 = 0; | |
| 535 | + } | |
| 536 | + | |
| 537 | + /// <summary> | |
| 538 | + /// 计算利润 | |
| 539 | + /// </summary> | |
| 540 | + private void CalculateProfit(LqShareStatisticsStoreEntity entity) | |
| 541 | + { | |
| 542 | + // 人工工资汇总 | |
| 543 | + var totalSalary = entity.SalaryBaseHealthCoach + entity.SalaryBaseAssistant + entity.SalaryBaseDirector | |
| 544 | + + entity.SalaryBaseStoreManager + entity.SalaryBaseGeneralManager | |
| 545 | + + entity.SalaryCommissionHealthCoach + entity.SalaryCommissionAssistant + entity.SalaryCommissionDirector | |
| 546 | + + entity.SalaryCommissionStoreManager + entity.SalaryCommissionGeneralManager | |
| 547 | + + entity.SalaryManual + entity.SalaryAttendance + entity.SalaryPhoneCustody | |
| 548 | + + entity.SalaryHeadcountReward + entity.SalaryTZone; | |
| 549 | + | |
| 550 | + // 主营成本汇总 | |
| 551 | + var totalCost = entity.CostProduct + entity.CostFutian + entity.CostTowel + entity.CostTechDept | |
| 552 | + + entity.CostManagementFee + entity.CostCooperation + entity.CostMajorProject + entity.CostOther; | |
| 553 | + | |
| 554 | + // 费用汇总 | |
| 555 | + var totalExpense = entity.SocialSecurity + entity.StoreRent + entity.DormRent + entity.CurrentPeriodExpense | |
| 556 | + + entity.CurrentPeriodExpenseQin + entity.FinancialFee; | |
| 557 | + | |
| 558 | + // 奖励汇总 | |
| 559 | + var totalReward = entity.RewardMedicalBeauty + entity.RewardOther + entity.RewardLargeOrder | |
| 560 | + + entity.RewardFirstOrder + entity.RewardGeneral; | |
| 561 | + | |
| 562 | + // 其他汇总 | |
| 563 | + var totalOther = entity.Other1 + entity.Other2; | |
| 564 | + | |
| 565 | + // 利润 = 预收 - 实退 - 主营成本 - 人工工资 - 房租 - 费用 - 奖励 - 其他 | |
| 566 | + entity.Profit = entity.AdvanceReceipt - entity.Refund - totalCost - totalSalary - totalExpense - totalReward - totalOther; | |
| 567 | + | |
| 568 | + // 财务利润 = 主营收入 + 其他收入 - 主营成本 - 人工工资 - 房租 - 费用 - 奖励 - 其他 | |
| 569 | + entity.FinancialProfit = entity.MainIncome + entity.OtherIncome - totalCost - totalSalary - totalExpense - totalReward - totalOther; | |
| 570 | + } | |
| 571 | + | |
| 572 | + #endregion | |
| 573 | + } | |
| 574 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqShareStatisticsTechDeptService.cs
0 → 100644
| 1 | +using Microsoft.AspNetCore.Mvc; | |
| 2 | +using NCC.Common.Filter; | |
| 3 | +using NCC.Dependency; | |
| 4 | +using NCC.DynamicApiController; | |
| 5 | +using NCC.Extend.Entitys.Dto.LqShareStatisticsTechDept; | |
| 6 | +using NCC.Extend.Entitys.lq_share_statistics_tech_dept; | |
| 7 | +using NCC.Extend.Entitys.lq_mdxx; | |
| 8 | +using NCC.Extend.Entitys.lq_kd_pxmx; | |
| 9 | +using NCC.Extend.Entitys.lq_kd_kdjlb; | |
| 10 | +using NCC.Extend.Entitys.lq_hytk_jksyj; | |
| 11 | +using NCC.Extend.Entitys.lq_xh_jksyj; | |
| 12 | +using SqlSugar; | |
| 13 | +using System; | |
| 14 | +using System.Collections.Generic; | |
| 15 | +using System.Linq; | |
| 16 | +using System.Threading.Tasks; | |
| 17 | +using Yitter.IdGenerator; | |
| 18 | + | |
| 19 | +namespace NCC.Extend | |
| 20 | +{ | |
| 21 | + /// <summary> | |
| 22 | + /// 科技部股份统计服务 | |
| 23 | + /// </summary> | |
| 24 | + [ApiDescriptionSettings(Tag = "科技部股份统计服务", Name = "LqShareStatisticsTechDept", Order = 401)] | |
| 25 | + [Route("api/Extend/[controller]")] | |
| 26 | + public class LqShareStatisticsTechDeptService : IDynamicApiController, ITransient | |
| 27 | + { | |
| 28 | + private readonly ISqlSugarClient _db; | |
| 29 | + | |
| 30 | + public LqShareStatisticsTechDeptService(ISqlSugarClient db) | |
| 31 | + { | |
| 32 | + _db = db; | |
| 33 | + } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 生成科技部股份统计数据 | |
| 37 | + /// </summary> | |
| 38 | + /// <param name="input">生成参数</param> | |
| 39 | + /// <returns>生成结果</returns> | |
| 40 | + [HttpPost("generate")] | |
| 41 | + public async Task<dynamic> GenerateStatistics([FromBody] ShareStatisticsTechDeptGenerateInput input) | |
| 42 | + { | |
| 43 | + if (string.IsNullOrEmpty(input.StatisticsMonth) || input.StatisticsMonth.Length != 6) | |
| 44 | + { | |
| 45 | + return new { code = 500, msg = "统计月份格式错误,应为 YYYYMM" }; | |
| 46 | + } | |
| 47 | + | |
| 48 | + var year = int.Parse(input.StatisticsMonth.Substring(0, 4)); | |
| 49 | + var month = int.Parse(input.StatisticsMonth.Substring(4, 2)); | |
| 50 | + var startDate = new DateTime(year, month, 1); | |
| 51 | + var endDate = startDate.AddMonths(1).AddDays(-1); | |
| 52 | + | |
| 53 | + // 确定要生成的部门列表 | |
| 54 | + var departments = new List<string>(); | |
| 55 | + if (!string.IsNullOrEmpty(input.DepartmentName)) | |
| 56 | + { | |
| 57 | + departments.Add(input.DepartmentName); | |
| 58 | + } | |
| 59 | + else | |
| 60 | + { | |
| 61 | + departments.Add("科技一部"); | |
| 62 | + departments.Add("科技二部"); | |
| 63 | + } | |
| 64 | + | |
| 65 | + var generatedCount = 0; | |
| 66 | + var updatedCount = 0; | |
| 67 | + | |
| 68 | + foreach (var deptName in departments) | |
| 69 | + { | |
| 70 | + // 检查是否已存在 | |
| 71 | + var existing = await _db.Queryable<LqShareStatisticsTechDeptEntity>() | |
| 72 | + .FirstAsync(x => x.DepartmentName == deptName && x.StatisticsMonth == input.StatisticsMonth); | |
| 73 | + | |
| 74 | + var entity = existing ?? new LqShareStatisticsTechDeptEntity | |
| 75 | + { | |
| 76 | + Id = YitIdHelper.NextId().ToString(), | |
| 77 | + DepartmentName = deptName, | |
| 78 | + StatisticsMonth = input.StatisticsMonth, | |
| 79 | + IsEffective = 1, | |
| 80 | + CreateTime = DateTime.Now, | |
| 81 | + CreateUser = "System" | |
| 82 | + }; | |
| 83 | + | |
| 84 | + // 计算各项数据 | |
| 85 | + await CalculateIncome(entity, deptName, startDate, endDate); | |
| 86 | + await CalculateCost(entity, deptName, startDate, endDate, input.StatisticsMonth); | |
| 87 | + CalculateProfit(entity); | |
| 88 | + | |
| 89 | + entity.UpdateTime = DateTime.Now; | |
| 90 | + entity.UpdateUser = "System"; | |
| 91 | + | |
| 92 | + if (existing == null) | |
| 93 | + { | |
| 94 | + await _db.Insertable(entity).ExecuteCommandAsync(); | |
| 95 | + generatedCount++; | |
| 96 | + } | |
| 97 | + else | |
| 98 | + { | |
| 99 | + await _db.Updateable(entity).ExecuteCommandAsync(); | |
| 100 | + updatedCount++; | |
| 101 | + } | |
| 102 | + } | |
| 103 | + | |
| 104 | + return new | |
| 105 | + { | |
| 106 | + code = 200, | |
| 107 | + msg = "生成成功", | |
| 108 | + data = new | |
| 109 | + { | |
| 110 | + generatedCount, | |
| 111 | + updatedCount, | |
| 112 | + totalCount = departments.Count | |
| 113 | + } | |
| 114 | + }; | |
| 115 | + } | |
| 116 | + | |
| 117 | + /// <summary> | |
| 118 | + /// 查询科技部股份统计列表 | |
| 119 | + /// </summary> | |
| 120 | + /// <param name="input">查询参数</param> | |
| 121 | + /// <returns>统计列表</returns> | |
| 122 | + [HttpGet("list")] | |
| 123 | + public async Task<dynamic> GetList([FromQuery] ShareStatisticsTechDeptQueryInput input) | |
| 124 | + { | |
| 125 | + var query = _db.Queryable<LqShareStatisticsTechDeptEntity>() | |
| 126 | + .Where(x => x.IsEffective == 1); | |
| 127 | + | |
| 128 | + if (!string.IsNullOrEmpty(input.StatisticsMonth)) | |
| 129 | + { | |
| 130 | + query = query.Where(x => x.StatisticsMonth == input.StatisticsMonth); | |
| 131 | + } | |
| 132 | + | |
| 133 | + if (!string.IsNullOrEmpty(input.DepartmentName)) | |
| 134 | + { | |
| 135 | + query = query.Where(x => x.DepartmentName == input.DepartmentName); | |
| 136 | + } | |
| 137 | + | |
| 138 | + var list = await query.OrderBy(x => x.StatisticsMonth, OrderByType.Desc) | |
| 139 | + .OrderBy(x => x.DepartmentName) | |
| 140 | + .Select(x => new ShareStatisticsTechDeptOutput | |
| 141 | + { | |
| 142 | + Id = x.Id, | |
| 143 | + DepartmentName = x.DepartmentName, | |
| 144 | + StatisticsMonth = x.StatisticsMonth, | |
| 145 | + Income = x.Income, | |
| 146 | + CostReimbursement = x.CostReimbursement, | |
| 147 | + CostTeacherBase = x.CostTeacherBase, | |
| 148 | + CostTeacherManual = x.CostTeacherManual, | |
| 149 | + CostTeacherBillingComm = x.CostTeacherBillingComm, | |
| 150 | + CostTeacherConsumeComm = x.CostTeacherConsumeComm, | |
| 151 | + CostTeacherExpertComm = x.CostTeacherExpertComm, | |
| 152 | + CostTeacherOvertime = x.CostTeacherOvertime, | |
| 153 | + CostGMBase = x.CostGMBase, | |
| 154 | + CostGMComm = x.CostGMComm, | |
| 155 | + RewardTechDept = x.RewardTechDept, | |
| 156 | + CostOther1 = x.CostOther1, | |
| 157 | + CostOther2 = x.CostOther2, | |
| 158 | + Profit = x.Profit, | |
| 159 | + CreateTime = x.CreateTime, | |
| 160 | + UpdateTime = x.UpdateTime | |
| 161 | + }) | |
| 162 | + .ToListAsync(); | |
| 163 | + | |
| 164 | + return new { code = 200, msg = "查询成功", data = list }; | |
| 165 | + } | |
| 166 | + | |
| 167 | + #region 私有计算方法 | |
| 168 | + | |
| 169 | + /// <summary> | |
| 170 | + /// 计算收入部分 | |
| 171 | + /// </summary> | |
| 172 | + private async Task CalculateIncome(LqShareStatisticsTechDeptEntity entity, string deptName, DateTime startDate, DateTime endDate) | |
| 173 | + { | |
| 174 | + // 1. 找到该科技部管辖的所有门店 | |
| 175 | + var stores = await _db.Queryable<LqMdxxEntity>() | |
| 176 | + .Where(x => x.Kjb == deptName) | |
| 177 | + .Select(x => x.Id) | |
| 178 | + .ToListAsync(); | |
| 179 | + | |
| 180 | + if (stores.Count == 0) | |
| 181 | + { | |
| 182 | + entity.Income = 0; | |
| 183 | + return; | |
| 184 | + } | |
| 185 | + | |
| 186 | + // 2. 统计这些门店的科美项目开单实付业绩 | |
| 187 | + // 需要关联 lq_kd_kdjlb 来获取门店信息 | |
| 188 | + var kemeiIncome = await _db.Queryable<LqKdPxmxEntity, LqKdKdjlbEntity>((px, kd) => new JoinQueryInfos( | |
| 189 | + JoinType.Inner, px.Glkdbh == kd.Id)) | |
| 190 | + .Where((px, kd) => stores.Contains(kd.Djmd) && px.Yjsj >= startDate && px.Yjsj <= endDate && px.IsEffective == 1) | |
| 191 | + .Where((px, kd) => px.ItemCategory == "科美") | |
| 192 | + .SumAsync((px, kd) => px.TotalPrice); | |
| 193 | + | |
| 194 | + // 3. 减去对应的科美项目实退金额 | |
| 195 | + var kemeiRefund = await _db.Queryable<LqHytkJksyjEntity>() | |
| 196 | + .Where(x => stores.Contains(x.StoreId) && x.Tksj >= startDate && x.Tksj <= endDate && x.IsEffective == 1) | |
| 197 | + .Where(x => x.ItemCategory == "科美") | |
| 198 | + .SumAsync(x => x.Jksyj ?? 0); | |
| 199 | + | |
| 200 | + // 4. 结果 * 30% | |
| 201 | + entity.Income = (kemeiIncome - kemeiRefund) * 0.3m; | |
| 202 | + } | |
| 203 | + | |
| 204 | + /// <summary> | |
| 205 | + /// 计算成本部分 | |
| 206 | + /// </summary> | |
| 207 | + private async Task CalculateCost(LqShareStatisticsTechDeptEntity entity, string deptName, DateTime startDate, DateTime endDate, string statisticsMonth) | |
| 208 | + { | |
| 209 | + // 1. 成本-报销 (TODO: 需要确认报销分类的具体判定方式) | |
| 210 | + entity.CostReimbursement = 0; | |
| 211 | + | |
| 212 | + // 2. 成本-人工-科技部老师底薪 (TODO: 需要确认科技部老师工资表名和字段) | |
| 213 | + entity.CostTeacherBase = 0; | |
| 214 | + | |
| 215 | + // 3. 成本-人工-科技部手工费 | |
| 216 | + // 从消耗表中统计科技部老师的手工费 | |
| 217 | + // TODO: 需要确认如何识别科技部老师 | |
| 218 | + entity.CostTeacherManual = 0; | |
| 219 | + | |
| 220 | + // 4. 成本-人工-科技部开单提成 (TODO: 需要确认字段名) | |
| 221 | + entity.CostTeacherBillingComm = 0; | |
| 222 | + | |
| 223 | + // 5. 成本-人工-科技部消耗提成 (TODO: 需要确认字段名) | |
| 224 | + entity.CostTeacherConsumeComm = 0; | |
| 225 | + | |
| 226 | + // 6. 成本-人工-科技部总经理 (TODO: 需要确认科技部总经理工资表) | |
| 227 | + entity.CostGMBase = 0; | |
| 228 | + entity.CostGMComm = 0; | |
| 229 | + | |
| 230 | + // 保留字段 | |
| 231 | + entity.CostTeacherExpertComm = 0; | |
| 232 | + entity.CostTeacherOvertime = 0; | |
| 233 | + entity.RewardTechDept = 0; | |
| 234 | + entity.CostOther1 = 0; | |
| 235 | + entity.CostOther2 = 0; | |
| 236 | + } | |
| 237 | + | |
| 238 | + /// <summary> | |
| 239 | + /// 计算利润 | |
| 240 | + /// </summary> | |
| 241 | + private void CalculateProfit(LqShareStatisticsTechDeptEntity entity) | |
| 242 | + { | |
| 243 | + // 科技部利润 = 收入 - 成本报销 - 成本人工 - 其他 | |
| 244 | + var totalCost = entity.CostReimbursement | |
| 245 | + + entity.CostTeacherBase | |
| 246 | + + entity.CostTeacherManual | |
| 247 | + + entity.CostTeacherBillingComm | |
| 248 | + + entity.CostTeacherConsumeComm | |
| 249 | + + entity.CostTeacherExpertComm | |
| 250 | + + entity.CostTeacherOvertime | |
| 251 | + + entity.CostGMBase | |
| 252 | + + entity.CostGMComm | |
| 253 | + + entity.RewardTechDept | |
| 254 | + + entity.CostOther1 | |
| 255 | + + entity.CostOther2; | |
| 256 | + | |
| 257 | + entity.Profit = entity.Income - totalCost; | |
| 258 | + } | |
| 259 | + | |
| 260 | + #endregion | |
| 261 | + } | |
| 262 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqStoreExpenseService.cs
| ... | ... | @@ -66,10 +66,36 @@ namespace NCC.Extend.LqStoreExpense |
| 66 | 66 | .Where(x => x.Id == id && x.IsEffective == StatusEnum.有效.GetHashCode()) |
| 67 | 67 | .FirstAsync(); |
| 68 | 68 | _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); |
| 69 | - var output = entity.Adapt<LqStoreExpenseInfoOutput>(); | |
| 69 | + var output = new LqStoreExpenseInfoOutput | |
| 70 | + { | |
| 71 | + id = entity.Id, | |
| 72 | + storeId = entity.StoreId, | |
| 73 | + storeName = entity.StoreName, | |
| 74 | + expenseCategoryId = entity.ExpenseCategoryId, | |
| 75 | + expenseCategoryName = entity.ExpenseCategoryName, | |
| 76 | + expenseDate = entity.ExpenseDate, | |
| 77 | + unitPrice = entity.UnitPrice, | |
| 78 | + quantity = entity.Quantity, | |
| 79 | + amount = entity.Amount, | |
| 80 | + memo = entity.Memo, | |
| 81 | + relatedReimbursementId = entity.RelatedReimbursementId, | |
| 82 | + relatedPurchaseRecordId = entity.RelatedPurchaseRecordId, | |
| 83 | + createUser = entity.CreateUser, | |
| 84 | + createTime = entity.CreateTime, | |
| 85 | + updateUser = entity.UpdateUser, | |
| 86 | + updateTime = entity.UpdateTime | |
| 87 | + }; | |
| 88 | + | |
| 70 | 89 | if (!string.IsNullOrEmpty(entity.Attachment)) |
| 71 | 90 | { |
| 72 | - output.attachment = entity.Attachment.ToObject<List<NCC.Common.Model.FileControlsModel>>(); | |
| 91 | + try | |
| 92 | + { | |
| 93 | + output.attachment = entity.Attachment.ToObject<List<NCC.Common.Model.FileControlsModel>>(); | |
| 94 | + } | |
| 95 | + catch | |
| 96 | + { | |
| 97 | + output.attachment = new List<NCC.Common.Model.FileControlsModel>(); | |
| 98 | + } | |
| 73 | 99 | } |
| 74 | 100 | return output; |
| 75 | 101 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqStoreManagerSalaryService.cs
| ... | ... | @@ -294,6 +294,7 @@ namespace NCC.Extend |
| 294 | 294 | .ToDictionary(x => x.StoreId.ToString(), x => Convert.ToDecimal(x.ExpenseAmount ?? 0)); |
| 295 | 295 | |
| 296 | 296 | // 1.12 洗毛巾费用统计(只统计送出的记录,F_FlowType = 0) |
| 297 | + // 优先使用送出时间(F_SendTime),如果为空则使用创建时间(F_CreateTime) | |
| 297 | 298 | var laundryCostSql = $@" |
| 298 | 299 | SELECT |
| 299 | 300 | F_StoreId as StoreId, |
| ... | ... | @@ -301,7 +302,7 @@ namespace NCC.Extend |
| 301 | 302 | FROM lq_laundry_flow |
| 302 | 303 | WHERE F_IsEffective = 1 |
| 303 | 304 | AND F_FlowType = 0 |
| 304 | - AND DATE_FORMAT(F_CreateTime, '%Y%m') = @monthStr | |
| 305 | + AND DATE_FORMAT(COALESCE(F_SendTime, F_CreateTime), '%Y%m') = @monthStr | |
| 305 | 306 | GROUP BY F_StoreId"; |
| 306 | 307 | |
| 307 | 308 | var laundryCostData = await _db.Ado.SqlQueryAsync<dynamic>(laundryCostSql, new { monthStr }); | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqYcsdJsjService.cs
| ... | ... | @@ -347,6 +347,18 @@ namespace NCC.Extend.LqYcsdJsj |
| 347 | 347 | } |
| 348 | 348 | } |
| 349 | 349 | |
| 350 | + // 验证成员 UserID 不能为空 | |
| 351 | + if (input.members != null && input.members.Count > 0) | |
| 352 | + { | |
| 353 | + foreach (var member in input.members) | |
| 354 | + { | |
| 355 | + if (string.IsNullOrEmpty(member.userId)) | |
| 356 | + { | |
| 357 | + throw NCCException.Oh(ErrorCode.COM1000, $"成员【{member.userName}】的 UserID 不能为空"); | |
| 358 | + } | |
| 359 | + } | |
| 360 | + } | |
| 361 | + | |
| 350 | 362 | // 验证金三角名称是否已存在 |
| 351 | 363 | var existingJsj = await _db.Queryable<LqYcsdJsjEntity>().Where(x => x.Yf == input.yf && x.Md == input.md && x.Jsj == input.jsj).FirstAsync(); |
| 352 | 364 | if (existingJsj != null) |
| ... | ... | @@ -434,7 +446,7 @@ namespace NCC.Extend.LqYcsdJsj |
| 434 | 446 | if (string.IsNullOrEmpty(input.jsjId)) |
| 435 | 447 | throw NCCException.Oh("金三角ID不能为空"); |
| 436 | 448 | if (string.IsNullOrEmpty(input.userId)) |
| 437 | - throw NCCException.Oh("用户ID不能为空"); | |
| 449 | + throw NCCException.Oh("用户ID不能为空(请确保选择了有效的系统用户)"); | |
| 438 | 450 | if (string.IsNullOrEmpty(input.userName)) |
| 439 | 451 | throw NCCException.Oh("用户姓名不能为空"); |
| 440 | 452 | ... | ... |
sql/主任工资表新增毛利相关字段.sql
| ... | ... | @@ -31,3 +31,4 @@ ADD COLUMN F_GrossProfit DECIMAL(18,2) DEFAULT 0.00 COMMENT '毛利(销售业ç |
| 31 | 31 | ALTER TABLE lq_director_salary_statistics |
| 32 | 32 | MODIFY COLUMN F_StoreTotalPerformance DECIMAL(18,2) DEFAULT 0.00 COMMENT 'é—¨åº—æ€»ä¸šç»©ï¼ˆæ¯›åˆ©ï¼Œç”¨äºŽææˆè®¡ç®—ï¼‰'; |
| 33 | 33 | |
| 34 | + | ... | ... |
sql/修复储扣表ItemCategory字段为空的数据.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 修复储扣表(lq_kd_deductinfo)中 F_ItemCategory 字段为空的数据 | |
| 3 | +-- ============================================ | |
| 4 | +-- 说明:此脚本用于修复储扣表中品项分类字段为空的历史数据 | |
| 5 | +-- | |
| 6 | +-- 问题原因: | |
| 7 | +-- 1. 更新开单时使用了错误的字段(item.DeductId 而不是 item.ItemId)来查询品项分类 | |
| 8 | +-- 2. 如果 item.ItemId 为空或无效,查询会返回 null | |
| 9 | +-- | |
| 10 | +-- 修复逻辑: | |
| 11 | +-- 通过 F_ItemId 关联 lq_xmzl 表,获取品项分类(qt2)并更新到 F_ItemCategory 字段 | |
| 12 | +-- | |
| 13 | +-- 注意事项: | |
| 14 | +-- - 只更新有效记录(F_IsEffective = 1) | |
| 15 | +-- - 只更新分类字段为空的记录 | |
| 16 | +-- - 只更新品项分类存在且不为空的记录 | |
| 17 | + | |
| 18 | +-- ============================================ | |
| 19 | +-- 1. 查看需要修复的记录数 | |
| 20 | +-- ============================================ | |
| 21 | +SELECT | |
| 22 | + COUNT(*) as TotalNullCount, | |
| 23 | + COUNT(CASE WHEN item.qt2 IS NOT NULL THEN 1 END) as CanFixCount | |
| 24 | +FROM lq_kd_deductinfo deduct | |
| 25 | +LEFT JOIN lq_xmzl item ON deduct.F_ItemId = item.F_Id | |
| 26 | +WHERE deduct.F_IsEffective = 1 | |
| 27 | + AND deduct.F_ItemCategory IS NULL; | |
| 28 | + | |
| 29 | +-- ============================================ | |
| 30 | +-- 2. 修复历史数据:从项目资料表中获取品项分类 | |
| 31 | +-- ============================================ | |
| 32 | +UPDATE lq_kd_deductinfo deduct | |
| 33 | +INNER JOIN lq_xmzl item ON deduct.F_ItemId = item.F_Id | |
| 34 | +SET deduct.F_ItemCategory = item.qt2 | |
| 35 | +WHERE deduct.F_IsEffective = 1 | |
| 36 | + AND deduct.F_ItemCategory IS NULL | |
| 37 | + AND item.qt2 IS NOT NULL; | |
| 38 | + | |
| 39 | +-- ============================================ | |
| 40 | +-- 3. 验证修复结果 | |
| 41 | +-- ============================================ | |
| 42 | +-- 查看修复后的统计信息 | |
| 43 | +SELECT | |
| 44 | + COUNT(*) as TotalCount, | |
| 45 | + COUNT(F_ItemCategory) as HasCategoryCount, | |
| 46 | + COUNT(*) - COUNT(F_ItemCategory) as NullCategoryCount | |
| 47 | +FROM lq_kd_deductinfo | |
| 48 | +WHERE F_IsEffective = 1; | |
| 49 | + | |
| 50 | +-- 查看仍然为空的记录(需要人工处理) | |
| 51 | +SELECT | |
| 52 | + deduct.F_Id, | |
| 53 | + deduct.F_BillingId, | |
| 54 | + deduct.F_ItemId, | |
| 55 | + deduct.F_ItemName, | |
| 56 | + deduct.F_ItemCategory, | |
| 57 | + item.qt2 as ItemQt2, | |
| 58 | + CASE | |
| 59 | + WHEN item.F_Id IS NULL THEN '品项不存在' | |
| 60 | + WHEN item.qt2 IS NULL THEN '品项分类为空' | |
| 61 | + ELSE '其他原因' | |
| 62 | + END as Reason | |
| 63 | +FROM lq_kd_deductinfo deduct | |
| 64 | +LEFT JOIN lq_xmzl item ON deduct.F_ItemId = item.F_Id | |
| 65 | +WHERE deduct.F_IsEffective = 1 | |
| 66 | + AND deduct.F_ItemCategory IS NULL | |
| 67 | +LIMIT 10; | |
| 68 | + | ... | ... |
sql/创建合同成本按月统计表.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 创建合同成本按月统计表(lq_contract_monthly_cost) | |
| 3 | +-- ============================================ | |
| 4 | +-- 说明:用于按月统计每个合同的成本,便于后续统计和查询 | |
| 5 | +-- | |
| 6 | +-- 业务逻辑: | |
| 7 | +-- 1. 根据合同起始日期、结束日期,按月生成成本记录 | |
| 8 | +-- 2. 每个月成本 = 缴租金额 / 交租周期 | |
| 9 | +-- 3. 例如:合同一年,交费周期3个月,缴租金额3000元 | |
| 10 | +-- - 每个月成本 = 3000 / 3 = 1000元 | |
| 11 | +-- - 生成12个月的记录,每个月都是1000元 | |
| 12 | +-- | |
| 13 | +-- 字段说明: | |
| 14 | +-- F_Month:统计月份(格式:YYYY-MM-01,表示该月的第一天) | |
| 15 | +-- F_MonthlyCost:该月的合同成本(缴租金额 / 交租周期) | |
| 16 | +-- | |
| 17 | +-- 索引设计: | |
| 18 | +-- - 主键:F_Id | |
| 19 | +-- - 合同ID索引:F_ContractId(用于查询某个合同的所有月份成本) | |
| 20 | +-- - 月份索引:F_Month(用于按月份统计) | |
| 21 | +-- - 门店ID索引:F_StoreId(用于按门店统计) | |
| 22 | +-- - 联合索引:(F_StoreId, F_Month) - 用于查询某个门店某个月的成本 | |
| 23 | +-- - 联合索引:(F_ContractId, F_Month) - 用于查询某个合同某个月的成本 | |
| 24 | + | |
| 25 | +CREATE TABLE IF NOT EXISTS `lq_contract_monthly_cost` ( | |
| 26 | + `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID', | |
| 27 | + `F_ContractId` VARCHAR(50) NOT NULL COMMENT '合同ID(关联lq_contract.F_Id)', | |
| 28 | + `F_StoreId` VARCHAR(50) NOT NULL COMMENT '门店ID(关联lq_mdxx.F_Id,冗余字段便于查询)', | |
| 29 | + `F_StoreName` VARCHAR(200) NOT NULL COMMENT '店名(冗余字段,便于查询)', | |
| 30 | + `F_Category` VARCHAR(100) DEFAULT NULL COMMENT '分类(冗余字段,便于按分类统计)', | |
| 31 | + `F_Month` DATETIME NOT NULL COMMENT '统计月份(格式:YYYY-MM-01,表示该月的第一天)', | |
| 32 | + `F_MonthlyCost` DECIMAL(18,2) NOT NULL COMMENT '该月的合同成本(缴租金额 / 交租周期)', | |
| 33 | + `F_PaymentCycle` INT NOT NULL COMMENT '交租周期(冗余字段,便于查询)', | |
| 34 | + `F_PaymentAmount` DECIMAL(18,2) NOT NULL COMMENT '缴租金额(冗余字段,便于查询)', | |
| 35 | + `F_IsEffective` INT DEFAULT 1 COMMENT '是否有效(1-有效,0-无效)', | |
| 36 | + `F_CreateUser` VARCHAR(50) NOT NULL COMMENT '创建人ID', | |
| 37 | + `F_CreateTime` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | |
| 38 | + `F_UpdateTime` DATETIME DEFAULT NULL COMMENT '更新时间', | |
| 39 | + PRIMARY KEY (`F_Id`), | |
| 40 | + KEY `idx_contract_id` (`F_ContractId`) COMMENT '合同ID索引', | |
| 41 | + KEY `idx_month` (`F_Month`) COMMENT '月份索引', | |
| 42 | + KEY `idx_store_id` (`F_StoreId`) COMMENT '门店ID索引', | |
| 43 | + KEY `idx_category` (`F_Category`) COMMENT '分类索引', | |
| 44 | + KEY `idx_store_month` (`F_StoreId`, `F_Month`) COMMENT '门店+月份联合索引', | |
| 45 | + KEY `idx_contract_month` (`F_ContractId`, `F_Month`) COMMENT '合同+月份联合索引', | |
| 46 | + KEY `idx_category_month` (`F_Category`, `F_Month`) COMMENT '分类+月份联合索引', | |
| 47 | + KEY `idx_is_effective` (`F_IsEffective`) COMMENT '是否有效索引', | |
| 48 | + KEY `idx_create_time` (`F_CreateTime`) COMMENT '创建时间索引' | |
| 49 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='合同成本按月统计表'; | |
| 50 | + | |
| 51 | +-- ============================================ | |
| 52 | +-- 数据示例说明 | |
| 53 | +-- ============================================ | |
| 54 | +-- 合同示例: | |
| 55 | +-- F_ContractId: '768041985045955845' | |
| 56 | +-- F_StoreId: '1649328471923847168' | |
| 57 | +-- F_StoreName: '绿纤总部' | |
| 58 | +-- F_ContractStartDate: '2025-01-01 00:00:00' | |
| 59 | +-- F_ContractEndDate: '2025-12-31 23:59:59' | |
| 60 | +-- F_PaymentAmount: 3000.00 | |
| 61 | +-- F_PaymentCycle: 3 | |
| 62 | +-- | |
| 63 | +-- 每个月成本 = 3000 / 3 = 1000元 | |
| 64 | +-- | |
| 65 | +-- 对应的成本记录(自动生成): | |
| 66 | +-- 记录1: F_Month='2025-01-01', F_MonthlyCost=1000.00 | |
| 67 | +-- 记录2: F_Month='2025-02-01', F_MonthlyCost=1000.00 | |
| 68 | +-- 记录3: F_Month='2025-03-01', F_MonthlyCost=1000.00 | |
| 69 | +-- 记录4: F_Month='2025-04-01', F_MonthlyCost=1000.00 | |
| 70 | +-- ...(共12条记录) | |
| 71 | + | ... | ... |
sql/合同成本表添加分类字段.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 为合同成本按月统计表(lq_contract_monthly_cost)添加分类字段 | |
| 3 | +-- ============================================ | |
| 4 | +-- 说明:此脚本为合同成本表添加分类字段,用于按分类统计合同成本 | |
| 5 | +-- | |
| 6 | +-- 字段说明: | |
| 7 | +-- F_Category:分类(冗余字段,便于按分类统计) | |
| 8 | +-- | |
| 9 | +-- 业务含义: | |
| 10 | +-- - 分类字段用于区分不同类型的合同(如:租门店、员工宿舍、车辆、场所等) | |
| 11 | +-- - 便于后续按分类统计合同成本 | |
| 12 | +-- | |
| 13 | +-- 注意事项: | |
| 14 | +-- - 字段类型为VARCHAR(100),允许为NULL(历史数据可能没有分类) | |
| 15 | +-- - 字段位置:放在 F_StoreName 字段之后 | |
| 16 | +-- - 创建后需要更新历史数据,从合同表中获取对应的分类 | |
| 17 | + | |
| 18 | +-- ============================================ | |
| 19 | +-- 1. 添加分类字段 | |
| 20 | +-- ============================================ | |
| 21 | +ALTER TABLE `lq_contract_monthly_cost` | |
| 22 | +ADD COLUMN `F_Category` VARCHAR(100) NULL COMMENT '分类(冗余字段,便于按分类统计)' AFTER `F_StoreName`; | |
| 23 | + | |
| 24 | +-- ============================================ | |
| 25 | +-- 2. 添加分类索引 | |
| 26 | +-- ============================================ | |
| 27 | +ALTER TABLE `lq_contract_monthly_cost` | |
| 28 | +ADD INDEX `idx_category` (`F_Category`) COMMENT '分类索引'; | |
| 29 | + | |
| 30 | +-- ============================================ | |
| 31 | +-- 3. 添加分类+月份联合索引 | |
| 32 | +-- ============================================ | |
| 33 | +ALTER TABLE `lq_contract_monthly_cost` | |
| 34 | +ADD INDEX `idx_category_month` (`F_Category`, `F_Month`) COMMENT '分类+月份联合索引'; | |
| 35 | + | |
| 36 | +-- ============================================ | |
| 37 | +-- 4. 更新历史数据:从合同表中获取分类 | |
| 38 | +-- ============================================ | |
| 39 | +UPDATE `lq_contract_monthly_cost` cost | |
| 40 | +INNER JOIN `lq_contract` contract ON cost.F_ContractId = contract.F_Id | |
| 41 | +SET cost.F_Category = contract.F_Category | |
| 42 | +WHERE cost.F_Category IS NULL | |
| 43 | + AND contract.F_Category IS NOT NULL; | |
| 44 | + | |
| 45 | +-- ============================================ | |
| 46 | +-- 5. 验证更新结果 | |
| 47 | +-- ============================================ | |
| 48 | +-- 查看更新后的统计信息 | |
| 49 | +SELECT | |
| 50 | + COUNT(*) as TotalCount, | |
| 51 | + COUNT(F_Category) as HasCategoryCount, | |
| 52 | + COUNT(*) - COUNT(F_Category) as NullCategoryCount | |
| 53 | +FROM lq_contract_monthly_cost | |
| 54 | +WHERE F_IsEffective = 1; | |
| 55 | + | |
| 56 | +-- 查看各分类的统计信息 | |
| 57 | +SELECT | |
| 58 | + F_Category, | |
| 59 | + COUNT(*) as RecordCount, | |
| 60 | + SUM(F_MonthlyCost) as TotalCost | |
| 61 | +FROM lq_contract_monthly_cost | |
| 62 | +WHERE F_IsEffective = 1 | |
| 63 | +GROUP BY F_Category | |
| 64 | +ORDER BY TotalCost DESC; | |
| 65 | + | |
| 66 | + | ... | ... |
sql/开单扣减信息表添加开单时间字段.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 为开单扣减信息表(lq_kd_deductinfo)添加开单时间字段 | |
| 3 | +-- ============================================ | |
| 4 | +-- 说明:此脚本为开单扣减信息表添加开单时间字段,用于存储对应的开单时间 | |
| 5 | +-- | |
| 6 | +-- 字段说明: | |
| 7 | +-- F_BillingTime:开单时间,用于存储对应的开单记录的开单时间(kdrq) | |
| 8 | +-- | |
| 9 | +-- 业务含义: | |
| 10 | +-- - 开单时间用于记录储扣对应的开单时间,便于统计和查询 | |
| 11 | +-- - 开单时间来源于开单记录表(lq_kd_kdjlb)的 kdrq 字段 | |
| 12 | +-- | |
| 13 | +-- 注意事项: | |
| 14 | +-- - 字段类型为DATETIME,允许为NULL(历史数据可能没有开单时间) | |
| 15 | +-- - 字段位置:放在 F_BillingId 字段之后 | |
| 16 | +-- - 创建后需要更新历史数据,从开单记录表中获取对应的开单时间 | |
| 17 | + | |
| 18 | +-- ============================================ | |
| 19 | +-- 1. 添加开单时间字段 | |
| 20 | +-- ============================================ | |
| 21 | +ALTER TABLE `lq_kd_deductinfo` | |
| 22 | +ADD COLUMN `F_BillingTime` DATETIME NULL COMMENT '开单时间' AFTER `F_BillingId`; | |
| 23 | + | |
| 24 | +-- ============================================ | |
| 25 | +-- 2. 更新历史数据:从开单记录表中获取开单时间 | |
| 26 | +-- ============================================ | |
| 27 | +UPDATE `lq_kd_deductinfo` deduct | |
| 28 | +INNER JOIN `lq_kd_kdjlb` billing ON deduct.F_BillingId = billing.F_Id | |
| 29 | +SET deduct.F_BillingTime = billing.kdrq | |
| 30 | +WHERE deduct.F_BillingTime IS NULL | |
| 31 | + AND billing.kdrq IS NOT NULL; | |
| 32 | + | |
| 33 | +-- ============================================ | |
| 34 | +-- 3. 验证更新结果 | |
| 35 | +-- ============================================ | |
| 36 | +-- 查看更新后的统计信息 | |
| 37 | +SELECT | |
| 38 | + COUNT(*) as TotalCount, | |
| 39 | + COUNT(F_BillingTime) as HasBillingTimeCount, | |
| 40 | + COUNT(*) - COUNT(F_BillingTime) as NullBillingTimeCount | |
| 41 | +FROM lq_kd_deductinfo; | |
| 42 | + | |
| 43 | +-- 查看有开单时间但开单记录不存在的记录(数据异常检查) | |
| 44 | +SELECT | |
| 45 | + deduct.F_Id, | |
| 46 | + deduct.F_BillingId, | |
| 47 | + deduct.F_BillingTime | |
| 48 | +FROM lq_kd_deductinfo deduct | |
| 49 | +LEFT JOIN lq_kd_kdjlb billing ON deduct.F_BillingId = billing.F_Id | |
| 50 | +WHERE deduct.F_BillingTime IS NOT NULL | |
| 51 | + AND billing.F_Id IS NULL | |
| 52 | +LIMIT 10; | |
| 53 | + | ... | ... |
sql/排查生美业绩统计差异-简化版.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 排查生美业绩统计差异 - 简化版 | |
| 3 | +-- ============================================ | |
| 4 | +-- 问题:品项明细表统计生美数据是1869781.81,但日报天王团统计教育一部+教育二部合计是1876061.21,差异6279.40 | |
| 5 | +-- | |
| 6 | +-- 分析思路: | |
| 7 | +-- 1. 检查是否有门店在lq_md_target表中有多条记录(同一月份) | |
| 8 | +-- 2. 对比品项明细表统计和日报天王团统计的差异 | |
| 9 | +-- 3. 检查是否有数据被重复统计 | |
| 10 | + | |
| 11 | +-- ============================================ | |
| 12 | +-- 1. 品项明细表统计生美业绩(所有门店,不限制部门归属) | |
| 13 | +-- ============================================ | |
| 14 | +SELECT | |
| 15 | + '品项明细表统计' AS 统计来源, | |
| 16 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩总额 | |
| 17 | +FROM lq_kd_pxmx pxmx | |
| 18 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 19 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 20 | +WHERE pxmx.F_IsEffective = 1 | |
| 21 | + AND billing.F_IsEffective = 1 | |
| 22 | + AND item.F_IsEffective = 1 | |
| 23 | + AND item.qt2 = '生美' | |
| 24 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 25 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH); | |
| 26 | + | |
| 27 | +-- ============================================ | |
| 28 | +-- 2. 日报天王团统计生美业绩(只统计有部门归属的门店,按部门分组) | |
| 29 | +-- ============================================ | |
| 30 | +SELECT | |
| 31 | + '日报天王团统计' AS 统计来源, | |
| 32 | + target.F_EducationDepartment as 部门ID, | |
| 33 | + dept.F_FullName as 部门名称, | |
| 34 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩 | |
| 35 | +FROM lq_kd_pxmx pxmx | |
| 36 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 37 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 38 | +INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 39 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 40 | +LEFT JOIN base_organize dept ON target.F_EducationDepartment = dept.F_Id | |
| 41 | +WHERE pxmx.F_IsEffective = 1 | |
| 42 | + AND billing.F_IsEffective = 1 | |
| 43 | + AND item.F_IsEffective = 1 | |
| 44 | + AND item.qt2 = '生美' | |
| 45 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 46 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 47 | + AND target.F_EducationDepartment IS NOT NULL | |
| 48 | + AND target.F_EducationDepartment != '' | |
| 49 | +GROUP BY target.F_EducationDepartment, dept.F_FullName; | |
| 50 | + | |
| 51 | +-- ============================================ | |
| 52 | +-- 3. 检查是否有门店在lq_md_target表中有多条记录(同一月份) | |
| 53 | +-- ============================================ | |
| 54 | +SELECT | |
| 55 | + F_StoreId, | |
| 56 | + F_Month, | |
| 57 | + COUNT(*) as record_count | |
| 58 | +FROM lq_md_target | |
| 59 | +WHERE F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 60 | + AND (F_EducationDepartment IS NOT NULL AND F_EducationDepartment != '') | |
| 61 | +GROUP BY F_StoreId, F_Month | |
| 62 | +HAVING COUNT(*) > 1; | |
| 63 | + | |
| 64 | +-- ============================================ | |
| 65 | +-- 4. 检查是否有生美品项的开单记录,但门店在lq_md_target表中没有设置F_EducationDepartment | |
| 66 | +-- ============================================ | |
| 67 | +SELECT | |
| 68 | + '未归属门店的生美业绩' AS 统计来源, | |
| 69 | + COUNT(DISTINCT billing.djmd) as 门店数量, | |
| 70 | + COUNT(*) as 开单记录数, | |
| 71 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩总额 | |
| 72 | +FROM lq_kd_pxmx pxmx | |
| 73 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 74 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 75 | +LEFT JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 76 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 77 | +WHERE pxmx.F_IsEffective = 1 | |
| 78 | + AND billing.F_IsEffective = 1 | |
| 79 | + AND item.F_IsEffective = 1 | |
| 80 | + AND item.qt2 = '生美' | |
| 81 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 82 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 83 | + AND (target.F_StoreId IS NULL | |
| 84 | + OR target.F_EducationDepartment IS NULL | |
| 85 | + OR target.F_EducationDepartment = ''); | |
| 86 | + | |
| 87 | +-- ============================================ | |
| 88 | +-- 5. 关键检查:查看每个门店的生美业绩,看看是否有重复统计 | |
| 89 | +-- ============================================ | |
| 90 | +SELECT | |
| 91 | + billing.djmd as 门店ID, | |
| 92 | + md.Dm as 门店名称, | |
| 93 | + target.F_EducationDepartment as 教育部门ID, | |
| 94 | + dept.F_FullName as 教育部门名称, | |
| 95 | + COUNT(*) as 开单记录数, | |
| 96 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩, | |
| 97 | + COUNT(DISTINCT target.F_Id) as 目标表记录数 | |
| 98 | +FROM lq_kd_pxmx pxmx | |
| 99 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 100 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 101 | +INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 102 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 103 | +LEFT JOIN lq_mdxx md ON billing.djmd = md.F_Id | |
| 104 | +LEFT JOIN base_organize dept ON target.F_EducationDepartment = dept.F_Id | |
| 105 | +WHERE pxmx.F_IsEffective = 1 | |
| 106 | + AND billing.F_IsEffective = 1 | |
| 107 | + AND item.F_IsEffective = 1 | |
| 108 | + AND item.qt2 = '生美' | |
| 109 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 110 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 111 | + AND target.F_EducationDepartment IS NOT NULL | |
| 112 | + AND target.F_EducationDepartment != '' | |
| 113 | +GROUP BY billing.djmd, md.Dm, target.F_EducationDepartment, dept.F_FullName | |
| 114 | +HAVING COUNT(DISTINCT target.F_Id) > 1 | |
| 115 | +ORDER BY 生美业绩 DESC; | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | ... | ... |
sql/排查生美业绩统计差异详细.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 排查生美业绩统计差异详细分析 | |
| 3 | +-- ============================================ | |
| 4 | +-- 问题:品项明细表统计生美数据是1869781.81,但日报天王团统计教育一部+教育二部合计是1876061.21,差异6279.40 | |
| 5 | +-- | |
| 6 | +-- 分析思路: | |
| 7 | +-- 1. 检查是否有门店在lq_md_target表中有多条记录(同一月份) | |
| 8 | +-- 2. 对比品项明细表统计和日报天王团统计的差异 | |
| 9 | +-- 3. 检查是否有数据被重复统计 | |
| 10 | + | |
| 11 | +-- ============================================ | |
| 12 | +-- 1. 检查lq_md_target表中是否有重复记录(同一门店同一月份多条记录) | |
| 13 | +-- ============================================ | |
| 14 | +SELECT | |
| 15 | + F_StoreId, | |
| 16 | + F_Month, | |
| 17 | + COUNT(*) as record_count, | |
| 18 | + GROUP_CONCAT(DISTINCT F_EducationDepartment ORDER BY F_EducationDepartment) as education_depts | |
| 19 | +FROM lq_md_target | |
| 20 | +WHERE F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 21 | + AND (F_EducationDepartment IS NOT NULL AND F_EducationDepartment != '') | |
| 22 | +GROUP BY F_StoreId, F_Month | |
| 23 | +HAVING COUNT(*) > 1; | |
| 24 | + | |
| 25 | +-- ============================================ | |
| 26 | +-- 2. 品项明细表统计生美业绩(所有门店,不限制部门归属) | |
| 27 | +-- ============================================ | |
| 28 | +SELECT | |
| 29 | + '品项明细表统计' AS 统计来源, | |
| 30 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩总额 | |
| 31 | +FROM lq_kd_pxmx pxmx | |
| 32 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 33 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 34 | +WHERE pxmx.F_IsEffective = 1 | |
| 35 | + AND billing.F_IsEffective = 1 | |
| 36 | + AND item.F_IsEffective = 1 | |
| 37 | + AND item.qt2 = '生美' | |
| 38 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 39 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH); | |
| 40 | + | |
| 41 | +-- ============================================ | |
| 42 | +-- 3. 日报天王团统计生美业绩(只统计有部门归属的门店,按部门分组) | |
| 43 | +-- ============================================ | |
| 44 | +SELECT | |
| 45 | + '日报天王团统计' AS 统计来源, | |
| 46 | + target.F_EducationDepartment as 部门ID, | |
| 47 | + dept.F_FullName as 部门名称, | |
| 48 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩 | |
| 49 | +FROM lq_kd_pxmx pxmx | |
| 50 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 51 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 52 | +INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 53 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 54 | +LEFT JOIN base_organize dept ON target.F_EducationDepartment = dept.F_Id | |
| 55 | +WHERE pxmx.F_IsEffective = 1 | |
| 56 | + AND billing.F_IsEffective = 1 | |
| 57 | + AND item.F_IsEffective = 1 | |
| 58 | + AND item.qt2 = '生美' | |
| 59 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 60 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 61 | + AND target.F_EducationDepartment IS NOT NULL | |
| 62 | + AND target.F_EducationDepartment != '' | |
| 63 | +GROUP BY target.F_EducationDepartment, dept.F_FullName; | |
| 64 | + | |
| 65 | +-- ============================================ | |
| 66 | +-- 4. 检查是否有门店在lq_md_target表中有多条记录,导致重复统计 | |
| 67 | +-- ============================================ | |
| 68 | +SELECT | |
| 69 | + billing.djmd as 门店ID, | |
| 70 | + md.Dm as 门店名称, | |
| 71 | + COUNT(DISTINCT target.F_Id) as 目标表记录数, | |
| 72 | + COUNT(*) as 开单记录数, | |
| 73 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩总额, | |
| 74 | + GROUP_CONCAT(DISTINCT target.F_EducationDepartment ORDER BY target.F_EducationDepartment) as 教育部门列表 | |
| 75 | +FROM lq_kd_pxmx pxmx | |
| 76 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 77 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 78 | +INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 79 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 80 | +LEFT JOIN lq_mdxx md ON billing.djmd = md.F_Id | |
| 81 | +WHERE pxmx.F_IsEffective = 1 | |
| 82 | + AND billing.F_IsEffective = 1 | |
| 83 | + AND item.F_IsEffective = 1 | |
| 84 | + AND item.qt2 = '生美' | |
| 85 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 86 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 87 | + AND target.F_EducationDepartment IS NOT NULL | |
| 88 | + AND target.F_EducationDepartment != '' | |
| 89 | +GROUP BY billing.djmd, md.Dm | |
| 90 | +HAVING COUNT(DISTINCT target.F_Id) > 1; | |
| 91 | + | |
| 92 | +-- ============================================ | |
| 93 | +-- 5. 检查是否有生美品项的开单记录,但门店在lq_md_target表中没有设置F_EducationDepartment | |
| 94 | +-- ============================================ | |
| 95 | +SELECT | |
| 96 | + '未归属门店的生美业绩' AS 统计来源, | |
| 97 | + COUNT(DISTINCT billing.djmd) as 门店数量, | |
| 98 | + COUNT(*) as 开单记录数, | |
| 99 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩总额 | |
| 100 | +FROM lq_kd_pxmx pxmx | |
| 101 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 102 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 103 | +LEFT JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 104 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 105 | +WHERE pxmx.F_IsEffective = 1 | |
| 106 | + AND billing.F_IsEffective = 1 | |
| 107 | + AND item.F_IsEffective = 1 | |
| 108 | + AND item.qt2 = '生美' | |
| 109 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 110 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 111 | + AND (target.F_StoreId IS NULL | |
| 112 | + OR target.F_EducationDepartment IS NULL | |
| 113 | + OR target.F_EducationDepartment = ''); | |
| 114 | + | |
| 115 | +-- ============================================ | |
| 116 | +-- 6. 关键检查:查看每个门店的生美业绩,看看是否有重复统计 | |
| 117 | +-- ============================================ | |
| 118 | +SELECT | |
| 119 | + billing.djmd as 门店ID, | |
| 120 | + md.Dm as 门店名称, | |
| 121 | + target.F_EducationDepartment as 教育部门ID, | |
| 122 | + dept.F_FullName as 教育部门名称, | |
| 123 | + COUNT(*) as 开单记录数, | |
| 124 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩, | |
| 125 | + COUNT(DISTINCT target.F_Id) as 目标表记录数 | |
| 126 | +FROM lq_kd_pxmx pxmx | |
| 127 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 128 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 129 | +INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 130 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 131 | +LEFT JOIN lq_mdxx md ON billing.djmd = md.F_Id | |
| 132 | +LEFT JOIN base_organize dept ON target.F_EducationDepartment = dept.F_Id | |
| 133 | +WHERE pxmx.F_IsEffective = 1 | |
| 134 | + AND billing.F_IsEffective = 1 | |
| 135 | + AND item.F_IsEffective = 1 | |
| 136 | + AND item.qt2 = '生美' | |
| 137 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 138 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 139 | + AND target.F_EducationDepartment IS NOT NULL | |
| 140 | + AND target.F_EducationDepartment != '' | |
| 141 | +GROUP BY billing.djmd, md.Dm, target.F_EducationDepartment, dept.F_FullName | |
| 142 | +ORDER BY 生美业绩 DESC; | |
| 143 | + | |
| 144 | +-- ============================================ | |
| 145 | +-- 7. 检查是否有门店在lq_md_target表中有多条记录(不同月份,但查询时可能有问题) | |
| 146 | +-- ============================================ | |
| 147 | +SELECT | |
| 148 | + F_StoreId, | |
| 149 | + COUNT(DISTINCT F_Month) as 月份数, | |
| 150 | + GROUP_CONCAT(DISTINCT F_Month ORDER BY F_Month) as 月份列表, | |
| 151 | + COUNT(*) as 总记录数 | |
| 152 | +FROM lq_md_target | |
| 153 | +WHERE F_StoreId IN ( | |
| 154 | + SELECT DISTINCT billing.djmd | |
| 155 | + FROM lq_kd_pxmx pxmx | |
| 156 | + INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 157 | + INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 158 | + WHERE pxmx.F_IsEffective = 1 | |
| 159 | + AND billing.F_IsEffective = 1 | |
| 160 | + AND item.F_IsEffective = 1 | |
| 161 | + AND item.qt2 = '生美' | |
| 162 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 163 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 164 | +) | |
| 165 | + AND (F_EducationDepartment IS NOT NULL AND F_EducationDepartment != '') | |
| 166 | +GROUP BY F_StoreId | |
| 167 | +HAVING COUNT(*) > 1; | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | ... | ... |
sql/检查生美业绩统计差异.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 检查生美业绩统计差异 | |
| 3 | +-- ============================================ | |
| 4 | +-- 问题:品项明细表统计生美数据是1869781.81,但日报天王团统计教育一部+教育二部合计是1876061.21,差异6279.40 | |
| 5 | +-- | |
| 6 | +-- 可能原因: | |
| 7 | +-- 1. 门店在lq_md_target表中有重复记录(同一月份多条记录) | |
| 8 | +-- 2. 统计范围不一致(时间范围或门店范围) | |
| 9 | +-- 3. 数据关联逻辑问题 | |
| 10 | + | |
| 11 | +-- ============================================ | |
| 12 | +-- 1. 检查lq_md_target表中是否有重复记录(同一门店同一月份多条记录) | |
| 13 | +-- ============================================ | |
| 14 | +SELECT | |
| 15 | + F_StoreId, | |
| 16 | + F_Month, | |
| 17 | + COUNT(*) as record_count, | |
| 18 | + GROUP_CONCAT(DISTINCT F_EducationDepartment) as education_depts | |
| 19 | +FROM lq_md_target | |
| 20 | +WHERE F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 21 | + AND (F_EducationDepartment IS NOT NULL AND F_EducationDepartment != '') | |
| 22 | +GROUP BY F_StoreId, F_Month | |
| 23 | +HAVING COUNT(*) > 1; | |
| 24 | + | |
| 25 | +-- ============================================ | |
| 26 | +-- 2. 检查品项明细表统计生美业绩(所有门店,不限制部门归属) | |
| 27 | +-- ============================================ | |
| 28 | +SELECT | |
| 29 | + '品项明细表统计' AS 统计来源, | |
| 30 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩总额 | |
| 31 | +FROM lq_kd_pxmx pxmx | |
| 32 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 33 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 34 | +WHERE pxmx.F_IsEffective = 1 | |
| 35 | + AND billing.F_IsEffective = 1 | |
| 36 | + AND item.F_IsEffective = 1 | |
| 37 | + AND item.qt2 = '生美' | |
| 38 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 39 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH); | |
| 40 | + | |
| 41 | +-- ============================================ | |
| 42 | +-- 3. 检查日报天王团统计生美业绩(只统计有部门归属的门店,按部门分组) | |
| 43 | +-- ============================================ | |
| 44 | +SELECT | |
| 45 | + '日报天王团统计' AS 统计来源, | |
| 46 | + target.F_EducationDepartment as 部门ID, | |
| 47 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩 | |
| 48 | +FROM lq_kd_pxmx pxmx | |
| 49 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 50 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 51 | +INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 52 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 53 | +WHERE pxmx.F_IsEffective = 1 | |
| 54 | + AND billing.F_IsEffective = 1 | |
| 55 | + AND item.F_IsEffective = 1 | |
| 56 | + AND item.qt2 = '生美' | |
| 57 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 58 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 59 | + AND target.F_EducationDepartment IS NOT NULL | |
| 60 | + AND target.F_EducationDepartment != '' | |
| 61 | +GROUP BY target.F_EducationDepartment; | |
| 62 | + | |
| 63 | +-- ============================================ | |
| 64 | +-- 4. 检查是否有生美品项的开单记录,但门店在lq_md_target表中没有设置F_EducationDepartment | |
| 65 | +-- ============================================ | |
| 66 | +SELECT | |
| 67 | + '未归属门店的生美业绩' AS 统计来源, | |
| 68 | + COUNT(DISTINCT billing.djmd) as 门店数量, | |
| 69 | + COUNT(*) as 开单记录数, | |
| 70 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩总额 | |
| 71 | +FROM lq_kd_pxmx pxmx | |
| 72 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 73 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 74 | +LEFT JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 75 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 76 | +WHERE pxmx.F_IsEffective = 1 | |
| 77 | + AND billing.F_IsEffective = 1 | |
| 78 | + AND item.F_IsEffective = 1 | |
| 79 | + AND item.qt2 = '生美' | |
| 80 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 81 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 82 | + AND (target.F_StoreId IS NULL | |
| 83 | + OR target.F_EducationDepartment IS NULL | |
| 84 | + OR target.F_EducationDepartment = ''); | |
| 85 | + | |
| 86 | +-- ============================================ | |
| 87 | +-- 5. 检查是否有门店在lq_md_target表中有多条记录(可能导致重复统计) | |
| 88 | +-- ============================================ | |
| 89 | +SELECT | |
| 90 | + billing.djmd as 门店ID, | |
| 91 | + COUNT(DISTINCT target.F_Id) as 目标表记录数, | |
| 92 | + COUNT(*) as 开单记录数, | |
| 93 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩总额, | |
| 94 | + GROUP_CONCAT(DISTINCT target.F_EducationDepartment) as 教育部门列表 | |
| 95 | +FROM lq_kd_pxmx pxmx | |
| 96 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 97 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 98 | +INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 99 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 100 | +WHERE pxmx.F_IsEffective = 1 | |
| 101 | + AND billing.F_IsEffective = 1 | |
| 102 | + AND item.F_IsEffective = 1 | |
| 103 | + AND item.qt2 = '生美' | |
| 104 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 105 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 106 | + AND target.F_EducationDepartment IS NOT NULL | |
| 107 | + AND target.F_EducationDepartment != '' | |
| 108 | +GROUP BY billing.djmd | |
| 109 | +HAVING COUNT(DISTINCT target.F_Id) > 1; | |
| 110 | + | |
| 111 | +-- ============================================ | |
| 112 | +-- 6. 详细检查:查看每个门店的生美业绩和部门归属情况 | |
| 113 | +-- ============================================ | |
| 114 | +SELECT | |
| 115 | + billing.djmd as 门店ID, | |
| 116 | + md.Dm as 门店名称, | |
| 117 | + target.F_EducationDepartment as 教育部门ID, | |
| 118 | + dept.F_FullName as 教育部门名称, | |
| 119 | + COUNT(*) as 开单记录数, | |
| 120 | + COALESCE(SUM(pxmx.F_ActualPrice), 0) as 生美业绩 | |
| 121 | +FROM lq_kd_pxmx pxmx | |
| 122 | +INNER JOIN lq_kd_kdjlb billing ON pxmx.glkdbh = billing.F_Id | |
| 123 | +INNER JOIN lq_xmzl item ON pxmx.px = item.F_Id | |
| 124 | +INNER JOIN lq_md_target target ON billing.djmd = target.F_StoreId | |
| 125 | + AND target.F_Month = DATE_FORMAT(NOW(), '%Y%m') | |
| 126 | +LEFT JOIN lq_mdxx md ON billing.djmd = md.F_Id | |
| 127 | +LEFT JOIN base_organize dept ON target.F_EducationDepartment = dept.F_Id | |
| 128 | +WHERE pxmx.F_IsEffective = 1 | |
| 129 | + AND billing.F_IsEffective = 1 | |
| 130 | + AND item.F_IsEffective = 1 | |
| 131 | + AND item.qt2 = '生美' | |
| 132 | + AND billing.kdrq >= DATE_FORMAT(NOW(), '%Y-%m-01') | |
| 133 | + AND billing.kdrq < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH) | |
| 134 | + AND target.F_EducationDepartment IS NOT NULL | |
| 135 | + AND target.F_EducationDepartment != '' | |
| 136 | +GROUP BY billing.djmd, md.Dm, target.F_EducationDepartment, dept.F_FullName | |
| 137 | +ORDER BY 生美业绩 DESC; | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | ... | ... |
sql/清洗流水表添加送出送回时间字段.sql
0 → 100644
| 1 | +-- ============================================ | |
| 2 | +-- 为清洗流水表(lq_laundry_flow)添加送出/送回时间字段 | |
| 3 | +-- ============================================ | |
| 4 | +-- 说明:此脚本为清洗流水表添加送出时间和送回时间字段,用于记录实际的送出/送回时间 | |
| 5 | +-- | |
| 6 | +-- 字段说明: | |
| 7 | +-- F_SendTime:送出时间,用于记录实际的送出时间(流水类型为0时使用) | |
| 8 | +-- F_ReturnTime:送回时间,用于记录实际的送回时间(流水类型为1时使用) | |
| 9 | +-- | |
| 10 | +-- 业务含义: | |
| 11 | +-- - 送出时间:记录实际送出清洗的时间,便于后续统计 | |
| 12 | +-- - 送回时间:记录实际送回清洗的时间,便于后续统计 | |
| 13 | +-- - F_CreateTime:保持为记录创建时间(系统时间),用于记录数据录入时间 | |
| 14 | +-- | |
| 15 | +-- 注意事项: | |
| 16 | +-- - 字段类型为DATETIME,允许为NULL(历史数据可能没有这些时间) | |
| 17 | +-- - 字段位置:放在 F_CreateTime 字段之后 | |
| 18 | +-- - 创建后需要更新历史数据,将 F_CreateTime 的值复制到对应的时间字段 | |
| 19 | + | |
| 20 | +-- ============================================ | |
| 21 | +-- 1. 添加送出时间字段 | |
| 22 | +-- ============================================ | |
| 23 | +ALTER TABLE `lq_laundry_flow` | |
| 24 | +ADD COLUMN `F_SendTime` DATETIME NULL COMMENT '送出时间(流水类型为0时使用)' AFTER `F_CreateTime`; | |
| 25 | + | |
| 26 | +-- ============================================ | |
| 27 | +-- 2. 添加送回时间字段 | |
| 28 | +-- ============================================ | |
| 29 | +ALTER TABLE `lq_laundry_flow` | |
| 30 | +ADD COLUMN `F_ReturnTime` DATETIME NULL COMMENT '送回时间(流水类型为1时使用)' AFTER `F_SendTime`; | |
| 31 | + | |
| 32 | +-- ============================================ | |
| 33 | +-- 3. 更新历史数据:将创建时间复制到对应的时间字段 | |
| 34 | +-- ============================================ | |
| 35 | +-- 更新送出记录:将创建时间复制到送出时间 | |
| 36 | +UPDATE `lq_laundry_flow` | |
| 37 | +SET `F_SendTime` = `F_CreateTime` | |
| 38 | +WHERE `F_FlowType` = 0 | |
| 39 | + AND `F_SendTime` IS NULL | |
| 40 | + AND `F_CreateTime` IS NOT NULL; | |
| 41 | + | |
| 42 | +-- 更新送回记录:将创建时间复制到送回时间 | |
| 43 | +UPDATE `lq_laundry_flow` | |
| 44 | +SET `F_ReturnTime` = `F_CreateTime` | |
| 45 | +WHERE `F_FlowType` = 1 | |
| 46 | + AND `F_ReturnTime` IS NULL | |
| 47 | + AND `F_CreateTime` IS NOT NULL; | |
| 48 | + | |
| 49 | +-- ============================================ | |
| 50 | +-- 4. 验证更新结果 | |
| 51 | +-- ============================================ | |
| 52 | +-- 查看更新后的统计信息 | |
| 53 | +SELECT | |
| 54 | + F_FlowType, | |
| 55 | + CASE WHEN F_FlowType = 0 THEN '送出' ELSE '送回' END as FlowTypeName, | |
| 56 | + COUNT(*) as TotalCount, | |
| 57 | + COUNT(CASE WHEN F_FlowType = 0 THEN F_SendTime END) as HasSendTimeCount, | |
| 58 | + COUNT(CASE WHEN F_FlowType = 1 THEN F_ReturnTime END) as HasReturnTimeCount | |
| 59 | +FROM lq_laundry_flow | |
| 60 | +WHERE F_IsEffective = 1 | |
| 61 | +GROUP BY F_FlowType; | |
| 62 | + | |
| 63 | + | ... | ... |
test_tianwang_api.py
0 → 100644
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# -*- coding: utf-8 -*- | |
| 3 | +import json | |
| 4 | +import sys | |
| 5 | +from datetime import datetime | |
| 6 | + | |
| 7 | +# 测试接口 | |
| 8 | +import subprocess | |
| 9 | + | |
| 10 | +# 测试2025年1月的数据 | |
| 11 | +cmd1 = [ | |
| 12 | + 'curl', '-s', '-X', 'POST', | |
| 13 | + 'http://localhost:2011/api/Extend/LqDailyReport/get-tianwang-group-performance-completion', | |
| 14 | + '-H', 'Content-Type: application/json', | |
| 15 | + '-d', '{"startTime": "2025-01-01", "endTime": "2025-01-31"}' | |
| 16 | +] | |
| 17 | + | |
| 18 | +result1 = subprocess.run(cmd1, capture_output=True, text=True) | |
| 19 | +data1 = json.loads(result1.stdout) | |
| 20 | + | |
| 21 | +print("=" * 60) | |
| 22 | +print("2025年1月数据") | |
| 23 | +print("=" * 60) | |
| 24 | +print(f"接口状态: {data1.get('code')}") | |
| 25 | +print(f"返回消息: {data1.get('msg')}") | |
| 26 | + | |
| 27 | +depts1 = [d for d in data1.get('data', []) if '教育' in d.get('DepartmentName', '')] | |
| 28 | +print("\n=== 教育部数据 ===") | |
| 29 | +for d in depts1: | |
| 30 | + print(f"{d.get('DepartmentName')}:") | |
| 31 | + print(f" BillingPerformance: {d.get('BillingPerformance')}") | |
| 32 | + print(f" RefundPerformance: {d.get('RefundPerformance')}") | |
| 33 | + print(f" DeductAmount: {d.get('DeductAmount')}") | |
| 34 | + print(f" CompletedPerformance: {d.get('CompletedPerformance')}") | |
| 35 | + print(f" StoreCount: {d.get('StoreCount')}") | |
| 36 | + | |
| 37 | +total1 = sum([d.get('BillingPerformance', 0) for d in depts1]) | |
| 38 | +print(f"\n教育一部+教育二部合计 BillingPerformance: {total1}") | |
| 39 | + | |
| 40 | +# 测试当前月份的数据 | |
| 41 | +current_month = datetime.now().strftime("%Y-%m") | |
| 42 | +start_date = f"{current_month}-01" | |
| 43 | +end_date = datetime.now().strftime("%Y-%m-%d") | |
| 44 | + | |
| 45 | +cmd2 = [ | |
| 46 | + 'curl', '-s', '-X', 'POST', | |
| 47 | + 'http://localhost:2011/api/Extend/LqDailyReport/get-tianwang-group-performance-completion', | |
| 48 | + '-H', 'Content-Type: application/json', | |
| 49 | + '-d', f'{{"startTime": "{start_date}", "endTime": "{end_date}"}}' | |
| 50 | +] | |
| 51 | + | |
| 52 | +result2 = subprocess.run(cmd2, capture_output=True, text=True) | |
| 53 | +data2 = json.loads(result2.stdout) | |
| 54 | + | |
| 55 | +print("\n" + "=" * 60) | |
| 56 | +print(f"当前月份 ({current_month}) 数据") | |
| 57 | +print("=" * 60) | |
| 58 | +print(f"接口状态: {data2.get('code')}") | |
| 59 | + | |
| 60 | +depts2 = [d for d in data2.get('data', []) if '教育' in d.get('DepartmentName', '')] | |
| 61 | +print("\n=== 教育部数据 ===") | |
| 62 | +for d in depts2: | |
| 63 | + print(f"{d.get('DepartmentName')}:") | |
| 64 | + print(f" BillingPerformance: {d.get('BillingPerformance')}") | |
| 65 | + print(f" RefundPerformance: {d.get('RefundPerformance')}") | |
| 66 | + print(f" DeductAmount: {d.get('DeductAmount')}") | |
| 67 | + print(f" CompletedPerformance: {d.get('CompletedPerformance')}") | |
| 68 | + print(f" StoreCount: {d.get('StoreCount')}") | |
| 69 | + | |
| 70 | +total2 = sum([d.get('BillingPerformance', 0) for d in depts2]) | |
| 71 | +print(f"\n教育一部+教育二部合计 BillingPerformance: {total2}") | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | ... | ... |
主任工资毛利计算逻辑梳理.md
储扣表ItemCategory字段为空问题梳理.md
0 → 100644
| 1 | +# 储扣表 F_ItemCategory 字段为空问题梳理 | |
| 2 | + | |
| 3 | +## 问题描述 | |
| 4 | +储扣表(lq_kd_deductinfo)中的 `F_ItemCategory` 字段有时候是空的。 | |
| 5 | + | |
| 6 | +## 数据库统计 | |
| 7 | +- 总有效记录数:1865 | |
| 8 | +- 有分类字段的记录数:1863 | |
| 9 | +- 分类字段为空的记录数:2 | |
| 10 | + | |
| 11 | +## 问题分析 | |
| 12 | + | |
| 13 | +### 1. 创建开单时的逻辑(第910行) | |
| 14 | +```csharp | |
| 15 | +ItemCategory = await _db.Queryable<LqXmzlEntity>() | |
| 16 | + .Where(x => x.Id == item.ItemId) | |
| 17 | + .Select(x => x.Qt2) | |
| 18 | + .FirstAsync(), | |
| 19 | +``` | |
| 20 | +- 使用 `item.ItemId` 查询品项分类 | |
| 21 | +- 如果 `item.ItemId` 为空或无效,查询会返回 null | |
| 22 | + | |
| 23 | +### 2. 更新开单时的逻辑(第1257行) | |
| 24 | +```csharp | |
| 25 | +ItemCategory = await _db.Queryable<LqXmzlEntity>() | |
| 26 | + .Where(x => x.Id == item.DeductId) // ❌ 错误:应该使用 item.ItemId | |
| 27 | + .Select(x => x.Qt2) | |
| 28 | + .FirstAsync() | |
| 29 | +``` | |
| 30 | +- **问题**:使用了 `item.DeductId` 而不是 `item.ItemId` | |
| 31 | +- `DeductId` 是品项明细表(lq_kd_pxmx)的ID,不是项目资料表(lq_xmzl)的ID | |
| 32 | +- 这会导致查询不到结果,返回 null | |
| 33 | + | |
| 34 | +### 3. 字段说明 | |
| 35 | +- **DeductId**:扣减品项关联ID,对应 `lq_kd_pxmx.F_Id`(品项明细表的主键) | |
| 36 | +- **ItemId**:品项id,对应 `lq_xmzl.F_Id`(项目资料表的主键) | |
| 37 | +- **ItemCategory**:品项分类,应该从 `lq_xmzl.qt2` 获取 | |
| 38 | + | |
| 39 | +### 4. 导致空值的原因 | |
| 40 | +1. **更新开单时使用了错误的字段**:使用 `item.DeductId` 而不是 `item.ItemId` | |
| 41 | +2. **查询不到结果**:如果 `item.ItemId` 为空或无效,`FirstAsync()` 会返回 null | |
| 42 | +3. **品项不存在或无效**:如果品项在 `lq_xmzl` 表中不存在或无效,查询也会返回 null | |
| 43 | + | |
| 44 | +## 解决方案 | |
| 45 | + | |
| 46 | +### ✅ 方案1:修复更新开单时的逻辑(已修复) | |
| 47 | +将第1257行的 `item.DeductId` 改为 `item.ItemId`,与创建开单时的逻辑保持一致。 | |
| 48 | + | |
| 49 | +**修复代码**: | |
| 50 | +```csharp | |
| 51 | +// 修复前(错误) | |
| 52 | +ItemCategory = await _db.Queryable<LqXmzlEntity>() | |
| 53 | + .Where(x => x.Id == item.DeductId) // ❌ 错误 | |
| 54 | + .Select(x => x.Qt2) | |
| 55 | + .FirstAsync() | |
| 56 | + | |
| 57 | +// 修复后(正确) | |
| 58 | +ItemCategory = await _db.Queryable<LqXmzlEntity>() | |
| 59 | + .Where(x => x.Id == item.ItemId) // ✅ 正确 | |
| 60 | + .Select(x => x.Qt2) | |
| 61 | + .FirstAsync() | |
| 62 | +``` | |
| 63 | + | |
| 64 | +### ✅ 方案2:修复历史数据(已提供SQL脚本) | |
| 65 | +对于已经存在的空值记录,执行 `sql/修复储扣表ItemCategory字段为空的数据.sql` 脚本修复。 | |
| 66 | + | |
| 67 | +### 方案3:增加容错处理(可选) | |
| 68 | +在查询时增加容错处理,如果查询不到结果,尝试从其他途径获取分类。 | |
| 69 | + | |
| 70 | +## 验证 | |
| 71 | +从数据库查询结果看: | |
| 72 | +- F_ItemId = '76',对应的 lq_xmzl 表中 qt2 = '科美'(存在且有效) | |
| 73 | +- F_ItemId = '100024',对应的 lq_xmzl 表中 qt2 = '科美'(存在且有效) | |
| 74 | + | |
| 75 | +说明这些记录的 `ItemId` 是有效的,问题应该是在更新开单时使用了错误的字段。 | |
| 76 | + | ... | ... |
总部股份统计说明.md
0 → 100644
| 1 | +# 总部股份统计说明 (Headquarters Share Statistics) | |
| 2 | + | |
| 3 | +## 1. 概述 | |
| 4 | +本以文档详细说明总部股份统计的逻辑。 | |
| 5 | + | |
| 6 | +--- | |
| 7 | + | |
| 8 | +## 2. 收入 (Income) | |
| 9 | + | |
| 10 | +### 2.1 收入 (General Income) | |
| 11 | +* **说明**: 全部开单业绩的 9%(需减去退款)。 | |
| 12 | +* **计算逻辑**: (所有门店的总开单实付业绩 - 所有门店的总实退金额) * 9%。 | |
| 13 | +* **数据来源**: `lq_kd_kdjlb`, `lq_hytk_hytk`。 | |
| 14 | + | |
| 15 | +### 2.2 收入-科技部 (Tech Dept Income) | |
| 16 | +* **说明**: 科美开单的 30% 里面的 9%。 | |
| 17 | +* **计算逻辑**: (所有门店的科美开单业绩 * 30%) * 9%。 | |
| 18 | + * 即: `(总科美业绩 - 总科美退款) * 0.3 * 0.09`。 | |
| 19 | +* **数据来源**: `lq_kd_pxmx` (筛选科美) 关联开单和退款。 | |
| 20 | + | |
| 21 | +--- | |
| 22 | + | |
| 23 | +## 3. 成本 (Cost) | |
| 24 | + | |
| 25 | +### 3.1 成本-报销 (Headquarters Expenses) | |
| 26 | +* **说明**: 报销里面的总部的费用。 | |
| 27 | +* **计算逻辑**: 筛选报销申请中,归属于“总部”的报销。 | |
| 28 | +* **数据来源**: | |
| 29 | + * 表: `lq_reimbursement_application`。 | |
| 30 | + * **判定方式**: 根据 `F_ApplicationStoreId` 是否为总部机构,或 报销分类是否标记为总部费用。 | |
| 31 | + | |
| 32 | +### 3.2 成本-人工 (Labor) | |
| 33 | +* **说明**: 不处理(保留字段)。 | |
| 34 | + | |
| 35 | +### 3.3 成本-教育部房租 (Education Dept Rent) | |
| 36 | +* **说明**: 合同里面获取。 | |
| 37 | +* **数据来源**: | |
| 38 | + * 表: `lq_contract_rent_detail`。 | |
| 39 | + * **判定**: 关联 `lq_contract`,筛选 合同类型/归属 为“教育部” 的合同。 | |
| 40 | + | |
| 41 | +### 3.4 成本-仓库房租 (Warehouse Rent) | |
| 42 | +* **说明**: 合同里面获取。 | |
| 43 | +* **数据来源**: | |
| 44 | + * 表: `lq_contract_rent_detail`。 | |
| 45 | + * **判定**: 关联 `lq_contract`,筛选 合同类型/归属 为“仓库” 的合同。 | |
| 46 | + | |
| 47 | +### 3.5 成本-总部房租 (HQ Rent) | |
| 48 | +* **说明**: 合同里面获取。 | |
| 49 | +* **数据来源**: | |
| 50 | + * 表: `lq_contract_rent_detail`。 | |
| 51 | + * **判定**: 关联 `lq_contract`,筛选 合同类型/归属 为“总部” 的合同。 | |
| 52 | + | |
| 53 | +--- | |
| 54 | + | |
| 55 | +## 4. 利润计算 (Profit Calculation) | |
| 56 | + | |
| 57 | +### 4.1 总部运营利润 | |
| 58 | +``` | |
| 59 | +总部运营利润 = 收入(门店9%) | |
| 60 | + + 收入(科技部9%) | |
| 61 | + - 成本(报销) | |
| 62 | + - 成本(人工) | |
| 63 | + - 成本(总部房租 + 教育部房租 + 仓库房租) | |
| 64 | +``` | ... | ... |
测试储扣接口.sh
0 → 100755
| 1 | +#!/bin/bash | |
| 2 | + | |
| 3 | +# 测试储扣相关接口 | |
| 4 | + | |
| 5 | +# 1. 获取token | |
| 6 | +echo "=== 1. 获取Token ===" | |
| 7 | +TOKEN_RESPONSE=$(curl -s -X POST "http://localhost:2011/api/oauth/Login" \ | |
| 8 | + -H "Content-Type: application/x-www-form-urlencoded" \ | |
| 9 | + -d "account=admin&password=e10adc3949ba59abbe56e057f20f883e") | |
| 10 | + | |
| 11 | +echo "$TOKEN_RESPONSE" | python3 -m json.tool | |
| 12 | + | |
| 13 | +TOKEN=$(echo "$TOKEN_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data['data']['token'])") | |
| 14 | + | |
| 15 | +if [ -z "$TOKEN" ]; then | |
| 16 | + echo "❌ Token获取失败" | |
| 17 | + exit 1 | |
| 18 | +fi | |
| 19 | + | |
| 20 | +echo "✅ Token获取成功: ${TOKEN:0:50}..." | |
| 21 | +echo "" | |
| 22 | + | |
| 23 | +# 2. 测试储扣金额统计接口(按品项分类) | |
| 24 | +echo "=== 2. 测试储扣金额统计接口(get-deduct-amount-statistics)===" | |
| 25 | +echo "请求参数: {\"startTime\": \"2025-11-01\", \"endTime\": \"2025-11-30\"}" | |
| 26 | +echo "" | |
| 27 | + | |
| 28 | +DEDUCT_RESPONSE=$(curl -s -X POST "http://localhost:2011/api/Extend/LqDailyReport/get-deduct-amount-statistics" \ | |
| 29 | + -H "Authorization: $TOKEN" \ | |
| 30 | + -H "Content-Type: application/json" \ | |
| 31 | + -d '{"startTime": "2025-11-01", "endTime": "2025-11-30"}') | |
| 32 | + | |
| 33 | +echo "$DEDUCT_RESPONSE" | python3 -m json.tool | |
| 34 | + | |
| 35 | +# 检查返回结果 | |
| 36 | +if echo "$DEDUCT_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); exit(0 if data.get('code') == 200 else 1)" 2>/dev/null; then | |
| 37 | + echo "✅ 储扣金额统计接口调用成功" | |
| 38 | + | |
| 39 | + # 提取数据 | |
| 40 | + YIMEI=$(echo "$DEDUCT_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('data', {}).get('yiMeiAmount', 0))" 2>/dev/null) | |
| 41 | + SHENGMEI=$(echo "$DEDUCT_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('data', {}).get('shengMeiAmount', 0))" 2>/dev/null) | |
| 42 | + KEMEI=$(echo "$DEDUCT_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('data', {}).get('keMeiAmount', 0))" 2>/dev/null) | |
| 43 | + TOTAL=$(echo "$DEDUCT_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('data', {}).get('totalAmount', 0))" 2>/dev/null) | |
| 44 | + | |
| 45 | + echo "医美储扣金额: $YIMEI" | |
| 46 | + echo "生美储扣金额: $SHENGMEI" | |
| 47 | + echo "科美储扣金额: $KEMEI" | |
| 48 | + echo "总储扣金额: $TOTAL" | |
| 49 | +else | |
| 50 | + echo "❌ 储扣金额统计接口调用失败" | |
| 51 | +fi | |
| 52 | + | |
| 53 | +echo "" | |
| 54 | +echo "" | |
| 55 | + | |
| 56 | +# 3. 测试部门业绩完成情况接口(包含储扣统计) | |
| 57 | +echo "=== 3. 测试部门业绩完成情况接口(get-tianwang-group-performance-completion)===" | |
| 58 | +echo "请求参数: {\"startTime\": \"2025-11-01\", \"endTime\": \"2025-11-30\"}" | |
| 59 | +echo "" | |
| 60 | + | |
| 61 | +DEPT_RESPONSE=$(curl -s -X POST "http://localhost:2011/api/Extend/LqDailyReport/get-tianwang-group-performance-completion" \ | |
| 62 | + -H "Authorization: $TOKEN" \ | |
| 63 | + -H "Content-Type: application/json" \ | |
| 64 | + -d '{"startTime": "2025-11-01", "endTime": "2025-11-30"}') | |
| 65 | + | |
| 66 | +echo "$DEPT_RESPONSE" | python3 -m json.tool | |
| 67 | + | |
| 68 | +# 检查返回结果 | |
| 69 | +if echo "$DEPT_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); exit(0 if data.get('code') == 200 else 1)" 2>/dev/null; then | |
| 70 | + echo "✅ 部门业绩完成情况接口调用成功" | |
| 71 | + | |
| 72 | + # 提取第一个部门的数据 | |
| 73 | + FIRST_DEPT=$(echo "$DEPT_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); depts=data.get('data', []); print(json.dumps(depts[0] if depts else {}, indent=2))" 2>/dev/null) | |
| 74 | + | |
| 75 | + if [ ! -z "$FIRST_DEPT" ] && [ "$FIRST_DEPT" != "{}" ]; then | |
| 76 | + echo "" | |
| 77 | + echo "第一个部门的数据:" | |
| 78 | + echo "$FIRST_DEPT" | |
| 79 | + fi | |
| 80 | +else | |
| 81 | + echo "❌ 部门业绩完成情况接口调用失败" | |
| 82 | +fi | |
| 83 | + | |
| 84 | +echo "" | |
| 85 | +echo "=== 测试完成 ===" | |
| 86 | + | ... | ... |
科技部股份统计说明.md
0 → 100644
| 1 | +# 科技部股份统计说明 (Tech Department Share Statistics) | |
| 2 | + | |
| 3 | +## 1. 概述 | |
| 4 | +本以文档详细说明科技部股份统计的逻辑。科技部需区分 **科技一部** 和 **科技二部**,根据管理的门店归属进行区分。 | |
| 5 | + | |
| 6 | +--- | |
| 7 | + | |
| 8 | +## 2. 收入 (Income) | |
| 9 | + | |
| 10 | +### 2.1 收入 | |
| 11 | +* **说明**: 各个门店科美开单的 30%(需减去退款)。 | |
| 12 | +* **计算逻辑**: | |
| 13 | + 1. 找到该科技部(一部或二部)管辖的所有门店。 | |
| 14 | + 2. 统计这些门店的 **科美项目** 开单实付业绩。 | |
| 15 | + 3. 减去 对应的科美项目实退金额。 | |
| 16 | + 4. 结果 * 30%。 | |
| 17 | +* **数据来源**: | |
| 18 | + * 门店归属: `lq_mdxx` (门店信息表) 中的 `kjb` (科技部) 字段或其他归属字段。 | |
| 19 | + * 开单: `lq_kd_pxmx` (`F_BeautyType` = 科美) 关联 `lq_kd_kdjlb`. | |
| 20 | + * 退款: `lq_hytk_jksyj` (包含退款品项信息) 或 `lq_hytk_hytk`。 | |
| 21 | + | |
| 22 | +--- | |
| 23 | + | |
| 24 | +## 3. 成本 (Cost) | |
| 25 | + | |
| 26 | +### 3.1 成本-报销 (Reimbursement) | |
| 27 | +* **说明**: 报销费用里面的“科技部报销”。 | |
| 28 | +* **计算逻辑**: | |
| 29 | + * 筛选一级分类为“科技部报销”的申请。 | |
| 30 | + * 区分一部/二部:根据报销单的 `F_ApplicationStoreId` (申请门店) 的归属。 | |
| 31 | +* **数据来源**: | |
| 32 | + * 表: `lq_reimbursement_application`, `lq_reimbursement_category`。 | |
| 33 | + | |
| 34 | +### 3.2 成本-人工-科技部老师 (Teacher Base Salary) | |
| 35 | +* **说明**: 科技部老师底薪。 | |
| 36 | +* **数据来源**: | |
| 37 | + * 表: `lq_tech_teacher_salary_statistics` (科技部老师工资统计表 - 需确认表名)。 | |
| 38 | + * 字段: `F_ActualBaseSalary`。 | |
| 39 | + | |
| 40 | +### 3.3 成本-人工-科技部手工费 (Teacher Manual Fee) | |
| 41 | +* **说明**: 来源于消耗里面科技部老师业绩的手工费。 | |
| 42 | +* **数据来源**: | |
| 43 | + * 表: `lq_xh_jksyj` (消耗表)。 | |
| 44 | + * 条件: `Jks` (健康师) 为科技部老师 且 `F_BeautyType` = 科美。 | |
| 45 | + * 字段: `F_LaborCost`。 | |
| 46 | + | |
| 47 | +### 3.4 成本-人工-科技部开单提成 (Teacher Billing Commission) | |
| 48 | +* **说明**: 科技部老师的卡单提成。 | |
| 49 | +* **数据来源**: | |
| 50 | + * 表: `lq_tech_teacher_salary_statistics`。 | |
| 51 | + * 字段: `F_BillingCommission` (开单提成 - 需确认具体字段名)。 | |
| 52 | + | |
| 53 | +### 3.5 成本-人工-科技部消耗提成 (Teacher Consumption Commission) | |
| 54 | +* **说明**: 科技部老师的消耗提成。 | |
| 55 | +* **数据来源**: | |
| 56 | + * 表: `lq_tech_teacher_salary_statistics`。 | |
| 57 | + * 字段: `F_ConsumptionCommission` (消耗提成 - 需确认具体字段名)。 | |
| 58 | + | |
| 59 | +### 3.6 成本-人工-科技部总经理 (General Manager Base Salary) | |
| 60 | +* **说明**: 科技部总经理底薪。 | |
| 61 | +* **数据来源**: | |
| 62 | + * 表: `lq_tech_general_manager_salary_statistics` (科技部总经理工资表)。 | |
| 63 | + * 字段: `F_ActualBaseSalary`。 | |
| 64 | + | |
| 65 | +### 3.7 成本-人工-科技部提成 (General Manager Commission) | |
| 66 | +* **说明**: 科技部总经理提成。 | |
| 67 | +* **数据来源**: | |
| 68 | + * 表: `lq_tech_general_manager_salary_statistics`。 | |
| 69 | + * 字段: `F_TotalCommissionAmount`。 | |
| 70 | + | |
| 71 | +### 3.8 保留/不处理字段 | |
| 72 | +* 成本-人工-科技部专家提成 | |
| 73 | +* 成本-人工-科技部加班 | |
| 74 | +* 奖励-科技部 | |
| 75 | +* 成本-其他1, 其他2 | |
| 76 | + | |
| 77 | +--- | |
| 78 | + | |
| 79 | +## 4. 利润计算 (Profit Calculation) | |
| 80 | + | |
| 81 | +### 4.1 科技部利润 | |
| 82 | +``` | |
| 83 | +科技部利润 = 收入 | |
| 84 | + - 成本报销 | |
| 85 | + - 成本人工(底薪 + 手工 + 开单提成 + 消耗提成 + 专家提成 + 加班 + 总经理底薪 + 总经理提成) | |
| 86 | + - 其他 | |
| 87 | +``` | ... | ... |
股份统计表结构.sql
0 → 100644
| 1 | +/* | |
| 2 | + 股份统计相关表结构设计 | |
| 3 | + Created Date: 2025-12-16 | |
| 4 | + Description: 用于存储门店、科技部、总部的股份统计数据 | |
| 5 | +*/ | |
| 6 | + | |
| 7 | +-- ---------------------------- | |
| 8 | +-- 1. 门店股份统计表 (lq_share_statistics_store) | |
| 9 | +-- ---------------------------- | |
| 10 | +DROP TABLE IF EXISTS `lq_share_statistics_store`; | |
| 11 | +CREATE TABLE `lq_share_statistics_store` ( | |
| 12 | + `F_Id` varchar(50) NOT NULL COMMENT '主键ID', | |
| 13 | + `F_StoreId` varchar(50) NOT NULL COMMENT '门店ID', | |
| 14 | + `F_StoreName` varchar(100) DEFAULT NULL COMMENT '门店名称', | |
| 15 | + `F_StatisticsMonth` varchar(50) NOT NULL COMMENT '统计月份(YYYYMM)', | |
| 16 | + | |
| 17 | + -- 收入部分 | |
| 18 | + `F_MainIncome` decimal(18,2) DEFAULT '0.00' COMMENT '主营收入(消耗总业绩, 不扣减退款)', | |
| 19 | + `F_ConsumeLifeBeauty` decimal(18,2) DEFAULT '0.00' COMMENT '生美消耗业绩', | |
| 20 | + `F_ConsumeTechBeauty` decimal(18,2) DEFAULT '0.00' COMMENT '科美消耗业绩', | |
| 21 | + `F_ConsumeMedicalBeauty` decimal(18,2) DEFAULT '0.00' COMMENT '医美消耗业绩', | |
| 22 | + `F_ConsumeCooperation` decimal(18,2) DEFAULT '0.00' COMMENT '合作消费业绩', | |
| 23 | + `F_ConsumeProduct` decimal(18,2) DEFAULT '0.00' COMMENT '产品消耗业绩', | |
| 24 | + `F_ConsumeOther` decimal(18,2) DEFAULT '0.00' COMMENT '其他消耗业绩', | |
| 25 | + `F_OtherIncome` decimal(18,2) DEFAULT '0.00' COMMENT '其他收入(退款差额>0)', | |
| 26 | + `F_AdvanceReceipt` decimal(18,2) DEFAULT '0.00' COMMENT '预收款(开单实付)', | |
| 27 | + `F_Refund` decimal(18,2) DEFAULT '0.00' COMMENT '实际退款(实退业绩)', | |
| 28 | + `F_Receivables` decimal(18,2) DEFAULT '0.00' COMMENT '应收(合作医院)', | |
| 29 | + `F_BankDeposit` decimal(18,2) DEFAULT '0.00' COMMENT '银行存款(开单实付-应收)', | |
| 30 | + | |
| 31 | + -- 成本部分 | |
| 32 | + `F_CostProduct` decimal(18,2) DEFAULT '0.00' COMMENT '主营成本-产品(仓库领取)', | |
| 33 | + `F_CostFutian` decimal(18,2) DEFAULT '0.00' COMMENT '主营成本-福田(福田仓库领取)', | |
| 34 | + `F_CostTowel` decimal(18,2) DEFAULT '0.00' COMMENT '主营成本-毛巾(清洗送出)', | |
| 35 | + `F_CostTechDept` decimal(18,2) DEFAULT '0.00' COMMENT '主营成本-科技部(科美业绩30%)', | |
| 36 | + `F_CostManagementFee` decimal(18,2) DEFAULT '0.00' COMMENT '主营成本-管理费(总业绩9%)', | |
| 37 | + `F_CostCooperation` decimal(18,2) DEFAULT '0.00' COMMENT '主营成本-合作(保留)', | |
| 38 | + `F_CostMajorProject` decimal(18,2) DEFAULT '0.00' COMMENT '主营成本-大项目(保留)', | |
| 39 | + `F_CostOther` decimal(18,2) DEFAULT '0.00' COMMENT '其他成本(退款差额<0)', | |
| 40 | + | |
| 41 | + -- 人工工资部分 | |
| 42 | + `F_SalaryBaseHealthCoach` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-健康师底薪', | |
| 43 | + `F_SalaryBaseAssistant` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-店助底薪', | |
| 44 | + `F_SalaryBaseDirector` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-店助主任底薪', | |
| 45 | + `F_SalaryBaseStoreManager` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-店长底薪', | |
| 46 | + `F_SalaryBaseGeneralManager` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-总经理/经理底薪', | |
| 47 | + | |
| 48 | + `F_SalaryCommissionHealthCoach` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-健康师提成', | |
| 49 | + `F_SalaryCommissionAssistant` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-店助提成', | |
| 50 | + `F_SalaryCommissionDirector` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-店助主任提成', | |
| 51 | + `F_SalaryCommissionStoreManager` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-店长提成', | |
| 52 | + `F_SalaryCommissionGeneralManager` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-总经理/经理提成', | |
| 53 | + `F_SalaryManual` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-手工', | |
| 54 | + `F_SalaryAttendance` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-出勤(保留)', | |
| 55 | + `F_SalaryPhoneCustody` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-岗位-手机保管费', | |
| 56 | + `F_SalaryHeadcountReward` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-岗位-人头', | |
| 57 | + `F_SalaryTZone` decimal(18,2) DEFAULT '0.00' COMMENT '人工工资-门店T区', | |
| 58 | + | |
| 59 | + -- 费用与其他 | |
| 60 | + `F_SocialSecurity` decimal(18,2) DEFAULT '0.00' COMMENT '社保(保留)', | |
| 61 | + `F_StoreRent` decimal(18,2) DEFAULT '0.00' COMMENT '门店房租', | |
| 62 | + `F_DormRent` decimal(18,2) DEFAULT '0.00' COMMENT '宿舍房租(保留)', | |
| 63 | + `F_CurrentPeriodExpense` decimal(18,2) DEFAULT '0.00' COMMENT '当期费用(报销)', | |
| 64 | + `F_CurrentPeriodExpenseQin` decimal(18,2) DEFAULT '0.00' COMMENT '当前费用-秦董(保留)', | |
| 65 | + `F_FinancialFee` decimal(18,2) DEFAULT '0.00' COMMENT '财务费用(保留)', | |
| 66 | + | |
| 67 | + -- 奖励 | |
| 68 | + `F_RewardMedicalBeauty` decimal(18,2) DEFAULT '0.00' COMMENT '奖励-医美(保留)', | |
| 69 | + `F_RewardOther` decimal(18,2) DEFAULT '0.00' COMMENT '奖励-其他(保留)', | |
| 70 | + `F_RewardLargeOrder` decimal(18,2) DEFAULT '0.00' COMMENT '奖励-大单奖(保留)', | |
| 71 | + `F_RewardFirstOrder` decimal(18,2) DEFAULT '0.00' COMMENT '奖励-首单奖(保留)', | |
| 72 | + `F_RewardGeneral` decimal(18,2) DEFAULT '0.00' COMMENT '奖励(保留)', | |
| 73 | + | |
| 74 | + -- 其他保留字段 | |
| 75 | + `F_Other1` decimal(18,2) DEFAULT '0.00' COMMENT '其他1(保留)', | |
| 76 | + `F_Other2` decimal(18,2) DEFAULT '0.00' COMMENT '其他2(保留)', | |
| 77 | + | |
| 78 | + -- 利润结果 | |
| 79 | + `F_Profit` decimal(18,2) DEFAULT '0.00' COMMENT '利润(预收-实退-主营成本-人工工资[分项汇总]-房租-费用-奖励-其他)', | |
| 80 | + `F_FinancialProfit` decimal(18,2) DEFAULT '0.00' COMMENT '财务利润(主营-其他收入-主营成本-人工工资[分项汇总]-房租-费用-奖励-其他)', | |
| 81 | + | |
| 82 | + -- 基础字段 | |
| 83 | + `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', | |
| 84 | + `F_UpdateTime` datetime DEFAULT NULL COMMENT '更新时间', | |
| 85 | + `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建人', | |
| 86 | + `F_UpdateUser` varchar(50) DEFAULT NULL COMMENT '更新人', | |
| 87 | + `F_IsEffective` int(1) DEFAULT '1' COMMENT '是否有效(1:有效 0:无效)', | |
| 88 | + | |
| 89 | + PRIMARY KEY (`F_Id`), | |
| 90 | + KEY `Idx_StoreId` (`F_StoreId`), | |
| 91 | + KEY `Idx_Month` (`F_StatisticsMonth`) | |
| 92 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='门店股份统计表'; | |
| 93 | + | |
| 94 | + | |
| 95 | +-- ---------------------------- | |
| 96 | +-- 2. 科技部股份统计表 (lq_share_statistics_tech_dept) | |
| 97 | +-- ---------------------------- | |
| 98 | +DROP TABLE IF EXISTS `lq_share_statistics_tech_dept`; | |
| 99 | +CREATE TABLE `lq_share_statistics_tech_dept` ( | |
| 100 | + `F_Id` varchar(50) NOT NULL COMMENT '主键ID', | |
| 101 | + `F_DepartmentName` varchar(100) DEFAULT NULL COMMENT '部门名称(科技一部/二部)', | |
| 102 | + `F_StatisticsMonth` varchar(50) NOT NULL COMMENT '统计月份(YYYYMM)', | |
| 103 | + | |
| 104 | + -- 收入 | |
| 105 | + `F_Income` decimal(18,2) DEFAULT '0.00' COMMENT '收入(门店科美开单30%)', | |
| 106 | + | |
| 107 | + -- 成本 | |
| 108 | + `F_CostReimbursement` decimal(18,2) DEFAULT '0.00' COMMENT '成本-报销', | |
| 109 | + `F_CostTeacherBase` decimal(18,2) DEFAULT '0.00' COMMENT '成本-人工-科技部老师底薪', | |
| 110 | + `F_CostTeacherManual` decimal(18,2) DEFAULT '0.00' COMMENT '成本-人工-科技部手工费', | |
| 111 | + `F_CostTeacherBillingComm` decimal(18,2) DEFAULT '0.00' COMMENT '成本-人工-科技部开单提成', | |
| 112 | + `F_CostTeacherConsumeComm` decimal(18,2) DEFAULT '0.00' COMMENT '成本-人工-科技部消耗提成', | |
| 113 | + `F_CostTeacherExpertComm` decimal(18,2) DEFAULT '0.00' COMMENT '成本-人工-科技部专家提成(保留)', | |
| 114 | + `F_CostTeacherOvertime` decimal(18,2) DEFAULT '0.00' COMMENT '成本-人工-科技部加班(保留)', | |
| 115 | + `F_CostGMBase` decimal(18,2) DEFAULT '0.00' COMMENT '成本-人工-科技部总经理底薪', | |
| 116 | + `F_CostGMComm` decimal(18,2) DEFAULT '0.00' COMMENT '成本-人工-科技部总经理提成', | |
| 117 | + `F_RewardTechDept` decimal(18,2) DEFAULT '0.00' COMMENT '奖励-科技部(保留)', | |
| 118 | + | |
| 119 | + -- 其他 | |
| 120 | + `F_CostOther1` decimal(18,2) DEFAULT '0.00' COMMENT '成本-其他1(保留)', | |
| 121 | + `F_CostOther2` decimal(18,2) DEFAULT '0.00' COMMENT '成本-其他2(保留)', | |
| 122 | + | |
| 123 | + -- 利润结果 | |
| 124 | + `F_Profit` decimal(18,2) DEFAULT '0.00' COMMENT '科技部利润', | |
| 125 | + | |
| 126 | + -- 基础字段 | |
| 127 | + `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', | |
| 128 | + `F_UpdateTime` datetime DEFAULT NULL COMMENT '更新时间', | |
| 129 | + `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建人', | |
| 130 | + `F_UpdateUser` varchar(50) DEFAULT NULL COMMENT '更新人', | |
| 131 | + `F_IsEffective` int(1) DEFAULT '1' COMMENT '是否有效(1:有效 0:无效)', | |
| 132 | + | |
| 133 | + PRIMARY KEY (`F_Id`), | |
| 134 | + KEY `Idx_Month` (`F_StatisticsMonth`) | |
| 135 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='科技部股份统计表'; | |
| 136 | + | |
| 137 | + | |
| 138 | +-- ---------------------------- | |
| 139 | +-- 3. 总部股份统计表 (lq_share_statistics_hq) | |
| 140 | +-- ---------------------------- | |
| 141 | +DROP TABLE IF EXISTS `lq_share_statistics_hq`; | |
| 142 | +CREATE TABLE `lq_share_statistics_hq` ( | |
| 143 | + `F_Id` varchar(50) NOT NULL COMMENT '主键ID', | |
| 144 | + `F_StatisticsMonth` varchar(50) NOT NULL COMMENT '统计月份(YYYYMM)', | |
| 145 | + | |
| 146 | + -- 收入 | |
| 147 | + `F_IncomeGeneral` decimal(18,2) DEFAULT '0.00' COMMENT '收入-全部(开单业绩9%)', | |
| 148 | + `F_IncomeTechDept` decimal(18,2) DEFAULT '0.00' COMMENT '收入-科技部(科美30%的9%)', | |
| 149 | + | |
| 150 | + -- 成本 | |
| 151 | + `F_CostReimbursement` decimal(18,2) DEFAULT '0.00' COMMENT '成本-报销(总部费用)', | |
| 152 | + `F_CostLabor` decimal(18,2) DEFAULT '0.00' COMMENT '成本-人工(保留)', | |
| 153 | + `F_CostEducationRent` decimal(18,2) DEFAULT '0.00' COMMENT '成本-教育部房租', | |
| 154 | + `F_CostWarehouseRent` decimal(18,2) DEFAULT '0.00' COMMENT '成本-仓库房租', | |
| 155 | + `F_CostHQRent` decimal(18,2) DEFAULT '0.00' COMMENT '成本-总部房租', | |
| 156 | + | |
| 157 | + -- 利润结果 | |
| 158 | + `F_OperationalProfit` decimal(18,2) DEFAULT '0.00' COMMENT '总部运营利润', | |
| 159 | + | |
| 160 | + -- 基础字段 | |
| 161 | + `F_CreateTime` datetime DEFAULT NULL COMMENT '创建时间', | |
| 162 | + `F_UpdateTime` datetime DEFAULT NULL COMMENT '更新时间', | |
| 163 | + `F_CreateUser` varchar(50) DEFAULT NULL COMMENT '创建人', | |
| 164 | + `F_UpdateUser` varchar(50) DEFAULT NULL COMMENT '更新人', | |
| 165 | + `F_IsEffective` int(1) DEFAULT '1' COMMENT '是否有效(1:有效 0:无效)', | |
| 166 | + | |
| 167 | + PRIMARY KEY (`F_Id`), | |
| 168 | + KEY `Idx_Month` (`F_StatisticsMonth`) | |
| 169 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='总部股份统计表'; | ... | ... |
门店股份统计说明.md
0 → 100644
| 1 | +# 门店股份统计说明 (Store Share Statistics) | |
| 2 | + | |
| 3 | +## 1. 概述 | |
| 4 | +本以文档详细说明门店股份统计的各个组成部分,包括主营收入、其他收入、各项成本、人工工资及费用的计算逻辑和数据来源。 | |
| 5 | + | |
| 6 | +--- | |
| 7 | + | |
| 8 | +## 2. 收入 (Income) | |
| 9 | + | |
| 10 | +### 2.1 主营收入 (Main Income) | |
| 11 | +* **说明**: 按照消耗业绩统计(需区分生美、科美、医美、产品等)。 | |
| 12 | +* **计算逻辑**: 统计当月 `lq_xh_jksyj` (耗卡健康师业绩表) 中的 `jksyj` (健康师业绩)。 | |
| 13 | +* **数据来源**: | |
| 14 | + * 表: `lq_xh_jksyj` | |
| 15 | + * 字段: `jksyj` (业绩金额), `F_ItemCategory` (品项分类 - 生美/科美/医美/产品) | |
| 16 | + * **注意**: 需关联 `lq_kd_pxmx` (开单品项明细) 或通过 `F_ItemId` 获取更详细属性。 | |
| 17 | + * **退款扣除**: 主营收入计算时,应为 **总消耗业绩 - 对应品项的消耗退款**。即 `(开单消耗业绩 - 退款消耗业绩)`。需确保“主营收入”是净收入。 | |
| 18 | + | |
| 19 | +### 2.2 其他收入 (Other Income) | |
| 20 | +* **说明**: 退款差额 > 0 的部分。 | |
| 21 | +* **计算逻辑**: | |
| 22 | + 1. 计算单笔退款的差额 = `应退金额` - `实退金额`。 | |
| 23 | + 2. 若差额 > 0,计入其他收入。 | |
| 24 | +* **数据来源**: | |
| 25 | + * 表: `lq_hytk_hytk` (会员退款表) | |
| 26 | + * 字段: `tkje` (应退金额/退卡金额), `F_ActualRefundAmount` (实退金额) | |
| 27 | + * 计算: `SUM(CASE WHEN (tkje - F_ActualRefundAmount) > 0 THEN (tkje - F_ActualRefundAmount) ELSE 0 END)` | |
| 28 | + | |
| 29 | +### 2.3 预收款 (Advance Receipt) | |
| 30 | +* **说明**: 来源于开单的实收业绩。 | |
| 31 | +* **数据来源**: | |
| 32 | + * 表: `lq_kd_kdjlb` (开单记录表) | |
| 33 | + * 字段: `sfyj` (实付业绩) | |
| 34 | + | |
| 35 | +### 2.4 退款 (Refund) | |
| 36 | +* **说明**: 按照退款单里面的实退业绩来计算。 | |
| 37 | +* **数据来源**: | |
| 38 | + * 表: `lq_hytk_hytk` | |
| 39 | + * 字段: `F_ActualRefundAmount` (实退金额) | |
| 40 | + | |
| 41 | +### 2.5 退款差额 (Refund Difference) | |
| 42 | +* **说明**: `应退金额` - `实退金额`。 | |
| 43 | + * `> 0`: 归纳到 **其他收入** | |
| 44 | + * `< 0`: 归纳到 **其他成本** | |
| 45 | +* **数据来源**: 同 2.2。 | |
| 46 | + | |
| 47 | +### 2.6 应收 (Receivables) | |
| 48 | +* **说明**: 开单时选择了合作医院的款项。 | |
| 49 | +* **计算逻辑**: 统计支付方式或关联机构为“合作医院”的开单金额。 | |
| 50 | +* **数据来源**: | |
| 51 | + * 表: `lq_kd_kdjlb` | |
| 52 | + * 字段: `Hgjg` (合作机构) 或 支付方式字段。 | |
| 53 | + * **判定**: `Hgjg` 不为空 且 属于医院类型。 | |
| 54 | + | |
| 55 | +### 2.7 银行存款 (Bank Deposit) | |
| 56 | +* **说明**: 开单时,**除了**选择了合作医院以外的所有款项。 | |
| 57 | +* **计算逻辑**: 总实付业绩 - 应收。 | |
| 58 | + | |
| 59 | +--- | |
| 60 | + | |
| 61 | +## 3. 成本 (Cost) | |
| 62 | + | |
| 63 | +### 3.1 主营成本-产品 (product) | |
| 64 | +* **说明**: 仓库领取数据,算上个月成本。 | |
| 65 | +* **数据来源**: | |
| 66 | + * 表: `lq_inventory_usage` (库存使用记录) | |
| 67 | + * 字段: `F_TotalAmount` (合计金额) | |
| 68 | + * **条件**: `F_UsageTime` 在上个月范围内。 | |
| 69 | + | |
| 70 | +### 3.2 主营成本-福田 (futian) | |
| 71 | +* **说明**: 仓库地址是“福田”的领取数据,算上个月成本。 | |
| 72 | +* **数据来源**: | |
| 73 | + * 表: `lq_inventory_usage` | |
| 74 | + * **待确认**: 需确认“福田”仓库的 `StoreId` 或如何标识。暂时需通过 `StoreId` 或 `ProductId` 对应的仓库属性识别。 | |
| 75 | + | |
| 76 | +### 3.3 主营成本-毛巾 (towel) | |
| 77 | +* **说明**: 清洗记录里面的送出。 | |
| 78 | +* **数据来源**: | |
| 79 | + * 表: `lq_laundry_flow` (清洗流水表) | |
| 80 | + * 字段: `F_TotalPrice` | |
| 81 | + * **条件**: `F_FlowType` = 0 (送出)。 | |
| 82 | + | |
| 83 | +### 3.4 主营成本-科技部 (tech_dept) | |
| 84 | +* **说明**: 门店开单中属于科美的(总业绩 - 退款业绩) * 30%。 | |
| 85 | +* **计算逻辑**: | |
| 86 | + 1. 筛选科美项目 (`lq_kd_pxmx.F_BeautyType` 为科美 或 `ItemCategory` 为科美)。 | |
| 87 | + 2. 计算科美项目的实付业绩总和 - 对应退款。 | |
| 88 | + 3. 乘以 30%。 | |
| 89 | + | |
| 90 | +### 3.5 主营成本-管理费 (management_fee) | |
| 91 | +* **说明**: 门店所有开单(总业绩 - 退款业绩) * 9%。 | |
| 92 | +* **计算逻辑**: (`lq_kd_kdjlb.sfyj` 总和 - `lq_hytk_hytk.F_ActualRefundAmount` 总和) * 9%。 | |
| 93 | + | |
| 94 | +### 3.6 主营成本-合作 (cooperation) | |
| 95 | +* **说明**: 不处理(保留字段)。 | |
| 96 | + | |
| 97 | +### 3.7 主营成本-大项目 (major_project) | |
| 98 | +* **说明**: 不处理(保留字段)。 | |
| 99 | + | |
| 100 | +--- | |
| 101 | + | |
| 102 | +## 4. 人工工资 (Labor Cost) | |
| 103 | + | |
| 104 | +### 4.1 人工工资-底薪 (base_salary) | |
| 105 | +* **人工工资-健康师底薪**: 来源于 `lq_salary_statistics.F_ActualBaseSalary`。 | |
| 106 | +* **人工工资-店助底薪**: 来源于 `lq_assistant_salary_statistics.F_ActualBaseSalary` (岗位为“店助”)。 | |
| 107 | +* **人工工资-店助主任底薪**: 来源于 `lq_director_salary_statistics.F_ActualBaseSalary` (岗位为“店助主任”)。 | |
| 108 | +* **人工工资-店长底薪**: 来源于 `lq_store_manager_salary_statistics.F_ActualBaseSalary`。 | |
| 109 | +* **人工工资-总经理/经理底薪**: 来源于事业部总经理/经理工资表 `F_ActualBaseSalary` (按门店平均分摊 - 底薪固定4000,按管理门店数平均)。 | |
| 110 | + | |
| 111 | +### 4.2 人工工资-提成 (commission) | |
| 112 | +* **人工工资-健康师提成**: 来源于 `lq_salary_statistics.F_TotalCommissionAmount`。 | |
| 113 | +* **人工工资-店助提成**: 来源于 `lq_assistant_salary_statistics.F_TotalCommissionAmount`。 | |
| 114 | +* **人工工资-店助主任提成**: 来源于 `lq_director_salary_statistics.F_TotalCommissionAmount`。 | |
| 115 | +* **人工工资-店长提成**: 来源于 `lq_store_manager_salary_statistics.F_TotalCommissionAmount`。 | |
| 116 | +* **人工工资-总经理/经理提成**: 来源于事业部总经理/经理工资表中该门店对应的提成金额。 | |
| 117 | + * **注意**: 总经理提成是按门店单独计算汇总的,工资表中需记录分店提成明细或能反查单店提成。 | |
| 118 | + * 根据规则:`总提成 = SUM(各门店提成金额)`。统计时需提取该门店贡献的部分。 | |
| 119 | + | |
| 120 | +### 4.3 人工工资-手工 (manual_fee) | |
| 121 | +* **说明**: 健康师手工费(消耗表)。 | |
| 122 | +* **数据来源**: | |
| 123 | + * 表: `lq_xh_jksyj` | |
| 124 | + * 字段: `F_LaborCost` (手工费) | |
| 125 | + * **或者**: 直接取工资表中的 `F_HandworkFee` (如果有统计)。推荐使用消耗表累加更精准。 | |
| 126 | + | |
| 127 | +### 4.4 人工工资-岗位-手机保管费 (phone_custody) | |
| 128 | +* **说明**: 店助的手机保管费。 | |
| 129 | +* **数据来源**: | |
| 130 | + * 表: `lq_assistant_salary_statistics` | |
| 131 | + * 字段: `F_PhoneManagementFee` (手机保管费,已确认字段存在)。 | |
| 132 | + | |
| 133 | +### 4.5 人工工资-岗位-人头 (headcount_reward) | |
| 134 | +* **说明**: 店助的人头阶段奖励。 | |
| 135 | +* **数据来源**: | |
| 136 | + * 表: `lq_assistant_salary_statistics` | |
| 137 | + * 字段: 需确认具体的奖励字段,可能是 `F_HeadcountAward` 或包含在奖金中。 | |
| 138 | + | |
| 139 | +### 4.6 人工工资-门店T区 (t_zone) | |
| 140 | +* **说明**: 健康师工资表里面T区的工资金额。 | |
| 141 | +* **数据来源**: | |
| 142 | + * 表: `lq_salary_statistics` | |
| 143 | + * 字段: 筛选 T区员工的 `F_ActualSalary` 或 特定 T区 补贴字段。 | |
| 144 | + | |
| 145 | +--- | |
| 146 | + | |
| 147 | +## 5. 费用与其他 (Expenses & Others) | |
| 148 | + | |
| 149 | +### 5.1 门店房租 (store_rent) | |
| 150 | +* **说明**: 来源于合同对应的每月房租明细。 | |
| 151 | +* **数据来源**: | |
| 152 | + * 表: `lq_contract_rent_detail` (月租明细表) | |
| 153 | + * 字段: `F_DueAmount` (应缴金额) | |
| 154 | + * 条件: `F_PaymentMonth` 匹配当月。 | |
| 155 | + | |
| 156 | +### 5.2 当期费用 (current_period_expense) | |
| 157 | +* **说明**: 报销费用 -> 费用分类 = "当期费用"。 | |
| 158 | +* **数据来源**: | |
| 159 | + * 表: `lq_reimbursement_application` (报销申请) 关联 `lq_reimbursement_category` (报销分类)。 | |
| 160 | + * 条件: 通过分类表筛选出一级分类或二级分类为“当期费用”的记录。 | |
| 161 | + | |
| 162 | +### 5.3 其他成本 (other_cost) | |
| 163 | +* **说明**: 退款差额 < 0 的部分 (即 `(应退 - 实退) < 0`,多退了)。 | |
| 164 | +* **计算逻辑**: 绝对值(`应退` - `实退`)。 | |
| 165 | + | |
| 166 | +### 5.4 保留/不处理字段 | |
| 167 | +* 人工工资-出勤 | |
| 168 | +* 社保 | |
| 169 | +* 宿舍房租 | |
| 170 | +* 当前费用-秦董 | |
| 171 | +* 财务费用 | |
| 172 | +* 各项奖励 (医美/其他/大单/首单) | |
| 173 | +* 其他1, 其他2 | |
| 174 | + | |
| 175 | +--- | |
| 176 | + | |
| 177 | +## 6. 利润计算 (Profit Calculation) | |
| 178 | + | |
| 179 | +### 6.1 利润 (Profit) | |
| 180 | +``` | |
| 181 | +利润 = 预收款 | |
| 182 | + - 实际退款 | |
| 183 | + - 主营成本(产品 + 福田 + 毛巾 + 合作 + 大项目 + 科技部 + 管理费) | |
| 184 | + - 人工工资(底薪 + 提成 + 手工 + 出勤 + 岗位 + T区) | |
| 185 | + - 社保(公司部分) | |
| 186 | + - 门店房租 | |
| 187 | + - 宿舍房租 | |
| 188 | + - 当前费用 | |
| 189 | + - 当前费用(秦董) | |
| 190 | + - 财务费用 | |
| 191 | + - 奖励(各项) | |
| 192 | + - 其他1 - 其他2 | |
| 193 | +``` | |
| 194 | + | |
| 195 | +### 6.2 财务利润 (Financial Profit) | |
| 196 | +``` | |
| 197 | +财务利润 = 主营收入 | |
| 198 | + - 其他收入 | |
| 199 | + - 主营成本(...) | |
| 200 | + - 人工工资(...) | |
| 201 | + - 社保(...) | |
| 202 | + - 门店房租 | |
| 203 | + - ... (同上扣除项) | |
| 204 | +``` | ... | ... |