using Microsoft.AspNetCore.Mvc;
using NCC.Common.Filter;
using NCC.Dependency;
using NCC.DynamicApiController;
using NCC.Extend.Entitys.Dto.LqShareStatisticsTechDept;
using NCC.Extend.Entitys.lq_share_statistics_tech_dept;
using NCC.Extend.Entitys.lq_md_target;
using NCC.Extend.Entitys.lq_mdxx;
using NCC.Extend.Entitys.lq_kd_pxmx;
using NCC.Extend.Entitys.lq_kd_kdjlb;
using NCC.Extend.Entitys.lq_hytk_jksyj;
using NCC.Extend.Entitys.lq_xh_jksyj;
using NCC.Extend.Entitys.lq_xh_kjbsyj;
using NCC.Extend.Entitys.lq_tech_teacher_salary_statistics;
using NCC.Extend.Entitys.lq_tech_general_manager_salary_statistics;
using NCC.Extend.Entitys.lq_md_general_manager_lifeline;
using NCC.System.Entitys.Permission;
using NCC.Extend.Entitys;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yitter.IdGenerator;
namespace NCC.Extend
{
///
/// 科技部股份统计服务
///
[ApiDescriptionSettings(Tag = "科技部股份统计服务", Name = "LqShareStatisticsTechDept", Order = 401)]
[Route("api/Extend/[controller]")]
public class LqShareStatisticsTechDeptService : IDynamicApiController, ITransient
{
private readonly ISqlSugarClient _db;
public LqShareStatisticsTechDeptService(ISqlSugarClient db)
{
_db = db;
}
///
/// 生成科技部股份统计数据
///
/// 生成参数
/// 生成结果
[HttpPost("generate")]
public async Task GenerateStatistics([FromBody] ShareStatisticsTechDeptGenerateInput input)
{
if (string.IsNullOrEmpty(input.StatisticsMonth) || input.StatisticsMonth.Length != 6)
{
return new { code = 500, msg = "统计月份格式错误,应为 YYYYMM" };
}
var year = int.Parse(input.StatisticsMonth.Substring(0, 4));
var month = int.Parse(input.StatisticsMonth.Substring(4, 2));
var startDate = new DateTime(year, month, 1);
var endDate = startDate.AddMonths(1); // 下个月的第一天,用于 < 比较
// 确定要生成的部门列表
var departments = new List();
if (!string.IsNullOrEmpty(input.DepartmentName))
{
departments.Add(input.DepartmentName);
}
else
{
departments.Add("科技一部");
departments.Add("科技二部");
}
var generatedCount = 0;
var updatedCount = 0;
foreach (var deptName in departments)
{
// 检查是否已存在
var existing = await _db.Queryable()
.FirstAsync(x => x.DepartmentName == deptName && x.StatisticsMonth == input.StatisticsMonth);
var entity = existing ?? new LqShareStatisticsTechDeptEntity
{
Id = YitIdHelper.NextId().ToString(),
DepartmentName = deptName,
StatisticsMonth = input.StatisticsMonth,
IsEffective = 1,
CreateTime = DateTime.Now,
CreateUser = "System"
};
// 计算各项数据
await CalculateIncome(entity, deptName, startDate, endDate, input.StatisticsMonth);
await CalculateCost(entity, deptName, startDate, endDate, input.StatisticsMonth);
CalculateProfit(entity);
entity.UpdateTime = DateTime.Now;
entity.UpdateUser = "System";
if (existing == null)
{
await _db.Insertable(entity).ExecuteCommandAsync();
generatedCount++;
}
else
{
await _db.Updateable(entity).ExecuteCommandAsync();
updatedCount++;
}
}
return new
{
code = 200,
msg = "生成成功",
data = new
{
generatedCount,
updatedCount,
totalCount = departments.Count
}
};
}
///
/// 查询科技部股份统计列表
///
/// 查询参数
/// 统计列表
[HttpGet("list")]
public async Task GetList([FromQuery] ShareStatisticsTechDeptQueryInput input)
{
var query = _db.Queryable()
.Where(x => x.IsEffective == 1);
if (!string.IsNullOrEmpty(input.StatisticsMonth))
{
query = query.Where(x => x.StatisticsMonth == input.StatisticsMonth);
}
if (!string.IsNullOrEmpty(input.DepartmentName))
{
query = query.Where(x => x.DepartmentName == input.DepartmentName);
}
var list = await query.OrderBy(x => x.StatisticsMonth, OrderByType.Desc)
.OrderBy(x => x.DepartmentName)
.Select(x => new ShareStatisticsTechDeptOutput
{
Id = x.Id,
DepartmentName = x.DepartmentName,
StatisticsMonth = x.StatisticsMonth,
Income = x.Income,
CostReimbursement = x.CostReimbursement,
CostTeacherBase = x.CostTeacherBase,
CostTeacherManual = x.CostTeacherManual,
CostTeacherBillingComm = x.CostTeacherBillingComm,
CostTeacherConsumeComm = x.CostTeacherConsumeComm,
CostTeacherExpertComm = x.CostTeacherExpertComm,
CostTeacherOvertime = x.CostTeacherOvertime,
CostGMBase = x.CostGMBase,
CostGMComm = x.CostGMComm,
RewardTechDept = x.RewardTechDept,
CostOther1 = x.CostOther1,
CostOther2 = x.CostOther2,
Profit = x.Profit,
CreateTime = x.CreateTime,
UpdateTime = x.UpdateTime
})
.ToListAsync();
return new { code = 200, msg = "查询成功", data = list };
}
#region 私有计算方法
///
/// 计算收入部分
///
private async Task CalculateIncome(LqShareStatisticsTechDeptEntity entity, string deptName, DateTime startDate, DateTime endDate, string statisticsMonth)
{
// 1. 通过组织名称找到组织ID
var organize = await _db.Queryable()
.Where(x => x.FullName == deptName && x.DeleteMark == null && x.EnabledMark == 1)
.FirstAsync();
if (organize == null)
{
entity.Income = 0;
return;
}
// 2. 找到该科技部当月管辖的门店(lq_mdxx.kjb 已废弃,从 lq_md_target 按月份取)
var stores = await _db.Queryable()
.Where(x => x.Month == statisticsMonth && x.TechDepartment == organize.Id)
.Select(x => x.StoreId)
.Distinct()
.ToListAsync();
if (stores.Count == 0)
{
entity.Income = 0;
return;
}
// 3. 统计这些门店的科美项目开单实付业绩
// 需要关联 lq_kd_kdjlb 来获取门店信息
decimal kemeiIncome = 0;
if (stores.Count > 0)
{
var storeIdsStr = string.Join("','", stores);
var kemeiIncomeSql = $@"
SELECT COALESCE(SUM(px.F_TotalPrice), 0) as Total
FROM lq_kd_pxmx px
INNER JOIN lq_kd_kdjlb kd ON px.glkdbh = kd.F_Id
WHERE kd.djmd IN ('{storeIdsStr}')
AND px.yjsj >= '{startDate:yyyy-MM-dd} 00:00:00'
AND px.yjsj < '{endDate:yyyy-MM-dd} 00:00:00'
AND px.F_IsEffective = 1
AND px.F_ItemCategory = '科美'";
var kemeiIncomeResult = await _db.Ado.SqlQueryAsync(kemeiIncomeSql);
if (kemeiIncomeResult != null && kemeiIncomeResult.Any())
{
var first = kemeiIncomeResult.FirstOrDefault();
if (first != null)
{
kemeiIncome = Convert.ToDecimal(first.Total ?? 0);
}
}
}
// 4. 减去对应的科美项目实退金额
decimal kemeiRefund = 0;
if (stores.Count > 0)
{
var storeIdsStr = string.Join("','", stores);
var kemeiRefundSql = $@"
SELECT COALESCE(SUM(jksyj), 0) as Total
FROM lq_hytk_jksyj
WHERE F_StoreId IN ('{storeIdsStr}')
AND tksj >= '{startDate:yyyy-MM-dd} 00:00:00'
AND tksj < '{endDate:yyyy-MM-dd} 00:00:00'
AND F_IsEffective = 1
AND F_ItemCategory = '科美'";
var kemeiRefundResult = await _db.Ado.SqlQueryAsync(kemeiRefundSql);
if (kemeiRefundResult != null && kemeiRefundResult.Any())
{
var first = kemeiRefundResult.FirstOrDefault();
if (first != null)
{
kemeiRefund = Convert.ToDecimal(first.Total ?? 0);
}
}
}
// 5. 结果 * 30%
entity.Income = (kemeiIncome - kemeiRefund) * 0.3m;
}
///
/// 计算成本部分
///
private async Task CalculateCost(LqShareStatisticsTechDeptEntity entity, string deptName, DateTime startDate, DateTime endDate, string statisticsMonth)
{
// 1. 通过组织名称找到组织ID
var organize = await _db.Queryable()
.Where(x => x.FullName == deptName && x.DeleteMark == null && x.EnabledMark == 1)
.FirstAsync();
if (organize == null)
{
// 如果找不到组织,所有成本为0
entity.CostReimbursement = 0;
entity.CostTeacherBase = 0;
entity.CostTeacherManual = 0;
entity.CostTeacherBillingComm = 0;
entity.CostTeacherConsumeComm = 0;
entity.CostGMBase = 0;
entity.CostGMComm = 0;
entity.CostTeacherExpertComm = 0;
entity.CostTeacherOvertime = 0;
entity.RewardTechDept = 0;
entity.CostOther1 = 0;
entity.CostOther2 = 0;
return;
}
// 2. 找到该科技部当月管辖的门店(lq_mdxx.kjb 已废弃,从 lq_md_target 按月份取)
var stores = await _db.Queryable()
.Where(x => x.Month == statisticsMonth && x.TechDepartment == organize.Id)
.Select(x => x.StoreId)
.Distinct()
.ToListAsync();
// 1. 成本-报销:筛选一级分类为"科技部费用"的申请,根据申请门店的归属区分一部/二部
var reimbursementAmount = await _db.Queryable(
(app, purchase, category) => app.PurchaseRecordsId == purchase.Id && purchase.ReimbursementCategoryId == category.Id)
.Where((app, purchase, category) =>
category.Level1Name == "科技部费用"
&& app.ApprovalStatus == "已通过"
&& stores.Contains(app.ApplicationStoreId)
&& app.ApplicationTime >= startDate
&& app.ApplicationTime <= endDate.AddDays(1))
.SumAsync((app, purchase, category) => purchase.Amount);
entity.CostReimbursement = reimbursementAmount;
// 2. 成本-人工-科技部老师底薪:从科技部老师工资统计表统计
// 需要筛选该科技部管理的门店的老师
var teacherBaseSalary = await _db.Queryable()
.Where(x => x.StatisticsMonth == statisticsMonth
&& stores.Contains(x.StoreId))
.SumAsync(x => x.BaseSalary);
entity.CostTeacherBase = teacherBaseSalary;
// 3. 成本-人工-科技部手工费:从消耗表中统计科技部老师的手工费
// 数据来源:lq_xh_kjbsyj(科技部老师业绩表)
// 条件:ItemCategory = 科美,根据门店归属区分一部/二部
var teacherManualFee = await _db.Queryable()
.Where(x => x.ItemCategory == "科美"
&& stores.Contains(x.StoreId)
&& x.Yjsj >= startDate
&& x.Yjsj < endDate
&& x.IsEffective == 1)
.SumAsync(x => x.LaborCost ?? 0);
entity.CostTeacherManual = teacherManualFee;
// 4. 成本-人工-科技部开单提成:从科技部老师工资统计表统计业绩提成金额
var teacherBillingComm = await _db.Queryable()
.Where(x => x.StatisticsMonth == statisticsMonth
&& stores.Contains(x.StoreId))
.SumAsync(x => x.PerformanceCommissionAmount);
entity.CostTeacherBillingComm = teacherBillingComm;
// 5. 成本-人工-科技部消耗提成:从科技部老师工资统计表统计消耗提成金额
var teacherConsumeComm = await _db.Queryable()
.Where(x => x.StatisticsMonth == statisticsMonth
&& stores.Contains(x.StoreId))
.SumAsync(x => x.ConsumeCommissionAmount);
entity.CostTeacherConsumeComm = teacherConsumeComm;
// 6. 成本-人工-科技部总经理底薪和提成
// 直接根据工资统计表中的 F_Position 字段区分科技一部/二部,不需要通过用户表过滤
// 科技部总经理底薪
var gmBaseSalary = await _db.Queryable()
.Where(x => x.StatisticsMonth == statisticsMonth
&& x.Position == deptName)
.SumAsync(x => x.BaseSalary);
entity.CostGMBase = gmBaseSalary;
// 科技部总经理提成
var gmComm = await _db.Queryable()
.Where(x => x.StatisticsMonth == statisticsMonth
&& x.Position == deptName)
.SumAsync(x => x.TotalCommission);
entity.CostGMComm = gmComm;
// 保留字段
entity.CostTeacherExpertComm = 0;
entity.CostTeacherOvertime = 0;
entity.RewardTechDept = 0;
entity.CostOther1 = 0;
entity.CostOther2 = 0;
}
///
/// 计算利润
///
private void CalculateProfit(LqShareStatisticsTechDeptEntity entity)
{
// 科技部利润 = 收入 - 成本报销 - 成本人工(底薪 + 手工 + 开单提成 + 消耗提成 + 专家提成 + 加班 + 总经理底薪 + 总经理提成) - 其他
var totalCost = entity.CostReimbursement // 成本报销
+ entity.CostTeacherBase // 成本人工-底薪
+ entity.CostTeacherManual // 成本人工-手工
+ entity.CostTeacherBillingComm // 成本人工-开单提成
+ entity.CostTeacherConsumeComm // 成本人工-消耗提成
+ entity.CostTeacherExpertComm // 成本人工-专家提成
+ entity.CostTeacherOvertime // 成本人工-加班
+ entity.CostGMBase // 成本人工-总经理底薪
+ entity.CostGMComm // 成本人工-总经理提成
+ entity.CostOther1 // 其他1
+ entity.CostOther2; // 其他2
// 注意:RewardTechDept(奖励-科技部)是保留字段,不计入成本
entity.Profit = entity.Income - totalCost;
}
#endregion
}
}