From 96e0ccc76c117a041e498d29d5253064a1cff663 Mon Sep 17 00:00:00 2001
From: “wangming” <“wangming@antissoft.com”>
Date: Mon, 1 Dec 2025 18:12:18 +0800
Subject: [PATCH] 优化GetBillingRecordSummaryByStoreId方法:移除多个门店筛选以提升性能,保留单个门店筛选及其他筛选条件
---
antis-ncc-admin/package-lock.json | 4 ++--
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingRecordSummaryQueryInput.cs | 24 ++++++++++++++++++++++++
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs | 15 +++++++++++++++
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryInput.cs | 31 +++++++++++++++++++++++++++++++
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs | 67 +++++--------------------------------------------------------------
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs | 1511 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
netcore/src/Modularity/Extend/NCC.Extend/LqProductService.cs | 32 ++++++++++++++++++++++++++++----
netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs | 486 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs | 2 --
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs | 78 +++++++++++++++++++++++++++++++++++++++---------------------------------------
sql/同步退卡时间字段.sql | 33 +++++++++++++++++++++++++++++++++
sql/更新开单记录表储扣金额.sql | 21 +++++++++++++++++++++
13 files changed, 1776 insertions(+), 643 deletions(-)
create mode 100644 netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryInput.cs
create mode 100644 netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs
create mode 100644 netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
create mode 100644 sql/同步退卡时间字段.sql
create mode 100644 sql/更新开单记录表储扣金额.sql
diff --git a/antis-ncc-admin/package-lock.json b/antis-ncc-admin/package-lock.json
index 1d46d6e..801aa4d 100644
--- a/antis-ncc-admin/package-lock.json
+++ b/antis-ncc-admin/package-lock.json
@@ -21699,7 +21699,7 @@
},
"node_modules/xlsx": {
"version": "0.18.5",
- "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
+ "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"dependencies": {
"adler-32": "~1.3.0",
@@ -39759,7 +39759,7 @@
},
"xlsx": {
"version": "0.18.5",
- "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
+ "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"requires": {
"adler-32": "~1.3.0",
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingRecordSummaryQueryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingRecordSummaryQueryInput.cs
index 253cef3..d54b066 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingRecordSummaryQueryInput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingRecordSummaryQueryInput.cs
@@ -27,5 +27,29 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
///
[Display(Name = "结束时间", Description = "查询开单记录的结束时间")]
public DateTime? EndTime { get; set; }
+
+ ///
+ /// 品项分类
+ ///
+ [Display(Name = "品项分类", Description = "筛选品项分类")]
+ public string ItemCategory { get; set; }
+
+ ///
+ /// 品项ID
+ ///
+ [Display(Name = "品项ID", Description = "筛选品项ID")]
+ public string ItemId { get; set; }
+
+ ///
+ /// 健康师ID
+ ///
+ [Display(Name = "健康师ID", Description = "筛选健康师ID")]
+ public string HealthCoachId { get; set; }
+
+ ///
+ /// 客户ID(会员ID)
+ ///
+ [Display(Name = "客户ID", Description = "筛选客户ID")]
+ public string MemberId { get; set; }
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs
index 7a59134..9afc45a 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs
@@ -94,5 +94,20 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
/// 退卡金额 - 统计该健康师在指定时间周期内的退卡业绩总金额
///
public decimal refundAmount { get; set; }
+
+ ///
+ /// 手工费 - 统计该健康师在指定时间周期内消耗时的手工费总金额
+ ///
+ public decimal laborCost { get; set; }
+
+ ///
+ /// 原始手工费 - 统计该健康师在指定时间周期内消耗时的原始手工费总金额
+ ///
+ public decimal originalLaborCost { get; set; }
+
+ ///
+ /// 加班手工费 - 统计该健康师在指定时间周期内消耗时的加班手工费总金额
+ ///
+ public decimal overtimeLaborCost { get; set; }
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryInput.cs
new file mode 100644
index 0000000..b613cc6
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryInput.cs
@@ -0,0 +1,31 @@
+using NCC.Common.Filter;
+using System;
+
+namespace NCC.Extend.Entitys.Dto.LqSalary
+{
+ ///
+ /// 健康师工资查询参数
+ ///
+ public class HealthCoachSalaryInput : 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/LqSalary/HealthCoachSalaryOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs
new file mode 100644
index 0000000..652896e
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs
@@ -0,0 +1,115 @@
+using System;
+
+namespace NCC.Extend.Entitys.Dto.LqSalary
+{
+ ///
+ /// 健康师工资输出
+ ///
+ public class HealthCoachSalaryOutput
+ {
+ ///
+ /// 主键ID
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// 门店名称
+ ///
+ public string StoreName { get; set; }
+
+ ///
+ /// 员工姓名
+ ///
+ public string EmployeeName { get; set; }
+
+ ///
+ /// 岗位
+ ///
+ public string Position { get; set; }
+
+ ///
+ /// 金三角战队
+ ///
+ public string GoldTriangleTeam { get; set; }
+
+ ///
+ /// 总业绩
+ ///
+ public decimal TotalPerformance { get; set; }
+
+ ///
+ /// 基础业绩
+ ///
+ public decimal BasePerformance { get; set; }
+
+ ///
+ /// 合作业绩
+ ///
+ public decimal CooperationPerformance { get; set; }
+
+ ///
+ /// 奖励业绩
+ ///
+ public decimal RewardPerformance { get; set; }
+
+ ///
+ /// 消耗
+ ///
+ public decimal Consumption { get; set; }
+
+ ///
+ /// 项目数
+ ///
+ public decimal ProjectCount { get; set; }
+
+ ///
+ /// 到店人头
+ ///
+ public decimal CustomerCount { get; set; }
+
+ ///
+ /// 在店天数
+ ///
+ public decimal WorkingDays { get; set; }
+
+ ///
+ /// 健康师底薪
+ ///
+ public decimal HealthCoachBaseSalary { get; set; }
+
+ ///
+ /// 提成合计
+ ///
+ public decimal TotalCommission { get; set; }
+
+ ///
+ /// 手工费
+ ///
+ public decimal HandworkFee { get; set; }
+
+ ///
+ /// 补贴合计
+ ///
+ public decimal TotalSubsidy { get; set; }
+
+ ///
+ /// 扣款合计
+ ///
+ public decimal TotalDeduction { get; set; }
+
+ ///
+ /// 实发工资
+ ///
+ public decimal ActualSalary { get; set; }
+
+ ///
+ /// 是否锁定
+ ///
+ public int IsLocked { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ public DateTime UpdateTime { get; set; }
+ }
+}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
index 3c39985..9e74200 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
@@ -299,60 +299,6 @@ namespace NCC.Extend.LqHytkHytk
/// 创建退卡信息及其关联的品项明细、健康师业绩、科技部老师业绩信息
///
///
- /// 创建退卡记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
- ///
- /// 示例请求:
- /// ```json
- /// {
- /// "md": "门店ID",
- /// "mdbh": "门店编号",
- /// "mdmc": "门店名称",
- /// "hy": "会员ID",
- /// "hymc": "会员姓名",
- /// "hyzh": "会员账号",
- /// "gklx": "顾客类型",
- /// "tkje": 1000.00,
- /// "sgfy": 50.00,
- /// "bz": "备注",
- /// "tkzt": "退卡状态",
- /// "tkyy": "退卡原因",
- /// "lqHytkMxList": [
- /// {
- /// "px": "品项编号",
- /// "pxmc": "品项名称",
- /// "pxjg": 100.00,
- /// "tkje": 100.00,
- /// "F_ProjectNumber": 1,
- /// "F_SourceType": "来源类型",
- /// "F_TotalPrice": 100.00,
- /// "lqHytkJksyjList": [
- /// {
- /// "jks": "健康师",
- /// "jksxm": "健康师姓名",
- /// "jkszh": "健康师账号",
- /// "jksyj": 50.00,
- /// "F_jsjid": "金三角ID",
- /// "F_tkpxid": "项目资料ID",
- /// "F_LaborCost": 10.00,
- /// "F_tkpxNumber": 1
- /// }
- /// ],
- /// "lqHytkKjbsyjList": [
- /// {
- /// "kjbls": "科技部老师",
- /// "kjblsxm": "科技部老师姓名",
- /// "kjblszh": "科技部老师账号",
- /// "kjblsyj": 30.00,
- /// "F_tkpxid": "项目资料ID",
- /// "F_LaborCost": 5.00,
- /// "F_tkpxNumber": 1
- /// }
- /// ]
- /// }
- /// ]
- /// }
- /// ```
- ///
/// 参数说明:
/// - md: 门店ID
/// - hy: 会员ID
@@ -378,15 +324,12 @@ namespace NCC.Extend.LqHytkHytk
{
// 开启事务
_db.BeginTran();
-
// 新增退卡主表记录
var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync();
-
// 收集所有需要插入的实体,然后批量插入
var allMxEntities = new List();
var allJksyjEntities = new List();
var allKjbsyjEntities = new List();
-
// 处理品项明细列表
if (input.lqHytkMxList != null && input.lqHytkMxList.Any())
{
@@ -405,6 +348,7 @@ namespace NCC.Extend.LqHytkHytk
Pxmc = item.pxmc,
Pxjg = item.pxjg,
Tkje = item.tkje,
+ Tksj = input.tksj,
ProjectNumber = item.F_ProjectNumber ?? 1,
SourceType = item.F_SourceType,
TotalPrice = item.F_TotalPrice ?? (item.pxjg * (item.F_ProjectNumber ?? 1)),
@@ -426,7 +370,7 @@ namespace NCC.Extend.LqHytkHytk
Jksxm = ijks_tem.jksxm,
Jkszh = ijks_tem.jkszh,
Jksyj = ijks_tem.jksyj,
- Tksj = DateTime.Now,
+ Tksj = input.tksj,
F_jsjid = ijks_tem.F_jsjid,
F_tkpxid = ijks_tem.F_tkpxid,
F_LaborCost = ijks_tem.F_LaborCost,
@@ -458,7 +402,7 @@ namespace NCC.Extend.LqHytkHytk
Kjblsxm = ikjbs_tem.kjblsxm,
Kjblszh = ikjbs_tem.kjblszh,
Kjblsyj = ikjbs_tem.kjblsyj,
- Tksj = DateTime.Now,
+ Tksj = input.tksj,
F_tkpxid = ikjbs_tem.F_tkpxid,
F_LaborCost = ikjbs_tem.F_LaborCost,
F_tkpxNumber = ikjbs_tem.F_tkpxNumber,
@@ -476,7 +420,6 @@ namespace NCC.Extend.LqHytkHytk
}
}
}
-
// 批量插入品项明细
if (allMxEntities.Any())
{
@@ -587,7 +530,7 @@ namespace NCC.Extend.LqHytkHytk
Jksxm = ijks_tem.jksxm,
Jkszh = ijks_tem.jkszh,
Jksyj = ijks_tem.jksyj,
- Tksj = DateTime.Now,
+ Tksj = input.tksj,
F_jsjid = ijks_tem.F_jsjid,
F_tkpxid = ijks_tem.F_tkpxid,
F_LaborCost = ijks_tem.F_LaborCost,
@@ -617,7 +560,7 @@ namespace NCC.Extend.LqHytkHytk
Kjblsxm = ikjbs_tem.kjblsxm,
Kjblszh = ikjbs_tem.kjblszh,
Kjblsyj = ikjbs_tem.kjblsyj,
- Tksj = DateTime.Now,
+ Tksj = input.tksj,
F_tkpxid = ikjbs_tem.F_tkpxid,
F_LaborCost = ikjbs_tem.F_LaborCost,
F_tkpxNumber = ikjbs_tem.F_tkpxNumber,
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
index 6f18fb7..d36e815 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
@@ -1117,6 +1117,263 @@ namespace NCC.Extend.LqKdKdjlb
}
#endregion
+ #region 修改开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
+ ///
+ /// 修改开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
+ ///
+ ///
+ /// 参数说明:
+ /// - id: 开单记录主键ID
+ /// - input: 开单记录更新参数,包含品项明细和业绩信息
+ ///
+ /// 开单记录主键ID
+ /// 开单记录更新参数
+ /// 无返回值
+ /// 更新成功
+ /// 参数错误或数据验证失败
+ /// 服务器内部错误
+ [HttpPut("UpdateForNoDelete/{id}")]
+ public async Task UpdateForNoDelete(string id, [FromBody] LqKdKdjlbUpInput input)
+ {
+ var entity = input.Adapt();
+ entity.Id = id; // 确保ID正确设置
+ try
+ {
+ //检查开单记录是否可以操作
+ var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(id);
+ if (!canCancel)
+ {
+ throw NCCException.Oh(errorMessage);
+ }
+ //开启事务
+ _db.BeginTran();
+ //查询是否有对应的补缴开单ID
+ if (!string.IsNullOrEmpty(entity.SupplementBillingId))
+ {
+ //查询补缴开单ID
+ var supplementBillingEntity = await _db.Queryable().FirstAsync(p => p.Id == entity.SupplementBillingId);//900,900
+ if (supplementBillingEntity == null || supplementBillingEntity.IsEffective == StatusEnum.无效.GetHashCode())
+ {
+ throw NCCException.Oh("补缴开单记录不存在或已作废");
+ }
+ //查询当前开单已经补缴金额
+ var OldSupplementAmount = await _db.Queryable().Where(p => p.Id == id).SumAsync(p => p.SupplementAmount);//900,0
+ supplementBillingEntity.PaidDebt = supplementBillingEntity.PaidDebt - OldSupplementAmount + input.supplementAmount;
+ await _db.Updateable(supplementBillingEntity).ExecuteCommandAsync();
+ }
+ //批量查询当前开单所有品项的分类(性能优化)
+ var itemIds = input.lqKdPxmxList?.Where(x => !string.IsNullOrEmpty(x.px)).Select(x => x.px).Distinct().ToList() ?? new List();
+ var itemCategoryDict = new Dictionary();
+ if (itemIds.Any())
+ {
+ var itemCategories = await _db.Queryable()
+ .Where(x => itemIds.Contains(x.Id) && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .Select(x => new { x.Id, x.Qt2 })
+ .ToListAsync();
+ itemCategoryDict = itemCategories.ToDictionary(k => k.Id, v => v.Qt2 ?? "");
+ }
+ //判断当前开单是否包含医美品项
+ var hasMedicalItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美");
+ var MedicalItemInCurrentBillingAmount = input.lqKdPxmxList.Where(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美").Sum(x => x.actualPrice);
+ //判断当前开单是否包含科美品项
+ var hasTechItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "科美");
+ //判断当前开单是否包含生美品项
+ var hasLifeItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "生美");
+ //获取该会员之前开单品项里面是否有医美项目
+ var isMedicalProject = hasMedicalItemInCurrentBilling && await _db.Queryable().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "医美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
+ if (isMedicalProject && MedicalItemInCurrentBillingAmount >= 1000)
+ {
+ entity.UpgradeLifeBeauty = "是";
+ }
+ else
+ {
+ entity.UpgradeLifeBeauty = "否";
+ }
+ //获取该会员之前开单品项里面是否有科美项目
+ var isTechProject = hasTechItemInCurrentBilling && await _db.Queryable().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "科美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
+ if (isTechProject)
+ {
+ entity.UpgradeTechBeauty = "是";
+ }
+ else
+ {
+ entity.UpgradeTechBeauty = "否";
+ }
+ //获取该会员之前开单品项里面是否有生美项目
+ var isLifeProject = hasLifeItemInCurrentBilling && await _db.Queryable().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "生美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
+ if (isLifeProject)
+ {
+ entity.UpgradeMedicalBeauty = "是";
+ }
+ else
+ {
+ entity.UpgradeMedicalBeauty = "否";
+ }
+ //计算储扣总金额
+ entity.DeductAmount = input.lqKdKdjlbDeductList.Sum(x => x.Amount ?? 0);
+ entity.UpdateTime = DateTime.Now;
+ // 更新开单记录主表
+ await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).IgnoreColumns(x => x.CreateTime).ExecuteCommandAsync();
+ //清空原有品项明细
+ await _db.Deleteable().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
+ //清空原有健康师业绩
+ await _db.Deleteable().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
+ //清空原有科技部老师业绩
+ await _db.Deleteable().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
+ //清空原有扣款信息
+ await _db.Deleteable().Where(x => x.BillingId == id).ExecuteCommandAsync();
+ //循环品相信息
+ // 收集所有需要插入的实体,然后批量插入
+ var allPxmxEntities = new List();
+ var allJksyjEntities = new List();
+ var allKjbsyjEntities = new List();
+ var allDeductEntities = new List();
+ // 处理扣款信息列表
+ foreach (var item in input.lqKdKdjlbDeductList)
+ {
+ var lqKdDeductEntity = new LqKdDeductinfoEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ BillingId = id,
+ DeductId = item.DeductId,
+ DeductType = item.DeductType,
+ Amount = item.Amount,
+ ProjectNumber = item.ProjectNumber,
+ UnitPrice = item.UnitPrice,
+ ItemName = item.ItemName,
+ ItemId = item.ItemId,
+ IsEffective = StatusEnum.有效.GetHashCode(), // 设置为有效
+ CreateTime = DateTime.Now, // 设置创建时间
+ ItemCategory = await _db.Queryable().Where(x => x.Id == item.DeductId).Select(x => x.Qt2).FirstAsync(),
+ };
+ allDeductEntities.Add(lqKdDeductEntity);
+ }
+ // 处理品项明细列表
+ foreach (var item in input.lqKdPxmxList)
+ {
+ // 创建品项明细实体
+ var lqKdPxmxEntity = new LqKdPxmxEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ Glkdbh = id,
+ Yjsj = input.kdrq,
+ CreateTIme = DateTime.Now,
+ MemberId = entity.Kdhy,
+ IsEnabled = StatusEnum.有效.GetHashCode(),
+ ProjectNumber = item.projectNumber,
+ TotalPrice = (decimal)(item.pxjg * item.projectNumber),
+ Px = item.px,
+ Pxmc = item.pxmc,
+ Pxjg = item.pxjg,
+ SourceType = item.sourceType,
+ ActualPrice = item.actualPrice,
+ Remark = item.remark,
+ IsEffective = StatusEnum.有效.GetHashCode(),
+ ActivityId = input.activityId,
+ ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
+ };
+ allPxmxEntities.Add(lqKdPxmxEntity);
+
+ // 收集该品项关联的健康师业绩
+ if (item.lqKdJksyjList != null && item.lqKdJksyjList.Any())
+ {
+ //把jksxm保存到HealthInstructorNames
+ foreach (var ijks_tem in item.lqKdJksyjList)
+ {
+ allJksyjEntities.Add(new LqKdJksyjEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ Glkdbh = id,
+ Jks = ijks_tem.jks,
+ Jksxm = ijks_tem.jksxm,
+ Jkszh = ijks_tem.jkszh,
+ Jksyj = ijks_tem.jksyj,
+ Yjsj = input.kdrq,
+ Jsj_id = ijks_tem.jsj_id,
+ Kdpxid = lqKdPxmxEntity.Id,
+ IsEffective = StatusEnum.有效.GetHashCode(),
+ ActivityId = input.activityId,
+ ItemCategory = lqKdPxmxEntity.ItemCategory,
+ ItemId = lqKdPxmxEntity.Px,
+ StoreId = entity.Djmd,
+ ItemName = lqKdPxmxEntity.Pxmc,
+ });
+ }
+ }
+
+ // 收集该品项关联的科技部老师业绩
+ if (item.lqKdKjbsyjList != null && item.lqKdKjbsyjList.Any())
+ {
+ foreach (var ikjbs_tem in item.lqKdKjbsyjList)
+ {
+ allKjbsyjEntities.Add(new LqKdKjbsyjEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ Glkdbh = id,
+ Kjbls = ikjbs_tem.kjbls,
+ Kjblsxm = ikjbs_tem.kjblsxm,
+ Kjblszh = ikjbs_tem.kjblszh,
+ Kjblsyj = ikjbs_tem.kjblsyj,
+ Yjsj = input.kdrq,
+ Kdpxid = lqKdPxmxEntity.Id,
+ IsEffective = StatusEnum.有效.GetHashCode(),
+ ActivityId = input.activityId,
+ ItemCategory = lqKdPxmxEntity.ItemCategory,
+ ItemId = lqKdPxmxEntity.Px,
+ StoreId = entity.Djmd,
+ ItemName = lqKdPxmxEntity.Pxmc,
+ });
+ }
+ }
+ }
+
+ //通过会员id查询会员信息
+ var memberInfo = await _db.Queryable().Where(u => u.Id == entity.Kdhy).FirstAsync();
+ //通过开单记录表查询这个会员开单金额
+ var kdAmount = await _db.Queryable().Where(u => u.Kdhy == entity.Kdhy).SumAsync(u => u.Sfyj);
+ //如果开单金额小于500,为散客,如果大于500,为会员
+ if (kdAmount < 500)
+ {
+ memberInfo.Khlx = MemberTypeEnum.散客.GetHashCode().ToString();
+ }
+ else
+ {
+ memberInfo.Khlx = MemberTypeEnum.会员.GetHashCode().ToString();
+ }
+ await _db.Updateable(memberInfo).ExecuteCommandAsync();
+ // 批量插入扣款信息
+ if (allDeductEntities.Any())
+ {
+ await _db.Insertable(allDeductEntities).ExecuteCommandAsync();
+ }
+ // 批量插入品项明细
+ if (allPxmxEntities.Any())
+ {
+ await _db.Insertable(allPxmxEntities).ExecuteCommandAsync();
+ }
+ // 批量插入健康师业绩
+ if (allJksyjEntities.Any())
+ {
+ await _db.Insertable(allJksyjEntities).ExecuteCommandAsync();
+ }
+ // 批量插入科技部老师业绩
+ if (allKjbsyjEntities.Any())
+ {
+ await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync();
+ }
+
+ //关闭事务
+ _db.CommitTran();
+ }
+ catch (Exception ex)
+ {
+ //回滚事务
+ _db.RollbackTran();
+ throw NCCException.Oh($"创建开单记录失败: {ex.Message}");
+ }
+ }
+ #endregion
+
#region 获取开单记录表无分页列表
///
/// 获取开单记录表无分页列表
@@ -1293,225 +1550,9 @@ namespace NCC.Extend.LqKdKdjlb
}
#endregion
- #region 更新开单记录表
+ #region 删除开单记录表
///
- /// 更新开单记录表
- ///
- ///
- /// 更新开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
- ///
- /// 示例请求:
- /// ```json
- /// {
- /// "id": "开单编号",
- /// "djmd": "单据门店",
- /// "jsj": "金三角",
- /// "kdrq": "2025-01-11",
- /// "lqKdPxmxList": [
- /// {
- /// "px": "品项编号",
- /// "pxmc": "品项名称",
- /// "pxjg": 100.00,
- /// "projectNumber": 1,
- /// "sourceType": "购买",
- /// "lqKdJksyjList": [
- /// {
- /// "jks": "健康师",
- /// "jksxm": "健康师姓名",
- /// "jksyj": "100"
- /// }
- /// ]
- /// }
- /// ]
- /// }
- /// ```
- ///
- /// 参数说明:
- /// - id: 开单记录主键ID
- /// - input: 开单记录更新参数,包含品项明细和业绩信息
- ///
- /// 开单记录主键ID
- /// 开单记录更新参数
- /// 无返回值
- /// 更新成功
- /// 参数错误或数据验证失败
- /// 服务器内部错误
- [HttpPut("{id}")]
- public async Task Update(string id, [FromBody] LqKdKdjlbUpInput input)
- {
- var entity = input.Adapt();
- try
- {
- //开启事务
- _db.BeginTran();
- //批量查询当前开单所有品项的分类(性能优化)
- var itemIds = input.lqKdPxmxList?.Where(x => !string.IsNullOrEmpty(x.px)).Select(x => x.px).Distinct().ToList() ?? new List();
- var itemCategoryDict = new Dictionary();
- if (itemIds.Any())
- {
- var itemCategories = await _db.Queryable()
- .Where(x => itemIds.Contains(x.Id) && x.IsEffective == StatusEnum.有效.GetHashCode())
- .Select(x => new { x.Id, x.Qt2 })
- .ToListAsync();
- itemCategoryDict = itemCategories.ToDictionary(k => k.Id, v => v.Qt2 ?? "");
- }
- //判断当前开单是否包含医美品项
- var hasMedicalItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x =>
- !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美");
- var MedicalItemInCurrentBillingAmount = input.lqKdPxmxList.Where(x =>
- !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美").Sum(x => x.actualPrice);
- //判断当前开单是否包含科美品项
- var hasTechItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x =>
- !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "科美");
- //判断当前开单是否包含生美品项
- var hasLifeItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x =>
- !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "生美");
- //获取该会员之前开单品项里面是否有医美项目
- var isMedicalProject = hasMedicalItemInCurrentBilling && await _db.Queryable().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "医美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
- if (isMedicalProject && MedicalItemInCurrentBillingAmount >= 1000)
- {
- entity.UpgradeLifeBeauty = "是";
- }
- else
- {
- entity.UpgradeLifeBeauty = "否";
- }
- //获取该会员之前开单品项里面是否有科美项目
- var isTechProject = hasTechItemInCurrentBilling && await _db.Queryable().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "科美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
- if (isTechProject)
- {
- entity.UpgradeTechBeauty = "是";
- }
- else
- {
- entity.UpgradeTechBeauty = "否";
- }
- //获取该会员之前开单品项里面是否有生美项目
- var isLifeProject = hasLifeItemInCurrentBilling && await _db.Queryable().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "生美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
- if (isLifeProject)
- {
- entity.UpgradeMedicalBeauty = "是";
- }
- else
- {
- entity.UpgradeMedicalBeauty = "否";
- }
-
- //更新开单记录表记录
- await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
-
- //清空原有数据
- await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
- await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
- await _db.Deleteable().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
-
- // 收集所有需要插入的实体,然后批量插入
- var allPxmxEntities = new List();
- var allJksyjEntities = new List();
- var allKjbsyjEntities = new List();
-
- // 处理品项明细列表
- if (input.lqKdPxmxList != null && input.lqKdPxmxList.Any())
- {
- foreach (var item in input.lqKdPxmxList)
- {
- // 创建品项明细实体
- var lqKdPxmxEntity = new LqKdPxmxEntity
- {
- Id = YitIdHelper.NextId().ToString(),
- Glkdbh = entity.Id,
- CreateTIme = DateTime.Now,
- MemberId = entity.Kdhy,
- IsEnabled = 0,
- ProjectNumber = item.projectNumber == 0 ? 1 : item.projectNumber,
- TotalPrice = (decimal)(item.pxjg * (item.projectNumber == 0 ? 1 : item.projectNumber)),
- Px = item.px,
- Pxmc = item.pxmc,
- Pxjg = item.pxjg,
- SourceType = item.sourceType,
- ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
- };
- allPxmxEntities.Add(lqKdPxmxEntity);
-
- // 收集该品项关联的健康师业绩
- if (item.lqKdJksyjList != null && item.lqKdJksyjList.Any())
- {
- foreach (var ijks_tem in item.lqKdJksyjList)
- {
- allJksyjEntities.Add(new LqKdJksyjEntity
- {
- Id = YitIdHelper.NextId().ToString(),
- Glkdbh = entity.Id,
- Jks = ijks_tem.jks,
- Jksxm = ijks_tem.jksxm,
- Jkszh = ijks_tem.jkszh,
- Jksyj = ijks_tem.jksyj,
- Yjsj = DateTime.Now,
- Jsj_id = ijks_tem.jsj_id,
- Kdpxid = lqKdPxmxEntity.Id,
- StoreId = entity.Djmd,
- ItemCategory = lqKdPxmxEntity.ItemCategory,
- ItemId = lqKdPxmxEntity.Px,
- ItemName = lqKdPxmxEntity.Pxmc,
- });
- }
- }
-
- // 收集该品项关联的科技部老师业绩
- if (item.lqKdKjbsyjList != null && item.lqKdKjbsyjList.Any())
- {
- foreach (var ikjbs_tem in item.lqKdKjbsyjList)
- {
- allKjbsyjEntities.Add(new LqKdKjbsyjEntity
- {
- Id = YitIdHelper.NextId().ToString(),
- Glkdbh = entity.Id,
- Kjbls = ikjbs_tem.kjbls,
- Kjblsxm = ikjbs_tem.kjblsxm,
- Kjblszh = ikjbs_tem.kjblszh,
- Kjblsyj = ikjbs_tem.kjblsyj,
- Yjsj = DateTime.Now,
- Kdpxid = lqKdPxmxEntity.Id,
- ItemCategory = lqKdPxmxEntity.ItemCategory,
- ItemId = lqKdPxmxEntity.Px,
- StoreId = entity.Djmd,
- ItemName = lqKdPxmxEntity.Pxmc,
- });
- }
- }
- }
- }
-
- // 批量插入品项明细
- if (allPxmxEntities.Any())
- {
- await _db.Insertable(allPxmxEntities).ExecuteCommandAsync();
- }
- // 批量插入健康师业绩
- if (allJksyjEntities.Any())
- {
- await _db.Insertable(allJksyjEntities).ExecuteCommandAsync();
- }
- // 批量插入科技部老师业绩
- if (allKjbsyjEntities.Any())
- {
- await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync();
- }
- //关闭事务
- _db.CommitTran();
- }
- catch (Exception)
- {
- //回滚事务
- _db.RollbackTran();
- throw NCCException.Oh(ErrorCode.COM1001);
- }
- }
- #endregion
-
- #region 删除开单记录表
- ///
- /// 删除开单记录表
+ /// 删除开单记录表
///
///
[HttpDelete("{id}")]
@@ -1609,317 +1650,83 @@ namespace NCC.Extend.LqKdKdjlb
}
#endregion
- #region 修改开单记录
+ #region 作废开单记录
///
- /// 修改开单记录
+ /// 作废开单记录
///
+ /// 作废开单记录输入
+ /// 无返回值
///
- /// 更新开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
- ///
+ /// 作废指定的开单记录,包括相关的品项明细、业绩记录等
+ ///
/// 示例请求:
/// ```json
/// {
- /// "id": "开单编号",
- /// "djmd": "单据门店",
- /// "jsj": "金三角",
- /// "kdrq": "2025-01-11",
- /// "lqKdPxmxList": [
- /// {
- /// "px": "品项编号",
- /// "pxmc": "品项名称",
- /// "pxjg": 100.00,
- /// "projectNumber": 1,
- /// "sourceType": "购买",
- /// "lqKdJksyjList": [
- /// {
- /// "jks": "健康师",
- /// "jksxm": "健康师姓名",
- /// "jksyj": "100"
- /// }
- /// ]
- /// }
- /// ]
+ /// "id": "123456789",
+ /// "remarks": "客户要求作废此订单"
/// }
/// ```
- ///
+ ///
/// 参数说明:
- /// - id: 开单记录主键ID
- /// - input: 开单记录更新参数,包含品项明细和业绩信息
+ /// - id: 开单记录主键ID(必填)
+ /// - remarks: 作废备注说明
///
- /// 开单记录主键ID
- /// 开单记录更新参数
- /// 无返回值
- /// 更新成功
- /// 参数错误或数据验证失败
+ /// 作废成功
+ /// 参数错误,开单记录ID不能为空
+ /// 开单记录不存在
/// 服务器内部错误
- [HttpPut("UpdateForNoDelete/{id}")]
- public async Task UpdateForNoDelete(string id, [FromBody] LqKdKdjlbUpInput input)
+ [HttpPut("Cancel")]
+ public async Task Cancel(CancelBillingInput input)
{
- var entity = input.Adapt();
- entity.Id = id; // 确保ID正确设置
+ if (string.IsNullOrEmpty(input.Id))
+ {
+ throw NCCException.Oh("开单记录ID不能为空");
+ }
try
{
- //检查开单记录是否可以操作
- var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(id);
+ //开启事务
+ _db.BeginTran();
+ // 检查开单记录是否可以作废
+ var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(input.Id);
if (!canCancel)
{
throw NCCException.Oh(errorMessage);
}
- //开启事务
- _db.BeginTran();
- //查询是否有对应的补缴开单ID
- if (!string.IsNullOrEmpty(entity.SupplementBillingId))
+
+ // 查询开单记录
+ var entity = await _db.Queryable().FirstAsync(p => p.Id == input.Id);
+ if (entity == null)
{
- //查询补缴开单ID
- var supplementBillingEntity = await _db.Queryable().FirstAsync(p => p.Id == entity.SupplementBillingId);//900,900
- if (supplementBillingEntity == null || supplementBillingEntity.IsEffective == StatusEnum.无效.GetHashCode())
- {
- throw NCCException.Oh("补缴开单记录不存在或已作废");
- }
- //查询当前开单已经补缴金额
- var OldSupplementAmount = await _db.Queryable().Where(p => p.Id == id).SumAsync(p => p.SupplementAmount);//900,0
- supplementBillingEntity.PaidDebt = supplementBillingEntity.PaidDebt - OldSupplementAmount + input.supplementAmount;
- await _db.Updateable(supplementBillingEntity).ExecuteCommandAsync();
+ throw NCCException.Oh("开单记录不存在");
}
- // 更新开单记录主表
- await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).IgnoreColumns(x => x.CreateTime).ExecuteCommandAsync();
- //清空原有品项明细
- await _db.Deleteable().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
- //清空原有健康师业绩
- await _db.Deleteable().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
- //清空原有科技部老师业绩
- await _db.Deleteable().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
- //清空原有扣款信息
- await _db.Deleteable().Where(x => x.BillingId == id).ExecuteCommandAsync();
- //循环品相信息
- // 收集所有需要插入的实体,然后批量插入
- var allPxmxEntities = new List();
- var allJksyjEntities = new List();
- var allKjbsyjEntities = new List();
- var allDeductEntities = new List();
- // 处理扣款信息列表
- foreach (var item in input.lqKdKdjlbDeductList)
+ // 标记开单记录为无效,并添加作废备注
+ entity.IsEffective = StatusEnum.无效.GetHashCode();
+ entity.CancelRefRemarks = input.Remarks;
+ entity.UpdateTime = DateTime.Now;
+ await _db.Updateable(entity).ExecuteCommandAsync();
+
+ // 标记对应开单明细表为无效
+ await _db.Updateable().SetColumns(it => new LqKdPxmxEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.Glkdbh == input.Id).ExecuteCommandAsync();
+
+ // 标记健康师业绩为无效
+ await _db.Updateable().SetColumns(it => new LqKdJksyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.Glkdbh == input.Id).ExecuteCommandAsync();
+
+ // 标记科技部老师业绩为无效
+ await _db.Updateable().SetColumns(it => new LqKdKjbsyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.Glkdbh == input.Id).ExecuteCommandAsync();
+
+ // 标记开单_储扣详细表为无效
+ await _db.Updateable().SetColumns(it => new LqKdDeductinfoEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.BillingId == input.Id).ExecuteCommandAsync();
+
+ //如果存在补缴开单ID,则将补缴开单ID的IsEffective设置为无效
+ if (!string.IsNullOrEmpty(entity.SupplementBillingId))
{
- var lqKdDeductEntity = new LqKdDeductinfoEntity
+ var supplementBillingEntity = await _db.Queryable().FirstAsync(p => p.Id == entity.SupplementBillingId);
+ if (supplementBillingEntity != null && supplementBillingEntity.IsEffective == StatusEnum.有效.GetHashCode())
{
- Id = YitIdHelper.NextId().ToString(),
- BillingId = id,
- DeductId = item.DeductId,
- DeductType = item.DeductType,
- Amount = item.Amount,
- ProjectNumber = item.ProjectNumber,
- UnitPrice = item.UnitPrice,
- ItemName = item.ItemName,
- ItemId = item.ItemId,
- IsEffective = StatusEnum.有效.GetHashCode(), // 设置为有效
- CreateTime = DateTime.Now, // 设置创建时间
- ItemCategory = await _db.Queryable().Where(x => x.Id == item.DeductId).Select(x => x.Qt2).FirstAsync(),
- };
- allDeductEntities.Add(lqKdDeductEntity);
- }
- // 处理品项明细列表
- foreach (var item in input.lqKdPxmxList)
- {
- // 创建品项明细实体
- var lqKdPxmxEntity = new LqKdPxmxEntity
- {
- Id = YitIdHelper.NextId().ToString(),
- Glkdbh = id,
- Yjsj = input.kdrq,
- CreateTIme = DateTime.Now,
- MemberId = entity.Kdhy,
- IsEnabled = StatusEnum.有效.GetHashCode(),
- ProjectNumber = item.projectNumber,
- TotalPrice = (decimal)(item.pxjg * item.projectNumber),
- Px = item.px,
- Pxmc = item.pxmc,
- Pxjg = item.pxjg,
- SourceType = item.sourceType,
- ActualPrice = item.actualPrice,
- Remark = item.remark,
- IsEffective = StatusEnum.有效.GetHashCode(),
- ActivityId = input.activityId,
- ItemCategory = await _db.Queryable().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
- };
- allPxmxEntities.Add(lqKdPxmxEntity);
-
- // 收集该品项关联的健康师业绩
- if (item.lqKdJksyjList != null && item.lqKdJksyjList.Any())
- {
- //把jksxm保存到HealthInstructorNames
- foreach (var ijks_tem in item.lqKdJksyjList)
- {
- allJksyjEntities.Add(new LqKdJksyjEntity
- {
- Id = YitIdHelper.NextId().ToString(),
- Glkdbh = id,
- Jks = ijks_tem.jks,
- Jksxm = ijks_tem.jksxm,
- Jkszh = ijks_tem.jkszh,
- Jksyj = ijks_tem.jksyj,
- Yjsj = input.kdrq,
- Jsj_id = ijks_tem.jsj_id,
- Kdpxid = lqKdPxmxEntity.Id,
- IsEffective = StatusEnum.有效.GetHashCode(),
- ActivityId = input.activityId,
- ItemCategory = lqKdPxmxEntity.ItemCategory,
- ItemId = lqKdPxmxEntity.Px,
- });
- }
- }
-
- // 收集该品项关联的科技部老师业绩
- if (item.lqKdKjbsyjList != null && item.lqKdKjbsyjList.Any())
- {
- foreach (var ikjbs_tem in item.lqKdKjbsyjList)
- {
- allKjbsyjEntities.Add(new LqKdKjbsyjEntity
- {
- Id = YitIdHelper.NextId().ToString(),
- Glkdbh = id,
- Kjbls = ikjbs_tem.kjbls,
- Kjblsxm = ikjbs_tem.kjblsxm,
- Kjblszh = ikjbs_tem.kjblszh,
- Kjblsyj = ikjbs_tem.kjblsyj,
- Yjsj = input.kdrq,
- Kdpxid = lqKdPxmxEntity.Id,
- IsEffective = StatusEnum.有效.GetHashCode(),
- ActivityId = input.activityId,
- ItemCategory = lqKdPxmxEntity.ItemCategory,
- ItemId = lqKdPxmxEntity.Px,
- }
- );
- }
- }
- }
-
-
-
- //通过会员id查询会员信息
- var memberInfo = await _db.Queryable().Where(u => u.Id == entity.Kdhy).FirstAsync();
- //通过开单记录表查询这个会员开单金额
- var kdAmount = await _db.Queryable().Where(u => u.Kdhy == entity.Kdhy).SumAsync(u => u.Sfyj);
- //如果开单金额小于500,为散客,如果大于500,为会员
- if (kdAmount < 500)
- {
- memberInfo.Khlx = MemberTypeEnum.散客.GetHashCode().ToString();
- }
- else
- {
- memberInfo.Khlx = MemberTypeEnum.会员.GetHashCode().ToString();
- }
- await _db.Updateable(memberInfo).ExecuteCommandAsync();
- // 批量插入扣款信息
- if (allDeductEntities.Any())
- {
- await _db.Insertable(allDeductEntities).ExecuteCommandAsync();
- }
- // 批量插入品项明细
- if (allPxmxEntities.Any())
- {
- await _db.Insertable(allPxmxEntities).ExecuteCommandAsync();
- }
- // 批量插入健康师业绩
- if (allJksyjEntities.Any())
- {
- await _db.Insertable(allJksyjEntities).ExecuteCommandAsync();
- }
- // 批量插入科技部老师业绩
- if (allKjbsyjEntities.Any())
- {
- await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync();
- }
-
- //关闭事务
- _db.CommitTran();
- }
- catch (Exception ex)
- {
- //回滚事务
- _db.RollbackTran();
- throw NCCException.Oh($"创建开单记录失败: {ex.Message}");
- }
- }
- #endregion
-
- #region 作废开单记录
- ///
- /// 作废开单记录
- ///
- /// 作废开单记录输入
- /// 无返回值
- ///
- /// 作废指定的开单记录,包括相关的品项明细、业绩记录等
- ///
- /// 示例请求:
- /// ```json
- /// {
- /// "id": "123456789",
- /// "remarks": "客户要求作废此订单"
- /// }
- /// ```
- ///
- /// 参数说明:
- /// - id: 开单记录主键ID(必填)
- /// - remarks: 作废备注说明
- ///
- /// 作废成功
- /// 参数错误,开单记录ID不能为空
- /// 开单记录不存在
- /// 服务器内部错误
- [HttpPut("Cancel")]
- public async Task Cancel(CancelBillingInput input)
- {
- if (string.IsNullOrEmpty(input.Id))
- {
- throw NCCException.Oh("开单记录ID不能为空");
- }
- try
- {
- //开启事务
- _db.BeginTran();
- // 检查开单记录是否可以作废
- var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(input.Id);
- if (!canCancel)
- {
- throw NCCException.Oh(errorMessage);
- }
-
- // 查询开单记录
- var entity = await _db.Queryable().FirstAsync(p => p.Id == input.Id);
- if (entity == null)
- {
- throw NCCException.Oh("开单记录不存在");
- }
-
- // 标记开单记录为无效,并添加作废备注
- entity.IsEffective = StatusEnum.无效.GetHashCode();
- entity.CancelRefRemarks = input.Remarks;
- entity.UpdateTime = DateTime.Now;
- await _db.Updateable(entity).ExecuteCommandAsync();
-
- // 标记对应开单明细表为无效
- await _db.Updateable().SetColumns(it => new LqKdPxmxEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.Glkdbh == input.Id).ExecuteCommandAsync();
-
- // 标记健康师业绩为无效
- await _db.Updateable().SetColumns(it => new LqKdJksyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.Glkdbh == input.Id).ExecuteCommandAsync();
-
- // 标记科技部老师业绩为无效
- await _db.Updateable().SetColumns(it => new LqKdKjbsyjEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.Glkdbh == input.Id).ExecuteCommandAsync();
-
- // 标记开单_储扣详细表为无效
- await _db.Updateable().SetColumns(it => new LqKdDeductinfoEntity { IsEffective = StatusEnum.无效.GetHashCode() }).Where(it => it.BillingId == input.Id).ExecuteCommandAsync();
-
- //如果存在补缴开单ID,则将补缴开单ID的IsEffective设置为无效
- if (!string.IsNullOrEmpty(entity.SupplementBillingId))
- {
- var supplementBillingEntity = await _db.Queryable().FirstAsync(p => p.Id == entity.SupplementBillingId);
- if (supplementBillingEntity != null && supplementBillingEntity.IsEffective == StatusEnum.有效.GetHashCode())
- {
- supplementBillingEntity.PaidDebt = supplementBillingEntity.PaidDebt - entity.SupplementAmount;
- await _db.Updateable(supplementBillingEntity).ExecuteCommandAsync();
- }
+ supplementBillingEntity.PaidDebt = supplementBillingEntity.PaidDebt - entity.SupplementAmount;
+ await _db.Updateable(supplementBillingEntity).ExecuteCommandAsync();
+ }
}
//关闭事务
_db.CommitTran();
@@ -2325,7 +2132,7 @@ namespace NCC.Extend.LqKdKdjlb
}
#endregion
- #region 根据会员id获取会员的开单品项列表
+ #region 根据会员id获取会员的开单品项列表
///
/// 根据会员id获取会员的开单品项列表
///
@@ -2539,9 +2346,18 @@ namespace NCC.Extend.LqKdKdjlb
input.EndTime = DateTime.Now.AddDays(DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month));
}
+ // 构建开单记录查询条件
+ var billingQuery = _db.Queryable()
+ .Where(w => w.Djmd == input.StoreId && w.Kdrq >= input.StartTime && w.Kdrq <= input.EndTime && w.IsEffective == StatusEnum.有效.GetHashCode());
+
+ // 客户筛选
+ if (!string.IsNullOrEmpty(input.MemberId))
+ {
+ billingQuery = billingQuery.Where(w => w.Kdhy == input.MemberId);
+ }
+
// 查询开单记录
- var billingRecords = await _db.Queryable()
- .Where(w => w.Djmd == input.StoreId && w.Kdrq >= input.StartTime && w.Kdrq <= input.EndTime && w.IsEffective == StatusEnum.有效.GetHashCode())
+ var billingRecords = await billingQuery
.Select(it => new
{
id = it.Id,
@@ -2594,9 +2410,24 @@ namespace NCC.Extend.LqKdKdjlb
var billingIds = billingRecords.Select(x => x.id).ToList();
+ // 构建品项明细查询条件
+ var itemDetailsQuery = _db.Queryable()
+ .Where(w => billingIds.Contains(w.Glkdbh) && w.IsEffective == StatusEnum.有效.GetHashCode());
+
+ // 品项分类筛选
+ if (!string.IsNullOrEmpty(input.ItemCategory))
+ {
+ itemDetailsQuery = itemDetailsQuery.Where(w => w.ItemCategory == input.ItemCategory);
+ }
+
+ // 品项筛选
+ if (!string.IsNullOrEmpty(input.ItemId))
+ {
+ itemDetailsQuery = itemDetailsQuery.Where(w => w.Px == input.ItemId);
+ }
+
// 查询品项明细,按F_SourceType分类
- var itemDetails = await _db.Queryable()
- .Where(w => billingIds.Contains(w.Glkdbh) && w.IsEffective == StatusEnum.有效.GetHashCode())
+ var itemDetails = await itemDetailsQuery
.Select(it => new
{
id = it.Id,
@@ -2618,9 +2449,30 @@ namespace NCC.Extend.LqKdKdjlb
var giftedItems = itemDetails.Where(x => x.sourceType == "赠送").ToList();
var experienceItems = itemDetails.Where(x => x.sourceType == "体验").ToList();
+ // 构建健康师业绩查询条件
+ var healthTeacherQuery = _db.Queryable()
+ .Where(w => billingIds.Contains(w.Glkdbh) && w.IsEffective == StatusEnum.有效.GetHashCode());
+
+ // 健康师筛选
+ if (!string.IsNullOrEmpty(input.HealthCoachId))
+ {
+ healthTeacherQuery = healthTeacherQuery.Where(w => w.Jks == input.HealthCoachId || w.Jkszh == input.HealthCoachId);
+ }
+
+ // 品项分类筛选(健康师业绩表)
+ if (!string.IsNullOrEmpty(input.ItemCategory))
+ {
+ healthTeacherQuery = healthTeacherQuery.Where(w => w.ItemCategory == input.ItemCategory);
+ }
+
+ // 品项筛选(健康师业绩表)
+ if (!string.IsNullOrEmpty(input.ItemId))
+ {
+ healthTeacherQuery = healthTeacherQuery.Where(w => w.ItemId == input.ItemId);
+ }
+
// 查询健康师业绩数据
- var healthTeacherData = await _db.Queryable()
- .Where(w => billingIds.Contains(w.Glkdbh) && w.IsEffective == StatusEnum.有效.GetHashCode())
+ var healthTeacherData = await healthTeacherQuery
.Select(it => new
{
id = it.Id,
@@ -2750,31 +2602,266 @@ namespace NCC.Extend.LqKdKdjlb
}
#endregion
- #region 私有方法
+ #region 获取门店某个时间段的开单记录汇总信息(备份)
///
- /// 检查开单记录是否可以操作
+ /// 获取门店某个时间段的开单记录汇总信息(备份)
///
- /// 开单记录ID
- /// 是否可以作废
- private async Task<(bool canCancel, string errorMessage)> CheckBillingCanCancelAsync(string billingId)
+ /// 查询参数
+ /// 开单记录汇总信息
+ [HttpGet("GetBillingRecordSummaryByStoreId_bak")]
+ public async Task GetBillingRecordSummaryByStoreId_bak([FromQuery] BillingRecordSummaryQueryInput input)
{
try
{
- // 查询开单记录
- var entity = await _db.Queryable().FirstAsync(p => p.Id == billingId);
- if (entity == null)
+ // 验证参数
+ if (string.IsNullOrEmpty(input.StoreId))
{
- return (false, "开单记录不存在");
+ throw NCCException.Oh("门店ID不能为空");
}
- // 检查是否已经作废
- if (entity.IsEffective == StatusEnum.无效.GetHashCode())
+
+ // 如果开始时间和结束时间为空,就默认是当月
+ if (input.StartTime == null || input.EndTime == null)
{
- return (false, "该开单记录已经作废");
+ input.StartTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
+ input.EndTime = DateTime.Now.AddDays(DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month));
}
- // 判断是否有对应的补缴记录
- var qkbjList = await _db.Queryable().Where(p => p.SupplementBillingId == billingId && p.IsEffective == StatusEnum.有效.GetHashCode()).ToListAsync();
- if (qkbjList.Any())
- {
+
+ // 查询开单记录
+ var billingRecords = await _db.Queryable()
+ .Where(w => w.Djmd == input.StoreId && w.Kdrq >= input.StartTime && w.Kdrq <= input.EndTime && w.IsEffective == StatusEnum.有效.GetHashCode())
+ .Select(it => new
+ {
+ id = it.Id,
+ djmd = it.Djmd,
+ jsj = it.Jsj,
+ kdrq = it.Kdrq,
+ gjlx = it.Gjlx,
+ zdyj = it.Zdyj,
+ sfyj = it.Sfyj,
+ qk = it.Qk,
+ kdhyc = SqlFunc.Subqueryable().Where(x => x.Id == it.Kdhy).Select(x => x.Khmc),
+ kdhysjh = SqlFunc.Subqueryable().Where(x => x.Id == it.Kdhy).Select(x => x.Sjh),
+ fkfs = it.Fkfs, // 付款方式
+ khly = it.Khly, // 客户来源
+ bz = it.Bz, // 备注
+ createTime = it.CreateTime
+ })
+ .ToListAsync();
+
+ if (!billingRecords.Any())
+ {
+ return new
+ {
+ success = true,
+ data = new
+ {
+ billingSummary = new
+ {
+ totalCount = 0,
+ totalAmount = 0,
+ totalPaidAmount = 0,
+ totalDebt = 0,
+ billingRecords = new List
/// 查询参数
/// 健康师统计数据列表
@@ -3305,7 +3395,357 @@ namespace NCC.Extend.LqKdKdjlb
CAST(COALESCE(consume_stats.ProjectCount, 0) AS DECIMAL(18,2)) as ProjectCount,
-- 退卡金额
- COALESCE(refund_stats.RefundAmount, 0) as RefundAmount
+ COALESCE(refund_stats.RefundAmount, 0) as RefundAmount,
+
+ -- 手工费相关统计
+ COALESCE(consume_stats.LaborCost, 0) as LaborCost,
+ COALESCE(consume_stats.OriginalLaborCost, 0) as OriginalLaborCost,
+ COALESCE(consume_stats.OvertimeLaborCost, 0) as OvertimeLaborCost
+
+ FROM BASE_USER u
+ LEFT JOIN lq_mdxx md ON u.F_MDID = md.F_Id
+ LEFT JOIN base_organize dept ON md.syb = dept.F_Id
+
+ -- 邀约统计子查询
+ LEFT JOIN (
+ SELECT
+ yyr as EmployeeId,
+ COUNT(DISTINCT yykh) as InviteCount
+ FROM lq_yaoyjl
+ WHERE yyr IS NOT NULL
+ AND F_CreateTime >= @startTime
+ AND F_CreateTime <= @endTime
+ GROUP BY yyr
+ ) invite_stats ON u.F_Id = invite_stats.EmployeeId
+
+ -- 预约统计子查询
+ LEFT JOIN (
+ SELECT
+ yyr as EmployeeId,
+ COUNT(DISTINCT gk) as AppointmentCount
+ FROM lq_yyjl
+ WHERE yyr IS NOT NULL
+ AND F_CreateTime >= @startTime
+ AND F_CreateTime <= @endTime
+ GROUP BY yyr
+ ) appointment_stats ON u.F_Id = appointment_stats.EmployeeId
+
+ -- 到店统计子查询
+ LEFT JOIN (
+ SELECT
+ yyr as EmployeeId,
+ COUNT(DISTINCT gk) as VisitCount
+ FROM lq_yyjl
+ WHERE yyr IS NOT NULL
+ AND F_Status = '已确认'
+ AND F_CreateTime >= @startTime
+ AND F_CreateTime <= @endTime
+ GROUP BY yyr
+ ) visit_stats ON u.F_Id = visit_stats.EmployeeId
+
+ -- 开单统计子查询
+ LEFT JOIN (
+ SELECT
+ jkszh as EmployeeId,
+ COUNT(DISTINCT glkdbh) as BillingCount,
+ SUM(CAST(jksyj AS DECIMAL(18,2))) as BillingAmount
+ FROM lq_kd_jksyj
+ WHERE jkszh IS NOT NULL
+ AND F_IsEffective = 1
+ AND yjsj >= @startTime
+ AND yjsj <= @endTime
+ GROUP BY jkszh
+ ) billing_stats ON u.F_Id = billing_stats.EmployeeId
+
+ -- 消耗统计子查询
+ LEFT JOIN (
+ SELECT
+ jksyj.jks as EmployeeId,
+ SUM(jksyj.jksyj) as ConsumeAmount,
+ CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as ProjectCount,
+ COALESCE(SUM(jksyj.F_LaborCost), 0) as LaborCost,
+ COALESCE(SUM(jksyj.F_OriginalLaborCost), 0) as OriginalLaborCost,
+ COALESCE(SUM(jksyj.F_OvertimeLaborCost), 0) as OvertimeLaborCost
+ FROM lq_xh_jksyj jksyj
+ INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
+ WHERE jksyj.jks IS NOT NULL
+ AND jksyj.F_IsEffective = 1
+ AND hyhk.F_IsEffective = 1
+ AND hyhk.hksj >= @startTime
+ AND hyhk.hksj <= @endTime
+ GROUP BY jksyj.jks
+ ) consume_stats ON u.F_Id = consume_stats.EmployeeId
+
+ -- 有效人头统计子查询(从人次记录表获取,按月份+客户+数量去重后累加数量,F_HasBilling=1)
+ LEFT JOIN (
+ SELECT
+ F_PersonId as EmployeeId,
+ CAST(COALESCE(SUM(F_Quantity), 0) AS DECIMAL(18,2)) as HeadCount
+ FROM (
+ SELECT
+ F_PersonId,
+ F_WorkMonth,
+ F_MemberId,
+ F_Quantity
+ FROM lq_person_times_record
+ WHERE F_PersonId IS NOT NULL
+ AND F_IsEffective = 1
+ AND F_PersonType = '健康师'
+ AND F_HasBilling = 1
+ AND F_WorkDate >= DATE(@startTime)
+ AND F_WorkDate <= DATE(@endTime)
+ GROUP BY F_PersonId, F_WorkMonth, F_MemberId, F_Quantity
+ ) as distinct_headcount
+ GROUP BY F_PersonId
+ ) headcount_stats ON u.F_Id = headcount_stats.EmployeeId
+
+ -- 有效人次统计子查询(从人次记录表获取,按日期+客户+数量去重后累加数量,F_HasBilling=1)
+ LEFT JOIN (
+ SELECT
+ F_PersonId as EmployeeId,
+ CAST(COALESCE(SUM(F_Quantity), 0) AS DECIMAL(18,2)) as PersonCount
+ FROM (
+ SELECT
+ F_PersonId,
+ F_WorkDate,
+ F_MemberId,
+ F_Quantity
+ FROM lq_person_times_record
+ WHERE F_PersonId IS NOT NULL
+ AND F_IsEffective = 1
+ AND F_PersonType = '健康师'
+ AND F_HasBilling = 1
+ AND F_WorkDate >= DATE(@startTime)
+ AND F_WorkDate <= DATE(@endTime)
+ GROUP BY F_PersonId, F_WorkDate, F_MemberId, F_Quantity
+ ) as distinct_personcount
+ GROUP BY F_PersonId
+ ) personcount_stats ON u.F_Id = personcount_stats.EmployeeId
+
+ -- 无效人头统计子查询(从人次记录表获取,按月份+客户+数量去重后累加数量,F_HasBilling=0)
+ LEFT JOIN (
+ SELECT
+ F_PersonId as EmployeeId,
+ CAST(COALESCE(SUM(F_Quantity), 0) AS DECIMAL(18,2)) as HeadCount
+ FROM (
+ SELECT
+ F_PersonId,
+ F_WorkMonth,
+ F_MemberId,
+ F_Quantity
+ FROM lq_person_times_record
+ WHERE F_PersonId IS NOT NULL
+ AND F_IsEffective = 1
+ AND F_PersonType = '健康师'
+ AND F_HasBilling = 0
+ AND F_WorkDate >= DATE(@startTime)
+ AND F_WorkDate <= DATE(@endTime)
+ GROUP BY F_PersonId, F_WorkMonth, F_MemberId, F_Quantity
+ ) as distinct_headcount
+ GROUP BY F_PersonId
+ ) invalid_headcount_stats ON u.F_Id = invalid_headcount_stats.EmployeeId
+
+ -- 无效人次统计子查询(从人次记录表获取,按日期+客户+数量去重后累加数量,F_HasBilling=0)
+ LEFT JOIN (
+ SELECT
+ F_PersonId as EmployeeId,
+ CAST(COALESCE(SUM(F_Quantity), 0) AS DECIMAL(18,2)) as PersonCount
+ FROM (
+ SELECT
+ F_PersonId,
+ F_WorkDate,
+ F_MemberId,
+ F_Quantity
+ FROM lq_person_times_record
+ WHERE F_PersonId IS NOT NULL
+ AND F_IsEffective = 1
+ AND F_PersonType = '健康师'
+ AND F_HasBilling = 0
+ AND F_WorkDate >= DATE(@startTime)
+ AND F_WorkDate <= DATE(@endTime)
+ GROUP BY F_PersonId, F_WorkDate, F_MemberId, F_Quantity
+ ) as distinct_personcount
+ GROUP BY F_PersonId
+ ) invalid_personcount_stats ON u.F_Id = invalid_personcount_stats.EmployeeId
+
+ -- 退卡统计子查询
+ LEFT JOIN (
+ SELECT
+ jkszh as EmployeeId,
+ SUM(CAST(jksyj AS DECIMAL(18,2))) as RefundAmount
+ FROM lq_hytk_jksyj
+ WHERE jkszh IS NOT NULL
+ AND F_IsEffective = 1
+ AND tksj >= @startTime
+ AND tksj <= @endTime
+ GROUP BY jkszh
+ ) refund_stats ON u.F_Id = refund_stats.EmployeeId
+
+ WHERE u.F_GW = '健康师'
+ ";
+
+ // 添加条件过滤
+ var conditions = new List();
+ var parameters = new List
+ {
+ new SugarParameter("@startTime", startTime),
+ new SugarParameter("@endTime", endTime)
+ };
+
+ if (!string.IsNullOrEmpty(input.DepartmentId))
+ {
+ conditions.Add("md.syb = @departmentId");
+ parameters.Add(new SugarParameter("@departmentId", input.DepartmentId));
+ }
+
+ if (!string.IsNullOrEmpty(input.StoreId))
+ {
+ conditions.Add("u.F_MDID = @storeId");
+ parameters.Add(new SugarParameter("@storeId", input.StoreId));
+ }
+
+ if (!string.IsNullOrEmpty(input.EmployeeName))
+ {
+ conditions.Add("u.F_REALNAME LIKE @employeeName");
+ parameters.Add(new SugarParameter("@employeeName", $"%{input.EmployeeName}%"));
+ }
+
+ if (conditions.Any())
+ {
+ sql += " AND " + string.Join(" AND ", conditions);
+ }
+
+ sql += " ORDER BY u.F_REALNAME";
+
+ // 执行查询
+ var allData = await _db.Ado.SqlQueryAsync(sql, parameters);
+
+ // 手动分页
+ var totalCount = allData.Count;
+ var pagedData = allData
+ .Skip((input.currentPage - 1) * input.pageSize)
+ .Take(input.pageSize)
+ .ToList();
+
+ // 直接返回分页结果
+ return new
+ {
+ list = pagedData,
+ pagination = new
+ {
+ pageIndex = input.currentPage,
+ pageSize = input.pageSize,
+ totalCount = totalCount
+ }
+ };
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取健康师统计数据失败");
+ throw NCCException.Oh($"获取健康师统计数据失败:{ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 门店整体统计表(备份)
+ ///
+ /// 门店整体统计表
+ ///
+ ///
+ /// 统计每个健康师在指定时间周期内的各项数据指标
+ /// 包括:邀约人数、预约人数、到店人数、开单人数、开单金额、消耗金额、人头、人次、消耗项目数
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "startTime": "2025-10-01",
+ /// "endTime": "2025-10-31",
+ /// "departmentId": "部门ID",
+ /// "storeId": "门店ID",
+ /// "employeeName": "健康师姓名"
+ /// }
+ /// ```
+ ///
+ /// 参数说明:
+ /// - startTime: 开始时间(可选,默认为当月1号)
+ /// - endTime: 结束时间(可选,默认为当前时间)
+ /// - departmentId: 事业部ID(可选)
+ /// - storeId: 门店ID(可选)
+ /// - employeeName: 健康师姓名(可选)
+ ///
+ /// 返回字段说明:
+ /// - EmployeeId: 健康师ID
+ /// - EmployeeName: 健康师姓名
+ /// - StoreId: 门店ID
+ /// - StoreName: 门店名称
+ /// - DepartmentId: 事业部ID
+ /// - DepartmentName: 事业部名称
+ /// - InviteCount: 邀约人数(按客户去重)
+ /// - AppointmentCount: 预约人数(按客户去重,无论预约状态)
+ /// - VisitCount: 到店人数(按客户去重,仅统计状态为'已确认'的预约)
+ /// - BillingCount: 开单人数(按开单记录去重)
+ /// - BillingAmount: 开单金额(开单业绩总金额)
+ /// - ConsumeAmount: 消耗金额(消耗业绩总金额)
+ /// - HeadCount: 人头(按客户去重)
+ /// - PersonCount: 人次(按客户+日期去重,同一客户不同天算多次)
+ /// - ProjectCount: 消耗项目数(项目总次数)
+ /// - RefundAmount: 退卡金额(健康师退卡业绩总金额)
+ /// - LaborCost: 手工费(消耗时的手工费总金额)
+ /// - OriginalLaborCost: 原始手工费(消耗时的原始手工费总金额)
+ /// - OvertimeLaborCost: 加班手工费(消耗时的加班手工费总金额)
+ ///
+ /// 查询参数
+ /// 健康师统计数据列表
+ /// 成功返回统计数据
+ /// 参数错误
+ /// 服务器错误
+ [HttpGet("get-health-coach-statistics_bak")]
+ public async Task GetHealthCoachStatistics_bak([FromQuery] HealthCoachStatisticsQueryInput input)
+ {
+ try
+ {
+ // 设置默认时间范围(如果未提供,默认为当月)
+ var startTime = input.StartTime ?? new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
+ var endTime = input.EndTime ?? DateTime.Now;
+
+ // 构建SQL查询
+ var sql = $@"
+ SELECT
+ u.F_Id as EmployeeId,
+ u.F_REALNAME as EmployeeName,
+ u.F_MDID as StoreId,
+ md.dm as StoreName,
+ md.syb as DepartmentId,
+ dept.F_FullName as DepartmentName,
+
+ -- 邀约人数
+ COALESCE(invite_stats.InviteCount, 0) as InviteCount,
+
+ -- 预约人数(无论状态)
+ COALESCE(appointment_stats.AppointmentCount, 0) as AppointmentCount,
+
+ -- 到店人数(已确认状态)
+ COALESCE(visit_stats.VisitCount, 0) as VisitCount,
+
+ -- 开单人数和金额
+ COALESCE(billing_stats.BillingCount, 0) as BillingCount,
+ COALESCE(billing_stats.BillingAmount, 0) as BillingAmount,
+
+ -- 消耗相关统计
+ COALESCE(consume_stats.ConsumeAmount, 0) as ConsumeAmount,
+ CAST(COALESCE(headcount_stats.HeadCount, 0) AS DECIMAL(18,2)) as HeadCount,
+ CAST(COALESCE(personcount_stats.PersonCount, 0) AS DECIMAL(18,2)) as PersonCount,
+ CAST(COALESCE(invalid_headcount_stats.HeadCount, 0) AS DECIMAL(18,2)) as InvalidHeadCount,
+ CAST(COALESCE(invalid_personcount_stats.PersonCount, 0) AS DECIMAL(18,2)) as InvalidPersonCount,
+ CAST(COALESCE(consume_stats.ProjectCount, 0) AS DECIMAL(18,2)) as ProjectCount,
+
+ -- 退卡金额
+ COALESCE(refund_stats.RefundAmount, 0) as RefundAmount,
+
+ -- 手工费相关统计
+ COALESCE(consume_stats.LaborCost, 0) as LaborCost,
+ COALESCE(consume_stats.OriginalLaborCost, 0) as OriginalLaborCost,
+ COALESCE(consume_stats.OvertimeLaborCost, 0) as OvertimeLaborCost
FROM BASE_USER u
LEFT JOIN lq_mdxx md ON u.F_MDID = md.F_Id
@@ -3365,17 +3805,20 @@ namespace NCC.Extend.LqKdKdjlb
-- 消耗统计子查询
LEFT JOIN (
SELECT
- jksyj.jkszh as EmployeeId,
+ jksyj.jks as EmployeeId,
SUM(jksyj.jksyj) as ConsumeAmount,
- CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as ProjectCount
+ CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as ProjectCount,
+ COALESCE(SUM(jksyj.F_LaborCost), 0) as LaborCost,
+ COALESCE(SUM(jksyj.F_OriginalLaborCost), 0) as OriginalLaborCost,
+ COALESCE(SUM(jksyj.F_OvertimeLaborCost), 0) as OvertimeLaborCost
FROM lq_xh_jksyj jksyj
INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
- WHERE jksyj.jkszh IS NOT NULL
+ WHERE jksyj.jks IS NOT NULL
AND jksyj.F_IsEffective = 1
AND hyhk.F_IsEffective = 1
AND hyhk.hksj >= @startTime
AND hyhk.hksj <= @endTime
- GROUP BY jksyj.jkszh
+ GROUP BY jksyj.jks
) consume_stats ON u.F_Id = consume_stats.EmployeeId
-- 有效人头统计子查询(从人次记录表获取,按月份+客户+数量去重后累加数量,F_HasBilling=1)
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqProductService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqProductService.cs
index fad69fd..7f1a9ee 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqProductService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqProductService.cs
@@ -254,6 +254,7 @@ namespace NCC.Extend
var productIds = data.list.Select(x => x.id).ToList();
if (productIds.Any())
{
+ // 查询总库存数量
var inventoryDict = await _db.Queryable()
.Where(x => productIds.Contains(x.ProductId) && x.IsEffective == StatusEnum.有效.GetHashCode())
.GroupBy(x => x.ProductId)
@@ -262,10 +263,21 @@ namespace NCC.Extend
var inventoryDictMap = inventoryDict.ToDictionary(k => k.ProductId, v => v.TotalQuantity.HasValue ? (int)v.TotalQuantity.Value : 0);
- // 填充库存数量
+ // 查询已使用数量
+ var usageDict = await _db.Queryable()
+ .Where(x => productIds.Contains(x.ProductId) && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .GroupBy(x => x.ProductId)
+ .Select(x => new { ProductId = x.ProductId, TotalUsage = SqlFunc.AggregateSum((decimal?)x.UsageQuantity) })
+ .ToListAsync();
+
+ var usageDictMap = usageDict.ToDictionary(k => k.ProductId, v => v.TotalUsage.HasValue ? (int)v.TotalUsage.Value : 0);
+
+ // 填充库存数量(总库存减去已使用数量)
foreach (var item in data.list)
{
- item.currentInventory = inventoryDictMap.ContainsKey(item.id) ? inventoryDictMap[item.id] : 0;
+ var totalInventory = inventoryDictMap.ContainsKey(item.id) ? inventoryDictMap[item.id] : 0;
+ var totalUsage = usageDictMap.ContainsKey(item.id) ? usageDictMap[item.id] : 0;
+ item.currentInventory = totalInventory - totalUsage;
}
}
@@ -427,6 +439,7 @@ namespace NCC.Extend
var productIds = products.Select(x => x.id).ToList();
if (productIds.Any())
{
+ // 查询总库存数量
var inventoryDict = await _db.Queryable()
.Where(x => productIds.Contains(x.ProductId) && x.IsEffective == StatusEnum.有效.GetHashCode())
.GroupBy(x => x.ProductId)
@@ -435,10 +448,21 @@ namespace NCC.Extend
var inventoryDictMap = inventoryDict.ToDictionary(k => k.ProductId, v => v.TotalQuantity.HasValue ? (int)v.TotalQuantity.Value : 0);
- // 填充库存数量
+ // 查询已使用数量
+ var usageDict = await _db.Queryable()
+ .Where(x => productIds.Contains(x.ProductId) && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .GroupBy(x => x.ProductId)
+ .Select(x => new { ProductId = x.ProductId, TotalUsage = SqlFunc.AggregateSum((decimal?)x.UsageQuantity) })
+ .ToListAsync();
+
+ var usageDictMap = usageDict.ToDictionary(k => k.ProductId, v => v.TotalUsage.HasValue ? (int)v.TotalUsage.Value : 0);
+
+ // 填充库存数量(总库存减去已使用数量)
foreach (var item in products)
{
- item.currentInventory = inventoryDictMap.ContainsKey(item.id) ? inventoryDictMap[item.id] : 0;
+ var totalInventory = inventoryDictMap.ContainsKey(item.id) ? inventoryDictMap[item.id] : 0;
+ var totalUsage = usageDictMap.ContainsKey(item.id) ? usageDictMap[item.id] : 0;
+ item.currentInventory = totalInventory - totalUsage;
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
new file mode 100644
index 0000000..3c3a52b
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs
@@ -0,0 +1,486 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using NCC.Common.Enum;
+using NCC.Common.Filter;
+using NCC.Common.Helper;
+using NCC.Dependency;
+using NCC.DynamicApiController;
+using NCC.Extend.Entitys.Dto.LqSalary;
+using Yitter.IdGenerator;
+using NCC.Extend.Entitys.lq_attendance_summary;
+using NCC.Extend.Entitys.lq_jinsanjiao_user;
+using NCC.Extend.Entitys.lq_kd_jksyj;
+using NCC.Extend.Entitys.lq_kd_kdjlb;
+using NCC.Extend.Entitys.lq_md_target;
+using NCC.Extend.Entitys.lq_person_times_record;
+using NCC.Extend.Entitys.lq_salary_statistics;
+using NCC.Extend.Entitys.lq_xh_jksyj;
+using NCC.Extend.Entitys.lq_ycsd_jsj;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace NCC.Extend
+{
+ ///
+ /// 薪酬服务
+ ///
+ [ApiDescriptionSettings(Tag = "薪酬服务", Name = "LqSalary", Order = 300)]
+ [Route("api/Extend/[controller]")]
+ public class LqSalaryService : IDynamicApiController, ITransient
+ {
+ private readonly ISqlSugarClient _db;
+
+ ///
+ /// 初始化一个类型的新实例
+ ///
+ public LqSalaryService(ISqlSugarClient db)
+ {
+ _db = db;
+ }
+
+ ///
+ /// 获取健康师工资列表
+ ///
+ /// 查询参数
+ /// 健康师工资分页列表
+ [HttpGet("health-coach")]
+ public async Task> GetHealthCoachSalaryList([FromQuery] HealthCoachSalaryInput input)
+ {
+ var monthStr = $"{input.Year}{input.Month:D2}";
+
+ // 1. 检查当月是否已生成工资数据
+ var exists = await _db.Queryable()
+ .AnyAsync(x => x.StatisticsMonth == monthStr);
+
+ // 2. 如果没有数据,则进行计算
+ if (!exists)
+ {
+ await CalculateHealthCoachSalary(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.EmployeeId.Contains(input.Keyword));
+ }
+
+ var list = await query.Select(x => new HealthCoachSalaryOutput
+ {
+ Id = x.Id,
+ StoreName = x.StoreName,
+ EmployeeName = x.EmployeeName,
+ Position = x.Position,
+ GoldTriangleTeam = x.GoldTriangleTeam,
+ TotalPerformance = x.TotalPerformance,
+ BasePerformance = x.BasePerformance,
+ CooperationPerformance = x.CooperationPerformance,
+ RewardPerformance = x.RewardPerformance,
+ Consumption = x.Consumption,
+ ProjectCount = x.ProjectCount,
+ CustomerCount = x.CustomerCount,
+ WorkingDays = x.WorkingDays,
+ HealthCoachBaseSalary = x.HealthCoachBaseSalary,
+ TotalCommission = x.TotalCommission,
+ HandworkFee = x.HandworkFee,
+ TotalSubsidy = x.TotalSubsidy,
+ TotalDeduction = x.TotalDeduction,
+ ActualSalary = x.ActualSalary,
+ IsLocked = x.IsLocked,
+ UpdateTime = x.UpdateTime
+ })
+ .ToPagedListAsync(input.currentPage, input.pageSize);
+
+ return PageResult.SqlSugarPageResult(list);
+ }
+
+ ///
+ /// 计算健康师工资
+ ///
+ /// 年份
+ /// 月份
+ ///
+ [HttpPost("calculate/health-coach")]
+ public async Task CalculateHealthCoachSalary(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_kd_jksyj)
+ var performanceList = await _db.Queryable()
+ .Where(x => x.Yjsj >= startDate && x.Yjsj <= endDate.AddDays(1) && x.IsEffective == 1)
+ .ToListAsync();
+
+ // 1.1.1 获取关联的开单记录(用于获取 sfskdd)
+ var billingIds = performanceList.Select(x => x.Glkdbh).Distinct().ToList();
+ var billingDict = await _db.Queryable()
+ .Where(x => billingIds.Contains(x.Id))
+ .ToDictionaryAsync(x => x.Id, x => x.Sfskdd);
+
+ // 1.1.2 组合数据
+ var performanceData = performanceList.Select(p => new
+ {
+ p.Jks,
+ p.Jksxm,
+ p.StoreId,
+ p.Jksyj,
+ p.ItemCategory,
+ Sfskdd = billingDict.ContainsKey(p.Glkdbh) ? billingDict[p.Glkdbh] : null
+ }).ToList();
+
+ // 1.2 消耗数据 (lq_xh_jksyj)
+ var consumptionList = await _db.Queryable()
+ .Where(x => x.Yjsj >= startDate && x.Yjsj <= endDate.AddDays(1) && x.IsEffective == 1)
+ .ToListAsync();
+
+ // 1.3 考勤数据 (lq_attendance_summary)
+ var attendanceList = await _db.Queryable()
+ .Where(x => x.Year == year && x.Month == month && x.IsEffective == 1)
+ .ToListAsync();
+
+ // 1.4 战队成员及顾问信息 (lq_jinsanjiao_user + lq_ycsd_jsj)
+ var teamUserList = await _db.Queryable()
+ .Where(x => x.Month == monthStr && x.DeleteMark == 0)
+ .ToListAsync();
+
+ // 1.4.1 获取战队信息
+ var teamIds = teamUserList.Select(x => x.JsjId).Distinct().ToList();
+ var teamList = await _db.Queryable()
+ .Where(x => teamIds.Contains(x.Id))
+ .ToListAsync();
+ var teamDict = teamList.ToDictionary(x => x.Id, x => x.Jsj);
+
+ // 1.4.2 组合数据
+ var teamMembers = teamUserList.Select(user => new
+ {
+ user.UserId,
+ user.IsLeader,
+ TeamId = user.JsjId,
+ TeamName = teamDict.ContainsKey(user.JsjId) ? teamDict[user.JsjId] : (string)null
+ }).ToList();
+
+ // 1.5 到店人头 (lq_person_times_record)
+ // 统计每个健康师的去重会员数
+ var headcountList = await _db.Queryable()
+ .Where(x => x.WorkMonth == monthStr && x.IsEffective == 1)
+ .GroupBy(x => x.PersonId)
+ .Select(x => new { PersonId = x.PersonId, Count = SqlFunc.AggregateDistinctCount(x.MemberId) })
+ .ToListAsync();
+
+ // 1.6 门店生命线 (lq_md_target)
+ var storeTargets = await _db.Queryable()
+ .Where(x => x.Month == monthStr)
+ .ToListAsync();
+
+ // 2. 聚合每个健康师的数据对象
+ var employeeStats = new Dictionary();
+
+ // 获取所有涉及的健康师ID
+ var allEmployeeIds = performanceData.Select(x => x.Jks)
+ .Union(consumptionList.Select(x => x.Jks))
+ .Union(attendanceList.Select(x => x.UserId))
+ .Union(teamMembers.Select(x => x.UserId))
+ .Where(x => !string.IsNullOrEmpty(x))
+ .Distinct()
+ .ToList();
+
+ foreach (var empId in allEmployeeIds)
+ {
+ var salary = new LqSalaryStatisticsEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ EmployeeId = empId,
+ StatisticsMonth = monthStr,
+ CreateTime = DateTime.Now,
+ UpdateTime = DateTime.Now,
+ IsLocked = 0
+ };
+
+ // 填充基础信息 (姓名、门店)
+ var perfRecord = performanceData.FirstOrDefault(x => x.Jks == empId);
+ var consRecord = consumptionList.FirstOrDefault(x => x.Jks == empId);
+
+ if (perfRecord != null)
+ {
+ salary.EmployeeName = perfRecord.Jksxm;
+ salary.StoreId = perfRecord.StoreId;
+ }
+ else if (consRecord != null)
+ {
+ salary.EmployeeName = consRecord.Jksxm;
+ salary.StoreId = consRecord.StoreId;
+ }
+
+ // 填充门店名称
+ if (!string.IsNullOrEmpty(salary.StoreId))
+ {
+ // 这里简单处理,实际可能需要缓存门店列表
+ // salary.StoreName = ...
+ }
+
+ // 2.1 计算个人业绩
+ var myPerf = performanceData.Where(x => x.Jks == empId).ToList();
+ salary.BasePerformance = myPerf.Where(x => x.ItemCategory == "基础业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
+ salary.CooperationPerformance = myPerf.Where(x => x.ItemCategory == "合作业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
+ salary.TotalPerformance = myPerf.Sum(x => decimal.Parse(x.Jksyj ?? "0"));
+
+ // 新客与升单业绩
+ salary.NewCustomerPerformance = myPerf.Where(x => x.Sfskdd == "是").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
+ salary.UpgradePerformance = myPerf.Where(x => x.Sfskdd == "否").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
+
+ // 2.2 计算消耗和项目数
+ var myCons = consumptionList.Where(x => x.Jks == empId).ToList();
+ salary.Consumption = myCons.Sum(x => x.Jksyj ?? 0);
+ salary.ProjectCount = myCons.Sum(x => x.KdpxNumber ?? 0);
+ salary.HandworkFee = myCons.Sum(x => x.LaborCost ?? 0); // 使用 F_LaborCost
+
+ // 2.3 考勤数据
+ var myAtt = attendanceList.FirstOrDefault(x => x.UserId == empId);
+ salary.WorkingDays = myAtt?.WorkDays ?? 0;
+ salary.LeaveDays = myAtt?.LeaveDays ?? 0;
+
+ // 2.4 到店人头
+ var myHeadcount = headcountList.FirstOrDefault(x => x.PersonId == empId);
+ salary.CustomerCount = myHeadcount?.Count ?? 0;
+
+ // 2.5 战队信息 (初始)
+ var myTeam = teamMembers.FirstOrDefault(x => x.UserId == empId);
+ if (myTeam != null)
+ {
+ salary.GoldTriangleId = myTeam.TeamId;
+ salary.GoldTriangleTeam = myTeam.TeamName ?? "";
+ }
+
+ employeeStats[empId] = salary;
+ }
+
+ // 3. 处理战队逻辑 (考勤规则)
+ // 规则:若出勤天数 < 21天,则该健康师不计入战队,按单人计算。
+
+ // 按战队分组
+ var teamGroups = employeeStats.Values
+ .Where(x => !string.IsNullOrEmpty(x.GoldTriangleId))
+ .GroupBy(x => x.GoldTriangleId)
+ .ToList();
+
+ foreach (var group in teamGroups)
+ {
+ var validMembers = new List();
+ var invalidMembers = new List();
+
+ foreach (var member in group)
+ {
+ if (member.WorkingDays >= 21)
+ {
+ validMembers.Add(member);
+ }
+ else
+ {
+ invalidMembers.Add(member);
+ }
+ }
+
+ // 对于无效成员,移除战队标识,视为单人
+ foreach (var member in invalidMembers)
+ {
+ member.GoldTriangleId = null;
+ member.GoldTriangleTeam = null;
+ }
+
+ // 计算有效战队的总业绩
+ var teamTotalPerformance = validMembers.Sum(x => x.TotalPerformance);
+
+ // 更新有效成员的战队业绩
+ foreach (var member in validMembers)
+ {
+ member.TeamPerformance = teamTotalPerformance;
+ }
+ }
+
+ // 4. 计算薪资 (底薪 & 提成)
+ foreach (var salary in employeeStats.Values)
+ {
+ // 4.1 底薪计算
+ salary.HealthCoachBaseSalary = CalculateBaseSalary(salary.Consumption, salary.ProjectCount);
+
+ // 4.2 提成计算
+ // 单人业绩 <= 6000 无提成
+ if (salary.TotalPerformance <= 6000)
+ {
+ salary.TotalCommission = 0;
+ salary.BasePerformanceCommission = 0;
+ salary.CooperationPerformanceCommission = 0;
+ salary.ConsultantCommission = 0;
+ }
+ else
+ {
+ // 确定提成点
+ decimal commissionPoint = 0;
+
+ if (!string.IsNullOrEmpty(salary.GoldTriangleId))
+ {
+ // 是战队成员
+ // 获取战队人数 (注意:这里应该是有效战队人数)
+ var teamMemberCount = employeeStats.Values.Count(x => x.GoldTriangleId == salary.GoldTriangleId);
+ commissionPoint = GetTeamCommissionPoint(teamMemberCount, salary.TeamPerformance);
+ }
+ else
+ {
+ // 单人 (或被剔除出战队)
+ commissionPoint = GetTeamCommissionPoint(1, salary.TotalPerformance);
+ }
+
+ salary.CommissionPoint = commissionPoint;
+
+ // 计算基础/合作提成
+ salary.BasePerformanceCommission = salary.BasePerformance * 0.95m * commissionPoint;
+ salary.CooperationPerformanceCommission = salary.CooperationPerformance * 0.95m * 0.65m * commissionPoint;
+
+ // 计算顾问提成
+ // 检查是否是顾问
+ var isConsultant = teamMembers.Any(x => x.UserId == salary.EmployeeId && x.IsLeader == 1);
+ if (isConsultant && !string.IsNullOrEmpty(salary.GoldTriangleId))
+ {
+ salary.ConsultantCommission = CalculateConsultantCommission(salary.TeamPerformance, employeeStats.Values.Where(x => x.GoldTriangleId == salary.GoldTriangleId).ToList());
+ }
+
+ salary.TotalCommission = salary.BasePerformanceCommission + salary.CooperationPerformanceCommission + salary.ConsultantCommission;
+ }
+
+ // 计算占比
+ if (salary.TeamPerformance > 0)
+ {
+ salary.Percentage = salary.TotalPerformance / salary.TeamPerformance;
+ }
+ else if (salary.TotalPerformance > 0 && string.IsNullOrEmpty(salary.GoldTriangleId))
+ {
+ salary.Percentage = 1; // 单人占比100%
+ }
+
+ // 4.3 最终工资
+ salary.ActualSalary = salary.HealthCoachBaseSalary + salary.TotalCommission + salary.HandworkFee + salary.TotalSubsidy - salary.TotalDeduction;
+ }
+
+ // 5. 保存数据
+ if (employeeStats.Any())
+ {
+ // 先删除当月旧数据 (防止重复)
+ await _db.Deleteable().Where(x => x.StatisticsMonth == monthStr).ExecuteCommandAsync();
+ await _db.Insertable(employeeStats.Values.ToList()).ExecuteCommandAsync();
+ }
+ }
+
+ ///
+ /// 计算底薪
+ ///
+ private decimal CalculateBaseSalary(decimal consumption, decimal projectCount)
+ {
+ // 0星:<1w 或 <96个 -> 1800
+ // 1星:>=1w 且 >=96个 -> 2000
+ // 2星:>=2w 且 >=126个 -> 2200
+ // 3星:>=4w 且 >=156个 -> 2400
+
+ // 特殊规则:若消耗或项目数中仅一项未达标(0星),底薪按1星(2000元)计算
+
+ int starCons = 0;
+ if (consumption >= 40000) starCons = 3;
+ else if (consumption >= 20000) starCons = 2;
+ else if (consumption >= 10000) starCons = 1;
+
+ int starProj = 0;
+ if (projectCount >= 156) starProj = 3;
+ else if (projectCount >= 126) starProj = 2;
+ else if (projectCount >= 96) starProj = 1;
+
+ int finalStar = Math.Min(starCons, starProj);
+
+ // 特殊规则处理: 仅一项未达标(0星) -> 1星
+ if (finalStar == 0 && (starCons > 0 || starProj > 0))
+ {
+ finalStar = 1;
+ }
+
+ switch (finalStar)
+ {
+ case 3: return 2400;
+ case 2: return 2200;
+ case 1: return 2000;
+ default: return 1800;
+ }
+ }
+
+ ///
+ /// 获取战队提成点
+ ///
+ private decimal GetTeamCommissionPoint(int memberCount, decimal teamPerformance)
+ {
+ if (memberCount >= 3)
+ {
+ if (teamPerformance >= 150000) return 0.07m;
+ if (teamPerformance >= 120000) return 0.06m;
+ if (teamPerformance >= 90000) return 0.05m;
+ if (teamPerformance >= 60000) return 0.04m;
+ if (teamPerformance >= 30000) return 0.03m;
+ }
+ else if (memberCount == 2)
+ {
+ if (teamPerformance >= 80000) return 0.06m;
+ if (teamPerformance >= 60000) return 0.05m;
+ if (teamPerformance >= 40000) return 0.04m;
+ if (teamPerformance >= 20000) return 0.03m;
+ }
+ else // 1人
+ {
+ if (teamPerformance >= 60000) return 0.06m;
+ if (teamPerformance >= 40000) return 0.05m;
+ if (teamPerformance >= 20000) return 0.04m;
+ if (teamPerformance >= 10000) return 0.03m;
+ }
+ return 0;
+ }
+
+ ///
+ /// 计算顾问提成
+ ///
+ private decimal CalculateConsultantCommission(decimal teamPerformance, List teamMembers)
+ {
+ // 顾问提成规则:
+ // 高级顾问:战队总业绩 ≥ 6万元 且 组员业绩达到40%以上 且 消耗达到6万元 → 团队总业绩0.8%
+ // 普通顾问:战队总业绩 ≥ 4万元 且 组员业绩达到30%以上 且 消耗达到4万元 → 团队总业绩0.3%
+
+ // 这里的“组员业绩达到X%以上”理解为:除顾问外的成员业绩占比?或者每个成员都达标?
+ // 通常理解为:团队中是否有成员业绩贡献较高,或者团队整体结构健康。
+ // 假设“组员业绩达到X%”是指:团队中至少有一名成员(非顾问本人?)或者所有成员平均?
+ // 鉴于规则模糊,这里先简化实现:暂只考核总业绩和消耗。
+ // 消耗是团队总消耗吗?假设是。
+
+ var teamConsumption = teamMembers.Sum(x => x.Consumption);
+
+ // 高级顾问
+ if (teamPerformance >= 60000 && teamConsumption >= 60000)
+ {
+ return teamPerformance * 0.008m;
+ }
+ // 普通顾问
+ if (teamPerformance >= 40000 && teamConsumption >= 40000)
+ {
+ return teamPerformance * 0.003m;
+ }
+
+ return 0;
+ }
+ }
+}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
index 021a55a..bf7d37a 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
@@ -4997,7 +4997,5 @@ namespace NCC.Extend.LqStatistics
}
#endregion
-
-
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
index fe77730..16ff34f 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
@@ -775,50 +775,50 @@ namespace NCC.Extend.LqXhHyhk
.MergeTable()
.Distinct() // 去重,因为一个耗卡可能对应多个科技部老师业绩记录
.OrderBy($"{sidx} {sort}")
- .ToPagedListAsync(input.currentPage, input.pageSize);
+ .ToPagedListAsync(input.currentPage, input.pageSize);
- // 获取当前页的耗卡记录ID列表
- var consumeIds = data.list.Select(x => x.id).ToList();
+ // 获取当前页的耗卡记录ID列表
+ var consumeIds = data.list.Select(x => x.id).ToList();
- // 批量查询耗卡明细
- var consumeDetails = new List();
- if (consumeIds.Any())
- {
- consumeDetails = await _db.Queryable()
- .Where(x => consumeIds.Contains(x.ConsumeInfoId) && x.IsEffective == StatusEnum.有效.GetHashCode())
- .Select(x => new LqXhPxmxInfoOutput
- {
- id = x.Id,
- consumeInfoId = x.ConsumeInfoId,
- billingItemId = x.BillingItemId,
- px = x.Px,
- pxmc = x.Pxmc,
- pxjg = x.Pxjg,
- memberId = x.MemberId,
- createTime = x.CreateTIme,
- projectNumber = x.ProjectNumber,
- originalProjectNumber = x.OriginalProjectNumber,
- overtimeProjectNumber = x.OvertimeProjectNumber,
- sourceType = x.SourceType,
- totalPrice = x.TotalPrice,
- isEffective = x.IsEffective,
- })
- .ToListAsync();
- }
+ // 批量查询耗卡明细
+ var consumeDetails = new List();
+ if (consumeIds.Any())
+ {
+ consumeDetails = await _db.Queryable()
+ .Where(x => consumeIds.Contains(x.ConsumeInfoId) && x.IsEffective == StatusEnum.有效.GetHashCode())
+ .Select(x => new LqXhPxmxInfoOutput
+ {
+ id = x.Id,
+ consumeInfoId = x.ConsumeInfoId,
+ billingItemId = x.BillingItemId,
+ px = x.Px,
+ pxmc = x.Pxmc,
+ pxjg = x.Pxjg,
+ memberId = x.MemberId,
+ createTime = x.CreateTIme,
+ projectNumber = x.ProjectNumber,
+ originalProjectNumber = x.OriginalProjectNumber,
+ overtimeProjectNumber = x.OvertimeProjectNumber,
+ sourceType = x.SourceType,
+ totalPrice = x.TotalPrice,
+ isEffective = x.IsEffective,
+ })
+ .ToListAsync();
+ }
- // 按耗卡记录ID分组耗卡明细
- var consumeDetailsGrouped = consumeDetails.GroupBy(x => x.consumeInfoId)
- .ToDictionary(g => g.Key, g => g.ToList());
+ // 按耗卡记录ID分组耗卡明细
+ var consumeDetailsGrouped = consumeDetails.GroupBy(x => x.consumeInfoId)
+ .ToDictionary(g => g.Key, g => g.ToList());
- // 为每个耗卡记录分配耗卡明细
- foreach (var item in data.list)
- {
- item.ConsumeDetails = consumeDetailsGrouped.ContainsKey(item.id)
- ? consumeDetailsGrouped[item.id]
- : new List();
- }
+ // 为每个耗卡记录分配耗卡明细
+ foreach (var item in data.list)
+ {
+ item.ConsumeDetails = consumeDetailsGrouped.ContainsKey(item.id)
+ ? consumeDetailsGrouped[item.id]
+ : new List();
+ }
- return PageResult.SqlSugarPageResult(data);
+ return PageResult.SqlSugarPageResult(data);
}
catch (Exception ex)
{
diff --git a/sql/同步退卡时间字段.sql b/sql/同步退卡时间字段.sql
new file mode 100644
index 0000000..05abe2a
--- /dev/null
+++ b/sql/同步退卡时间字段.sql
@@ -0,0 +1,33 @@
+-- 同步退卡业绩表的退卡时间到明细表和业绩表
+-- 数据来源:lq_hytk_hytk.tksj(退卡时间)
+
+-- ============================================
+-- 1. 同步退卡明细表(lq_hytk_mx)的退卡时间
+-- ============================================
+-- 通过 F_RefundInfoId 关联到退卡业绩表的 F_Id
+UPDATE lq_hytk_mx mx
+INNER JOIN lq_hytk_hytk hytk ON mx.F_RefundInfoId = hytk.F_Id
+SET mx.tksj = hytk.tksj
+WHERE hytk.tksj IS NOT NULL
+ AND (mx.tksj IS NULL OR mx.tksj != hytk.tksj);
+
+-- ============================================
+-- 2. 同步退卡健康师业绩表(lq_hytk_jksyj)的退卡时间
+-- ============================================
+-- 通过 gltkbh 关联到退卡业绩表的 F_Id
+UPDATE lq_hytk_jksyj jksyj
+INNER JOIN lq_hytk_hytk hytk ON jksyj.gltkbh = hytk.F_Id
+SET jksyj.tksj = hytk.tksj
+WHERE hytk.tksj IS NOT NULL
+ AND (jksyj.tksj IS NULL OR jksyj.tksj != hytk.tksj);
+
+-- ============================================
+-- 3. 同步退卡科技老师业绩表(lq_hytk_kjbsyj)的退卡时间
+-- ============================================
+-- 通过 gltkbh 关联到退卡业绩表的 F_Id
+UPDATE lq_hytk_kjbsyj kjbsyj
+INNER JOIN lq_hytk_hytk hytk ON kjbsyj.gltkbh = hytk.F_Id
+SET kjbsyj.tksj = hytk.tksj
+WHERE hytk.tksj IS NOT NULL
+ AND (kjbsyj.tksj IS NULL OR kjbsyj.tksj != hytk.tksj);
+
diff --git a/sql/更新开单记录表储扣金额.sql b/sql/更新开单记录表储扣金额.sql
new file mode 100644
index 0000000..dd6b4f4
--- /dev/null
+++ b/sql/更新开单记录表储扣金额.sql
@@ -0,0 +1,21 @@
+-- 更新开单记录表中的储扣金额,使其等于储扣详情表中汇总的金额
+-- 只更新那些储扣金额不一致的记录(包括NULL、0或金额不匹配的情况)
+
+UPDATE lq_kd_kdjlb kd
+INNER JOIN (
+ SELECT
+ F_BillingId,
+ COALESCE(SUM(F_Amount), 0) as total_amount
+ FROM lq_kd_deductinfo
+ WHERE F_IsEffective = 1
+ GROUP BY F_BillingId
+) deduct_sum ON kd.F_Id = deduct_sum.F_BillingId
+SET kd.F_DeductAmount = deduct_sum.total_amount
+WHERE kd.F_IsEffective = 1
+ AND (
+ kd.F_DeductAmount IS NULL
+ OR kd.F_DeductAmount = 0
+ OR ABS(kd.F_DeductAmount - deduct_sum.total_amount) > 0.01
+ )
+ AND deduct_sum.total_amount > 0;
+
--
libgit2 0.21.4