Commit 26d4de1ee358beccd5f56530c8ede693fb6393ae
1 parent
b50927a5
feat: 添加健康师工资额外计算表和工资表新字段
- 创建健康师工资额外计算表(lq_salary_extra_calculation),包含基础奖励业绩、合作奖励业绩、新客业绩、新客成交率、升单业绩、升单成交率、升单人头数、其他业绩加、其他业绩减等字段 - 在健康师工资表中添加:基础奖励业绩、合作奖励业绩、升单人头数、新客业绩提成金额、升单业绩提成金额、其他业绩加、其他业绩减、实际基础业绩、实际合作业绩 - 创建健康师工资额外计算服务,包含列表查询和Excel导入功能 - 添加为新店健康师生成模拟数据的方法 - 添加相关SQL脚本用于创建表和添加字段
Showing
16 changed files
with
1322 additions
and
48 deletions
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs
| @@ -111,5 +111,45 @@ namespace NCC.Extend.Entitys.Dto.LqSalary | @@ -111,5 +111,45 @@ namespace NCC.Extend.Entitys.Dto.LqSalary | ||
| 111 | /// 更新时间 | 111 | /// 更新时间 |
| 112 | /// </summary> | 112 | /// </summary> |
| 113 | public DateTime UpdateTime { get; set; } | 113 | public DateTime UpdateTime { get; set; } |
| 114 | + | ||
| 115 | + /// <summary> | ||
| 116 | + /// 是否新店 | ||
| 117 | + /// </summary> | ||
| 118 | + public string IsNewStore { get; set; } | ||
| 119 | + | ||
| 120 | + /// <summary> | ||
| 121 | + /// 新店保护阶段 | ||
| 122 | + /// </summary> | ||
| 123 | + public int NewStoreProtectionStage { get; set; } | ||
| 124 | + | ||
| 125 | + /// <summary> | ||
| 126 | + /// 门店类型 | ||
| 127 | + /// </summary> | ||
| 128 | + public int? StoreType { get; set; } | ||
| 129 | + | ||
| 130 | + /// <summary> | ||
| 131 | + /// 门店类别 | ||
| 132 | + /// </summary> | ||
| 133 | + public int? StoreCategory { get; set; } | ||
| 134 | + | ||
| 135 | + /// <summary> | ||
| 136 | + /// 实际基础业绩 | ||
| 137 | + /// </summary> | ||
| 138 | + public decimal ActualBasePerformance { get; set; } | ||
| 139 | + | ||
| 140 | + /// <summary> | ||
| 141 | + /// 实际合作业绩 | ||
| 142 | + /// </summary> | ||
| 143 | + public decimal ActualCooperationPerformance { get; set; } | ||
| 144 | + | ||
| 145 | + /// <summary> | ||
| 146 | + /// 新客业绩提成 | ||
| 147 | + /// </summary> | ||
| 148 | + public decimal NewCustomerCommission { get; set; } | ||
| 149 | + | ||
| 150 | + /// <summary> | ||
| 151 | + /// 升单业绩提成 | ||
| 152 | + /// </summary> | ||
| 153 | + public decimal UpgradeCommission { get; set; } | ||
| 114 | } | 154 | } |
| 115 | } | 155 | } |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationImportInput.cs
0 → 100644
| 1 | +namespace NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation | ||
| 2 | +{ | ||
| 3 | + /// <summary> | ||
| 4 | + /// 健康师工资额外计算导入数据 | ||
| 5 | + /// </summary> | ||
| 6 | + public class SalaryExtraCalculationImportInput | ||
| 7 | + { | ||
| 8 | + /// <summary> | ||
| 9 | + /// ID(可选,用于更新已有记录) | ||
| 10 | + /// </summary> | ||
| 11 | + public string Id { get; set; } | ||
| 12 | + | ||
| 13 | + /// <summary> | ||
| 14 | + /// 健康师姓名 | ||
| 15 | + /// </summary> | ||
| 16 | + public string EmployeeName { get; set; } | ||
| 17 | + | ||
| 18 | + /// <summary> | ||
| 19 | + /// 健康师电话 | ||
| 20 | + /// </summary> | ||
| 21 | + public string EmployeePhone { get; set; } | ||
| 22 | + | ||
| 23 | + /// <summary> | ||
| 24 | + /// 年份 | ||
| 25 | + /// </summary> | ||
| 26 | + public int Year { get; set; } | ||
| 27 | + | ||
| 28 | + /// <summary> | ||
| 29 | + /// 月份 | ||
| 30 | + /// </summary> | ||
| 31 | + public int Month { get; set; } | ||
| 32 | + | ||
| 33 | + /// <summary> | ||
| 34 | + /// 基础奖励业绩 | ||
| 35 | + /// </summary> | ||
| 36 | + public decimal BaseRewardPerformance { get; set; } | ||
| 37 | + | ||
| 38 | + /// <summary> | ||
| 39 | + /// 合作奖励业绩 | ||
| 40 | + /// </summary> | ||
| 41 | + public decimal CooperationRewardPerformance { get; set; } | ||
| 42 | + | ||
| 43 | + /// <summary> | ||
| 44 | + /// 新客业绩 | ||
| 45 | + /// </summary> | ||
| 46 | + public decimal NewCustomerPerformance { get; set; } | ||
| 47 | + | ||
| 48 | + /// <summary> | ||
| 49 | + /// 新客成交率 | ||
| 50 | + /// </summary> | ||
| 51 | + public decimal NewCustomerConversionRate { get; set; } | ||
| 52 | + | ||
| 53 | + /// <summary> | ||
| 54 | + /// 升单业绩 | ||
| 55 | + /// </summary> | ||
| 56 | + public decimal UpgradePerformance { get; set; } | ||
| 57 | + | ||
| 58 | + /// <summary> | ||
| 59 | + /// 升单成交率 | ||
| 60 | + /// </summary> | ||
| 61 | + public decimal UpgradeConversionRate { get; set; } | ||
| 62 | + | ||
| 63 | + /// <summary> | ||
| 64 | + /// 升单人头数 | ||
| 65 | + /// </summary> | ||
| 66 | + public decimal UpgradeCustomerCount { get; set; } | ||
| 67 | + | ||
| 68 | + /// <summary> | ||
| 69 | + /// 其他业绩加 | ||
| 70 | + /// </summary> | ||
| 71 | + public decimal OtherPerformanceAdd { get; set; } | ||
| 72 | + | ||
| 73 | + /// <summary> | ||
| 74 | + /// 其他业绩减 | ||
| 75 | + /// </summary> | ||
| 76 | + public decimal OtherPerformanceSubtract { get; set; } | ||
| 77 | + } | ||
| 78 | +} | ||
| 79 | + |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationInput.cs
0 → 100644
| 1 | +using NCC.Common.Filter; | ||
| 2 | +using System; | ||
| 3 | + | ||
| 4 | +namespace NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation | ||
| 5 | +{ | ||
| 6 | + /// <summary> | ||
| 7 | + /// 健康师工资额外计算查询参数 | ||
| 8 | + /// </summary> | ||
| 9 | + public class SalaryExtraCalculationInput : PageInputBase | ||
| 10 | + { | ||
| 11 | + /// <summary> | ||
| 12 | + /// 年份 | ||
| 13 | + /// </summary> | ||
| 14 | + public int? Year { get; set; } | ||
| 15 | + | ||
| 16 | + /// <summary> | ||
| 17 | + /// 月份 | ||
| 18 | + /// </summary> | ||
| 19 | + public int? Month { get; set; } | ||
| 20 | + | ||
| 21 | + /// <summary> | ||
| 22 | + /// 健康师ID(可选,用于筛选特定健康师) | ||
| 23 | + /// </summary> | ||
| 24 | + public string EmployeeId { get; set; } | ||
| 25 | + | ||
| 26 | + /// <summary> | ||
| 27 | + /// 健康师姓名/电话(可选,用于模糊搜索) | ||
| 28 | + /// </summary> | ||
| 29 | + public string Keyword { get; set; } | ||
| 30 | + } | ||
| 31 | +} | ||
| 32 | + |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalaryExtraCalculation/SalaryExtraCalculationOutput.cs
0 → 100644
| 1 | +using System; | ||
| 2 | + | ||
| 3 | +namespace NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation | ||
| 4 | +{ | ||
| 5 | + /// <summary> | ||
| 6 | + /// 健康师工资额外计算输出 | ||
| 7 | + /// </summary> | ||
| 8 | + public class SalaryExtraCalculationOutput | ||
| 9 | + { | ||
| 10 | + /// <summary> | ||
| 11 | + /// 主键ID | ||
| 12 | + /// </summary> | ||
| 13 | + public string Id { get; set; } | ||
| 14 | + | ||
| 15 | + /// <summary> | ||
| 16 | + /// 健康师ID | ||
| 17 | + /// </summary> | ||
| 18 | + public string EmployeeId { get; set; } | ||
| 19 | + | ||
| 20 | + /// <summary> | ||
| 21 | + /// 健康师姓名 | ||
| 22 | + /// </summary> | ||
| 23 | + public string EmployeeName { get; set; } | ||
| 24 | + | ||
| 25 | + /// <summary> | ||
| 26 | + /// 健康师电话 | ||
| 27 | + /// </summary> | ||
| 28 | + public string EmployeePhone { get; set; } | ||
| 29 | + | ||
| 30 | + /// <summary> | ||
| 31 | + /// 年份 | ||
| 32 | + /// </summary> | ||
| 33 | + public int Year { get; set; } | ||
| 34 | + | ||
| 35 | + /// <summary> | ||
| 36 | + /// 月份 | ||
| 37 | + /// </summary> | ||
| 38 | + public int Month { get; set; } | ||
| 39 | + | ||
| 40 | + /// <summary> | ||
| 41 | + /// 基础奖励业绩 | ||
| 42 | + /// </summary> | ||
| 43 | + public decimal BaseRewardPerformance { get; set; } | ||
| 44 | + | ||
| 45 | + /// <summary> | ||
| 46 | + /// 合作奖励业绩 | ||
| 47 | + /// </summary> | ||
| 48 | + public decimal CooperationRewardPerformance { get; set; } | ||
| 49 | + | ||
| 50 | + /// <summary> | ||
| 51 | + /// 新客业绩 | ||
| 52 | + /// </summary> | ||
| 53 | + public decimal NewCustomerPerformance { get; set; } | ||
| 54 | + | ||
| 55 | + /// <summary> | ||
| 56 | + /// 新客成交率 | ||
| 57 | + /// </summary> | ||
| 58 | + public decimal NewCustomerConversionRate { get; set; } | ||
| 59 | + | ||
| 60 | + /// <summary> | ||
| 61 | + /// 升单业绩 | ||
| 62 | + /// </summary> | ||
| 63 | + public decimal UpgradePerformance { get; set; } | ||
| 64 | + | ||
| 65 | + /// <summary> | ||
| 66 | + /// 升单成交率 | ||
| 67 | + /// </summary> | ||
| 68 | + public decimal UpgradeConversionRate { get; set; } | ||
| 69 | + | ||
| 70 | + /// <summary> | ||
| 71 | + /// 升单人头数 | ||
| 72 | + /// </summary> | ||
| 73 | + public decimal UpgradeCustomerCount { get; set; } | ||
| 74 | + | ||
| 75 | + /// <summary> | ||
| 76 | + /// 其他业绩加 | ||
| 77 | + /// </summary> | ||
| 78 | + public decimal OtherPerformanceAdd { get; set; } | ||
| 79 | + | ||
| 80 | + /// <summary> | ||
| 81 | + /// 其他业绩减 | ||
| 82 | + /// </summary> | ||
| 83 | + public decimal OtherPerformanceSubtract { get; set; } | ||
| 84 | + } | ||
| 85 | +} | ||
| 86 | + |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_extra_calculation/LqSalaryExtraCalculationEntity.cs
0 → 100644
| 1 | +using System; | ||
| 2 | +using NCC.Common.Const; | ||
| 3 | +using SqlSugar; | ||
| 4 | + | ||
| 5 | +namespace NCC.Extend.Entitys.lq_salary_extra_calculation | ||
| 6 | +{ | ||
| 7 | + /// <summary> | ||
| 8 | + /// 健康师工资额外计算表 | ||
| 9 | + /// </summary> | ||
| 10 | + [SugarTable("lq_salary_extra_calculation")] | ||
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | ||
| 12 | + public class LqSalaryExtraCalculationEntity | ||
| 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_EmployeeId")] | ||
| 24 | + public string EmployeeId { get; set; } | ||
| 25 | + | ||
| 26 | + /// <summary> | ||
| 27 | + /// 年份 | ||
| 28 | + /// </summary> | ||
| 29 | + [SugarColumn(ColumnName = "F_Year")] | ||
| 30 | + public int Year { get; set; } | ||
| 31 | + | ||
| 32 | + /// <summary> | ||
| 33 | + /// 月份 | ||
| 34 | + /// </summary> | ||
| 35 | + [SugarColumn(ColumnName = "F_Month")] | ||
| 36 | + public int Month { get; set; } | ||
| 37 | + | ||
| 38 | + /// <summary> | ||
| 39 | + /// 基础奖励业绩 | ||
| 40 | + /// </summary> | ||
| 41 | + [SugarColumn(ColumnName = "F_BaseRewardPerformance")] | ||
| 42 | + public decimal BaseRewardPerformance { get; set; } | ||
| 43 | + | ||
| 44 | + /// <summary> | ||
| 45 | + /// 合作奖励业绩 | ||
| 46 | + /// </summary> | ||
| 47 | + [SugarColumn(ColumnName = "F_CooperationRewardPerformance")] | ||
| 48 | + public decimal CooperationRewardPerformance { get; set; } | ||
| 49 | + | ||
| 50 | + /// <summary> | ||
| 51 | + /// 新客业绩 | ||
| 52 | + /// </summary> | ||
| 53 | + [SugarColumn(ColumnName = "F_NewCustomerPerformance")] | ||
| 54 | + public decimal NewCustomerPerformance { get; set; } | ||
| 55 | + | ||
| 56 | + /// <summary> | ||
| 57 | + /// 新客成交率 | ||
| 58 | + /// </summary> | ||
| 59 | + [SugarColumn(ColumnName = "F_NewCustomerConversionRate")] | ||
| 60 | + public decimal NewCustomerConversionRate { get; set; } | ||
| 61 | + | ||
| 62 | + /// <summary> | ||
| 63 | + /// 升单业绩 | ||
| 64 | + /// </summary> | ||
| 65 | + [SugarColumn(ColumnName = "F_UpgradePerformance")] | ||
| 66 | + public decimal UpgradePerformance { get; set; } | ||
| 67 | + | ||
| 68 | + /// <summary> | ||
| 69 | + /// 升单成交率 | ||
| 70 | + /// </summary> | ||
| 71 | + [SugarColumn(ColumnName = "F_UpgradeConversionRate")] | ||
| 72 | + public decimal UpgradeConversionRate { get; set; } | ||
| 73 | + | ||
| 74 | + /// <summary> | ||
| 75 | + /// 升单人头数 | ||
| 76 | + /// </summary> | ||
| 77 | + [SugarColumn(ColumnName = "F_UpgradeCustomerCount")] | ||
| 78 | + public decimal UpgradeCustomerCount { get; set; } | ||
| 79 | + | ||
| 80 | + /// <summary> | ||
| 81 | + /// 其他业绩加 | ||
| 82 | + /// </summary> | ||
| 83 | + [SugarColumn(ColumnName = "F_OtherPerformanceAdd")] | ||
| 84 | + public decimal OtherPerformanceAdd { get; set; } | ||
| 85 | + | ||
| 86 | + /// <summary> | ||
| 87 | + /// 其他业绩减 | ||
| 88 | + /// </summary> | ||
| 89 | + [SugarColumn(ColumnName = "F_OtherPerformanceSubtract")] | ||
| 90 | + public decimal OtherPerformanceSubtract { get; set; } | ||
| 91 | + } | ||
| 92 | +} | ||
| 93 | + |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_salary_statistics/LqSalaryStatisticsEntity.cs
| @@ -78,6 +78,30 @@ namespace NCC.Extend.Entitys.lq_salary_statistics | @@ -78,6 +78,30 @@ namespace NCC.Extend.Entitys.lq_salary_statistics | ||
| 78 | public decimal CooperationPerformance { get; set; } | 78 | public decimal CooperationPerformance { get; set; } |
| 79 | 79 | ||
| 80 | /// <summary> | 80 | /// <summary> |
| 81 | + /// 基础奖励业绩 | ||
| 82 | + /// </summary> | ||
| 83 | + [SugarColumn(ColumnName = "F_BaseRewardPerformance")] | ||
| 84 | + public decimal BaseRewardPerformance { get; set; } | ||
| 85 | + | ||
| 86 | + /// <summary> | ||
| 87 | + /// 合作奖励业绩 | ||
| 88 | + /// </summary> | ||
| 89 | + [SugarColumn(ColumnName = "F_CooperationRewardPerformance")] | ||
| 90 | + public decimal CooperationRewardPerformance { get; set; } | ||
| 91 | + | ||
| 92 | + /// <summary> | ||
| 93 | + /// 实际基础业绩 | ||
| 94 | + /// </summary> | ||
| 95 | + [SugarColumn(ColumnName = "F_ActualBasePerformance")] | ||
| 96 | + public decimal ActualBasePerformance { get; set; } | ||
| 97 | + | ||
| 98 | + /// <summary> | ||
| 99 | + /// 实际合作业绩 | ||
| 100 | + /// </summary> | ||
| 101 | + [SugarColumn(ColumnName = "F_ActualCooperationPerformance")] | ||
| 102 | + public decimal ActualCooperationPerformance { get; set; } | ||
| 103 | + | ||
| 104 | + /// <summary> | ||
| 81 | /// 奖励业绩 | 105 | /// 奖励业绩 |
| 82 | /// </summary> | 106 | /// </summary> |
| 83 | [SugarColumn(ColumnName = "F_RewardPerformance")] | 107 | [SugarColumn(ColumnName = "F_RewardPerformance")] |
| @@ -120,6 +144,12 @@ namespace NCC.Extend.Entitys.lq_salary_statistics | @@ -120,6 +144,12 @@ namespace NCC.Extend.Entitys.lq_salary_statistics | ||
| 120 | public decimal NewCustomerPoint { get; set; } | 144 | public decimal NewCustomerPoint { get; set; } |
| 121 | 145 | ||
| 122 | /// <summary> | 146 | /// <summary> |
| 147 | + /// 升单人头数 | ||
| 148 | + /// </summary> | ||
| 149 | + [SugarColumn(ColumnName = "F_UpgradeCustomerCount")] | ||
| 150 | + public decimal UpgradeCustomerCount { get; set; } | ||
| 151 | + | ||
| 152 | + /// <summary> | ||
| 123 | /// 升单业绩 | 153 | /// 升单业绩 |
| 124 | /// </summary> | 154 | /// </summary> |
| 125 | [SugarColumn(ColumnName = "F_UpgradePerformance")] | 155 | [SugarColumn(ColumnName = "F_UpgradePerformance")] |
| @@ -132,6 +162,30 @@ namespace NCC.Extend.Entitys.lq_salary_statistics | @@ -132,6 +162,30 @@ namespace NCC.Extend.Entitys.lq_salary_statistics | ||
| 132 | public decimal UpgradePoint { get; set; } | 162 | public decimal UpgradePoint { get; set; } |
| 133 | 163 | ||
| 134 | /// <summary> | 164 | /// <summary> |
| 165 | + /// 新客业绩提成金额 | ||
| 166 | + /// </summary> | ||
| 167 | + [SugarColumn(ColumnName = "F_NewCustomerPerformanceCommission")] | ||
| 168 | + public decimal NewCustomerPerformanceCommission { get; set; } | ||
| 169 | + | ||
| 170 | + /// <summary> | ||
| 171 | + /// 升单业绩提成金额 | ||
| 172 | + /// </summary> | ||
| 173 | + [SugarColumn(ColumnName = "F_UpgradePerformanceCommission")] | ||
| 174 | + public decimal UpgradePerformanceCommission { get; set; } | ||
| 175 | + | ||
| 176 | + /// <summary> | ||
| 177 | + /// 其他业绩加 | ||
| 178 | + /// </summary> | ||
| 179 | + [SugarColumn(ColumnName = "F_OtherPerformanceAdd")] | ||
| 180 | + public decimal OtherPerformanceAdd { get; set; } | ||
| 181 | + | ||
| 182 | + /// <summary> | ||
| 183 | + /// 其他业绩减 | ||
| 184 | + /// </summary> | ||
| 185 | + [SugarColumn(ColumnName = "F_OtherPerformanceSubtract")] | ||
| 186 | + public decimal OtherPerformanceSubtract { get; set; } | ||
| 187 | + | ||
| 188 | + /// <summary> | ||
| 135 | /// 消耗 | 189 | /// 消耗 |
| 136 | /// </summary> | 190 | /// </summary> |
| 137 | [SugarColumn(ColumnName = "F_Consumption")] | 191 | [SugarColumn(ColumnName = "F_Consumption")] |
| @@ -442,5 +496,29 @@ namespace NCC.Extend.Entitys.lq_salary_statistics | @@ -442,5 +496,29 @@ namespace NCC.Extend.Entitys.lq_salary_statistics | ||
| 442 | /// </summary> | 496 | /// </summary> |
| 443 | [SugarColumn(ColumnName = "F_UpdateUser")] | 497 | [SugarColumn(ColumnName = "F_UpdateUser")] |
| 444 | public string UpdateUser { get; set; } | 498 | public string UpdateUser { get; set; } |
| 499 | + | ||
| 500 | + /// <summary> | ||
| 501 | + /// 是否新店 | ||
| 502 | + /// </summary> | ||
| 503 | + [SugarColumn(ColumnName = "F_IsNewStore")] | ||
| 504 | + public string IsNewStore { get; set; } | ||
| 505 | + | ||
| 506 | + /// <summary> | ||
| 507 | + /// 新店保护阶段 | ||
| 508 | + /// </summary> | ||
| 509 | + [SugarColumn(ColumnName = "F_NewStoreProtectionStage")] | ||
| 510 | + public int NewStoreProtectionStage { get; set; } | ||
| 511 | + | ||
| 512 | + /// <summary> | ||
| 513 | + /// 门店类型 | ||
| 514 | + /// </summary> | ||
| 515 | + [SugarColumn(ColumnName = "F_StoreType")] | ||
| 516 | + public int? StoreType { get; set; } | ||
| 517 | + | ||
| 518 | + /// <summary> | ||
| 519 | + /// 门店类别 | ||
| 520 | + /// </summary> | ||
| 521 | + [SugarColumn(ColumnName = "F_StoreCategory")] | ||
| 522 | + public int? StoreCategory { get; set; } | ||
| 445 | } | 523 | } |
| 446 | } | 524 | } |
| 447 | \ No newline at end of file | 525 | \ No newline at end of file |
netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs
0 → 100644
| 1 | +using Microsoft.AspNetCore.Authorization; | ||
| 2 | +using Microsoft.AspNetCore.Http; | ||
| 3 | +using Microsoft.AspNetCore.Mvc; | ||
| 4 | +using NCC.Common.Enum; | ||
| 5 | +using NCC.Common.Filter; | ||
| 6 | +using NCC.Common.Helper; | ||
| 7 | +using NCC.Dependency; | ||
| 8 | +using NCC.DynamicApiController; | ||
| 9 | +using NCC.Extend.Entitys.Dto.LqSalaryExtraCalculation; | ||
| 10 | +using Yitter.IdGenerator; | ||
| 11 | +using NCC.Extend.Entitys.lq_salary_extra_calculation; | ||
| 12 | +using NCC.Extend.Entitys.lq_md_xdbhsj; | ||
| 13 | +using NCC.System.Entitys.Permission; | ||
| 14 | +using SqlSugar; | ||
| 15 | +using System; | ||
| 16 | +using System.Collections.Generic; | ||
| 17 | +using System.IO; | ||
| 18 | +using System.Linq; | ||
| 19 | +using System.Threading.Tasks; | ||
| 20 | +using NCC.Common.Core.Manager; | ||
| 21 | +using NCC.FriendlyException; | ||
| 22 | + | ||
| 23 | +namespace NCC.Extend | ||
| 24 | +{ | ||
| 25 | + /// <summary> | ||
| 26 | + /// 健康师工资额外计算服务 | ||
| 27 | + /// </summary> | ||
| 28 | + [ApiDescriptionSettings(Tag = "健康师工资额外计算", Name = "LqSalaryExtraCalculation", Order = 301)] | ||
| 29 | + [Route("api/Extend/[controller]")] | ||
| 30 | + public class LqSalaryExtraCalculationService : IDynamicApiController, ITransient | ||
| 31 | + { | ||
| 32 | + private readonly ISqlSugarClient _db; | ||
| 33 | + private readonly IUserManager _userManager; | ||
| 34 | + | ||
| 35 | + /// <summary> | ||
| 36 | + /// 初始化一个<see cref="LqSalaryExtraCalculationService"/>类型的新实例 | ||
| 37 | + /// </summary> | ||
| 38 | + public LqSalaryExtraCalculationService(ISqlSugarClient db, IUserManager userManager) | ||
| 39 | + { | ||
| 40 | + _db = db; | ||
| 41 | + _userManager = userManager; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + /// <summary> | ||
| 45 | + /// 获取健康师工资额外计算列表 | ||
| 46 | + /// </summary> | ||
| 47 | + /// <param name="input">查询参数</param> | ||
| 48 | + /// <returns>健康师工资额外计算分页列表</returns> | ||
| 49 | + [HttpGet] | ||
| 50 | + public async Task<PageResult<SalaryExtraCalculationOutput>> GetList([FromQuery] SalaryExtraCalculationInput input) | ||
| 51 | + { | ||
| 52 | + var query = _db.Queryable<LqSalaryExtraCalculationEntity>() | ||
| 53 | + .LeftJoin<UserEntity>((ec, u) => ec.EmployeeId == u.Id); | ||
| 54 | + | ||
| 55 | + // 年份筛选 | ||
| 56 | + if (input.Year.HasValue) | ||
| 57 | + { | ||
| 58 | + query = query.Where((ec, u) => ec.Year == input.Year.Value); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + // 月份筛选 | ||
| 62 | + if (input.Month.HasValue) | ||
| 63 | + { | ||
| 64 | + query = query.Where((ec, u) => ec.Month == input.Month.Value); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + // 健康师ID筛选 | ||
| 68 | + if (!string.IsNullOrEmpty(input.EmployeeId)) | ||
| 69 | + { | ||
| 70 | + query = query.Where((ec, u) => ec.EmployeeId == input.EmployeeId); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + // 关键词搜索(姓名或电话) | ||
| 74 | + if (!string.IsNullOrEmpty(input.Keyword)) | ||
| 75 | + { | ||
| 76 | + query = query.Where((ec, u) => u.RealName.Contains(input.Keyword) || u.MobilePhone.Contains(input.Keyword)); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + var list = await query.Select((ec, u) => new SalaryExtraCalculationOutput | ||
| 80 | + { | ||
| 81 | + Id = ec.Id, | ||
| 82 | + EmployeeId = ec.EmployeeId, | ||
| 83 | + EmployeeName = u.RealName, | ||
| 84 | + EmployeePhone = u.MobilePhone, | ||
| 85 | + Year = ec.Year, | ||
| 86 | + Month = ec.Month, | ||
| 87 | + BaseRewardPerformance = ec.BaseRewardPerformance, | ||
| 88 | + CooperationRewardPerformance = ec.CooperationRewardPerformance, | ||
| 89 | + NewCustomerPerformance = ec.NewCustomerPerformance, | ||
| 90 | + NewCustomerConversionRate = ec.NewCustomerConversionRate, | ||
| 91 | + UpgradePerformance = ec.UpgradePerformance, | ||
| 92 | + UpgradeConversionRate = ec.UpgradeConversionRate, | ||
| 93 | + UpgradeCustomerCount = ec.UpgradeCustomerCount, | ||
| 94 | + OtherPerformanceAdd = ec.OtherPerformanceAdd, | ||
| 95 | + OtherPerformanceSubtract = ec.OtherPerformanceSubtract | ||
| 96 | + }) | ||
| 97 | + .ToPagedListAsync(input.currentPage, input.pageSize); | ||
| 98 | + | ||
| 99 | + return PageResult<SalaryExtraCalculationOutput>.SqlSugarPageResult(list); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + /// <summary> | ||
| 103 | + /// 从Excel导入健康师工资额外计算数据 | ||
| 104 | + /// </summary> | ||
| 105 | + /// <param name="file">Excel文件</param> | ||
| 106 | + /// <returns>导入结果</returns> | ||
| 107 | + [HttpPost("ImportFromExcel")] | ||
| 108 | + public async Task<dynamic> ImportFromExcel(IFormFile file) | ||
| 109 | + { | ||
| 110 | + try | ||
| 111 | + { | ||
| 112 | + if (file == null || file.Length == 0) | ||
| 113 | + { | ||
| 114 | + throw NCCException.Oh("请选择要上传的Excel文件"); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + // 检查文件格式 | ||
| 118 | + var allowedExtensions = new[] { ".xlsx", ".xls" }; | ||
| 119 | + var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant(); | ||
| 120 | + if (!allowedExtensions.Contains(fileExtension)) | ||
| 121 | + { | ||
| 122 | + throw NCCException.Oh("只支持.xlsx和.xls格式的Excel文件"); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + var importData = new List<SalaryExtraCalculationImportInput>(); | ||
| 126 | + | ||
| 127 | + // 保存临时文件 | ||
| 128 | + var tempFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + Path.GetExtension(file.FileName)); | ||
| 129 | + try | ||
| 130 | + { | ||
| 131 | + using (var stream = new FileStream(tempFilePath, FileMode.Create)) | ||
| 132 | + { | ||
| 133 | + await file.CopyToAsync(stream); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + // 使用ExcelImportHelper读取Excel文件 | ||
| 137 | + // 参数说明:0表示第一个工作表,0表示第一行是标题行 | ||
| 138 | + var dataTable = ExcelImportHelper.ToDataTable(tempFilePath, 0, 0); | ||
| 139 | + | ||
| 140 | + if (dataTable.Rows.Count == 0) | ||
| 141 | + { | ||
| 142 | + throw NCCException.Oh("Excel文件中没有数据行"); | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + // Excel字段顺序:id, 健康师姓名, 健康师电话, 年份, 月份, 基础奖励业绩, 合作奖励业绩, 新客业绩, 新客成交率, 升单业绩, 升单成交率, 升单人头数, 其他业绩加, 其他业绩减 | ||
| 146 | + // 从第1行开始读取数据(跳过标题行) | ||
| 147 | + for (int i = 1; i < dataTable.Rows.Count; i++) | ||
| 148 | + { | ||
| 149 | + try | ||
| 150 | + { | ||
| 151 | + var row = dataTable.Rows[i]; | ||
| 152 | + var id = row[0]?.ToString()?.Trim(); | ||
| 153 | + var employeeName = row[1]?.ToString()?.Trim(); | ||
| 154 | + var employeePhone = row[2]?.ToString()?.Trim(); | ||
| 155 | + var yearText = row[3]?.ToString()?.Trim(); | ||
| 156 | + var monthText = row[4]?.ToString()?.Trim(); | ||
| 157 | + var baseRewardPerformanceText = row[5]?.ToString()?.Trim(); | ||
| 158 | + var cooperationRewardPerformanceText = row[6]?.ToString()?.Trim(); | ||
| 159 | + var newCustomerPerformanceText = row[7]?.ToString()?.Trim(); | ||
| 160 | + var newCustomerConversionRateText = row[8]?.ToString()?.Trim(); | ||
| 161 | + var upgradePerformanceText = row[9]?.ToString()?.Trim(); | ||
| 162 | + var upgradeConversionRateText = row[10]?.ToString()?.Trim(); | ||
| 163 | + var upgradeCustomerCountText = row[11]?.ToString()?.Trim(); | ||
| 164 | + var otherPerformanceAddText = row[12]?.ToString()?.Trim(); | ||
| 165 | + var otherPerformanceSubtractText = row[13]?.ToString()?.Trim(); | ||
| 166 | + | ||
| 167 | + // 跳过空行 | ||
| 168 | + if (string.IsNullOrEmpty(employeeName) && string.IsNullOrEmpty(employeePhone)) | ||
| 169 | + { | ||
| 170 | + continue; | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + // 验证必填字段 | ||
| 174 | + if (string.IsNullOrEmpty(employeeName)) | ||
| 175 | + { | ||
| 176 | + throw new Exception($"第{i + 1}行:健康师姓名不能为空"); | ||
| 177 | + } | ||
| 178 | + if (string.IsNullOrEmpty(employeePhone)) | ||
| 179 | + { | ||
| 180 | + throw new Exception($"第{i + 1}行:健康师电话不能为空"); | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + // 解析年份 | ||
| 184 | + if (!int.TryParse(yearText, out int year)) | ||
| 185 | + { | ||
| 186 | + throw new Exception($"第{i + 1}行:年份格式错误"); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + // 解析月份 | ||
| 190 | + if (!int.TryParse(monthText, out int month)) | ||
| 191 | + { | ||
| 192 | + throw new Exception($"第{i + 1}行:月份格式错误"); | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + // 验证月份范围 | ||
| 196 | + if (month < 1 || month > 12) | ||
| 197 | + { | ||
| 198 | + throw new Exception($"第{i + 1}行:月份必须在1-12之间"); | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + // 解析数值字段(允许为空,默认为0) | ||
| 202 | + decimal.TryParse(baseRewardPerformanceText, out decimal baseRewardPerformance); | ||
| 203 | + decimal.TryParse(cooperationRewardPerformanceText, out decimal cooperationRewardPerformance); | ||
| 204 | + decimal.TryParse(newCustomerPerformanceText, out decimal newCustomerPerformance); | ||
| 205 | + decimal.TryParse(newCustomerConversionRateText, out decimal newCustomerConversionRate); | ||
| 206 | + decimal.TryParse(upgradePerformanceText, out decimal upgradePerformance); | ||
| 207 | + decimal.TryParse(upgradeConversionRateText, out decimal upgradeConversionRate); | ||
| 208 | + decimal.TryParse(upgradeCustomerCountText, out decimal upgradeCustomerCount); | ||
| 209 | + decimal.TryParse(otherPerformanceAddText, out decimal otherPerformanceAdd); | ||
| 210 | + decimal.TryParse(otherPerformanceSubtractText, out decimal otherPerformanceSubtract); | ||
| 211 | + | ||
| 212 | + var item = new SalaryExtraCalculationImportInput | ||
| 213 | + { | ||
| 214 | + Id = id, | ||
| 215 | + EmployeeName = employeeName, | ||
| 216 | + EmployeePhone = employeePhone, | ||
| 217 | + Year = year, | ||
| 218 | + Month = month, | ||
| 219 | + BaseRewardPerformance = baseRewardPerformance, | ||
| 220 | + CooperationRewardPerformance = cooperationRewardPerformance, | ||
| 221 | + NewCustomerPerformance = newCustomerPerformance, | ||
| 222 | + NewCustomerConversionRate = newCustomerConversionRate, | ||
| 223 | + UpgradePerformance = upgradePerformance, | ||
| 224 | + UpgradeConversionRate = upgradeConversionRate, | ||
| 225 | + UpgradeCustomerCount = upgradeCustomerCount, | ||
| 226 | + OtherPerformanceAdd = otherPerformanceAdd, | ||
| 227 | + OtherPerformanceSubtract = otherPerformanceSubtract | ||
| 228 | + }; | ||
| 229 | + | ||
| 230 | + importData.Add(item); | ||
| 231 | + } | ||
| 232 | + catch (Exception ex) | ||
| 233 | + { | ||
| 234 | + throw new Exception($"第{i + 1}行数据解析失败: {ex.Message}"); | ||
| 235 | + } | ||
| 236 | + } | ||
| 237 | + } | ||
| 238 | + finally | ||
| 239 | + { | ||
| 240 | + // 清理临时文件 | ||
| 241 | + if (File.Exists(tempFilePath)) | ||
| 242 | + { | ||
| 243 | + File.Delete(tempFilePath); | ||
| 244 | + } | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + if (!importData.Any()) | ||
| 248 | + { | ||
| 249 | + throw NCCException.Oh("Excel文件中没有有效的数据行"); | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + // 处理导入数据 | ||
| 253 | + return await ProcessImportData(importData); | ||
| 254 | + } | ||
| 255 | + catch (Exception ex) | ||
| 256 | + { | ||
| 257 | + throw NCCException.Oh($"上传Excel文件导入健康师工资额外计算数据失败: {ex.Message}"); | ||
| 258 | + } | ||
| 259 | + } | ||
| 260 | + | ||
| 261 | + #region 处理导入数据 | ||
| 262 | + /// <summary> | ||
| 263 | + /// 处理导入数据 | ||
| 264 | + /// </summary> | ||
| 265 | + /// <param name="importData">导入数据列表</param> | ||
| 266 | + /// <returns>导入结果</returns> | ||
| 267 | + private async Task<dynamic> ProcessImportData(List<SalaryExtraCalculationImportInput> importData) | ||
| 268 | + { | ||
| 269 | + var successCount = 0; | ||
| 270 | + var failCount = 0; | ||
| 271 | + var errorMessages = new List<string>(); | ||
| 272 | + var entitiesToInsert = new List<LqSalaryExtraCalculationEntity>(); | ||
| 273 | + var entitiesToUpdate = new List<LqSalaryExtraCalculationEntity>(); | ||
| 274 | + | ||
| 275 | + foreach (var item in importData) | ||
| 276 | + { | ||
| 277 | + try | ||
| 278 | + { | ||
| 279 | + // 1. 根据健康师姓名和电话查找用户ID | ||
| 280 | + var user = await _db.Queryable<UserEntity>() | ||
| 281 | + .Where(u => u.RealName == item.EmployeeName && u.MobilePhone == item.EmployeePhone) | ||
| 282 | + .FirstAsync(); | ||
| 283 | + | ||
| 284 | + if (user == null) | ||
| 285 | + { | ||
| 286 | + errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 不存在"); | ||
| 287 | + failCount++; | ||
| 288 | + continue; | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + // 2. 检查是否已存在相同记录(根据健康师ID、年份、月份) | ||
| 292 | + LqSalaryExtraCalculationEntity existingRecord = null; | ||
| 293 | + | ||
| 294 | + // 如果提供了ID,先尝试根据ID查找 | ||
| 295 | + if (!string.IsNullOrEmpty(item.Id)) | ||
| 296 | + { | ||
| 297 | + existingRecord = await _db.Queryable<LqSalaryExtraCalculationEntity>() | ||
| 298 | + .Where(x => x.Id == item.Id) | ||
| 299 | + .FirstAsync(); | ||
| 300 | + } | ||
| 301 | + | ||
| 302 | + // 如果没有找到,则根据健康师ID、年份、月份查找 | ||
| 303 | + if (existingRecord == null) | ||
| 304 | + { | ||
| 305 | + existingRecord = await _db.Queryable<LqSalaryExtraCalculationEntity>() | ||
| 306 | + .Where(x => x.EmployeeId == user.Id && x.Year == item.Year && x.Month == item.Month) | ||
| 307 | + .FirstAsync(); | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + if (existingRecord != null) | ||
| 311 | + { | ||
| 312 | + // 更新现有记录 | ||
| 313 | + existingRecord.BaseRewardPerformance = item.BaseRewardPerformance; | ||
| 314 | + existingRecord.CooperationRewardPerformance = item.CooperationRewardPerformance; | ||
| 315 | + existingRecord.NewCustomerPerformance = item.NewCustomerPerformance; | ||
| 316 | + existingRecord.NewCustomerConversionRate = item.NewCustomerConversionRate; | ||
| 317 | + existingRecord.UpgradePerformance = item.UpgradePerformance; | ||
| 318 | + existingRecord.UpgradeConversionRate = item.UpgradeConversionRate; | ||
| 319 | + existingRecord.UpgradeCustomerCount = item.UpgradeCustomerCount; | ||
| 320 | + existingRecord.OtherPerformanceAdd = item.OtherPerformanceAdd; | ||
| 321 | + existingRecord.OtherPerformanceSubtract = item.OtherPerformanceSubtract; | ||
| 322 | + entitiesToUpdate.Add(existingRecord); | ||
| 323 | + } | ||
| 324 | + else | ||
| 325 | + { | ||
| 326 | + // 创建新记录 | ||
| 327 | + var entity = new LqSalaryExtraCalculationEntity | ||
| 328 | + { | ||
| 329 | + Id = YitIdHelper.NextId().ToString(), | ||
| 330 | + EmployeeId = user.Id, | ||
| 331 | + Year = item.Year, | ||
| 332 | + Month = item.Month, | ||
| 333 | + BaseRewardPerformance = item.BaseRewardPerformance, | ||
| 334 | + CooperationRewardPerformance = item.CooperationRewardPerformance, | ||
| 335 | + NewCustomerPerformance = item.NewCustomerPerformance, | ||
| 336 | + NewCustomerConversionRate = item.NewCustomerConversionRate, | ||
| 337 | + UpgradePerformance = item.UpgradePerformance, | ||
| 338 | + UpgradeConversionRate = item.UpgradeConversionRate, | ||
| 339 | + UpgradeCustomerCount = item.UpgradeCustomerCount, | ||
| 340 | + OtherPerformanceAdd = item.OtherPerformanceAdd, | ||
| 341 | + OtherPerformanceSubtract = item.OtherPerformanceSubtract | ||
| 342 | + }; | ||
| 343 | + entitiesToInsert.Add(entity); | ||
| 344 | + } | ||
| 345 | + | ||
| 346 | + successCount++; | ||
| 347 | + } | ||
| 348 | + catch (Exception ex) | ||
| 349 | + { | ||
| 350 | + errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 处理失败: {ex.Message}"); | ||
| 351 | + failCount++; | ||
| 352 | + } | ||
| 353 | + } | ||
| 354 | + | ||
| 355 | + // 批量插入新记录 | ||
| 356 | + if (entitiesToInsert.Any()) | ||
| 357 | + { | ||
| 358 | + await _db.Insertable(entitiesToInsert).ExecuteCommandAsync(); | ||
| 359 | + } | ||
| 360 | + | ||
| 361 | + // 批量更新现有记录 | ||
| 362 | + if (entitiesToUpdate.Any()) | ||
| 363 | + { | ||
| 364 | + await _db.Updateable(entitiesToUpdate).ExecuteCommandAsync(); | ||
| 365 | + } | ||
| 366 | + | ||
| 367 | + var result = new | ||
| 368 | + { | ||
| 369 | + successCount, | ||
| 370 | + failCount, | ||
| 371 | + totalCount = importData.Count, | ||
| 372 | + errorMessages = errorMessages.Take(50).ToList() // 最多返回50条错误信息 | ||
| 373 | + }; | ||
| 374 | + | ||
| 375 | + if (failCount > 0) | ||
| 376 | + { | ||
| 377 | + throw NCCException.Oh($"导入完成,成功:{successCount}条,失败:{failCount}条。{string.Join("; ", errorMessages.Take(10))}"); | ||
| 378 | + } | ||
| 379 | + | ||
| 380 | + return result; | ||
| 381 | + } | ||
| 382 | + #endregion | ||
| 383 | + | ||
| 384 | + /// <summary> | ||
| 385 | + /// 为新店健康师生成模拟数据 | ||
| 386 | + /// </summary> | ||
| 387 | + /// <param name="year">年份</param> | ||
| 388 | + /// <param name="month">月份</param> | ||
| 389 | + /// <returns>生成结果</returns> | ||
| 390 | + [HttpPost("GenerateMockData")] | ||
| 391 | + public async Task<dynamic> GenerateMockData(int year, int month) | ||
| 392 | + { | ||
| 393 | + try | ||
| 394 | + { | ||
| 395 | + // 验证月份范围 | ||
| 396 | + if (month < 1 || month > 12) | ||
| 397 | + { | ||
| 398 | + throw NCCException.Oh("月份必须在1-12之间"); | ||
| 399 | + } | ||
| 400 | + | ||
| 401 | + var startDate = new DateTime(year, month, 1); | ||
| 402 | + var endDate = startDate.AddMonths(1).AddDays(-1); | ||
| 403 | + | ||
| 404 | + // 1. 查询新店保护信息 | ||
| 405 | + var newStoreProtectionList = await _db.Queryable<LqMdXdbhsjEntity>() | ||
| 406 | + .Where(x => x.Sfqy == 1) | ||
| 407 | + .ToListAsync(); | ||
| 408 | + | ||
| 409 | + // 筛选出在统计月份处于保护期内的门店 | ||
| 410 | + var newStoreIds = newStoreProtectionList | ||
| 411 | + .Where(x => x.Bhkssj <= endDate && x.Bhjssj >= startDate) | ||
| 412 | + .Select(x => x.Mdid) | ||
| 413 | + .Distinct() | ||
| 414 | + .ToList(); | ||
| 415 | + | ||
| 416 | + if (!newStoreIds.Any()) | ||
| 417 | + { | ||
| 418 | + throw NCCException.Oh("当前月份没有新店"); | ||
| 419 | + } | ||
| 420 | + | ||
| 421 | + // 2. 查询新店下的健康师 | ||
| 422 | + // 健康师的岗位字段是F_GW,值为"健康师" | ||
| 423 | + var healthCoaches = await _db.Queryable<UserEntity>() | ||
| 424 | + .Where(u => newStoreIds.Contains(u.Mdid) && u.Gw == "健康师" && u.EnabledMark == 1 && (u.DeleteMark == null || u.DeleteMark == 0)) | ||
| 425 | + .Select(u => new { u.Id, u.RealName, u.Mdid }) | ||
| 426 | + .ToListAsync(); | ||
| 427 | + | ||
| 428 | + if (!healthCoaches.Any()) | ||
| 429 | + { | ||
| 430 | + throw NCCException.Oh("新店下没有健康师"); | ||
| 431 | + } | ||
| 432 | + | ||
| 433 | + // 3. 检查是否已存在数据 | ||
| 434 | + var existingEmployeeIds = await _db.Queryable<LqSalaryExtraCalculationEntity>() | ||
| 435 | + .Where(x => x.Year == year && x.Month == month) | ||
| 436 | + .Select(x => x.EmployeeId) | ||
| 437 | + .ToListAsync(); | ||
| 438 | + | ||
| 439 | + var employeesToProcess = healthCoaches | ||
| 440 | + .Where(hc => !existingEmployeeIds.Contains(hc.Id)) | ||
| 441 | + .ToList(); | ||
| 442 | + | ||
| 443 | + if (!employeesToProcess.Any()) | ||
| 444 | + { | ||
| 445 | + throw NCCException.Oh("所有新店健康师已存在该月份的数据"); | ||
| 446 | + } | ||
| 447 | + | ||
| 448 | + // 4. 生成模拟数据 | ||
| 449 | + var random = new Random(); | ||
| 450 | + var entitiesToInsert = new List<LqSalaryExtraCalculationEntity>(); | ||
| 451 | + var successCount = 0; | ||
| 452 | + | ||
| 453 | + foreach (var employee in employeesToProcess) | ||
| 454 | + { | ||
| 455 | + try | ||
| 456 | + { | ||
| 457 | + // 生成金额 | ||
| 458 | + // 基础业绩奖励和合作业绩奖励:0-20000之间 | ||
| 459 | + var baseRewardPerformance = (decimal)(random.NextDouble() * 20000); | ||
| 460 | + var cooperationRewardPerformance = (decimal)(random.NextDouble() * 20000); | ||
| 461 | + // 其他金额字段:10000-30000之间 | ||
| 462 | + var newCustomerPerformance = (decimal)(random.NextDouble() * 20000 + 10000); | ||
| 463 | + var upgradePerformance = (decimal)(random.NextDouble() * 20000 + 10000); | ||
| 464 | + var otherPerformanceAdd = (decimal)(random.NextDouble() * 20000 + 10000); | ||
| 465 | + var otherPerformanceSubtract = (decimal)(random.NextDouble() * 20000 + 10000); | ||
| 466 | + | ||
| 467 | + // 生成转化率(0-60%之间,转换为0-0.6) | ||
| 468 | + var newCustomerConversionRate = (decimal)(random.NextDouble() * 0.6); | ||
| 469 | + var upgradeConversionRate = (decimal)(random.NextDouble() * 0.6); | ||
| 470 | + | ||
| 471 | + // 生成升单人头数(0-10之间) | ||
| 472 | + var upgradeCustomerCount = (decimal)random.Next(0, 11); | ||
| 473 | + | ||
| 474 | + var entity = new LqSalaryExtraCalculationEntity | ||
| 475 | + { | ||
| 476 | + Id = YitIdHelper.NextId().ToString(), | ||
| 477 | + EmployeeId = employee.Id, | ||
| 478 | + Year = year, | ||
| 479 | + Month = month, | ||
| 480 | + BaseRewardPerformance = Math.Round(baseRewardPerformance, 2), | ||
| 481 | + CooperationRewardPerformance = Math.Round(cooperationRewardPerformance, 2), | ||
| 482 | + NewCustomerPerformance = Math.Round(newCustomerPerformance, 2), | ||
| 483 | + NewCustomerConversionRate = Math.Round(newCustomerConversionRate, 4), | ||
| 484 | + UpgradePerformance = Math.Round(upgradePerformance, 2), | ||
| 485 | + UpgradeConversionRate = Math.Round(upgradeConversionRate, 4), | ||
| 486 | + UpgradeCustomerCount = upgradeCustomerCount, | ||
| 487 | + OtherPerformanceAdd = Math.Round(otherPerformanceAdd, 2), | ||
| 488 | + OtherPerformanceSubtract = Math.Round(otherPerformanceSubtract, 2) | ||
| 489 | + }; | ||
| 490 | + | ||
| 491 | + entitiesToInsert.Add(entity); | ||
| 492 | + successCount++; | ||
| 493 | + } | ||
| 494 | + catch | ||
| 495 | + { | ||
| 496 | + // 记录错误但继续处理其他健康师 | ||
| 497 | + continue; | ||
| 498 | + } | ||
| 499 | + } | ||
| 500 | + | ||
| 501 | + // 5. 批量插入数据 | ||
| 502 | + if (entitiesToInsert.Any()) | ||
| 503 | + { | ||
| 504 | + await _db.Insertable(entitiesToInsert).ExecuteCommandAsync(); | ||
| 505 | + } | ||
| 506 | + | ||
| 507 | + return new | ||
| 508 | + { | ||
| 509 | + successCount, | ||
| 510 | + totalCount = employeesToProcess.Count, | ||
| 511 | + message = $"成功为新店健康师生成 {successCount} 条模拟数据" | ||
| 512 | + }; | ||
| 513 | + } | ||
| 514 | + catch (Exception ex) | ||
| 515 | + { | ||
| 516 | + throw NCCException.Oh($"生成模拟数据失败: {ex.Message}"); | ||
| 517 | + } | ||
| 518 | + } | ||
| 519 | + } | ||
| 520 | +} | ||
| 521 | + |
netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
| @@ -16,6 +16,11 @@ using NCC.Extend.Entitys.lq_person_times_record; | @@ -16,6 +16,11 @@ using NCC.Extend.Entitys.lq_person_times_record; | ||
| 16 | using NCC.Extend.Entitys.lq_salary_statistics; | 16 | using NCC.Extend.Entitys.lq_salary_statistics; |
| 17 | using NCC.Extend.Entitys.lq_xh_jksyj; | 17 | using NCC.Extend.Entitys.lq_xh_jksyj; |
| 18 | using NCC.Extend.Entitys.lq_ycsd_jsj; | 18 | using NCC.Extend.Entitys.lq_ycsd_jsj; |
| 19 | +using NCC.Extend.Entitys.lq_mdxx; | ||
| 20 | +using NCC.Extend.Entitys.lq_hytk_hytk; | ||
| 21 | +using NCC.Extend.Entitys.lq_md_xdbhsj; | ||
| 22 | +using NCC.Extend.Entitys.lq_salary_extra_calculation; | ||
| 23 | +using NCC.System.Entitys.Permission; | ||
| 19 | using SqlSugar; | 24 | using SqlSugar; |
| 20 | using System; | 25 | using System; |
| 21 | using System.Collections.Generic; | 26 | using System.Collections.Generic; |
| @@ -97,7 +102,15 @@ namespace NCC.Extend | @@ -97,7 +102,15 @@ namespace NCC.Extend | ||
| 97 | TotalDeduction = x.TotalDeduction, | 102 | TotalDeduction = x.TotalDeduction, |
| 98 | ActualSalary = x.ActualSalary, | 103 | ActualSalary = x.ActualSalary, |
| 99 | IsLocked = x.IsLocked, | 104 | IsLocked = x.IsLocked, |
| 100 | - UpdateTime = x.UpdateTime | 105 | + UpdateTime = x.UpdateTime, |
| 106 | + IsNewStore = x.IsNewStore, | ||
| 107 | + NewStoreProtectionStage = x.NewStoreProtectionStage, | ||
| 108 | + StoreType = x.StoreType, | ||
| 109 | + StoreCategory = x.StoreCategory, | ||
| 110 | + ActualBasePerformance = x.ActualBasePerformance, | ||
| 111 | + ActualCooperationPerformance = x.ActualCooperationPerformance, | ||
| 112 | + NewCustomerCommission = x.NewCustomerPerformanceCommission, | ||
| 113 | + UpgradeCommission = x.UpgradePerformanceCommission | ||
| 101 | }) | 114 | }) |
| 102 | .ToPagedListAsync(input.currentPage, input.pageSize); | 115 | .ToPagedListAsync(input.currentPage, input.pageSize); |
| 103 | 116 | ||
| @@ -127,17 +140,18 @@ namespace NCC.Extend | @@ -127,17 +140,18 @@ namespace NCC.Extend | ||
| 127 | // 1.1.1 获取关联的开单记录(用于获取 sfskdd) | 140 | // 1.1.1 获取关联的开单记录(用于获取 sfskdd) |
| 128 | var billingIds = performanceList.Select(x => x.Glkdbh).Distinct().ToList(); | 141 | var billingIds = performanceList.Select(x => x.Glkdbh).Distinct().ToList(); |
| 129 | var billingDict = await _db.Queryable<LqKdKdjlbEntity>() | 142 | var billingDict = await _db.Queryable<LqKdKdjlbEntity>() |
| 130 | - .Where(x => billingIds.Contains(x.Id)) | 143 | + .Where(x => billingIds.Contains(x.Id) && x.Id != null) |
| 131 | .ToDictionaryAsync(x => x.Id, x => x.Sfskdd); | 144 | .ToDictionaryAsync(x => x.Id, x => x.Sfskdd); |
| 132 | 145 | ||
| 133 | // 1.1.2 组合数据 | 146 | // 1.1.2 组合数据 |
| 134 | var performanceData = performanceList.Select(p => new | 147 | var performanceData = performanceList.Select(p => new |
| 135 | { | 148 | { |
| 136 | - p.Jks, | 149 | + Jks = p.Jkszh, // 使用 Jkszh (账号/ID) 而不是 Jks (姓名) |
| 137 | p.Jksxm, | 150 | p.Jksxm, |
| 138 | p.StoreId, | 151 | p.StoreId, |
| 139 | p.Jksyj, | 152 | p.Jksyj, |
| 140 | p.ItemCategory, | 153 | p.ItemCategory, |
| 154 | + p.PerformanceType, // 新增业绩类型字段 | ||
| 141 | Sfskdd = billingDict.ContainsKey(p.Glkdbh) ? billingDict[p.Glkdbh] : null | 155 | Sfskdd = billingDict.ContainsKey(p.Glkdbh) ? billingDict[p.Glkdbh] : null |
| 142 | }).ToList(); | 156 | }).ToList(); |
| 143 | 157 | ||
| @@ -161,7 +175,7 @@ namespace NCC.Extend | @@ -161,7 +175,7 @@ namespace NCC.Extend | ||
| 161 | var teamList = await _db.Queryable<LqYcsdJsjEntity>() | 175 | var teamList = await _db.Queryable<LqYcsdJsjEntity>() |
| 162 | .Where(x => teamIds.Contains(x.Id)) | 176 | .Where(x => teamIds.Contains(x.Id)) |
| 163 | .ToListAsync(); | 177 | .ToListAsync(); |
| 164 | - var teamDict = teamList.ToDictionary(x => x.Id, x => x.Jsj); | 178 | + var teamDict = teamList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x.Jsj); |
| 165 | 179 | ||
| 166 | // 1.4.2 组合数据 | 180 | // 1.4.2 组合数据 |
| 167 | var teamMembers = teamUserList.Select(user => new | 181 | var teamMembers = teamUserList.Select(user => new |
| @@ -185,6 +199,55 @@ namespace NCC.Extend | @@ -185,6 +199,55 @@ namespace NCC.Extend | ||
| 185 | .Where(x => x.Month == monthStr) | 199 | .Where(x => x.Month == monthStr) |
| 186 | .ToListAsync(); | 200 | .ToListAsync(); |
| 187 | 201 | ||
| 202 | + // 1.6.1 门店总业绩计算 (开单实付 - 退款金额) | ||
| 203 | + // 开单实付 | ||
| 204 | + var storeBillingList = await _db.Queryable<LqKdKdjlbEntity>() | ||
| 205 | + .Where(x => x.Kdrq >= startDate && x.Kdrq <= endDate.AddDays(1) && x.IsEffective == 1) | ||
| 206 | + .Select(x => new { x.Djmd, x.Sfyj }) | ||
| 207 | + .ToListAsync(); | ||
| 208 | + var storeBillingDict = storeBillingList | ||
| 209 | + .Where(x => !string.IsNullOrEmpty(x.Djmd)) | ||
| 210 | + .GroupBy(x => x.Djmd) | ||
| 211 | + .ToDictionary(g => g.Key, g => g.Sum(x => x.Sfyj)); | ||
| 212 | + | ||
| 213 | + // 退款金额 | ||
| 214 | + var storeRefundList = await _db.Queryable<LqHytkHytkEntity>() | ||
| 215 | + .Where(x => x.Tksj >= startDate && x.Tksj <= endDate.AddDays(1) && x.IsEffective == 1) | ||
| 216 | + .Select(x => new { x.Mdbh, x.Tkje }) | ||
| 217 | + .ToListAsync(); | ||
| 218 | + var storeRefundDict = storeRefundList | ||
| 219 | + .Where(x => !string.IsNullOrEmpty(x.Mdbh)) | ||
| 220 | + .GroupBy(x => x.Mdbh) | ||
| 221 | + .ToDictionary(g => g.Key, g => g.Sum(x => x.Tkje ?? 0)); | ||
| 222 | + | ||
| 223 | + // 1.7 门店信息 (lq_mdxx) | ||
| 224 | + var storeList = await _db.Queryable<LqMdxxEntity>().ToListAsync(); | ||
| 225 | + var storeDict = storeList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x); | ||
| 226 | + | ||
| 227 | + // 1.7.1 门店新店保护信息 (lq_md_xdbhsj) | ||
| 228 | + var newStoreProtectionList = await _db.Queryable<LqMdXdbhsjEntity>() | ||
| 229 | + .Where(x => x.Sfqy == 1) | ||
| 230 | + .ToListAsync(); | ||
| 231 | + | ||
| 232 | + // 构造新店保护查找字典: StoreId -> List<ProtectionInfo> | ||
| 233 | + // 因为一个门店可能有多个阶段配置,虽然通常是一个时间段,但为了严谨取符合当前月份的配置 | ||
| 234 | + // 逻辑: 统计月份 (startDate ~ endDate) 是否在 bhkssj ~ bhjssj 范围内 | ||
| 235 | + // 只要统计月份与保护期有交集,就算保护期。或者严格一点,统计月份的第一天在保护期内。 | ||
| 236 | + // 这里取: 统计月份的第一天 (startDate) 在保护期内 | ||
| 237 | + var newStoreProtectionDict = newStoreProtectionList | ||
| 238 | + .Where(x => x.Bhkssj <= startDate && x.Bhjssj >= startDate) | ||
| 239 | + .GroupBy(x => x.Mdid) | ||
| 240 | + .ToDictionary(g => g.Key, g => g.First()); // 取第一个匹配的配置 | ||
| 241 | + | ||
| 242 | + // 1.8 健康师工资额外计算数据 (lq_salary_extra_calculation) | ||
| 243 | + var extraCalculationList = await _db.Queryable<LqSalaryExtraCalculationEntity>() | ||
| 244 | + .Where(x => x.Year == year && x.Month == month) | ||
| 245 | + .ToListAsync(); | ||
| 246 | + | ||
| 247 | + var extraCalculationDict = extraCalculationList | ||
| 248 | + .Where(x => !string.IsNullOrEmpty(x.EmployeeId)) | ||
| 249 | + .ToDictionary(x => x.EmployeeId, x => x); | ||
| 250 | + | ||
| 188 | // 2. 聚合每个健康师的数据对象 | 251 | // 2. 聚合每个健康师的数据对象 |
| 189 | var employeeStats = new Dictionary<string, LqSalaryStatisticsEntity>(); | 252 | var employeeStats = new Dictionary<string, LqSalaryStatisticsEntity>(); |
| 190 | 253 | ||
| @@ -197,6 +260,19 @@ namespace NCC.Extend | @@ -197,6 +260,19 @@ namespace NCC.Extend | ||
| 197 | .Distinct() | 260 | .Distinct() |
| 198 | .ToList(); | 261 | .ToList(); |
| 199 | 262 | ||
| 263 | + // 1.8 批量获取员工信息 (BASE_USER + BASE_POSITION) | ||
| 264 | + // 使用 allEmployeeIds 作为驱动,查询 BASE_USER | ||
| 265 | + var userList = await _db.Queryable<UserEntity>() | ||
| 266 | + .Where(x => allEmployeeIds.Contains(x.Id)) | ||
| 267 | + .Select(x => new { x.Id, x.RealName, x.PositionId, x.Mdid }) | ||
| 268 | + .ToListAsync(); | ||
| 269 | + | ||
| 270 | + var userDict = userList.ToDictionary(x => x.Id, x => x); | ||
| 271 | + | ||
| 272 | + var positionIds = userList.Select(x => x.PositionId).Distinct().ToList(); | ||
| 273 | + var positionList = await _db.Queryable<PositionEntity>().Where(x => positionIds.Contains(x.Id)).ToListAsync(); | ||
| 274 | + var positionLookup = positionList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x.FullName); | ||
| 275 | + | ||
| 200 | foreach (var empId in allEmployeeIds) | 276 | foreach (var empId in allEmployeeIds) |
| 201 | { | 277 | { |
| 202 | var salary = new LqSalaryStatisticsEntity | 278 | var salary = new LqSalaryStatisticsEntity |
| @@ -209,38 +285,121 @@ namespace NCC.Extend | @@ -209,38 +285,121 @@ namespace NCC.Extend | ||
| 209 | IsLocked = 0 | 285 | IsLocked = 0 |
| 210 | }; | 286 | }; |
| 211 | 287 | ||
| 212 | - // 填充基础信息 (姓名、门店) | ||
| 213 | - var perfRecord = performanceData.FirstOrDefault(x => x.Jks == empId); | ||
| 214 | - var consRecord = consumptionList.FirstOrDefault(x => x.Jks == empId); | 288 | + // 填充基础信息 (优先从 BASE_USER 获取) |
| 289 | + string userMdid = null; | ||
| 290 | + if (userDict.ContainsKey(empId)) | ||
| 291 | + { | ||
| 292 | + var user = userDict[empId]; | ||
| 293 | + salary.EmployeeName = user.RealName; | ||
| 294 | + userMdid = user.Mdid; | ||
| 295 | + | ||
| 296 | + // 岗位 | ||
| 297 | + if (user.PositionId != null && positionLookup.ContainsKey(user.PositionId)) | ||
| 298 | + { | ||
| 299 | + salary.Position = positionLookup[user.PositionId]; | ||
| 300 | + } | ||
| 301 | + } | ||
| 215 | 302 | ||
| 216 | - if (perfRecord != null) | 303 | + // 如果 BASE_USER 没名字,尝试从业务表获取 |
| 304 | + if (string.IsNullOrEmpty(salary.EmployeeName)) | ||
| 217 | { | 305 | { |
| 218 | - salary.EmployeeName = perfRecord.Jksxm; | ||
| 219 | - salary.StoreId = perfRecord.StoreId; | 306 | + var perfRecord = performanceData.FirstOrDefault(x => x.Jks == empId); |
| 307 | + var consRecord = consumptionList.FirstOrDefault(x => x.Jks == empId); | ||
| 308 | + if (perfRecord != null) salary.EmployeeName = perfRecord.Jksxm; | ||
| 309 | + else if (consRecord != null) salary.EmployeeName = consRecord.Jksxm; | ||
| 220 | } | 310 | } |
| 221 | - else if (consRecord != null) | 311 | + |
| 312 | + // 填充门店ID (从业务数据获取,因为 User 表的 OrganizeId 未必是门店) | ||
| 313 | + var perfStore = performanceData.FirstOrDefault(x => x.Jks == empId && !string.IsNullOrEmpty(x.StoreId)); | ||
| 314 | + var consStore = consumptionList.FirstOrDefault(x => x.Jks == empId && !string.IsNullOrEmpty(x.StoreId)); | ||
| 315 | + | ||
| 316 | + if (perfStore != null) salary.StoreId = perfStore.StoreId; | ||
| 317 | + else if (consStore != null) salary.StoreId = consStore.StoreId; | ||
| 318 | + | ||
| 319 | + // 如果业务数据没门店,尝试使用 User.Mdid | ||
| 320 | + if (string.IsNullOrEmpty(salary.StoreId) && !string.IsNullOrEmpty(userMdid)) | ||
| 222 | { | 321 | { |
| 223 | - salary.EmployeeName = consRecord.Jksxm; | ||
| 224 | - salary.StoreId = consRecord.StoreId; | 322 | + if (storeDict.ContainsKey(userMdid)) |
| 323 | + { | ||
| 324 | + salary.StoreId = userMdid; | ||
| 325 | + } | ||
| 225 | } | 326 | } |
| 226 | 327 | ||
| 227 | - // 填充门店名称 | ||
| 228 | - if (!string.IsNullOrEmpty(salary.StoreId)) | 328 | + // 填充门店名称及分类信息 |
| 329 | + if (!string.IsNullOrEmpty(salary.StoreId) && storeDict.ContainsKey(salary.StoreId)) | ||
| 330 | + { | ||
| 331 | + var store = storeDict[salary.StoreId]; | ||
| 332 | + salary.StoreName = store.Dm; | ||
| 333 | + salary.StoreType = store.StoreType; | ||
| 334 | + salary.StoreCategory = store.StoreCategory; | ||
| 335 | + } | ||
| 336 | + | ||
| 337 | + // 填充新店保护信息 | ||
| 338 | + if (!string.IsNullOrEmpty(salary.StoreId) && newStoreProtectionDict.ContainsKey(salary.StoreId)) | ||
| 339 | + { | ||
| 340 | + var protection = newStoreProtectionDict[salary.StoreId]; | ||
| 341 | + salary.IsNewStore = "是"; | ||
| 342 | + salary.NewStoreProtectionStage = protection.Stage; | ||
| 343 | + } | ||
| 344 | + else | ||
| 229 | { | 345 | { |
| 230 | - // 这里简单处理,实际可能需要缓存门店列表 | ||
| 231 | - // salary.StoreName = ... | 346 | + salary.IsNewStore = "否"; |
| 347 | + salary.NewStoreProtectionStage = 0; | ||
| 232 | } | 348 | } |
| 233 | 349 | ||
| 234 | // 2.1 计算个人业绩 | 350 | // 2.1 计算个人业绩 |
| 235 | var myPerf = performanceData.Where(x => x.Jks == empId).ToList(); | 351 | var myPerf = performanceData.Where(x => x.Jks == empId).ToList(); |
| 236 | - salary.BasePerformance = myPerf.Where(x => x.ItemCategory == "基础业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0")); | ||
| 237 | - salary.CooperationPerformance = myPerf.Where(x => x.ItemCategory == "合作业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0")); | 352 | + salary.BasePerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "基础业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0")); |
| 353 | + salary.CooperationPerformance = myPerf.Where(x => (x.PerformanceType ?? "").Trim() == "合作业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0")); | ||
| 238 | salary.TotalPerformance = myPerf.Sum(x => decimal.Parse(x.Jksyj ?? "0")); | 354 | salary.TotalPerformance = myPerf.Sum(x => decimal.Parse(x.Jksyj ?? "0")); |
| 239 | 355 | ||
| 240 | // 新客与升单业绩 | 356 | // 新客与升单业绩 |
| 241 | salary.NewCustomerPerformance = myPerf.Where(x => x.Sfskdd == "是").Sum(x => decimal.Parse(x.Jksyj ?? "0")); | 357 | salary.NewCustomerPerformance = myPerf.Where(x => x.Sfskdd == "是").Sum(x => decimal.Parse(x.Jksyj ?? "0")); |
| 242 | salary.UpgradePerformance = myPerf.Where(x => x.Sfskdd == "否").Sum(x => decimal.Parse(x.Jksyj ?? "0")); | 358 | salary.UpgradePerformance = myPerf.Where(x => x.Sfskdd == "否").Sum(x => decimal.Parse(x.Jksyj ?? "0")); |
| 243 | 359 | ||
| 360 | + // 2.1.1 填充额外计算数据 | ||
| 361 | + if (extraCalculationDict.ContainsKey(empId)) | ||
| 362 | + { | ||
| 363 | + var extraData = extraCalculationDict[empId]; | ||
| 364 | + salary.BaseRewardPerformance = extraData.BaseRewardPerformance; | ||
| 365 | + salary.CooperationRewardPerformance = extraData.CooperationRewardPerformance; | ||
| 366 | + salary.OtherPerformanceAdd = extraData.OtherPerformanceAdd; | ||
| 367 | + salary.OtherPerformanceSubtract = extraData.OtherPerformanceSubtract; | ||
| 368 | + salary.UpgradeCustomerCount = extraData.UpgradeCustomerCount; | ||
| 369 | + salary.NewCustomerConversionRate = extraData.NewCustomerConversionRate; | ||
| 370 | + } | ||
| 371 | + | ||
| 372 | + // 2.1.2 计算实际基础业绩和实际合作业绩 | ||
| 373 | + // 定义新店相关变量,供后续多处使用 | ||
| 374 | + bool isNewStore = salary.IsNewStore == "是"; | ||
| 375 | + int newStoreStage = salary.NewStoreProtectionStage; | ||
| 376 | + | ||
| 377 | + // 实际基础业绩 = 基础业绩 - 基础奖励业绩 + 其他业绩加 - 其他业绩减 | ||
| 378 | + decimal actualBasePerformance = salary.BasePerformance | ||
| 379 | + - salary.BaseRewardPerformance | ||
| 380 | + + salary.OtherPerformanceAdd | ||
| 381 | + - salary.OtherPerformanceSubtract; | ||
| 382 | + | ||
| 383 | + // 新店额外调整:根据阶段扣除新客业绩或升单业绩 | ||
| 384 | + if (isNewStore) | ||
| 385 | + { | ||
| 386 | + if (newStoreStage == 1) | ||
| 387 | + { | ||
| 388 | + // 第一阶段:扣除新客业绩 | ||
| 389 | + actualBasePerformance -= salary.NewCustomerPerformance; | ||
| 390 | + } | ||
| 391 | + else if (newStoreStage == 2) | ||
| 392 | + { | ||
| 393 | + // 第二阶段:扣除升单业绩 | ||
| 394 | + actualBasePerformance -= salary.UpgradePerformance; | ||
| 395 | + } | ||
| 396 | + } | ||
| 397 | + | ||
| 398 | + salary.ActualBasePerformance = actualBasePerformance; | ||
| 399 | + | ||
| 400 | + // 实际合作业绩 = 合作业绩 - 合作奖励业绩 | ||
| 401 | + salary.ActualCooperationPerformance = salary.CooperationPerformance - salary.CooperationRewardPerformance; | ||
| 402 | + | ||
| 244 | // 2.2 计算消耗和项目数 | 403 | // 2.2 计算消耗和项目数 |
| 245 | var myCons = consumptionList.Where(x => x.Jks == empId).ToList(); | 404 | var myCons = consumptionList.Where(x => x.Jks == empId).ToList(); |
| 246 | salary.Consumption = myCons.Sum(x => x.Jksyj ?? 0); | 405 | salary.Consumption = myCons.Sum(x => x.Jksyj ?? 0); |
| @@ -258,10 +417,33 @@ namespace NCC.Extend | @@ -258,10 +417,33 @@ namespace NCC.Extend | ||
| 258 | 417 | ||
| 259 | // 2.5 战队信息 (初始) | 418 | // 2.5 战队信息 (初始) |
| 260 | var myTeam = teamMembers.FirstOrDefault(x => x.UserId == empId); | 419 | var myTeam = teamMembers.FirstOrDefault(x => x.UserId == empId); |
| 420 | + // 初始判断岗位:如果是战队队长(IsLeader=1)则是顾问,否则是健康师 | ||
| 421 | + // 注意:这里先根据战队设置判断,后续考勤不足21天会降级 | ||
| 422 | + if (myTeam != null && myTeam.IsLeader == 1) | ||
| 423 | + { | ||
| 424 | + salary.Position = "顾问"; | ||
| 425 | + } | ||
| 426 | + else if (string.IsNullOrEmpty(salary.Position)) // 如果BASE_USER没岗位,且不是队长,默认为健康师 | ||
| 427 | + { | ||
| 428 | + salary.Position = "健康师"; | ||
| 429 | + } | ||
| 430 | + | ||
| 261 | if (myTeam != null) | 431 | if (myTeam != null) |
| 262 | { | 432 | { |
| 263 | salary.GoldTriangleId = myTeam.TeamId; | 433 | salary.GoldTriangleId = myTeam.TeamId; |
| 264 | - salary.GoldTriangleTeam = myTeam.TeamName ?? ""; | 434 | + salary.GoldTriangleTeam = myTeam.TeamName ?? "个人"; |
| 435 | + } | ||
| 436 | + else | ||
| 437 | + { | ||
| 438 | + salary.GoldTriangleTeam = "个人"; | ||
| 439 | + } | ||
| 440 | + | ||
| 441 | + // 2.6 门店总业绩 | ||
| 442 | + if (!string.IsNullOrEmpty(salary.StoreId)) | ||
| 443 | + { | ||
| 444 | + decimal billing = storeBillingDict.ContainsKey(salary.StoreId) ? storeBillingDict[salary.StoreId] : 0; | ||
| 445 | + decimal refund = storeRefundDict.ContainsKey(salary.StoreId) ? storeRefundDict[salary.StoreId] : 0; | ||
| 446 | + salary.StoreTotalPerformance = billing - refund; | ||
| 265 | } | 447 | } |
| 266 | 448 | ||
| 267 | employeeStats[empId] = salary; | 449 | employeeStats[empId] = salary; |
| @@ -293,11 +475,12 @@ namespace NCC.Extend | @@ -293,11 +475,12 @@ namespace NCC.Extend | ||
| 293 | } | 475 | } |
| 294 | } | 476 | } |
| 295 | 477 | ||
| 296 | - // 对于无效成员,移除战队标识,视为单人 | 478 | + // 对于无效成员,移除战队标识,视为单人,并重置岗位为健康师 |
| 297 | foreach (var member in invalidMembers) | 479 | foreach (var member in invalidMembers) |
| 298 | { | 480 | { |
| 299 | member.GoldTriangleId = null; | 481 | member.GoldTriangleId = null; |
| 300 | - member.GoldTriangleTeam = null; | 482 | + member.GoldTriangleTeam = "个人"; |
| 483 | + member.Position = "健康师"; // 降级为健康师 | ||
| 301 | } | 484 | } |
| 302 | 485 | ||
| 303 | // 计算有效战队的总业绩 | 486 | // 计算有效战队的总业绩 |
| @@ -313,8 +496,12 @@ namespace NCC.Extend | @@ -313,8 +496,12 @@ namespace NCC.Extend | ||
| 313 | // 4. 计算薪资 (底薪 & 提成) | 496 | // 4. 计算薪资 (底薪 & 提成) |
| 314 | foreach (var salary in employeeStats.Values) | 497 | foreach (var salary in employeeStats.Values) |
| 315 | { | 498 | { |
| 499 | + // 定义新店相关变量,供底薪和提成计算使用 | ||
| 500 | + bool isNewStore = salary.IsNewStore == "是"; | ||
| 501 | + int newStoreStage = salary.NewStoreProtectionStage; | ||
| 502 | + | ||
| 316 | // 4.1 底薪计算 | 503 | // 4.1 底薪计算 |
| 317 | - salary.HealthCoachBaseSalary = CalculateBaseSalary(salary.Consumption, salary.ProjectCount); | 504 | + salary.HealthCoachBaseSalary = CalculateBaseSalary(salary.Consumption, salary.ProjectCount, isNewStore); |
| 318 | 505 | ||
| 319 | // 4.2 提成计算 | 506 | // 4.2 提成计算 |
| 320 | // 单人业绩 <= 6000 无提成 | 507 | // 单人业绩 <= 6000 无提成 |
| @@ -335,29 +522,60 @@ namespace NCC.Extend | @@ -335,29 +522,60 @@ namespace NCC.Extend | ||
| 335 | // 是战队成员 | 522 | // 是战队成员 |
| 336 | // 获取战队人数 (注意:这里应该是有效战队人数) | 523 | // 获取战队人数 (注意:这里应该是有效战队人数) |
| 337 | var teamMemberCount = employeeStats.Values.Count(x => x.GoldTriangleId == salary.GoldTriangleId); | 524 | var teamMemberCount = employeeStats.Values.Count(x => x.GoldTriangleId == salary.GoldTriangleId); |
| 525 | + // 注意:提成点按原始基础业绩计算,不是实际基础业绩 | ||
| 338 | commissionPoint = GetTeamCommissionPoint(teamMemberCount, salary.TeamPerformance); | 526 | commissionPoint = GetTeamCommissionPoint(teamMemberCount, salary.TeamPerformance); |
| 339 | } | 527 | } |
| 340 | else | 528 | else |
| 341 | { | 529 | { |
| 342 | // 单人 (或被剔除出战队) | 530 | // 单人 (或被剔除出战队) |
| 531 | + // 注意:提成点按原始总业绩计算 | ||
| 343 | commissionPoint = GetTeamCommissionPoint(1, salary.TotalPerformance); | 532 | commissionPoint = GetTeamCommissionPoint(1, salary.TotalPerformance); |
| 344 | } | 533 | } |
| 345 | 534 | ||
| 346 | salary.CommissionPoint = commissionPoint; | 535 | salary.CommissionPoint = commissionPoint; |
| 347 | 536 | ||
| 348 | - // 计算基础/合作提成 | ||
| 349 | - salary.BasePerformanceCommission = salary.BasePerformance * 0.95m * commissionPoint; | ||
| 350 | - salary.CooperationPerformanceCommission = salary.CooperationPerformance * 0.95m * 0.65m * commissionPoint; | 537 | + // 计算基础/合作提成(使用实际业绩) |
| 538 | + salary.BasePerformanceCommission = salary.ActualBasePerformance * 0.95m * commissionPoint; | ||
| 539 | + salary.CooperationPerformanceCommission = salary.ActualCooperationPerformance * 0.95m * 0.65m * commissionPoint; | ||
| 540 | + | ||
| 541 | + // 计算新客转化率提成和升单人头提成(根据新店阶段) | ||
| 542 | + // isNewStore 和 newStoreStage 已在上面定义 | ||
| 543 | + | ||
| 544 | + if (isNewStore) | ||
| 545 | + { | ||
| 546 | + if (newStoreStage == 1) | ||
| 547 | + { | ||
| 548 | + // 第一阶段:计算新客转化率提成 | ||
| 549 | + salary.NewCustomerPerformanceCommission = CalculateNewCustomerConversionCommission( | ||
| 550 | + salary.NewCustomerPerformance, | ||
| 551 | + salary.NewCustomerConversionRate); | ||
| 552 | + } | ||
| 553 | + else if (newStoreStage == 2) | ||
| 554 | + { | ||
| 555 | + // 第二阶段:计算升单人头提成 | ||
| 556 | + salary.UpgradePerformanceCommission = CalculateUpgradeCustomerCommission( | ||
| 557 | + salary.UpgradePerformance, | ||
| 558 | + salary.UpgradeCustomerCount); | ||
| 559 | + } | ||
| 560 | + // 第三阶段:不计算新客/升单提成 | ||
| 561 | + } | ||
| 351 | 562 | ||
| 352 | // 计算顾问提成 | 563 | // 计算顾问提成 |
| 353 | // 检查是否是顾问 | 564 | // 检查是否是顾问 |
| 354 | - var isConsultant = teamMembers.Any(x => x.UserId == salary.EmployeeId && x.IsLeader == 1); | ||
| 355 | - if (isConsultant && !string.IsNullOrEmpty(salary.GoldTriangleId)) | 565 | + // 注意:这里需要重新判断是否是顾问,因为可能被降级了 |
| 566 | + if (salary.Position == "顾问" && !string.IsNullOrEmpty(salary.GoldTriangleId)) | ||
| 356 | { | 567 | { |
| 357 | - salary.ConsultantCommission = CalculateConsultantCommission(salary.TeamPerformance, employeeStats.Values.Where(x => x.GoldTriangleId == salary.GoldTriangleId).ToList()); | 568 | + salary.ConsultantCommission = CalculateConsultantCommission( |
| 569 | + salary.TeamPerformance, | ||
| 570 | + employeeStats.Values.Where(x => x.GoldTriangleId == salary.GoldTriangleId).ToList(), | ||
| 571 | + isNewStore); | ||
| 358 | } | 572 | } |
| 359 | 573 | ||
| 360 | - salary.TotalCommission = salary.BasePerformanceCommission + salary.CooperationPerformanceCommission + salary.ConsultantCommission; | 574 | + salary.TotalCommission = salary.BasePerformanceCommission |
| 575 | + + salary.CooperationPerformanceCommission | ||
| 576 | + + salary.ConsultantCommission | ||
| 577 | + + salary.NewCustomerPerformanceCommission | ||
| 578 | + + salary.UpgradePerformanceCommission; | ||
| 361 | } | 579 | } |
| 362 | 580 | ||
| 363 | // 计算占比 | 581 | // 计算占比 |
| @@ -386,7 +604,7 @@ namespace NCC.Extend | @@ -386,7 +604,7 @@ namespace NCC.Extend | ||
| 386 | /// <summary> | 604 | /// <summary> |
| 387 | /// 计算底薪 | 605 | /// 计算底薪 |
| 388 | /// </summary> | 606 | /// </summary> |
| 389 | - private decimal CalculateBaseSalary(decimal consumption, decimal projectCount) | 607 | + private decimal CalculateBaseSalary(decimal consumption, decimal projectCount, bool isNewStore) |
| 390 | { | 608 | { |
| 391 | // 0星:<1w 或 <96个 -> 1800 | 609 | // 0星:<1w 或 <96个 -> 1800 |
| 392 | // 1星:>=1w 且 >=96个 -> 2000 | 610 | // 1星:>=1w 且 >=96个 -> 2000 |
| @@ -394,6 +612,7 @@ namespace NCC.Extend | @@ -394,6 +612,7 @@ namespace NCC.Extend | ||
| 394 | // 3星:>=4w 且 >=156个 -> 2400 | 612 | // 3星:>=4w 且 >=156个 -> 2400 |
| 395 | 613 | ||
| 396 | // 特殊规则:若消耗或项目数中仅一项未达标(0星),底薪按1星(2000元)计算 | 614 | // 特殊规则:若消耗或项目数中仅一项未达标(0星),底薪按1星(2000元)计算 |
| 615 | + // 新店规则:新店底薪最低为1星(2000元),不满足1星按1星算 | ||
| 397 | 616 | ||
| 398 | int starCons = 0; | 617 | int starCons = 0; |
| 399 | if (consumption >= 40000) starCons = 3; | 618 | if (consumption >= 40000) starCons = 3; |
| @@ -413,13 +632,21 @@ namespace NCC.Extend | @@ -413,13 +632,21 @@ namespace NCC.Extend | ||
| 413 | finalStar = 1; | 632 | finalStar = 1; |
| 414 | } | 633 | } |
| 415 | 634 | ||
| 416 | - switch (finalStar) | 635 | + decimal baseSalary = finalStar switch |
| 417 | { | 636 | { |
| 418 | - case 3: return 2400; | ||
| 419 | - case 2: return 2200; | ||
| 420 | - case 1: return 2000; | ||
| 421 | - default: return 1800; | 637 | + 3 => 2400, |
| 638 | + 2 => 2200, | ||
| 639 | + 1 => 2000, | ||
| 640 | + _ => 1800 | ||
| 641 | + }; | ||
| 642 | + | ||
| 643 | + // 新店保底1星(2000元) | ||
| 644 | + if (isNewStore && baseSalary < 2000) | ||
| 645 | + { | ||
| 646 | + baseSalary = 2000; | ||
| 422 | } | 647 | } |
| 648 | + | ||
| 649 | + return baseSalary; | ||
| 423 | } | 650 | } |
| 424 | 651 | ||
| 425 | /// <summary> | 652 | /// <summary> |
| @@ -455,32 +682,72 @@ namespace NCC.Extend | @@ -455,32 +682,72 @@ namespace NCC.Extend | ||
| 455 | /// <summary> | 682 | /// <summary> |
| 456 | /// 计算顾问提成 | 683 | /// 计算顾问提成 |
| 457 | /// </summary> | 684 | /// </summary> |
| 458 | - private decimal CalculateConsultantCommission(decimal teamPerformance, List<LqSalaryStatisticsEntity> teamMembers) | 685 | + private decimal CalculateConsultantCommission(decimal teamPerformance, List<LqSalaryStatisticsEntity> teamMembers, bool isNewStore) |
| 459 | { | 686 | { |
| 460 | // 顾问提成规则: | 687 | // 顾问提成规则: |
| 461 | // 高级顾问:战队总业绩 ≥ 6万元 且 组员业绩达到40%以上 且 消耗达到6万元 → 团队总业绩0.8% | 688 | // 高级顾问:战队总业绩 ≥ 6万元 且 组员业绩达到40%以上 且 消耗达到6万元 → 团队总业绩0.8% |
| 462 | // 普通顾问:战队总业绩 ≥ 4万元 且 组员业绩达到30%以上 且 消耗达到4万元 → 团队总业绩0.3% | 689 | // 普通顾问:战队总业绩 ≥ 4万元 且 组员业绩达到30%以上 且 消耗达到4万元 → 团队总业绩0.3% |
| 463 | 690 | ||
| 464 | - // 这里的“组员业绩达到X%以上”理解为:除顾问外的成员业绩占比?或者每个成员都达标? | ||
| 465 | - // 通常理解为:团队中是否有成员业绩贡献较高,或者团队整体结构健康。 | ||
| 466 | - // 假设“组员业绩达到X%”是指:团队中至少有一名成员(非顾问本人?)或者所有成员平均? | ||
| 467 | - // 鉴于规则模糊,这里先简化实现:暂只考核总业绩和消耗。 | ||
| 468 | - // 消耗是团队总消耗吗?假设是。 | 691 | + // 注意: |
| 692 | + // 1. "组员业绩"指除顾问外的其他成员业绩总和 | ||
| 693 | + // 2. 只统计有效战队成员(考勤≥21天,未被剔除的成员) | ||
| 694 | + // 3. "达到X%以上"指:组员业绩总和 ≥ 团队总业绩 × X% | ||
| 695 | + // 4. 新店顾问不考核消耗 | ||
| 469 | 696 | ||
| 470 | var teamConsumption = teamMembers.Sum(x => x.Consumption); | 697 | var teamConsumption = teamMembers.Sum(x => x.Consumption); |
| 471 | 698 | ||
| 472 | - // 高级顾问 | ||
| 473 | - if (teamPerformance >= 60000 && teamConsumption >= 60000) | 699 | + // 计算组员(非顾问)业绩总和 |
| 700 | + // teamMembers 已经是过滤后的有效成员列表(GoldTriangleId 相同且未被剔除) | ||
| 701 | + var memberPerformance = teamMembers.Where(x => x.Position != "顾问").Sum(x => x.TotalPerformance); | ||
| 702 | + | ||
| 703 | + // 高级顾问:业绩≥6万 且 组员业绩≥40% 且 (新店 或 消耗≥6万) | ||
| 704 | + if (teamPerformance >= 60000 && memberPerformance >= teamPerformance * 0.4m) | ||
| 474 | { | 705 | { |
| 475 | - return teamPerformance * 0.008m; | 706 | + if (isNewStore || teamConsumption >= 60000) |
| 707 | + { | ||
| 708 | + return teamPerformance * 0.008m; | ||
| 709 | + } | ||
| 476 | } | 710 | } |
| 477 | - // 普通顾问 | ||
| 478 | - if (teamPerformance >= 40000 && teamConsumption >= 40000) | 711 | + |
| 712 | + // 普通顾问:业绩≥4万 且 组员业绩≥30% 且 (新店 或 消耗≥4万) | ||
| 713 | + if (teamPerformance >= 40000 && memberPerformance >= teamPerformance * 0.3m) | ||
| 479 | { | 714 | { |
| 480 | - return teamPerformance * 0.003m; | 715 | + if (isNewStore || teamConsumption >= 40000) |
| 716 | + { | ||
| 717 | + return teamPerformance * 0.003m; | ||
| 718 | + } | ||
| 481 | } | 719 | } |
| 482 | 720 | ||
| 483 | return 0; | 721 | return 0; |
| 484 | } | 722 | } |
| 723 | + | ||
| 724 | + /// <summary> | ||
| 725 | + /// 计算新客转化率提成 | ||
| 726 | + /// </summary> | ||
| 727 | + private decimal CalculateNewCustomerConversionCommission(decimal newCustomerPerformance, decimal conversionRate) | ||
| 728 | + { | ||
| 729 | + decimal commissionRate = 0; | ||
| 730 | + | ||
| 731 | + if (conversionRate >= 0.5m) commissionRate = 0.20m; | ||
| 732 | + else if (conversionRate >= 0.45m) commissionRate = 0.15m; | ||
| 733 | + else if (conversionRate >= 0.35m) commissionRate = 0.10m; | ||
| 734 | + else if (conversionRate >= 0) commissionRate = 0.06m; | ||
| 735 | + | ||
| 736 | + return newCustomerPerformance * commissionRate; | ||
| 737 | + } | ||
| 738 | + | ||
| 739 | + /// <summary> | ||
| 740 | + /// 计算升单人头提成 | ||
| 741 | + /// </summary> | ||
| 742 | + private decimal CalculateUpgradeCustomerCommission(decimal upgradePerformance, decimal upgradeCustomerCount) | ||
| 743 | + { | ||
| 744 | + decimal commissionRate = 0; | ||
| 745 | + | ||
| 746 | + if (upgradeCustomerCount >= 10) commissionRate = 0.20m; | ||
| 747 | + else if (upgradeCustomerCount >= 4) commissionRate = 0.10m; | ||
| 748 | + // 0-4个: 0% | ||
| 749 | + | ||
| 750 | + return upgradePerformance * commissionRate; | ||
| 751 | + } | ||
| 485 | } | 752 | } |
| 486 | } | 753 | } |
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
sql/创建健康师工资额外计算表.sql
0 → 100644
| 1 | +-- 创建健康师工资额外计算表 | ||
| 2 | +CREATE TABLE IF NOT EXISTS `lq_salary_extra_calculation` ( | ||
| 3 | + `F_Id` VARCHAR(50) NOT NULL COMMENT '主键ID', | ||
| 4 | + `F_EmployeeId` VARCHAR(50) NULL COMMENT '健康师ID', | ||
| 5 | + `F_Year` INT NULL COMMENT '年份', | ||
| 6 | + `F_Month` INT NULL COMMENT '月份', | ||
| 7 | + `F_BaseRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '基础奖励业绩', | ||
| 8 | + `F_CooperationRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '合作奖励业绩', | ||
| 9 | + `F_NewCustomerPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '新客业绩', | ||
| 10 | + `F_NewCustomerConversionRate` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '新客成交率', | ||
| 11 | + `F_UpgradePerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单业绩', | ||
| 12 | + `F_UpgradeConversionRate` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '升单成交率', | ||
| 13 | + PRIMARY KEY (`F_Id`), | ||
| 14 | + KEY `idx_employee_year_month` (`F_EmployeeId`, `F_Year`, `F_Month`), | ||
| 15 | + KEY `idx_year_month` (`F_Year`, `F_Month`) | ||
| 16 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康师工资额外计算表'; | ||
| 17 | + |
sql/添加健康师工资表其他业绩字段.sql
0 → 100644
| 1 | +-- 在健康师工资表中添加其他业绩加和其他业绩减字段 | ||
| 2 | + | ||
| 3 | +-- 添加其他业绩加字段 | ||
| 4 | +ALTER TABLE `lq_salary_statistics` | ||
| 5 | +ADD COLUMN `F_OtherPerformanceAdd` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩加' AFTER `F_UpgradePerformanceCommission`; | ||
| 6 | + | ||
| 7 | +-- 添加其他业绩减字段 | ||
| 8 | +ALTER TABLE `lq_salary_statistics` | ||
| 9 | +ADD COLUMN `F_OtherPerformanceSubtract` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩减' AFTER `F_OtherPerformanceAdd`; | ||
| 10 | + |
sql/添加健康师工资表升单人头数和提成金额字段.sql
0 → 100644
| 1 | +-- 在健康师工资表中添加升单人头数、新客业绩提成金额、升单业绩提成金额字段 | ||
| 2 | + | ||
| 3 | +-- 添加升单人头数字段(放在F_NewCustomerPoint字段后面) | ||
| 4 | +ALTER TABLE `lq_salary_statistics` | ||
| 5 | +ADD COLUMN `F_UpgradeCustomerCount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单人头数' AFTER `F_NewCustomerPoint`; | ||
| 6 | + | ||
| 7 | +-- 添加新客业绩提成金额字段 | ||
| 8 | +ALTER TABLE `lq_salary_statistics` | ||
| 9 | +ADD COLUMN `F_NewCustomerPerformanceCommission` DECIMAL(18,2) DEFAULT 0.00 COMMENT '新客业绩提成金额' AFTER `F_UpgradePoint`; | ||
| 10 | + | ||
| 11 | +-- 添加升单业绩提成金额字段 | ||
| 12 | +ALTER TABLE `lq_salary_statistics` | ||
| 13 | +ADD COLUMN `F_UpgradePerformanceCommission` DECIMAL(18,2) DEFAULT 0.00 COMMENT '升单业绩提成金额' AFTER `F_NewCustomerPerformanceCommission`; | ||
| 14 | + |
sql/添加健康师工资表奖励业绩字段.sql
0 → 100644
| 1 | +-- 在健康师工资表中添加基础奖励业绩和合作奖励业绩字段 | ||
| 2 | + | ||
| 3 | +-- 添加基础奖励业绩字段 | ||
| 4 | +ALTER TABLE `lq_salary_statistics` | ||
| 5 | +ADD COLUMN `F_BaseRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '基础奖励业绩' AFTER `F_CooperationPerformance`; | ||
| 6 | + | ||
| 7 | +-- 添加合作奖励业绩字段 | ||
| 8 | +ALTER TABLE `lq_salary_statistics` | ||
| 9 | +ADD COLUMN `F_CooperationRewardPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '合作奖励业绩' AFTER `F_BaseRewardPerformance`; | ||
| 10 | + |
sql/添加健康师工资表实际业绩字段.sql
0 → 100644
| 1 | +-- 在健康师工资表中添加实际基础业绩和实际合作业绩字段 | ||
| 2 | + | ||
| 3 | +-- 添加实际基础业绩字段 | ||
| 4 | +ALTER TABLE `lq_salary_statistics` | ||
| 5 | +ADD COLUMN `F_ActualBasePerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '实际基础业绩' AFTER `F_CooperationRewardPerformance`; | ||
| 6 | + | ||
| 7 | +-- 添加实际合作业绩字段 | ||
| 8 | +ALTER TABLE `lq_salary_statistics` | ||
| 9 | +ADD COLUMN `F_ActualCooperationPerformance` DECIMAL(18,2) DEFAULT 0.00 COMMENT '实际合作业绩' AFTER `F_ActualBasePerformance`; | ||
| 10 | + |
sql/添加健康师工资额外计算表其他业绩字段.sql
0 → 100644
| 1 | +-- 在健康师工资额外计算表中添加其他业绩加和其他业绩减字段 | ||
| 2 | + | ||
| 3 | +-- 添加其他业绩加字段 | ||
| 4 | +ALTER TABLE `lq_salary_extra_calculation` | ||
| 5 | +ADD COLUMN `F_OtherPerformanceAdd` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩加' AFTER `F_UpgradeCustomerCount`; | ||
| 6 | + | ||
| 7 | +-- 添加其他业绩减字段 | ||
| 8 | +ALTER TABLE `lq_salary_extra_calculation` | ||
| 9 | +ADD COLUMN `F_OtherPerformanceSubtract` DECIMAL(18,2) DEFAULT 0.00 COMMENT '其他业绩减' AFTER `F_OtherPerformanceAdd`; | ||
| 10 | + |