# Budget Fixed Costs & Subscriptions v1

Use this for FinanceManager Budget/Cashflow sprints that add recurring-payment planning: fixed costs, subscriptions, variable recurring expenses, dashboard warnings, and forecast integration.

## Scope boundaries

- No new bank/import source, OCR/camera, Telegram alerts, portfolio functions, tax forecast, Sankey/heatmap, or productive transaction auto-booking.
- Recurring payments are planning/forecast objects, not `budget_transactions` created automatically.
- Candidate detection for Fixkosten/Subscriptions must use **confirmed budget transactions only** (`budget_transactions` with `status='confirmed'`, expense/fee scope). Do not derive recurring-payment candidates from import/review candidates.
- Merchant/Alias/Rule Manager outputs may suggest future candidate metadata, but must not create productive transactions or activate recurring-payment records without Confirm.
- Keep all real amounts/IDs out of chat reports; report status/counts only.

## Data model pattern

- Add append-only migration for `budget_recurring_payments` when no suitable table exists.
- Money/tolerance fields that matter for precision are Decimal strings/TEXT; compute with Python `Decimal`, never float.
- Suggested fields: `recurring_id`, `name`, optional `merchant_id`, `category_id`, optional `account_id`, `expected_amount_text`, `currency`, `frequency`, `expected_day_of_month`, `expected_month`, `amount_tolerance_pct`, `date_tolerance_days`, `recurring_type`, `status`, `source`, `confidence`, `last_seen_date`, `next_expected_date`, `notes`, evidence JSON, timestamps.
- Valid `recurring_type`: `fixed_cost`, `subscription`, `variable_recurring`.
- Valid statuses: `candidate`, `active`, `paused`, `archived`, `rejected`. Avoid legacy `ignored` for recurring payments; user-facing rejection should map to `rejected`.

## Candidate detection

- Group confirmed expense/fee transactions by normalized merchant/payee/description plus category/account; infer monthly/quarterly/yearly/weekly cadence from dates.
- Do **not** scan open review/import candidates for recurring detection. Exclude candidate tables entirely: pending/ignored/duplicate/covered rows are not evidence for Fixkosten v1.
- Exclude or downgrade variable/one-off sources: Migros/Coop/Aldi/Lidl/groceries, Galaxus/Digitec, restaurants, fuel/tankstellen, one-off purchases, internal transfers, credit-card settlement, investment/True Wealth transfers.
- Classify with conservative heuristics: subscription terms/smaller recurring amounts → `subscription`; insurance/rent/tax/telecom/health/leasing or larger/quarterly/yearly → `fixed_cost`; groceries/fuel/restaurant-like recurrence → `variable_recurring` or excluded.
- Validate runtime dirty data defensively: candidate dates may be null or malformed due to imported workbook rows. Use safe date parsing and fall back to today only for open-candidate evidence; never crash the dashboard.

## Review and write boundary

- Candidate/active actions must be Preview → Confirm → Audit:
  - activate as fixed cost
  - activate as subscription
  - mark variable recurring
  - reject candidate (`rejected`)
  - edit candidate/active recurring object
  - pause active item (`paused`)
  - archive active item
  - manual create
- Preview returns monthly/yearly budget implications and warnings; Confirm writes `budget_recurring_payments` and audit event.
- Manual create requires name, expense category, expected amount, frequency, type, tolerances and optional note.

## Warnings

Implement dashboard-only warnings first, no push/Telegram:

- `missing`: no matching confirmed payment by expected date + tolerance.
- `due_soon`: next expected payment is inside the near-term window.
- `amount_changed`: observed amount differs beyond tolerance.
- `new_candidate`: candidate exists and awaits review.
- `inactive`: paused/subscription no longer active.
- `possible_duplicate_subscription`: similar active/candidate subscriptions.

## Budget and analytics integration

- Budget Planning per category should expose known recurring month/year, fixed-cost quote, and variable actual amount.
- Budgetstatus can inherit these fields from planning rows.
- Forecast can consider known future recurring amounts while leaving variable costs on actual average or budget logic.
- Data Explorer adds `recurring_type` filter: `fixed_cost`, `subscription`, `variable_recurring`, `one_off`, `unknown`.
- Analytics should expose fixed-cost quote, subscriptions month/year, top subscriptions, fixed costs by category, variable vs fixed.

## UI pattern

- Route: `Budget > Fixkosten & Abos`, canonical path `/planning/budget/fixed-costs`; keep `/planning/budget/recurring` as a redirect/alias if users or tests naturally try that name.
- API routes use `/api/budget/recurring`, `/api/budget/recurring/detect`, `/api/budget/recurring/manual/preview`, `/api/budget/recurring/manual/confirm`, `/api/budget/recurring/candidates/{id}/preview`, `/api/budget/recurring/candidates/{id}/confirm`, and `/api/budget/recurring/{id}/archive`. Do not invent `/api/budget/recurring/dashboard` during sanity checks.
- Sections: KPI cards, dashboard warnings, detected candidates, active recurring payments, manual create form.
- Desktop may use DataTable; mobile must use cards and hide technical IDs.
- Use PrimeVue/Aura components where already adopted; all visible writes must be real Preview/Confirm/Audit actions or disabled with reason.

## Verification

- RED tests for monthly subscription, yearly insurance, quarterly payment, grocery/fuel/transfer exclusions, candidate activation/ignore/edit/audit, next-date math, missing/amount-changed/duplicate warnings, Budgetstatus/DataExplorer/analytics integration, Git-safety.
- Full gate: Python compile, pytest, npm test, npm run build, source secret scan, frontend build secret scan, Git-safety, `git diff --check`, browser sanity for Fixed Costs & Abos, Budget Übersicht, Data Explorer and Effective Expenses, commit, push, remote-hash verification.

## Pitfalls

- Do not expose real detected candidate amounts in chat summaries even if visible in local UI.
- Do not create productive budget transaction rows for expected future payments.
- Do not call provider/import/OCR flows during recurring-payment detection.
- Do not allow malformed open-candidate dates to crash `/api/budget/recurring`; safe-parse if old candidate-derived evidence exists, but new detection should not read open candidates.
- If browser sanity on `/planning/budget/recurring` shows the Command Center/fallback, verify the canonical route `/planning/budget/fixed-costs` and add/keep a redirect alias. HTTP `200` alone is insufficient; snapshot text must show `Fixkosten & Subscriptions` and KPI/action labels.
- When hardening SQLite status constraints, remember that changing `CHECK(status IN (...))` may require a table rebuild/migration, not just service enum changes; update schema-version tests consistently.
