# Mobile Navigation, Income Planning & Bank Income Recognition Lessons

Use this reference when working on the FinanceManager Vue mobile dashboard, especially mobile/iPhone navigation, income-planning workflows, and bank CSV income recognition.

## Durable lessons

### Mobile navigation on iPhone/Tailscale

When a mobile route works in desktop/DOM tests but not on a real iPhone, do not stop at `RouterLink` unit tests. Prefer a boring native-link fallback for critical routes:

- primary mobile bottom-nav items should be native `<a href="...">` links for one-tap iOS reliability;
- avoid `router.push`, `preventDefault`, or custom touch handlers for primary navigation unless strictly required;
- verify `href`, no `disabled`, `pointer-events:auto`, sufficient touch target height (`min-h-11`/`min-h-12`), and high fixed-nav z-index;
- keep desktop sidebar behavior separate and unchanged.

If Bottom Nav or a compact `Mehr` panel remains unreliable on iPhone, add a separate **fullscreen mobile menu fallback**:

- visible `Menü` button on mobile;
- fixed fullscreen drawer/overlay with very high z-index (e.g. `z-[100]`);
- all important destinations as plain large `<a href="...">` links;
- no `RouterLink` requirement, no disabled states, no click-handler navigation;
- include at least: `/`, `/planning/budget`, `/planning/budget/expenses/actual`, `/planning/budget/expenses/review`, `/planning/budget/income`, `/crypto`, `/portfolio`, `/equity`, `/wallets`, `/reports`, `/planning/budget/categories`.

For Tailscale testing, ensure both sides are reachable from the phone:

- frontend on `0.0.0.0:5173`;
- backend on `0.0.0.0:8000`, not only `127.0.0.1`;
- `VITE_API_BASE_URL` must point to the backend origin, e.g. `http://<tailscale-ip-or-dns>:8000`, not the frontend port.

If no true iPhone automation is available, report precisely what was verified: Tailscale HTTP status for routes/API, browser console, DOM link attributes, and any browser/API Preview→Confirm smoke tests. Do not claim physical iPhone verification without it.

### Income planning data model

Keep a clear domain split:

1. planned income = household budget assumptions;
2. effective income / `Geld Eingang` = confirmed real transactions;
3. plan vs actual = comparison only, never importing or mutating data on render.

For planned income calculations:

- monthly income: monthly value = input, annual value = input × 12, monthly average = input;
- annual/one-time/irregular income: annual value = input, monthly average = annual / 12;
- 13th salary default due month: December;
- bonus/gratification default due month: April when the business case says so;
- templates should be amount-empty unless the user explicitly provides values.

Income templates must be idempotent and checked against income categories only. Do not let an expense-side plan with the same name, e.g. a solar reimbursement expense/reference entry, block creation of the corresponding income template.

### Mobile income editing workflow

For iPhone usability, prefer a mobile bottom-sheet/side-panel edit flow rather than inline controls buried in a table:

1. select income row/card;
2. open Planwert bearbeiten sheet;
3. enter monthly and/or annual value;
4. Preview shows name, type, monthly value, annual value, monthly average, due month;
5. Confirm persists;
6. Audit ID exists;
7. reload page/data and verify KPI/card/table update.

For smoke tests that intentionally write runtime data, create a temporary income entry via Preview→Confirm, run update Preview→Confirm, then archive it via the audited archive endpoint/service and verify no active temporary entry remains. Never commit the runtime DB.

## Bank CSV income recognition after import

When AKB/Raiffeisen bank CSVs have already been imported as candidates and income count is zero or suspiciously low, **do not reimport first**. Reclassify existing open candidates after a verified runtime backup:

1. Count existing `akb_bank` / `raiffeisen_bank` / `bank_csv` candidates by active budget year.
2. Exclude confirmed/ignored/covered/superseded/reference/archive statuses from mutation.
3. Re-score only candidate metadata: `classification`, `status`, `requires_review`, `review_reason`, `confidence`, `proposed_category_id`, `notes`.
4. Commit an audit event for the reclassification pass.
5. Do **not** create productive `budget_transactions` during reclassification.
6. Report aggregate counts only; never list real amounts in chat.

Positive income signals include:

- `Lohn`, `Salary`, `Gehalt`, `Payroll`, `Salär`;
- employer/source terms such as `ERNE`, `Musikschule`, `Gasser`, `Gasser Bauunternehmen`;
- `Bonus`, `Gratifikation`;
- `Rückvergütung`, `Rückerstattung`, `Erstattung`, `Solarstrom`, `Vergütung`;
- generic positive external payment only as `possible_income`/review, not auto-confirm.

Negative/non-income guards include:

- own-account transfers: AKB ↔ Raiffeisen, Umbuchung, Übertrag, eigenes Konto, Kontoausgleich;
- credit-card settlement: Kreditkarte, VISA, Kartenabrechnung;
- investment transfers: True Wealth, Depot, Investment, Wertschrift, Sparen;
- user-owned person-to-person transfers where known to be internal.

Priority rules:

1. Strong employer/payroll evidence may classify as `income_candidate` even if a generic transfer word appears.
2. Own-account/credit-card/investment guards beat generic positive cash-in evidence.
3. Unclear positive cash-ins become `needs_review` / `possible_income`, never confirmed automatically.

## Review UI for money-in candidates

For Budget Import Review, add tabs/workflows that make income recognition reviewable rather than hidden:

- `Geld Eingang` / money-in overview;
- `Einkommen erkannt` (`income_candidate`);
- `Mögliche Einkommen` (`possible_income`);
- `Interne Transfers`;
- `Nicht Einkommen / Transfer`;
- `Kreditkartenausgleich`;
- `True Wealth / Investment Transfers`.

Candidate confirm should create an income transaction only after explicit user action:

- `budget_transactions.transaction_type = 'income'`;
- `status = 'confirmed'`;
- account/category set;
- original amount/currency and CHF/FX fields preserved as Decimal text where applicable;
- `source_type = 'import_candidate'` and `source_candidate_id` set;
- candidate status set confirmed with `confirmed_transaction_id`;
- audit event written.

Transfers, credit-card settlement, and investment transfers must stay out of both income and expense totals.

## `Geld Eingang` table and manual actual income

On the Einkünfte page, show a separate `Geld Eingang` section for confirmed actual income transactions. It should not be confused with planned income templates.

Minimum useful version:

- confirmed `transaction_type='income'` rows only;
- date grouping or sortable date column;
- account, counterparty/description, income category, amount/currency/CHF value, source/origin;
- filters for year/month/account/category/source/search;
- summary cards for current month, year-to-date, forecast/plan year, and deviation from planned income;
- no import/reclassification side effects on render.

Manual actual income entry belongs here, not in planned income:

- account, date, source/description, category, amount, currency, note;
- Preview → Confirm → Audit;
- after Confirm, reload `Geld Eingang`, Effective Income, Budget Overview, and Plan vs Ist.

Avoid implementing risky reversal/storno/category-change shortcuts unless the audit path is clear. If not complete, state the limitation rather than shipping fake buttons.
