fix: Szenario-Vergleich zeigt vor heute Ist-Saldo, veraltete Szenarien werden neu gerechnet (A11-T2)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,11 @@ from datetime import date, timedelta
|
||||
from datetime import datetime, timezone
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import delete, select
|
||||
from sqlalchemy import delete, func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.config import get_settings
|
||||
from app.db import advisory_xact_lock
|
||||
from app.engine.loans import loan_schedule
|
||||
from app.engine.projection import project
|
||||
from app.engine.scenario import (PlainModifier, PlainPlanned, PlainRecurring,
|
||||
@@ -63,3 +64,32 @@ def run_projection(session: Session, scenario: Scenario, horizon_days: int,
|
||||
session.add(result)
|
||||
session.commit()
|
||||
return result
|
||||
|
||||
|
||||
# Advisory-Lock-Schlüssel (beliebig, aber projektweit eindeutig).
|
||||
STALE_PROJECTION_LOCK = 110_001
|
||||
|
||||
|
||||
def refresh_stale_projections(session: Session, today: date) -> int:
|
||||
"""Rechnet jedes bereits durchgerechnete Szenario neu, dessen Projektion
|
||||
vor `today` beginnt (erster gespeicherter Punkt <= today, d.h. Rechen-Start
|
||||
< today). Maßstab ist der Projektionsbeginn, nicht computed_at (UTC-
|
||||
Zeitstempel, würde um Mitternacht falsch schneiden). Nie gerechnete
|
||||
Szenarien bleiben unberührt. Je Szenario: Lock nehmen, Staleness NEU
|
||||
prüfen (ein paralleler Seitenaufruf kann schon gerechnet haben), rechnen;
|
||||
run_projection committet und gibt damit den Lock frei.
|
||||
Rückgabe: Anzahl neu gerechneter Szenarien."""
|
||||
horizon = get_settings().horizon_days
|
||||
ids = list(session.execute(select(ProjectionResult.scenario_id)).scalars())
|
||||
refreshed = 0
|
||||
for sid in ids:
|
||||
advisory_xact_lock(session, STALE_PROJECTION_LOCK)
|
||||
first = session.execute(select(func.min(ProjectionPoint.day))
|
||||
.where(ProjectionPoint.scenario_id == sid)).scalar()
|
||||
scenario = session.get(Scenario, sid)
|
||||
if scenario is None or first is None or first > today:
|
||||
session.commit() # Lock freigeben
|
||||
continue
|
||||
run_projection(session, scenario, horizon, today)
|
||||
refreshed += 1
|
||||
return refreshed
|
||||
|
||||
Reference in New Issue
Block a user