初始化
This commit is contained in:
Binary file not shown.
BIN
backend/app/services/__pycache__/report_service.cpython-312.pyc
Normal file
BIN
backend/app/services/__pycache__/report_service.cpython-312.pyc
Normal file
Binary file not shown.
604
backend/app/services/calculate_service.py
Normal file
604
backend/app/services/calculate_service.py
Normal file
@@ -0,0 +1,604 @@
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
from typing import Optional, Dict, List, Any
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, and_
|
||||
import json
|
||||
|
||||
from app.models import (
|
||||
Employee, SecondaryAgent, ProductCategory, PerformanceRecord,
|
||||
CalculationResult, User
|
||||
)
|
||||
|
||||
|
||||
def get_period_dates(period: str, year: int, month: Optional[int] = None,
|
||||
quarter: Optional[int] = None) -> tuple:
|
||||
"""
|
||||
根据周期类型获取开始和结束日期
|
||||
|
||||
Returns:
|
||||
tuple: (start_date, end_date, period_key)
|
||||
"""
|
||||
if period == "monthly":
|
||||
if month is None:
|
||||
raise ValueError("月度周期需要提供month参数")
|
||||
start_date = date(year, month, 1)
|
||||
if month == 12:
|
||||
end_date = date(year + 1, 1, 1)
|
||||
else:
|
||||
end_date = date(year, month + 1, 1)
|
||||
period_key = f"{year}-{month:02d}"
|
||||
|
||||
elif period == "quarterly":
|
||||
if quarter is None:
|
||||
raise ValueError("季度周期需要提供quarter参数")
|
||||
start_month = (quarter - 1) * 3 + 1
|
||||
end_month = quarter * 3 + 1
|
||||
start_date = date(year, start_month, 1)
|
||||
if end_month > 12:
|
||||
end_date = date(year + 1, 1, 1)
|
||||
else:
|
||||
end_date = date(year, end_month, 1)
|
||||
period_key = f"{year}-Q{quarter}"
|
||||
|
||||
elif period == "half_yearly":
|
||||
start_date = date(year, 1, 1)
|
||||
end_date = date(year, 7, 1)
|
||||
period_key = f"{year}-H1"
|
||||
|
||||
elif period == "yearly":
|
||||
start_date = date(year, 1, 1)
|
||||
end_date = date(year + 1, 1, 1)
|
||||
period_key = f"{year}"
|
||||
|
||||
else:
|
||||
raise ValueError(f"不支持的周期类型: {period}")
|
||||
|
||||
return start_date, end_date, period_key
|
||||
|
||||
|
||||
def get_target_by_period(employee: Employee, period: str) -> Decimal:
|
||||
"""根据周期类型获取员工目标"""
|
||||
if period == "monthly":
|
||||
return employee.monthly_target or Decimal("0")
|
||||
elif period == "quarterly":
|
||||
return employee.quarterly_target or Decimal("0")
|
||||
elif period == "half_yearly":
|
||||
return employee.half_year_target or Decimal("0")
|
||||
elif period == "yearly":
|
||||
return employee.yearly_target or Decimal("0")
|
||||
return Decimal("0")
|
||||
|
||||
|
||||
def get_rebate_rate_by_period(category: ProductCategory, period: str) -> Decimal:
|
||||
"""根据周期类型获取返点比例"""
|
||||
if period == "monthly":
|
||||
return category.monthly_rebate or Decimal("0")
|
||||
elif period == "quarterly":
|
||||
return category.quarterly_rebate or category.monthly_rebate or Decimal("0")
|
||||
elif period == "half_yearly":
|
||||
return category.quarterly_rebate or category.monthly_rebate or Decimal("0")
|
||||
elif period == "yearly":
|
||||
return category.quarterly_rebate or category.monthly_rebate or Decimal("0")
|
||||
return Decimal("0")
|
||||
|
||||
|
||||
def calculate_employee_income(
|
||||
db: Session,
|
||||
employee_id: int,
|
||||
period: str,
|
||||
year: int,
|
||||
month: Optional[int] = None,
|
||||
quarter: Optional[int] = None,
|
||||
save_result: bool = True
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
计算员工在指定周期的收益
|
||||
|
||||
计算逻辑:
|
||||
1. 获取员工业绩数据(根据period筛选)
|
||||
2. 计算完成率 = 总业绩 / 目标
|
||||
3. 绩效奖金 = 1000 * min(完成率, 1.0) 如果完成率>=50%,否则0
|
||||
4. 个人提成 = 业绩按分类汇总 * 各分类提成比例
|
||||
5. 代理提成 = 二级代理业绩总和 * 0.01
|
||||
6. 总收入 = 底薪 + 绩效奖金 + 个人提成 + 代理提成
|
||||
|
||||
Args:
|
||||
db: 数据库会话
|
||||
employee_id: 员工ID
|
||||
period: 周期类型 (monthly/quarterly/half_yearly/yearly)
|
||||
year: 年份
|
||||
month: 月份(月度周期需要)
|
||||
quarter: 季度(季度周期需要)
|
||||
save_result: 是否保存计算结果到数据库
|
||||
|
||||
Returns:
|
||||
计算结果字典
|
||||
"""
|
||||
# 获取员工信息
|
||||
employee = db.query(Employee).filter(Employee.id == employee_id).first()
|
||||
if not employee:
|
||||
raise ValueError(f"员工不存在: {employee_id}")
|
||||
|
||||
# 获取周期日期范围
|
||||
start_date, end_date, period_key = get_period_dates(period, year, month, quarter)
|
||||
|
||||
# 获取目标金额
|
||||
target_amount = get_target_by_period(employee, period)
|
||||
|
||||
# 1. 获取员工业绩数据(个人业绩)
|
||||
personal_records = db.query(PerformanceRecord).filter(
|
||||
and_(
|
||||
PerformanceRecord.employee_id == employee_id,
|
||||
PerformanceRecord.record_type == "employee",
|
||||
PerformanceRecord.record_date >= start_date,
|
||||
PerformanceRecord.record_date < end_date
|
||||
)
|
||||
).all()
|
||||
|
||||
# 按分类汇总个人业绩
|
||||
category_performance = {}
|
||||
total_personal_performance = Decimal("0")
|
||||
|
||||
for record in personal_records:
|
||||
category_id = record.category_id
|
||||
amount = record.amount or Decimal("0")
|
||||
total_personal_performance += amount
|
||||
|
||||
if category_id not in category_performance:
|
||||
category_performance[category_id] = {
|
||||
"amount": Decimal("0"),
|
||||
"category_name": record.category.name if record.category else "未知分类"
|
||||
}
|
||||
category_performance[category_id]["amount"] += amount
|
||||
|
||||
# 2. 获取二级代理业绩数据
|
||||
agent_records = db.query(PerformanceRecord).filter(
|
||||
and_(
|
||||
PerformanceRecord.employee_id == employee_id,
|
||||
PerformanceRecord.record_type == "agent",
|
||||
PerformanceRecord.record_date >= start_date,
|
||||
PerformanceRecord.record_date < end_date
|
||||
)
|
||||
).all()
|
||||
|
||||
total_agent_performance = Decimal("0")
|
||||
for record in agent_records:
|
||||
total_agent_performance += record.amount or Decimal("0")
|
||||
|
||||
# 计算总业绩
|
||||
total_performance = total_personal_performance + total_agent_performance
|
||||
|
||||
# 3. 计算完成率
|
||||
completion_rate = Decimal("0")
|
||||
if target_amount > 0:
|
||||
completion_rate = (total_performance / target_amount) * 100
|
||||
|
||||
# 4. 计算绩效奖金
|
||||
performance_bonus = Decimal("0")
|
||||
if completion_rate >= 50:
|
||||
rate = min(completion_rate / 100, Decimal("1.0"))
|
||||
performance_bonus = Decimal("1000") * rate
|
||||
|
||||
# 5. 计算个人提成
|
||||
personal_commission = Decimal("0")
|
||||
commission_details = []
|
||||
|
||||
for category_id, data in category_performance.items():
|
||||
category = db.query(ProductCategory).filter(ProductCategory.id == category_id).first()
|
||||
if category:
|
||||
commission_rate = category.commission_rate or Decimal("0")
|
||||
commission = data["amount"] * commission_rate
|
||||
personal_commission += commission
|
||||
commission_details.append({
|
||||
"category_id": category_id,
|
||||
"category_name": data["category_name"],
|
||||
"amount": float(data["amount"]),
|
||||
"commission_rate": float(commission_rate),
|
||||
"commission": float(commission)
|
||||
})
|
||||
|
||||
# 6. 计算代理提成(二级代理业绩的1%)
|
||||
agent_commission_rate = Decimal("0.01")
|
||||
agent_commission = total_agent_performance * agent_commission_rate
|
||||
|
||||
# 7. 计算总收入
|
||||
base_salary = employee.base_salary or Decimal("0")
|
||||
total_income = base_salary + performance_bonus + personal_commission + agent_commission
|
||||
|
||||
# 计算公司相关数据
|
||||
company_rebate = Decimal("0")
|
||||
for category_id, data in category_performance.items():
|
||||
category = db.query(ProductCategory).filter(ProductCategory.id == category_id).first()
|
||||
if category:
|
||||
rebate_rate = get_rebate_rate_by_period(category, period)
|
||||
company_rebate += data["amount"] * rebate_rate
|
||||
|
||||
# 公司成本
|
||||
company_cost = total_income # 员工成本
|
||||
|
||||
# 代理分成成本
|
||||
agent_share_cost = Decimal("0")
|
||||
for record in agent_records:
|
||||
if record.agent:
|
||||
share_rate = record.agent.profit_share_rate or Decimal("0.60")
|
||||
agent_share_cost += (record.amount or Decimal("0")) * share_rate
|
||||
|
||||
company_cost += agent_share_cost
|
||||
|
||||
# 公司利润
|
||||
company_profit = company_rebate - company_cost
|
||||
|
||||
# 构建详情JSON
|
||||
detail_json = {
|
||||
"personal_performance": {
|
||||
"total": float(total_personal_performance),
|
||||
"by_category": commission_details
|
||||
},
|
||||
"agent_performance": {
|
||||
"total": float(total_agent_performance),
|
||||
"commission_rate": float(agent_commission_rate),
|
||||
"commission": float(agent_commission)
|
||||
},
|
||||
"target": {
|
||||
"amount": float(target_amount),
|
||||
"completion_rate": float(completion_rate)
|
||||
},
|
||||
"bonus_calculation": {
|
||||
"threshold": 50,
|
||||
"max_bonus": 1000,
|
||||
"actual_bonus": float(performance_bonus)
|
||||
}
|
||||
}
|
||||
|
||||
result = {
|
||||
"employee_id": employee_id,
|
||||
"employee_name": employee.user.name if employee.user else "",
|
||||
"period": period,
|
||||
"year": year,
|
||||
"month": month,
|
||||
"quarter": quarter,
|
||||
"period_key": period_key,
|
||||
"period_start_date": start_date.isoformat(),
|
||||
"period_end_date": end_date.isoformat(),
|
||||
"total_performance": float(total_performance),
|
||||
"target_amount": float(target_amount),
|
||||
"completion_rate": float(completion_rate),
|
||||
"base_salary": float(base_salary),
|
||||
"performance_bonus": float(performance_bonus),
|
||||
"personal_commission": float(personal_commission),
|
||||
"agent_commission": float(agent_commission),
|
||||
"total_income": float(total_income),
|
||||
"company_rebate": float(company_rebate),
|
||||
"company_cost": float(company_cost),
|
||||
"company_profit": float(company_profit),
|
||||
"agent_performance": float(total_agent_performance),
|
||||
"agent_share_amount": float(agent_share_cost),
|
||||
"detail": detail_json
|
||||
}
|
||||
|
||||
# 保存计算结果到数据库
|
||||
if save_result:
|
||||
calc_result = CalculationResult(
|
||||
employee_id=employee_id,
|
||||
calc_period=period,
|
||||
calc_year=year,
|
||||
calc_month=month,
|
||||
calc_quarter=quarter,
|
||||
period_start_date=start_date,
|
||||
period_end_date=end_date,
|
||||
total_performance=total_performance,
|
||||
target_amount=target_amount,
|
||||
completion_rate=completion_rate,
|
||||
base_salary=base_salary,
|
||||
performance_bonus=performance_bonus,
|
||||
personal_commission=personal_commission,
|
||||
agent_commission=agent_commission,
|
||||
total_income=total_income,
|
||||
company_rebate=company_rebate,
|
||||
company_cost=company_cost,
|
||||
company_profit=company_profit,
|
||||
agent_performance=total_agent_performance,
|
||||
agent_share_amount=agent_share_cost,
|
||||
detail_json=json.dumps(detail_json, ensure_ascii=False)
|
||||
)
|
||||
db.add(calc_result)
|
||||
db.commit()
|
||||
db.refresh(calc_result)
|
||||
result["calculation_id"] = calc_result.id
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def calculate_company_profit(
|
||||
db: Session,
|
||||
period: str,
|
||||
year: int,
|
||||
month: Optional[int] = None,
|
||||
quarter: Optional[int] = None,
|
||||
save_result: bool = False
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
计算公司在指定周期的收益
|
||||
|
||||
计算逻辑:
|
||||
1. 获取所有员工业绩
|
||||
2. 公司返点 = 业绩按分类汇总 * 各分类返点比例
|
||||
3. 公司成本 = 所有员工(底薪+绩效+提成) + 代理分成
|
||||
4. 公司利润 = 返点 - 成本
|
||||
|
||||
Args:
|
||||
db: 数据库会话
|
||||
period: 周期类型
|
||||
year: 年份
|
||||
month: 月份
|
||||
quarter: 季度
|
||||
save_result: 是否保存结果
|
||||
|
||||
Returns:
|
||||
计算结果字典
|
||||
"""
|
||||
# 获取周期日期范围
|
||||
start_date, end_date, period_key = get_period_dates(period, year, month, quarter)
|
||||
|
||||
# 获取所有员工
|
||||
employees = db.query(Employee).join(User).filter(User.status == 1).all()
|
||||
|
||||
total_company_rebate = Decimal("0")
|
||||
total_company_cost = Decimal("0")
|
||||
total_agent_share = Decimal("0")
|
||||
employee_results = []
|
||||
|
||||
# 获取所有业绩记录
|
||||
all_records = db.query(PerformanceRecord).filter(
|
||||
and_(
|
||||
PerformanceRecord.record_date >= start_date,
|
||||
PerformanceRecord.record_date < end_date
|
||||
)
|
||||
).all()
|
||||
|
||||
# 按分类汇总业绩
|
||||
category_totals = {}
|
||||
for record in all_records:
|
||||
if record.record_type == "employee":
|
||||
cat_id = record.category_id
|
||||
if cat_id not in category_totals:
|
||||
category_totals[cat_id] = Decimal("0")
|
||||
category_totals[cat_id] += record.amount or Decimal("0")
|
||||
|
||||
# 计算公司返点
|
||||
rebate_details = []
|
||||
for cat_id, amount in category_totals.items():
|
||||
category = db.query(ProductCategory).filter(ProductCategory.id == cat_id).first()
|
||||
if category:
|
||||
rebate_rate = get_rebate_rate_by_period(category, period)
|
||||
rebate = amount * rebate_rate
|
||||
total_company_rebate += rebate
|
||||
rebate_details.append({
|
||||
"category_id": cat_id,
|
||||
"category_name": category.name,
|
||||
"amount": float(amount),
|
||||
"rebate_rate": float(rebate_rate),
|
||||
"rebate": float(rebate)
|
||||
})
|
||||
|
||||
# 计算每个员工的收益和代理分成
|
||||
for employee in employees:
|
||||
try:
|
||||
emp_result = calculate_employee_income(
|
||||
db, employee.id, period, year, month, quarter, save_result=False
|
||||
)
|
||||
total_company_cost += Decimal(str(emp_result["total_income"]))
|
||||
total_agent_share += Decimal(str(emp_result["agent_share_amount"]))
|
||||
employee_results.append({
|
||||
"employee_id": employee.id,
|
||||
"employee_name": emp_result["employee_name"],
|
||||
"total_income": emp_result["total_income"],
|
||||
"agent_share": emp_result["agent_share_amount"]
|
||||
})
|
||||
except Exception as e:
|
||||
# 跳过计算失败的员工
|
||||
continue
|
||||
|
||||
# 总成本
|
||||
total_cost = total_company_cost + total_agent_share
|
||||
|
||||
# 公司利润
|
||||
company_profit = total_company_rebate - total_cost
|
||||
|
||||
result = {
|
||||
"period": period,
|
||||
"year": year,
|
||||
"month": month,
|
||||
"quarter": quarter,
|
||||
"period_key": period_key,
|
||||
"period_start_date": start_date.isoformat(),
|
||||
"period_end_date": end_date.isoformat(),
|
||||
"total_rebate": float(total_company_rebate),
|
||||
"total_employee_cost": float(total_company_cost),
|
||||
"total_agent_share": float(total_agent_share),
|
||||
"total_cost": float(total_cost),
|
||||
"company_profit": float(company_profit),
|
||||
"employee_count": len(employee_results),
|
||||
"rebate_details": rebate_details,
|
||||
"employee_details": employee_results
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def calculate_agent_profit(
|
||||
db: Session,
|
||||
agent_id: int,
|
||||
period: str,
|
||||
year: int,
|
||||
month: Optional[int] = None,
|
||||
quarter: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
计算二级代理在指定周期的收益
|
||||
|
||||
计算逻辑:
|
||||
代理分成 = 代理业绩 * 代理分佣比例(默认60%)
|
||||
|
||||
Args:
|
||||
db: 数据库会话
|
||||
agent_id: 代理ID
|
||||
period: 周期类型
|
||||
year: 年份
|
||||
month: 月份
|
||||
quarter: 季度
|
||||
|
||||
Returns:
|
||||
计算结果字典
|
||||
"""
|
||||
# 获取代理信息
|
||||
agent = db.query(SecondaryAgent).filter(SecondaryAgent.id == agent_id).first()
|
||||
if not agent:
|
||||
raise ValueError(f"代理不存在: {agent_id}")
|
||||
|
||||
# 获取周期日期范围
|
||||
start_date, end_date, period_key = get_period_dates(period, year, month, quarter)
|
||||
|
||||
# 获取代理业绩
|
||||
agent_records = db.query(PerformanceRecord).filter(
|
||||
and_(
|
||||
PerformanceRecord.agent_id == agent_id,
|
||||
PerformanceRecord.record_date >= start_date,
|
||||
PerformanceRecord.record_date < end_date
|
||||
)
|
||||
).all()
|
||||
|
||||
total_performance = Decimal("0")
|
||||
performance_details = []
|
||||
|
||||
for record in agent_records:
|
||||
amount = record.amount or Decimal("0")
|
||||
total_performance += amount
|
||||
performance_details.append({
|
||||
"record_id": record.id,
|
||||
"date": record.record_date.isoformat() if record.record_date else None,
|
||||
"amount": float(amount),
|
||||
"customer_name": record.customer_name,
|
||||
"order_no": record.order_no
|
||||
})
|
||||
|
||||
# 计算代理分成
|
||||
profit_share_rate = agent.profit_share_rate or Decimal("0.60")
|
||||
profit_share_amount = total_performance * profit_share_rate
|
||||
|
||||
result = {
|
||||
"agent_id": agent_id,
|
||||
"agent_name": agent.company_name,
|
||||
"contact_name": agent.contact_name,
|
||||
"employee_id": agent.employee_id,
|
||||
"employee_name": agent.employee.user.name if agent.employee and agent.employee.user else "",
|
||||
"period": period,
|
||||
"year": year,
|
||||
"month": month,
|
||||
"quarter": quarter,
|
||||
"period_key": period_key,
|
||||
"period_start_date": start_date.isoformat(),
|
||||
"period_end_date": end_date.isoformat(),
|
||||
"total_performance": float(total_performance),
|
||||
"profit_share_rate": float(profit_share_rate),
|
||||
"profit_share_amount": float(profit_share_amount),
|
||||
"performance_count": len(agent_records),
|
||||
"performance_details": performance_details
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_calculation_history(
|
||||
db: Session,
|
||||
employee_id: Optional[int] = None,
|
||||
period: Optional[str] = None,
|
||||
year: Optional[int] = None,
|
||||
page: int = 1,
|
||||
page_size: int = 20
|
||||
) -> Dict[str, Any]:
|
||||
"""获取计算历史列表"""
|
||||
query = db.query(CalculationResult)
|
||||
|
||||
if employee_id:
|
||||
query = query.filter(CalculationResult.employee_id == employee_id)
|
||||
if period:
|
||||
query = query.filter(CalculationResult.calc_period == period)
|
||||
if year:
|
||||
query = query.filter(CalculationResult.calc_year == year)
|
||||
|
||||
total = query.count()
|
||||
|
||||
results = query.order_by(
|
||||
CalculationResult.calc_year.desc(),
|
||||
CalculationResult.created_at.desc()
|
||||
).offset((page - 1) * page_size).limit(page_size).all()
|
||||
|
||||
items = []
|
||||
for result in results:
|
||||
items.append({
|
||||
"id": result.id,
|
||||
"employee_id": result.employee_id,
|
||||
"employee_name": result.employee.user.name if result.employee and result.employee.user else "",
|
||||
"calc_period": result.calc_period,
|
||||
"calc_year": result.calc_year,
|
||||
"calc_month": result.calc_month,
|
||||
"calc_quarter": result.calc_quarter,
|
||||
"period_start_date": result.period_start_date.isoformat() if result.period_start_date else None,
|
||||
"period_end_date": result.period_end_date.isoformat() if result.period_end_date else None,
|
||||
"total_performance": float(result.total_performance) if result.total_performance else 0,
|
||||
"target_amount": float(result.target_amount) if result.target_amount else 0,
|
||||
"completion_rate": float(result.completion_rate) if result.completion_rate else 0,
|
||||
"total_income": float(result.total_income) if result.total_income else 0,
|
||||
"company_profit": float(result.company_profit) if result.company_profit else 0,
|
||||
"created_at": result.created_at.isoformat() if result.created_at else None
|
||||
})
|
||||
|
||||
return {
|
||||
"items": items,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": page_size
|
||||
}
|
||||
|
||||
|
||||
def get_calculation_detail(db: Session, calculation_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""获取计算历史详情"""
|
||||
result = db.query(CalculationResult).filter(CalculationResult.id == calculation_id).first()
|
||||
if not result:
|
||||
return None
|
||||
|
||||
detail = None
|
||||
if result.detail_json:
|
||||
try:
|
||||
detail = json.loads(result.detail_json)
|
||||
except:
|
||||
detail = None
|
||||
|
||||
return {
|
||||
"id": result.id,
|
||||
"employee_id": result.employee_id,
|
||||
"employee_name": result.employee.user.name if result.employee and result.employee.user else "",
|
||||
"calc_period": result.calc_period,
|
||||
"calc_year": result.calc_year,
|
||||
"calc_month": result.calc_month,
|
||||
"calc_quarter": result.calc_quarter,
|
||||
"period_start_date": result.period_start_date.isoformat() if result.period_start_date else None,
|
||||
"period_end_date": result.period_end_date.isoformat() if result.period_end_date else None,
|
||||
"total_performance": float(result.total_performance) if result.total_performance else 0,
|
||||
"target_amount": float(result.target_amount) if result.target_amount else 0,
|
||||
"completion_rate": float(result.completion_rate) if result.completion_rate else 0,
|
||||
"base_salary": float(result.base_salary) if result.base_salary else 0,
|
||||
"performance_bonus": float(result.performance_bonus) if result.performance_bonus else 0,
|
||||
"personal_commission": float(result.personal_commission) if result.personal_commission else 0,
|
||||
"agent_commission": float(result.agent_commission) if result.agent_commission else 0,
|
||||
"total_income": float(result.total_income) if result.total_income else 0,
|
||||
"company_rebate": float(result.company_rebate) if result.company_rebate else 0,
|
||||
"company_cost": float(result.company_cost) if result.company_cost else 0,
|
||||
"company_profit": float(result.company_profit) if result.company_profit else 0,
|
||||
"agent_performance": float(result.agent_performance) if result.agent_performance else 0,
|
||||
"agent_share_amount": float(result.agent_share_amount) if result.agent_share_amount else 0,
|
||||
"detail": detail,
|
||||
"created_at": result.created_at.isoformat() if result.created_at else None
|
||||
}
|
||||
369
backend/app/services/report_service.py
Normal file
369
backend/app/services/report_service.py
Normal file
@@ -0,0 +1,369 @@
|
||||
import pandas as pd
|
||||
import io
|
||||
from datetime import date, datetime
|
||||
from typing import Optional, Dict, List, Any
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, and_
|
||||
|
||||
from app.models import Employee, SecondaryAgent, ProductCategory, PerformanceRecord, User
|
||||
from app.services.calculate_service import (
|
||||
calculate_employee_income, calculate_company_profit,
|
||||
get_period_dates, get_rebate_rate_by_period
|
||||
)
|
||||
|
||||
|
||||
def export_employee_report(
|
||||
db: Session,
|
||||
employee_id: int,
|
||||
period: str,
|
||||
year: int,
|
||||
month: Optional[int] = None
|
||||
) -> tuple:
|
||||
"""
|
||||
导出员工收益明细Excel
|
||||
|
||||
Returns:
|
||||
tuple: (excel_bytes, filename)
|
||||
"""
|
||||
# 计算员工收益
|
||||
result = calculate_employee_income(db, employee_id, period, year, month, save_result=False)
|
||||
|
||||
# 获取周期日期范围
|
||||
start_date, end_date, period_key = get_period_dates(period, year, month)
|
||||
|
||||
# 获取业绩明细
|
||||
personal_records = db.query(PerformanceRecord).filter(
|
||||
and_(
|
||||
PerformanceRecord.employee_id == employee_id,
|
||||
PerformanceRecord.record_type == "employee",
|
||||
PerformanceRecord.record_date >= start_date,
|
||||
PerformanceRecord.record_date < end_date
|
||||
)
|
||||
).all()
|
||||
|
||||
agent_records = db.query(PerformanceRecord).filter(
|
||||
and_(
|
||||
PerformanceRecord.employee_id == employee_id,
|
||||
PerformanceRecord.record_type == "agent",
|
||||
PerformanceRecord.record_date >= start_date,
|
||||
PerformanceRecord.record_date < end_date
|
||||
)
|
||||
).all()
|
||||
|
||||
# 创建Excel writer
|
||||
output = io.BytesIO()
|
||||
with pd.ExcelWriter(output, engine='openpyxl') as writer:
|
||||
# Sheet 1: 收益汇总
|
||||
summary_data = {
|
||||
'项目': [
|
||||
'员工姓名',
|
||||
'计算周期',
|
||||
'业绩总额',
|
||||
'目标金额',
|
||||
'完成率(%)',
|
||||
'底薪',
|
||||
'绩效奖金',
|
||||
'个人提成',
|
||||
'代理提成',
|
||||
'总收入',
|
||||
'公司返点',
|
||||
'公司成本',
|
||||
'公司利润'
|
||||
],
|
||||
'金额': [
|
||||
result['employee_name'],
|
||||
result['period_key'],
|
||||
result['total_performance'],
|
||||
result['target_amount'],
|
||||
f"{result['completion_rate']:.2f}",
|
||||
result['base_salary'],
|
||||
result['performance_bonus'],
|
||||
result['personal_commission'],
|
||||
result['agent_commission'],
|
||||
result['total_income'],
|
||||
result['company_rebate'],
|
||||
result['company_cost'],
|
||||
result['company_profit']
|
||||
]
|
||||
}
|
||||
df_summary = pd.DataFrame(summary_data)
|
||||
df_summary.to_excel(writer, sheet_name='收益汇总', index=False)
|
||||
|
||||
# Sheet 2: 个人业绩明细
|
||||
if personal_records:
|
||||
personal_data = []
|
||||
for record in personal_records:
|
||||
personal_data.append({
|
||||
'日期': record.record_date,
|
||||
'客户名称': record.customer_name or '',
|
||||
'订单号': record.order_no or '',
|
||||
'产品分类': record.category.name if record.category else '',
|
||||
'业绩金额': float(record.amount or 0),
|
||||
'提成比例': float(record.category.commission_rate or 0) if record.category else 0,
|
||||
'提成金额': float(record.amount or 0) * float(record.category.commission_rate or 0) if record.category else 0
|
||||
})
|
||||
df_personal = pd.DataFrame(personal_data)
|
||||
df_personal.to_excel(writer, sheet_name='个人业绩明细', index=False)
|
||||
else:
|
||||
pd.DataFrame({'提示': ['该周期内无个人业绩记录']}).to_excel(writer, sheet_name='个人业绩明细', index=False)
|
||||
|
||||
# Sheet 3: 代理业绩明细
|
||||
if agent_records:
|
||||
agent_data = []
|
||||
for record in agent_records:
|
||||
agent = record.agent
|
||||
agent_data.append({
|
||||
'日期': record.record_date,
|
||||
'代理公司': agent.company_name if agent else '',
|
||||
'客户名称': record.customer_name or '',
|
||||
'订单号': record.order_no or '',
|
||||
'产品分类': record.category.name if record.category else '',
|
||||
'业绩金额': float(record.amount or 0),
|
||||
'分佣比例': float(agent.profit_share_rate or 0.6) if agent else 0.6,
|
||||
'分佣金额': float(record.amount or 0) * float(agent.profit_share_rate or 0.6) if agent else float(record.amount or 0) * 0.6
|
||||
})
|
||||
df_agent = pd.DataFrame(agent_data)
|
||||
df_agent.to_excel(writer, sheet_name='代理业绩明细', index=False)
|
||||
else:
|
||||
pd.DataFrame({'提示': ['该周期内无代理业绩记录']}).to_excel(writer, sheet_name='代理业绩明细', index=False)
|
||||
|
||||
# Sheet 4: 提成明细
|
||||
if result.get('detail') and result['detail'].get('personal_performance'):
|
||||
commission_data = []
|
||||
for item in result['detail']['personal_performance'].get('by_category', []):
|
||||
commission_data.append({
|
||||
'产品分类': item['category_name'],
|
||||
'业绩金额': item['amount'],
|
||||
'提成比例': item['commission_rate'],
|
||||
'提成金额': item['commission']
|
||||
})
|
||||
if commission_data:
|
||||
df_commission = pd.DataFrame(commission_data)
|
||||
df_commission.to_excel(writer, sheet_name='提成明细', index=False)
|
||||
|
||||
output.seek(0)
|
||||
filename = f"employee_report_{employee_id}_{period_key}.xlsx"
|
||||
return output.getvalue(), filename
|
||||
|
||||
|
||||
def export_company_report(
|
||||
db: Session,
|
||||
period: str,
|
||||
year: int,
|
||||
month: Optional[int] = None
|
||||
) -> tuple:
|
||||
"""
|
||||
导出公司收益汇总Excel
|
||||
|
||||
Returns:
|
||||
tuple: (excel_bytes, filename)
|
||||
"""
|
||||
# 计算公司收益
|
||||
result = calculate_company_profit(db, period, year, month)
|
||||
|
||||
# 获取周期日期范围
|
||||
start_date, end_date, period_key = get_period_dates(period, year, month)
|
||||
|
||||
# 创建Excel writer
|
||||
output = io.BytesIO()
|
||||
with pd.ExcelWriter(output, engine='openpyxl') as writer:
|
||||
# Sheet 1: 公司收益汇总
|
||||
summary_data = {
|
||||
'项目': [
|
||||
'计算周期',
|
||||
'员工数量',
|
||||
'总返点收入',
|
||||
'员工成本',
|
||||
'代理分成',
|
||||
'总成本',
|
||||
'公司利润'
|
||||
],
|
||||
'金额': [
|
||||
result['period_key'],
|
||||
result['employee_count'],
|
||||
result['total_rebate'],
|
||||
result['total_employee_cost'],
|
||||
result['total_agent_share'],
|
||||
result['total_cost'],
|
||||
result['company_profit']
|
||||
]
|
||||
}
|
||||
df_summary = pd.DataFrame(summary_data)
|
||||
df_summary.to_excel(writer, sheet_name='公司收益汇总', index=False)
|
||||
|
||||
# Sheet 2: 返点明细
|
||||
if result.get('rebate_details'):
|
||||
rebate_data = []
|
||||
for item in result['rebate_details']:
|
||||
rebate_data.append({
|
||||
'产品分类': item['category_name'],
|
||||
'业绩金额': item['amount'],
|
||||
'返点比例': item['rebate_rate'],
|
||||
'返点金额': item['rebate']
|
||||
})
|
||||
df_rebate = pd.DataFrame(rebate_data)
|
||||
df_rebate.to_excel(writer, sheet_name='返点明细', index=False)
|
||||
|
||||
# Sheet 3: 员工成本明细
|
||||
if result.get('employee_details'):
|
||||
emp_data = []
|
||||
for item in result['employee_details']:
|
||||
emp_data.append({
|
||||
'员工ID': item['employee_id'],
|
||||
'员工姓名': item['employee_name'],
|
||||
'员工收入': item['total_income'],
|
||||
'代理分成': item['agent_share']
|
||||
})
|
||||
df_emp = pd.DataFrame(emp_data)
|
||||
df_emp.to_excel(writer, sheet_name='员工成本明细', index=False)
|
||||
|
||||
output.seek(0)
|
||||
filename = f"company_report_{period_key}.xlsx"
|
||||
return output.getvalue(), filename
|
||||
|
||||
|
||||
def export_performance_report(
|
||||
db: Session,
|
||||
filters: Dict[str, Any]
|
||||
) -> tuple:
|
||||
"""
|
||||
导出业绩报表Excel
|
||||
|
||||
Args:
|
||||
filters: 筛选条件
|
||||
- start_date: 开始日期
|
||||
- end_date: 结束日期
|
||||
- employee_id: 员工ID(可选)
|
||||
- agent_id: 代理ID(可选)
|
||||
- category_id: 分类ID(可选)
|
||||
- record_type: 记录类型(employee/agent)
|
||||
|
||||
Returns:
|
||||
tuple: (excel_bytes, filename)
|
||||
"""
|
||||
# 构建查询
|
||||
query = db.query(PerformanceRecord)
|
||||
|
||||
# 应用筛选条件
|
||||
if filters.get('start_date'):
|
||||
query = query.filter(PerformanceRecord.record_date >= filters['start_date'])
|
||||
if filters.get('end_date'):
|
||||
query = query.filter(PerformanceRecord.record_date <= filters['end_date'])
|
||||
if filters.get('employee_id'):
|
||||
query = query.filter(PerformanceRecord.employee_id == filters['employee_id'])
|
||||
if filters.get('agent_id'):
|
||||
query = query.filter(PerformanceRecord.agent_id == filters['agent_id'])
|
||||
if filters.get('category_id'):
|
||||
query = query.filter(PerformanceRecord.category_id == filters['category_id'])
|
||||
if filters.get('record_type'):
|
||||
query = query.filter(PerformanceRecord.record_type == filters['record_type'])
|
||||
|
||||
records = query.order_by(PerformanceRecord.record_date.desc()).all()
|
||||
|
||||
# 创建Excel writer
|
||||
output = io.BytesIO()
|
||||
with pd.ExcelWriter(output, engine='openpyxl') as writer:
|
||||
# Sheet 1: 业绩明细
|
||||
if records:
|
||||
data = []
|
||||
total_amount = 0
|
||||
for record in records:
|
||||
amount = float(record.amount or 0)
|
||||
total_amount += amount
|
||||
|
||||
employee_name = ""
|
||||
if record.employee and record.employee.user:
|
||||
employee_name = record.employee.user.name
|
||||
|
||||
agent_name = ""
|
||||
if record.agent:
|
||||
agent_name = record.agent.company_name
|
||||
|
||||
category_name = ""
|
||||
if record.category:
|
||||
category_name = record.category.name
|
||||
|
||||
data.append({
|
||||
'ID': record.id,
|
||||
'记录类型': '员工业绩' if record.record_type == 'employee' else '代理业绩',
|
||||
'日期': record.record_date,
|
||||
'员工': employee_name,
|
||||
'代理': agent_name,
|
||||
'产品分类': category_name,
|
||||
'客户名称': record.customer_name or '',
|
||||
'订单号': record.order_no or '',
|
||||
'业绩金额': amount,
|
||||
'备注': record.remark or ''
|
||||
})
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
df.to_excel(writer, sheet_name='业绩明细', index=False)
|
||||
|
||||
# Sheet 2: 汇总统计
|
||||
summary_data = {
|
||||
'统计项': [
|
||||
'记录总数',
|
||||
'业绩总额',
|
||||
'平均单笔业绩',
|
||||
'员工业绩笔数',
|
||||
'代理业绩笔数'
|
||||
],
|
||||
'数值': [
|
||||
len(records),
|
||||
total_amount,
|
||||
round(total_amount / len(records), 2) if records else 0,
|
||||
len([r for r in records if r.record_type == 'employee']),
|
||||
len([r for r in records if r.record_type == 'agent'])
|
||||
]
|
||||
}
|
||||
df_summary = pd.DataFrame(summary_data)
|
||||
df_summary.to_excel(writer, sheet_name='汇总统计', index=False)
|
||||
|
||||
# Sheet 3: 按员工汇总
|
||||
emp_summary = {}
|
||||
for record in records:
|
||||
emp_name = record.employee.user.name if record.employee and record.employee.user else '未知'
|
||||
if emp_name not in emp_summary:
|
||||
emp_summary[emp_name] = {'count': 0, 'amount': 0}
|
||||
emp_summary[emp_name]['count'] += 1
|
||||
emp_summary[emp_name]['amount'] += float(record.amount or 0)
|
||||
|
||||
emp_data = []
|
||||
for name, stats in emp_summary.items():
|
||||
emp_data.append({
|
||||
'员工': name,
|
||||
'业绩笔数': stats['count'],
|
||||
'业绩总额': stats['amount']
|
||||
})
|
||||
df_emp = pd.DataFrame(emp_data)
|
||||
df_emp.to_excel(writer, sheet_name='按员工汇总', index=False)
|
||||
|
||||
# Sheet 4: 按分类汇总
|
||||
cat_summary = {}
|
||||
for record in records:
|
||||
cat_name = record.category.name if record.category else '未知'
|
||||
if cat_name not in cat_summary:
|
||||
cat_summary[cat_name] = {'count': 0, 'amount': 0}
|
||||
cat_summary[cat_name]['count'] += 1
|
||||
cat_summary[cat_name]['amount'] += float(record.amount or 0)
|
||||
|
||||
cat_data = []
|
||||
for name, stats in cat_summary.items():
|
||||
cat_data.append({
|
||||
'产品分类': name,
|
||||
'业绩笔数': stats['count'],
|
||||
'业绩总额': stats['amount']
|
||||
})
|
||||
df_cat = pd.DataFrame(cat_data)
|
||||
df_cat.to_excel(writer, sheet_name='按分类汇总', index=False)
|
||||
|
||||
else:
|
||||
pd.DataFrame({'提示': ['无业绩记录']}).to_excel(writer, sheet_name='业绩明细', index=False)
|
||||
|
||||
output.seek(0)
|
||||
|
||||
# 生成文件名
|
||||
date_str = datetime.now().strftime('%Y%m%d')
|
||||
filename = f"performance_report_{date_str}.xlsx"
|
||||
|
||||
return output.getvalue(), filename
|
||||
Reference in New Issue
Block a user