Files
Django/config/management/commands/fix_income_month.py

115 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.
"""
修复 repair_wechat_income 搞乱的收入200 显示成 600+)。
两种模式(先预览,再加 --yes
A. 默认:重扫本月真实微信支付,删掉脏数据后重建
python manage.py fix_income_month
python manage.py fix_income_month --yes
B. 若日志金额已对、只是日统计表脏了:
python manage.py fix_income_month --logs-only --yes
"""
from calendar import monthrange
from datetime import date
from django.core.management.base import BaseCommand
from django.db.models import Sum
from config.models import DailyIncomeStat, PlatformIncomeLog
from jituan.services.szjilu_accounting import (
align_daily_income_from_logs_only,
dedupe_platform_income_logs,
fix_income_period,
)
class Command(BaseCommand):
help = '修复本月收入统计错乱repair 重复记账)'
def add_arguments(self, parser):
parser.add_argument('--year', type=int, default=0, help='年,默认今年')
parser.add_argument('--month', type=int, default=0, help='月,默认本月')
parser.add_argument('--today-only', action='store_true', help='只修今天')
parser.add_argument('--logs-only', action='store_true', help='只按现有日志重写日统计,不重扫订单')
parser.add_argument('--yes', action='store_true', help='确认执行')
def handle(self, *args, **options):
today = date.today()
year = options['year'] or today.year
month = options['month'] or today.month
dry_run = not options['yes']
if options['today_only']:
start = end = today
label = f'今日 {today}'
else:
_, last = monthrange(year, month)
start = date(year, month, 1)
end = date(year, month, last)
label = f'{year}-{month:02d} 全月'
if options['logs_only']:
self._run_logs_only(dry_run, label)
return
self.stdout.write(self.style.WARNING(f'{label}】收入修复\n'))
if dry_run:
self.stdout.write('预览模式(未改库)\n')
r = fix_income_period(start, end, dry_run=dry_run)
self.stdout.write(f' 错乱日统计合计: {r["before_daily_amount"]:.2f} 元 / {r["before_daily_count"]}')
self.stdout.write(f' 错乱日志合计: {r["before_log_amount"]:.2f} 元 / {r["before_log_count"]}')
self.stdout.write(self.style.SUCCESS(
f' 重建后(严格): {r["after_amount"]:.2f} 元 / {r["after_count"]}'
))
if r['items']:
self.stdout.write(' 明细:')
for row in r['items']:
if len(row) == 5:
d, ref, cid, amt, kind = row
self.stdout.write(f' {d} {ref} {amt}元 club={cid} ({kind})')
else:
ref, cid, amt, kind = row
self.stdout.write(f' {ref} {amt}元 club={cid} ({kind})')
if not dry_run:
self._print_today_total(today)
else:
self.stdout.write(self.style.WARNING(
'\n重建后金额对 → python manage.py fix_income_month --yes\n'
'只修今天 → python manage.py fix_income_month --today-only --yes\n'
'日志已对、日统计脏 → python manage.py fix_income_month --logs-only --yes'
))
def _run_logs_only(self, dry_run, label):
try:
log_sum = PlatformIncomeLog.objects.aggregate(t=Sum('amount'))['t'] or 0
log_cnt = PlatformIncomeLog.objects.count()
daily_sum = DailyIncomeStat.objects.aggregate(t=Sum('total_amount'))['t'] or 0
except Exception as exc:
self.stderr.write(f'读取失败: {exc}')
return
self.stdout.write(f'{label}】仅按日志对齐日统计\n')
self.stdout.write(f' platform_income_log 合计: {float(log_sum):.2f} 元 / {log_cnt}')
self.stdout.write(f' daily_income_stat 现合计: {float(daily_sum):.2f}')
if dry_run:
self.stdout.write(self.style.WARNING('\n预览。确认后: python manage.py fix_income_month --logs-only --yes'))
return
n = align_daily_income_from_logs_only()
self.stdout.write(self.style.SUCCESS(f'已重写 daily_income_stat {n} 行,并重算 szjilu'))
self._print_today_total(date.today())
def _print_today_total(self, today):
agg = DailyIncomeStat.objects.filter(date=today).aggregate(
amt=Sum('total_amount'), cnt=Sum('total_count'),
)
self.stdout.write(self.style.SUCCESS(
f'\n数据库今日日统计: {float(agg["amt"] or 0):.2f} 元 / {int(agg["cnt"] or 0)}'
))
self.stdout.write('请刷新后台财务页核对。')