# PLAN-M7c: Feature expense ## 1. Goal Build `:feature:expense`: the full-screen expense editor (S-7) and the expense detail with its timeline (S-8), against the `:core:model` interfaces and `:core:fakes`. Every state of both screens is screenshot-tested in Swedish and English, light and dark. ## 2. Read first 1. `docs/ux/design-spec.md`: tokens, component inventory, and the sections for S-7, S-8. 2. `docs/ux/strings.csv` (keys for these screens) and `docs/ux/screens/S-7-*`, `docs/ux/screens/S-8-*`. 3. SPEC §13.2 Navigation model and §13.3 Screens (rows S-7, S-8 and the states paragraph); §9.11 Category suggestion (D-049). M12c must have landed (§11.2). After saving, the snackbar "Utgiften sparades" has no undo (S-7, D-052). 4. SPEC §14.2.4 UI contract: `ExpenseRepository`, `ExpenseDraft`, `ExpenseEditorOptions`, `ExpenseDetailState`, `TimelineLine`, `FieldChange`, `Share`, `SaveResult`, `FieldError`, `SplitMode`, `Account`, `Category`, `User`. 5. SPEC §4 FR-8 Expenses (AC-8.1 to AC-8.11); FR-12 AC-12.4, AC-12.5; §9.1 Splits; §9.6 (limits, amount input parsing); §9.10 (exact-mode edit); §14.5.3 error tokens. 6. SPEC §6.2 (module table), §9.8 Formatting, §9.9 Online-only actions, §11.2 (row M7c), §11.3 (note M7x), §14.7. 7. Files: `android/core/model/**`, `android/core/design/**` (read only), `android/core/fakes/**`, `android/build-logic/**`, `CLAUDE.md` (BUILD AND VERIFY: Screenshots; ARCHITECTURE RULES; ANTI-PATTERNS). ## 3. Already established - Features depend only on `:core:model`, `:core:design` and `:core:fakes`; `:core:fakes` only in test, screenshot-test and debug-preview configurations, never in a release build. Features never depend on `:data` or `:domain` (§6.2, §11.3, CLAUDE.md Layering). - Screens consume the `*State` classes through the repository interfaces of §14.2.4; no field may be added except through a contract change (§14.2.4, §14.8). - Fake scenarios go only in `android/core/fakes/src/main/kotlin/se/hagstedt/kvitt/fakes//` (§11.3, §14.7). - Strings live in the module's `src/main/res/values/strings.xml` and `values-sv/strings.xml` with keys from `docs/ux/strings.csv`; no string outside that file unless the report proposes it as a decision entry for the orchestrator (§13.4, §14.7, AC-19.1). - No hard-coded colours, sizes or strings; tokens and shared components come from `:core:design`, which features may not edit (CLAUDE.md, §14.7). Money and dates go through `MoneyFormatter` and `DateFormatter` (§9.8). - Online-only actions are disabled with "Needs connection" when offline (§9.9); a REST failure shows "Could not reach the server". - Feature convention (§6.2, D-017): one composable entry point per screen, taking a `hiltViewModel()`-provided ViewModel and its navigation as plain lambdas; the feature never references another feature or the `NavHost`; ViewModels get repositories through the `:core:model` interfaces. Navigation graph, deep links and Hilt bindings are in `:app` (M8). - M2b created this module's shell with one empty entry composable (D-004); this plan owns it and replaces it with the real entry points. - Keys are prefixed by the feature name; lint `MissingTranslation` is an error in this module (§6.2, §13.4, D-017). - The states each screen must have are the ones `docs/ux/design-spec.md` lists for it (§13.3). - Shared UI (D-019): the expense editor fields (S-7) come from `:core:design`, built by M6; never copy or re-implement it here. - Layout and visuals follow `docs/ux/design-spec.md`; TalkBack labels as it specifies; 48 dp targets, 200 % font scale reflow (§13.1). - No placeholder text in previews or screenshots; goldens are committed; open every changed PNG before committing (CLAUDE.md). - The add flow needs at most: open app, tap add, type amount, type description, tap save; account, category, payer and split are defaulted (§13.1, AC-8.6). - Editor is a full screen, not a bottom sheet (§13.2). Amount input accepts digits, one decimal separator (`,` or `.`), at most two decimals, no sign, converted to öre by string parsing, never floating point (§9.6). - Defaults: account = last used on this phone if active else oldest active; category for a new expense = the suggestion of §9.11 (D-049): the editor keeps a "picked by hand" flag (CS-2) and, while it is false, calls `ExpenseRepository.suggestCategory(accountId, description)` on every description and account change (CS-3), showing the result with no marker (CS-7); before any description it is `ExpenseEditorOptions.defaultCategoryFor` (last used in that account if active else `other`); editing never changes the category (CS-1); split mode `default`; payer = current user; date = today in the phone's zone (AC-8.3, AC-8.4, AC-8.6). Defaults come from `ExpenseEditorOptions`. - Split modes `default` ("Account default 60/40"), `ratio`, `exact` (type one share, the other is amount minus it, read-only; a share larger than the amount blocks saving inline), `full` (one bearer); changing account in `default` re-copies the ratio; live preview from `ExpenseRepository.preview` (AC-8.5, AC-8.7, AC-8.8, §13.3 S-7). - Validation errors come back as `SaveResult.Invalid` with §14.5.3 tokens, `required`, or `validation_failed` for a §9.6 limit without a domain token (lengths, dates) (§9.6, §14.2.4); deleting asks for confirmation (AC-8.9). - Category quick row (D-059, SPEC §14.2.4 "Changes in v0.9"): `ExpenseEditorOptions.frequentCategoryIds[accountId]`, read once on open and on an account change, filled up to five from the remaining active categories in `sort_order`; the selected category (suggested, defaulted or picked in "Alla") takes the fifth place when it is not among them. M12d must have landed (§11.2). When the suggestion returns null (`other` archived and no active last-used category), the editor shows no category until the user picks one, and saving is blocked as for a missing category (AC-8.6). - Design-spec open points (`docs/ux/design-spec.md` §6) carried here: tabular figures for amounts as M6 delivers them (M6 checks them on the S26); Material 3 Expressive components only as M6 provides them, with M6's stable fallbacks; the recurring editor S-19 (M7f) has no category suggestion, so nothing in this feature is shared with it beyond the `:core:design` editor fields. - Detail shows amount, description, category, account, date, payer, shares and mode, notes, "Recurring: