202 lines
7.9 KiB
Python
202 lines
7.9 KiB
Python
"""俱乐部维度财务统计(供 /jituan/houtai/caiwu,旧 /houtai/caiwu 不变)。"""
|
||
from calendar import monthrange
|
||
from datetime import date, datetime, timedelta
|
||
from decimal import Decimal
|
||
|
||
from django.db.models import Sum
|
||
|
||
from jituan.services.club_context import (
|
||
filter_club_char_field,
|
||
filter_queryset_by_club,
|
||
resolve_club_id_from_request,
|
||
resolve_club_scope,
|
||
)
|
||
from jituan.constants import DATA_SCOPE_ALL
|
||
|
||
from config.models import DailyIncomeStat, DailyPayoutStat, Szjilu
|
||
from orders.models import Order
|
||
from products.models import Czjilu, Huiyuangoumai
|
||
from users.models import UserDashou, UserGuanshi, UserShangjia, UserZuzhang
|
||
|
||
|
||
def _role_profile_qs(profile_model, request):
|
||
"""按俱乐部过滤角色扩展表(通过 user.ClubID)。"""
|
||
qs = profile_model.query.all()
|
||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||
return qs
|
||
return qs.filter(user__ClubID=resolve_club_id_from_request(request))
|
||
|
||
|
||
def _sum_role_balance(profile_model, balance_field, request):
|
||
return float(
|
||
_role_profile_qs(profile_model, request).aggregate(total=Sum(balance_field))['total'] or 0.00
|
||
)
|
||
|
||
|
||
def _build_szjilu_payload(request):
|
||
"""全局收支流水表 szjilu(按俱乐部或集团汇总)。"""
|
||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||
agg = Szjilu.query.aggregate(
|
||
zongshouyi=Sum('TotalIncome'),
|
||
zongliushui=Sum('TotalFlow'),
|
||
zongzhichu=Sum('TotalExpense'),
|
||
jrls=Sum('DailyFlow'),
|
||
jrzc=Sum('DailyExpense'),
|
||
)
|
||
return {
|
||
'zongliushui': float(agg['zongliushui'] or 0),
|
||
'zongshouyi': float(agg['zongshouyi'] or 0),
|
||
'zongzhichu': float(agg['zongzhichu'] or 0),
|
||
'jrls': float(agg['jrls'] or 0),
|
||
'jrzc': float(agg['jrzc'] or 0),
|
||
}
|
||
from jituan.services.szjilu_accounting import get_szjilu_snapshot
|
||
return get_szjilu_snapshot(resolve_club_id_from_request(request))
|
||
|
||
|
||
def _order_qs(request):
|
||
return filter_queryset_by_club(Order.query.all(), request, club_field='ClubID')
|
||
|
||
|
||
def _czjilu_qs(request):
|
||
return filter_club_char_field(Czjilu.query.all(), request, field='club_id')
|
||
|
||
|
||
def _huiyuangoumai_qs(request):
|
||
return filter_club_char_field(Huiyuangoumai.query.all(), request, field='club_id')
|
||
|
||
|
||
def _daily_income_qs(request):
|
||
return filter_club_char_field(DailyIncomeStat.query.all(), request, field='club_id')
|
||
|
||
|
||
def _daily_payout_qs(request):
|
||
return filter_club_char_field(DailyPayoutStat.query.all(), request, field='club_id')
|
||
|
||
|
||
def build_caiwu_payload(request):
|
||
"""与 CaiwuView 返回结构一致,但按俱乐部过滤订单/充值流水。"""
|
||
today = date.today()
|
||
today_start = datetime.combine(today, datetime.min.time())
|
||
today_end = today_start + timedelta(days=1)
|
||
club_id = resolve_club_id_from_request(request)
|
||
|
||
order_qs = _order_qs(request)
|
||
cz_qs = _czjilu_qs(request)
|
||
hy_qs = _huiyuangoumai_qs(request)
|
||
income_qs = _daily_income_qs(request)
|
||
payout_qs = _daily_payout_qs(request)
|
||
|
||
today_income = income_qs.filter(date=today).first()
|
||
today_income_amount = float(today_income.total_amount) if today_income else 0.00
|
||
today_income_count = today_income.total_count if today_income else 0
|
||
|
||
today_payout = payout_qs.filter(date=today).first()
|
||
today_payout_amount = float(today_payout.total_amount) if today_payout else 0.00
|
||
today_payout_count = today_payout.total_count if today_payout else 0
|
||
|
||
today_orders = order_qs.filter(
|
||
CreateTime__gte=today_start,
|
||
CreateTime__lt=today_end,
|
||
Status__in=[1, 2, 3, 4, 5, 6, 7, 8],
|
||
)
|
||
today_order_count = today_orders.count()
|
||
today_order_amount = float(today_orders.aggregate(total=Sum('Amount'))['total'] or 0.00)
|
||
|
||
today_completed = order_qs.filter(
|
||
CreateTime__gte=today_start, CreateTime__lt=today_end, Status=3,
|
||
)
|
||
today_completed_count = today_completed.count()
|
||
today_completed_amount = float(today_completed.aggregate(total=Sum('Amount'))['total'] or 0.00)
|
||
|
||
today_tuikuan = order_qs.filter(
|
||
CreateTime__gte=today_start, CreateTime__lt=today_end, Status=5,
|
||
)
|
||
today_tuikuan_count = today_tuikuan.count()
|
||
today_tuikuan_amount = float(today_tuikuan.aggregate(total=Sum('Amount'))['total'] or 0.00)
|
||
|
||
today_chongzhi = cz_qs.filter(
|
||
CreateTime__gte=today_start, CreateTime__lt=today_end, leixing=1, zhuangtai=3,
|
||
)
|
||
today_chongzhi_count = today_chongzhi.count()
|
||
today_chongzhi_amount = float(today_chongzhi.aggregate(total=Sum('jine'))['total'] or 0.00)
|
||
|
||
new_huiyuan_user_ids = hy_qs.filter(
|
||
CreateTime__gte=today_start, CreateTime__lt=today_end,
|
||
).values_list('yonghu_id', flat=True).distinct()
|
||
today_new_huiyuan = len(list(new_huiyuan_user_ids))
|
||
|
||
today_new_huiyuan_amount = 0.0
|
||
if new_huiyuan_user_ids:
|
||
amount_sum = cz_qs.filter(
|
||
CreateTime__gte=today_start,
|
||
CreateTime__lt=today_end,
|
||
yonghuid__in=new_huiyuan_user_ids,
|
||
leixing=1,
|
||
zhuangtai=3,
|
||
).aggregate(total=Sum('jine'))
|
||
today_new_huiyuan_amount = float(amount_sum['total'] or 0.0)
|
||
|
||
all_guanshi_yue = _sum_role_balance(UserGuanshi, 'yue', request)
|
||
all_dashou_yue = _sum_role_balance(UserDashou, 'yue', request)
|
||
all_zuzhang_yue = _sum_role_balance(UserZuzhang, 'ketixian_jine', request)
|
||
all_shangjia_yue = _sum_role_balance(UserShangjia, 'yue', request)
|
||
|
||
szjilu_payload = _build_szjilu_payload(request)
|
||
|
||
# 账户总收入/总支出:szjilu 全历史累计(非今日)
|
||
total_income = float(szjilu_payload.get('zongliushui') or 0.00)
|
||
total_payout = float(szjilu_payload.get('zongzhichu') or 0.00)
|
||
# 俱乐部利润:累计总流水 − 累计总支出
|
||
platform_profit = round(total_income - total_payout, 2)
|
||
|
||
# 日统计表全历史合计(供核对)
|
||
daily_sum_income = float(income_qs.aggregate(total=Sum('total_amount'))['total'] or 0.00)
|
||
daily_sum_payout = float(payout_qs.aggregate(total=Sum('total_amount'))['total'] or 0.00)
|
||
|
||
daily_stats = []
|
||
income_list = income_qs.order_by('-date')[:30]
|
||
payout_dict = {
|
||
str(p.date): float(p.total_amount)
|
||
for p in payout_qs.filter(date__gte=today - timedelta(days=30))
|
||
}
|
||
for inc in income_list:
|
||
d = str(inc.date)
|
||
daily_stats.append({
|
||
'date': d,
|
||
'income': float(inc.total_amount),
|
||
'income_count': inc.total_count,
|
||
'payout': payout_dict.get(d, 0.00),
|
||
'profit': round(float(inc.total_amount) - payout_dict.get(d, 0.00), 2),
|
||
})
|
||
|
||
return {
|
||
'club_id': club_id,
|
||
'scope': resolve_club_scope(request),
|
||
'szjilu': szjilu_payload,
|
||
'today_income': round(today_income_amount, 2),
|
||
'today_income_count': today_income_count,
|
||
'today_payout': round(today_payout_amount, 2),
|
||
'today_payout_count': today_payout_count,
|
||
'today_order_count': today_order_count,
|
||
'today_order_amount': round(today_order_amount, 2),
|
||
'today_completed_count': today_completed_count,
|
||
'today_completed_amount': round(today_completed_amount, 2),
|
||
'today_tuikuan_count': today_tuikuan_count,
|
||
'today_tuikuan_amount': round(today_tuikuan_amount, 2),
|
||
'today_chongzhi_count': today_chongzhi_count,
|
||
'today_chongzhi_amount': round(today_chongzhi_amount, 2),
|
||
'today_new_huiyuan': today_new_huiyuan,
|
||
'today_new_huiyuan_amount': round(today_new_huiyuan_amount, 2),
|
||
'all_guanshi_yue': round(all_guanshi_yue, 2),
|
||
'all_dashou_yue': round(all_dashou_yue, 2),
|
||
'all_zuzhang_yue': round(all_zuzhang_yue, 2),
|
||
'all_shangjia_yue': round(all_shangjia_yue, 2),
|
||
'total_income': round(total_income, 2),
|
||
'total_payout': round(total_payout, 2),
|
||
'platform_profit': platform_profit,
|
||
'daily_stat_sum_income': round(daily_sum_income, 2),
|
||
'daily_stat_sum_payout': round(daily_sum_payout, 2),
|
||
'daily_stats': daily_stats,
|
||
}
|