diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitPerformanceCompletionOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitPerformanceCompletionOutput.cs
index 1209cba..9143895 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitPerformanceCompletionOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitPerformanceCompletionOutput.cs
@@ -24,12 +24,27 @@ namespace NCC.Extend.Entitys.Dto.LqDailyReport
public decimal TargetPerformance { get; set; }
///
- /// 完成业绩(管理门店的开单业绩总和)
+ /// 开单业绩(管理门店的开单业绩总和)
+ ///
+ public decimal BillingPerformance { get; set; }
+
+ ///
+ /// 退款业绩(退卡业绩总和)
+ ///
+ public decimal RefundPerformance { get; set; }
+
+ ///
+ /// 开单业绩(管理门店的开单业绩总和)
+ ///
+ public decimal ActualPerformance { get; set; }
+
+ ///
+ /// 实际完成业绩(开单业绩 - 退款业绩)
///
public decimal CompletedPerformance { get; set; }
///
- /// 完成率(百分比)
+ /// 完成率(百分比,基于实际完成业绩计算)
///
public decimal CompletionRate { get; set; }
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TianwangGroupPerformanceCompletionOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TianwangGroupPerformanceCompletionOutput.cs
index b033c17..e35dd64 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TianwangGroupPerformanceCompletionOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TianwangGroupPerformanceCompletionOutput.cs
@@ -24,12 +24,27 @@ namespace NCC.Extend.Entitys.Dto.LqDailyReport
public decimal TargetPerformance { get; set; }
///
- /// 完成业绩(管理门店的开单业绩总和)
+ /// 开单业绩(管理门店的开单业绩总和)
+ ///
+ public decimal BillingPerformance { get; set; }
+
+ ///
+ /// 退款业绩(退卡业绩总和)
+ ///
+ public decimal RefundPerformance { get; set; }
+
+ ///
+ /// 开单业绩(与BillingPerformance相同,用于兼容)
+ ///
+ public decimal ActualPerformance { get; set; }
+
+ ///
+ /// 实际完成业绩(开单业绩 - 退款业绩)
///
public decimal CompletedPerformance { get; set; }
///
- /// 完成率(百分比)
+ /// 完成率(百分比,基于实际完成业绩计算)
///
public decimal CompletionRate { get; set; }
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqPackageInfo/ActivityStatisticsByStoreItemOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqPackageInfo/ActivityStatisticsByStoreItemOutput.cs
index 3f75828..abb9115 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqPackageInfo/ActivityStatisticsByStoreItemOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqPackageInfo/ActivityStatisticsByStoreItemOutput.cs
@@ -18,15 +18,15 @@ namespace NCC.Extend.Entitys.Dto.LqPackageInfo
public string ActivityName { get; set; }
///
- /// 门店品项统计列表
+ /// 门店统计列表(层级结构:每个门店下包含品项列表)
///
- public List StoreItemList { get; set; }
+ public List StoreList { get; set; }
}
///
- /// 门店品项统计项
+ /// 门店统计(包含品项列表)
///
- public class StoreItemStatisticsItem
+ public class StoreStatisticsWithItems
{
///
/// 门店ID
@@ -39,6 +39,17 @@ namespace NCC.Extend.Entitys.Dto.LqPackageInfo
public string StoreName { get; set; }
///
+ /// 该门店下的品项统计列表
+ ///
+ public List ItemList { get; set; }
+ }
+
+ ///
+ /// 门店品项统计项
+ ///
+ public class StoreItemStatisticsItem
+ {
+ ///
/// 品项ID
///
public string ItemId { get; set; }
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/EmployeePerformanceStatisticsOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/EmployeePerformanceStatisticsOutput.cs
index 3ded29a..92a6d54 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/EmployeePerformanceStatisticsOutput.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/EmployeePerformanceStatisticsOutput.cs
@@ -41,7 +41,7 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics
public decimal BillingAmount { get; set; }
///
- /// 消耗数量
+ /// 消耗单数(消耗主表的去重记录数)
///
public int ConsumeCount { get; set; }
@@ -51,7 +51,7 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics
public decimal ConsumeAmount { get; set; }
///
- /// 退卡数量
+ /// 退卡单数量(退卡主表的去重记录数)
///
public int RefundCount { get; set; }
@@ -76,8 +76,28 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics
public int BillingProjectCount { get; set; }
///
- /// 消耗项目数(项目次数总和)
+ /// 消耗项目数(项目次数总和,包含原始+加班+陪同)
///
public int ConsumeProjectCount { get; set; }
+
+ ///
+ /// 消耗原始项目数(原始项目次数总和)
+ ///
+ public decimal ConsumeOriginalProjectCount { get; set; }
+
+ ///
+ /// 消耗加班项目数(加班项目次数总和)
+ ///
+ public decimal ConsumeOvertimeProjectCount { get; set; }
+
+ ///
+ /// 消耗陪同项目数(陪同项目次数总和)
+ ///
+ public decimal ConsumeAccompaniedProjectCount { get; set; }
+
+ ///
+ /// 手工费(消耗手工费总和)
+ ///
+ public decimal LaborCost { get; set; }
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
index fa274d2..286d5ce 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Yitter.IdGenerator;
using NCC.Common.Helper;
@@ -124,7 +125,7 @@ namespace NCC.Extend
storeFilter = $"AND consume.Md IN ('{storeIdsStr}')";
}
- // SQL查询:获取门店每日运营数据
+ // SQL查询:获取门店每日运营数据(只统计zxzt为"开店"的门店)
var sql = $@"
SELECT
consume.Md as StoreId,
@@ -147,7 +148,7 @@ namespace NCC.Extend
-- 消耗业绩(总金额)
COALESCE(SUM(project.F_TotalPrice), 0) as ConsumePerformance
FROM lq_xh_hyhk consume
- LEFT JOIN lq_mdxx store ON consume.Md = store.F_Id
+ INNER JOIN lq_mdxx store ON consume.Md = store.F_Id AND store.zxzt = '开店'
LEFT JOIN lq_xh_pxmx project ON consume.F_Id = project.F_ConsumeInfoId AND project.F_IsEffective = 1
WHERE consume.F_IsEffective = 1
AND DATE(consume.Hksj) >= '{startDate:yyyy-MM-dd}'
@@ -226,7 +227,7 @@ namespace NCC.Extend
storeFilter = $"AND store.F_Id IN ('{storeIdsStr}')";
}
- // SQL查询:获取门店业绩完成情况
+ // SQL查询:获取门店业绩完成情况(只统计zxzt为"开店"的门店)
var sql = $@"
SELECT
store.F_Id as StoreId,
@@ -253,7 +254,7 @@ namespace NCC.Extend
), 0) as RefundPerformance
FROM lq_mdxx store
LEFT JOIN lq_md_target target ON target.F_StoreId = store.F_Id AND target.F_Month = '{month}'
- WHERE 1=1 {storeFilter}
+ WHERE store.zxzt = '开店' {storeFilter}
ORDER BY (BillingPerformance - RefundPerformance) DESC";
var result = await _db.Ado.SqlQueryAsync(sql);
@@ -288,7 +289,7 @@ namespace NCC.Extend
/// 获取事业部业绩完成情况
///
///
- /// 根据指定日期统计各事业部的业绩完成情况,包括目标业绩、完成业绩、完成率
+ /// 根据指定日期统计各事业部的业绩完成情况,包括目标业绩、开单业绩、退卡金额、实际完成业绩、完成率
///
/// 示例请求:
/// ```json
@@ -308,7 +309,10 @@ namespace NCC.Extend
/// - BusinessUnitId: 事业部ID
/// - BusinessUnitName: 事业部名称
/// - TargetPerformance: 目标业绩(来自门店目标表lq_md_target的F_BusinessUnitTarget字段,根据开始时间所在月份获取,通过F_BusinessUnit字段关联,如果未查询到则为0)
- /// - CompletedPerformance: 完成业绩(指定时间范围内的开单业绩总和)
+ /// - BillingPerformance: 开单业绩(指定时间范围内的开单业绩总和)
+ /// - RefundPerformance: 退款业绩(指定时间范围内的退卡业绩总和)
+ /// - ActualPerformance: 开单业绩(与BillingPerformance相同,用于兼容)
+ /// - CompletedPerformance: 实际完成业绩(开单业绩 - 退款业绩)
/// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100)
/// - StoreCount: 门店数量(根据门店目标表中归属该事业部的门店数统计)
///
@@ -367,20 +371,23 @@ namespace NCC.Extend
BusinessUnitId = unit.BusinessUnitId.ToString(),
BusinessUnitName = unit.BusinessUnitName.ToString(),
TargetPerformance = Convert.ToDecimal(unit.TargetPerformance),
+ BillingPerformance = 0,
+ RefundPerformance = 0,
+ ActualPerformance = 0,
CompletedPerformance = 0,
CompletionRate = 0,
StoreCount = Convert.ToInt32(unit.StoreCount)
};
}
- // 第二步:统计各事业部的完成业绩
+ // 第二步:统计各事业部的开单业绩
var businessUnitIds = businessUnitDict.Keys.ToList();
var unitIdsStr = string.Join("','", businessUnitIds);
- var completedPerformanceSql = $@"
+ var billingPerformanceSql = $@"
SELECT
store.syb as BusinessUnitId,
- COALESCE(SUM(billing.sfyj), 0) as CompletedPerformance
+ COALESCE(SUM(billing.sfyj), 0) as BillingPerformance
FROM lq_kd_kdjlb billing
INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id
INNER JOIN base_organize o ON store.syb = o.F_Id
@@ -393,28 +400,58 @@ namespace NCC.Extend
AND store.syb IN ('{unitIdsStr}')
GROUP BY store.syb";
- var completedPerformanceData = await _db.Ado.SqlQueryAsync(completedPerformanceSql);
+ var billingPerformanceData = await _db.Ado.SqlQueryAsync(billingPerformanceSql);
+
+ // 第三步:统计各事业部的退卡金额
+ var refundPerformanceSql = $@"
+ SELECT
+ store.syb as BusinessUnitId,
+ COALESCE(SUM(refund.F_ActualRefundAmount), 0) as RefundPerformance
+ FROM lq_hytk_hytk refund
+ INNER JOIN lq_mdxx store ON refund.md = store.F_Id
+ INNER JOIN base_organize o ON store.syb = o.F_Id
+ WHERE refund.F_IsEffective = 1
+ AND o.F_Category = 'department'
+ AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
+ AND o.F_FullName IN ('事业一部', '事业二部', '事业三部', '事业四部', '事业五部', '事业六部')
+ AND DATE(refund.tksj) >= '{startDate:yyyy-MM-dd}'
+ AND DATE(refund.tksj) <= '{endDate:yyyy-MM-dd}'
+ AND store.syb IN ('{unitIdsStr}')
+ GROUP BY store.syb";
+
+ var refundPerformanceData = await _db.Ado.SqlQueryAsync(refundPerformanceSql);
- // 第三步:合并数据并计算完成率
- foreach (var item in completedPerformanceData)
+ // 第四步:合并开单业绩数据
+ foreach (var item in billingPerformanceData)
{
var businessUnitId = item.BusinessUnitId.ToString();
if (businessUnitDict.ContainsKey(businessUnitId))
{
- var completedPerformance = Convert.ToDecimal(item.CompletedPerformance);
- var unit = businessUnitDict[businessUnitId];
- unit.CompletedPerformance = completedPerformance;
-
- var completionRate = unit.TargetPerformance > 0
- ? (completedPerformance / unit.TargetPerformance * 100m)
- : 0m;
- unit.CompletionRate = decimal.Round(completionRate, 2);
+ businessUnitDict[businessUnitId].BillingPerformance = Convert.ToDecimal(item.BillingPerformance);
}
}
- var outputList = businessUnitDict.Values
- .OrderByDescending(x => x.CompletedPerformance)
- .ToList();
+ // 第五步:合并退卡金额数据
+ foreach (var item in refundPerformanceData)
+ {
+ var businessUnitId = item.BusinessUnitId.ToString();
+ if (businessUnitDict.ContainsKey(businessUnitId))
+ {
+ businessUnitDict[businessUnitId].RefundPerformance = Convert.ToDecimal(item.RefundPerformance);
+ }
+ }
+
+ // 第六步:计算实际完成业绩和完成率
+ foreach (var unit in businessUnitDict.Values)
+ {
+ unit.ActualPerformance = unit.BillingPerformance; // 开单业绩
+ var actualCompletedPerformance = unit.BillingPerformance - unit.RefundPerformance; // 实际完成业绩
+ unit.CompletedPerformance = actualCompletedPerformance; // 实际完成业绩
+ var completionRate = unit.TargetPerformance > 0 ? (actualCompletedPerformance / unit.TargetPerformance * 100m) : 0m;
+ unit.CompletionRate = decimal.Round(completionRate, 2);
+ }
+
+ var outputList = businessUnitDict.Values.OrderByDescending(x => x.CompletedPerformance).ToList();
return outputList;
}
@@ -425,7 +462,7 @@ namespace NCC.Extend
/// 获取天王团业绩完成情况
///
///
- /// 根据指定日期统计各天王团的业绩完成情况,包括目标业绩、完成业绩、完成率
+ /// 根据指定日期统计各天王团的业绩完成情况,包括目标业绩、开单业绩、退卡金额、实际完成业绩、完成率
///
/// 示例请求:
/// ```json
@@ -445,7 +482,10 @@ namespace NCC.Extend
/// - DepartmentId: 部门ID
/// - DepartmentName: 部门名称(教育一部、教育二部、科技一部、科技二部、大项目一部、大项目二部)
/// - TargetPerformance: 目标业绩(来自门店目标表lq_md_target,根据部门类型使用对应字段:教育部使用F_EducationDepartmentTarget,科技部使用F_TechDepartmentTarget,大项目部使用F_MajorProjectDepartmentTarget,根据开始时间所在月份获取,通过对应归属字段关联,如果未查询到则为0)
- /// - CompletedPerformance: 完成业绩(指定时间范围内的开单业绩总和)
+ /// - BillingPerformance: 开单业绩(指定时间范围内的开单业绩总和)
+ /// - RefundPerformance: 退款业绩(指定时间范围内的退卡业绩总和)
+ /// - ActualPerformance: 开单业绩(与BillingPerformance相同,用于兼容)
+ /// - CompletedPerformance: 实际完成业绩(开单业绩 - 退款业绩)
/// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100)
/// - StoreCount: 门店数量(根据门店目标表中归属该部门的门店数统计)
///
@@ -516,6 +556,9 @@ namespace NCC.Extend
DepartmentId = dept.DepartmentId.ToString(),
DepartmentName = dept.DepartmentName.ToString(),
TargetPerformance = Convert.ToDecimal(dept.TargetPerformance),
+ BillingPerformance = 0,
+ RefundPerformance = 0,
+ ActualPerformance = 0,
CompletedPerformance = 0,
CompletionRate = 0,
StoreCount = Convert.ToInt32(dept.StoreCount)
@@ -529,65 +572,84 @@ namespace NCC.Extend
return new List();
}
- // 第二步:统计各天王团的完成业绩(分类型统计)
- var completedPerformanceDataList = new List();
+ // 第二步:统计各天王团的开单业绩(直接按部门ID统计,避免重复计算)
+ var deptIdsStrForQuery = string.Join("','", departmentDict.Keys);
- foreach (var deptType in tianwangDepartments)
- {
- var deptIdsStrForQuery = string.Join("','", departmentDict.Keys);
+ // 使用 OR 条件连接三个部门类型字段,确保每个开单记录只被统计一次到对应的部门
+ var billingPerformanceSql = $@"
+ SELECT
+ o.F_Id as DepartmentId,
+ COALESCE(SUM(billing.sfyj), 0) as BillingPerformance
+ FROM lq_kd_kdjlb billing
+ INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id
+ INNER JOIN base_organize o ON (
+ (store.jyb = o.F_Id AND o.F_FullName IN ('教育一部', '教育二部'))
+ OR (store.kjb = o.F_Id AND o.F_FullName IN ('科技一部', '科技二部'))
+ OR (store.dxmb = o.F_Id AND o.F_FullName IN ('大项目一部', '大项目二部'))
+ )
+ WHERE billing.F_IsEffective = 1
+ AND o.F_Category = 'department'
+ AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
+ AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
+ AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
+ AND o.F_Id IN ('{deptIdsStrForQuery}')
+ GROUP BY o.F_Id";
- var completedPerformanceSql = $@"
- SELECT
- o.F_Id as DepartmentId,
- COALESCE(SUM(billing.sfyj), 0) as CompletedPerformance
- FROM lq_kd_kdjlb billing
- INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id
- INNER JOIN base_organize o ON store.{deptType.Key} = o.F_Id
- WHERE billing.F_IsEffective = 1
- AND o.F_Category = 'department'
- AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
- AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
- AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
- AND o.F_Id IN ('{deptIdsStrForQuery}')
- GROUP BY o.F_Id";
+ var billingPerformanceData = await _db.Ado.SqlQueryAsync(billingPerformanceSql);
- var completedPerformanceData = await _db.Ado.SqlQueryAsync(completedPerformanceSql);
- if (completedPerformanceData != null)
+ // 第三步:统计各天王团的退卡金额
+ var refundPerformanceSql = $@"
+ SELECT
+ o.F_Id as DepartmentId,
+ COALESCE(SUM(refund.F_ActualRefundAmount), 0) as RefundPerformance
+ FROM lq_hytk_hytk refund
+ INNER JOIN lq_mdxx store ON refund.md = store.F_Id
+ INNER JOIN base_organize o ON (
+ (store.jyb = o.F_Id AND o.F_FullName IN ('教育一部', '教育二部'))
+ OR (store.kjb = o.F_Id AND o.F_FullName IN ('科技一部', '科技二部'))
+ OR (store.dxmb = o.F_Id AND o.F_FullName IN ('大项目一部', '大项目二部'))
+ )
+ WHERE refund.F_IsEffective = 1
+ AND o.F_Category = 'department'
+ AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
+ AND DATE(refund.tksj) >= '{startDate:yyyy-MM-dd}'
+ AND DATE(refund.tksj) <= '{endDate:yyyy-MM-dd}'
+ AND o.F_Id IN ('{deptIdsStrForQuery}')
+ GROUP BY o.F_Id";
+
+ var refundPerformanceData = await _db.Ado.SqlQueryAsync(refundPerformanceSql);
+
+ // 第四步:合并开单业绩数据
+ foreach (var item in billingPerformanceData ?? Enumerable.Empty())
+ {
+ var departmentId = item.DepartmentId.ToString();
+ if (departmentDict.ContainsKey(departmentId))
{
- completedPerformanceDataList.AddRange(completedPerformanceData);
+ departmentDict[departmentId].BillingPerformance = Convert.ToDecimal(item.BillingPerformance);
}
}
- // 第三步:合并数据并计算完成率(按部门ID去重累加)
- var departmentPerformanceDict = new Dictionary();
- foreach (var item in completedPerformanceDataList)
+ // 第五步:合并退卡金额数据
+ foreach (var item in refundPerformanceData ?? Enumerable.Empty())
{
var departmentId = item.DepartmentId.ToString();
- var completedPerformance = Convert.ToDecimal(item.CompletedPerformance);
-
- if (departmentPerformanceDict.ContainsKey(departmentId))
+ if (departmentDict.ContainsKey(departmentId))
{
- departmentPerformanceDict[departmentId] += completedPerformance;
- }
- else
- {
- departmentPerformanceDict[departmentId] = completedPerformance;
+ departmentDict[departmentId].RefundPerformance = Convert.ToDecimal(item.RefundPerformance);
}
}
- // 计算完成率
- foreach (var kvp in departmentPerformanceDict)
+ // 第六步:计算实际完成业绩和完成率
+ foreach (var dept in departmentDict.Values)
{
- if (departmentDict.ContainsKey(kvp.Key))
- {
- var dept = departmentDict[kvp.Key];
- dept.CompletedPerformance = kvp.Value;
-
- var completionRate = dept.TargetPerformance > 0
- ? (kvp.Value / dept.TargetPerformance * 100m)
- : 0m;
- dept.CompletionRate = decimal.Round(completionRate, 2);
- }
+ dept.ActualPerformance = dept.BillingPerformance; // 开单业绩
+ var actualCompletedPerformance = dept.BillingPerformance - dept.RefundPerformance; // 实际完成业绩
+ dept.CompletedPerformance = actualCompletedPerformance; // 实际完成业绩
+
+ var completionRate = dept.TargetPerformance > 0
+ ? (actualCompletedPerformance / dept.TargetPerformance * 100m)
+ : 0m;
+ dept.CompletionRate = decimal.Round(completionRate, 2);
}
var outputList = departmentDict.Values
@@ -1069,7 +1131,9 @@ namespace NCC.Extend
})
.ToList(),
})
- .OrderBy(x => x.BusinessUnitName) // 按事业部名称排序
+ .ToList()
+ .OrderBy(x => ExtractBusinessUnitNumber(x.BusinessUnitName)) // 按事业部数字排序(事业一部、事业二部...)
+ .ThenBy(x => x.BusinessUnitName) // 如果数字相同,按名称排序
.ToList();
return result;
@@ -1152,19 +1216,12 @@ namespace NCC.Extend
// 日期(格式:11月8日)
textBuilder.AppendLine($"{targetDate.Month}月{targetDate.Day}日");
- textBuilder.AppendLine();
-
// 今日总业绩和总单量
textBuilder.AppendLine($"今日总业绩:{totalPerformance:F0}");
- textBuilder.AppendLine();
textBuilder.AppendLine($"今日总单量:{totalOrderCount}");
- textBuilder.AppendLine();
-
// 标语
textBuilder.AppendLine("绿纤人用结果捍卫尊严 ");
- textBuilder.AppendLine();
textBuilder.AppendLine("业绩捷报 ");
- textBuilder.AppendLine();
// 每个事业部的数据
foreach (var unit in statistics)
@@ -1175,10 +1232,7 @@ namespace NCC.Extend
// 事业部业绩和单量
textBuilder.AppendLine($"{unit.BusinessUnitName}总业绩:{unit.TotalPerformance:F0}");
- textBuilder.AppendLine();
textBuilder.AppendLine($"{unit.BusinessUnitName}总单量:{unit.TotalOrderCount}");
- textBuilder.AppendLine();
-
// 按门店分组订单
var ordersByStore = unit.Orders
.GroupBy(x => x.StoreName)
@@ -1207,7 +1261,6 @@ namespace NCC.Extend
{
textBuilder.AppendLine($"第{orderIndex}单:{order.StoreName}{teacherNames}{order.Amount:F0}");
}
- textBuilder.AppendLine();
orderIndex++;
}
}
@@ -1220,6 +1273,84 @@ namespace NCC.Extend
throw NCCException.Oh($"获取事业部开单统计文本失败: {ex.Message}", ex);
}
}
+
+ ///
+ /// 从事业部名称中提取数字(用于排序)
+ /// 例如:"事业一部" -> 1, "事业二部" -> 2, "事业三部" -> 3
+ ///
+ /// 事业部名称
+ /// 提取的数字,如果提取失败返回999(排在最后)
+ private int ExtractBusinessUnitNumber(string businessUnitName)
+ {
+ if (string.IsNullOrEmpty(businessUnitName))
+ {
+ return 999;
+ }
+
+ // 使用正则表达式提取数字
+ var match = Regex.Match(businessUnitName, @"[一二三四五六七八九十]+|[\d]+");
+ if (match.Success)
+ {
+ var numberStr = match.Value;
+
+ // 如果是中文数字,转换为阿拉伯数字
+ if (Regex.IsMatch(numberStr, @"[一二三四五六七八九十]+"))
+ {
+ var chineseNumbers = new Dictionary
+ {
+ { "一", 1 }, { "二", 2 }, { "三", 3 }, { "四", 4 }, { "五", 5 },
+ { "六", 6 }, { "七", 7 }, { "八", 8 }, { "九", 9 }, { "十", 10 }
+ };
+
+ // 处理"十"的情况(如"十一"、"十二"等)
+ if (numberStr == "十")
+ {
+ return 10;
+ }
+ else if (numberStr.StartsWith("十"))
+ {
+ // 十一、十二...十九
+ var remainder = numberStr.Substring(1);
+ if (chineseNumbers.ContainsKey(remainder))
+ {
+ return 10 + chineseNumbers[remainder];
+ }
+ }
+ else if (numberStr.EndsWith("十"))
+ {
+ // 二十、三十...九十
+ var prefix = numberStr.Substring(0, numberStr.Length - 1);
+ if (chineseNumbers.ContainsKey(prefix))
+ {
+ return chineseNumbers[prefix] * 10;
+ }
+ }
+ else if (numberStr.Length == 2 && numberStr.Contains("十"))
+ {
+ // 二十一、二十二...九十九
+ var parts = numberStr.Split('十');
+ if (parts.Length == 2)
+ {
+ var tens = parts[0] == "" ? 1 : (chineseNumbers.ContainsKey(parts[0]) ? chineseNumbers[parts[0]] : 0);
+ var ones = parts[1] == "" ? 0 : (chineseNumbers.ContainsKey(parts[1]) ? chineseNumbers[parts[1]] : 0);
+ return tens * 10 + ones;
+ }
+ }
+ else if (chineseNumbers.ContainsKey(numberStr))
+ {
+ return chineseNumbers[numberStr];
+ }
+ }
+ else if (int.TryParse(numberStr, out var number))
+ {
+ // 如果是阿拉伯数字,直接返回
+ return number;
+ }
+ }
+
+ // 如果提取失败,返回999(排在最后)
+ return 999;
+ }
#endregion
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqMdxxService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqMdxxService.cs
index ab17b91..1680509 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqMdxxService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqMdxxService.cs
@@ -361,6 +361,7 @@ namespace NCC.Extend.LqMdxx
public async Task GetSelector()
{
var list = await _db.Queryable()
+ .Where(it => it.Zxzt == "开店")
.Select(it => new { id = it.Id, fullName = it.Dm, enCode = it.Mdbm })
.ToListAsync();
return new { list = list };
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqPackageInfoService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqPackageInfoService.cs
index eb6bc42..59d0fa8 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqPackageInfoService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqPackageInfoService.cs
@@ -901,6 +901,7 @@ namespace NCC.Extend.LqPackageInfo
}
// 5. 按品项分组统计(先获取基础统计数据)
+ // 注意:SqlSugar 的 AggregateSum 可能不支持三元运算符,需要先查询数据再计算
var itemStatistics = await baseQuery
.GroupBy((px, kd) => new { ItemId = px.Px, ItemName = px.Pxmc })
.Select((px, kd) => new
@@ -908,14 +909,25 @@ namespace NCC.Extend.LqPackageInfo
ItemId = px.Px ?? "",
ItemName = px.Pxmc ?? "",
SalesQuantity = SqlFunc.AggregateSum(px.ProjectNumber),
- SalesAmount = SqlFunc.AggregateSum(px.ActualPrice > 0 ? px.ActualPrice : px.TotalPrice),
+ SalesAmountActual = SqlFunc.AggregateSum(px.ActualPrice),
+ SalesAmountTotal = SqlFunc.AggregateSum(px.TotalPrice),
SalesCount = SqlFunc.AggregateCount(px.Id)
})
.ToListAsync();
+ // 计算实际销售金额(ActualPrice > 0 时使用 ActualPrice,否则使用 TotalPrice)
+ var itemStatisticsWithAmount = itemStatistics.Select(stat => new
+ {
+ stat.ItemId,
+ stat.ItemName,
+ stat.SalesQuantity,
+ SalesAmount = stat.SalesAmountActual > 0 ? stat.SalesAmountActual : stat.SalesAmountTotal,
+ stat.SalesCount
+ }).ToList();
+
// 6. 单独统计每个品项的开单数量(去重开单ID)
var itemList = new List();
- foreach (var stat in itemStatistics)
+ foreach (var stat in itemStatisticsWithAmount)
{
var billingCountQuery = _db.Queryable(
(px, kd) => px.Glkdbh == kd.Id)
@@ -981,6 +993,20 @@ namespace NCC.Extend.LqPackageInfo
/// "storeIds": ["门店ID1", "门店ID2"]
/// }
/// ```
+ ///
+ /// 返回格式(层级结构):
+ /// - ActivityId: 营销活动ID
+ /// - ActivityName: 营销活动名称
+ /// - StoreList: 门店统计列表
+ /// - StoreId: 门店ID
+ /// - StoreName: 门店名称
+ /// - ItemList: 该门店下的品项统计列表
+ /// - ItemId: 品项ID
+ /// - ItemName: 品项名称
+ /// - SalesQuantity: 销售数量(项目次数总和)
+ /// - SalesAmount: 销售金额
+ /// - BillingCount: 开单数量(包含该品项的开单数)
+ /// - SalesCount: 销售次数(品项明细记录数)
///
/// 查询参数
/// 按门店品项统计数据
@@ -1021,6 +1047,7 @@ namespace NCC.Extend.LqPackageInfo
}
// 5. 按门店和品项分组统计(先获取基础统计数据)
+ // 注意:SqlSugar 的 AggregateSum 可能不支持三元运算符,需要先查询数据再计算
var storeItemStatistics = await baseQuery
.GroupBy((px, kd, md) => new
{
@@ -1036,14 +1063,28 @@ namespace NCC.Extend.LqPackageInfo
ItemId = px.Px ?? "",
ItemName = px.Pxmc ?? "",
SalesQuantity = SqlFunc.AggregateSum(px.ProjectNumber),
- SalesAmount = SqlFunc.AggregateSum(px.ActualPrice > 0 ? px.ActualPrice : px.TotalPrice),
+ SalesAmountActual = SqlFunc.AggregateSum(px.ActualPrice),
+ SalesAmountTotal = SqlFunc.AggregateSum(px.TotalPrice),
SalesCount = SqlFunc.AggregateCount(px.Id)
})
.ToListAsync();
- // 6. 单独统计每个门店品项的开单数量(去重开单ID)
- var storeItemList = new List();
- foreach (var stat in storeItemStatistics)
+ // 计算实际销售金额(ActualPrice > 0 时使用 ActualPrice,否则使用 TotalPrice)
+ var storeItemStatisticsWithAmount = storeItemStatistics.Select(stat => new
+ {
+ stat.StoreId,
+ stat.StoreName,
+ stat.ItemId,
+ stat.ItemName,
+ stat.SalesQuantity,
+ SalesAmount = stat.SalesAmountActual > 0 ? stat.SalesAmountActual : stat.SalesAmountTotal,
+ stat.SalesCount
+ }).ToList();
+
+ // 6. 单独统计每个门店品项的开单数量(去重开单ID),并组装成层级结构
+ var storeItemDict = new Dictionary>();
+
+ foreach (var stat in storeItemStatisticsWithAmount)
{
var billingCountQuery = _db.Queryable(
(px, kd) => px.Glkdbh == kd.Id)
@@ -1064,28 +1105,53 @@ namespace NCC.Extend.LqPackageInfo
.Select((px, kd) => px.Glkdbh)
.CountAsync();
- storeItemList.Add(new StoreItemStatisticsItem
+ var itemStat = new StoreItemStatisticsItem
{
- StoreId = stat.StoreId ?? "",
- StoreName = stat.StoreName ?? "",
ItemId = stat.ItemId ?? "",
ItemName = stat.ItemName ?? "",
SalesQuantity = stat.SalesQuantity,
SalesAmount = stat.SalesAmount,
BillingCount = billingCount,
SalesCount = stat.SalesCount
- });
+ };
+
+ // 按门店分组
+ var storeKey = stat.StoreId ?? "";
+ if (!storeItemDict.ContainsKey(storeKey))
+ {
+ storeItemDict[storeKey] = new List();
+ }
+ storeItemDict[storeKey].Add(itemStat);
}
- // 7. 排序
- storeItemList = storeItemList.OrderBy(x => x.StoreName).ThenBy(x => x.ItemName).ToList();
+ // 7. 组装层级结构:门店 -> 品项列表
+ var storeList = new List();
+ foreach (var kvp in storeItemDict)
+ {
+ var firstItem = storeItemStatistics.FirstOrDefault(s => s.StoreId == kvp.Key);
+ if (firstItem != null)
+ {
+ // 对品项列表排序
+ var sortedItemList = kvp.Value.OrderBy(x => x.ItemName).ToList();
- // 7. 返回统计结果
+ storeList.Add(new StoreStatisticsWithItems
+ {
+ StoreId = firstItem.StoreId ?? "",
+ StoreName = firstItem.StoreName ?? "",
+ ItemList = sortedItemList
+ });
+ }
+ }
+
+ // 8. 按门店名称排序
+ storeList = storeList.OrderBy(x => x.StoreName).ToList();
+
+ // 9. 返回统计结果
return new ActivityStatisticsByStoreItemOutput
{
ActivityId = input.ActivityId,
ActivityName = activity.ActivityName,
- StoreItemList = storeItemList
+ StoreList = storeList
};
}
catch (Exception ex)
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
index 9101b8c..e79ab7c 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
@@ -1073,45 +1073,62 @@ namespace NCC.Extend.LqStatistics
var startDate = new DateTime(now.Year, now.Month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);
- // 构建查询参数
- var parameters = new Dictionary { { "@startDate", startDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", endDate.ToString("yyyy-MM-dd 23:59:59") } };
+ // 获取当前月份(YYYYMM格式)
+ var currentMonth = now.ToString("yyyyMM");
- // 查询本月全门店统计数据
- var sql =
- @"
+ // 1. 获取本月的门店目标业绩总和(从门店目标表获取 F_StoreTarget 字段,按月份过滤)
+ var targetPerformanceSql = @"
SELECT
- SUM(target_performance) AS TotalTargetPerformance,
- SUM(actual_performance) AS TotalActualPerformance,
- CASE
- WHEN SUM(target_performance) > 0
- THEN (SUM(actual_performance) / SUM(target_performance)) * 100
- ELSE 0
- END AS TotalCompletionRate
- FROM v_store_performance_simple";
+ COALESCE(SUM(F_StoreTarget), 0) AS TotalTargetPerformance
+ FROM lq_md_target
+ WHERE F_Month = @month";
- var result = await _db.Ado.SqlQueryAsync(sql, parameters);
- var stats = result.FirstOrDefault();
+ var targetParameters = new Dictionary
+ {
+ { "@month", currentMonth }
+ };
- if (stats != null)
+ var targetResult = await _db.Ado.SqlQueryAsync(targetPerformanceSql, targetParameters);
+ var targetPerformance = 0m;
+ if (targetResult != null && targetResult.Any())
{
- var targetPerformance = Convert.ToDecimal(stats.TotalTargetPerformance ?? 0);
- var actualPerformance = Convert.ToDecimal(stats.TotalActualPerformance ?? 0);
- var completionRate = Convert.ToDecimal(stats.TotalCompletionRate ?? 0);
+ targetPerformance = Convert.ToDecimal(targetResult.FirstOrDefault()?.TotalTargetPerformance ?? 0);
+ }
- return new
- {
- TargetPerformance = targetPerformance,
- ActualPerformance = actualPerformance,
- CompletionRate = completionRate,
- Month = now.ToString("yyyy年MM月"),
- };
+ // 2. 从开单记录表获取本月实付金额的总和(sfyj 字段)
+ var actualPerformanceSql = @"
+ SELECT
+ COALESCE(SUM(sfyj), 0) AS TotalActualPerformance
+ FROM lq_kd_kdjlb
+ WHERE F_IsEffective = 1
+ AND kdrq >= @startDate
+ AND kdrq <= @endDate";
+
+ var parameters = new Dictionary
+ {
+ { "@startDate", startDate.ToString("yyyy-MM-dd 00:00:00") },
+ { "@endDate", endDate.ToString("yyyy-MM-dd 23:59:59") }
+ };
+
+ var actualResult = await _db.Ado.SqlQueryAsync(actualPerformanceSql, parameters);
+ var actualPerformance = 0m;
+ if (actualResult != null && actualResult.Any())
+ {
+ actualPerformance = Convert.ToDecimal(actualResult.FirstOrDefault()?.TotalActualPerformance ?? 0);
+ }
+
+ // 3. 计算完成率
+ var completionRate = 0m;
+ if (targetPerformance > 0)
+ {
+ completionRate = (actualPerformance / targetPerformance) * 100m;
}
return new
{
- TargetPerformance = 0m,
- ActualPerformance = 0m,
- CompletionRate = 0m,
+ TargetPerformance = targetPerformance,
+ ActualPerformance = actualPerformance,
+ CompletionRate = decimal.Round(completionRate, 2),
Month = now.ToString("yyyy年MM月"),
};
}
@@ -3478,13 +3495,17 @@ namespace NCC.Extend.LqStatistics
/// - BillingCount: 开单数量
/// - BillingAmount: 开单金额
/// - BillingProjectCount: 开单项目数(项目次数总和)
- /// - ConsumeCount: 消耗数量
+ /// - ConsumeCount: 消耗单数(消耗主表的去重记录数)
/// - ConsumeAmount: 消耗金额
- /// - ConsumeProjectCount: 消耗项目数(项目次数总和)
- /// - RefundCount: 退卡数量
+ /// - ConsumeProjectCount: 消耗项目数(项目次数总和,包含原始+加班+陪同)
+ /// - ConsumeOriginalProjectCount: 消耗原始项目数(原始项目次数总和)
+ /// - ConsumeOvertimeProjectCount: 消耗加班项目数(加班项目次数总和)
+ /// - ConsumeAccompaniedProjectCount: 消耗陪同项目数(陪同项目次数总和)
+ /// - RefundCount: 退卡单数量
/// - RefundAmount: 退卡金额
/// - HeadCount: 人头(月度去重客户数)
/// - PersonCount: 人次(日度去重客户数)
+ /// - LaborCost: 手工费(消耗手工费总和)
///
/// 查询参数
/// 员工业绩统计数据
@@ -3532,8 +3553,11 @@ namespace NCC.Extend.LqStatistics
// 9. 开单项目数统计
var billingProjectCount = await GetBillingProjectCount(input.UserId, statisticsMonth);
- // 10. 消耗项目数统计
- var consumeProjectCount = await GetConsumeProjectCount(input.UserId, statisticsMonth);
+ // 10. 消耗项目数统计(包含总项目数、原始项目数、加班项目数、陪同项目数)
+ var consumeProjectStats = await GetConsumeProjectCountDetails(input.UserId, statisticsMonth);
+
+ // 11. 手工费统计
+ var laborCost = await GetLaborCost(input.UserId, statisticsMonth);
return new EmployeePerformanceStatisticsOutput
{
@@ -3551,7 +3575,11 @@ namespace NCC.Extend.LqStatistics
HeadCount = headCount,
PersonCount = personCount,
BillingProjectCount = billingProjectCount,
- ConsumeProjectCount = consumeProjectCount
+ ConsumeProjectCount = consumeProjectStats.TotalCount,
+ ConsumeOriginalProjectCount = consumeProjectStats.OriginalCount,
+ ConsumeOvertimeProjectCount = consumeProjectStats.OvertimeCount,
+ ConsumeAccompaniedProjectCount = consumeProjectStats.AccompaniedCount,
+ LaborCost = laborCost
};
}
catch (Exception ex)
@@ -3630,9 +3658,10 @@ namespace NCC.Extend.LqStatistics
///
private async Task<(int Count, decimal Amount)> GetConsumeStats(string userId, string month)
{
+ // 统计消耗单数(消耗主表的去重记录数)和消耗金额(健康师业绩总和)
var sql = $@"
SELECT
- COUNT(*) as Count,
+ COUNT(DISTINCT hyhk.F_Id) as Count,
COALESCE(SUM(jksyj.jksyj), 0) as Amount
FROM lq_xh_jksyj jksyj
INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
@@ -3651,14 +3680,17 @@ namespace NCC.Extend.LqStatistics
///
private async Task<(int Count, decimal Amount)> GetRefundStats(string userId, string month)
{
+ // 统计退卡单数(退卡主表的去重记录数)和退卡金额(健康师业绩总和)
var sql = $@"
SELECT
- COUNT(*) as Count,
- COALESCE(SUM(jksyj), 0) as Amount
- FROM lq_hytk_jksyj
- WHERE jkszh = '{userId}'
- AND F_IsEffective = 1
- AND DATE_FORMAT(tksj, '%Y%m') = '{month}'";
+ COUNT(DISTINCT hytk.F_Id) as Count,
+ COALESCE(SUM(jksyj.jksyj), 0) as Amount
+ FROM lq_hytk_jksyj jksyj
+ INNER JOIN lq_hytk_hytk hytk ON jksyj.gltkbh = hytk.F_Id
+ WHERE jksyj.jkszh = '{userId}'
+ AND jksyj.F_IsEffective = 1
+ AND hytk.F_IsEffective = 1
+ AND DATE_FORMAT(hytk.tksj, '%Y%m') = '{month}'";
var result = await _db.Ado.SqlQueryAsync(sql);
var data = result.FirstOrDefault();
@@ -3737,6 +3769,53 @@ namespace NCC.Extend.LqStatistics
return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0);
}
+ ///
+ /// 统计消耗项目数详情(包含总项目数、原始项目数、加班项目数、陪同项目数)
+ ///
+ private async Task<(int TotalCount, decimal OriginalCount, decimal OvertimeCount, decimal AccompaniedCount)> GetConsumeProjectCountDetails(string userId, string month)
+ {
+ var sql = $@"
+ SELECT
+ COALESCE(SUM(pxmx.F_ProjectNumber), 0) as TotalCount,
+ COALESCE(SUM(COALESCE(pxmx.F_OriginalProjectNumber, pxmx.F_ProjectNumber)), 0) as OriginalCount,
+ COALESCE(SUM(COALESCE(pxmx.F_OvertimeProjectNumber, 0)), 0) as OvertimeCount,
+ COALESCE(SUM(COALESCE(jksyj.F_AccompaniedProjectNumber, 0)), 0) as AccompaniedCount
+ FROM lq_xh_jksyj jksyj
+ INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
+ INNER JOIN lq_xh_pxmx pxmx ON pxmx.F_ConsumeInfoId = hyhk.F_Id
+ WHERE jksyj.jkszh = '{userId}'
+ AND jksyj.F_IsEffective = 1
+ AND hyhk.F_IsEffective = 1
+ AND DATE_FORMAT(hyhk.hksj, '%Y%m') = '{month}'";
+
+ var result = await _db.Ado.SqlQueryAsync(sql);
+ var data = result.FirstOrDefault();
+ return (
+ Convert.ToInt32(data?.TotalCount ?? 0),
+ Convert.ToDecimal(data?.OriginalCount ?? 0),
+ Convert.ToDecimal(data?.OvertimeCount ?? 0),
+ Convert.ToDecimal(data?.AccompaniedCount ?? 0)
+ );
+ }
+
+ ///
+ /// 统计手工费(消耗手工费总和)
+ ///
+ private async Task GetLaborCost(string userId, string month)
+ {
+ var sql = $@"
+ SELECT COALESCE(SUM(jksyj.F_LaborCost), 0) as LaborCost
+ FROM lq_xh_jksyj jksyj
+ INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
+ WHERE jksyj.jkszh = '{userId}'
+ AND jksyj.F_IsEffective = 1
+ AND hyhk.F_IsEffective = 1
+ AND DATE_FORMAT(hyhk.hksj, '%Y%m') = '{month}'";
+
+ var result = await _db.Ado.SqlQueryAsync(sql);
+ return Convert.ToDecimal(result.FirstOrDefault()?.LaborCost ?? 0);
+ }
+
#endregion
}
}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
index 63bccf5..dac043f 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
@@ -964,6 +964,13 @@ namespace NCC.Extend.LqXhHyhk
{
// 开启事务
_db.BeginTran();
+ // 查询会员信息
+ var memberInfo = await _db.Queryable().Where(w => w.Id == entity.Hy).FirstAsync();
+ //如果会员类型是线索,那么就更新为新客
+ if (memberInfo.Khlx == MemberTypeEnum.线索.GetHashCode().ToString())
+ {
+ memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString();
+ }
// 新增会员耗卡记录
var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync();
// 收集所有需要插入的实体,然后批量插入