Files
Django/jituan/services/szxx_stats.py

120 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""每日收支详细统计(供 /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