Files
Django/jituan/services/daily_finance_display.py

138 lines
4.7 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.
"""财务看板:只读 daily_income_stat / daily_payout_stat支持按俱乐部 Header 过滤。"""
from datetime import date, timedelta
from decimal import Decimal
from django.db.models import Sum
from config.models import DailyIncomeStat, DailyPayoutStat
from jituan.constants import CLUB_ID_DEFAULT, DATA_SCOPE_ALL
from jituan.services.club_context import filter_club_char_field, resolve_club_scope
from jituan.services.szjilu_accounting import (
collect_today_live_wechat_payments,
normalize_szjilu_club_id,
)
def _income_qs(request):
if request is None:
return DailyIncomeStat.objects.all()
return filter_club_char_field(DailyIncomeStat.objects.all(), request, field='club_id')
def _payout_qs(request):
if request is None:
return DailyPayoutStat.objects.all()
return filter_club_char_field(DailyPayoutStat.objects.all(), request, field='club_id')
def _payment_matches_request(club_id, request):
if request is None or resolve_club_scope(request) == DATA_SCOPE_ALL:
return True
from jituan.services.club_context import resolve_club_id_from_request
cid = resolve_club_id_from_request(request)
ic = normalize_szjilu_club_id(club_id)
if cid == CLUB_ID_DEFAULT:
return ic == CLUB_ID_DEFAULT
return ic == cid
def sum_income_stat_for_date(stat_date, request=None):
amount = Decimal('0')
count = 0
for row in _income_qs(request).filter(date=stat_date).iterator():
amount += Decimal(str(row.total_amount or 0))
count += int(row.total_count or 0)
return float(amount), count
def sum_payout_stat_for_date(stat_date, request=None):
amount = Decimal('0')
count = 0
for row in _payout_qs(request).filter(date=stat_date).iterator():
amount += Decimal(str(row.total_amount or 0))
count += int(row.total_count or 0)
return float(amount), count
def count_live_income_payments(stat_date, request=None):
"""当日真实微信支付笔数(去重),可按俱乐部过滤。"""
items = collect_today_live_wechat_payments(stat_date)
if request is None:
return len(items)
return sum(
1 for _ref, cid, _jine, _kind in items
if _payment_matches_request(cid, request)
)
def today_income_for_dashboard(stat_date=None, request=None):
"""
今日收入:金额与笔数均来自 daily_income_stat按俱乐部过滤
笔数若被 repair 虚增,用当日真实支付笔数校正。
"""
stat_date = stat_date or date.today()
amount, table_count = sum_income_stat_for_date(stat_date, request)
live_count = count_live_income_payments(stat_date, request)
if live_count > 0 and table_count > live_count:
display_count = live_count
else:
display_count = table_count
return amount, display_count
def cumulative_income_payout(request=None):
"""累计收入/支出/利润(按当前俱乐部视图范围)。"""
total_income = float(
_income_qs(request).aggregate(t=Sum('total_amount'))['t'] or 0
)
total_payout = float(
_payout_qs(request).aggregate(t=Sum('total_amount'))['t'] or 0
)
return total_income, total_payout, round(total_income - total_payout, 2)
def daily_stats_for_request(request=None, days=30):
"""近 N 天每日收支(按俱乐部视图)。"""
since = date.today() - timedelta(days=days)
income_by_date = {
str(row['date']): {
'income': float(row['total_amount'] or 0),
'income_count': int(row['total_count'] or 0),
}
for row in _income_qs(request).filter(date__gte=since).values('date').annotate(
total_amount=Sum('total_amount'),
total_count=Sum('total_count'),
)
}
payout_by_date = {
str(row['date']): float(row['total_amount'] or 0)
for row in _payout_qs(request).filter(date__gte=since).values('date').annotate(
total_amount=Sum('total_amount'),
)
}
daily_stats = []
all_dates = sorted(
set(income_by_date.keys()) | set(payout_by_date.keys()),
reverse=True,
)[:days]
for d in all_dates:
inc = income_by_date.get(d, {'income': 0.0, 'income_count': 0})
payout_amt = payout_by_date.get(d, 0.00)
daily_stats.append({
'date': d,
'income': inc['income'],
'income_count': inc['income_count'],
'payout': payout_amt,
'profit': round(inc['income'] - payout_amt, 2),
})
return daily_stats
def income_count_for_summary_date(stat_date, request=None):
if stat_date == date.today():
_, count = today_income_for_dashboard(stat_date, request)
return count
_, count = sum_income_stat_for_date(stat_date, request)
return count