feat: Engine+Schema fuer Szenario-Ende und szenario-eigene Einmalzahlungen
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,7 @@ class PlainModifier:
|
||||
target_id: int
|
||||
kind: str
|
||||
value: Decimal
|
||||
end_date: date | None = None
|
||||
|
||||
|
||||
def _modified(amount: Decimal, category_id: int | None, recurring_id: int | None,
|
||||
@@ -55,6 +56,19 @@ def _modified(amount: Decimal, category_id: int | None, recurring_id: int | None
|
||||
return amount
|
||||
|
||||
|
||||
def _effective_end(r: PlainRecurring, modifiers: list[PlainModifier]) -> date | None:
|
||||
"""Fruehestes Ende aus eigenem end_date und allen treffenden ende-Modifikatoren."""
|
||||
end = r.end_date
|
||||
for m in modifiers:
|
||||
if m.kind != "ende" or m.end_date is None:
|
||||
continue
|
||||
hit = ((m.target_type == "category" and r.category_id == m.target_id)
|
||||
or (m.target_type == "recurring" and r.id == m.target_id))
|
||||
if hit:
|
||||
end = m.end_date if end is None else min(end, m.end_date)
|
||||
return end
|
||||
|
||||
|
||||
def build_cashflows(recurring: list[PlainRecurring], planned: list[PlainPlanned],
|
||||
loan_schedules: list[list[Installment]],
|
||||
loan_payouts: list[tuple[date, Decimal]],
|
||||
@@ -66,7 +80,7 @@ def build_cashflows(recurring: list[PlainRecurring], planned: list[PlainPlanned]
|
||||
if amount is None:
|
||||
continue
|
||||
for d in occurrences(r.rhythm, r.due_day, window_start, window_end,
|
||||
r.start_date, r.end_date):
|
||||
r.start_date, _effective_end(r, modifiers)):
|
||||
flows.append((d, amount))
|
||||
for p in planned:
|
||||
amount = _modified(p.amount, p.category_id, None, modifiers)
|
||||
|
||||
@@ -123,6 +123,21 @@ class ScenarioModifier(Base):
|
||||
target_id: Mapped[int] = mapped_column(Integer)
|
||||
kind: Mapped[str] = mapped_column(String(20))
|
||||
value: Mapped[Decimal] = mapped_column(MONEY, default=Decimal("0"))
|
||||
# Nur fuer kind="ende" gesetzt: Datum, an dem der Ziel-Posten innerhalb
|
||||
# dieses Szenarios endet (Ausbaustufe 5).
|
||||
end_date: Mapped[date | None] = mapped_column(Date, nullable=True)
|
||||
|
||||
|
||||
class ScenarioPlannedItem(Base):
|
||||
"""Einmalzahlung, die NUR in einem bestimmten Szenario zaehlt -
|
||||
unabhaengig von Scenario.include_planned (das steuert nur die globalen
|
||||
PlannedItems). Ausbaustufe 5."""
|
||||
__tablename__ = "scenario_planned_items"
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
scenario_id: Mapped[int] = mapped_column(ForeignKey("scenarios.id"), index=True)
|
||||
name: Mapped[str] = mapped_column(String(200))
|
||||
amount: Mapped[Decimal] = mapped_column(MONEY)
|
||||
due: Mapped[date] = mapped_column(Date)
|
||||
|
||||
|
||||
class ProjectionPoint(Base):
|
||||
|
||||
Reference in New Issue
Block a user