"""Nachweis, dass die Engine unmittelbar auf den ORM-Objekten arbeitet. Die Objekte werden nur im Speicher gebaut – es wird keine Session benötigt. """ from datetime import date from decimal import Decimal from app.models import AmountVersion, Occurrence, Recurrence from app.models.enums import BusinessDayShift, EntryKind, OccurrenceStatus from app.services.recurrence import expand, monthly_reserve, notice_deadline def test_engine_arbeitet_direkt_auf_dem_ormmodell() -> None: recurrence = Recurrence( id=7, kind=EntryKind.EXPENSE, title="Netflix", category_id=1, account_id=2, amount=Decimal("13.99"), is_variable=False, currency="EUR", rrule="FREQ=MONTHLY;BYMONTHDAY=1", dtstart=date(2026, 1, 1), business_day_shift=BusinessDayShift.NEXT, holiday_region="DE-NW", reserve_enabled=False, is_active=True, ) recurrence.amount_versions = [ AmountVersion(amount=Decimal("9.99"), valid_from=date(2026, 1, 1)), AmountVersion(amount=Decimal("13.99"), valid_from=date(2026, 4, 1)), ] overlay = Occurrence( id=99, recurrence_id=7, occurrence_date=date(2026, 2, 1), status=OccurrenceStatus.CONFIRMED, planned_amount=Decimal("9.99"), actual_amount=Decimal("9.99"), actual_date=date(2026, 2, 2), ) result = expand(recurrence, date(2026, 1, 1), date(2026, 5, 31), occurrences=[overlay]) assert [item.nominal_date.month for item in result] == [1, 2, 3, 4, 5] assert result[0].recurrence_id == 7 assert result[0].account_id == 2 assert result[0].due_date == date(2026, 1, 2) # 01.01. ist Feiertag assert [item.amount for item in result] == [ Decimal("9.99"), Decimal("9.99"), Decimal("9.99"), Decimal("13.99"), Decimal("13.99"), ] assert result[1].status is OccurrenceStatus.CONFIRMED assert result[1].occurrence_id == 99 def test_vertrags_und_ruecklagenlogik_auf_dem_ormmodell() -> None: recurrence = Recurrence( kind=EntryKind.EXPENSE, title="Kfz-Versicherung", category_id=1, account_id=1, amount=Decimal("612.00"), is_variable=False, currency="EUR", rrule="FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=15", dtstart=date(2026, 1, 15), business_day_shift=BusinessDayShift.NEXT, holiday_region="DE-NW", contract_start=date(2026, 1, 1), contract_min_term_months=12, contract_notice_period_days=30, contract_auto_renew_months=12, reserve_enabled=True, is_active=True, ) assert monthly_reserve(recurrence, as_of=date(2026, 1, 1)) == Decimal("51.00") assert notice_deadline(recurrence, as_of=date(2026, 6, 1)) == date(2026, 12, 1)