115 lines
4.7 KiB
Python
115 lines
4.7 KiB
Python
"""
|
||
修复 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('请刷新后台财务页核对。')
|