fix: 财务接口+恢复今日收入
This commit is contained in:
70
config/management/commands/rebuild_income_stats.py
Normal file
70
config/management/commands/rebuild_income_stats.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""
|
||||
【终极恢复】清空全部收支脏数据,按真实微信支付逐笔重建。
|
||||
|
||||
只执行这一条,不要再用 repair / undo / fix:
|
||||
|
||||
预览:python manage.py rebuild_income_stats
|
||||
执行:python manage.py rebuild_income_stats --yes
|
||||
"""
|
||||
from datetime import date
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db.models import Sum
|
||||
|
||||
from config.models import DailyIncomeStat
|
||||
from jituan.services.szjilu_accounting import rebuild_income_stats_from_scratch
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '清空收支脏数据,按真实微信支付逐笔重建(终极恢复)'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--since-year', type=int, default=0, help='从哪一年扫,默认去年')
|
||||
parser.add_argument('--until-year', type=int, default=0, help='到哪一年,默认今年')
|
||||
parser.add_argument('--yes', action='store_true', help='确认执行')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
until_year = options['until_year'] or date.today().year
|
||||
since_year = options['since_year'] or (until_year - 1)
|
||||
dry_run = not options['yes']
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.WARNING('【预览】未改数据库\n'))
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR(
|
||||
'【执行】删除全部收入日志和日统计,按真实支付重建...\n'
|
||||
))
|
||||
|
||||
r = rebuild_income_stats_from_scratch(
|
||||
since_year=since_year, until_year=until_year, dry_run=dry_run,
|
||||
)
|
||||
|
||||
self.stdout.write(f'=== {since_year} ~ {until_year} ===')
|
||||
self.stdout.write(f' 错乱前 daily 合计: {r["before_daily_total"]:.2f} 元')
|
||||
self.stdout.write(f' 错乱前 log: {r["before_log_total"]:.2f} 元 / {r["before_log_count"]} 条')
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f' 重建后合计: {r["after_total"]:.2f} 元 / {r["after_count"]} 笔'
|
||||
))
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f' 重建后本月: {r["month_amount"]:.2f} 元'
|
||||
))
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f' 重建后今日: {r["today_amount"]:.2f} 元 / {r["today_count"]} 笔 ← 看这条'
|
||||
))
|
||||
|
||||
if not dry_run:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f'\ndaily 表合计: {r.get("after_daily_total", 0):.2f} 元'
|
||||
))
|
||||
agg = DailyIncomeStat.objects.filter(date=date.today()).aggregate(
|
||||
amt=Sum('total_amount'), cnt=Sum('total_count'),
|
||||
)
|
||||
self.stdout.write(
|
||||
f'今日 daily 行合计: {float(agg["amt"] or 0):.2f} 元 / {int(agg["cnt"] or 0)} 笔'
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS('\n完成。刷新后台财务页。'))
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING(
|
||||
f'\n今日 {r["today_amount"]:.2f} 元对的话:\n'
|
||||
f' python manage.py rebuild_income_stats --yes'
|
||||
))
|
||||
48
config/management/commands/repopulate_today_income.py
Normal file
48
config/management/commands/repopulate_today_income.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""只恢复今日收入(修复后台四个板子显示 0)。"""
|
||||
from datetime import date
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db.models import Sum
|
||||
|
||||
from config.models import DailyIncomeStat
|
||||
from jituan.services.szjilu_accounting import repopulate_today_income_stats
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '重建今日 daily_income_stat(修复财务页今日收入为 0)'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--date', type=str, default='', help='日期 YYYY-MM-DD,默认今天')
|
||||
parser.add_argument('--yes', action='store_true', help='确认执行')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
stat_date = date.fromisoformat(options['date']) if options['date'] else date.today()
|
||||
dry_run = not options['yes']
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.WARNING('【预览】\n'))
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING('【执行】重建今日收入...\n'))
|
||||
|
||||
r = repopulate_today_income_stats(stat_date, dry_run=dry_run)
|
||||
self.stdout.write(f'日期: {r["date"]}')
|
||||
self.stdout.write(f' 当前: {r["before_amount"]:.2f} 元 / {r["before_count"]} 笔')
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f' 重建后: {r["after_amount"]:.2f} 元 / {r["after_count"]} 笔'
|
||||
))
|
||||
if r['items']:
|
||||
for ref, cid, amt, kind in r['items']:
|
||||
self.stdout.write(f' {ref} {amt}元 club={cid} ({kind})')
|
||||
|
||||
if not dry_run:
|
||||
agg = DailyIncomeStat.objects.filter(date=stat_date).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('请刷新后台财务页。')
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING(
|
||||
'\n确认后: python manage.py repopulate_today_income --yes'
|
||||
))
|
||||
Reference in New Issue
Block a user