Commit 6f98913a43d5200b84fdd47dab62f347a54a8f08
Merge branch 'master' of http://39.98.150.180/antissoft/lvqianmeiye_ERP
Showing
9 changed files
with
494 additions
and
149 deletions
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitPerformanceCompletionOutput.cs
| @@ -24,12 +24,27 @@ namespace NCC.Extend.Entitys.Dto.LqDailyReport | @@ -24,12 +24,27 @@ namespace NCC.Extend.Entitys.Dto.LqDailyReport | ||
| 24 | public decimal TargetPerformance { get; set; } | 24 | public decimal TargetPerformance { get; set; } |
| 25 | 25 | ||
| 26 | /// <summary> | 26 | /// <summary> |
| 27 | - /// 完成业绩(管理门店的开单业绩总和) | 27 | + /// 开单业绩(管理门店的开单业绩总和) |
| 28 | + /// </summary> | ||
| 29 | + public decimal BillingPerformance { get; set; } | ||
| 30 | + | ||
| 31 | + /// <summary> | ||
| 32 | + /// 退款业绩(退卡业绩总和) | ||
| 33 | + /// </summary> | ||
| 34 | + public decimal RefundPerformance { get; set; } | ||
| 35 | + | ||
| 36 | + /// <summary> | ||
| 37 | + /// 开单业绩(管理门店的开单业绩总和) | ||
| 38 | + /// </summary> | ||
| 39 | + public decimal ActualPerformance { get; set; } | ||
| 40 | + | ||
| 41 | + /// <summary> | ||
| 42 | + /// 实际完成业绩(开单业绩 - 退款业绩) | ||
| 28 | /// </summary> | 43 | /// </summary> |
| 29 | public decimal CompletedPerformance { get; set; } | 44 | public decimal CompletedPerformance { get; set; } |
| 30 | 45 | ||
| 31 | /// <summary> | 46 | /// <summary> |
| 32 | - /// 完成率(百分比) | 47 | + /// 完成率(百分比,基于实际完成业绩计算) |
| 33 | /// </summary> | 48 | /// </summary> |
| 34 | public decimal CompletionRate { get; set; } | 49 | public decimal CompletionRate { get; set; } |
| 35 | 50 |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TianwangGroupPerformanceCompletionOutput.cs
| @@ -24,12 +24,27 @@ namespace NCC.Extend.Entitys.Dto.LqDailyReport | @@ -24,12 +24,27 @@ namespace NCC.Extend.Entitys.Dto.LqDailyReport | ||
| 24 | public decimal TargetPerformance { get; set; } | 24 | public decimal TargetPerformance { get; set; } |
| 25 | 25 | ||
| 26 | /// <summary> | 26 | /// <summary> |
| 27 | - /// 完成业绩(管理门店的开单业绩总和) | 27 | + /// 开单业绩(管理门店的开单业绩总和) |
| 28 | + /// </summary> | ||
| 29 | + public decimal BillingPerformance { get; set; } | ||
| 30 | + | ||
| 31 | + /// <summary> | ||
| 32 | + /// 退款业绩(退卡业绩总和) | ||
| 33 | + /// </summary> | ||
| 34 | + public decimal RefundPerformance { get; set; } | ||
| 35 | + | ||
| 36 | + /// <summary> | ||
| 37 | + /// 开单业绩(与BillingPerformance相同,用于兼容) | ||
| 38 | + /// </summary> | ||
| 39 | + public decimal ActualPerformance { get; set; } | ||
| 40 | + | ||
| 41 | + /// <summary> | ||
| 42 | + /// 实际完成业绩(开单业绩 - 退款业绩) | ||
| 28 | /// </summary> | 43 | /// </summary> |
| 29 | public decimal CompletedPerformance { get; set; } | 44 | public decimal CompletedPerformance { get; set; } |
| 30 | 45 | ||
| 31 | /// <summary> | 46 | /// <summary> |
| 32 | - /// 完成率(百分比) | 47 | + /// 完成率(百分比,基于实际完成业绩计算) |
| 33 | /// </summary> | 48 | /// </summary> |
| 34 | public decimal CompletionRate { get; set; } | 49 | public decimal CompletionRate { get; set; } |
| 35 | 50 |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqPackageInfo/ActivityStatisticsByStoreItemOutput.cs
| @@ -18,15 +18,15 @@ namespace NCC.Extend.Entitys.Dto.LqPackageInfo | @@ -18,15 +18,15 @@ namespace NCC.Extend.Entitys.Dto.LqPackageInfo | ||
| 18 | public string ActivityName { get; set; } | 18 | public string ActivityName { get; set; } |
| 19 | 19 | ||
| 20 | /// <summary> | 20 | /// <summary> |
| 21 | - /// 门店品项统计列表 | 21 | + /// 门店统计列表(层级结构:每个门店下包含品项列表) |
| 22 | /// </summary> | 22 | /// </summary> |
| 23 | - public List<StoreItemStatisticsItem> StoreItemList { get; set; } | 23 | + public List<StoreStatisticsWithItems> StoreList { get; set; } |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | /// <summary> | 26 | /// <summary> |
| 27 | - /// 门店品项统计项 | 27 | + /// 门店统计(包含品项列表) |
| 28 | /// </summary> | 28 | /// </summary> |
| 29 | - public class StoreItemStatisticsItem | 29 | + public class StoreStatisticsWithItems |
| 30 | { | 30 | { |
| 31 | /// <summary> | 31 | /// <summary> |
| 32 | /// 门店ID | 32 | /// 门店ID |
| @@ -39,6 +39,17 @@ namespace NCC.Extend.Entitys.Dto.LqPackageInfo | @@ -39,6 +39,17 @@ namespace NCC.Extend.Entitys.Dto.LqPackageInfo | ||
| 39 | public string StoreName { get; set; } | 39 | public string StoreName { get; set; } |
| 40 | 40 | ||
| 41 | /// <summary> | 41 | /// <summary> |
| 42 | + /// 该门店下的品项统计列表 | ||
| 43 | + /// </summary> | ||
| 44 | + public List<StoreItemStatisticsItem> ItemList { get; set; } | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + /// <summary> | ||
| 48 | + /// 门店品项统计项 | ||
| 49 | + /// </summary> | ||
| 50 | + public class StoreItemStatisticsItem | ||
| 51 | + { | ||
| 52 | + /// <summary> | ||
| 42 | /// 品项ID | 53 | /// 品项ID |
| 43 | /// </summary> | 54 | /// </summary> |
| 44 | public string ItemId { get; set; } | 55 | public string ItemId { get; set; } |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/EmployeePerformanceStatisticsOutput.cs
| @@ -41,7 +41,7 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics | @@ -41,7 +41,7 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics | ||
| 41 | public decimal BillingAmount { get; set; } | 41 | public decimal BillingAmount { get; set; } |
| 42 | 42 | ||
| 43 | /// <summary> | 43 | /// <summary> |
| 44 | - /// 消耗数量 | 44 | + /// 消耗单数(消耗主表的去重记录数) |
| 45 | /// </summary> | 45 | /// </summary> |
| 46 | public int ConsumeCount { get; set; } | 46 | public int ConsumeCount { get; set; } |
| 47 | 47 | ||
| @@ -51,7 +51,7 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics | @@ -51,7 +51,7 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics | ||
| 51 | public decimal ConsumeAmount { get; set; } | 51 | public decimal ConsumeAmount { get; set; } |
| 52 | 52 | ||
| 53 | /// <summary> | 53 | /// <summary> |
| 54 | - /// 退卡数量 | 54 | + /// 退卡单数量(退卡主表的去重记录数) |
| 55 | /// </summary> | 55 | /// </summary> |
| 56 | public int RefundCount { get; set; } | 56 | public int RefundCount { get; set; } |
| 57 | 57 | ||
| @@ -76,8 +76,28 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics | @@ -76,8 +76,28 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics | ||
| 76 | public int BillingProjectCount { get; set; } | 76 | public int BillingProjectCount { get; set; } |
| 77 | 77 | ||
| 78 | /// <summary> | 78 | /// <summary> |
| 79 | - /// 消耗项目数(项目次数总和) | 79 | + /// 消耗项目数(项目次数总和,包含原始+加班+陪同) |
| 80 | /// </summary> | 80 | /// </summary> |
| 81 | public int ConsumeProjectCount { get; set; } | 81 | public int ConsumeProjectCount { get; set; } |
| 82 | + | ||
| 83 | + /// <summary> | ||
| 84 | + /// 消耗原始项目数(原始项目次数总和) | ||
| 85 | + /// </summary> | ||
| 86 | + public decimal ConsumeOriginalProjectCount { get; set; } | ||
| 87 | + | ||
| 88 | + /// <summary> | ||
| 89 | + /// 消耗加班项目数(加班项目次数总和) | ||
| 90 | + /// </summary> | ||
| 91 | + public decimal ConsumeOvertimeProjectCount { get; set; } | ||
| 92 | + | ||
| 93 | + /// <summary> | ||
| 94 | + /// 消耗陪同项目数(陪同项目次数总和) | ||
| 95 | + /// </summary> | ||
| 96 | + public decimal ConsumeAccompaniedProjectCount { get; set; } | ||
| 97 | + | ||
| 98 | + /// <summary> | ||
| 99 | + /// 手工费(消耗手工费总和) | ||
| 100 | + /// </summary> | ||
| 101 | + public decimal LaborCost { get; set; } | ||
| 82 | } | 102 | } |
| 83 | } | 103 | } |
netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
| @@ -13,6 +13,7 @@ using System; | @@ -13,6 +13,7 @@ using System; | ||
| 13 | using System.Collections.Generic; | 13 | using System.Collections.Generic; |
| 14 | using System.Linq; | 14 | using System.Linq; |
| 15 | using System.Text; | 15 | using System.Text; |
| 16 | +using System.Text.RegularExpressions; | ||
| 16 | using System.Threading.Tasks; | 17 | using System.Threading.Tasks; |
| 17 | using Yitter.IdGenerator; | 18 | using Yitter.IdGenerator; |
| 18 | using NCC.Common.Helper; | 19 | using NCC.Common.Helper; |
| @@ -124,7 +125,7 @@ namespace NCC.Extend | @@ -124,7 +125,7 @@ namespace NCC.Extend | ||
| 124 | storeFilter = $"AND consume.Md IN ('{storeIdsStr}')"; | 125 | storeFilter = $"AND consume.Md IN ('{storeIdsStr}')"; |
| 125 | } | 126 | } |
| 126 | 127 | ||
| 127 | - // SQL查询:获取门店每日运营数据 | 128 | + // SQL查询:获取门店每日运营数据(只统计zxzt为"开店"的门店) |
| 128 | var sql = $@" | 129 | var sql = $@" |
| 129 | SELECT | 130 | SELECT |
| 130 | consume.Md as StoreId, | 131 | consume.Md as StoreId, |
| @@ -147,7 +148,7 @@ namespace NCC.Extend | @@ -147,7 +148,7 @@ namespace NCC.Extend | ||
| 147 | -- 消耗业绩(总金额) | 148 | -- 消耗业绩(总金额) |
| 148 | COALESCE(SUM(project.F_TotalPrice), 0) as ConsumePerformance | 149 | COALESCE(SUM(project.F_TotalPrice), 0) as ConsumePerformance |
| 149 | FROM lq_xh_hyhk consume | 150 | FROM lq_xh_hyhk consume |
| 150 | - LEFT JOIN lq_mdxx store ON consume.Md = store.F_Id | 151 | + INNER JOIN lq_mdxx store ON consume.Md = store.F_Id AND store.zxzt = '开店' |
| 151 | LEFT JOIN lq_xh_pxmx project ON consume.F_Id = project.F_ConsumeInfoId AND project.F_IsEffective = 1 | 152 | LEFT JOIN lq_xh_pxmx project ON consume.F_Id = project.F_ConsumeInfoId AND project.F_IsEffective = 1 |
| 152 | WHERE consume.F_IsEffective = 1 | 153 | WHERE consume.F_IsEffective = 1 |
| 153 | AND DATE(consume.Hksj) >= '{startDate:yyyy-MM-dd}' | 154 | AND DATE(consume.Hksj) >= '{startDate:yyyy-MM-dd}' |
| @@ -226,7 +227,7 @@ namespace NCC.Extend | @@ -226,7 +227,7 @@ namespace NCC.Extend | ||
| 226 | storeFilter = $"AND store.F_Id IN ('{storeIdsStr}')"; | 227 | storeFilter = $"AND store.F_Id IN ('{storeIdsStr}')"; |
| 227 | } | 228 | } |
| 228 | 229 | ||
| 229 | - // SQL查询:获取门店业绩完成情况 | 230 | + // SQL查询:获取门店业绩完成情况(只统计zxzt为"开店"的门店) |
| 230 | var sql = $@" | 231 | var sql = $@" |
| 231 | SELECT | 232 | SELECT |
| 232 | store.F_Id as StoreId, | 233 | store.F_Id as StoreId, |
| @@ -253,7 +254,7 @@ namespace NCC.Extend | @@ -253,7 +254,7 @@ namespace NCC.Extend | ||
| 253 | ), 0) as RefundPerformance | 254 | ), 0) as RefundPerformance |
| 254 | FROM lq_mdxx store | 255 | FROM lq_mdxx store |
| 255 | LEFT JOIN lq_md_target target ON target.F_StoreId = store.F_Id AND target.F_Month = '{month}' | 256 | LEFT JOIN lq_md_target target ON target.F_StoreId = store.F_Id AND target.F_Month = '{month}' |
| 256 | - WHERE 1=1 {storeFilter} | 257 | + WHERE store.zxzt = '开店' {storeFilter} |
| 257 | ORDER BY (BillingPerformance - RefundPerformance) DESC"; | 258 | ORDER BY (BillingPerformance - RefundPerformance) DESC"; |
| 258 | 259 | ||
| 259 | var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | 260 | var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); |
| @@ -288,7 +289,7 @@ namespace NCC.Extend | @@ -288,7 +289,7 @@ namespace NCC.Extend | ||
| 288 | /// 获取事业部业绩完成情况 | 289 | /// 获取事业部业绩完成情况 |
| 289 | /// </summary> | 290 | /// </summary> |
| 290 | /// <remarks> | 291 | /// <remarks> |
| 291 | - /// 根据指定日期统计各事业部的业绩完成情况,包括目标业绩、完成业绩、完成率 | 292 | + /// 根据指定日期统计各事业部的业绩完成情况,包括目标业绩、开单业绩、退卡金额、实际完成业绩、完成率 |
| 292 | /// | 293 | /// |
| 293 | /// 示例请求: | 294 | /// 示例请求: |
| 294 | /// ```json | 295 | /// ```json |
| @@ -308,7 +309,10 @@ namespace NCC.Extend | @@ -308,7 +309,10 @@ namespace NCC.Extend | ||
| 308 | /// - BusinessUnitId: 事业部ID | 309 | /// - BusinessUnitId: 事业部ID |
| 309 | /// - BusinessUnitName: 事业部名称 | 310 | /// - BusinessUnitName: 事业部名称 |
| 310 | /// - TargetPerformance: 目标业绩(来自门店目标表lq_md_target的F_BusinessUnitTarget字段,根据开始时间所在月份获取,通过F_BusinessUnit字段关联,如果未查询到则为0) | 311 | /// - TargetPerformance: 目标业绩(来自门店目标表lq_md_target的F_BusinessUnitTarget字段,根据开始时间所在月份获取,通过F_BusinessUnit字段关联,如果未查询到则为0) |
| 311 | - /// - CompletedPerformance: 完成业绩(指定时间范围内的开单业绩总和) | 312 | + /// - BillingPerformance: 开单业绩(指定时间范围内的开单业绩总和) |
| 313 | + /// - RefundPerformance: 退款业绩(指定时间范围内的退卡业绩总和) | ||
| 314 | + /// - ActualPerformance: 开单业绩(与BillingPerformance相同,用于兼容) | ||
| 315 | + /// - CompletedPerformance: 实际完成业绩(开单业绩 - 退款业绩) | ||
| 312 | /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100) | 316 | /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100) |
| 313 | /// - StoreCount: 门店数量(根据门店目标表中归属该事业部的门店数统计) | 317 | /// - StoreCount: 门店数量(根据门店目标表中归属该事业部的门店数统计) |
| 314 | /// </remarks> | 318 | /// </remarks> |
| @@ -367,20 +371,23 @@ namespace NCC.Extend | @@ -367,20 +371,23 @@ namespace NCC.Extend | ||
| 367 | BusinessUnitId = unit.BusinessUnitId.ToString(), | 371 | BusinessUnitId = unit.BusinessUnitId.ToString(), |
| 368 | BusinessUnitName = unit.BusinessUnitName.ToString(), | 372 | BusinessUnitName = unit.BusinessUnitName.ToString(), |
| 369 | TargetPerformance = Convert.ToDecimal(unit.TargetPerformance), | 373 | TargetPerformance = Convert.ToDecimal(unit.TargetPerformance), |
| 374 | + BillingPerformance = 0, | ||
| 375 | + RefundPerformance = 0, | ||
| 376 | + ActualPerformance = 0, | ||
| 370 | CompletedPerformance = 0, | 377 | CompletedPerformance = 0, |
| 371 | CompletionRate = 0, | 378 | CompletionRate = 0, |
| 372 | StoreCount = Convert.ToInt32(unit.StoreCount) | 379 | StoreCount = Convert.ToInt32(unit.StoreCount) |
| 373 | }; | 380 | }; |
| 374 | } | 381 | } |
| 375 | 382 | ||
| 376 | - // 第二步:统计各事业部的完成业绩 | 383 | + // 第二步:统计各事业部的开单业绩 |
| 377 | var businessUnitIds = businessUnitDict.Keys.ToList(); | 384 | var businessUnitIds = businessUnitDict.Keys.ToList(); |
| 378 | var unitIdsStr = string.Join("','", businessUnitIds); | 385 | var unitIdsStr = string.Join("','", businessUnitIds); |
| 379 | 386 | ||
| 380 | - var completedPerformanceSql = $@" | 387 | + var billingPerformanceSql = $@" |
| 381 | SELECT | 388 | SELECT |
| 382 | store.syb as BusinessUnitId, | 389 | store.syb as BusinessUnitId, |
| 383 | - COALESCE(SUM(billing.sfyj), 0) as CompletedPerformance | 390 | + COALESCE(SUM(billing.sfyj), 0) as BillingPerformance |
| 384 | FROM lq_kd_kdjlb billing | 391 | FROM lq_kd_kdjlb billing |
| 385 | INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id | 392 | INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id |
| 386 | INNER JOIN base_organize o ON store.syb = o.F_Id | 393 | INNER JOIN base_organize o ON store.syb = o.F_Id |
| @@ -393,28 +400,58 @@ namespace NCC.Extend | @@ -393,28 +400,58 @@ namespace NCC.Extend | ||
| 393 | AND store.syb IN ('{unitIdsStr}') | 400 | AND store.syb IN ('{unitIdsStr}') |
| 394 | GROUP BY store.syb"; | 401 | GROUP BY store.syb"; |
| 395 | 402 | ||
| 396 | - var completedPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(completedPerformanceSql); | 403 | + var billingPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(billingPerformanceSql); |
| 404 | + | ||
| 405 | + // 第三步:统计各事业部的退卡金额 | ||
| 406 | + var refundPerformanceSql = $@" | ||
| 407 | + SELECT | ||
| 408 | + store.syb as BusinessUnitId, | ||
| 409 | + COALESCE(SUM(refund.F_ActualRefundAmount), 0) as RefundPerformance | ||
| 410 | + FROM lq_hytk_hytk refund | ||
| 411 | + INNER JOIN lq_mdxx store ON refund.md = store.F_Id | ||
| 412 | + INNER JOIN base_organize o ON store.syb = o.F_Id | ||
| 413 | + WHERE refund.F_IsEffective = 1 | ||
| 414 | + AND o.F_Category = 'department' | ||
| 415 | + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1) | ||
| 416 | + AND o.F_FullName IN ('事业一部', '事业二部', '事业三部', '事业四部', '事业五部', '事业六部') | ||
| 417 | + AND DATE(refund.tksj) >= '{startDate:yyyy-MM-dd}' | ||
| 418 | + AND DATE(refund.tksj) <= '{endDate:yyyy-MM-dd}' | ||
| 419 | + AND store.syb IN ('{unitIdsStr}') | ||
| 420 | + GROUP BY store.syb"; | ||
| 421 | + | ||
| 422 | + var refundPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(refundPerformanceSql); | ||
| 397 | 423 | ||
| 398 | - // 第三步:合并数据并计算完成率 | ||
| 399 | - foreach (var item in completedPerformanceData) | 424 | + // 第四步:合并开单业绩数据 |
| 425 | + foreach (var item in billingPerformanceData) | ||
| 400 | { | 426 | { |
| 401 | var businessUnitId = item.BusinessUnitId.ToString(); | 427 | var businessUnitId = item.BusinessUnitId.ToString(); |
| 402 | if (businessUnitDict.ContainsKey(businessUnitId)) | 428 | if (businessUnitDict.ContainsKey(businessUnitId)) |
| 403 | { | 429 | { |
| 404 | - var completedPerformance = Convert.ToDecimal(item.CompletedPerformance); | ||
| 405 | - var unit = businessUnitDict[businessUnitId]; | ||
| 406 | - unit.CompletedPerformance = completedPerformance; | ||
| 407 | - | ||
| 408 | - var completionRate = unit.TargetPerformance > 0 | ||
| 409 | - ? (completedPerformance / unit.TargetPerformance * 100m) | ||
| 410 | - : 0m; | ||
| 411 | - unit.CompletionRate = decimal.Round(completionRate, 2); | 430 | + businessUnitDict[businessUnitId].BillingPerformance = Convert.ToDecimal(item.BillingPerformance); |
| 412 | } | 431 | } |
| 413 | } | 432 | } |
| 414 | 433 | ||
| 415 | - var outputList = businessUnitDict.Values | ||
| 416 | - .OrderByDescending(x => x.CompletedPerformance) | ||
| 417 | - .ToList(); | 434 | + // 第五步:合并退卡金额数据 |
| 435 | + foreach (var item in refundPerformanceData) | ||
| 436 | + { | ||
| 437 | + var businessUnitId = item.BusinessUnitId.ToString(); | ||
| 438 | + if (businessUnitDict.ContainsKey(businessUnitId)) | ||
| 439 | + { | ||
| 440 | + businessUnitDict[businessUnitId].RefundPerformance = Convert.ToDecimal(item.RefundPerformance); | ||
| 441 | + } | ||
| 442 | + } | ||
| 443 | + | ||
| 444 | + // 第六步:计算实际完成业绩和完成率 | ||
| 445 | + foreach (var unit in businessUnitDict.Values) | ||
| 446 | + { | ||
| 447 | + unit.ActualPerformance = unit.BillingPerformance; // 开单业绩 | ||
| 448 | + var actualCompletedPerformance = unit.BillingPerformance - unit.RefundPerformance; // 实际完成业绩 | ||
| 449 | + unit.CompletedPerformance = actualCompletedPerformance; // 实际完成业绩 | ||
| 450 | + var completionRate = unit.TargetPerformance > 0 ? (actualCompletedPerformance / unit.TargetPerformance * 100m) : 0m; | ||
| 451 | + unit.CompletionRate = decimal.Round(completionRate, 2); | ||
| 452 | + } | ||
| 453 | + | ||
| 454 | + var outputList = businessUnitDict.Values.OrderByDescending(x => x.CompletedPerformance).ToList(); | ||
| 418 | 455 | ||
| 419 | return outputList; | 456 | return outputList; |
| 420 | } | 457 | } |
| @@ -425,7 +462,7 @@ namespace NCC.Extend | @@ -425,7 +462,7 @@ namespace NCC.Extend | ||
| 425 | /// 获取天王团业绩完成情况 | 462 | /// 获取天王团业绩完成情况 |
| 426 | /// </summary> | 463 | /// </summary> |
| 427 | /// <remarks> | 464 | /// <remarks> |
| 428 | - /// 根据指定日期统计各天王团的业绩完成情况,包括目标业绩、完成业绩、完成率 | 465 | + /// 根据指定日期统计各天王团的业绩完成情况,包括目标业绩、开单业绩、退卡金额、实际完成业绩、完成率 |
| 429 | /// | 466 | /// |
| 430 | /// 示例请求: | 467 | /// 示例请求: |
| 431 | /// ```json | 468 | /// ```json |
| @@ -445,7 +482,10 @@ namespace NCC.Extend | @@ -445,7 +482,10 @@ namespace NCC.Extend | ||
| 445 | /// - DepartmentId: 部门ID | 482 | /// - DepartmentId: 部门ID |
| 446 | /// - DepartmentName: 部门名称(教育一部、教育二部、科技一部、科技二部、大项目一部、大项目二部) | 483 | /// - DepartmentName: 部门名称(教育一部、教育二部、科技一部、科技二部、大项目一部、大项目二部) |
| 447 | /// - TargetPerformance: 目标业绩(来自门店目标表lq_md_target,根据部门类型使用对应字段:教育部使用F_EducationDepartmentTarget,科技部使用F_TechDepartmentTarget,大项目部使用F_MajorProjectDepartmentTarget,根据开始时间所在月份获取,通过对应归属字段关联,如果未查询到则为0) | 484 | /// - TargetPerformance: 目标业绩(来自门店目标表lq_md_target,根据部门类型使用对应字段:教育部使用F_EducationDepartmentTarget,科技部使用F_TechDepartmentTarget,大项目部使用F_MajorProjectDepartmentTarget,根据开始时间所在月份获取,通过对应归属字段关联,如果未查询到则为0) |
| 448 | - /// - CompletedPerformance: 完成业绩(指定时间范围内的开单业绩总和) | 485 | + /// - BillingPerformance: 开单业绩(指定时间范围内的开单业绩总和) |
| 486 | + /// - RefundPerformance: 退款业绩(指定时间范围内的退卡业绩总和) | ||
| 487 | + /// - ActualPerformance: 开单业绩(与BillingPerformance相同,用于兼容) | ||
| 488 | + /// - CompletedPerformance: 实际完成业绩(开单业绩 - 退款业绩) | ||
| 449 | /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100) | 489 | /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100) |
| 450 | /// - StoreCount: 门店数量(根据门店目标表中归属该部门的门店数统计) | 490 | /// - StoreCount: 门店数量(根据门店目标表中归属该部门的门店数统计) |
| 451 | /// </remarks> | 491 | /// </remarks> |
| @@ -516,6 +556,9 @@ namespace NCC.Extend | @@ -516,6 +556,9 @@ namespace NCC.Extend | ||
| 516 | DepartmentId = dept.DepartmentId.ToString(), | 556 | DepartmentId = dept.DepartmentId.ToString(), |
| 517 | DepartmentName = dept.DepartmentName.ToString(), | 557 | DepartmentName = dept.DepartmentName.ToString(), |
| 518 | TargetPerformance = Convert.ToDecimal(dept.TargetPerformance), | 558 | TargetPerformance = Convert.ToDecimal(dept.TargetPerformance), |
| 559 | + BillingPerformance = 0, | ||
| 560 | + RefundPerformance = 0, | ||
| 561 | + ActualPerformance = 0, | ||
| 519 | CompletedPerformance = 0, | 562 | CompletedPerformance = 0, |
| 520 | CompletionRate = 0, | 563 | CompletionRate = 0, |
| 521 | StoreCount = Convert.ToInt32(dept.StoreCount) | 564 | StoreCount = Convert.ToInt32(dept.StoreCount) |
| @@ -529,65 +572,84 @@ namespace NCC.Extend | @@ -529,65 +572,84 @@ namespace NCC.Extend | ||
| 529 | return new List<TianwangGroupPerformanceCompletionOutput>(); | 572 | return new List<TianwangGroupPerformanceCompletionOutput>(); |
| 530 | } | 573 | } |
| 531 | 574 | ||
| 532 | - // 第二步:统计各天王团的完成业绩(分类型统计) | ||
| 533 | - var completedPerformanceDataList = new List<dynamic>(); | 575 | + // 第二步:统计各天王团的开单业绩(直接按部门ID统计,避免重复计算) |
| 576 | + var deptIdsStrForQuery = string.Join("','", departmentDict.Keys); | ||
| 534 | 577 | ||
| 535 | - foreach (var deptType in tianwangDepartments) | ||
| 536 | - { | ||
| 537 | - var deptIdsStrForQuery = string.Join("','", departmentDict.Keys); | 578 | + // 使用 OR 条件连接三个部门类型字段,确保每个开单记录只被统计一次到对应的部门 |
| 579 | + var billingPerformanceSql = $@" | ||
| 580 | + SELECT | ||
| 581 | + o.F_Id as DepartmentId, | ||
| 582 | + COALESCE(SUM(billing.sfyj), 0) as BillingPerformance | ||
| 583 | + FROM lq_kd_kdjlb billing | ||
| 584 | + INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id | ||
| 585 | + INNER JOIN base_organize o ON ( | ||
| 586 | + (store.jyb = o.F_Id AND o.F_FullName IN ('教育一部', '教育二部')) | ||
| 587 | + OR (store.kjb = o.F_Id AND o.F_FullName IN ('科技一部', '科技二部')) | ||
| 588 | + OR (store.dxmb = o.F_Id AND o.F_FullName IN ('大项目一部', '大项目二部')) | ||
| 589 | + ) | ||
| 590 | + WHERE billing.F_IsEffective = 1 | ||
| 591 | + AND o.F_Category = 'department' | ||
| 592 | + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1) | ||
| 593 | + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}' | ||
| 594 | + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}' | ||
| 595 | + AND o.F_Id IN ('{deptIdsStrForQuery}') | ||
| 596 | + GROUP BY o.F_Id"; | ||
| 538 | 597 | ||
| 539 | - var completedPerformanceSql = $@" | ||
| 540 | - SELECT | ||
| 541 | - o.F_Id as DepartmentId, | ||
| 542 | - COALESCE(SUM(billing.sfyj), 0) as CompletedPerformance | ||
| 543 | - FROM lq_kd_kdjlb billing | ||
| 544 | - INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id | ||
| 545 | - INNER JOIN base_organize o ON store.{deptType.Key} = o.F_Id | ||
| 546 | - WHERE billing.F_IsEffective = 1 | ||
| 547 | - AND o.F_Category = 'department' | ||
| 548 | - AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1) | ||
| 549 | - AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}' | ||
| 550 | - AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}' | ||
| 551 | - AND o.F_Id IN ('{deptIdsStrForQuery}') | ||
| 552 | - GROUP BY o.F_Id"; | 598 | + var billingPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(billingPerformanceSql); |
| 553 | 599 | ||
| 554 | - var completedPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(completedPerformanceSql); | ||
| 555 | - if (completedPerformanceData != null) | 600 | + // 第三步:统计各天王团的退卡金额 |
| 601 | + var refundPerformanceSql = $@" | ||
| 602 | + SELECT | ||
| 603 | + o.F_Id as DepartmentId, | ||
| 604 | + COALESCE(SUM(refund.F_ActualRefundAmount), 0) as RefundPerformance | ||
| 605 | + FROM lq_hytk_hytk refund | ||
| 606 | + INNER JOIN lq_mdxx store ON refund.md = store.F_Id | ||
| 607 | + INNER JOIN base_organize o ON ( | ||
| 608 | + (store.jyb = o.F_Id AND o.F_FullName IN ('教育一部', '教育二部')) | ||
| 609 | + OR (store.kjb = o.F_Id AND o.F_FullName IN ('科技一部', '科技二部')) | ||
| 610 | + OR (store.dxmb = o.F_Id AND o.F_FullName IN ('大项目一部', '大项目二部')) | ||
| 611 | + ) | ||
| 612 | + WHERE refund.F_IsEffective = 1 | ||
| 613 | + AND o.F_Category = 'department' | ||
| 614 | + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1) | ||
| 615 | + AND DATE(refund.tksj) >= '{startDate:yyyy-MM-dd}' | ||
| 616 | + AND DATE(refund.tksj) <= '{endDate:yyyy-MM-dd}' | ||
| 617 | + AND o.F_Id IN ('{deptIdsStrForQuery}') | ||
| 618 | + GROUP BY o.F_Id"; | ||
| 619 | + | ||
| 620 | + var refundPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(refundPerformanceSql); | ||
| 621 | + | ||
| 622 | + // 第四步:合并开单业绩数据 | ||
| 623 | + foreach (var item in billingPerformanceData ?? Enumerable.Empty<dynamic>()) | ||
| 624 | + { | ||
| 625 | + var departmentId = item.DepartmentId.ToString(); | ||
| 626 | + if (departmentDict.ContainsKey(departmentId)) | ||
| 556 | { | 627 | { |
| 557 | - completedPerformanceDataList.AddRange(completedPerformanceData); | 628 | + departmentDict[departmentId].BillingPerformance = Convert.ToDecimal(item.BillingPerformance); |
| 558 | } | 629 | } |
| 559 | } | 630 | } |
| 560 | 631 | ||
| 561 | - // 第三步:合并数据并计算完成率(按部门ID去重累加) | ||
| 562 | - var departmentPerformanceDict = new Dictionary<string, decimal>(); | ||
| 563 | - foreach (var item in completedPerformanceDataList) | 632 | + // 第五步:合并退卡金额数据 |
| 633 | + foreach (var item in refundPerformanceData ?? Enumerable.Empty<dynamic>()) | ||
| 564 | { | 634 | { |
| 565 | var departmentId = item.DepartmentId.ToString(); | 635 | var departmentId = item.DepartmentId.ToString(); |
| 566 | - var completedPerformance = Convert.ToDecimal(item.CompletedPerformance); | ||
| 567 | - | ||
| 568 | - if (departmentPerformanceDict.ContainsKey(departmentId)) | 636 | + if (departmentDict.ContainsKey(departmentId)) |
| 569 | { | 637 | { |
| 570 | - departmentPerformanceDict[departmentId] += completedPerformance; | ||
| 571 | - } | ||
| 572 | - else | ||
| 573 | - { | ||
| 574 | - departmentPerformanceDict[departmentId] = completedPerformance; | 638 | + departmentDict[departmentId].RefundPerformance = Convert.ToDecimal(item.RefundPerformance); |
| 575 | } | 639 | } |
| 576 | } | 640 | } |
| 577 | 641 | ||
| 578 | - // 计算完成率 | ||
| 579 | - foreach (var kvp in departmentPerformanceDict) | 642 | + // 第六步:计算实际完成业绩和完成率 |
| 643 | + foreach (var dept in departmentDict.Values) | ||
| 580 | { | 644 | { |
| 581 | - if (departmentDict.ContainsKey(kvp.Key)) | ||
| 582 | - { | ||
| 583 | - var dept = departmentDict[kvp.Key]; | ||
| 584 | - dept.CompletedPerformance = kvp.Value; | ||
| 585 | - | ||
| 586 | - var completionRate = dept.TargetPerformance > 0 | ||
| 587 | - ? (kvp.Value / dept.TargetPerformance * 100m) | ||
| 588 | - : 0m; | ||
| 589 | - dept.CompletionRate = decimal.Round(completionRate, 2); | ||
| 590 | - } | 645 | + dept.ActualPerformance = dept.BillingPerformance; // 开单业绩 |
| 646 | + var actualCompletedPerformance = dept.BillingPerformance - dept.RefundPerformance; // 实际完成业绩 | ||
| 647 | + dept.CompletedPerformance = actualCompletedPerformance; // 实际完成业绩 | ||
| 648 | + | ||
| 649 | + var completionRate = dept.TargetPerformance > 0 | ||
| 650 | + ? (actualCompletedPerformance / dept.TargetPerformance * 100m) | ||
| 651 | + : 0m; | ||
| 652 | + dept.CompletionRate = decimal.Round(completionRate, 2); | ||
| 591 | } | 653 | } |
| 592 | 654 | ||
| 593 | var outputList = departmentDict.Values | 655 | var outputList = departmentDict.Values |
| @@ -1069,7 +1131,9 @@ namespace NCC.Extend | @@ -1069,7 +1131,9 @@ namespace NCC.Extend | ||
| 1069 | }) | 1131 | }) |
| 1070 | .ToList(), | 1132 | .ToList(), |
| 1071 | }) | 1133 | }) |
| 1072 | - .OrderBy(x => x.BusinessUnitName) // 按事业部名称排序 | 1134 | + .ToList() |
| 1135 | + .OrderBy(x => ExtractBusinessUnitNumber(x.BusinessUnitName)) // 按事业部数字排序(事业一部、事业二部...) | ||
| 1136 | + .ThenBy(x => x.BusinessUnitName) // 如果数字相同,按名称排序 | ||
| 1073 | .ToList(); | 1137 | .ToList(); |
| 1074 | 1138 | ||
| 1075 | return result; | 1139 | return result; |
| @@ -1152,19 +1216,12 @@ namespace NCC.Extend | @@ -1152,19 +1216,12 @@ namespace NCC.Extend | ||
| 1152 | 1216 | ||
| 1153 | // 日期(格式:11月8日) | 1217 | // 日期(格式:11月8日) |
| 1154 | textBuilder.AppendLine($"{targetDate.Month}月{targetDate.Day}日"); | 1218 | textBuilder.AppendLine($"{targetDate.Month}月{targetDate.Day}日"); |
| 1155 | - textBuilder.AppendLine(); | ||
| 1156 | - | ||
| 1157 | // 今日总业绩和总单量 | 1219 | // 今日总业绩和总单量 |
| 1158 | textBuilder.AppendLine($"今日总业绩:{totalPerformance:F0}"); | 1220 | textBuilder.AppendLine($"今日总业绩:{totalPerformance:F0}"); |
| 1159 | - textBuilder.AppendLine(); | ||
| 1160 | textBuilder.AppendLine($"今日总单量:{totalOrderCount}"); | 1221 | textBuilder.AppendLine($"今日总单量:{totalOrderCount}"); |
| 1161 | - textBuilder.AppendLine(); | ||
| 1162 | - | ||
| 1163 | // 标语 | 1222 | // 标语 |
| 1164 | textBuilder.AppendLine("绿纤人用结果捍卫尊严 "); | 1223 | textBuilder.AppendLine("绿纤人用结果捍卫尊严 "); |
| 1165 | - textBuilder.AppendLine(); | ||
| 1166 | textBuilder.AppendLine("业绩捷报 "); | 1224 | textBuilder.AppendLine("业绩捷报 "); |
| 1167 | - textBuilder.AppendLine(); | ||
| 1168 | 1225 | ||
| 1169 | // 每个事业部的数据 | 1226 | // 每个事业部的数据 |
| 1170 | foreach (var unit in statistics) | 1227 | foreach (var unit in statistics) |
| @@ -1175,10 +1232,7 @@ namespace NCC.Extend | @@ -1175,10 +1232,7 @@ namespace NCC.Extend | ||
| 1175 | 1232 | ||
| 1176 | // 事业部业绩和单量 | 1233 | // 事业部业绩和单量 |
| 1177 | textBuilder.AppendLine($"{unit.BusinessUnitName}总业绩:{unit.TotalPerformance:F0}"); | 1234 | textBuilder.AppendLine($"{unit.BusinessUnitName}总业绩:{unit.TotalPerformance:F0}"); |
| 1178 | - textBuilder.AppendLine(); | ||
| 1179 | textBuilder.AppendLine($"{unit.BusinessUnitName}总单量:{unit.TotalOrderCount}"); | 1235 | textBuilder.AppendLine($"{unit.BusinessUnitName}总单量:{unit.TotalOrderCount}"); |
| 1180 | - textBuilder.AppendLine(); | ||
| 1181 | - | ||
| 1182 | // 按门店分组订单 | 1236 | // 按门店分组订单 |
| 1183 | var ordersByStore = unit.Orders | 1237 | var ordersByStore = unit.Orders |
| 1184 | .GroupBy(x => x.StoreName) | 1238 | .GroupBy(x => x.StoreName) |
| @@ -1207,7 +1261,6 @@ namespace NCC.Extend | @@ -1207,7 +1261,6 @@ namespace NCC.Extend | ||
| 1207 | { | 1261 | { |
| 1208 | textBuilder.AppendLine($"第{orderIndex}单:{order.StoreName}{teacherNames}{order.Amount:F0}"); | 1262 | textBuilder.AppendLine($"第{orderIndex}单:{order.StoreName}{teacherNames}{order.Amount:F0}"); |
| 1209 | } | 1263 | } |
| 1210 | - textBuilder.AppendLine(); | ||
| 1211 | orderIndex++; | 1264 | orderIndex++; |
| 1212 | } | 1265 | } |
| 1213 | } | 1266 | } |
| @@ -1220,6 +1273,84 @@ namespace NCC.Extend | @@ -1220,6 +1273,84 @@ namespace NCC.Extend | ||
| 1220 | throw NCCException.Oh($"获取事业部开单统计文本失败: {ex.Message}", ex); | 1273 | throw NCCException.Oh($"获取事业部开单统计文本失败: {ex.Message}", ex); |
| 1221 | } | 1274 | } |
| 1222 | } | 1275 | } |
| 1276 | + | ||
| 1277 | + /// <summary> | ||
| 1278 | + /// 从事业部名称中提取数字(用于排序) | ||
| 1279 | + /// 例如:"事业一部" -> 1, "事业二部" -> 2, "事业三部" -> 3 | ||
| 1280 | + /// </summary> | ||
| 1281 | + /// <param name="businessUnitName">事业部名称</param> | ||
| 1282 | + /// <returns>提取的数字,如果提取失败返回999(排在最后)</returns> | ||
| 1283 | + private int ExtractBusinessUnitNumber(string businessUnitName) | ||
| 1284 | + { | ||
| 1285 | + if (string.IsNullOrEmpty(businessUnitName)) | ||
| 1286 | + { | ||
| 1287 | + return 999; | ||
| 1288 | + } | ||
| 1289 | + | ||
| 1290 | + // 使用正则表达式提取数字 | ||
| 1291 | + var match = Regex.Match(businessUnitName, @"[一二三四五六七八九十]+|[\d]+"); | ||
| 1292 | + if (match.Success) | ||
| 1293 | + { | ||
| 1294 | + var numberStr = match.Value; | ||
| 1295 | + | ||
| 1296 | + // 如果是中文数字,转换为阿拉伯数字 | ||
| 1297 | + if (Regex.IsMatch(numberStr, @"[一二三四五六七八九十]+")) | ||
| 1298 | + { | ||
| 1299 | + var chineseNumbers = new Dictionary<string, int> | ||
| 1300 | + { | ||
| 1301 | + { "一", 1 }, { "二", 2 }, { "三", 3 }, { "四", 4 }, { "五", 5 }, | ||
| 1302 | + { "六", 6 }, { "七", 7 }, { "八", 8 }, { "九", 9 }, { "十", 10 } | ||
| 1303 | + }; | ||
| 1304 | + | ||
| 1305 | + // 处理"十"的情况(如"十一"、"十二"等) | ||
| 1306 | + if (numberStr == "十") | ||
| 1307 | + { | ||
| 1308 | + return 10; | ||
| 1309 | + } | ||
| 1310 | + else if (numberStr.StartsWith("十")) | ||
| 1311 | + { | ||
| 1312 | + // 十一、十二...十九 | ||
| 1313 | + var remainder = numberStr.Substring(1); | ||
| 1314 | + if (chineseNumbers.ContainsKey(remainder)) | ||
| 1315 | + { | ||
| 1316 | + return 10 + chineseNumbers[remainder]; | ||
| 1317 | + } | ||
| 1318 | + } | ||
| 1319 | + else if (numberStr.EndsWith("十")) | ||
| 1320 | + { | ||
| 1321 | + // 二十、三十...九十 | ||
| 1322 | + var prefix = numberStr.Substring(0, numberStr.Length - 1); | ||
| 1323 | + if (chineseNumbers.ContainsKey(prefix)) | ||
| 1324 | + { | ||
| 1325 | + return chineseNumbers[prefix] * 10; | ||
| 1326 | + } | ||
| 1327 | + } | ||
| 1328 | + else if (numberStr.Length == 2 && numberStr.Contains("十")) | ||
| 1329 | + { | ||
| 1330 | + // 二十一、二十二...九十九 | ||
| 1331 | + var parts = numberStr.Split('十'); | ||
| 1332 | + if (parts.Length == 2) | ||
| 1333 | + { | ||
| 1334 | + var tens = parts[0] == "" ? 1 : (chineseNumbers.ContainsKey(parts[0]) ? chineseNumbers[parts[0]] : 0); | ||
| 1335 | + var ones = parts[1] == "" ? 0 : (chineseNumbers.ContainsKey(parts[1]) ? chineseNumbers[parts[1]] : 0); | ||
| 1336 | + return tens * 10 + ones; | ||
| 1337 | + } | ||
| 1338 | + } | ||
| 1339 | + else if (chineseNumbers.ContainsKey(numberStr)) | ||
| 1340 | + { | ||
| 1341 | + return chineseNumbers[numberStr]; | ||
| 1342 | + } | ||
| 1343 | + } | ||
| 1344 | + else if (int.TryParse(numberStr, out var number)) | ||
| 1345 | + { | ||
| 1346 | + // 如果是阿拉伯数字,直接返回 | ||
| 1347 | + return number; | ||
| 1348 | + } | ||
| 1349 | + } | ||
| 1350 | + | ||
| 1351 | + // 如果提取失败,返回999(排在最后) | ||
| 1352 | + return 999; | ||
| 1353 | + } | ||
| 1223 | #endregion | 1354 | #endregion |
| 1224 | 1355 | ||
| 1225 | 1356 |
netcore/src/Modularity/Extend/NCC.Extend/LqMdxxService.cs
| @@ -361,6 +361,7 @@ namespace NCC.Extend.LqMdxx | @@ -361,6 +361,7 @@ namespace NCC.Extend.LqMdxx | ||
| 361 | public async Task<dynamic> GetSelector() | 361 | public async Task<dynamic> GetSelector() |
| 362 | { | 362 | { |
| 363 | var list = await _db.Queryable<LqMdxxEntity>() | 363 | var list = await _db.Queryable<LqMdxxEntity>() |
| 364 | + .Where(it => it.Zxzt == "开店") | ||
| 364 | .Select(it => new { id = it.Id, fullName = it.Dm, enCode = it.Mdbm }) | 365 | .Select(it => new { id = it.Id, fullName = it.Dm, enCode = it.Mdbm }) |
| 365 | .ToListAsync(); | 366 | .ToListAsync(); |
| 366 | return new { list = list }; | 367 | return new { list = list }; |
netcore/src/Modularity/Extend/NCC.Extend/LqPackageInfoService.cs
| @@ -901,6 +901,7 @@ namespace NCC.Extend.LqPackageInfo | @@ -901,6 +901,7 @@ namespace NCC.Extend.LqPackageInfo | ||
| 901 | } | 901 | } |
| 902 | 902 | ||
| 903 | // 5. 按品项分组统计(先获取基础统计数据) | 903 | // 5. 按品项分组统计(先获取基础统计数据) |
| 904 | + // 注意:SqlSugar 的 AggregateSum 可能不支持三元运算符,需要先查询数据再计算 | ||
| 904 | var itemStatistics = await baseQuery | 905 | var itemStatistics = await baseQuery |
| 905 | .GroupBy((px, kd) => new { ItemId = px.Px, ItemName = px.Pxmc }) | 906 | .GroupBy((px, kd) => new { ItemId = px.Px, ItemName = px.Pxmc }) |
| 906 | .Select((px, kd) => new | 907 | .Select((px, kd) => new |
| @@ -908,14 +909,25 @@ namespace NCC.Extend.LqPackageInfo | @@ -908,14 +909,25 @@ namespace NCC.Extend.LqPackageInfo | ||
| 908 | ItemId = px.Px ?? "", | 909 | ItemId = px.Px ?? "", |
| 909 | ItemName = px.Pxmc ?? "", | 910 | ItemName = px.Pxmc ?? "", |
| 910 | SalesQuantity = SqlFunc.AggregateSum(px.ProjectNumber), | 911 | SalesQuantity = SqlFunc.AggregateSum(px.ProjectNumber), |
| 911 | - SalesAmount = SqlFunc.AggregateSum(px.ActualPrice > 0 ? px.ActualPrice : px.TotalPrice), | 912 | + SalesAmountActual = SqlFunc.AggregateSum(px.ActualPrice), |
| 913 | + SalesAmountTotal = SqlFunc.AggregateSum(px.TotalPrice), | ||
| 912 | SalesCount = SqlFunc.AggregateCount(px.Id) | 914 | SalesCount = SqlFunc.AggregateCount(px.Id) |
| 913 | }) | 915 | }) |
| 914 | .ToListAsync(); | 916 | .ToListAsync(); |
| 915 | 917 | ||
| 918 | + // 计算实际销售金额(ActualPrice > 0 时使用 ActualPrice,否则使用 TotalPrice) | ||
| 919 | + var itemStatisticsWithAmount = itemStatistics.Select(stat => new | ||
| 920 | + { | ||
| 921 | + stat.ItemId, | ||
| 922 | + stat.ItemName, | ||
| 923 | + stat.SalesQuantity, | ||
| 924 | + SalesAmount = stat.SalesAmountActual > 0 ? stat.SalesAmountActual : stat.SalesAmountTotal, | ||
| 925 | + stat.SalesCount | ||
| 926 | + }).ToList(); | ||
| 927 | + | ||
| 916 | // 6. 单独统计每个品项的开单数量(去重开单ID) | 928 | // 6. 单独统计每个品项的开单数量(去重开单ID) |
| 917 | var itemList = new List<ItemStatisticsItem>(); | 929 | var itemList = new List<ItemStatisticsItem>(); |
| 918 | - foreach (var stat in itemStatistics) | 930 | + foreach (var stat in itemStatisticsWithAmount) |
| 919 | { | 931 | { |
| 920 | var billingCountQuery = _db.Queryable<LqKdPxmxEntity, LqKdKdjlbEntity>( | 932 | var billingCountQuery = _db.Queryable<LqKdPxmxEntity, LqKdKdjlbEntity>( |
| 921 | (px, kd) => px.Glkdbh == kd.Id) | 933 | (px, kd) => px.Glkdbh == kd.Id) |
| @@ -981,6 +993,20 @@ namespace NCC.Extend.LqPackageInfo | @@ -981,6 +993,20 @@ namespace NCC.Extend.LqPackageInfo | ||
| 981 | /// "storeIds": ["门店ID1", "门店ID2"] | 993 | /// "storeIds": ["门店ID1", "门店ID2"] |
| 982 | /// } | 994 | /// } |
| 983 | /// ``` | 995 | /// ``` |
| 996 | + /// | ||
| 997 | + /// 返回格式(层级结构): | ||
| 998 | + /// - ActivityId: 营销活动ID | ||
| 999 | + /// - ActivityName: 营销活动名称 | ||
| 1000 | + /// - StoreList: 门店统计列表 | ||
| 1001 | + /// - StoreId: 门店ID | ||
| 1002 | + /// - StoreName: 门店名称 | ||
| 1003 | + /// - ItemList: 该门店下的品项统计列表 | ||
| 1004 | + /// - ItemId: 品项ID | ||
| 1005 | + /// - ItemName: 品项名称 | ||
| 1006 | + /// - SalesQuantity: 销售数量(项目次数总和) | ||
| 1007 | + /// - SalesAmount: 销售金额 | ||
| 1008 | + /// - BillingCount: 开单数量(包含该品项的开单数) | ||
| 1009 | + /// - SalesCount: 销售次数(品项明细记录数) | ||
| 984 | /// </remarks> | 1010 | /// </remarks> |
| 985 | /// <param name="input">查询参数</param> | 1011 | /// <param name="input">查询参数</param> |
| 986 | /// <returns>按门店品项统计数据</returns> | 1012 | /// <returns>按门店品项统计数据</returns> |
| @@ -1021,6 +1047,7 @@ namespace NCC.Extend.LqPackageInfo | @@ -1021,6 +1047,7 @@ namespace NCC.Extend.LqPackageInfo | ||
| 1021 | } | 1047 | } |
| 1022 | 1048 | ||
| 1023 | // 5. 按门店和品项分组统计(先获取基础统计数据) | 1049 | // 5. 按门店和品项分组统计(先获取基础统计数据) |
| 1050 | + // 注意:SqlSugar 的 AggregateSum 可能不支持三元运算符,需要先查询数据再计算 | ||
| 1024 | var storeItemStatistics = await baseQuery | 1051 | var storeItemStatistics = await baseQuery |
| 1025 | .GroupBy((px, kd, md) => new | 1052 | .GroupBy((px, kd, md) => new |
| 1026 | { | 1053 | { |
| @@ -1036,14 +1063,28 @@ namespace NCC.Extend.LqPackageInfo | @@ -1036,14 +1063,28 @@ namespace NCC.Extend.LqPackageInfo | ||
| 1036 | ItemId = px.Px ?? "", | 1063 | ItemId = px.Px ?? "", |
| 1037 | ItemName = px.Pxmc ?? "", | 1064 | ItemName = px.Pxmc ?? "", |
| 1038 | SalesQuantity = SqlFunc.AggregateSum(px.ProjectNumber), | 1065 | SalesQuantity = SqlFunc.AggregateSum(px.ProjectNumber), |
| 1039 | - SalesAmount = SqlFunc.AggregateSum(px.ActualPrice > 0 ? px.ActualPrice : px.TotalPrice), | 1066 | + SalesAmountActual = SqlFunc.AggregateSum(px.ActualPrice), |
| 1067 | + SalesAmountTotal = SqlFunc.AggregateSum(px.TotalPrice), | ||
| 1040 | SalesCount = SqlFunc.AggregateCount(px.Id) | 1068 | SalesCount = SqlFunc.AggregateCount(px.Id) |
| 1041 | }) | 1069 | }) |
| 1042 | .ToListAsync(); | 1070 | .ToListAsync(); |
| 1043 | 1071 | ||
| 1044 | - // 6. 单独统计每个门店品项的开单数量(去重开单ID) | ||
| 1045 | - var storeItemList = new List<StoreItemStatisticsItem>(); | ||
| 1046 | - foreach (var stat in storeItemStatistics) | 1072 | + // 计算实际销售金额(ActualPrice > 0 时使用 ActualPrice,否则使用 TotalPrice) |
| 1073 | + var storeItemStatisticsWithAmount = storeItemStatistics.Select(stat => new | ||
| 1074 | + { | ||
| 1075 | + stat.StoreId, | ||
| 1076 | + stat.StoreName, | ||
| 1077 | + stat.ItemId, | ||
| 1078 | + stat.ItemName, | ||
| 1079 | + stat.SalesQuantity, | ||
| 1080 | + SalesAmount = stat.SalesAmountActual > 0 ? stat.SalesAmountActual : stat.SalesAmountTotal, | ||
| 1081 | + stat.SalesCount | ||
| 1082 | + }).ToList(); | ||
| 1083 | + | ||
| 1084 | + // 6. 单独统计每个门店品项的开单数量(去重开单ID),并组装成层级结构 | ||
| 1085 | + var storeItemDict = new Dictionary<string, List<StoreItemStatisticsItem>>(); | ||
| 1086 | + | ||
| 1087 | + foreach (var stat in storeItemStatisticsWithAmount) | ||
| 1047 | { | 1088 | { |
| 1048 | var billingCountQuery = _db.Queryable<LqKdPxmxEntity, LqKdKdjlbEntity>( | 1089 | var billingCountQuery = _db.Queryable<LqKdPxmxEntity, LqKdKdjlbEntity>( |
| 1049 | (px, kd) => px.Glkdbh == kd.Id) | 1090 | (px, kd) => px.Glkdbh == kd.Id) |
| @@ -1064,28 +1105,53 @@ namespace NCC.Extend.LqPackageInfo | @@ -1064,28 +1105,53 @@ namespace NCC.Extend.LqPackageInfo | ||
| 1064 | .Select((px, kd) => px.Glkdbh) | 1105 | .Select((px, kd) => px.Glkdbh) |
| 1065 | .CountAsync(); | 1106 | .CountAsync(); |
| 1066 | 1107 | ||
| 1067 | - storeItemList.Add(new StoreItemStatisticsItem | 1108 | + var itemStat = new StoreItemStatisticsItem |
| 1068 | { | 1109 | { |
| 1069 | - StoreId = stat.StoreId ?? "", | ||
| 1070 | - StoreName = stat.StoreName ?? "", | ||
| 1071 | ItemId = stat.ItemId ?? "", | 1110 | ItemId = stat.ItemId ?? "", |
| 1072 | ItemName = stat.ItemName ?? "", | 1111 | ItemName = stat.ItemName ?? "", |
| 1073 | SalesQuantity = stat.SalesQuantity, | 1112 | SalesQuantity = stat.SalesQuantity, |
| 1074 | SalesAmount = stat.SalesAmount, | 1113 | SalesAmount = stat.SalesAmount, |
| 1075 | BillingCount = billingCount, | 1114 | BillingCount = billingCount, |
| 1076 | SalesCount = stat.SalesCount | 1115 | SalesCount = stat.SalesCount |
| 1077 | - }); | 1116 | + }; |
| 1117 | + | ||
| 1118 | + // 按门店分组 | ||
| 1119 | + var storeKey = stat.StoreId ?? ""; | ||
| 1120 | + if (!storeItemDict.ContainsKey(storeKey)) | ||
| 1121 | + { | ||
| 1122 | + storeItemDict[storeKey] = new List<StoreItemStatisticsItem>(); | ||
| 1123 | + } | ||
| 1124 | + storeItemDict[storeKey].Add(itemStat); | ||
| 1078 | } | 1125 | } |
| 1079 | 1126 | ||
| 1080 | - // 7. 排序 | ||
| 1081 | - storeItemList = storeItemList.OrderBy(x => x.StoreName).ThenBy(x => x.ItemName).ToList(); | 1127 | + // 7. 组装层级结构:门店 -> 品项列表 |
| 1128 | + var storeList = new List<StoreStatisticsWithItems>(); | ||
| 1129 | + foreach (var kvp in storeItemDict) | ||
| 1130 | + { | ||
| 1131 | + var firstItem = storeItemStatistics.FirstOrDefault(s => s.StoreId == kvp.Key); | ||
| 1132 | + if (firstItem != null) | ||
| 1133 | + { | ||
| 1134 | + // 对品项列表排序 | ||
| 1135 | + var sortedItemList = kvp.Value.OrderBy(x => x.ItemName).ToList(); | ||
| 1082 | 1136 | ||
| 1083 | - // 7. 返回统计结果 | 1137 | + storeList.Add(new StoreStatisticsWithItems |
| 1138 | + { | ||
| 1139 | + StoreId = firstItem.StoreId ?? "", | ||
| 1140 | + StoreName = firstItem.StoreName ?? "", | ||
| 1141 | + ItemList = sortedItemList | ||
| 1142 | + }); | ||
| 1143 | + } | ||
| 1144 | + } | ||
| 1145 | + | ||
| 1146 | + // 8. 按门店名称排序 | ||
| 1147 | + storeList = storeList.OrderBy(x => x.StoreName).ToList(); | ||
| 1148 | + | ||
| 1149 | + // 9. 返回统计结果 | ||
| 1084 | return new ActivityStatisticsByStoreItemOutput | 1150 | return new ActivityStatisticsByStoreItemOutput |
| 1085 | { | 1151 | { |
| 1086 | ActivityId = input.ActivityId, | 1152 | ActivityId = input.ActivityId, |
| 1087 | ActivityName = activity.ActivityName, | 1153 | ActivityName = activity.ActivityName, |
| 1088 | - StoreItemList = storeItemList | 1154 | + StoreList = storeList |
| 1089 | }; | 1155 | }; |
| 1090 | } | 1156 | } |
| 1091 | catch (Exception ex) | 1157 | catch (Exception ex) |
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
| @@ -1073,45 +1073,62 @@ namespace NCC.Extend.LqStatistics | @@ -1073,45 +1073,62 @@ namespace NCC.Extend.LqStatistics | ||
| 1073 | var startDate = new DateTime(now.Year, now.Month, 1); | 1073 | var startDate = new DateTime(now.Year, now.Month, 1); |
| 1074 | var endDate = startDate.AddMonths(1).AddDays(-1); | 1074 | var endDate = startDate.AddMonths(1).AddDays(-1); |
| 1075 | 1075 | ||
| 1076 | - // 构建查询参数 | ||
| 1077 | - var parameters = new Dictionary<string, object> { { "@startDate", startDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", endDate.ToString("yyyy-MM-dd 23:59:59") } }; | 1076 | + // 获取当前月份(YYYYMM格式) |
| 1077 | + var currentMonth = now.ToString("yyyyMM"); | ||
| 1078 | 1078 | ||
| 1079 | - // 查询本月全门店统计数据 | ||
| 1080 | - var sql = | ||
| 1081 | - @" | 1079 | + // 1. 获取本月的门店目标业绩总和(从门店目标表获取 F_StoreTarget 字段,按月份过滤) |
| 1080 | + var targetPerformanceSql = @" | ||
| 1082 | SELECT | 1081 | SELECT |
| 1083 | - SUM(target_performance) AS TotalTargetPerformance, | ||
| 1084 | - SUM(actual_performance) AS TotalActualPerformance, | ||
| 1085 | - CASE | ||
| 1086 | - WHEN SUM(target_performance) > 0 | ||
| 1087 | - THEN (SUM(actual_performance) / SUM(target_performance)) * 100 | ||
| 1088 | - ELSE 0 | ||
| 1089 | - END AS TotalCompletionRate | ||
| 1090 | - FROM v_store_performance_simple"; | 1082 | + COALESCE(SUM(F_StoreTarget), 0) AS TotalTargetPerformance |
| 1083 | + FROM lq_md_target | ||
| 1084 | + WHERE F_Month = @month"; | ||
| 1091 | 1085 | ||
| 1092 | - var result = await _db.Ado.SqlQueryAsync<dynamic>(sql, parameters); | ||
| 1093 | - var stats = result.FirstOrDefault(); | 1086 | + var targetParameters = new Dictionary<string, object> |
| 1087 | + { | ||
| 1088 | + { "@month", currentMonth } | ||
| 1089 | + }; | ||
| 1094 | 1090 | ||
| 1095 | - if (stats != null) | 1091 | + var targetResult = await _db.Ado.SqlQueryAsync<dynamic>(targetPerformanceSql, targetParameters); |
| 1092 | + var targetPerformance = 0m; | ||
| 1093 | + if (targetResult != null && targetResult.Any()) | ||
| 1096 | { | 1094 | { |
| 1097 | - var targetPerformance = Convert.ToDecimal(stats.TotalTargetPerformance ?? 0); | ||
| 1098 | - var actualPerformance = Convert.ToDecimal(stats.TotalActualPerformance ?? 0); | ||
| 1099 | - var completionRate = Convert.ToDecimal(stats.TotalCompletionRate ?? 0); | 1095 | + targetPerformance = Convert.ToDecimal(targetResult.FirstOrDefault()?.TotalTargetPerformance ?? 0); |
| 1096 | + } | ||
| 1100 | 1097 | ||
| 1101 | - return new | ||
| 1102 | - { | ||
| 1103 | - TargetPerformance = targetPerformance, | ||
| 1104 | - ActualPerformance = actualPerformance, | ||
| 1105 | - CompletionRate = completionRate, | ||
| 1106 | - Month = now.ToString("yyyy年MM月"), | ||
| 1107 | - }; | 1098 | + // 2. 从开单记录表获取本月实付金额的总和(sfyj 字段) |
| 1099 | + var actualPerformanceSql = @" | ||
| 1100 | + SELECT | ||
| 1101 | + COALESCE(SUM(sfyj), 0) AS TotalActualPerformance | ||
| 1102 | + FROM lq_kd_kdjlb | ||
| 1103 | + WHERE F_IsEffective = 1 | ||
| 1104 | + AND kdrq >= @startDate | ||
| 1105 | + AND kdrq <= @endDate"; | ||
| 1106 | + | ||
| 1107 | + var parameters = new Dictionary<string, object> | ||
| 1108 | + { | ||
| 1109 | + { "@startDate", startDate.ToString("yyyy-MM-dd 00:00:00") }, | ||
| 1110 | + { "@endDate", endDate.ToString("yyyy-MM-dd 23:59:59") } | ||
| 1111 | + }; | ||
| 1112 | + | ||
| 1113 | + var actualResult = await _db.Ado.SqlQueryAsync<dynamic>(actualPerformanceSql, parameters); | ||
| 1114 | + var actualPerformance = 0m; | ||
| 1115 | + if (actualResult != null && actualResult.Any()) | ||
| 1116 | + { | ||
| 1117 | + actualPerformance = Convert.ToDecimal(actualResult.FirstOrDefault()?.TotalActualPerformance ?? 0); | ||
| 1118 | + } | ||
| 1119 | + | ||
| 1120 | + // 3. 计算完成率 | ||
| 1121 | + var completionRate = 0m; | ||
| 1122 | + if (targetPerformance > 0) | ||
| 1123 | + { | ||
| 1124 | + completionRate = (actualPerformance / targetPerformance) * 100m; | ||
| 1108 | } | 1125 | } |
| 1109 | 1126 | ||
| 1110 | return new | 1127 | return new |
| 1111 | { | 1128 | { |
| 1112 | - TargetPerformance = 0m, | ||
| 1113 | - ActualPerformance = 0m, | ||
| 1114 | - CompletionRate = 0m, | 1129 | + TargetPerformance = targetPerformance, |
| 1130 | + ActualPerformance = actualPerformance, | ||
| 1131 | + CompletionRate = decimal.Round(completionRate, 2), | ||
| 1115 | Month = now.ToString("yyyy年MM月"), | 1132 | Month = now.ToString("yyyy年MM月"), |
| 1116 | }; | 1133 | }; |
| 1117 | } | 1134 | } |
| @@ -3478,13 +3495,17 @@ namespace NCC.Extend.LqStatistics | @@ -3478,13 +3495,17 @@ namespace NCC.Extend.LqStatistics | ||
| 3478 | /// - BillingCount: 开单数量 | 3495 | /// - BillingCount: 开单数量 |
| 3479 | /// - BillingAmount: 开单金额 | 3496 | /// - BillingAmount: 开单金额 |
| 3480 | /// - BillingProjectCount: 开单项目数(项目次数总和) | 3497 | /// - BillingProjectCount: 开单项目数(项目次数总和) |
| 3481 | - /// - ConsumeCount: 消耗数量 | 3498 | + /// - ConsumeCount: 消耗单数(消耗主表的去重记录数) |
| 3482 | /// - ConsumeAmount: 消耗金额 | 3499 | /// - ConsumeAmount: 消耗金额 |
| 3483 | - /// - ConsumeProjectCount: 消耗项目数(项目次数总和) | ||
| 3484 | - /// - RefundCount: 退卡数量 | 3500 | + /// - ConsumeProjectCount: 消耗项目数(项目次数总和,包含原始+加班+陪同) |
| 3501 | + /// - ConsumeOriginalProjectCount: 消耗原始项目数(原始项目次数总和) | ||
| 3502 | + /// - ConsumeOvertimeProjectCount: 消耗加班项目数(加班项目次数总和) | ||
| 3503 | + /// - ConsumeAccompaniedProjectCount: 消耗陪同项目数(陪同项目次数总和) | ||
| 3504 | + /// - RefundCount: 退卡单数量 | ||
| 3485 | /// - RefundAmount: 退卡金额 | 3505 | /// - RefundAmount: 退卡金额 |
| 3486 | /// - HeadCount: 人头(月度去重客户数) | 3506 | /// - HeadCount: 人头(月度去重客户数) |
| 3487 | /// - PersonCount: 人次(日度去重客户数) | 3507 | /// - PersonCount: 人次(日度去重客户数) |
| 3508 | + /// - LaborCost: 手工费(消耗手工费总和) | ||
| 3488 | /// </remarks> | 3509 | /// </remarks> |
| 3489 | /// <param name="input">查询参数</param> | 3510 | /// <param name="input">查询参数</param> |
| 3490 | /// <returns>员工业绩统计数据</returns> | 3511 | /// <returns>员工业绩统计数据</returns> |
| @@ -3532,8 +3553,11 @@ namespace NCC.Extend.LqStatistics | @@ -3532,8 +3553,11 @@ namespace NCC.Extend.LqStatistics | ||
| 3532 | // 9. 开单项目数统计 | 3553 | // 9. 开单项目数统计 |
| 3533 | var billingProjectCount = await GetBillingProjectCount(input.UserId, statisticsMonth); | 3554 | var billingProjectCount = await GetBillingProjectCount(input.UserId, statisticsMonth); |
| 3534 | 3555 | ||
| 3535 | - // 10. 消耗项目数统计 | ||
| 3536 | - var consumeProjectCount = await GetConsumeProjectCount(input.UserId, statisticsMonth); | 3556 | + // 10. 消耗项目数统计(包含总项目数、原始项目数、加班项目数、陪同项目数) |
| 3557 | + var consumeProjectStats = await GetConsumeProjectCountDetails(input.UserId, statisticsMonth); | ||
| 3558 | + | ||
| 3559 | + // 11. 手工费统计 | ||
| 3560 | + var laborCost = await GetLaborCost(input.UserId, statisticsMonth); | ||
| 3537 | 3561 | ||
| 3538 | return new EmployeePerformanceStatisticsOutput | 3562 | return new EmployeePerformanceStatisticsOutput |
| 3539 | { | 3563 | { |
| @@ -3551,7 +3575,11 @@ namespace NCC.Extend.LqStatistics | @@ -3551,7 +3575,11 @@ namespace NCC.Extend.LqStatistics | ||
| 3551 | HeadCount = headCount, | 3575 | HeadCount = headCount, |
| 3552 | PersonCount = personCount, | 3576 | PersonCount = personCount, |
| 3553 | BillingProjectCount = billingProjectCount, | 3577 | BillingProjectCount = billingProjectCount, |
| 3554 | - ConsumeProjectCount = consumeProjectCount | 3578 | + ConsumeProjectCount = consumeProjectStats.TotalCount, |
| 3579 | + ConsumeOriginalProjectCount = consumeProjectStats.OriginalCount, | ||
| 3580 | + ConsumeOvertimeProjectCount = consumeProjectStats.OvertimeCount, | ||
| 3581 | + ConsumeAccompaniedProjectCount = consumeProjectStats.AccompaniedCount, | ||
| 3582 | + LaborCost = laborCost | ||
| 3555 | }; | 3583 | }; |
| 3556 | } | 3584 | } |
| 3557 | catch (Exception ex) | 3585 | catch (Exception ex) |
| @@ -3630,9 +3658,10 @@ namespace NCC.Extend.LqStatistics | @@ -3630,9 +3658,10 @@ namespace NCC.Extend.LqStatistics | ||
| 3630 | /// </summary> | 3658 | /// </summary> |
| 3631 | private async Task<(int Count, decimal Amount)> GetConsumeStats(string userId, string month) | 3659 | private async Task<(int Count, decimal Amount)> GetConsumeStats(string userId, string month) |
| 3632 | { | 3660 | { |
| 3661 | + // 统计消耗单数(消耗主表的去重记录数)和消耗金额(健康师业绩总和) | ||
| 3633 | var sql = $@" | 3662 | var sql = $@" |
| 3634 | SELECT | 3663 | SELECT |
| 3635 | - COUNT(*) as Count, | 3664 | + COUNT(DISTINCT hyhk.F_Id) as Count, |
| 3636 | COALESCE(SUM(jksyj.jksyj), 0) as Amount | 3665 | COALESCE(SUM(jksyj.jksyj), 0) as Amount |
| 3637 | FROM lq_xh_jksyj jksyj | 3666 | FROM lq_xh_jksyj jksyj |
| 3638 | INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id | 3667 | INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id |
| @@ -3651,14 +3680,17 @@ namespace NCC.Extend.LqStatistics | @@ -3651,14 +3680,17 @@ namespace NCC.Extend.LqStatistics | ||
| 3651 | /// </summary> | 3680 | /// </summary> |
| 3652 | private async Task<(int Count, decimal Amount)> GetRefundStats(string userId, string month) | 3681 | private async Task<(int Count, decimal Amount)> GetRefundStats(string userId, string month) |
| 3653 | { | 3682 | { |
| 3683 | + // 统计退卡单数(退卡主表的去重记录数)和退卡金额(健康师业绩总和) | ||
| 3654 | var sql = $@" | 3684 | var sql = $@" |
| 3655 | SELECT | 3685 | SELECT |
| 3656 | - COUNT(*) as Count, | ||
| 3657 | - COALESCE(SUM(jksyj), 0) as Amount | ||
| 3658 | - FROM lq_hytk_jksyj | ||
| 3659 | - WHERE jkszh = '{userId}' | ||
| 3660 | - AND F_IsEffective = 1 | ||
| 3661 | - AND DATE_FORMAT(tksj, '%Y%m') = '{month}'"; | 3686 | + COUNT(DISTINCT hytk.F_Id) as Count, |
| 3687 | + COALESCE(SUM(jksyj.jksyj), 0) as Amount | ||
| 3688 | + FROM lq_hytk_jksyj jksyj | ||
| 3689 | + INNER JOIN lq_hytk_hytk hytk ON jksyj.gltkbh = hytk.F_Id | ||
| 3690 | + WHERE jksyj.jkszh = '{userId}' | ||
| 3691 | + AND jksyj.F_IsEffective = 1 | ||
| 3692 | + AND hytk.F_IsEffective = 1 | ||
| 3693 | + AND DATE_FORMAT(hytk.tksj, '%Y%m') = '{month}'"; | ||
| 3662 | 3694 | ||
| 3663 | var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | 3695 | var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); |
| 3664 | var data = result.FirstOrDefault(); | 3696 | var data = result.FirstOrDefault(); |
| @@ -3737,6 +3769,53 @@ namespace NCC.Extend.LqStatistics | @@ -3737,6 +3769,53 @@ namespace NCC.Extend.LqStatistics | ||
| 3737 | return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0); | 3769 | return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0); |
| 3738 | } | 3770 | } |
| 3739 | 3771 | ||
| 3772 | + /// <summary> | ||
| 3773 | + /// 统计消耗项目数详情(包含总项目数、原始项目数、加班项目数、陪同项目数) | ||
| 3774 | + /// </summary> | ||
| 3775 | + private async Task<(int TotalCount, decimal OriginalCount, decimal OvertimeCount, decimal AccompaniedCount)> GetConsumeProjectCountDetails(string userId, string month) | ||
| 3776 | + { | ||
| 3777 | + var sql = $@" | ||
| 3778 | + SELECT | ||
| 3779 | + COALESCE(SUM(pxmx.F_ProjectNumber), 0) as TotalCount, | ||
| 3780 | + COALESCE(SUM(COALESCE(pxmx.F_OriginalProjectNumber, pxmx.F_ProjectNumber)), 0) as OriginalCount, | ||
| 3781 | + COALESCE(SUM(COALESCE(pxmx.F_OvertimeProjectNumber, 0)), 0) as OvertimeCount, | ||
| 3782 | + COALESCE(SUM(COALESCE(jksyj.F_AccompaniedProjectNumber, 0)), 0) as AccompaniedCount | ||
| 3783 | + FROM lq_xh_jksyj jksyj | ||
| 3784 | + INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id | ||
| 3785 | + INNER JOIN lq_xh_pxmx pxmx ON pxmx.F_ConsumeInfoId = hyhk.F_Id | ||
| 3786 | + WHERE jksyj.jkszh = '{userId}' | ||
| 3787 | + AND jksyj.F_IsEffective = 1 | ||
| 3788 | + AND hyhk.F_IsEffective = 1 | ||
| 3789 | + AND DATE_FORMAT(hyhk.hksj, '%Y%m') = '{month}'"; | ||
| 3790 | + | ||
| 3791 | + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | ||
| 3792 | + var data = result.FirstOrDefault(); | ||
| 3793 | + return ( | ||
| 3794 | + Convert.ToInt32(data?.TotalCount ?? 0), | ||
| 3795 | + Convert.ToDecimal(data?.OriginalCount ?? 0), | ||
| 3796 | + Convert.ToDecimal(data?.OvertimeCount ?? 0), | ||
| 3797 | + Convert.ToDecimal(data?.AccompaniedCount ?? 0) | ||
| 3798 | + ); | ||
| 3799 | + } | ||
| 3800 | + | ||
| 3801 | + /// <summary> | ||
| 3802 | + /// 统计手工费(消耗手工费总和) | ||
| 3803 | + /// </summary> | ||
| 3804 | + private async Task<decimal> GetLaborCost(string userId, string month) | ||
| 3805 | + { | ||
| 3806 | + var sql = $@" | ||
| 3807 | + SELECT COALESCE(SUM(jksyj.F_LaborCost), 0) as LaborCost | ||
| 3808 | + FROM lq_xh_jksyj jksyj | ||
| 3809 | + INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id | ||
| 3810 | + WHERE jksyj.jkszh = '{userId}' | ||
| 3811 | + AND jksyj.F_IsEffective = 1 | ||
| 3812 | + AND hyhk.F_IsEffective = 1 | ||
| 3813 | + AND DATE_FORMAT(hyhk.hksj, '%Y%m') = '{month}'"; | ||
| 3814 | + | ||
| 3815 | + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql); | ||
| 3816 | + return Convert.ToDecimal(result.FirstOrDefault()?.LaborCost ?? 0); | ||
| 3817 | + } | ||
| 3818 | + | ||
| 3740 | #endregion | 3819 | #endregion |
| 3741 | } | 3820 | } |
| 3742 | } | 3821 | } |
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
| @@ -964,6 +964,13 @@ namespace NCC.Extend.LqXhHyhk | @@ -964,6 +964,13 @@ namespace NCC.Extend.LqXhHyhk | ||
| 964 | { | 964 | { |
| 965 | // 开启事务 | 965 | // 开启事务 |
| 966 | _db.BeginTran(); | 966 | _db.BeginTran(); |
| 967 | + // 查询会员信息 | ||
| 968 | + var memberInfo = await _db.Queryable<LqKhxxEntity>().Where(w => w.Id == entity.Hy).FirstAsync(); | ||
| 969 | + //如果会员类型是线索,那么就更新为新客 | ||
| 970 | + if (memberInfo.Khlx == MemberTypeEnum.线索.GetHashCode().ToString()) | ||
| 971 | + { | ||
| 972 | + memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString(); | ||
| 973 | + } | ||
| 967 | // 新增会员耗卡记录 | 974 | // 新增会员耗卡记录 |
| 968 | var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync(); | 975 | var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync(); |
| 969 | // 收集所有需要插入的实体,然后批量插入 | 976 | // 收集所有需要插入的实体,然后批量插入 |