From ac25ff124b26024e5debb94b5b3bca78ed99587c Mon Sep 17 00:00:00 2001 From: “wangming” <“wangming@antissoft.com”> Date: Sat, 13 Dec 2025 00:55:27 +0800 Subject: [PATCH] feat: 添加大项目部老师工资计算、事业部总经理/经理工资计算、健康师额外数据导入修复、OSS URL迁移功能 --- excel/健康师额外数据模板.xlsx | Bin 0 -> 25937 bytes netcore/src/Application/NCC.API/appsettings.json | 4 ++-- netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryInput.cs | 32 ++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryOutput.cs | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs | 10 ++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryInput.cs | 32 ++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryOutput.cs | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentCrInput.cs | 5 +++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentInfoOutput.cs | 10 ++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListOutput.cs | 10 ++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListQueryInput.cs | 5 +++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentUpInput.cs | 5 +++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_business_unit_manager_salary_statistics/LqBusinessUnitManagerSalaryStatisticsEntity.cs | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_jksyj/LqHytkJksyjEntity.cs | 6 ++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_kjbsyj/LqHytkKjbsyjEntity.cs | 6 ++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_mx/LqHytkMxEntity.cs | 6 ++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs | 4 ++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs | 4 ++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_jksyj/LqKdJksyjEntity.cs | 6 ++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs | 6 ++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_pxmx/LqKdPxmxEntity.cs | 7 +++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_major_project_teacher_salary_statistics/LqMajorProjectTeacherSalaryStatisticsEntity.cs | 381 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_md_major_project_teacher_assignment/LqMdMajorProjectTeacherAssignmentEntity.cs | 6 ++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_jksyj/LqXhJksyjEntity.cs | 6 ++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_kjbsyj/LqXhKjbsyjEntity.cs | 6 ++++++ netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_pxmx/LqXhPxmxEntity.cs | 6 ++++++ netcore/src/Modularity/Extend/NCC.Extend/FileUrlMigrationService.cs | 518 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend/LqBusinessUnitManagerSalaryService.cs | 514 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs | 14 ++++++++++---- netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs | 33 ++++++++++++++++++++++++++++++++- netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs | 26 +++++++++++++++++++------- netcore/src/Modularity/Extend/NCC.Extend/LqMajorProjectTeacherSalaryService.cs | 435 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ netcore/src/Modularity/Extend/NCC.Extend/LqMdMajorProjectTeacherAssignmentService.cs | 7 +++++++ netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs | 14 ++++++++++---- netcore/src/Modularity/System/NCC.System/Service/Common/FileService.cs | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- sql/创建事业部总经理经理工资统计表.sql | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/创建大项目部老师工资统计表.sql | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/创建库存使用申请审批流程表.sql | 4 ++++ sql/同步BeautyType字段数据.sql | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/添加教育部老师字段.sql | 35 +++++++++++++++++++++++++++++++++++ sql/添加科技部归类字段.sql | 2 +- sql/重命名科技部归类字段为BeautyType.sql | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 事业部总经理经理工资计算规则梳理.md | 350 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 大项目部老师工资计算规则梳理.md | 253 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 45 files changed, 4509 insertions(+), 70 deletions(-) create mode 100644 excel/健康师额外数据模板.xlsx create mode 100644 netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryInput.cs create mode 100644 netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryOutput.cs create mode 100644 netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryInput.cs create mode 100644 netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryOutput.cs create mode 100644 netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_business_unit_manager_salary_statistics/LqBusinessUnitManagerSalaryStatisticsEntity.cs create mode 100644 netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_major_project_teacher_salary_statistics/LqMajorProjectTeacherSalaryStatisticsEntity.cs create mode 100644 netcore/src/Modularity/Extend/NCC.Extend/FileUrlMigrationService.cs create mode 100644 netcore/src/Modularity/Extend/NCC.Extend/LqBusinessUnitManagerSalaryService.cs create mode 100644 netcore/src/Modularity/Extend/NCC.Extend/LqMajorProjectTeacherSalaryService.cs create mode 100644 sql/创建事业部总经理经理工资统计表.sql create mode 100644 sql/创建大项目部老师工资统计表.sql create mode 100644 sql/同步BeautyType字段数据.sql create mode 100644 sql/添加教育部老师字段.sql create mode 100644 sql/重命名科技部归类字段为BeautyType.sql create mode 100644 事业部总经理经理工资计算规则梳理.md create mode 100644 大项目部老师工资计算规则梳理.md diff --git a/excel/健康师额外数据模板.xlsx b/excel/健康师额外数据模板.xlsx new file mode 100644 index 0000000..7afe8fd Binary files /dev/null and b/excel/健康师额外数据模板.xlsx differ diff --git a/netcore/src/Application/NCC.API/appsettings.json b/netcore/src/Application/NCC.API/appsettings.json index 5272291..bfdb337 100644 --- a/netcore/src/Application/NCC.API/appsettings.json +++ b/netcore/src/Application/NCC.API/appsettings.json @@ -189,7 +189,7 @@ "NCC_App": { "CodeAreasName": "SubDev,Food,Extend,test", //系统文件路径(末尾必须带斜杆) - "SystemPath": "Files/", + "SystemPath": "/", //微信公众号允许上传文件类型 "MPUploadFileType": "bmp,png,jpeg,jpg,gif,mp3,wma,wav,amr,mp4", //微信允许上传文件类型 @@ -213,7 +213,7 @@ "AccessKeySecret": "84dpUAlu2eoyFOIEhFGkZlIy45h0B6", "Endpoint": "oss-cn-chengdu.aliyuncs.com", "Region": "cn-chengdu", - "CustomDomain": "http://oss.lvqianmeiye.com" + "CustomDomain": "https://lvqian-erip.oss-cn-chengdu.aliyuncs.com" }, //================== 系统错误邮件报告反馈相关 ============================== --> //软件的错误报告 diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryInput.cs new file mode 100644 index 0000000..7973481 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryInput.cs @@ -0,0 +1,32 @@ +using NCC.Common.Filter; +using System; + +namespace NCC.Extend.Entitys.Dto.LqBusinessUnitManagerSalary +{ + /// + /// 事业部总经理/经理工资查询参数 + /// + public class BusinessUnitManagerSalaryInput : PageInputBase + { + /// + /// 年份 + /// + public int Year { get; set; } + + /// + /// 月份 + /// + public int Month { get; set; } + + /// + /// 经理类型(0=经理,1=总经理,不传则查询全部) + /// + public int? ManagerType { get; set; } + + /// + /// 员工姓名/账号(可选,用于模糊搜索) + /// + public string Keyword { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryOutput.cs new file mode 100644 index 0000000..c6e1ee8 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqBusinessUnitManagerSalary/BusinessUnitManagerSalaryOutput.cs @@ -0,0 +1,211 @@ +using System; + +namespace NCC.Extend.Entitys.Dto.LqBusinessUnitManagerSalary +{ + /// + /// 事业部总经理/经理工资输出 + /// + public class BusinessUnitManagerSalaryOutput + { + /// + /// 主键ID + /// + public string Id { get; set; } + + /// + /// 统计月份 + /// + public string StatisticsMonth { get; set; } + + /// + /// 核算岗位 + /// + public string Position { get; set; } + + /// + /// 员工姓名 + /// + public string EmployeeName { get; set; } + + /// + /// 员工ID + /// + public string EmployeeId { get; set; } + + /// + /// 员工账号 + /// + public string EmployeeAccount { get; set; } + + /// + /// 经理类型(0=经理,1=总经理) + /// + public int ManagerType { get; set; } + + /// + /// 是否离职 + /// + public int IsTerminated { get; set; } + + /// + /// 门店业绩明细(JSON格式) + /// + public string StorePerformanceDetail { get; set; } + + /// + /// 底薪 + /// + public decimal BaseSalary { get; set; } + + /// + /// 提成合计 + /// + public decimal TotalCommission { get; set; } + + /// + /// 在店天数 + /// + public decimal WorkingDays { get; set; } + + /// + /// 请假天数 + /// + public decimal LeaveDays { get; set; } + + /// + /// 核算应发工资 + /// + public decimal CalculatedGrossSalary { get; set; } + + /// + /// 最终应发工资 + /// + public decimal FinalGrossSalary { get; set; } + + /// + /// 当月培训补贴 + /// + public decimal MonthlyTrainingSubsidy { get; set; } + + /// + /// 当月交通补贴 + /// + public decimal MonthlyTransportSubsidy { get; set; } + + /// + /// 上月培训补贴 + /// + public decimal LastMonthTrainingSubsidy { get; set; } + + /// + /// 上月交通补贴 + /// + public decimal LastMonthTransportSubsidy { get; set; } + + /// + /// 补贴合计 + /// + public decimal TotalSubsidy { get; set; } + + /// + /// 缺卡扣款 + /// + public decimal MissingCard { get; set; } + + /// + /// 迟到扣款 + /// + public decimal LateArrival { get; set; } + + /// + /// 请假扣款 + /// + public decimal LeaveDeduction { get; set; } + + /// + /// 扣社保 + /// + public decimal SocialInsuranceDeduction { get; set; } + + /// + /// 扣除奖励 + /// + public decimal RewardDeduction { get; set; } + + /// + /// 扣住宿费 + /// + public decimal AccommodationDeduction { get; set; } + + /// + /// 扣学习期费用 + /// + public decimal StudyPeriodDeduction { get; set; } + + /// + /// 扣工作服费用 + /// + public decimal WorkClothesDeduction { get; set; } + + /// + /// 扣款合计 + /// + public decimal TotalDeduction { get; set; } + + /// + /// 发奖金 + /// + public decimal Bonus { get; set; } + + /// + /// 退手机押金 + /// + public decimal ReturnPhoneDeposit { get; set; } + + /// + /// 退住宿押金 + /// + public decimal ReturnAccommodationDeposit { get; set; } + + /// + /// 实发工资 + /// + public decimal ActualSalary { get; set; } + + /// + /// 当月是否发放 + /// + public string MonthlyPaymentStatus { get; set; } + + /// + /// 支付金额 + /// + public decimal PaidAmount { get; set; } + + /// + /// 待支付金额 + /// + public decimal PendingAmount { get; set; } + + /// + /// 补发上月 + /// + public decimal LastMonthSupplement { get; set; } + + /// + /// 当月支付总额 + /// + public decimal MonthlyTotalPayment { get; set; } + + /// + /// 是否锁定 + /// + public int IsLocked { get; set; } + + /// + /// 更新时间 + /// + public DateTime UpdateTime { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs index 4bb526f..f3501d1 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsage/LqInventoryUsageListOutput.cs @@ -111,5 +111,15 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsage /// 使用批次ID(同一批次申请的使用记录使用相同的批次ID) /// public string usageBatchId { get; set; } + + /// + /// 审批状态(待审批/审批中/已通过/未通过/已退回),通过usageBatchId关联申请表获取 + /// + public string approvalStatus { get; set; } + + /// + /// 是否已领取(1-已领取,0-未领取),通过usageBatchId关联申请表获取 + /// + public int? isReceived { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryInput.cs new file mode 100644 index 0000000..875abc2 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryInput.cs @@ -0,0 +1,32 @@ +using NCC.Common.Filter; +using System; + +namespace NCC.Extend.Entitys.Dto.LqMajorProjectTeacherSalary +{ + /// + /// 大项目部老师工资查询参数 + /// + public class MajorProjectTeacherSalaryInput : PageInputBase + { + /// + /// 年份 + /// + public int Year { get; set; } + + /// + /// 月份 + /// + public int Month { get; set; } + + /// + /// 门店ID(可选,用于筛选特定门店) + /// + public string StoreId { get; set; } + + /// + /// 员工姓名/账号(可选,用于模糊搜索) + /// + public string Keyword { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryOutput.cs new file mode 100644 index 0000000..42d3c48 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMajorProjectTeacherSalary/MajorProjectTeacherSalaryOutput.cs @@ -0,0 +1,301 @@ +using System; + +namespace NCC.Extend.Entitys.Dto.LqMajorProjectTeacherSalary +{ + /// + /// 大项目部老师工资输出 + /// + public class MajorProjectTeacherSalaryOutput + { + /// + /// 主键ID + /// + public string Id { get; set; } + + /// + /// 统计月份 + /// + public string StatisticsMonth { get; set; } + + /// + /// 门店ID + /// + public string StoreId { get; set; } + + /// + /// 门店名称 + /// + public string StoreName { get; set; } + + /// + /// 核算岗位 + /// + public string Position { get; set; } + + /// + /// 员工姓名 + /// + public string EmployeeName { get; set; } + + /// + /// 员工ID + /// + public string EmployeeId { get; set; } + + /// + /// 员工账号 + /// + public string EmployeeAccount { get; set; } + + /// + /// 开单业绩 + /// + public decimal OrderAchievement { get; set; } + + /// + /// 消耗业绩 + /// + public decimal ConsumeAchievement { get; set; } + + /// + /// 退卡业绩 + /// + public decimal RefundAchievement { get; set; } + + /// + /// 总业绩 + /// + public decimal TotalPerformance { get; set; } + + /// + /// 底薪 + /// + public decimal BaseSalary { get; set; } + + /// + /// 业绩提成比例 + /// + public decimal PerformanceCommissionRate { get; set; } + + /// + /// 业绩提成金额 + /// + public decimal PerformanceCommissionAmount { get; set; } + + /// + /// 提成合计 + /// + public decimal TotalCommission { get; set; } + + /// + /// 手工费 + /// + public decimal HandworkFee { get; set; } + + /// + /// 在店天数 + /// + public decimal WorkingDays { get; set; } + + /// + /// 请假天数 + /// + public decimal LeaveDays { get; set; } + + /// + /// 车补 + /// + public decimal TransportationAllowance { get; set; } + + /// + /// 少休费 + /// + public decimal LessRest { get; set; } + + /// + /// 全勤奖 + /// + public decimal FullAttendance { get; set; } + + /// + /// 核算应发工资 + /// + public decimal CalculatedGrossSalary { get; set; } + + /// + /// 保底工资 + /// + public decimal GuaranteedSalary { get; set; } + + /// + /// 保底请假扣款 + /// + public decimal GuaranteedLeaveDeduction { get; set; } + + /// + /// 保底底薪 + /// + public decimal GuaranteedBaseSalary { get; set; } + + /// + /// 保底补差 + /// + public decimal GuaranteedSupplement { get; set; } + + /// + /// 最终应发工资 + /// + public decimal FinalGrossSalary { get; set; } + + /// + /// 当月培训补贴 + /// + public decimal MonthlyTrainingSubsidy { get; set; } + + /// + /// 当月交通补贴 + /// + public decimal MonthlyTransportSubsidy { get; set; } + + /// + /// 上月培训补贴 + /// + public decimal LastMonthTrainingSubsidy { get; set; } + + /// + /// 上月交通补贴 + /// + public decimal LastMonthTransportSubsidy { get; set; } + + /// + /// 补贴合计 + /// + public decimal TotalSubsidy { get; set; } + + /// + /// 缺卡扣款 + /// + public decimal MissingCard { get; set; } + + /// + /// 迟到扣款 + /// + public decimal LateArrival { get; set; } + + /// + /// 请假扣款 + /// + public decimal LeaveDeduction { get; set; } + + /// + /// 扣社保 + /// + public decimal SocialInsuranceDeduction { get; set; } + + /// + /// 扣除奖励 + /// + public decimal RewardDeduction { get; set; } + + /// + /// 扣住宿费 + /// + public decimal AccommodationDeduction { get; set; } + + /// + /// 扣学习期费用 + /// + public decimal StudyPeriodDeduction { get; set; } + + /// + /// 扣工作服费用 + /// + public decimal WorkClothesDeduction { get; set; } + + /// + /// 扣款合计 + /// + public decimal TotalDeduction { get; set; } + + /// + /// 发奖金 + /// + public decimal Bonus { get; set; } + + /// + /// 退手机押金 + /// + public decimal ReturnPhoneDeposit { get; set; } + + /// + /// 退住宿押金 + /// + public decimal ReturnAccommodationDeposit { get; set; } + + /// + /// 实发工资 + /// + public decimal ActualSalary { get; set; } + + /// + /// 当月是否发放 + /// + public string MonthlyPaymentStatus { get; set; } + + /// + /// 支付金额 + /// + public decimal PaidAmount { get; set; } + + /// + /// 待支付金额 + /// + public decimal PendingAmount { get; set; } + + /// + /// 补发上月 + /// + public decimal LastMonthSupplement { get; set; } + + /// + /// 当月支付总额 + /// + public decimal MonthlyTotalPayment { get; set; } + + /// + /// 是否锁定 + /// + public int IsLocked { get; set; } + + /// + /// 是否离职 + /// + public int IsTerminated { get; set; } + + /// + /// 更新时间 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 门店类型 + /// + public int? StoreType { get; set; } + + /// + /// 门店类别 + /// + public int? StoreCategory { get; set; } + + /// + /// 是否新店 + /// + public string IsNewStore { get; set; } + + /// + /// 新店保护阶段 + /// + public int NewStoreProtectionStage { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentCrInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentCrInput.cs index 50dbc83..a55f2ca 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentCrInput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentCrInput.cs @@ -34,6 +34,11 @@ namespace NCC.Extend.Entitys.Dto.LqMdMajorProjectTeacherAssignment public string teacherId { get; set; } /// + /// 教育部老师用户ID + /// + public string educationTeacherId { get; set; } + + /// /// 备注说明 /// public string remark { get; set; } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentInfoOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentInfoOutput.cs index 394d4da..3f7c89b 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentInfoOutput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentInfoOutput.cs @@ -43,6 +43,16 @@ namespace NCC.Extend.Entitys.Dto.LqMdMajorProjectTeacherAssignment public string teacherName { get; set; } /// + /// 教育部老师用户ID + /// + public string educationTeacherId { get; set; } + + /// + /// 教育部老师姓名 + /// + public string educationTeacherName { get; set; } + + /// /// 备注说明 /// public string remark { get; set; } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListOutput.cs index db6b5f9..c9b1ffe 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListOutput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListOutput.cs @@ -43,6 +43,16 @@ namespace NCC.Extend.Entitys.Dto.LqMdMajorProjectTeacherAssignment public string teacherName { get; set; } /// + /// 教育部老师用户ID + /// + public string educationTeacherId { get; set; } + + /// + /// 教育部老师姓名 + /// + public string educationTeacherName { get; set; } + + /// /// 备注说明 /// public string remark { get; set; } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListQueryInput.cs index 5182254..1e3308c 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListQueryInput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentListQueryInput.cs @@ -26,5 +26,10 @@ namespace NCC.Extend.Entitys.Dto.LqMdMajorProjectTeacherAssignment /// 大项目部老师用户ID /// public string teacherId { get; set; } + + /// + /// 教育部老师用户ID + /// + public string educationTeacherId { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentUpInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentUpInput.cs index ece1829..702def4 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentUpInput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdMajorProjectTeacherAssignment/LqMdMajorProjectTeacherAssignmentUpInput.cs @@ -40,6 +40,11 @@ namespace NCC.Extend.Entitys.Dto.LqMdMajorProjectTeacherAssignment public string teacherId { get; set; } /// + /// 教育部老师用户ID + /// + public string educationTeacherId { get; set; } + + /// /// 备注说明 /// public string remark { get; set; } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_business_unit_manager_salary_statistics/LqBusinessUnitManagerSalaryStatisticsEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_business_unit_manager_salary_statistics/LqBusinessUnitManagerSalaryStatisticsEntity.cs new file mode 100644 index 0000000..d13f728 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_business_unit_manager_salary_statistics/LqBusinessUnitManagerSalaryStatisticsEntity.cs @@ -0,0 +1,273 @@ +using System; +using NCC.Common.Const; +using SqlSugar; + +namespace NCC.Extend.Entitys.lq_business_unit_manager_salary_statistics +{ + /// + /// 事业部总经理/经理工资统计表 + /// + [SugarTable("lq_business_unit_manager_salary_statistics")] + [Tenant(ClaimConst.TENANT_ID)] + public class LqBusinessUnitManagerSalaryStatisticsEntity + { + /// + /// 主键ID + /// + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] + public string Id { get; set; } + + /// + /// 统计月份(YYYYMM) + /// + [SugarColumn(ColumnName = "F_StatisticsMonth", Length = 6)] + public string StatisticsMonth { get; set; } + + /// + /// 核算岗位 + /// + [SugarColumn(ColumnName = "F_Position")] + public string Position { get; set; } + + /// + /// 员工姓名 + /// + [SugarColumn(ColumnName = "F_EmployeeName")] + public string EmployeeName { get; set; } + + /// + /// 员工ID + /// + [SugarColumn(ColumnName = "F_EmployeeId")] + public string EmployeeId { get; set; } + + /// + /// 员工账号 + /// + [SugarColumn(ColumnName = "F_EmployeeAccount")] + public string EmployeeAccount { get; set; } + + /// + /// 经理类型(0=经理,1=总经理) + /// + [SugarColumn(ColumnName = "F_ManagerType")] + public int ManagerType { get; set; } + + /// + /// 是否离职(0=在职,1=离职) + /// + [SugarColumn(ColumnName = "F_IsTerminated")] + public int IsTerminated { get; set; } + + /// + /// 门店业绩明细(JSON格式) + /// + [SugarColumn(ColumnName = "F_StorePerformanceDetail", ColumnDataType = "TEXT")] + public string StorePerformanceDetail { get; set; } + + /// + /// 底薪金额(固定4000元) + /// + [SugarColumn(ColumnName = "F_BaseSalary")] + public decimal BaseSalary { get; set; } + + /// + /// 提成合计(所有门店提成金额汇总) + /// + [SugarColumn(ColumnName = "F_TotalCommission")] + public decimal TotalCommission { get; set; } + + /// + /// 在店天数 + /// + [SugarColumn(ColumnName = "F_WorkingDays")] + public decimal WorkingDays { get; set; } + + /// + /// 请假天数 + /// + [SugarColumn(ColumnName = "F_LeaveDays")] + public decimal LeaveDays { get; set; } + + /// + /// 核算应发工资(底薪 + 提成合计) + /// + [SugarColumn(ColumnName = "F_CalculatedGrossSalary")] + public decimal CalculatedGrossSalary { get; set; } + + /// + /// 最终应发工资 + /// + [SugarColumn(ColumnName = "F_FinalGrossSalary")] + public decimal FinalGrossSalary { get; set; } + + /// + /// 当月培训补贴 + /// + [SugarColumn(ColumnName = "F_MonthlyTrainingSubsidy")] + public decimal MonthlyTrainingSubsidy { get; set; } + + /// + /// 当月交通补贴 + /// + [SugarColumn(ColumnName = "F_MonthlyTransportSubsidy")] + public decimal MonthlyTransportSubsidy { get; set; } + + /// + /// 上月培训补贴 + /// + [SugarColumn(ColumnName = "F_LastMonthTrainingSubsidy")] + public decimal LastMonthTrainingSubsidy { get; set; } + + /// + /// 上月交通补贴 + /// + [SugarColumn(ColumnName = "F_LastMonthTransportSubsidy")] + public decimal LastMonthTransportSubsidy { get; set; } + + /// + /// 补贴合计 + /// + [SugarColumn(ColumnName = "F_TotalSubsidy")] + public decimal TotalSubsidy { get; set; } + + /// + /// 缺卡扣款 + /// + [SugarColumn(ColumnName = "F_MissingCard")] + public decimal MissingCard { get; set; } + + /// + /// 迟到扣款 + /// + [SugarColumn(ColumnName = "F_LateArrival")] + public decimal LateArrival { get; set; } + + /// + /// 请假扣款 + /// + [SugarColumn(ColumnName = "F_LeaveDeduction")] + public decimal LeaveDeduction { get; set; } + + /// + /// 扣社保 + /// + [SugarColumn(ColumnName = "F_SocialInsuranceDeduction")] + public decimal SocialInsuranceDeduction { get; set; } + + /// + /// 扣除奖励 + /// + [SugarColumn(ColumnName = "F_RewardDeduction")] + public decimal RewardDeduction { get; set; } + + /// + /// 扣住宿费 + /// + [SugarColumn(ColumnName = "F_AccommodationDeduction")] + public decimal AccommodationDeduction { get; set; } + + /// + /// 扣学习期费用 + /// + [SugarColumn(ColumnName = "F_StudyPeriodDeduction")] + public decimal StudyPeriodDeduction { get; set; } + + /// + /// 扣工作服费用 + /// + [SugarColumn(ColumnName = "F_WorkClothesDeduction")] + public decimal WorkClothesDeduction { get; set; } + + /// + /// 扣款合计 + /// + [SugarColumn(ColumnName = "F_TotalDeduction")] + public decimal TotalDeduction { get; set; } + + /// + /// 发奖金 + /// + [SugarColumn(ColumnName = "F_Bonus")] + public decimal Bonus { get; set; } + + /// + /// 退手机押金 + /// + [SugarColumn(ColumnName = "F_ReturnPhoneDeposit")] + public decimal ReturnPhoneDeposit { get; set; } + + /// + /// 退住宿押金 + /// + [SugarColumn(ColumnName = "F_ReturnAccommodationDeposit")] + public decimal ReturnAccommodationDeposit { get; set; } + + /// + /// 实发工资 + /// + [SugarColumn(ColumnName = "F_ActualSalary")] + public decimal ActualSalary { get; set; } + + /// + /// 当月是否发放 + /// + [SugarColumn(ColumnName = "F_MonthlyPaymentStatus")] + public string MonthlyPaymentStatus { get; set; } + + /// + /// 支付金额 + /// + [SugarColumn(ColumnName = "F_PaidAmount")] + public decimal PaidAmount { get; set; } + + /// + /// 待支付金额 + /// + [SugarColumn(ColumnName = "F_PendingAmount")] + public decimal PendingAmount { get; set; } + + /// + /// 补发上月 + /// + [SugarColumn(ColumnName = "F_LastMonthSupplement")] + public decimal LastMonthSupplement { get; set; } + + /// + /// 当月支付总额 + /// + [SugarColumn(ColumnName = "F_MonthlyTotalPayment")] + public decimal MonthlyTotalPayment { get; set; } + + /// + /// 是否锁定(0=未锁定,1=已锁定) + /// + [SugarColumn(ColumnName = "F_IsLocked")] + public int IsLocked { get; set; } + + /// + /// 创建时间 + /// + [SugarColumn(ColumnName = "F_CreateTime")] + public DateTime CreateTime { get; set; } + + /// + /// 更新时间 + /// + [SugarColumn(ColumnName = "F_UpdateTime")] + public DateTime UpdateTime { get; set; } + + /// + /// 创建人 + /// + [SugarColumn(ColumnName = "F_CreateUser")] + public string CreateUser { get; set; } + + /// + /// 更新人 + /// + [SugarColumn(ColumnName = "F_UpdateUser")] + public string UpdateUser { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_jksyj/LqHytkJksyjEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_jksyj/LqHytkJksyjEntity.cs index a433bd6..24fd153 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_jksyj/LqHytkJksyjEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_jksyj/LqHytkJksyjEntity.cs @@ -143,5 +143,11 @@ namespace NCC.Extend.Entitys.lq_hytk_jksyj /// [SugarColumn(ColumnName = "F_PerformanceType")] public string PerformanceType { get; set; } + + /// + /// 科美类型(来源:lq_xmzl.F_BeautyType) + /// + [SugarColumn(ColumnName = "F_BeautyType")] + public string BeautyType { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_kjbsyj/LqHytkKjbsyjEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_kjbsyj/LqHytkKjbsyjEntity.cs index 949d03e..785298c 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_kjbsyj/LqHytkKjbsyjEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_kjbsyj/LqHytkKjbsyjEntity.cs @@ -138,5 +138,11 @@ namespace NCC.Extend.Entitys.lq_hytk_kjbsyj /// [SugarColumn(ColumnName = "F_PerformanceType")] public string PerformanceType { get; set; } + + /// + /// 科美类型(来源:lq_xmzl.F_BeautyType) + /// + [SugarColumn(ColumnName = "F_BeautyType")] + public string BeautyType { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_mx/LqHytkMxEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_mx/LqHytkMxEntity.cs index 06b8317..7c71af7 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_mx/LqHytkMxEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_mx/LqHytkMxEntity.cs @@ -126,5 +126,11 @@ namespace NCC.Extend.Entitys.lq_hytk_mx /// [SugarColumn(ColumnName = "F_PerformanceType")] public string PerformanceType { get; set; } + + /// + /// 科美类型(来源:lq_xmzl.F_BeautyType) + /// + [SugarColumn(ColumnName = "F_BeautyType")] + public string BeautyType { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs index e6e28b0..68fa78a 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs @@ -60,3 +60,7 @@ namespace NCC.Extend.Entitys.lq_inventory_usage_application_node + + + + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs index 2a98eb9..b9933d3 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs @@ -78,3 +78,7 @@ namespace NCC.Extend.Entitys.lq_inventory_usage_approval_record + + + + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_jksyj/LqKdJksyjEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_jksyj/LqKdJksyjEntity.cs index 62095b3..8ab55d5 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_jksyj/LqKdJksyjEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_jksyj/LqKdJksyjEntity.cs @@ -106,5 +106,11 @@ namespace NCC.Extend.Entitys.lq_kd_jksyj /// [SugarColumn(ColumnName = "F_PerformanceType")] public string PerformanceType { get; set; } + + /// + /// 科美类型(来源:lq_xmzl.F_BeautyType) + /// + [SugarColumn(ColumnName = "F_BeautyType")] + public string BeautyType { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs index 4f736fb..e5b4513 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs @@ -106,5 +106,11 @@ namespace NCC.Extend.Entitys.lq_kd_kjbsyj /// [SugarColumn(ColumnName = "F_PerformanceType")] public string PerformanceType { get; set; } + + /// + /// 科美类型(来源:lq_xmzl.F_BeautyType) + /// + [SugarColumn(ColumnName = "F_BeautyType")] + public string BeautyType { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_pxmx/LqKdPxmxEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_pxmx/LqKdPxmxEntity.cs index 924121c..0833783 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_pxmx/LqKdPxmxEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_pxmx/LqKdPxmxEntity.cs @@ -119,5 +119,12 @@ namespace NCC.Extend.Entitys.lq_kd_pxmx /// [SugarColumn(ColumnName = "F_PerformanceType")] public string PerformanceType { get; set; } + + /// + /// 科美类型(来源:lq_xmzl.F_BeautyType) + /// + [SugarColumn(ColumnName = "F_BeautyType")] + public string BeautyType { get; set; } + } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_major_project_teacher_salary_statistics/LqMajorProjectTeacherSalaryStatisticsEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_major_project_teacher_salary_statistics/LqMajorProjectTeacherSalaryStatisticsEntity.cs new file mode 100644 index 0000000..c79c3c7 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_major_project_teacher_salary_statistics/LqMajorProjectTeacherSalaryStatisticsEntity.cs @@ -0,0 +1,381 @@ +using System; +using NCC.Common.Const; +using SqlSugar; + +namespace NCC.Extend.Entitys.lq_major_project_teacher_salary_statistics +{ + /// + /// 大项目部老师工资统计表 + /// + [SugarTable("lq_major_project_teacher_salary_statistics")] + [Tenant(ClaimConst.TENANT_ID)] + public class LqMajorProjectTeacherSalaryStatisticsEntity + { + /// + /// 主键ID + /// + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] + public string Id { get; set; } + + /// + /// 统计月份(YYYYMM) + /// + [SugarColumn(ColumnName = "F_StatisticsMonth", Length = 6)] + public string StatisticsMonth { get; set; } + + /// + /// 门店ID + /// + [SugarColumn(ColumnName = "F_StoreId")] + public string StoreId { get; set; } + + /// + /// 门店名称 + /// + [SugarColumn(ColumnName = "F_StoreName")] + public string StoreName { get; set; } + + /// + /// 核算岗位 + /// + [SugarColumn(ColumnName = "F_Position")] + public string Position { get; set; } + + /// + /// 员工姓名 + /// + [SugarColumn(ColumnName = "F_EmployeeName")] + public string EmployeeName { get; set; } + + /// + /// 员工ID + /// + [SugarColumn(ColumnName = "F_EmployeeId")] + public string EmployeeId { get; set; } + + /// + /// 员工账号 + /// + [SugarColumn(ColumnName = "F_EmployeeAccount")] + public string EmployeeAccount { get; set; } + + /// + /// 开单业绩 + /// + [SugarColumn(ColumnName = "F_OrderAchievement", DecimalDigits = 2)] + public decimal OrderAchievement { get; set; } + + /// + /// 消耗业绩 + /// + [SugarColumn(ColumnName = "F_ConsumeAchievement", DecimalDigits = 2)] + public decimal ConsumeAchievement { get; set; } + + /// + /// 退卡业绩 + /// + [SugarColumn(ColumnName = "F_RefundAchievement", DecimalDigits = 2)] + public decimal RefundAchievement { get; set; } + + /// + /// 总业绩(开单业绩 + 消耗业绩 + 退卡业绩) + /// + [SugarColumn(ColumnName = "F_TotalPerformance", DecimalDigits = 2)] + public decimal TotalPerformance { get; set; } + + /// + /// 底薪金额(固定3000元) + /// + [SugarColumn(ColumnName = "F_BaseSalary", DecimalDigits = 2)] + public decimal BaseSalary { get; set; } + + /// + /// 业绩提成比例(百分比,如2.00表示2%,0.00表示无提成) + /// + [SugarColumn(ColumnName = "F_PerformanceCommissionRate", DecimalDigits = 4)] + public decimal PerformanceCommissionRate { get; set; } + + /// + /// 业绩提成金额(总业绩 × 业绩提成比例,阶梯式计算) + /// + [SugarColumn(ColumnName = "F_PerformanceCommissionAmount", DecimalDigits = 2)] + public decimal PerformanceCommissionAmount { get; set; } + + /// + /// 提成合计(业绩提成金额) + /// + [SugarColumn(ColumnName = "F_TotalCommission", DecimalDigits = 2)] + public decimal TotalCommission { get; set; } + + /// + /// 手工费 + /// + [SugarColumn(ColumnName = "F_HandworkFee", DecimalDigits = 2)] + public decimal HandworkFee { get; set; } + + /// + /// 车补 + /// + [SugarColumn(ColumnName = "F_TransportationAllowance", DecimalDigits = 2)] + public decimal TransportationAllowance { get; set; } + + /// + /// 少休费 + /// + [SugarColumn(ColumnName = "F_LessRest", DecimalDigits = 2)] + public decimal LessRest { get; set; } + + /// + /// 全勤奖 + /// + [SugarColumn(ColumnName = "F_FullAttendance", DecimalDigits = 2)] + public decimal FullAttendance { get; set; } + + /// + /// 在店天数 + /// + [SugarColumn(ColumnName = "F_WorkingDays", DecimalDigits = 2)] + public decimal WorkingDays { get; set; } + + /// + /// 请假天数 + /// + [SugarColumn(ColumnName = "F_LeaveDays", DecimalDigits = 2)] + public decimal LeaveDays { get; set; } + + /// + /// 核算应发工资(底薪 + 提成合计 + 其他收入) + /// + [SugarColumn(ColumnName = "F_CalculatedGrossSalary", DecimalDigits = 2)] + public decimal CalculatedGrossSalary { get; set; } + + /// + /// 保底工资 + /// + [SugarColumn(ColumnName = "F_GuaranteedSalary", DecimalDigits = 2)] + public decimal GuaranteedSalary { get; set; } + + /// + /// 保底请假扣款 + /// + [SugarColumn(ColumnName = "F_GuaranteedLeaveDeduction", DecimalDigits = 2)] + public decimal GuaranteedLeaveDeduction { get; set; } + + /// + /// 保底底薪 + /// + [SugarColumn(ColumnName = "F_GuaranteedBaseSalary", DecimalDigits = 2)] + public decimal GuaranteedBaseSalary { get; set; } + + /// + /// 保底补差 + /// + [SugarColumn(ColumnName = "F_GuaranteedSupplement", DecimalDigits = 2)] + public decimal GuaranteedSupplement { get; set; } + + /// + /// 最终应发工资(取核算应发工资和保底工资的较大值) + /// + [SugarColumn(ColumnName = "F_FinalGrossSalary", DecimalDigits = 2)] + public decimal FinalGrossSalary { get; set; } + + /// + /// 当月培训补贴 + /// + [SugarColumn(ColumnName = "F_MonthlyTrainingSubsidy", DecimalDigits = 2)] + public decimal MonthlyTrainingSubsidy { get; set; } + + /// + /// 当月交通补贴 + /// + [SugarColumn(ColumnName = "F_MonthlyTransportSubsidy", DecimalDigits = 2)] + public decimal MonthlyTransportSubsidy { get; set; } + + /// + /// 上月培训补贴 + /// + [SugarColumn(ColumnName = "F_LastMonthTrainingSubsidy", DecimalDigits = 2)] + public decimal LastMonthTrainingSubsidy { get; set; } + + /// + /// 上月交通补贴 + /// + [SugarColumn(ColumnName = "F_LastMonthTransportSubsidy", DecimalDigits = 2)] + public decimal LastMonthTransportSubsidy { get; set; } + + /// + /// 补贴合计 + /// + [SugarColumn(ColumnName = "F_TotalSubsidy", DecimalDigits = 2)] + public decimal TotalSubsidy { get; set; } + + /// + /// 缺卡扣款 + /// + [SugarColumn(ColumnName = "F_MissingCard", DecimalDigits = 2)] + public decimal MissingCard { get; set; } + + /// + /// 迟到扣款 + /// + [SugarColumn(ColumnName = "F_LateArrival", DecimalDigits = 2)] + public decimal LateArrival { get; set; } + + /// + /// 请假扣款 + /// + [SugarColumn(ColumnName = "F_LeaveDeduction", DecimalDigits = 2)] + public decimal LeaveDeduction { get; set; } + + /// + /// 扣社保 + /// + [SugarColumn(ColumnName = "F_SocialInsuranceDeduction", DecimalDigits = 2)] + public decimal SocialInsuranceDeduction { get; set; } + + /// + /// 扣除奖励 + /// + [SugarColumn(ColumnName = "F_RewardDeduction", DecimalDigits = 2)] + public decimal RewardDeduction { get; set; } + + /// + /// 扣住宿费 + /// + [SugarColumn(ColumnName = "F_AccommodationDeduction", DecimalDigits = 2)] + public decimal AccommodationDeduction { get; set; } + + /// + /// 扣学习期费用 + /// + [SugarColumn(ColumnName = "F_StudyPeriodDeduction", DecimalDigits = 2)] + public decimal StudyPeriodDeduction { get; set; } + + /// + /// 扣工作服费用 + /// + [SugarColumn(ColumnName = "F_WorkClothesDeduction", DecimalDigits = 2)] + public decimal WorkClothesDeduction { get; set; } + + /// + /// 扣款合计 + /// + [SugarColumn(ColumnName = "F_TotalDeduction", DecimalDigits = 2)] + public decimal TotalDeduction { get; set; } + + /// + /// 发奖金 + /// + [SugarColumn(ColumnName = "F_Bonus", DecimalDigits = 2)] + public decimal Bonus { get; set; } + + /// + /// 退手机押金 + /// + [SugarColumn(ColumnName = "F_ReturnPhoneDeposit", DecimalDigits = 2)] + public decimal ReturnPhoneDeposit { get; set; } + + /// + /// 退住宿押金 + /// + [SugarColumn(ColumnName = "F_ReturnAccommodationDeposit", DecimalDigits = 2)] + public decimal ReturnAccommodationDeposit { get; set; } + + /// + /// 实发工资(最终应发工资 - 扣款合计 + 补贴合计 + 奖金) + /// + [SugarColumn(ColumnName = "F_ActualSalary", DecimalDigits = 2)] + public decimal ActualSalary { get; set; } + + /// + /// 当月是否发放 + /// + [SugarColumn(ColumnName = "F_MonthlyPaymentStatus")] + public string MonthlyPaymentStatus { get; set; } + + /// + /// 支付金额 + /// + [SugarColumn(ColumnName = "F_PaidAmount", DecimalDigits = 2)] + public decimal PaidAmount { get; set; } + + /// + /// 待支付金额 + /// + [SugarColumn(ColumnName = "F_PendingAmount", DecimalDigits = 2)] + public decimal PendingAmount { get; set; } + + /// + /// 补发上月 + /// + [SugarColumn(ColumnName = "F_LastMonthSupplement", DecimalDigits = 2)] + public decimal LastMonthSupplement { get; set; } + + /// + /// 当月支付总额 + /// + [SugarColumn(ColumnName = "F_MonthlyTotalPayment", DecimalDigits = 2)] + public decimal MonthlyTotalPayment { get; set; } + + /// + /// 是否锁定(0未锁定,1已锁定) + /// + [SugarColumn(ColumnName = "F_IsLocked")] + public int IsLocked { get; set; } + + /// + /// 是否离职(0=在职,1=离职) + /// + [SugarColumn(ColumnName = "F_IsTerminated")] + public int IsTerminated { get; set; } + + /// + /// 创建时间 + /// + [SugarColumn(ColumnName = "F_CreateTime")] + public DateTime CreateTime { get; set; } + + /// + /// 更新时间 + /// + [SugarColumn(ColumnName = "F_UpdateTime")] + public DateTime UpdateTime { get; set; } + + /// + /// 创建人 + /// + [SugarColumn(ColumnName = "F_CreateUser")] + public string CreateUser { get; set; } + + /// + /// 更新人 + /// + [SugarColumn(ColumnName = "F_UpdateUser")] + public string UpdateUser { get; set; } + + /// + /// 是否新店 + /// + [SugarColumn(ColumnName = "F_IsNewStore")] + public string IsNewStore { get; set; } + + /// + /// 新店保护阶段 + /// + [SugarColumn(ColumnName = "F_NewStoreProtectionStage")] + public int NewStoreProtectionStage { get; set; } + + /// + /// 门店类型 + /// + [SugarColumn(ColumnName = "F_StoreType")] + public int? StoreType { get; set; } + + /// + /// 门店类别 + /// + [SugarColumn(ColumnName = "F_StoreCategory")] + public int? StoreCategory { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_md_major_project_teacher_assignment/LqMdMajorProjectTeacherAssignmentEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_md_major_project_teacher_assignment/LqMdMajorProjectTeacherAssignmentEntity.cs index 4e76dbc..1e4ae53 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_md_major_project_teacher_assignment/LqMdMajorProjectTeacherAssignmentEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_md_major_project_teacher_assignment/LqMdMajorProjectTeacherAssignmentEntity.cs @@ -42,6 +42,12 @@ namespace NCC.Extend.Entitys.lq_md_major_project_teacher_assignment public string TeacherId { get; set; } /// + /// 教育部老师用户ID + /// + [SugarColumn(ColumnName = "F_EducationTeacherId")] + public string EducationTeacherId { get; set; } + + /// /// 备注说明 /// [SugarColumn(ColumnName = "F_Remark")] diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_jksyj/LqXhJksyjEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_jksyj/LqXhJksyjEntity.cs index 0f2d487..490ba04 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_jksyj/LqXhJksyjEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_jksyj/LqXhJksyjEntity.cs @@ -154,5 +154,11 @@ namespace NCC.Extend.Entitys.lq_xh_jksyj /// [SugarColumn(ColumnName = "F_PerformanceType")] public string PerformanceType { get; set; } + + /// + /// 科美类型(来源:lq_xmzl.F_BeautyType) + /// + [SugarColumn(ColumnName = "F_BeautyType")] + public string BeautyType { get; set; } } } \ No newline at end of file diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_kjbsyj/LqXhKjbsyjEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_kjbsyj/LqXhKjbsyjEntity.cs index 7d9588e..f228156 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_kjbsyj/LqXhKjbsyjEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_kjbsyj/LqXhKjbsyjEntity.cs @@ -136,5 +136,11 @@ namespace NCC.Extend.Entitys.lq_xh_kjbsyj /// [SugarColumn(ColumnName = "F_PerformanceType")] public string PerformanceType { get; set; } + + /// + /// 科美类型(来源:lq_xmzl.F_BeautyType) + /// + [SugarColumn(ColumnName = "F_BeautyType")] + public string BeautyType { get; set; } } } \ No newline at end of file diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_pxmx/LqXhPxmxEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_pxmx/LqXhPxmxEntity.cs index d2f3217..0a5de56 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_pxmx/LqXhPxmxEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_pxmx/LqXhPxmxEntity.cs @@ -112,5 +112,11 @@ namespace NCC.Extend.Entitys.lq_xh_pxmx /// [SugarColumn(ColumnName = "F_PerformanceType")] public string PerformanceType { get; set; } + + /// + /// 科技部归类 + /// + [SugarColumn(ColumnName = "F_BeautyType")] + public string BeautyType { get; set; } } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend/FileUrlMigrationService.cs b/netcore/src/Modularity/Extend/NCC.Extend/FileUrlMigrationService.cs new file mode 100644 index 0000000..56c2038 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend/FileUrlMigrationService.cs @@ -0,0 +1,518 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using NCC.Common.Extension; +using NCC.Dependency; +using NCC.DynamicApiController; +using NCC.Extend.Entitys.lq_kd_kdjlb; +using NCC.FriendlyException; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SqlSugar; + +namespace NCC.Extend +{ + /// + /// 文件URL迁移服务 - 将本地文件路径转换为OSS路径 + /// + [ApiDescriptionSettings(Tag = "数据迁移", Name = "FileUrlMigration", Order = 999)] + [Route("api/Extend/[controller]")] + public class FileUrlMigrationService : IDynamicApiController, ITransient + { + private readonly ISqlSugarRepository _repository; + private readonly SqlSugarScope _db; + private readonly ILogger _logger; + + private const string OSS_BASE_URL = "http://oss.lvqianmeiye.com/Files/SystemFile"; + private const string OLD_URL_PREFIX = "/api/File/Image/annexpic/"; + private const string OLD_OSS_URL = "http://oss.lvqianmeiye.com"; + private const string NEW_OSS_URL = "https://lvqian-erip.oss-cn-chengdu.aliyuncs.com"; + + /// + /// 初始化一个类型的新实例 + /// + /// SqlSugar仓储 + /// 日志记录器 + public FileUrlMigrationService(ISqlSugarRepository repository, ILogger logger) + { + _repository = repository; + _db = _repository.Context; + _logger = logger; + } + + /// + /// 迁移所有表的文件URL路径 + /// + /// + /// 将数据库中的文件URL从本地路径格式转换为OSS路径格式 + /// + /// 处理的表和字段: + /// 1. lq_kd_kdjlb: scwj, hyqz, F_FIleUrl + /// 2. lq_xh_feedback: F_BeforeImage, F_AfterImage + /// 3. lq_hytk_hytk: F_FileUrl, F_SignatureFile + /// 4. lq_purchase_records: F_Attachment + /// + /// URL转换规则: + /// 原格式:/api/File/Image/annexpic/文件名 + /// 新格式:http://oss.lvqianmeiye.com/Files/SystemFile/文件名 + /// + /// 迁移结果统计 + [HttpPost("MigrateAllFileUrls")] + public async Task MigrateAllFileUrls() + { + var startTime = DateTime.Now; + _logger.LogInformation("开始迁移文件URL路径..."); + + var totalResults = new + { + lq_kd_kdjlb_scwj = await MigrateTableField("lq_kd_kdjlb", "scwj", "F_Id"), + lq_kd_kdjlb_hyqz = await MigrateTableField("lq_kd_kdjlb", "hyqz", "F_Id"), + lq_kd_kdjlb_F_FIleUrl = await MigrateTableField("lq_kd_kdjlb", "F_FIleUrl", "F_Id"), + lq_xh_feedback_F_BeforeImage = await MigrateTableField("lq_xh_feedback", "F_BeforeImage", "F_Id"), + lq_xh_feedback_F_AfterImage = await MigrateTableField("lq_xh_feedback", "F_AfterImage", "F_Id"), + lq_hytk_hytk_F_FileUrl = await MigrateTableField("lq_hytk_hytk", "F_FileUrl", "F_Id"), + lq_hytk_hytk_F_SignatureFile = await MigrateTableField("lq_hytk_hytk", "F_SignatureFile", "F_Id"), + lq_purchase_records_F_Attachment = await MigrateTableField("lq_purchase_records", "F_Attachment", "F_Id") + }; + + var endTime = DateTime.Now; + var duration = (endTime - startTime).TotalSeconds; + + // 统计总数 + var totalProcessed = 0; + var totalUpdated = 0; + var totalErrors = 0; + + var resultsList = new List + { + totalResults.lq_kd_kdjlb_scwj, + totalResults.lq_kd_kdjlb_hyqz, + totalResults.lq_kd_kdjlb_F_FIleUrl, + totalResults.lq_xh_feedback_F_BeforeImage, + totalResults.lq_xh_feedback_F_AfterImage, + totalResults.lq_hytk_hytk_F_FileUrl, + totalResults.lq_hytk_hytk_F_SignatureFile, + totalResults.lq_purchase_records_F_Attachment + }; + + foreach (var result in resultsList) + { + totalProcessed += result.TotalProcessed; + totalUpdated += result.TotalUpdated; + totalErrors += result.TotalErrors; + } + + _logger.LogInformation($"文件URL迁移完成,耗时:{duration:F2}秒,总处理:{totalProcessed},总更新:{totalUpdated},总错误:{totalErrors}"); + + return new + { + success = true, + duration = $"{duration:F2}秒", + summary = new + { + totalProcessed, + totalUpdated, + totalErrors + }, + details = totalResults + }; + } + + /// + /// 迁移指定表的指定字段 + /// + private async Task MigrateTableField(string tableName, string fieldName, string primaryKey) + { + var result = new MigrationResult + { + TableName = tableName, + FieldName = fieldName + }; + + try + { + _logger.LogInformation($"开始处理表 {tableName}.{fieldName}"); + + // 查询需要处理的数据 + var sql = $@" + SELECT {primaryKey} as Id, {fieldName} as FieldValue + FROM {tableName} + WHERE {fieldName} IS NOT NULL + AND {fieldName} != '' + AND {fieldName} != '[]' + AND {fieldName} LIKE '%{OLD_URL_PREFIX}%'"; + + var records = await _db.Ado.SqlQueryAsync(sql); + result.TotalProcessed = records.Count; + + if (records.Count == 0) + { + _logger.LogInformation($"表 {tableName}.{fieldName} 没有需要处理的数据"); + return result; + } + + _logger.LogInformation($"表 {tableName}.{fieldName} 找到 {records.Count} 条需要处理的数据"); + + // 分批处理,每批100条 + const int batchSize = 100; + var totalBatches = (int)Math.Ceiling((double)records.Count / batchSize); + + for (int batchIndex = 0; batchIndex < totalBatches; batchIndex++) + { + var batch = records.Skip(batchIndex * batchSize).Take(batchSize).ToList(); + var updateList = new List<(string id, string newValue)>(); + + foreach (var record in batch) + { + try + { + var id = record.Id?.ToString(); + var fieldValue = record.FieldValue?.ToString(); + + if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(fieldValue)) + continue; + + var newValue = ConvertFileUrlJson(fieldValue); + if (newValue != null && newValue != fieldValue) + { + updateList.Add((id, newValue)); + } + } + catch (Exception ex) + { + result.TotalErrors++; + _logger.LogError(ex, $"处理记录时出错,表:{tableName},字段:{fieldName},记录ID:{record.Id}"); + } + } + + // 批量更新 + if (updateList.Any()) + { + try + { + _db.BeginTran(); + foreach (var (id, newValue) in updateList) + { + // 使用参数化查询防止SQL注入 + var updateSql = $"UPDATE `{tableName}` SET `{fieldName}` = @Value WHERE `{primaryKey}` = @Id"; + await _db.Ado.ExecuteCommandAsync(updateSql, new { Value = newValue, Id = id }); + } + _db.CommitTran(); + result.TotalUpdated += updateList.Count; + _logger.LogInformation($"表 {tableName}.{fieldName} 批次 {batchIndex + 1} 成功更新 {updateList.Count} 条记录"); + } + catch (Exception ex) + { + _db.RollbackTran(); + result.TotalErrors += updateList.Count; + _logger.LogError(ex, $"批量更新时出错,表:{tableName},字段:{fieldName},批次:{batchIndex + 1}"); + } + } + + _logger.LogInformation($"表 {tableName}.{fieldName} 批次 {batchIndex + 1}/{totalBatches} 处理完成"); + } + + _logger.LogInformation($"表 {tableName}.{fieldName} 处理完成,处理:{result.TotalProcessed},更新:{result.TotalUpdated},错误:{result.TotalErrors}"); + } + catch (Exception ex) + { + _logger.LogError(ex, $"处理表 {tableName}.{fieldName} 时发生错误"); + result.TotalErrors = result.TotalProcessed; + } + + return result; + } + + /// + /// 转换文件URL JSON字符串 + /// + private string ConvertFileUrlJson(string jsonString) + { + try + { + if (string.IsNullOrWhiteSpace(jsonString) || jsonString.Trim() == "[]") + return jsonString; + + // 解析JSON数组 + var jsonArray = JArray.Parse(jsonString); + bool hasChanges = false; + + foreach (var item in jsonArray) + { + if (item is JObject obj && obj["url"] != null) + { + var url = obj["url"].ToString(); + if (url.StartsWith(OLD_URL_PREFIX)) + { + // 提取文件名 + var fileName = url.Substring(OLD_URL_PREFIX.Length); + // 构建新的OSS URL + var newUrl = $"{OSS_BASE_URL}/{fileName}"; + obj["url"] = newUrl; + hasChanges = true; + } + } + } + + return hasChanges ? jsonArray.ToString(Formatting.None) : jsonString; + } + catch (Exception ex) + { + _logger.LogError(ex, $"转换JSON时出错:{jsonString}"); + return jsonString; // 转换失败时返回原值 + } + } + + /// + /// 迁移所有表的OSS URL(将旧OSS地址替换为新OSS地址) + /// + /// + /// 将数据库中的文件URL从旧OSS地址格式转换为新OSS地址格式 + /// + /// 处理的表和字段: + /// 1. lq_kd_kdjlb: scwj, hyqz, F_FIleUrl + /// 2. lq_xh_feedback: F_BeforeImage, F_AfterImage + /// 3. lq_hytk_hytk: F_FileUrl, F_SignatureFile + /// 4. lq_purchase_records: F_Attachment + /// + /// URL转换规则: + /// 原格式:http://oss.lvqianmeiye.com/... + /// 新格式:https://lvqian-erip.oss-cn-chengdu.aliyuncs.com/... + /// + /// 迁移结果统计 + [HttpPost("MigrateOssUrls")] + public async Task MigrateOssUrls() + { + var startTime = DateTime.Now; + _logger.LogInformation("开始迁移OSS URL路径..."); + + var totalResults = new + { + lq_kd_kdjlb_scwj = await MigrateOssUrlInTableField("lq_kd_kdjlb", "scwj", "F_Id"), + lq_kd_kdjlb_hyqz = await MigrateOssUrlInTableField("lq_kd_kdjlb", "hyqz", "F_Id"), + lq_kd_kdjlb_F_FIleUrl = await MigrateOssUrlInTableField("lq_kd_kdjlb", "F_FIleUrl", "F_Id"), + lq_xh_feedback_F_BeforeImage = await MigrateOssUrlInTableField("lq_xh_feedback", "F_BeforeImage", "F_Id"), + lq_xh_feedback_F_AfterImage = await MigrateOssUrlInTableField("lq_xh_feedback", "F_AfterImage", "F_Id"), + lq_hytk_hytk_F_FileUrl = await MigrateOssUrlInTableField("lq_hytk_hytk", "F_FileUrl", "F_Id"), + lq_hytk_hytk_F_SignatureFile = await MigrateOssUrlInTableField("lq_hytk_hytk", "F_SignatureFile", "F_Id"), + lq_purchase_records_F_Attachment = await MigrateOssUrlInTableField("lq_purchase_records", "F_Attachment", "F_Id") + }; + + var endTime = DateTime.Now; + var duration = (endTime - startTime).TotalSeconds; + + // 统计总数 + var totalProcessed = 0; + var totalUpdated = 0; + var totalErrors = 0; + + var resultsList = new List + { + totalResults.lq_kd_kdjlb_scwj, + totalResults.lq_kd_kdjlb_hyqz, + totalResults.lq_kd_kdjlb_F_FIleUrl, + totalResults.lq_xh_feedback_F_BeforeImage, + totalResults.lq_xh_feedback_F_AfterImage, + totalResults.lq_hytk_hytk_F_FileUrl, + totalResults.lq_hytk_hytk_F_SignatureFile, + totalResults.lq_purchase_records_F_Attachment + }; + + foreach (var result in resultsList) + { + totalProcessed += result.TotalProcessed; + totalUpdated += result.TotalUpdated; + totalErrors += result.TotalErrors; + } + + _logger.LogInformation($"OSS URL迁移完成,耗时:{duration:F2}秒,总处理:{totalProcessed},总更新:{totalUpdated},总错误:{totalErrors}"); + + return new + { + success = true, + duration = $"{duration:F2}秒", + summary = new + { + totalProcessed, + totalUpdated, + totalErrors + }, + details = totalResults + }; + } + + /// + /// 迁移指定表的指定字段中的OSS URL + /// + private async Task MigrateOssUrlInTableField(string tableName, string fieldName, string primaryKey) + { + var result = new MigrationResult + { + TableName = tableName, + FieldName = fieldName + }; + + try + { + _logger.LogInformation($"开始处理表 {tableName}.{fieldName} 的OSS URL迁移"); + + // 查询需要处理的数据(包含旧OSS URL的记录) + var sql = $@" + SELECT {primaryKey} as Id, {fieldName} as FieldValue + FROM {tableName} + WHERE {fieldName} IS NOT NULL + AND {fieldName} != '' + AND {fieldName} != '[]' + AND {fieldName} LIKE '%{OLD_OSS_URL}%'"; + + var records = await _db.Ado.SqlQueryAsync(sql); + result.TotalProcessed = records.Count; + + if (records.Count == 0) + { + _logger.LogInformation($"表 {tableName}.{fieldName} 没有需要处理的数据"); + return result; + } + + _logger.LogInformation($"表 {tableName}.{fieldName} 找到 {records.Count} 条需要处理的数据"); + + // 分批处理,每批100条 + const int batchSize = 100; + var totalBatches = (int)Math.Ceiling((double)records.Count / batchSize); + + for (int batchIndex = 0; batchIndex < totalBatches; batchIndex++) + { + var batch = records.Skip(batchIndex * batchSize).Take(batchSize).ToList(); + var updateList = new List<(string id, string newValue)>(); + + foreach (var record in batch) + { + try + { + var id = record.Id?.ToString(); + var fieldValue = record.FieldValue?.ToString(); + + if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(fieldValue)) + continue; + + var newValue = ReplaceOssUrlInJson(fieldValue); + if (newValue != null && newValue != fieldValue) + { + updateList.Add((id, newValue)); + } + } + catch (Exception ex) + { + result.TotalErrors++; + _logger.LogError(ex, $"处理记录时出错,表:{tableName},字段:{fieldName},记录ID:{record.Id}"); + } + } + + // 批量更新 + if (updateList.Any()) + { + try + { + _db.BeginTran(); + foreach (var (id, newValue) in updateList) + { + // 使用参数化查询防止SQL注入 + var updateSql = $"UPDATE `{tableName}` SET `{fieldName}` = @Value WHERE `{primaryKey}` = @Id"; + await _db.Ado.ExecuteCommandAsync(updateSql, new { Value = newValue, Id = id }); + } + _db.CommitTran(); + result.TotalUpdated += updateList.Count; + _logger.LogInformation($"表 {tableName}.{fieldName} 批次 {batchIndex + 1} 成功更新 {updateList.Count} 条记录"); + } + catch (Exception ex) + { + _db.RollbackTran(); + result.TotalErrors += updateList.Count; + _logger.LogError(ex, $"批量更新时出错,表:{tableName},字段:{fieldName},批次:{batchIndex + 1}"); + } + } + + _logger.LogInformation($"表 {tableName}.{fieldName} 批次 {batchIndex + 1}/{totalBatches} 处理完成"); + } + + _logger.LogInformation($"表 {tableName}.{fieldName} 处理完成,处理:{result.TotalProcessed},更新:{result.TotalUpdated},错误:{result.TotalErrors}"); + } + catch (Exception ex) + { + _logger.LogError(ex, $"处理表 {tableName}.{fieldName} 时发生错误"); + result.TotalErrors = result.TotalProcessed; + } + + return result; + } + + /// + /// 替换JSON字符串中的OSS URL + /// + private string ReplaceOssUrlInJson(string jsonString) + { + try + { + if (string.IsNullOrWhiteSpace(jsonString) || jsonString.Trim() == "[]") + return jsonString; + + // 如果包含旧OSS URL,直接替换 + if (jsonString.Contains(OLD_OSS_URL)) + { + var newJsonString = jsonString.Replace(OLD_OSS_URL, NEW_OSS_URL); + + // 如果是JSON数组格式,需要解析并替换 + try + { + var jsonArray = JArray.Parse(jsonString); + bool hasChanges = false; + + foreach (var item in jsonArray) + { + if (item is JObject obj && obj["url"] != null) + { + var url = obj["url"].ToString(); + if (url.Contains(OLD_OSS_URL)) + { + obj["url"] = url.Replace(OLD_OSS_URL, NEW_OSS_URL); + hasChanges = true; + } + } + } + + return hasChanges ? jsonArray.ToString(Formatting.None) : jsonString; + } + catch + { + // 如果不是JSON格式,直接替换字符串 + return newJsonString; + } + } + + return jsonString; + } + catch (Exception ex) + { + _logger.LogError(ex, $"替换OSS URL时出错:{jsonString}"); + return jsonString; // 转换失败时返回原值 + } + } + + /// + /// 迁移结果 + /// + private class MigrationResult + { + public string TableName { get; set; } + public string FieldName { get; set; } + public int TotalProcessed { get; set; } + public int TotalUpdated { get; set; } + public int TotalErrors { get; set; } + } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqBusinessUnitManagerSalaryService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqBusinessUnitManagerSalaryService.cs new file mode 100644 index 0000000..83a8deb --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqBusinessUnitManagerSalaryService.cs @@ -0,0 +1,514 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using NCC.Common.Filter; +using NCC.Common.Helper; +using NCC.Dependency; +using NCC.DynamicApiController; +using NCC.Extend.Entitys.Dto.LqBusinessUnitManagerSalary; +using NCC.Extend.Entitys.lq_attendance_summary; +using NCC.Extend.Entitys.lq_hytk_hytk; +using NCC.Extend.Entitys.lq_kd_kdjlb; +using NCC.Extend.Entitys.lq_md_general_manager_lifeline; +using NCC.Extend.Entitys.lq_md_target; +using NCC.Extend.Entitys.lq_mdxx; +using NCC.Extend.Entitys.lq_business_unit_manager_salary_statistics; +using NCC.System.Entitys.Permission; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Yitter.IdGenerator; + +namespace NCC.Extend +{ + /// + /// 事业部总经理/经理薪酬服务 + /// + [ApiDescriptionSettings(Tag = "事业部总经理/经理薪酬服务", Name = "LqBusinessUnitManagerSalary", Order = 304)] + [Route("api/Extend/[controller]")] + public class LqBusinessUnitManagerSalaryService : IDynamicApiController, ITransient + { + private readonly ISqlSugarClient _db; + + /// + /// 初始化一个类型的新实例 + /// + public LqBusinessUnitManagerSalaryService(ISqlSugarClient db) + { + _db = db; + } + + /// + /// 获取事业部总经理/经理工资列表 + /// + /// 查询参数 + /// 事业部总经理/经理工资分页列表 + [HttpGet("business-unit-manager")] + public async Task GetBusinessUnitManagerSalaryList([FromQuery] BusinessUnitManagerSalaryInput input) + { + var monthStr = $"{input.Year}{input.Month:D2}"; + + // 1. 检查当月是否已生成工资数据 + var exists = await _db.Queryable() + .AnyAsync(x => x.StatisticsMonth == monthStr); + + // 2. 如果没有数据,则进行计算 + if (!exists) + { + await CalculateBusinessUnitManagerSalary(input.Year, input.Month); + } + + // 3. 查询数据 + var query = _db.Queryable() + .Where(x => x.StatisticsMonth == monthStr); + + if (input.ManagerType.HasValue) + { + query = query.Where(x => x.ManagerType == input.ManagerType.Value); + } + + if (!string.IsNullOrEmpty(input.Keyword)) + { + query = query.Where(x => x.EmployeeName.Contains(input.Keyword) || x.EmployeeAccount.Contains(input.Keyword)); + } + + var list = await query.Select(x => new BusinessUnitManagerSalaryOutput + { + Id = x.Id, + StatisticsMonth = x.StatisticsMonth, + Position = x.Position, + EmployeeName = x.EmployeeName, + EmployeeId = x.EmployeeId, + EmployeeAccount = x.EmployeeAccount, + ManagerType = x.ManagerType, + IsTerminated = x.IsTerminated, + StorePerformanceDetail = x.StorePerformanceDetail, + BaseSalary = x.BaseSalary, + TotalCommission = x.TotalCommission, + WorkingDays = x.WorkingDays, + LeaveDays = x.LeaveDays, + CalculatedGrossSalary = x.CalculatedGrossSalary, + FinalGrossSalary = x.FinalGrossSalary, + MonthlyTrainingSubsidy = x.MonthlyTrainingSubsidy, + MonthlyTransportSubsidy = x.MonthlyTransportSubsidy, + LastMonthTrainingSubsidy = x.LastMonthTrainingSubsidy, + LastMonthTransportSubsidy = x.LastMonthTransportSubsidy, + TotalSubsidy = x.TotalSubsidy, + MissingCard = x.MissingCard, + LateArrival = x.LateArrival, + LeaveDeduction = x.LeaveDeduction, + SocialInsuranceDeduction = x.SocialInsuranceDeduction, + RewardDeduction = x.RewardDeduction, + AccommodationDeduction = x.AccommodationDeduction, + StudyPeriodDeduction = x.StudyPeriodDeduction, + WorkClothesDeduction = x.WorkClothesDeduction, + TotalDeduction = x.TotalDeduction, + Bonus = x.Bonus, + ReturnPhoneDeposit = x.ReturnPhoneDeposit, + ReturnAccommodationDeposit = x.ReturnAccommodationDeposit, + ActualSalary = x.ActualSalary, + MonthlyPaymentStatus = x.MonthlyPaymentStatus, + PaidAmount = x.PaidAmount, + PendingAmount = x.PendingAmount, + LastMonthSupplement = x.LastMonthSupplement, + MonthlyTotalPayment = x.MonthlyTotalPayment, + IsLocked = x.IsLocked, + UpdateTime = x.UpdateTime + }) + .ToPagedListAsync(input.currentPage, input.pageSize); + + return PageResult.SqlSugarPageResult(list); + } + + /// + /// 计算事业部总经理/经理工资 + /// + /// 年份 + /// 月份 + /// + [HttpPost("calculate/business-unit-manager")] + public async Task CalculateBusinessUnitManagerSalary(int year, int month) + { + var startDate = new DateTime(year, month, 1); + var endDate = startDate.AddMonths(1).AddDays(-1); + var monthStr = $"{year}{month:D2}"; + + // 1. 获取基础数据 + + // 1.1 获取总经理/经理归属信息(从lq_md_general_manager_lifeline表) + var lifelineList = await _db.Queryable() + .Where(x => x.Month == monthStr) + .ToListAsync(); + + if (!lifelineList.Any()) + { + // 如果没有归属信息,直接返回 + return; + } + + // 1.2 获取所有不重复的总经理/经理ID(确保所有总经理/经理都被计算) + var allManagerIds = lifelineList + .Where(x => !string.IsNullOrEmpty(x.GeneralManagerId)) + .Select(x => x.GeneralManagerId) + .Distinct() + .ToList(); + + // 1.3 按总经理/经理ID分组,获取每个总经理/经理管理的门店 + var managerStoreDict = lifelineList + .Where(x => !string.IsNullOrEmpty(x.GeneralManagerId) && !string.IsNullOrEmpty(x.StoreId)) + .GroupBy(x => x.GeneralManagerId) + .ToDictionary(g => g.Key, g => g.Select(x => x.StoreId).Distinct().ToList()); + + // 1.4 门店信息 (lq_mdxx) + var storeList = await _db.Queryable().ToListAsync(); + var storeDict = storeList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x); + + // 1.5 门店生命线信息 (lq_md_target) + var targetList = await _db.Queryable() + .Where(x => x.Month == monthStr) + .ToListAsync(); + var storeLifelineDict = targetList + .Where(x => !string.IsNullOrEmpty(x.StoreId)) + .ToDictionary(x => x.StoreId, x => x.StoreLifeline); + + // 1.6 门店总业绩计算 (开单实付 - 退卡金额) + // 开单实付(从lq_kd_kdjlb表统计sfyj字段) + var storeBillingList = await _db.Queryable() + .Where(x => x.Kdrq >= startDate && x.Kdrq <= endDate.AddDays(1) && x.IsEffective == 1) + .Select(x => new { x.Djmd, x.Sfyj }) + .ToListAsync(); + var storeBillingDict = storeBillingList + .Where(x => !string.IsNullOrEmpty(x.Djmd)) + .GroupBy(x => x.Djmd) + .ToDictionary(g => g.Key, g => g.Sum(x => x.Sfyj)); + + // 退卡金额(从lq_hytk_hytk表统计,使用F_ActualRefundAmount,如果没有则使用tkje) + var storeRefundList = await _db.Queryable() + .Where(x => x.Tksj >= startDate && x.Tksj <= endDate.AddDays(1) && x.IsEffective == 1) + .Select(x => new { x.Md, x.ActualRefundAmount, x.Tkje }) + .ToListAsync(); + var storeRefundDict = storeRefundList + .Where(x => !string.IsNullOrEmpty(x.Md)) + .GroupBy(x => x.Md) + .ToDictionary(g => g.Key, g => g.Sum(x => x.ActualRefundAmount ?? x.Tkje ?? 0)); + + // 1.7 考勤数据 (lq_attendance_summary) + var attendanceList = await _db.Queryable() + .Where(x => x.Year == year && x.Month == month && x.IsEffective == 1) + .ToListAsync(); + var attendanceDict = attendanceList.ToDictionary(x => x.UserId, x => x); + + // 1.8 获取员工信息 (BASE_USER) + var userList = await _db.Queryable() + .Where(x => allManagerIds.Contains(x.Id)) + .Select(x => new { x.Id, x.RealName, x.Account, x.IsOnJob }) + .ToListAsync(); + var userDict = userList.ToDictionary(x => x.Id, x => x); + + // 2. 按总经理/经理聚合数据 + var managerStats = new Dictionary(); + + foreach (var managerId in allManagerIds) + { + if (string.IsNullOrEmpty(managerId)) + { + continue; + } + + // 获取该总经理/经理的信息 + var managerLifeline = lifelineList.FirstOrDefault(x => x.GeneralManagerId == managerId); + if (managerLifeline == null) + { + continue; + } + + // 2.1 创建工资统计对象 + var salary = new LqBusinessUnitManagerSalaryStatisticsEntity + { + Id = YitIdHelper.NextId().ToString(), + StatisticsMonth = monthStr, + EmployeeId = managerId, + ManagerType = managerLifeline.ManagerType, + Position = managerLifeline.ManagerType == 1 ? "总经理" : "经理", + IsTerminated = 0, + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + IsLocked = 0 + }; + + // 2.2 填充员工信息 + if (userDict.ContainsKey(managerId)) + { + var user = userDict[managerId]; + salary.EmployeeName = user.RealName ?? ""; + salary.EmployeeAccount = user.Account ?? ""; + salary.IsTerminated = user.IsOnJob == 0 ? 1 : 0; + } + + // 2.3 考勤数据 + var attendance = attendanceDict.ContainsKey(managerId) ? attendanceDict[managerId] : null; + salary.WorkingDays = attendance?.WorkDays ?? 0; + salary.LeaveDays = attendance?.LeaveDays ?? 0; + + // 2.4 计算底薪(固定4000元) + salary.BaseSalary = 4000m; + + // 2.5 遍历该总经理/经理管理的每个门店,计算提成 + var storePerformanceDetails = new List(); + decimal totalCommission = 0m; + + // 获取该总经理/经理管理的门店列表(如果没有管理的门店,则为空列表) + var managedStores = managerStoreDict.ContainsKey(managerId) ? managerStoreDict[managerId] : new List(); + foreach (var storeId in managedStores) + { + if (string.IsNullOrEmpty(storeId)) + { + continue; + } + + // 获取该门店的提成阶梯设置 + var storeLifelineSetting = lifelineList.FirstOrDefault(x => x.StoreId == storeId && x.GeneralManagerId == managerId); + if (storeLifelineSetting == null) + { + continue; + } + + // 获取门店信息 + var storeName = storeDict.ContainsKey(storeId) ? storeDict[storeId].Dm ?? "" : ""; + + // 获取门店生命线(提成门槛) + if (!storeLifelineDict.ContainsKey(storeId)) + { + // 门店生命线未设置,跳过该门店 + storePerformanceDetails.Add(new StorePerformanceDetail + { + StoreId = storeId, + StoreName = storeName, + StoreLifeline = 0, + BillingPerformance = 0, + RefundPerformance = 0, + StorePerformance = 0, + ReachedLifeline = false, + CommissionAmount = 0, + CalculationDetail = "门店生命线未设置,无法计算提成" + }); + continue; + } + + var storeLifeline = storeLifelineDict[storeId]; + if (storeLifeline <= 0) + { + // 门店生命线未设置或为0,跳过该门店 + storePerformanceDetails.Add(new StorePerformanceDetail + { + StoreId = storeId, + StoreName = storeName, + StoreLifeline = 0, + BillingPerformance = 0, + RefundPerformance = 0, + StorePerformance = 0, + ReachedLifeline = false, + CommissionAmount = 0, + CalculationDetail = "门店生命线未设置或为0,无法计算提成" + }); + continue; + } + + // 获取门店业绩 + var billing = storeBillingDict.ContainsKey(storeId) ? storeBillingDict[storeId] : 0; + var refund = storeRefundDict.ContainsKey(storeId) ? storeRefundDict[storeId] : 0; + var storePerformance = billing - refund; + + // 判断是否达到门店生命线 + var reachedLifeline = storePerformance >= storeLifeline; + + // 计算提成 + decimal commissionAmount = 0m; + string calculationDetail = ""; + + if (reachedLifeline) + { + // 达到门店生命线,使用提成阶梯计算提成(分段累进) + var commissionResult = CalculateStoreCommission(storePerformance, storeLifelineSetting); + commissionAmount = commissionResult.Amount; + calculationDetail = commissionResult.Detail; + + totalCommission += commissionAmount; + } + else + { + calculationDetail = $"业绩{storePerformance:N2}元,未达到门店生命线{storeLifeline:N2}元,无提成"; + } + + // 添加到门店业绩明细 + storePerformanceDetails.Add(new StorePerformanceDetail + { + StoreId = storeId, + StoreName = storeName, + StoreLifeline = storeLifeline, + BillingPerformance = billing, + RefundPerformance = refund, + StorePerformance = storePerformance, + ReachedLifeline = reachedLifeline, + Lifeline1 = storeLifelineSetting.Lifeline1, + CommissionRate1 = storeLifelineSetting.CommissionRate1, + Lifeline2 = storeLifelineSetting.Lifeline2, + CommissionRate2 = storeLifelineSetting.CommissionRate2, + Lifeline3 = storeLifelineSetting.Lifeline3, + CommissionRate3 = storeLifelineSetting.CommissionRate3, + CommissionAmount = commissionAmount, + CalculationDetail = calculationDetail + }); + } + + // 2.6 保存门店业绩明细(JSON格式) + salary.StorePerformanceDetail = storePerformanceDetails.ToJson(); + + // 2.7 提成合计 + salary.TotalCommission = totalCommission; + + // 2.8 计算应发工资 + salary.CalculatedGrossSalary = salary.BaseSalary + salary.TotalCommission; + salary.FinalGrossSalary = salary.CalculatedGrossSalary; + + // 2.9 初始化其他字段(默认值为0) + salary.MonthlyTrainingSubsidy = 0; + salary.MonthlyTransportSubsidy = 0; + salary.LastMonthTrainingSubsidy = 0; + salary.LastMonthTransportSubsidy = 0; + salary.TotalSubsidy = 0; + salary.MissingCard = 0; + salary.LateArrival = 0; + salary.LeaveDeduction = 0; + salary.SocialInsuranceDeduction = 0; + salary.RewardDeduction = 0; + salary.AccommodationDeduction = 0; + salary.StudyPeriodDeduction = 0; + salary.WorkClothesDeduction = 0; + salary.TotalDeduction = 0; + salary.Bonus = 0; + salary.ReturnPhoneDeposit = 0; + salary.ReturnAccommodationDeposit = 0; + salary.ActualSalary = salary.FinalGrossSalary - salary.TotalDeduction + salary.TotalSubsidy + salary.Bonus; + salary.MonthlyPaymentStatus = "未发放"; + salary.PaidAmount = 0; + salary.PendingAmount = salary.ActualSalary; + salary.LastMonthSupplement = 0; + salary.MonthlyTotalPayment = 0; + + managerStats[managerId] = salary; + } + + // 3. 保存数据 + if (managerStats.Any()) + { + // 先删除当月旧数据 (防止重复) + await _db.Deleteable() + .Where(x => x.StatisticsMonth == monthStr) + .ExecuteCommandAsync(); + + await _db.Insertable(managerStats.Values.ToList()).ExecuteCommandAsync(); + } + } + + /// + /// 计算门店提成(分段累进) + /// + /// 门店业绩 + /// 提成阶梯设置 + /// 提成金额和计算说明 + private (decimal Amount, string Detail) CalculateStoreCommission(decimal storePerformance, LqMdGeneralManagerLifelineEntity lifelineSetting) + { + // 验证提成阶梯1和提成比例1必须设置 + if (lifelineSetting.Lifeline1 <= 0 || lifelineSetting.CommissionRate1 <= 0) + { + return (0m, "提成阶梯1或提成比例1未设置,无法计算提成"); + } + + decimal commissionAmount = 0m; + string detail = ""; + + var lifeline1 = lifelineSetting.Lifeline1; + var rate1 = lifelineSetting.CommissionRate1; + var lifeline2 = lifelineSetting.Lifeline2 ?? 0; + var rate2 = lifelineSetting.CommissionRate2 ?? 0; + var lifeline3 = lifelineSetting.Lifeline3 ?? 0; + var rate3 = lifelineSetting.CommissionRate3 ?? 0; + + // 分段累进计算 + if (storePerformance <= lifeline1) + { + // 业绩 ≤ 提成阶梯1 + commissionAmount = storePerformance * (rate1 / 100m); + detail = $"业绩{storePerformance:N2}元,≤ 提成阶梯1({lifeline1:N2}元),提成 = {storePerformance:N2} × {rate1}% = {commissionAmount:N2}元"; + } + else if (lifeline2 > 0 && storePerformance <= lifeline2) + { + // 提成阶梯1 < 业绩 ≤ 提成阶梯2 + var part1 = lifeline1 * (rate1 / 100m); + var part2 = (storePerformance - lifeline1) * (rate2 / 100m); + commissionAmount = part1 + part2; + detail = $"业绩{storePerformance:N2}元,> 提成阶梯1({lifeline1:N2}元) 且 ≤ 提成阶梯2({lifeline2:N2}元),提成 = {lifeline1:N2} × {rate1}% + ({storePerformance:N2} - {lifeline1:N2}) × {rate2}% = {part1:N2} + {part2:N2} = {commissionAmount:N2}元"; + } + else if (lifeline3 > 0 && storePerformance <= lifeline3) + { + // 提成阶梯2 < 业绩 ≤ 提成阶梯3 + var part1 = lifeline1 * (rate1 / 100m); + var part2 = (lifeline2 - lifeline1) * (rate2 / 100m); + var part3 = (storePerformance - lifeline2) * (rate3 / 100m); + commissionAmount = part1 + part2 + part3; + detail = $"业绩{storePerformance:N2}元,> 提成阶梯2({lifeline2:N2}元) 且 ≤ 提成阶梯3({lifeline3:N2}元),提成 = {lifeline1:N2} × {rate1}% + ({lifeline2:N2} - {lifeline1:N2}) × {rate2}% + ({storePerformance:N2} - {lifeline2:N2}) × {rate3}% = {part1:N2} + {part2:N2} + {part3:N2} = {commissionAmount:N2}元"; + } + else if (lifeline3 > 0) + { + // 业绩 > 提成阶梯3 + var part1 = lifeline1 * (rate1 / 100m); + var part2 = (lifeline2 - lifeline1) * (rate2 / 100m); + var part3 = (lifeline3 - lifeline2) * (rate3 / 100m); + var part4 = (storePerformance - lifeline3) * (rate3 / 100m); + commissionAmount = part1 + part2 + part3 + part4; + detail = $"业绩{storePerformance:N2}元,> 提成阶梯3({lifeline3:N2}元),提成 = {lifeline1:N2} × {rate1}% + ({lifeline2:N2} - {lifeline1:N2}) × {rate2}% + ({lifeline3:N2} - {lifeline2:N2}) × {rate3}% + ({storePerformance:N2} - {lifeline3:N2}) × {rate3}% = {part1:N2} + {part2:N2} + {part3:N2} + {part4:N2} = {commissionAmount:N2}元"; + } + else if (lifeline2 > 0) + { + // 提成阶梯3未设置,业绩 > 提成阶梯2,按提成比例2计算超出部分 + var part1 = lifeline1 * (rate1 / 100m); + var part2 = (storePerformance - lifeline1) * (rate2 / 100m); + commissionAmount = part1 + part2; + detail = $"业绩{storePerformance:N2}元,> 提成阶梯2({lifeline2:N2}元),提成阶梯3未设置,提成 = {lifeline1:N2} × {rate1}% + ({storePerformance:N2} - {lifeline1:N2}) × {rate2}% = {part1:N2} + {part2:N2} = {commissionAmount:N2}元"; + } + else + { + // 只有提成阶梯1,业绩 > 提成阶梯1,按提成比例1计算 + commissionAmount = storePerformance * (rate1 / 100m); + detail = $"业绩{storePerformance:N2}元,> 提成阶梯1({lifeline1:N2}元),提成阶梯2未设置,提成 = {storePerformance:N2} × {rate1}% = {commissionAmount:N2}元"; + } + + return (commissionAmount, detail); + } + + /// + /// 门店业绩明细(用于JSON序列化) + /// + private class StorePerformanceDetail + { + public string StoreId { get; set; } + public string StoreName { get; set; } + public decimal StoreLifeline { get; set; } + public decimal BillingPerformance { get; set; } + public decimal RefundPerformance { get; set; } + public decimal StorePerformance { get; set; } + public bool ReachedLifeline { get; set; } + public decimal Lifeline1 { get; set; } + public decimal CommissionRate1 { get; set; } + public decimal? Lifeline2 { get; set; } + public decimal? CommissionRate2 { get; set; } + public decimal? Lifeline3 { get; set; } + public decimal? CommissionRate3 { get; set; } + public decimal CommissionAmount { get; set; } + public string CalculationDetail { get; set; } + } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs index af4eb99..577b317 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs @@ -355,6 +355,7 @@ namespace NCC.Extend.LqHytkHytk IsEffective = StatusEnum.有效.GetHashCode(), ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), PerformanceType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", + BeautyType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), }; allMxEntities.Add(lqHytkMxEntity); @@ -384,7 +385,8 @@ namespace NCC.Extend.LqHytkHytk ItemId = lqHytkMxEntity.Px, StoreId = newEntity.Md, ItemName = lqHytkMxEntity.Pxmc, - PerformanceType = lqHytkMxEntity.PerformanceType + PerformanceType = lqHytkMxEntity.PerformanceType, + BeautyType = lqHytkMxEntity.BeautyType, } ); } @@ -416,7 +418,8 @@ namespace NCC.Extend.LqHytkHytk ItemId = lqHytkMxEntity.Px, StoreId = newEntity.Md, ItemName = lqHytkMxEntity.Pxmc, - PerformanceType = lqHytkMxEntity.PerformanceType + PerformanceType = lqHytkMxEntity.PerformanceType, + BeautyType = lqHytkMxEntity.BeautyType, } ); } @@ -517,6 +520,7 @@ namespace NCC.Extend.LqHytkHytk TotalPrice = item.F_TotalPrice ?? (item.pxjg * (item.F_ProjectNumber ?? 1)), ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), PerformanceType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", + BeautyType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), }; allMxEntities.Add(lqHytkMxEntity); @@ -545,7 +549,8 @@ namespace NCC.Extend.LqHytkHytk ItemId = lqHytkMxEntity.Px, StoreId = entity.Md, ItemName = lqHytkMxEntity.Pxmc, - PerformanceType = lqHytkMxEntity.PerformanceType + PerformanceType = lqHytkMxEntity.PerformanceType, + BeautyType = lqHytkMxEntity.BeautyType, } ); } @@ -575,7 +580,8 @@ namespace NCC.Extend.LqHytkHytk ItemId = lqHytkMxEntity.Px, StoreId = entity.Md, ItemName = lqHytkMxEntity.Pxmc, - PerformanceType = lqHytkMxEntity.PerformanceType + PerformanceType = lqHytkMxEntity.PerformanceType, + BeautyType = lqHytkMxEntity.BeautyType, } ); } diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs index f5b6d7c..8c53d98 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs @@ -538,7 +538,7 @@ namespace NCC.Extend { var sidx = input.sidx == null ? "id" : input.sidx; - // 查询使用记录信息,关联产品表 + // 查询使用记录信息,关联产品表(使用Queryable语法,与GetBatchInfoAsync保持一致) var data = await _db.Queryable((u, product) => u.ProductId == product.Id) .WhereIF(!string.IsNullOrWhiteSpace(input.ProductId), (u, product) => u.ProductId == input.ProductId) .WhereIF(!string.IsNullOrWhiteSpace(input.StoreId), (u, product) => u.StoreId == input.StoreId) @@ -562,6 +562,8 @@ namespace NCC.Extend totalAmount = u.TotalAmount, // 总价(单价 × 数量) relatedConsumeId = u.RelatedConsumeId, usageBatchId = u.UsageBatchId, + approvalStatus = null, // 审批状态(稍后在内存中补充) + isReceived = null, // 是否已领取(稍后在内存中补充) createUser = u.CreateUser, createUserName = "", createTime = u.CreateTime, @@ -574,6 +576,35 @@ namespace NCC.Extend .OrderBy(sidx + " " + input.sort) .ToPagedListAsync(input.currentPage, input.pageSize); + // 补充审批状态和是否已领取信息(通过usageBatchId关联申请表) + if (data.list.Any()) + { + var batchIds = data.list.Where(x => !string.IsNullOrEmpty(x.usageBatchId)) + .Select(x => x.usageBatchId) + .Distinct() + .ToList(); + + if (batchIds.Any()) + { + var applicationDict = await _db.Queryable() + .Where(x => batchIds.Contains(x.UsageBatchId) && x.IsEffective == StatusEnum.有效.GetHashCode()) + .Select(x => new { x.UsageBatchId, x.ApprovalStatus, x.IsReceived }) + .ToListAsync(); + + var applicationLookup = applicationDict.ToDictionary(x => x.UsageBatchId, x => new { x.ApprovalStatus, x.IsReceived }); + + foreach (var item in data.list) + { + if (!string.IsNullOrEmpty(item.usageBatchId) && applicationLookup.ContainsKey(item.usageBatchId)) + { + var appInfo = applicationLookup[item.usageBatchId]; + item.approvalStatus = appInfo.ApprovalStatus; + item.isReceived = appInfo.IsReceived; + } + } + } + } + // 补充用户信息 var userIds = data.list.SelectMany(x => new[] { x.createUser, x.updateUser }) .Where(x => !string.IsNullOrEmpty(x)) diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs index 17f8bfa..b4733cf 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs @@ -934,6 +934,7 @@ namespace NCC.Extend.LqKdKdjlb ActivityId = input.activityId, ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), PerformanceType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", + BeautyType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), }; allPxmxEntities.Add(lqKdPxmxEntity); // 收集该品项关联的健康师业绩 @@ -960,7 +961,8 @@ namespace NCC.Extend.LqKdKdjlb ItemId = lqKdPxmxEntity.Px, ItemName = lqKdPxmxEntity.Pxmc, StoreId = entity.Djmd, - PerformanceType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", + PerformanceType = lqKdPxmxEntity.PerformanceType, + BeautyType = lqKdPxmxEntity.BeautyType, }); } } @@ -986,7 +988,8 @@ namespace NCC.Extend.LqKdKdjlb ItemId = lqKdPxmxEntity.Px, ItemName = lqKdPxmxEntity.Pxmc, StoreId = entity.Djmd, - PerformanceType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", + PerformanceType = lqKdPxmxEntity.PerformanceType, + BeautyType = lqKdPxmxEntity.BeautyType, } ); } @@ -1249,7 +1252,7 @@ namespace NCC.Extend.LqKdKdjlb ItemId = item.ItemId, IsEffective = StatusEnum.有效.GetHashCode(), // 设置为有效 CreateTime = DateTime.Now, // 设置创建时间 - ItemCategory = await _db.Queryable().Where(x => x.Id == item.DeductId).Select(x => x.Qt2).FirstAsync(), + ItemCategory = await _db.Queryable().Where(x => x.Id == item.DeductId).Select(x => x.Qt2).FirstAsync() }; allDeductEntities.Add(lqKdDeductEntity); } @@ -1277,6 +1280,7 @@ namespace NCC.Extend.LqKdKdjlb ActivityId = input.activityId, ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), PerformanceType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", + BeautyType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), }; allPxmxEntities.Add(lqKdPxmxEntity); @@ -1304,6 +1308,7 @@ namespace NCC.Extend.LqKdKdjlb StoreId = entity.Djmd, ItemName = lqKdPxmxEntity.Pxmc, PerformanceType = lqKdPxmxEntity.PerformanceType, + BeautyType = lqKdPxmxEntity.BeautyType, }); } } @@ -1330,6 +1335,7 @@ namespace NCC.Extend.LqKdKdjlb StoreId = entity.Djmd, ItemName = lqKdPxmxEntity.Pxmc, PerformanceType = lqKdPxmxEntity.PerformanceType, + BeautyType = lqKdPxmxEntity.BeautyType, }); } } @@ -2878,6 +2884,7 @@ namespace NCC.Extend.LqKdKdjlb IsEffective = StatusEnum.有效.GetHashCode(), ItemCategory = await _db.Queryable().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Qt2).FirstAsync(), PerformanceType = await _db.Queryable().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Fl3).FirstAsync() ?? "", + BeautyType = await _db.Queryable().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.BeautyType).FirstAsync(), }; refundMxEntities.Add(refundMxEntity); var refundKdyjEntities = _db.Queryable().Where(p => p.Kdpxid == item.BillingItemId).ToList(); @@ -2913,7 +2920,8 @@ namespace NCC.Extend.LqKdKdjlb ItemId = refundMxEntity.Px, StoreId = refundEntity.Md, ItemName = refundMxEntity.Pxmc, - PerformanceType = refundMxEntity.PerformanceType + PerformanceType = refundMxEntity.PerformanceType, + BeautyType = refundMxEntity.BeautyType, }); } //查询科技部老师的业绩 @@ -2941,7 +2949,8 @@ namespace NCC.Extend.LqKdKdjlb ItemId = refundMxEntity.Px, StoreId = refundEntity.Md, ItemName = refundMxEntity.Pxmc, - PerformanceType = refundMxEntity.PerformanceType + PerformanceType = refundMxEntity.PerformanceType, + BeautyType = refundMxEntity.BeautyType, }); } } @@ -3025,6 +3034,7 @@ namespace NCC.Extend.LqKdKdjlb Remark = $"从会员 {fromMember.Khmc} 转入", ItemCategory = await _db.Queryable().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Qt2).FirstAsync(), PerformanceType = await _db.Queryable().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Fl3).FirstAsync() ?? "", + BeautyType = await _db.Queryable().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.BeautyType).FirstAsync(), }; billingPxmxEntities.Add(billingPxmxEntity); @@ -3056,7 +3066,8 @@ namespace NCC.Extend.LqKdKdjlb ItemId = billingPxmxEntity.Px, StoreId = billingEntity.Djmd, ItemName = billingPxmxEntity.Pxmc, - PerformanceType = billingPxmxEntity.PerformanceType + PerformanceType = billingPxmxEntity.PerformanceType, + BeautyType = billingPxmxEntity.BeautyType, }); } } @@ -3087,7 +3098,8 @@ namespace NCC.Extend.LqKdKdjlb ItemId = billingPxmxEntity.Px, StoreId = billingEntity.Djmd, ItemName = billingPxmxEntity.Pxmc, - PerformanceType = billingPxmxEntity.PerformanceType + PerformanceType = billingPxmxEntity.PerformanceType, + BeautyType = billingPxmxEntity.BeautyType, }); } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqMajorProjectTeacherSalaryService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqMajorProjectTeacherSalaryService.cs new file mode 100644 index 0000000..c2de6aa --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqMajorProjectTeacherSalaryService.cs @@ -0,0 +1,435 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using NCC.Common.Filter; +using NCC.Common.Helper; +using NCC.Dependency; +using NCC.DynamicApiController; +using NCC.Extend.Entitys.Dto.LqMajorProjectTeacherSalary; +using NCC.Extend.Entitys.lq_attendance_summary; +using NCC.Extend.Entitys.lq_hytk_hytk; +using NCC.Extend.Entitys.lq_kd_kdjlb; +using NCC.Extend.Entitys.lq_md_major_project_teacher_assignment; +using NCC.Extend.Entitys.lq_md_xdbhsj; +using NCC.Extend.Entitys.lq_mdxx; +using NCC.Extend.Entitys.lq_major_project_teacher_salary_statistics; +using NCC.System.Entitys.Permission; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Yitter.IdGenerator; + +namespace NCC.Extend +{ + /// + /// 大项目部老师薪酬服务 + /// + [ApiDescriptionSettings(Tag = "大项目部老师薪酬服务", Name = "LqMajorProjectTeacherSalary", Order = 303)] + [Route("api/Extend/[controller]")] + public class LqMajorProjectTeacherSalaryService : IDynamicApiController, ITransient + { + private readonly ISqlSugarClient _db; + + /// + /// 初始化一个类型的新实例 + /// + public LqMajorProjectTeacherSalaryService(ISqlSugarClient db) + { + _db = db; + } + + /// + /// 获取大项目部老师工资列表 + /// + /// 查询参数 + /// 大项目部老师工资分页列表 + [HttpGet("major-project-teacher")] + public async Task GetMajorProjectTeacherSalaryList([FromQuery] MajorProjectTeacherSalaryInput input) + { + var monthStr = $"{input.Year}{input.Month:D2}"; + + // 1. 检查当月是否已生成工资数据 + var exists = await _db.Queryable() + .AnyAsync(x => x.StatisticsMonth == monthStr); + + // 2. 如果没有数据,则进行计算 + if (!exists) + { + await CalculateMajorProjectTeacherSalary(input.Year, input.Month); + } + + // 3. 查询数据 + var query = _db.Queryable() + .Where(x => x.StatisticsMonth == monthStr); + + if (!string.IsNullOrEmpty(input.StoreId)) + { + query = query.Where(x => x.StoreId == input.StoreId); + } + + if (!string.IsNullOrEmpty(input.Keyword)) + { + query = query.Where(x => x.EmployeeName.Contains(input.Keyword) || x.EmployeeAccount.Contains(input.Keyword)); + } + + var list = await query.Select(x => new MajorProjectTeacherSalaryOutput + { + Id = x.Id, + StatisticsMonth = x.StatisticsMonth, + StoreId = x.StoreId, + StoreName = x.StoreName, + Position = x.Position, + EmployeeName = x.EmployeeName, + EmployeeId = x.EmployeeId, + EmployeeAccount = x.EmployeeAccount, + OrderAchievement = x.OrderAchievement, + ConsumeAchievement = x.ConsumeAchievement, + RefundAchievement = x.RefundAchievement, + TotalPerformance = x.TotalPerformance, + BaseSalary = x.BaseSalary, + PerformanceCommissionRate = x.PerformanceCommissionRate, + PerformanceCommissionAmount = x.PerformanceCommissionAmount, + TotalCommission = x.TotalCommission, + HandworkFee = x.HandworkFee, + WorkingDays = x.WorkingDays, + LeaveDays = x.LeaveDays, + TransportationAllowance = x.TransportationAllowance, + LessRest = x.LessRest, + FullAttendance = x.FullAttendance, + CalculatedGrossSalary = x.CalculatedGrossSalary, + GuaranteedSalary = x.GuaranteedSalary, + GuaranteedLeaveDeduction = x.GuaranteedLeaveDeduction, + GuaranteedBaseSalary = x.GuaranteedBaseSalary, + GuaranteedSupplement = x.GuaranteedSupplement, + FinalGrossSalary = x.FinalGrossSalary, + MonthlyTrainingSubsidy = x.MonthlyTrainingSubsidy, + MonthlyTransportSubsidy = x.MonthlyTransportSubsidy, + LastMonthTrainingSubsidy = x.LastMonthTrainingSubsidy, + LastMonthTransportSubsidy = x.LastMonthTransportSubsidy, + TotalSubsidy = x.TotalSubsidy, + MissingCard = x.MissingCard, + LateArrival = x.LateArrival, + LeaveDeduction = x.LeaveDeduction, + SocialInsuranceDeduction = x.SocialInsuranceDeduction, + RewardDeduction = x.RewardDeduction, + AccommodationDeduction = x.AccommodationDeduction, + StudyPeriodDeduction = x.StudyPeriodDeduction, + WorkClothesDeduction = x.WorkClothesDeduction, + TotalDeduction = x.TotalDeduction, + Bonus = x.Bonus, + ReturnPhoneDeposit = x.ReturnPhoneDeposit, + ReturnAccommodationDeposit = x.ReturnAccommodationDeposit, + ActualSalary = x.ActualSalary, + MonthlyPaymentStatus = x.MonthlyPaymentStatus, + PaidAmount = x.PaidAmount, + PendingAmount = x.PendingAmount, + LastMonthSupplement = x.LastMonthSupplement, + MonthlyTotalPayment = x.MonthlyTotalPayment, + IsLocked = x.IsLocked, + IsTerminated = x.IsTerminated, + UpdateTime = x.UpdateTime, + StoreType = x.StoreType, + StoreCategory = x.StoreCategory, + IsNewStore = x.IsNewStore, + NewStoreProtectionStage = x.NewStoreProtectionStage + }) + .ToPagedListAsync(input.currentPage, input.pageSize); + + return PageResult.SqlSugarPageResult(list); + } + + /// + /// 计算大项目部老师工资 + /// + /// 年份 + /// 月份 + /// + [HttpPost("calculate/major-project-teacher")] + public async Task CalculateMajorProjectTeacherSalary(int year, int month) + { + var startDate = new DateTime(year, month, 1); + var endDate = startDate.AddMonths(1).AddDays(-1); + var monthStr = $"{year}{month:D2}"; + var yearStr = year.ToString(); + var monthStr2 = month.ToString("D2"); + + // 1. 获取基础数据 + + // 1.1 获取大项目部老师归属信息(从lq_md_major_project_teacher_assignment表) + var assignmentList = await _db.Queryable() + .Where(x => x.Year == yearStr && x.Month == monthStr2) + .ToListAsync(); + + if (!assignmentList.Any()) + { + // 如果没有归属信息,直接返回 + return; + } + + // 1.2 门店信息 (lq_mdxx) + var storeList = await _db.Queryable().ToListAsync(); + var storeDict = storeList.Where(x => !string.IsNullOrEmpty(x.Id)).ToDictionary(x => x.Id, x => x); + + // 1.3 门店新店保护信息 (lq_md_xdbhsj) + var newStoreProtectionList = await _db.Queryable() + .Where(x => x.Sfqy == 1) + .ToListAsync(); + + var newStoreProtectionDict = newStoreProtectionList + .Where(x => x.Bhkssj <= startDate && x.Bhjssj >= startDate) + .GroupBy(x => x.Mdid) + .ToDictionary(g => g.Key, g => g.First()); + + // 1.4 门店总业绩计算 (开单实付 - 退卡金额) + // 开单实付(从lq_kd_kdjlb表统计sfyj字段) + var storeBillingList = await _db.Queryable() + .Where(x => x.Kdrq >= startDate && x.Kdrq <= endDate.AddDays(1) && x.IsEffective == 1) + .Select(x => new { x.Djmd, x.Sfyj }) + .ToListAsync(); + var storeBillingDict = storeBillingList + .Where(x => !string.IsNullOrEmpty(x.Djmd)) + .GroupBy(x => x.Djmd) + .ToDictionary(g => g.Key, g => g.Sum(x => x.Sfyj)); + + // 退卡金额(从lq_hytk_hytk表统计,使用F_ActualRefundAmount,如果没有则使用tkje) + var storeRefundList = await _db.Queryable() + .Where(x => x.Tksj >= startDate && x.Tksj <= endDate.AddDays(1) && x.IsEffective == 1) + .Select(x => new { x.Md, x.ActualRefundAmount, x.Tkje }) + .ToListAsync(); + var storeRefundDict = storeRefundList + .Where(x => !string.IsNullOrEmpty(x.Md)) + .GroupBy(x => x.Md) + .ToDictionary(g => g.Key, g => g.Sum(x => x.ActualRefundAmount ?? x.Tkje ?? 0)); + + // 1.5 考勤数据 (lq_attendance_summary) + var attendanceList = await _db.Queryable() + .Where(x => x.Year == year && x.Month == month && x.IsEffective == 1) + .ToListAsync(); + var attendanceDict = attendanceList.ToDictionary(x => x.UserId, x => x); + + // 1.6 获取员工信息 (BASE_USER) + var teacherIds = assignmentList.Select(x => x.TeacherId).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); + var userList = await _db.Queryable() + .Where(x => teacherIds.Contains(x.Id)) + .Select(x => new { x.Id, x.RealName, x.Account, x.IsOnJob }) + .ToListAsync(); + var userDict = userList.ToDictionary(x => x.Id, x => x); + + // 2. 按大项目部老师聚合数据 + var teacherStats = new Dictionary(); + + foreach (var assignment in assignmentList) + { + if (string.IsNullOrEmpty(assignment.TeacherId) || string.IsNullOrEmpty(assignment.StoreId)) + { + continue; + } + + var teacherId = assignment.TeacherId; + var storeId = assignment.StoreId; + + // 如果该老师已经处理过(可能有多条归属记录),则合并数据 + if (teacherStats.ContainsKey(teacherId)) + { + // 如果同一个老师归属于多个门店,需要合并业绩 + var existingSalary = teacherStats[teacherId]; + + // 获取该门店的业绩 + var additionalBilling = storeBillingDict.ContainsKey(storeId) ? storeBillingDict[storeId] : 0; + var additionalRefund = storeRefundDict.ContainsKey(storeId) ? storeRefundDict[storeId] : 0; + var additionalStorePerformance = additionalBilling - additionalRefund; + + // 累加业绩 + existingSalary.OrderAchievement += additionalBilling; + existingSalary.RefundAchievement += additionalRefund; + existingSalary.TotalPerformance += additionalStorePerformance; + + continue; + } + + // 2.1 创建工资统计对象 + var salary = new LqMajorProjectTeacherSalaryStatisticsEntity + { + Id = YitIdHelper.NextId().ToString(), + StatisticsMonth = monthStr, + EmployeeId = teacherId, + Position = "大项目部老师", + IsTerminated = 0, + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + IsLocked = 0 + }; + + // 2.2 填充员工信息 + if (userDict.ContainsKey(teacherId)) + { + var user = userDict[teacherId]; + salary.EmployeeName = user.RealName ?? ""; + salary.EmployeeAccount = user.Account ?? ""; + salary.IsTerminated = user.IsOnJob == 0 ? 1 : 0; + } + + // 2.3 填充门店信息 + if (storeDict.ContainsKey(storeId)) + { + var store = storeDict[storeId]; + salary.StoreId = storeId; + salary.StoreName = store.Dm ?? ""; + salary.StoreType = store.StoreType; + salary.StoreCategory = store.StoreCategory; + } + else + { + salary.StoreId = storeId; + salary.StoreName = ""; + } + + // 2.4 新店保护信息 + if (!string.IsNullOrEmpty(salary.StoreId) && newStoreProtectionDict.ContainsKey(salary.StoreId)) + { + var protection = newStoreProtectionDict[salary.StoreId]; + salary.IsNewStore = "是"; + salary.NewStoreProtectionStage = protection.Stage; + } + else + { + salary.IsNewStore = "否"; + salary.NewStoreProtectionStage = 0; + } + + // 2.5 统计门店总业绩(开单业绩 - 退卡业绩) + var billing = storeBillingDict.ContainsKey(storeId) ? storeBillingDict[storeId] : 0; + var refund = storeRefundDict.ContainsKey(storeId) ? storeRefundDict[storeId] : 0; + var storeTotalPerformance = billing - refund; + + salary.OrderAchievement = billing; // 开单业绩(门店开单实付) + salary.RefundAchievement = refund; // 退卡业绩(门店退卡金额) + salary.ConsumeAchievement = 0; // 大项目部老师不统计消耗业绩 + salary.TotalPerformance = storeTotalPerformance; // 门店总业绩(开单 - 退卡) + + // 2.6 考勤数据 + var attendance = attendanceDict.ContainsKey(teacherId) ? attendanceDict[teacherId] : null; + salary.WorkingDays = attendance?.WorkDays ?? 0; + salary.LeaveDays = attendance?.LeaveDays ?? 0; + + // 3. 工资计算 + if (salary.IsTerminated == 1) + { + // 离职员工特殊处理(待确认规则) + salary.BaseSalary = 0; + salary.PerformanceCommissionRate = 0; + salary.PerformanceCommissionAmount = 0; + salary.TotalCommission = 0; + } + else + { + // 在职员工正常计算 + + // 3.1 计算底薪(固定3000元) + salary.BaseSalary = 3000m; + + // 3.2 计算业绩提成(阶梯式) + var performanceCommissionResult = CalculatePerformanceCommission(salary.TotalPerformance); + salary.PerformanceCommissionRate = performanceCommissionResult.Rate; + salary.PerformanceCommissionAmount = performanceCommissionResult.Amount; + + // 3.3 提成合计(等于业绩提成金额) + salary.TotalCommission = salary.PerformanceCommissionAmount; + } + + // 3.4 初始化其他字段(默认值为0) + salary.HandworkFee = 0; + salary.TransportationAllowance = 0; + salary.LessRest = 0; + salary.FullAttendance = 0; + salary.CalculatedGrossSalary = salary.BaseSalary + salary.TotalCommission + salary.HandworkFee; + salary.GuaranteedSalary = 0; + salary.GuaranteedLeaveDeduction = 0; + salary.GuaranteedBaseSalary = 0; + salary.GuaranteedSupplement = 0; + salary.FinalGrossSalary = salary.CalculatedGrossSalary; + salary.MonthlyTrainingSubsidy = 0; + salary.MonthlyTransportSubsidy = 0; + salary.LastMonthTrainingSubsidy = 0; + salary.LastMonthTransportSubsidy = 0; + salary.TotalSubsidy = 0; + salary.MissingCard = 0; + salary.LateArrival = 0; + salary.LeaveDeduction = 0; + salary.SocialInsuranceDeduction = 0; + salary.RewardDeduction = 0; + salary.AccommodationDeduction = 0; + salary.StudyPeriodDeduction = 0; + salary.WorkClothesDeduction = 0; + salary.TotalDeduction = 0; + salary.Bonus = 0; + salary.ReturnPhoneDeposit = 0; + salary.ReturnAccommodationDeposit = 0; + salary.ActualSalary = salary.FinalGrossSalary - salary.TotalDeduction + salary.TotalSubsidy + salary.Bonus; + salary.MonthlyPaymentStatus = ""; + salary.PaidAmount = 0; + salary.PendingAmount = salary.ActualSalary; + salary.LastMonthSupplement = 0; + salary.MonthlyTotalPayment = 0; + + teacherStats[teacherId] = salary; + } + + // 4. 重新计算合并后的业绩提成(如果同一个老师归属于多个门店) + foreach (var salary in teacherStats.Values) + { + if (salary.IsTerminated == 0) + { + // 重新计算业绩提成(基于合并后的总业绩) + var performanceCommissionResult = CalculatePerformanceCommission(salary.TotalPerformance); + salary.PerformanceCommissionRate = performanceCommissionResult.Rate; + salary.PerformanceCommissionAmount = performanceCommissionResult.Amount; + salary.TotalCommission = salary.PerformanceCommissionAmount; + + // 重新计算应发工资 + salary.CalculatedGrossSalary = salary.BaseSalary + salary.TotalCommission + salary.HandworkFee; + salary.FinalGrossSalary = salary.CalculatedGrossSalary; + salary.ActualSalary = salary.FinalGrossSalary - salary.TotalDeduction + salary.TotalSubsidy + salary.Bonus; + salary.PendingAmount = salary.ActualSalary; + } + } + + // 5. 保存数据 + if (teacherStats.Any()) + { + // 先删除当月旧数据 (防止重复) + await _db.Deleteable() + .Where(x => x.StatisticsMonth == monthStr) + .ExecuteCommandAsync(); + + await _db.Insertable(teacherStats.Values.ToList()).ExecuteCommandAsync(); + } + } + + /// + /// 计算业绩提成(阶梯式) + /// + /// 总业绩(门店总业绩) + /// 提成比例和金额 + private (decimal Rate, decimal Amount) CalculatePerformanceCommission(decimal totalPerformance) + { + if (totalPerformance <= 200000m) + { + // ≤ 20万 → 0%(无提成) + return (0m, 0m); + } + else if (totalPerformance <= 1000000m) + { + // > 20万 且 ≤ 100万 → 2% + return (2m, totalPerformance * 0.02m); + } + else + { + // > 100万 → 2.5% + return (2.5m, totalPerformance * 0.025m); + } + } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqMdMajorProjectTeacherAssignmentService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqMdMajorProjectTeacherAssignmentService.cs index 536b56b..ccceb37 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqMdMajorProjectTeacherAssignmentService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqMdMajorProjectTeacherAssignmentService.cs @@ -63,6 +63,8 @@ namespace NCC.Extend month = it.Month, teacherId = it.TeacherId, teacherName = SqlFunc.Subqueryable().Where(x => x.Id == it.TeacherId).Select(x => x.RealName), + educationTeacherId = it.EducationTeacherId, + educationTeacherName = SqlFunc.Subqueryable().Where(x => x.Id == it.EducationTeacherId).Select(x => x.RealName), remark = it.Remark, createTime = it.CreateTime, createUserId = it.CreateUserId, @@ -91,6 +93,7 @@ namespace NCC.Extend .WhereIF(!string.IsNullOrEmpty(input.year), p => p.Year == input.year) .WhereIF(!string.IsNullOrEmpty(input.month), p => p.Month == input.month) .WhereIF(!string.IsNullOrEmpty(input.teacherId), p => p.TeacherId == input.teacherId) + .WhereIF(!string.IsNullOrEmpty(input.educationTeacherId), p => p.EducationTeacherId == input.educationTeacherId) .Select(it => new LqMdMajorProjectTeacherAssignmentListOutput { id = it.Id, @@ -100,6 +103,8 @@ namespace NCC.Extend month = it.Month, teacherId = it.TeacherId, teacherName = SqlFunc.Subqueryable().Where(x => x.Id == it.TeacherId).Select(x => x.RealName), + educationTeacherId = it.EducationTeacherId, + educationTeacherName = SqlFunc.Subqueryable().Where(x => x.Id == it.EducationTeacherId).Select(x => x.RealName), remark = it.Remark, createTime = it.CreateTime, createUserId = it.CreateUserId, @@ -212,6 +217,7 @@ namespace NCC.Extend entity.Year = input.year; entity.Month = input.month; entity.TeacherId = input.teacherId; + entity.EducationTeacherId = input.educationTeacherId; entity.Remark = input.remark; entity.UpdateTime = DateTime.Now; entity.UpdateUserId = userInfo.userId; @@ -327,6 +333,7 @@ namespace NCC.Extend Year = targetYearStr, Month = targetMonthStr, TeacherId = x.TeacherId, + EducationTeacherId = x.EducationTeacherId, Remark = x.Remark, CreateTime = DateTime.Now, CreateUserId = _userManager.UserId, diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs index d153b7d..7e15db7 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryExtraCalculationService.cs @@ -152,20 +152,30 @@ namespace NCC.Extend try { var row = dataTable.Rows[i]; - var id = row[0]?.ToString()?.Trim(); - var employeeName = row[1]?.ToString()?.Trim(); - var employeePhone = row[2]?.ToString()?.Trim(); - var yearText = row[3]?.ToString()?.Trim(); - var monthText = row[4]?.ToString()?.Trim(); - var baseRewardPerformanceText = row[5]?.ToString()?.Trim(); - var cooperationRewardPerformanceText = row[6]?.ToString()?.Trim(); - var newCustomerPerformanceText = row[7]?.ToString()?.Trim(); - var newCustomerConversionRateText = row[8]?.ToString()?.Trim(); - var upgradePerformanceText = row[9]?.ToString()?.Trim(); - var upgradeConversionRateText = row[10]?.ToString()?.Trim(); - var upgradeCustomerCountText = row[11]?.ToString()?.Trim(); - var otherPerformanceAddText = row[12]?.ToString()?.Trim(); - var otherPerformanceSubtractText = row[13]?.ToString()?.Trim(); + // 安全获取列值,如果列不存在则返回空字符串 + var GetColumnValue = new Func((colIndex) => + { + if (colIndex < row.ItemArray.Length) + { + return row[colIndex]?.ToString()?.Trim() ?? ""; + } + return ""; + }); + + var id = GetColumnValue(0); + var employeeName = GetColumnValue(1); + var employeePhone = GetColumnValue(2); + var yearText = GetColumnValue(3); + var monthText = GetColumnValue(4); + var baseRewardPerformanceText = GetColumnValue(5); + var cooperationRewardPerformanceText = GetColumnValue(6); + var newCustomerPerformanceText = GetColumnValue(7); + var newCustomerConversionRateText = GetColumnValue(8); + var upgradePerformanceText = GetColumnValue(9); + var upgradeConversionRateText = GetColumnValue(10); + var upgradeCustomerCountText = GetColumnValue(11); + var otherPerformanceAddText = GetColumnValue(12); + var otherPerformanceSubtractText = GetColumnValue(13); // 跳过空行 if (string.IsNullOrEmpty(employeeName) && string.IsNullOrEmpty(employeePhone)) @@ -279,30 +289,67 @@ namespace NCC.Extend { try { - // 1. 根据健康师姓名和电话查找用户ID - var user = await _db.Queryable() - .Where(u => u.RealName == item.EmployeeName && u.MobilePhone == item.EmployeePhone) - .FirstAsync(); - - if (user == null) - { - errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 不存在"); - failCount++; - continue; - } - - // 2. 检查是否已存在相同记录(根据健康师ID、年份、月份) + // 1. 查找用户ID(优先使用ID,否则通过姓名和电话查找) + UserEntity user = null; LqSalaryExtraCalculationEntity existingRecord = null; - // 如果提供了ID,先尝试根据ID查找 + // 如果提供了ID,先尝试根据ID查找现有记录,获取EmployeeId if (!string.IsNullOrEmpty(item.Id)) { existingRecord = await _db.Queryable() .Where(x => x.Id == item.Id) .FirstAsync(); + + if (existingRecord != null) + { + // 通过EmployeeId查找用户 + user = await _db.Queryable() + .Where(u => u.Id == existingRecord.EmployeeId) + .FirstAsync(); + } + } + + // 如果还没有找到用户,通过姓名和电话查找 + if (user == null) + { + // 处理姓名:去除"A"前缀 + var cleanName = item.EmployeeName; + if (cleanName.StartsWith("A")) + { + cleanName = cleanName.Substring(1).Trim(); + } + + // 处理电话:如果电话是"无"或空,只按姓名查找 + var phoneIsEmpty = string.IsNullOrWhiteSpace(item.EmployeePhone) || + item.EmployeePhone.Trim() == "无" || + item.EmployeePhone.Trim() == "00000000" || + item.EmployeePhone.Trim().Length < 8; // 排除明显无效的电话 + + if (phoneIsEmpty) + { + // 只按姓名查找(支持原姓名和去除前缀后的姓名) + user = await _db.Queryable() + .Where(u => u.RealName == item.EmployeeName || u.RealName == cleanName) + .FirstAsync(); + } + else + { + // 同时匹配姓名和电话(支持原姓名和去除前缀后的姓名) + user = await _db.Queryable() + .Where(u => (u.RealName == item.EmployeeName || u.RealName == cleanName) && + u.MobilePhone == item.EmployeePhone) + .FirstAsync(); + } + } + + if (user == null) + { + errorMessages.Add($"健康师 {item.EmployeeName}({item.EmployeePhone}) 不存在"); + failCount++; + continue; } - // 如果没有找到,则根据健康师ID、年份、月份查找 + // 2. 如果还没有找到现有记录,则根据健康师ID、年份、月份查找 if (existingRecord == null) { existingRecord = await _db.Queryable() diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs index b2215fc..8554c6d 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs @@ -939,6 +939,7 @@ namespace NCC.Extend.LqXhHyhk IsEffective = StatusEnum.有效.GetHashCode(), ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), PerformanceType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", + BeautyType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), }; allPxmxEntities.Add(lqXhPxmxEntity); @@ -973,7 +974,8 @@ namespace NCC.Extend.LqXhHyhk ItemId = lqXhPxmxEntity.Px, StoreId = entity.Md, ItemName = lqXhPxmxEntity.Pxmc, - PerformanceType = lqXhPxmxEntity.PerformanceType + PerformanceType = lqXhPxmxEntity.PerformanceType, + BeautyType = lqXhPxmxEntity.BeautyType, } ); } @@ -1013,7 +1015,8 @@ namespace NCC.Extend.LqXhHyhk ItemId = lqXhPxmxEntity.Px, StoreId = entity.Md, ItemName = lqXhPxmxEntity.Pxmc, - PerformanceType = lqXhPxmxEntity.PerformanceType + PerformanceType = lqXhPxmxEntity.PerformanceType, + BeautyType = lqXhPxmxEntity.BeautyType, } ); } @@ -1317,6 +1320,7 @@ namespace NCC.Extend.LqXhHyhk ProjectNumber = (decimal)((item.projectNumber ?? 0) + (entity.OvertimeCoefficient * (item.projectNumber ?? 0))), ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(), PerformanceType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "", + BeautyType = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.BeautyType).FirstAsync(), }; allPxmxEntities.Add(lqXhPxmxEntity); @@ -1351,7 +1355,8 @@ namespace NCC.Extend.LqXhHyhk ItemId = lqXhPxmxEntity.Px, StoreId = entity.Md, ItemName = lqXhPxmxEntity.Pxmc, - PerformanceType = lqXhPxmxEntity.PerformanceType + PerformanceType = lqXhPxmxEntity.PerformanceType, + BeautyType = lqXhPxmxEntity.BeautyType, } ); } @@ -1390,7 +1395,8 @@ namespace NCC.Extend.LqXhHyhk ItemId = lqXhPxmxEntity.Px, StoreId = entity.Md, ItemName = lqXhPxmxEntity.Pxmc, - PerformanceType = lqXhPxmxEntity.PerformanceType + PerformanceType = lqXhPxmxEntity.PerformanceType, + BeautyType = lqXhPxmxEntity.BeautyType, }); } } diff --git a/netcore/src/Modularity/System/NCC.System/Service/Common/FileService.cs b/netcore/src/Modularity/System/NCC.System/Service/Common/FileService.cs index f340644..1bb7b78 100644 --- a/netcore/src/Modularity/System/NCC.System/Service/Common/FileService.cs +++ b/netcore/src/Modularity/System/NCC.System/Service/Common/FileService.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.IO; using System.Linq; using System.Text; @@ -64,24 +65,33 @@ namespace NCC.System.Service.Common if (!this.AllowFileType(fileType, type)) throw NCCException.Oh(ErrorCode.D1800); var _filePath = GetPathByType(type); - var _fileName = DateTime.Now.ToString("yyyyMMdd") + "_" + YitIdHelper.NextId().ToString() + Path.GetExtension(file.FileName); + var now = DateTime.Now; + var _fileName = now.ToString("yyyyMMdd") + "_" + YitIdHelper.NextId().ToString() + Path.GetExtension(file.FileName); - // annexpic 类型强制使用阿里云OSS存储 + // annexpic 类型强制使用阿里云OSS存储,并按天生成文件夹(不包含Files/SystemFile前缀) string forceStoreType = type == "annexpic" ? "aliyun-oss" : null; - await UploadFileByType(file, _filePath, _fileName, forceStoreType); + string uploadFilePath = _filePath; + if (type == "annexpic") + { + // 按天生成文件夹:yyyy/MM/dd(直接使用日期文件夹,不包含Files/SystemFile前缀) + var dateFolder = now.ToString("yyyy/MM/dd"); + uploadFilePath = dateFolder; + } + await UploadFileByType(file, uploadFilePath, _fileName, forceStoreType); // 如果是annexpic类型且使用阿里云OSS,返回OSS的完整访问地址 string fileUrl; if (type == "annexpic") { - fileUrl = await GetOSSAccessUrl(_filePath, _fileName); + fileUrl = await GetOSSAccessUrl(uploadFilePath, _fileName); } else { fileUrl = string.Format("/api/File/Image/{0}/{1}", type, _fileName); } - return new { name = _fileName, url = fileUrl }; + // 返回格式与数据库保存格式一致:name(原始文件名), fileId(生成的文件名), url(OSS地址) + return new { name = file.FileName, fileId = _fileName, url = fileUrl }; } /// @@ -94,7 +104,40 @@ namespace NCC.System.Service.Common [AllowAnonymous] public async Task GetImg(string type, string fileName) { - var filePath = Path.Combine(GetPathByType(type), fileName.Replace("@", ".")); + var basePath = GetPathByType(type); + var actualFileName = fileName.Replace("@", "."); + + // annexpic 类型需要从文件名中提取日期来构建路径(不包含Files/SystemFile前缀) + string filePath; + if (type == "annexpic") + { + // 文件名格式:yyyyMMdd_xxx.ext,提取日期部分 + if (actualFileName.Length >= 8) + { + var datePart = actualFileName.Substring(0, 8); // 前8位是日期 + if (DateTime.TryParseExact(datePart, "yyyyMMdd", null, DateTimeStyles.None, out DateTime fileDate)) + { + // 构建日期文件夹路径:yyyy/MM/dd(直接使用日期文件夹,不包含Files/SystemFile前缀) + var dateFolder = fileDate.ToString("yyyy/MM/dd"); + filePath = $"{dateFolder}/{actualFileName}"; + } + else + { + // 如果无法解析日期,使用原路径(兼容旧文件) + filePath = Path.Combine(basePath, actualFileName); + } + } + else + { + // 文件名长度不足,使用原路径(兼容旧文件) + filePath = Path.Combine(basePath, actualFileName); + } + } + else + { + filePath = Path.Combine(basePath, actualFileName); + } + // annexpic 类型强制使用阿里云OSS存储 string forceStoreType = type == "annexpic" ? "aliyun-oss" : null; return await DownloadFileByType(filePath, fileName, forceStoreType); @@ -193,12 +236,46 @@ namespace NCC.System.Service.Common var fileName = paramsList.Count > 1 ? paramsList[1] : ""; string type = paramsList.Count > 2 ? paramsList[2] : ""; string exname = paramsList.Count > 3 ? paramsList[3] : ""; - var filePath = Path.Combine(GetPathByType(type), fileName.Replace("@", ".")); + + var basePath = GetPathByType(type); + var actualFileName = fileName.Replace("@", "."); + + // annexpic 类型需要从文件名中提取日期来构建路径(不包含Files/SystemFile前缀) + string filePath; + if (type == "annexpic") + { + // 文件名格式:yyyyMMdd_xxx.ext,提取日期部分 + if (actualFileName.Length >= 8) + { + var datePart = actualFileName.Substring(0, 8); // 前8位是日期 + if (DateTime.TryParseExact(datePart, "yyyyMMdd", null, DateTimeStyles.None, out DateTime fileDate)) + { + // 构建日期文件夹路径:yyyy/MM/dd(直接使用日期文件夹,不包含Files/SystemFile前缀) + var dateFolder = fileDate.ToString("yyyy/MM/dd"); + filePath = $"{dateFolder}/{actualFileName}"; + } + else + { + // 如果无法解析日期,使用原路径(兼容旧文件) + filePath = Path.Combine(basePath, actualFileName); + } + } + else + { + // 文件名长度不足,使用原路径(兼容旧文件) + filePath = Path.Combine(basePath, actualFileName); + } + } + else + { + filePath = Path.Combine(basePath, actualFileName); + } + var fileDownloadName = exname; if (fileDownloadName.IsNullOrWhiteSpace() || fileDownloadName.Split('.').Length < 2) fileDownloadName = Path.GetFileName(filePath); if (fileDownloadName.IsNullOrWhiteSpace()) - fileDownloadName = fileName.Replace(GetPathByType(type), ""); + fileDownloadName = fileName.Replace(basePath, ""); // annexpic 类型强制使用阿里云OSS存储 string forceStoreType = type == "annexpic" ? "aliyun-oss" : null; return await DownloadFileByType(filePath, fileDownloadName, forceStoreType); @@ -365,10 +442,21 @@ namespace NCC.System.Service.Common // 使用Uri对象来解析和替换域名,保留查询参数(签名信息) var originalUri = new Uri(urlString); + + // 提取域名(去掉协议和端口号) + var domainHost = domain.Replace("https://", "").Replace("http://", "").TrimEnd('/'); + // 如果域名中包含端口号,去掉端口号 + var colonIndex = domainHost.IndexOf(':'); + if (colonIndex > 0) + { + domainHost = domainHost.Substring(0, colonIndex); + } + var customUri = new UriBuilder(originalUri) { Scheme = domain.StartsWith("https://") ? "https" : "http", - Host = domain.Replace("https://", "").Replace("http://", "").TrimEnd('/'), + Host = domainHost, + Port = -1, // 使用默认端口(http:80, https:443),不显示在URL中 // 确保保留查询参数(签名信息) Query = originalUri.Query }; @@ -626,29 +714,54 @@ namespace NCC.System.Service.Common throw NCCException.Oh($"不支持的图片格式: {imageFormat}"); } - // 生成文件名 - var fileName = GenerateImageFileName(input.FileName, imageFormat); - // 获取存储路径 var imageType = string.IsNullOrEmpty(input.ImageType) ? "temporary" : input.ImageType; - var filePath = GetPathByType(imageType); - // 确保目录存在 - if (!Directory.Exists(filePath)) + // 所有类型都上传到阿里云OSS存储 + string uploadFilePath; + string fileName; + var now = DateTime.Now; + + if (imageType == "annexpic") + { + // 生成文件名(格式与 Uploader 一致:yyyyMMdd_xxx.ext) + fileName = now.ToString("yyyyMMdd") + "_" + YitIdHelper.NextId().ToString() + "." + imageFormat; + // 按天生成文件夹:yyyy/MM/dd(直接使用日期文件夹,不包含Files/SystemFile前缀) + var dateFolder = now.ToString("yyyy/MM/dd"); + uploadFilePath = dateFolder; + } + else + { + // 生成文件名 + fileName = GenerateImageFileName(input.FileName, imageFormat); + // 获取原始路径,用于OSS存储 + var originalPath = GetPathByType(imageType).TrimEnd('/').TrimEnd('\\'); + // 按天生成文件夹:yyyy/MM/dd,并保留原始路径结构 + var dateFolder = now.ToString("yyyy/MM/dd"); + uploadFilePath = $"{originalPath}/{dateFolder}"; + } + + // 上传到OSS + var bucketName = KeyVariable.BucketName; + var ossPath = $"{uploadFilePath.TrimEnd('/').TrimEnd('\\')}/{fileName}"; + using (var stream = new MemoryStream(imageData)) { - Directory.CreateDirectory(filePath); + await _oSSServiceFactory.Create("aliyun").PutObjectAsync(bucketName, ossPath, stream); } - // 保存图片文件 - var fullPath = Path.Combine(filePath, fileName); - await File.WriteAllBytesAsync(fullPath, imageData); + // 获取OSS的完整访问地址 + string accessUrl = await GetOSSAccessUrl(uploadFilePath, fileName); - // 生成访问URL - var accessUrl = $"/api/File/Image/{imageType}/{fileName}"; + // 返回格式与数据库保存格式一致:name(原始文件名), fileId(生成的文件名), url(OSS地址) + // 对于Base64上传,如果没有提供原始文件名,使用生成的文件名作为name + var originalFileName = string.IsNullOrEmpty(input.FileName) + ? fileName + : $"{input.FileName}.{imageFormat}"; return new { - name = fileName, + name = originalFileName, + fileId = fileName, url = accessUrl, fileSize = imageData.Length, imageFormat = imageFormat.ToUpper(), diff --git a/sql/创建事业部总经理经理工资统计表.sql b/sql/创建事业部总经理经理工资统计表.sql new file mode 100644 index 0000000..a205c0b --- /dev/null +++ b/sql/创建事业部总经理经理工资统计表.sql @@ -0,0 +1,198 @@ +-- ============================================ +-- 创建事业部总经理/经理工资统计表 +-- 功能:存储事业部总经理/经理每月的工资计算数据,包括底薪、业绩提成、扣款、补贴、奖金、支付等信息 +-- 创建时间:2025年 +-- ============================================ + +-- 删除表(如果存在) +DROP TABLE IF EXISTS lq_business_unit_manager_salary_statistics; + +-- ============================================ +-- 创建事业部总经理/经理工资统计表 +-- ============================================ +CREATE TABLE lq_business_unit_manager_salary_statistics ( + -- 主键 + F_Id VARCHAR(50) NOT NULL COMMENT '主键ID', + + -- 一、基础信息字段 + F_StatisticsMonth VARCHAR(6) NOT NULL COMMENT '统计月份(YYYYMM格式)', + F_Position VARCHAR(50) NOT NULL COMMENT '核算岗位(总经理/经理)', + F_EmployeeName VARCHAR(100) NOT NULL COMMENT '员工姓名', + F_EmployeeId VARCHAR(50) NOT NULL COMMENT '员工ID', + F_EmployeeAccount VARCHAR(100) NULL COMMENT '员工账号', + F_ManagerType INT NOT NULL COMMENT '经理类型(0=经理,1=总经理)', + F_IsTerminated INT NOT NULL DEFAULT 0 COMMENT '是否离职(0=在职,1=离职)', + + -- 二、管理的门店信息(JSON格式) + F_StorePerformanceDetail TEXT NULL COMMENT '门店业绩明细(JSON格式,记录每个门店的业绩和提成详情)', + + -- 三、底薪相关字段 + F_BaseSalary DECIMAL(18,2) NOT NULL DEFAULT 4000.00 COMMENT '底薪金额(固定4000元)', + + -- 四、提成相关字段 + F_TotalCommission DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '提成合计(所有门店提成金额汇总)', + + -- 五、考勤相关字段 + F_WorkingDays DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '在店天数', + F_LeaveDays DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '请假天数', + + -- 六、工资计算字段 + F_CalculatedGrossSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '核算应发工资(底薪 + 提成合计)', + F_FinalGrossSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '最终应发工资(等于核算应发工资)', + + -- 七、补贴相关字段 + F_MonthlyTrainingSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月培训补贴', + F_MonthlyTransportSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月交通补贴', + F_LastMonthTrainingSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '上月培训补贴', + F_LastMonthTransportSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '上月交通补贴', + F_TotalSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '补贴合计', + + -- 八、扣款相关字段 + F_MissingCard DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '缺卡扣款', + F_LateArrival DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '迟到扣款', + F_LeaveDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '请假扣款', + F_SocialInsuranceDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣社保', + F_RewardDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣除奖励', + F_AccommodationDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣住宿费', + F_StudyPeriodDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣学习期费用', + F_WorkClothesDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣工作服费用', + F_TotalDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣款合计', + + -- 九、奖金相关字段 + F_Bonus DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '发奖金', + F_ReturnPhoneDeposit DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '退手机押金', + F_ReturnAccommodationDeposit DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '退住宿押金', + + -- 十、支付相关字段 + F_ActualSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '实发工资(最终应发工资 - 扣款合计 + 补贴合计 + 奖金)', + F_MonthlyPaymentStatus VARCHAR(20) NOT NULL DEFAULT '未发放' COMMENT '当月是否发放(已发放/未发放/部分发放)', + F_PaidAmount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '支付金额', + F_PendingAmount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '待支付金额', + F_LastMonthSupplement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '补发上月', + F_MonthlyTotalPayment DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月支付总额', + + -- 十一、系统字段 + F_IsLocked INT NOT NULL DEFAULT 0 COMMENT '是否锁定(0=未锁定,1=已锁定)', + F_CreateTime DATETIME NOT NULL COMMENT '创建时间', + F_UpdateTime DATETIME NOT NULL COMMENT '更新时间', + F_CreateUser VARCHAR(50) NULL COMMENT '创建人', + F_UpdateUser VARCHAR(50) NULL COMMENT '更新人', + + -- 主键约束 + PRIMARY KEY (F_Id), + + -- 唯一索引:确保同一员工同一月份只有一条记录 + UNIQUE KEY `uk_employee_month` (F_EmployeeId, F_StatisticsMonth), + + -- 普通索引 + KEY `idx_statistics_month` (F_StatisticsMonth), + KEY `idx_employee_id` (F_EmployeeId), + KEY `idx_employee_account` (F_EmployeeAccount), + KEY `idx_manager_type` (F_ManagerType), + KEY `idx_is_terminated` (F_IsTerminated), + KEY `idx_create_time` (F_CreateTime) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='事业部总经理/经理工资统计表'; + +-- ============================================ +-- 表结构说明 +-- ============================================ +/* +表名:lq_business_unit_manager_salary_statistics(事业部总经理/经理工资统计表) + +功能说明: +1. 存储事业部总经理/经理每月的工资计算数据 +2. 包括底薪、业绩提成、扣款、补贴、奖金、支付等信息 +3. 支持按员工、月份查询 +4. 记录管理的门店汇总信息 + +主要字段说明: +- F_BaseSalary:底薪(固定4000元) +- F_StorePerformanceDetail:门店业绩明细(JSON格式,记录每个门店的业绩和提成详情) +- F_TotalCommission:提成合计(所有门店提成金额汇总) + +数据来源: +- 总经理/经理归属:lq_md_general_manager_lifeline 表(通过F_GeneralManagerId和F_Month获取) +- 门店生命线:lq_md_target 表的 F_StoreLifeline 字段 +- 提成阶梯:lq_md_general_manager_lifeline 表的 F_Lifeline1/2/3 和 F_CommissionRate1/2/3 +- 开单业绩:lq_kd_kdjlb 表的 sfyj 字段(按门店统计) +- 退卡业绩:lq_hytk_hytk 表的 F_ActualRefundAmount 或 tkje 字段(按门店统计) + +计算公式: +- 门店总业绩 = 开单业绩 - 退卡业绩 +- 提成计算逻辑: + 1. 判断是否达到提成门槛:门店业绩 ≥ 门店生命线? + - 如果否 → 该门店提成 = 0 + - 如果是 → 继续计算提成 + 2. 如果达到门槛,使用提成阶梯计算提成(分段累进): + - 业绩 ≤ 提成阶梯1:提成 = 业绩 × 提成比例1 + - 提成阶梯1 < 业绩 ≤ 提成阶梯2:提成 = 提成阶梯1 × 提成比例1 + (业绩 - 提成阶梯1) × 提成比例2 + - 提成阶梯2 < 业绩 ≤ 提成阶梯3:提成 = 提成阶梯1 × 提成比例1 + (提成阶梯2 - 提成阶梯1) × 提成比例2 + (业绩 - 提成阶梯2) × 提成比例3 + - 业绩 > 提成阶梯3:提成 = 提成阶梯1 × 提成比例1 + (提成阶梯2 - 提成阶梯1) × 提成比例2 + (提成阶梯3 - 提成阶梯2) × 提成比例3 + (业绩 - 提成阶梯3) × 提成比例3 +- 总提成 = SUM(各门店提成金额) +- 核算应发工资 = 底薪(4000) + 总提成 +- 最终应发工资 = 核算应发工资 +- 实发工资 = 最终应发工资 - 扣款合计 + 补贴合计 + 奖金 + +门店业绩明细JSON格式示例: +[ + { + "storeId": "门店ID", + "storeName": "门店名称", + "storeLifeline": 300000.00, + "billingPerformance": 400000.00, + "refundPerformance": 50000.00, + "storePerformance": 350000.00, + "reachedLifeline": true, + "lifeline1": 350000.00, + "commissionRate1": 1.00, + "lifeline2": 400000.00, + "commissionRate2": 1.50, + "lifeline3": 450000.00, + "commissionRate3": 2.00, + "commissionAmount": 3500.00, + "calculationDetail": "业绩350000元,达到门店生命线300000元,使用提成阶梯1计算:350000 × 1.0% = 3500元" + }, + { + "storeId": "门店ID2", + "storeName": "门店名称2", + "storeLifeline": 250000.00, + "billingPerformance": 220000.00, + "refundPerformance": 20000.00, + "storePerformance": 200000.00, + "reachedLifeline": false, + "commissionAmount": 0.00, + "calculationDetail": "业绩200000元,未达到门店生命线250000元,无提成" + } +] + +JSON字段说明: +- storeId:门店ID +- storeName:门店名称 +- storeLifeline:门店生命线(从lq_md_target表获取) +- billingPerformance:门店开单业绩 +- refundPerformance:门店退卡业绩 +- storePerformance:门店总业绩(开单业绩 - 退卡业绩) +- reachedLifeline:是否达到门店生命线(true/false) +- lifeline1/2/3:提成阶梯1/2/3(从lq_md_general_manager_lifeline表获取) +- commissionRate1/2/3:提成比例1/2/3(%) +- commissionAmount:该门店的提成金额 +- calculationDetail:计算说明(文字描述) + +索引说明: +- 主键索引:F_Id +- 唯一索引:F_EmployeeId + F_StatisticsMonth(确保同一员工同一月份只有一条记录) +- 普通索引: + - F_StatisticsMonth:按月份查询 + - F_EmployeeId:按员工查询 + - F_ManagerType:按经理类型查询(总经理/经理) + - F_CreateTime:按创建时间查询 + +数据校验要求: +1. 底薪固定为4000元 +2. 门店生命线必须设置,未设置应报错 +3. 提成阶梯1和提成比例1必须设置,未设置应报错 +4. 提成必须按照阶梯式计算(分段累进) +5. 如果门店业绩 < 门店生命线,则该门店提成为0 +6. 总经理和经理的计算规则相同 +*/ + diff --git a/sql/创建大项目部老师工资统计表.sql b/sql/创建大项目部老师工资统计表.sql new file mode 100644 index 0000000..f4535ec --- /dev/null +++ b/sql/创建大项目部老师工资统计表.sql @@ -0,0 +1,167 @@ +-- ============================================ +-- 创建大项目部老师工资统计表 +-- 功能:存储大项目部老师每月的工资计算数据,包括底薪、业绩提成、扣款、补贴、奖金、支付等信息 +-- 创建时间:2025年 +-- ============================================ + +-- 删除表(如果存在) +DROP TABLE IF EXISTS lq_major_project_teacher_salary_statistics; + +-- ============================================ +-- 创建大项目部老师工资统计表 +-- ============================================ +CREATE TABLE lq_major_project_teacher_salary_statistics ( + -- 主键 + F_Id VARCHAR(50) NOT NULL COMMENT '主键ID', + + -- 一、基础信息字段 + F_StatisticsMonth VARCHAR(6) NOT NULL COMMENT '统计月份(YYYYMM格式)', + F_StoreId VARCHAR(50) NOT NULL COMMENT '门店ID', + F_StoreName VARCHAR(200) NOT NULL COMMENT '门店名称', + F_Position VARCHAR(50) NOT NULL DEFAULT '大项目部老师' COMMENT '核算岗位(固定为"大项目部老师")', + F_EmployeeName VARCHAR(100) NOT NULL COMMENT '员工姓名', + F_EmployeeId VARCHAR(50) NOT NULL COMMENT '员工ID', + F_EmployeeAccount VARCHAR(100) NULL COMMENT '员工账号', + F_StoreType INT NULL COMMENT '门店类型(200平/旗舰店)', + F_StoreCategory INT NULL COMMENT '门店分类(1=A类,2=B类,3=C类)', + F_IsNewStore VARCHAR(10) NULL COMMENT '是否新店(是/否)', + F_NewStoreProtectionStage INT NOT NULL DEFAULT 0 COMMENT '新店保护阶段(0/1/2)', + + -- 二、业绩相关字段 + F_OrderAchievement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '开单业绩(从lq_kd_kjbsyj表统计,根据归属表关联)', + F_ConsumeAchievement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '消耗业绩(从lq_xh_kjbsyj表统计,根据归属表关联)', + F_RefundAchievement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '退卡业绩(从lq_hytk_kjbsyj表统计,根据归属表关联)', + F_TotalPerformance DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '总业绩(开单业绩 + 消耗业绩 + 退卡业绩,用于业绩提成计算)', + + -- 三、底薪相关字段 + F_BaseSalary DECIMAL(18,2) NOT NULL DEFAULT 3000.00 COMMENT '底薪金额(固定3000元)', + + -- 四、业绩提成相关字段 + F_PerformanceCommissionRate DECIMAL(18,4) NOT NULL DEFAULT 0.0000 COMMENT '业绩提成比例(百分比,如2.00表示2%,0.00表示无提成)', + F_PerformanceCommissionAmount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '业绩提成金额(总业绩 × 业绩提成比例,阶梯式计算)', + F_TotalCommission DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '提成合计(业绩提成金额)', + + -- 五、其他收入字段 + F_HandworkFee DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '手工费', + F_TransportationAllowance DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '车补', + F_LessRest DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '少休费', + F_FullAttendance DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '全勤奖', + + -- 六、考勤相关字段 + F_WorkingDays DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '在店天数', + F_LeaveDays DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '请假天数', + + -- 七、工资计算字段 + F_CalculatedGrossSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '核算应发工资(底薪 + 提成合计 + 其他收入)', + F_GuaranteedSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '保底工资', + F_GuaranteedLeaveDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '保底请假扣款', + F_GuaranteedBaseSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '保底底薪', + F_GuaranteedSupplement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '保底补差', + F_FinalGrossSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '最终应发工资(取核算应发工资和保底工资的较大值)', + + -- 八、补贴相关字段 + F_MonthlyTrainingSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月培训补贴', + F_MonthlyTransportSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月交通补贴', + F_LastMonthTrainingSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '上月培训补贴', + F_LastMonthTransportSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '上月交通补贴', + F_TotalSubsidy DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '补贴合计', + + -- 九、扣款相关字段 + F_MissingCard DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '缺卡扣款', + F_LateArrival DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '迟到扣款', + F_LeaveDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '请假扣款', + F_SocialInsuranceDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣社保', + F_RewardDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣除奖励', + F_AccommodationDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣住宿费', + F_StudyPeriodDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣学习期费用', + F_WorkClothesDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣工作服费用', + F_TotalDeduction DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '扣款合计', + + -- 十、奖金相关字段 + F_Bonus DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '发奖金', + F_ReturnPhoneDeposit DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '退手机押金', + F_ReturnAccommodationDeposit DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '退住宿押金', + + -- 十一、支付相关字段 + F_ActualSalary DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '实发工资(最终应发工资 - 扣款合计 + 补贴合计 + 奖金)', + F_MonthlyPaymentStatus VARCHAR(20) NOT NULL DEFAULT '未发放' COMMENT '当月是否发放(已发放/未发放/部分发放)', + F_PaidAmount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '支付金额', + F_PendingAmount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '待支付金额', + F_LastMonthSupplement DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '补发上月', + F_MonthlyTotalPayment DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '当月支付总额', + + -- 十二、系统字段 + F_IsLocked INT NOT NULL DEFAULT 0 COMMENT '是否锁定(0=未锁定,1=已锁定)', + F_IsTerminated INT NOT NULL DEFAULT 0 COMMENT '是否离职(0=在职,1=离职)', + F_CreateTime DATETIME NOT NULL COMMENT '创建时间', + F_UpdateTime DATETIME NOT NULL COMMENT '更新时间', + F_CreateUser VARCHAR(50) NULL COMMENT '创建人', + F_UpdateUser VARCHAR(50) NULL COMMENT '更新人', + + -- 主键约束 + PRIMARY KEY (F_Id), + + -- 唯一索引:确保同一员工同一月份只有一条记录 + UNIQUE KEY `uk_employee_month` (F_EmployeeId, F_StatisticsMonth), + + -- 普通索引 + KEY `idx_store_id` (F_StoreId), + KEY `idx_statistics_month` (F_StatisticsMonth), + KEY `idx_employee_id` (F_EmployeeId), + KEY `idx_employee_account` (F_EmployeeAccount), + KEY `idx_store_category` (F_StoreCategory), + KEY `idx_is_terminated` (F_IsTerminated), + KEY `idx_create_time` (F_CreateTime) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='大项目部老师工资统计表'; + +-- ============================================ +-- 表结构说明 +-- ============================================ +/* +表名:lq_major_project_teacher_salary_statistics(大项目部老师工资统计表) + +功能说明: +1. 存储大项目部老师每月的工资计算数据 +2. 包括底薪、业绩提成、扣款、补贴、奖金、支付等信息 +3. 支持按门店、员工、月份查询 + +主要字段说明: +- F_BaseSalary:底薪(固定3000元) +- F_TotalPerformance:总业绩(开单业绩 + 消耗业绩 + 退卡业绩),用于计算业绩提成 +- F_PerformanceCommissionRate:业绩提成比例(阶梯式:≤20万无提成,>20万且≤100万为2%,>100万为2.5%) +- F_PerformanceCommissionAmount:业绩提成金额(总业绩 × 业绩提成比例) +- F_TotalCommission:提成合计(等于业绩提成金额) + +数据来源: +- 老师归属:lq_md_major_project_teacher_assignment 表(每个月归属不一样) +- 开单业绩:lq_kd_kjbsyj 表(根据归属表关联) +- 消耗业绩:lq_xh_kjbsyj 表(根据归属表关联) +- 退卡业绩:lq_hytk_kjbsyj 表(根据归属表关联) + +计算公式: +- 总业绩 = 开单业绩 + 消耗业绩 + 退卡业绩 +- 业绩提成计算(阶梯式): + * 总业绩 ≤ 20万:无提成(0%) + * 总业绩 > 20万 且 ≤ 100万:2% + * 总业绩 > 100万:2.5% +- 提成金额 = 总业绩 × 对应提成比例 +- 核算应发工资 = 底薪(3000) + 提成合计 + 其他收入 +- 最终应发工资 = MAX(核算应发工资, 保底工资) +- 实发工资 = 最终应发工资 - 扣款合计 + 补贴合计 + 奖金 + +索引说明: +- 主键索引:F_Id +- 唯一索引:F_EmployeeId + F_StatisticsMonth(确保同一员工同一月份只有一条记录) +- 普通索引: + - F_StoreId:按门店查询 + - F_StatisticsMonth:按月份查询 + - F_EmployeeId:按员工查询 + - F_StoreCategory:按门店分类查询 + - F_CreateTime:按创建时间查询 + +数据校验要求: +1. 底薪固定为3000元 +2. 业绩提成必须按照阶梯式计算 +3. 总业绩必须从归属表关联的业绩数据中统计 +*/ + diff --git a/sql/创建库存使用申请审批流程表.sql b/sql/创建库存使用申请审批流程表.sql index 726c469..595ea58 100644 --- a/sql/创建库存使用申请审批流程表.sql +++ b/sql/创建库存使用申请审批流程表.sql @@ -104,3 +104,7 @@ WHERE u.`F_UnitPrice` = 0 OR u.`F_UnitPrice` IS NULL; + + + + diff --git a/sql/同步BeautyType字段数据.sql b/sql/同步BeautyType字段数据.sql new file mode 100644 index 0000000..9d26428 --- /dev/null +++ b/sql/同步BeautyType字段数据.sql @@ -0,0 +1,268 @@ +-- ============================================ +-- 同步 F_BeautyType 字段数据 +-- ============================================ +-- 说明:从 lq_xmzl 表的 F_BeautyType 字段同步数据到其他相关表 +-- +-- 关联关系: +-- 1. lq_kd_pxmx: 通过 px 字段关联到 lq_xmzl.F_Id +-- 2. lq_kd_jksyj: 通过 F_kdpxid 关联到 lq_kd_pxmx,或通过 F_ItemId 直接关联到 lq_xmzl +-- 3. lq_kd_kjbsyj: 通过 F_kdpxid 关联到 lq_kd_pxmx,或通过 F_ItemId 直接关联到 lq_xmzl +-- 4. lq_hytk_mx: 通过 px 字段关联到 lq_xmzl.F_Id +-- 5. lq_hytk_jksyj: 通过 F_tkpxid 关联到 lq_hytk_mx,或通过 F_ItemId 直接关联到 lq_xmzl +-- 6. lq_hytk_kjbsyj: 通过 F_tkpxid 关联到 lq_hytk_mx,或通过 F_ItemId 直接关联到 lq_xmzl +-- 7. lq_xh_pxmx: 通过 px 字段关联到 lq_xmzl.F_Id +-- 8. lq_xh_jksyj: 通过 F_kdpxid 关联到 lq_xh_pxmx,或通过 F_ItemId 直接关联到 lq_xmzl +-- 9. lq_xh_kjbsyj: 通过 F_hkpxid 关联到 lq_xh_pxmx,或通过 F_ItemId 直接关联到 lq_xmzl +-- +-- 注意事项: +-- - 优先使用 F_ItemId 直接关联(如果存在且有效) +-- - 如果 F_ItemId 为空,则通过关联表间接关联 +-- - 只更新 F_BeautyType 为 NULL 或空字符串的记录 + +-- ============================================ +-- 1. 同步 lq_kd_pxmx(开单品项明细表) +-- ============================================ +UPDATE lq_kd_pxmx t1 +INNER JOIN lq_xmzl t2 ON t1.px = t2.F_Id +SET t1.F_BeautyType = t2.F_BeautyType +WHERE t2.F_BeautyType IS NOT NULL + AND t2.F_BeautyType != '' + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- ============================================ +-- 2. 同步 lq_kd_jksyj(开单健康师业绩表) +-- ============================================ +-- 方式1:通过 F_ItemId 直接关联(优先) +UPDATE lq_kd_jksyj t1 +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id +SET t1.F_BeautyType = t2.F_BeautyType +WHERE t2.F_BeautyType IS NOT NULL + AND t2.F_BeautyType != '' + AND t1.F_ItemId IS NOT NULL + AND t1.F_ItemId != '' + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- 方式2:通过 F_kdpxid 关联到 lq_kd_pxmx,再关联到 lq_xmzl +UPDATE lq_kd_jksyj t1 +INNER JOIN lq_kd_pxmx t2 ON t1.F_kdpxid = t2.F_Id +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id +SET t1.F_BeautyType = t3.F_BeautyType +WHERE t3.F_BeautyType IS NOT NULL + AND t3.F_BeautyType != '' + AND t1.F_kdpxid IS NOT NULL + AND t1.F_kdpxid != '' + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- ============================================ +-- 3. 同步 lq_kd_kjbsyj(开单科技部老师业绩表) +-- ============================================ +-- 方式1:通过 F_ItemId 直接关联(优先) +UPDATE lq_kd_kjbsyj t1 +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id +SET t1.F_BeautyType = t2.F_BeautyType +WHERE t2.F_BeautyType IS NOT NULL + AND t2.F_BeautyType != '' + AND t1.F_ItemId IS NOT NULL + AND t1.F_ItemId != '' + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- 方式2:通过 F_kdpxid 关联到 lq_kd_pxmx,再关联到 lq_xmzl +UPDATE lq_kd_kjbsyj t1 +INNER JOIN lq_kd_pxmx t2 ON t1.F_kdpxid = t2.F_Id +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id +SET t1.F_BeautyType = t3.F_BeautyType +WHERE t3.F_BeautyType IS NOT NULL + AND t3.F_BeautyType != '' + AND t1.F_kdpxid IS NOT NULL + AND t1.F_kdpxid != '' + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- ============================================ +-- 4. 同步 lq_hytk_mx(退卡品项明细表) +-- ============================================ +UPDATE lq_hytk_mx t1 +INNER JOIN lq_xmzl t2 ON t1.px = t2.F_Id +SET t1.F_BeautyType = t2.F_BeautyType +WHERE t2.F_BeautyType IS NOT NULL + AND t2.F_BeautyType != '' + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- ============================================ +-- 5. 同步 lq_hytk_jksyj(退卡健康师业绩表) +-- ============================================ +-- 方式1:通过 F_ItemId 直接关联(优先) +UPDATE lq_hytk_jksyj t1 +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id +SET t1.F_BeautyType = t2.F_BeautyType +WHERE t2.F_BeautyType IS NOT NULL + AND t2.F_BeautyType != '' + AND t1.F_ItemId IS NOT NULL + AND t1.F_ItemId != '' + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- 方式2:通过 F_tkpxid 关联到 lq_hytk_mx,再关联到 lq_xmzl +UPDATE lq_hytk_jksyj t1 +INNER JOIN lq_hytk_mx t2 ON t1.F_tkpxid = t2.F_Id +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id +SET t1.F_BeautyType = t3.F_BeautyType +WHERE t3.F_BeautyType IS NOT NULL + AND t3.F_BeautyType != '' + AND t1.F_tkpxid IS NOT NULL + AND t1.F_tkpxid != '' + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- ============================================ +-- 6. 同步 lq_hytk_kjbsyj(退卡科技部老师业绩表) +-- ============================================ +-- 方式1:通过 F_ItemId 直接关联(优先) +UPDATE lq_hytk_kjbsyj t1 +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id +SET t1.F_BeautyType = t2.F_BeautyType +WHERE t2.F_BeautyType IS NOT NULL + AND t2.F_BeautyType != '' + AND t1.F_ItemId IS NOT NULL + AND t1.F_ItemId != '' + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- 方式2:通过 F_tkpxid 关联到 lq_hytk_mx,再关联到 lq_xmzl +UPDATE lq_hytk_kjbsyj t1 +INNER JOIN lq_hytk_mx t2 ON t1.F_tkpxid = t2.F_Id +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id +SET t1.F_BeautyType = t3.F_BeautyType +WHERE t3.F_BeautyType IS NOT NULL + AND t3.F_BeautyType != '' + AND t1.F_tkpxid IS NOT NULL + AND t1.F_tkpxid != '' + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- ============================================ +-- 7. 同步 lq_xh_pxmx(耗卡品项明细表) +-- ============================================ +UPDATE lq_xh_pxmx t1 +INNER JOIN lq_xmzl t2 ON t1.px = t2.F_Id +SET t1.F_BeautyType = t2.F_BeautyType +WHERE t2.F_BeautyType IS NOT NULL + AND t2.F_BeautyType != '' + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- ============================================ +-- 8. 同步 lq_xh_jksyj(耗卡健康师业绩表) +-- ============================================ +-- 方式1:通过 F_ItemId 直接关联(优先) +UPDATE lq_xh_jksyj t1 +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id +SET t1.F_BeautyType = t2.F_BeautyType +WHERE t2.F_BeautyType IS NOT NULL + AND t2.F_BeautyType != '' + AND t1.F_ItemId IS NOT NULL + AND t1.F_ItemId != '' + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- 方式2:通过 F_kdpxid 关联到 lq_xh_pxmx,再关联到 lq_xmzl +UPDATE lq_xh_jksyj t1 +INNER JOIN lq_xh_pxmx t2 ON t1.F_kdpxid = t2.F_Id +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id +SET t1.F_BeautyType = t3.F_BeautyType +WHERE t3.F_BeautyType IS NOT NULL + AND t3.F_BeautyType != '' + AND t1.F_kdpxid IS NOT NULL + AND t1.F_kdpxid != '' + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- ============================================ +-- 9. 同步 lq_xh_kjbsyj(耗卡科技部老师业绩表) +-- ============================================ +-- 方式1:通过 F_ItemId 直接关联(优先) +UPDATE lq_xh_kjbsyj t1 +INNER JOIN lq_xmzl t2 ON t1.F_ItemId = t2.F_Id +SET t1.F_BeautyType = t2.F_BeautyType +WHERE t2.F_BeautyType IS NOT NULL + AND t2.F_BeautyType != '' + AND t1.F_ItemId IS NOT NULL + AND t1.F_ItemId != '' + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- 方式2:通过 F_hkpxid 关联到 lq_xh_pxmx,再关联到 lq_xmzl +UPDATE lq_xh_kjbsyj t1 +INNER JOIN lq_xh_pxmx t2 ON t1.F_hkpxid = t2.F_Id +INNER JOIN lq_xmzl t3 ON t2.px = t3.F_Id +SET t1.F_BeautyType = t3.F_BeautyType +WHERE t3.F_BeautyType IS NOT NULL + AND t3.F_BeautyType != '' + AND t1.F_hkpxid IS NOT NULL + AND t1.F_hkpxid != '' + AND (t1.F_ItemId IS NULL OR t1.F_ItemId = '') + AND (t1.F_BeautyType IS NULL OR t1.F_BeautyType = ''); + +-- ============================================ +-- 10. 验证同步结果 +-- ============================================ +-- 查看各表同步的数据统计 +-- SELECT +-- 'lq_kd_pxmx' AS table_name, +-- COUNT(*) AS total_count, +-- COUNT(F_BeautyType) AS beauty_type_count, +-- COUNT(*) - COUNT(F_BeautyType) AS null_count +-- FROM lq_kd_pxmx +-- UNION ALL +-- SELECT +-- 'lq_kd_jksyj' AS table_name, +-- COUNT(*) AS total_count, +-- COUNT(F_BeautyType) AS beauty_type_count, +-- COUNT(*) - COUNT(F_BeautyType) AS null_count +-- FROM lq_kd_jksyj +-- UNION ALL +-- SELECT +-- 'lq_kd_kjbsyj' AS table_name, +-- COUNT(*) AS total_count, +-- COUNT(F_BeautyType) AS beauty_type_count, +-- COUNT(*) - COUNT(F_BeautyType) AS null_count +-- FROM lq_kd_kjbsyj +-- UNION ALL +-- SELECT +-- 'lq_hytk_mx' AS table_name, +-- COUNT(*) AS total_count, +-- COUNT(F_BeautyType) AS beauty_type_count, +-- COUNT(*) - COUNT(F_BeautyType) AS null_count +-- FROM lq_hytk_mx +-- UNION ALL +-- SELECT +-- 'lq_hytk_jksyj' AS table_name, +-- COUNT(*) AS total_count, +-- COUNT(F_BeautyType) AS beauty_type_count, +-- COUNT(*) - COUNT(F_BeautyType) AS null_count +-- FROM lq_hytk_jksyj +-- UNION ALL +-- SELECT +-- 'lq_hytk_kjbsyj' AS table_name, +-- COUNT(*) AS total_count, +-- COUNT(F_BeautyType) AS beauty_type_count, +-- COUNT(*) - COUNT(F_BeautyType) AS null_count +-- FROM lq_hytk_kjbsyj +-- UNION ALL +-- SELECT +-- 'lq_xh_pxmx' AS table_name, +-- COUNT(*) AS total_count, +-- COUNT(F_BeautyType) AS beauty_type_count, +-- COUNT(*) - COUNT(F_BeautyType) AS null_count +-- FROM lq_xh_pxmx +-- UNION ALL +-- SELECT +-- 'lq_xh_jksyj' AS table_name, +-- COUNT(*) AS total_count, +-- COUNT(F_BeautyType) AS beauty_type_count, +-- COUNT(*) - COUNT(F_BeautyType) AS null_count +-- FROM lq_xh_jksyj +-- UNION ALL +-- SELECT +-- 'lq_xh_kjbsyj' AS table_name, +-- COUNT(*) AS total_count, +-- COUNT(F_BeautyType) AS beauty_type_count, +-- COUNT(*) - COUNT(F_BeautyType) AS null_count +-- FROM lq_xh_kjbsyj; + diff --git a/sql/添加教育部老师字段.sql b/sql/添加教育部老师字段.sql new file mode 100644 index 0000000..c0b2319 --- /dev/null +++ b/sql/添加教育部老师字段.sql @@ -0,0 +1,35 @@ +-- ============================================ +-- 为门店大项目部老师归属设置表添加教育部老师字段 +-- ============================================ +-- 说明:在 lq_md_major_project_teacher_assignment 表中添加教育部老师字段 +-- +-- 字段说明: +-- F_EducationTeacherId:教育部老师用户ID,用于存储教育部老师的归属设置 +-- +-- 业务含义: +-- - 一个门店在一个月份可以同时有大项目部老师和教育部老师 +-- - 教育部老师ID关联BASE_USER.F_Id +-- +-- 注意事项: +-- - 字段类型为VARCHAR(50),允许为NULL(因为历史数据可能没有教育部老师) +-- - 字段位置:放在 F_TeacherId 字段之后 + +-- ============================================ +-- 添加教育部老师字段 +-- ============================================ +ALTER TABLE lq_md_major_project_teacher_assignment +ADD COLUMN F_EducationTeacherId VARCHAR(50) NULL COMMENT '教育部老师用户ID' AFTER F_TeacherId; + +-- ============================================ +-- 创建索引(可选,如果需要按教育部老师查询) +-- ============================================ +CREATE INDEX idx_education_teacher_id ON lq_md_major_project_teacher_assignment(F_EducationTeacherId); + +-- ============================================ +-- 验证字段创建 +-- ============================================ +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_md_major_project_teacher_assignment' +-- AND COLUMN_NAME = 'F_EducationTeacherId'; + diff --git a/sql/添加科技部归类字段.sql b/sql/添加科技部归类字段.sql index 8e8f7a6..49be161 100644 --- a/sql/添加科技部归类字段.sql +++ b/sql/添加科技部归类字段.sql @@ -8,7 +8,7 @@ -- -- 业务含义: -- - 科技部归类用于科技部对品项进行分类统计和分析 --- - 分类值来源于项目资料表(lq_xmzl)的 F_BeautyType 字段(科美类型) +-- - 分类值来源于项目资料表(lq_xmzl)的 F_BeautyType 字段 -- -- 注意事项: -- - 字段类型为VARCHAR(50),可存储各种分类值 diff --git a/sql/重命名科技部归类字段为BeautyType.sql b/sql/重命名科技部归类字段为BeautyType.sql new file mode 100644 index 0000000..26064e6 --- /dev/null +++ b/sql/重命名科技部归类字段为BeautyType.sql @@ -0,0 +1,131 @@ +-- ============================================ +-- 重命名科技部归类字段为 F_BeautyType +-- ============================================ +-- 说明:将已创建的 F_KjbCategory 字段重命名为 F_BeautyType,以便与 lq_xmzl 表的字段名统一 +-- +-- 字段说明: +-- F_BeautyType:科美类型,用于存储品项的科美类型信息(与 lq_xmzl.F_BeautyType 统一) +-- +-- 业务含义: +-- - 科美类型用于对品项进行分类统计和分析 +-- - 分类值来源于项目资料表(lq_xmzl)的 F_BeautyType 字段 +-- +-- 注意事项: +-- - 此脚本用于重命名已创建的字段 +-- - 字段类型保持不变:VARCHAR(50),允许为NULL +-- - 字段位置保持不变:在 F_PerformanceType 字段之后 + +-- ============================================ +-- 1. lq_kd_pxmx(开单品项明细表) - 重命名字段 +-- ============================================ +ALTER TABLE lq_kd_pxmx +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; + +-- ============================================ +-- 2. lq_kd_jksyj(开单健康师业绩表) - 重命名字段 +-- ============================================ +ALTER TABLE lq_kd_jksyj +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; + +-- ============================================ +-- 3. lq_kd_kjbsyj(开单科技部老师业绩表) - 重命名字段 +-- ============================================ +ALTER TABLE lq_kd_kjbsyj +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; + +-- ============================================ +-- 4. lq_hytk_mx(退卡品项明细表) - 重命名字段 +-- ============================================ +ALTER TABLE lq_hytk_mx +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; + +-- ============================================ +-- 5. lq_hytk_jksyj(退卡健康师业绩表) - 重命名字段 +-- ============================================ +ALTER TABLE lq_hytk_jksyj +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; + +-- ============================================ +-- 6. lq_hytk_kjbsyj(退卡科技部老师业绩表) - 重命名字段 +-- ============================================ +ALTER TABLE lq_hytk_kjbsyj +CHANGE COLUMN F_KjbCategory F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)'; + +-- ============================================ +-- 7. lq_xh_pxmx(耗卡品项明细表) - 添加字段 +-- ============================================ +-- 注意:如果字段已存在会报错,可以忽略;如果存在 F_KjbCategory 需要先重命名 +ALTER TABLE lq_xh_pxmx +ADD COLUMN F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)' AFTER F_PerformanceType; + +-- ============================================ +-- 8. lq_xh_jksyj(耗卡健康师业绩表) - 添加字段 +-- ============================================ +-- 注意:如果字段已存在会报错,可以忽略;如果存在 F_KjbCategory 需要先重命名 +ALTER TABLE lq_xh_jksyj +ADD COLUMN F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)' AFTER F_PerformanceType; + +-- ============================================ +-- 9. lq_xh_kjbsyj(耗卡科技部老师业绩表) - 添加字段 +-- ============================================ +-- 注意:如果字段已存在会报错,可以忽略;如果存在 F_KjbCategory 需要先重命名 +ALTER TABLE lq_xh_kjbsyj +ADD COLUMN F_BeautyType VARCHAR(50) NULL COMMENT '科美类型(来源:lq_xmzl.F_BeautyType)' AFTER F_PerformanceType; + +-- ============================================ +-- 10. 验证字段重命名 +-- ============================================ +-- 验证 lq_kd_pxmx 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_kd_pxmx' +-- AND COLUMN_NAME = 'F_BeautyType'; + +-- 验证 lq_kd_jksyj 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_kd_jksyj' +-- AND COLUMN_NAME = 'F_BeautyType'; + +-- 验证 lq_kd_kjbsyj 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_kd_kjbsyj' +-- AND COLUMN_NAME = 'F_BeautyType'; + +-- 验证 lq_hytk_mx 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_hytk_mx' +-- AND COLUMN_NAME = 'F_BeautyType'; + +-- 验证 lq_hytk_jksyj 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_hytk_jksyj' +-- AND COLUMN_NAME = 'F_BeautyType'; + +-- 验证 lq_hytk_kjbsyj 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_hytk_kjbsyj' +-- AND COLUMN_NAME = 'F_BeautyType'; + +-- 验证 lq_xh_pxmx 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_xh_pxmx' +-- AND COLUMN_NAME = 'F_BeautyType'; + +-- 验证 lq_xh_jksyj 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_xh_jksyj' +-- AND COLUMN_NAME = 'F_BeautyType'; + +-- 验证 lq_xh_kjbsyj 表 +-- SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT +-- FROM INFORMATION_SCHEMA.COLUMNS +-- WHERE TABLE_NAME = 'lq_xh_kjbsyj' +-- AND COLUMN_NAME = 'F_BeautyType'; + diff --git a/事业部总经理经理工资计算规则梳理.md b/事业部总经理经理工资计算规则梳理.md new file mode 100644 index 0000000..56de4d7 --- /dev/null +++ b/事业部总经理经理工资计算规则梳理.md @@ -0,0 +1,350 @@ +# 事业部总经理/经理工资计算规则梳理 + +## 📋 目录 +- [概述](#概述) +- [计算规则](#计算规则) +- [数据来源](#数据来源) +- [归属规则](#归属规则) +- [计算流程](#计算流程) + +--- + +## 📋 概述 + +事业部总经理/经理工资由以下几个部分组成: +1. **底薪**:固定4000元 +2. **提成**:根据管理的门店业绩,使用阶梯式提成计算(基于门店总业绩) + +**重要说明**: +- 每个总经理/经理都会管理多个门店 +- 提成计算基于门店总业绩(开单业绩 - 退卡业绩) +- 总经理和经理的计算规则相同 +- 必须先达到门店生命线才能计算提成 + +--- + +## 💰 计算规则 + +### 1. 底薪规则 + +**固定底薪**:4000元 + +- 无论业绩多少,底薪固定为4000元 +- 不设档位,不设条件 +- 不设考核扣款(与店长、主任不同) + +--- + +### 2. 提成规则(阶梯式) + +**提成计算方式**:根据管理的门店业绩,使用阶梯式提成计算 + +#### 2.1 提成门槛:门店生命线 + +**重要概念**:门店生命线是提成的**门槛条件**,不是提成阶梯 + +- **数据来源**:`lq_md_target` 表的 `F_StoreLifeline` 字段 +- **判断条件**: + - 如果门店业绩 < 门店生命线 → **无提成** + - 如果门店业绩 ≥ 门店生命线 → **可以计算提成** + +**重要说明**: +- 门店生命线是**必须设置的**,未设置应报错 +- 门店生命线是门店级别的指标,用于判断是否达到提成门槛 + +#### 2.2 提成阶梯设置 + +**数据来源**:`lq_md_general_manager_lifeline` 表 + +每个门店都有**三个等级的提成阶梯**,每个等级对应不同的提成比例: + +| 等级 | 生命线字段 | 提成比例字段 | 说明 | +|------|-----------|-------------|------| +| **等级1** | `F_Lifeline1` | `F_CommissionRate1` | 第一级提成阶梯(必填) | +| **等级2** | `F_Lifeline2` | `F_CommissionRate2` | 第二级提成阶梯(可选) | +| **等级3** | `F_Lifeline3` | `F_CommissionRate3` | 第三级提成阶梯(可选) | + +**重要说明**: +- 提成阶梯1和提成比例1为必填项 +- 提成阶梯2、3和对应的提成比例为可选项 +- 每个门店的提成阶梯设置可能不同 +- 提成阶梯由总经理在系统中配置 + +**注意**:提成阶梯(`lq_md_general_manager_lifeline`)和门店生命线(`lq_md_target.F_StoreLifeline`)是**两个不同的概念**: +- **门店生命线**:判断是否达到提成门槛 +- **提成阶梯**:计算提成金额的阶梯 + +#### 2.3 阶梯式提成计算规则 + +**前提条件**:门店业绩必须 ≥ 门店生命线,否则无提成 + +**计算逻辑**:根据门店业绩落在哪个提成阶梯区间,使用对应的提成比例计算(分段累进) + +**示例**(假设某门店的设置): +- 门店生命线(`lq_md_target.F_StoreLifeline`)= 300,000元 +- 提成阶梯1 = 350,000元,提成比例1 = 1.0% +- 提成阶梯2 = 400,000元,提成比例2 = 1.5% +- 提成阶梯3 = 450,000元,提成比例3 = 2.0% + +**计算规则**(分段累进): + +1. **业绩 < 门店生命线**: + - 提成 = 0(无提成) + - 示例:业绩 = 280,000元 → 提成 = 0 + +2. **门店生命线 ≤ 业绩 ≤ 提成阶梯1**: + - 提成 = 业绩 × 提成比例1 + - 示例:业绩 = 320,000元 → 提成 = 320,000 × 1.0% = 3,200元 + +3. **提成阶梯1 < 业绩 ≤ 提成阶梯2**: + - 提成 = 提成阶梯1 × 提成比例1 + (业绩 - 提成阶梯1) × 提成比例2 + - 示例:业绩 = 380,000元 → 提成 = 350,000 × 1.0% + (380,000 - 350,000) × 1.5% = 3,500 + 450 = 3,950元 + +4. **提成阶梯2 < 业绩 ≤ 提成阶梯3**: + - 提成 = 提成阶梯1 × 提成比例1 + (提成阶梯2 - 提成阶梯1) × 提成比例2 + (业绩 - 提成阶梯2) × 提成比例3 + - 示例:业绩 = 420,000元 → 提成 = 350,000 × 1.0% + (400,000 - 350,000) × 1.5% + (420,000 - 400,000) × 2.0% = 3,500 + 750 + 400 = 4,650元 + +5. **业绩 > 提成阶梯3**: + - 提成 = 提成阶梯1 × 提成比例1 + (提成阶梯2 - 提成阶梯1) × 提成比例2 + (提成阶梯3 - 提成阶梯2) × 提成比例3 + (业绩 - 提成阶梯3) × 提成比例3 + - 示例:业绩 = 500,000元 → 提成 = 350,000 × 1.0% + (400,000 - 350,000) × 1.5% + (450,000 - 400,000) × 2.0% + (500,000 - 450,000) × 2.0% = 3,500 + 750 + 1,000 + 1,000 = 6,250元 + +**注意**: +- 如果提成阶梯2或提成阶梯3未设置(为NULL或0),则只使用提成阶梯1计算 +- 如果提成阶梯2设置但提成阶梯3未设置,则业绩超过提成阶梯2的部分按提成比例2计算 + +#### 2.4 多门店提成汇总 + +**计算方式**: +- 总经理/经理管理的所有门店的提成分别计算 +- 将所有门店的提成金额汇总,得到总提成金额 + +**计算公式**: +``` +总提成 = SUM(各门店提成金额) +``` + +--- + +## 📊 数据来源 + +### 1. 总经理/经理列表和门店归属 + +**数据来源表**:`lq_md_general_manager_lifeline`(总经理门店生命线设置表) + +**关键字段**: +- `F_GeneralManagerId`:总经理/经理用户ID +- `F_ManagerType`:经理类型(0=经理,1=总经理) +- `F_StoreId`:门店ID +- `F_Month`:月份(YYYYMM格式) + +**查询方式**: +1. **获取当月所有的经理和总经理**: + ```sql + SELECT DISTINCT F_GeneralManagerId, F_ManagerType + FROM lq_md_general_manager_lifeline + WHERE F_Month = 'YYYYMM' + ``` + +2. **获取总经理/经理管理的门店**: + ```sql + SELECT F_StoreId + FROM lq_md_general_manager_lifeline + WHERE F_Month = 'YYYYMM' + AND F_GeneralManagerId = '用户ID' + ``` + +**重要说明**: +- 通过 `lq_md_general_manager_lifeline` 表可以获取当月所有的经理和总经理 +- 通过 `lq_md_general_manager_lifeline` 表可以看到当月总经理和经理管理的门店 +- 每个门店在每个月可以设置一个总经理/经理的提成阶梯 + +### 2. 门店生命线(提成门槛) + +**数据来源表**:`lq_md_target`(门店目标表) + +**关键字段**: +- `F_StoreId`:门店ID +- `F_Month`:月份(YYYYMM格式) +- `F_StoreLifeline`:门店生命线(**必须设置**) + +**查询条件**: +- 按门店ID和月份查询:`F_StoreId = '门店ID' AND F_Month = 'YYYYMM'` + +**重要说明**: +- 门店生命线是**必须设置的**,未设置应报错 +- 门店生命线用于判断是否达到提成门槛 +- 如果门店业绩 < 门店生命线,则无提成 + +### 3. 提成阶梯设置 + +**数据来源表**:`lq_md_general_manager_lifeline`(总经理门店生命线设置表) + +**关键字段**: +- `F_StoreId`:门店ID +- `F_Month`:月份(YYYYMM格式) +- `F_GeneralManagerId`:总经理/经理用户ID +- `F_Lifeline1`:第一级提成阶梯(必填) +- `F_CommissionRate1`:第一级提成比例(%,必填) +- `F_Lifeline2`:第二级提成阶梯(可选) +- `F_CommissionRate2`:第二级提成比例(%,可选) +- `F_Lifeline3`:第三级提成阶梯(可选) +- `F_CommissionRate3`:第三级提成比例(%,可选) + +**查询条件**: +- 按门店ID、月份、总经理/经理ID查询 +- 如果未找到提成阶梯设置,则无法计算提成(应报错或跳过该门店) + +### 4. 门店总业绩 + +**定义**:门店总业绩 = 开单业绩 - 退卡业绩 + +**数据来源表及字段**: + +| 业绩类型 | 数据表 | 字段 | 说明 | +|---------|--------|------|------| +| **开单业绩** | `lq_kd_kdjlb` | `sfyj` | 门店开单实付金额 | +| **退卡业绩** | `lq_hytk_hytk` | `F_ActualRefundAmount` 或 `tkje` | 门店退卡金额 | + +**计算方式**: +```sql +门店总业绩 = SUM(门店开单实付金额) - SUM(门店退卡金额) +``` + +**过滤条件**: +- 所有表记录必须满足:`F_IsEffective = 1`(有效记录) +- 按统计月份(YYYYMM格式)过滤时间范围 +- 按门店ID(`djmd` 或 `md`)过滤 + +**重要说明**: +- **确认**:提成计算基于门店总业绩(开单 - 退卡) + +--- + +## 👥 归属规则 + +### 1. 总经理/经理与门店的关联 + +**关联方式**: +- 通过 `lq_md_general_manager_lifeline` 表关联 +- 每个门店在每个月可以设置一个总经理/经理的提成阶梯 +- 同一个总经理/经理可以管理多个门店 + +**重要说明**: +- 通过 `lq_md_general_manager_lifeline` 表可以获取当月所有的经理和总经理 +- 通过 `lq_md_general_manager_lifeline` 表可以看到当月总经理和经理管理的门店 + +### 2. 门店生命线和提成阶梯的关系 + +**两个不同的概念**: + +1. **门店生命线**(`lq_md_target.F_StoreLifeline`): + - 用于判断是否达到提成门槛 + - 如果门店业绩 < 门店生命线,则无提成 + - 如果门店业绩 ≥ 门店生命线,则可以计算提成 + +2. **提成阶梯**(`lq_md_general_manager_lifeline` 表的 Lifeline1/2/3): + - 用于计算提成金额的阶梯 + - 只有在达到门店生命线的前提下,才使用提成阶梯计算提成 + +**重要说明**: +- 门店生命线是**必须设置的**,未设置应报错 +- 提成阶梯1和提成比例1是必填项,未设置应报错 +- 提成阶梯2、3和对应的提成比例为可选项 + +--- + +## 🔄 计算流程 + +### 1. 数据准备 + +1. **获取总经理/经理列表和门店归属**: + - 从 `lq_md_general_manager_lifeline` 表获取当月所有的经理和总经理 + - 按总经理/经理ID分组,得到每个总经理/经理管理的门店列表 + +2. **获取门店生命线**: + - 从 `lq_md_target` 表获取每个门店的生命线(`F_StoreLifeline`) + - 门店生命线是必须设置的,未设置应报错 + +3. **获取提成阶梯设置**: + - 从 `lq_md_general_manager_lifeline` 表获取每个门店的提成阶梯设置 + - 提成阶梯1和提成比例1是必填项,未设置应报错 + +4. **获取门店总业绩**: + - 从 `lq_kd_kdjlb` 表统计每个门店的开单业绩(`sfyj`) + - 从 `lq_hytk_hytk` 表统计每个门店的退卡业绩(`F_ActualRefundAmount` 或 `tkje`) + - 计算每个门店的总业绩 = 开单业绩 - 退卡业绩 + +### 2. 工资计算 + +**遍历每个总经理/经理**: + +1. **初始化工资统计对象**: + - 底薪 = 4000元 + - 总提成 = 0 + +2. **遍历该总经理/经理管理的每个门店**: + + a. **判断是否达到提成门槛**: + - 获取该门店的生命线(`lq_md_target.F_StoreLifeline`) + - 获取该门店的总业绩 + - 如果门店业绩 < 门店生命线 → 该门店提成 = 0,跳过 + - 如果门店业绩 ≥ 门店生命线 → 继续计算提成 + + b. **计算该门店的提成**(如果达到门槛): + - 获取该门店的提成阶梯设置(`lq_md_general_manager_lifeline`) + - 根据门店业绩和提成阶梯,使用分段累进方式计算提成金额 + - 累加到总提成 + +3. **计算最终工资**: + - 应发工资 = 底薪 + 总提成 + +### 3. 数据保存 + +- 将计算结果保存到工资统计表中(待创建表结构) +- 如果已存在当月数据,则更新;否则插入新数据 + +--- + +## 📝 注意事项 + +1. **数据一致性**: + - 门店业绩计算逻辑必须与其他统计接口保持一致 + - 门店生命线必须与门店目标表保持一致 + +2. **数据校验**: + - 门店生命线(`lq_md_target.F_StoreLifeline`)必须设置,未设置应报错 + - 提成阶梯1(`lq_md_general_manager_lifeline.F_Lifeline1`)必须设置,未设置应报错 + - 提成比例1(`lq_md_general_manager_lifeline.F_CommissionRate1`)必须设置,未设置应报错 + - 如果门店未在 `lq_md_general_manager_lifeline` 表中设置,则无法计算该门店的提成 + +3. **边界情况**: + - 如果门店没有业绩数据,业绩为0,未达到门店生命线,提成为0 + - 如果门店业绩 < 门店生命线,提成为0 + - 如果总经理/经理没有管理的门店,总提成为0,应发工资 = 底薪(4000元) + - 如果提成阶梯2或提成阶梯3未设置,则只使用提成阶梯1计算 + +4. **计算精度**: + - 涉及金额计算时,建议保留2位小数 + - 提成比例以百分比形式存储(如:1.0表示1%) + +5. **总经理和经理**: + - 总经理和经理的计算规则相同 + - 都使用门店生命线来判断是否达到提成门槛 + - 都使用 `lq_md_general_manager_lifeline` 表的提成阶梯来计算提成 + +6. **保底工资**: + - 暂时不考虑保底工资规则 + +--- + +## 📋 参考文档 + +- [项目信息-薪酬规则与名词解释.md](./项目信息-薪酬规则与名词解释.md) +- [店长工资计算规则梳理.md](./店长工资计算规则梳理.md) +- [主任工资计算规则梳理.md](./主任工资计算规则梳理.md) +- [大项目部老师工资计算规则梳理.md](./大项目部老师工资计算规则梳理.md) + +--- + +**最后更新时间**:2025年1月 + diff --git a/大项目部老师工资计算规则梳理.md b/大项目部老师工资计算规则梳理.md new file mode 100644 index 0000000..e31ed0e --- /dev/null +++ b/大项目部老师工资计算规则梳理.md @@ -0,0 +1,253 @@ +# 大项目部老师工资计算规则梳理 + +## 📋 目录 +- [计算规则](#计算规则) +- [数据来源](#数据来源) +- [归属规则](#归属规则) +- [计算流程](#计算流程) + +--- + +## 💰 计算规则 + +### 1. 底薪规则 + +**固定底薪**:3000元 + +- 无论业绩多少,底薪固定为3000元 +- 不设档位,不设条件 + +--- + +### 2. 业绩提成规则(阶梯式) + +业绩提成基于**总业绩**计算,采用阶梯式方式: + +| 总业绩范围 | 提成比例 | 说明 | +|-----------|---------|------| +| ≤ 20万 | 0% | 无提成 | +| > 20万 且 ≤ 100万 | 2% | 按2%计算提成 | +| > 100万 | 2.5% | 按2.5%计算提成 | + +**计算说明**: +- 提成金额 = 总业绩 × 对应提成比例 +- 采用阶梯式计算,不同区间按不同比例计算 +- **注意**:不是分段累进,而是整个总业绩按对应比例计算 + +**示例**: +- 总业绩 = 15万 → 提成 = 0(无提成) +- 总业绩 = 50万 → 提成 = 50万 × 2% = 1万 +- 总业绩 = 120万 → 提成 = 120万 × 2.5% = 3万 + +--- + +## 📊 数据来源 + +### 总业绩(TotalPerformance) + +**定义**:门店总业绩 = 开单业绩 - 退卡业绩 + +**重要说明**: +- **大项目部老师计算的是门店总业绩,不是个人业绩** +- 大项目部老师和科技部老师是两个不同的岗位 +- 根据归属表 `lq_md_major_project_teacher_assignment` 确定该老师在某个月归属于哪个门店 +- 然后统计该门店在该月的总业绩(开单业绩 - 退卡业绩) + +**数据来源表及字段**: + +| 业绩类型 | 数据表 | 字段 | 说明 | +|---------|--------|------|------| +| **开单业绩** | `lq_kd_kdjlb` | `sfyj` | 门店开单实付金额(根据归属表关联的门店ID统计) | +| **退卡业绩** | `lq_hytk_hytk` | `F_ActualRefundAmount` 或 `tkje` | 门店退卡金额(根据归属表关联的门店ID统计) | + +**计算方式**: +```sql +门店总业绩 = SUM(门店开单实付金额) - SUM(门店退卡金额) +``` + +**过滤条件**: +- 所有表记录必须满足:`F_IsEffective = 1`(有效记录) +- 按统计月份(YYYYMM格式)过滤时间范围 +- **关键**:根据 `lq_md_major_project_teacher_assignment` 归属表关联 + - 关联条件:`lq_md_major_project_teacher_assignment.F_TeacherId = 大项目部老师ID` + - 关联条件:`lq_md_major_project_teacher_assignment.F_Year = 统计年份` + - 关联条件:`lq_md_major_project_teacher_assignment.F_Month = 统计月份` + - 关联条件:`lq_md_major_project_teacher_assignment.F_StoreId = 门店ID` + - 然后统计该门店在该月的开单业绩和退卡业绩 + +**注意**: +- 大项目部老师的归属每个月可能不一样 +- 需要根据归属表来确定该老师在该月份归属于哪个门店 +- **统计的是门店的总业绩,不是个人业绩** +- 如果同一个老师在某个月归属于多个门店,需要合并多个门店的业绩 + +--- + +## 🔗 归属规则 + +### 归属表:`lq_md_major_project_teacher_assignment` + +**表结构**: +- `F_Id`:主键ID +- `F_StoreId`:门店ID +- `F_Year`:年份(YYYY格式) +- `F_Month`:月份(MM格式) +- `F_TeacherId`:大项目部老师用户ID +- `F_EducationTeacherId`:教育部老师用户ID(可选) +- `F_Remark`:备注说明 + +**归属规则**: +- 每个大项目部老师每个月可能归属于不同的门店 +- 归属信息存储在 `lq_md_major_project_teacher_assignment` 表中 +- 统计工资时,需要根据该表来确定: + 1. 该老师在该月份归属于哪个门店 + 2. 该门店的业绩数据中,哪些属于该老师 + +**关联逻辑**: +1. 根据 `F_Year` 和 `F_Month` 确定统计月份 +2. 根据 `F_TeacherId` 确定大项目部老师 +3. 根据 `F_StoreId` 确定归属门店 +4. 统计该门店在该月份的业绩数据(开单、消耗、退卡) +5. 根据业绩表中的老师ID字段关联到该大项目部老师 + +**业绩关联方式**(待确认): +- 开单业绩:`lq_kd_kjbsyj` 表中的 `kjblszh` 或 `kjbls` 字段 +- 消耗业绩:`lq_xh_kjbsyj` 表中的 `kjblszh` 或 `kjbls` 字段 +- 退卡业绩:`lq_hytk_kjbsyj` 表中的 `kjblszh` 或 `kjbls` 字段 + +**注意**: +- 需要确认大项目部老师的业绩是如何关联的 +- 可能需要通过 `BASE_USER` 表的账号或ID来关联 +- 或者通过其他关联字段来确定 + +--- + +## ⚠️ 特殊规则 + +### 离职员工规则 + +**适用条件**: +- 员工状态:`BASE_USER.F_IsOnJob = 0`(离职) + +**计算规则**: +- 离职员工的工资计算规则待确认 +- 可能需要特殊处理(如:只计算在职期间的业绩) + +--- + +## 📝 计算流程 + +### 1. 数据准备阶段 + +1. **获取归属信息** + - 从 `lq_md_major_project_teacher_assignment` 表查询指定月份的所有归属记录 + - 按 `F_TeacherId` 和 `F_StoreId` 分组 + +2. **获取业绩数据** + - 开单业绩:从 `lq_kd_kjbsyj` 表统计 + - 消耗业绩:从 `lq_xh_kjbsyj` 表统计 + - 退卡业绩:从 `lq_hytk_kjbsyj` 表统计 + - 按统计月份过滤时间范围 + - 按归属门店和老师ID关联 + +3. **获取员工信息** + - 从 `BASE_USER` 表获取员工基本信息 + - 包括:姓名、账号、门店信息等 + +### 2. 业绩统计阶段 + +1. **计算开单业绩** + - 根据归属表关联的开单业绩数据 + - 按大项目部老师ID汇总 + +2. **计算消耗业绩** + - 根据归属表关联的消耗业绩数据 + - 按大项目部老师ID汇总 + +3. **计算退卡业绩** + - 根据归属表关联的退卡业绩数据 + - 按大项目部老师ID汇总 + +4. **计算总业绩** + - 总业绩 = 开单业绩 + 消耗业绩 + 退卡业绩 + +### 3. 工资计算阶段 + +1. **计算底薪** + - 底薪 = 3000元(固定) + +2. **计算业绩提成** + - 判断总业绩范围: + - ≤ 20万:提成比例 = 0% + - > 20万 且 ≤ 100万:提成比例 = 2% + - > 100万:提成比例 = 2.5% + - 提成金额 = 总业绩 × 提成比例 + +3. **计算其他收入** + - 手工费、车补、少休费、全勤奖等(默认0) + +4. **计算核算应发工资** + - 核算应发工资 = 底薪 + 提成合计 + 其他收入 + +5. **计算最终应发工资** + - 最终应发工资 = MAX(核算应发工资, 保底工资) + - 如果没有保底工资,则等于核算应发工资 + +6. **计算实发工资** + - 实发工资 = 最终应发工资 - 扣款合计 + 补贴合计 + 奖金 + +### 4. 数据保存阶段 + +1. **保存工资统计记录** + - 保存到 `lq_major_project_teacher_salary_statistics` 表 + - 确保同一员工同一月份只有一条记录(唯一索引) + +2. **更新状态** + - 设置 `F_IsLocked = 0`(未锁定) + - 设置 `F_IsTerminated`(根据员工状态) + +--- + +## ❓ 待确认问题 + +1. **业绩关联方式** + - 大项目部老师的业绩是如何关联的? + - 是通过 `BASE_USER` 表的账号或ID来关联吗? + - 还是通过其他字段来确定? + +2. **归属门店的业绩统计** + - 如果一个大项目部老师在某个月归属于某个门店,那么: + - 该门店的所有业绩都算他的吗? + - 还是只统计该门店中他参与的业绩? + +3. **离职员工处理** + - 离职员工的工资计算规则是什么? + - 是否需要特殊处理? + +4. **保底工资** + - 大项目部老师是否有保底工资? + - 保底工资的计算规则是什么? + +--- + +## 📌 总结 + +### 核心规则 +1. **底薪**:固定3000元 +2. **业绩提成**:阶梯式计算 + - ≤ 20万:0% + - > 20万 且 ≤ 100万:2% + - > 100万:2.5% + +### 关键点 +1. **归属表关联**:需要根据 `lq_md_major_project_teacher_assignment` 表来确定归属关系 +2. **业绩统计**:需要根据归属表关联的业绩数据来统计 +3. **月份归属**:每个月的归属可能不一样,需要按月统计 + +### 下一步 +1. 确认业绩关联方式 +2. 确认归属门店的业绩统计规则 +3. 确认离职员工处理规则 +4. 确认保底工资规则 +5. 实现计算逻辑 + -- libgit2 0.21.4