"""Leichtgewichtige Testobjekte für die Recurrence-Engine – bewusst ohne Datenbank.""" from dataclasses import dataclass, field from datetime import date from decimal import Decimal from app.models.enums import BusinessDayShift, EntryKind, OccurrenceStatus @dataclass class FakeAmountVersion: """Preisversion, gültig ab `valid_from`.""" amount: Decimal valid_from: date note: str | None = None @dataclass class FakeOccurrence: """Materialisierte Fälligkeit, die das virtuelle Ergebnis überlagert.""" occurrence_date: date status: OccurrenceStatus = OccurrenceStatus.PLANNED actual_amount: Decimal | None = None actual_date: date | None = None account_id: int | None = None note: str | None = None id: int | None = 1 @dataclass class FakeRecurrence: """Erfüllt `RecurrenceLike` mit denselben Vorgabewerten wie das ORM-Modell.""" rrule: str dtstart: date amount: Decimal = Decimal("100.00") kind: EntryKind = EntryKind.EXPENSE id: int | None = 1 is_variable: bool = False until: date | None = None business_day_shift: BusinessDayShift = BusinessDayShift.NONE holiday_region: str = "DE-NW" installments_total: int | None = None principal_amount: Decimal | None = None contract_start: date | None = None contract_min_term_months: int | None = None contract_notice_period_days: int | None = None contract_auto_renew_months: int | None = None contract_cancelled_at: date | None = None reserve_enabled: bool = False account_id: int | None = 1 amount_versions: list[FakeAmountVersion] = field(default_factory=list) def d(value: str) -> date: """Kurzschreibweise für ISO-Datumsangaben in Tests.""" return date.fromisoformat(value) def euro(value: str) -> Decimal: return Decimal(value)