120 lines
4.4 KiB
Python
120 lines
4.4 KiB
Python
"""每日收支详细统计(供 /jituan/houtai/szxx,旧 /houtai/szxx 不变)。"""
|
||
from datetime import date, datetime
|
||
|
||
from django.db.models import Sum
|
||
|
||
from config.models import DailyIncomeStat, DailyPayoutStat
|
||
from jituan.services.club_context import filter_queryset_by_club, resolve_club_id_from_request, resolve_club_scope
|
||
|
||
|
||
def build_szxx_payload(request, granularity, year, month, summary_date):
|
||
"""
|
||
与 SzxxView 返回结构一致,按俱乐部过滤日收入/日出款统计。
|
||
返回 (data_dict, error_msg);error_msg 非空时表示参数错误。
|
||
"""
|
||
now = date.today()
|
||
|
||
if granularity == 'day':
|
||
if not year or not month:
|
||
return None, '按日统计需要年份和月份'
|
||
group_field = 'day'
|
||
income_filter = {'year': year, 'month': month}
|
||
payout_filter = {'year': year, 'month': month}
|
||
if not summary_date:
|
||
summary_date = now.strftime('%Y-%m-%d')
|
||
summary_parts = summary_date.split('-')
|
||
summary_filter = {
|
||
'year': int(summary_parts[0]),
|
||
'month': int(summary_parts[1]),
|
||
'day': int(summary_parts[2]),
|
||
}
|
||
elif granularity == 'month':
|
||
if not year:
|
||
return None, '按月统计需要年份'
|
||
group_field = 'month'
|
||
income_filter = {'year': year}
|
||
payout_filter = {'year': year}
|
||
if not summary_date:
|
||
summary_date = now.strftime('%Y-%m')
|
||
summary_parts = summary_date.split('-')
|
||
summary_filter = {'year': int(summary_parts[0]), 'month': int(summary_parts[1])}
|
||
elif granularity == 'year':
|
||
group_field = 'year'
|
||
income_filter = {}
|
||
payout_filter = {}
|
||
if not summary_date:
|
||
summary_date = str(now.year)
|
||
summary_filter = {'year': int(summary_date)}
|
||
else:
|
||
return None, '无效的颗粒度'
|
||
|
||
income_qs = filter_queryset_by_club(
|
||
DailyIncomeStat.query.filter(**income_filter), request,
|
||
).values(group_field).annotate(
|
||
total_income=Sum('total_amount'),
|
||
total_count=Sum('total_count'),
|
||
).order_by(group_field)
|
||
|
||
payout_qs = filter_queryset_by_club(
|
||
DailyPayoutStat.query.filter(**payout_filter), request,
|
||
).values(group_field).annotate(
|
||
total_payout=Sum('total_amount'),
|
||
total_count=Sum('total_count'),
|
||
).order_by(group_field)
|
||
|
||
data_map = {}
|
||
for item in income_qs:
|
||
key = item[group_field]
|
||
data_map[key] = {
|
||
'income': float(item['total_income'] or 0),
|
||
'income_count': item['total_count'] or 0,
|
||
'payout': 0.0,
|
||
'payout_count': 0,
|
||
}
|
||
|
||
for item in payout_qs:
|
||
key = item[group_field]
|
||
if key not in data_map:
|
||
data_map[key] = {'income': 0.0, 'income_count': 0, 'payout': 0.0, 'payout_count': 0}
|
||
data_map[key]['payout'] = float(item['total_payout'] or 0)
|
||
data_map[key]['payout_count'] = item['total_count'] or 0
|
||
|
||
time_series = []
|
||
for key, vals in data_map.items():
|
||
if granularity == 'day':
|
||
date_str = f"{year}-{month:02d}-{key:02d}"
|
||
elif granularity == 'month':
|
||
date_str = f"{year}-{key:02d}"
|
||
else:
|
||
date_str = str(key)
|
||
profit = round(vals['income'] - vals['payout'], 2)
|
||
time_series.append({
|
||
'date': date_str,
|
||
'income': vals['income'],
|
||
'payout': vals['payout'],
|
||
'profit': profit,
|
||
})
|
||
time_series.sort(key=lambda x: x['date'])
|
||
|
||
income_summary = filter_queryset_by_club(
|
||
DailyIncomeStat.query.filter(**summary_filter), request,
|
||
).aggregate(total_income=Sum('total_amount'), total_count=Sum('total_count'))
|
||
payout_summary = filter_queryset_by_club(
|
||
DailyPayoutStat.query.filter(**summary_filter), request,
|
||
).aggregate(total_payout=Sum('total_amount'), total_count=Sum('total_count'))
|
||
total_income = float(income_summary['total_income'] or 0)
|
||
total_payout = float(payout_summary['total_payout'] or 0)
|
||
|
||
return {
|
||
'club_id': resolve_club_id_from_request(request),
|
||
'scope': resolve_club_scope(request),
|
||
'time_series': time_series,
|
||
'summary': {
|
||
'total_income': total_income,
|
||
'total_income_count': income_summary['total_count'] or 0,
|
||
'total_payout': total_payout,
|
||
'total_payout_count': payout_summary['total_count'] or 0,
|
||
'total_profit': round(total_income - total_payout, 2),
|
||
},
|
||
}, None
|