49 lines
2.4 KiB
Plaintext
49 lines
2.4 KiB
Plaintext
# 日终切日任务端点:日结余额快照 + 日结总账 + 营业日期推进
|
||
# GET /accounting/api/dayend.dspy
|
||
#
|
||
# 安全:本端点是定时任务入口(写库),只允许本机调用——与 fetch_forex_rates.dspy 同模式。
|
||
# 判定依据 client_ip:nginx 用 $proxy_add_x_forwarded_for 追加模式、中间件取链尾值,
|
||
# 外部伪造 X-Forwarded-For 会被追加真实 IP 到链尾,伪造不成立;且应用端口不对外开放。
|
||
_ip = request.get('client_ip') or ''
|
||
if _ip not in ('127.0.0.1', '::1', 'localhost'):
|
||
return json.dumps({'success': False, 'message': '仅允许本机调用(定时任务入口)'},
|
||
ensure_ascii=False)
|
||
|
||
from sqlor.dbpools import get_sor_context
|
||
|
||
# 切日三步(同一 sor 上下文,全成才提交):
|
||
# 1. dayend_balance —— 上一营业日余额快照(acc_balance 补齐无当日行的账户)
|
||
# 2. accounting_ledger —— 上一营业日总账(delete+insert 幂等重建 ledger 当日行)
|
||
# 3. new_business_date —— params.business_date 推进 +1 天
|
||
# 顺序不可颠倒:前两步都读「上一营业日」,推进日期必须在最后。
|
||
async with get_sor_context(request._run_ns, 'accounting') as sor:
|
||
try:
|
||
from datetime import date
|
||
from accounting.dayend_balance import dayend_balance
|
||
from accounting.ledger import accounting_ledger
|
||
from appbase.businessdate import get_business_date, new_business_date
|
||
|
||
old_bd = await get_business_date(sor)
|
||
# 幂等守卫:business_date 已到今天则跳过(cron 重跑/手工重放不会多跳日期)
|
||
today = date.today().isoformat()
|
||
if old_bd >= today:
|
||
return json.dumps({
|
||
'success': True, 'skipped': True,
|
||
'business_date': old_bd,
|
||
'message': '已是当日营业日期,跳过切日(幂等)',
|
||
}, ensure_ascii=False)
|
||
|
||
snap_date = await dayend_balance(sor)
|
||
await accounting_ledger(sor)
|
||
await new_business_date(sor)
|
||
new_bd = await get_business_date(sor)
|
||
await sor.sqlExe("COMMIT", {})
|
||
return json.dumps({
|
||
'success': True,
|
||
'business_date': '%s -> %s' % (old_bd, new_bd),
|
||
'snapshot_date': snap_date,
|
||
}, ensure_ascii=False)
|
||
except Exception as e:
|
||
return json.dumps({'success': False, 'message': 'dayend failed: %s' % str(e)[:200]},
|
||
ensure_ascii=False)
|