refactor: Tilgungsplan-Helfer schedule_for, Kredit-Laufzeit 1–1200 Monate (A14-T5)
- Neuer schedule_for() in app/engine/loans.py: vereinheitlicht Tilgungsplan-Erstellung - Laufzeit-Validierung in LoanIn und LoanPatch: 1–1200 Monate - Aufrufstellen aktualisiert (routers/planning.py, services/breakdown.py, services/projection_service.py, services/planning_chart.py) - Keine direkten loan_schedule(Decimal(...)) Aufrufe außerhalb engine/loans.py mehr - Tests für schedule_for und Laufzeit-Grenzen (1, 1200, sowie 0/-5/1201 Ablehnung) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,14 +5,13 @@ Modifikatoren) - dieselbe Datenbasis wie load_scenario_inputs(None).
|
||||
"""
|
||||
from dataclasses import dataclass
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.engine.breakdown import (BreakdownRow, Filters, PlainLoan, Summary,
|
||||
breakdown_rows, summarize)
|
||||
from app.engine.loans import loan_schedule
|
||||
from app.engine.loans import schedule_for
|
||||
from app.models.tables import Category, Scenario
|
||||
from app.reserved import SYSTEM_PREFIX
|
||||
from app.services.double_count import Konflikt, all_conflicts
|
||||
@@ -34,10 +33,7 @@ def compute_breakdown(session: Session, month: date, scenario: Scenario | None,
|
||||
filters: Filters) -> Breakdown:
|
||||
inputs = load_scenario_inputs(session, scenario)
|
||||
names = {c.id: c.name for c in session.execute(select(Category)).scalars()}
|
||||
loans = [PlainLoan(l.id, l.name,
|
||||
loan_schedule(Decimal(l.principal), Decimal(l.annual_rate_pct),
|
||||
l.term_months, l.payout_date, l.repayment_type))
|
||||
for l in inputs.loans]
|
||||
loans = [PlainLoan(l.id, l.name, schedule_for(l)) for l in inputs.loans]
|
||||
rows = breakdown_rows(month, inputs.recurring, inputs.planned, loans,
|
||||
inputs.modifiers, filters, category_names=names,
|
||||
loan_category=KREDITRATEN, uncategorized=UNKATEGORISIERT)
|
||||
|
||||
@@ -15,7 +15,7 @@ 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.loans import schedule_for
|
||||
from app.engine.recurrence import occurrences
|
||||
from app.models.tables import (Category, Loan, PlannedItem, PlanningMonthly,
|
||||
RecurringItem)
|
||||
@@ -46,9 +46,7 @@ def planning_monthly_totals(session: Session, today: date,
|
||||
if window_start <= p.due <= window_end:
|
||||
totals[(p.due.replace(day=1), category(p.category_id))] += Decimal(p.amount)
|
||||
for loan in session.execute(select(Loan)).scalars():
|
||||
for inst in loan_schedule(Decimal(loan.principal), Decimal(loan.annual_rate_pct),
|
||||
loan.term_months, loan.payout_date,
|
||||
loan.repayment_type):
|
||||
for inst in schedule_for(loan):
|
||||
if window_start <= inst.due <= window_end:
|
||||
# payment ist bereits negativ (Abfluss), wie in build_cashflows.
|
||||
totals[(inst.due.replace(day=1), KREDITRATEN)] += inst.payment
|
||||
|
||||
@@ -8,7 +8,7 @@ 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.loans import schedule_for
|
||||
from app.engine.projection import project
|
||||
from app.engine.scenario import (PlainModifier, PlainPlanned, PlainRecurring,
|
||||
build_cashflows)
|
||||
@@ -76,9 +76,7 @@ def run_projection(session: Session, scenario: Scenario, horizon_days: int,
|
||||
start_date: date) -> ProjectionResult:
|
||||
end = start_date + timedelta(days=horizon_days)
|
||||
inputs = load_scenario_inputs(session, scenario)
|
||||
schedules = [loan_schedule(Decimal(l.principal), Decimal(l.annual_rate_pct),
|
||||
l.term_months, l.payout_date, l.repayment_type)
|
||||
for l in inputs.loans]
|
||||
schedules = [schedule_for(l) for l in inputs.loans]
|
||||
payouts = [(l.payout_date, Decimal(l.principal)) for l in inputs.loans]
|
||||
flows = build_cashflows(inputs.recurring, inputs.planned, schedules, payouts,
|
||||
inputs.modifiers, start_date, end)
|
||||
|
||||
Reference in New Issue
Block a user