Money
The money module is the largest single feature in Dawnlet and the only one gated as a whole. It's also where a few decisions get noticeably stricter — because being vaguely wrong about money is worse than being vaguely wrong about a todo.
§The shape
One month at a time, driven by a shared month stepper, with two views behind a segmented control:
There's no separate budgets segment — budgets live inside Overview, because a budget is only meaningful next to what you've actually spent. Splitting them would mean two screens that each tell half the story.
A transaction is income or expense, with an amount, a date, an optional category, an optional note and any number of tags — the same categories and tags every other module uses.
1Budgets
One budget per category per calendar month, enforced by a uniqueness constraint. Setting a grocery budget for March creates a March row; April is a separate row.
Underspending in March doesn't raise April's ceiling. Two reasons: rollover makes "am I over budget?" depend on history you can't see on the screen, and — more importantly — editing a limit would have to decide whether it rewrites the future. Here it can't: editing March's limit touches exactly March. Every month is independently true.
Budget status drives two notification kinds: crossing 80% of a limit, and going over it. Each fires at most once per budget per month, so a month spent over budget doesn't generate a daily scolding.
2Recurring transactions
A recurring transaction is income or expense on a schedule — reusing the same recurrence model todos use, including its editor screen.
Loading a month runs a generation pass: for each active recurring transaction, walk the days of the month and create a transaction for every date the schedule matches — unless that pairing already has one, skipped or not.
Three consequences fall out of that single rule:
- A skip is never regenerated. The skipped row still exists, so the generator sees the pairing is handled.
- A weekly schedule generates several rows in one month, without any special-casing of monthly-ness.
- Deactivating stops future generation but keeps history. A bill you've cancelled doesn't erase the year you paid it.
Editing a generated occurrence is intentionally restricted: instead of a full editor you get a read-only summary of the series, an amount override for that month alone, and a non-destructive skip in place of delete. The override exists because a real bill's amount varies; the restriction exists because editing an occurrence into something unrecognisable makes the series meaningless.
3The category breakdown
Two stacked bars — income by category, spending by category — each with a legend showing amount and percentage, sorted largest first.
A stacked bar rather than a pie chart, because comparing a 9% slice to an 11% slice by angle is a task nobody performs accurately. A single bar keeps the parts in a shared line and makes "rent is most of it" legible at a glance, which is the only question this chart is actually asked.
Uncategorised amounts appear as their own neutral-coloured band rather than being hidden, so the parts always sum to the total. A breakdown that doesn't add up to the number above it is worse than no breakdown.
4The Face ID lock
An optional setting requiring biometric authentication before the money module's contents are shown. Off by default, because a lock the user didn't ask for on a tab they open ten times a day is friction, not security.
Its state is mirrored on-device rather than read from the database on demand, for a specific reason covered next: the notification planner has to know about it while running in the background with no network.
5Redaction
Notifications are the hole in any in-app privacy lock. A locked money tab is pointless if the lock screen displays "Rent — $1,850 due today" to whoever picks up your phone.
So when the lock is on, amounts are removed from notification copy. "Rent — $1,850 due today" becomes "A recurring transaction is due today".
The redaction happens when the notification is composed — before it's written to the notification log and before that log syncs. Redacting at display time would mean the full amount was already sitting in the log, and the in-app lock has no meaning in a database row. The difference is invisible until it matters, and then it matters completely.
It's implemented as one shared helper every money-related notification passes through, rather than a conditional at each call site — because "we forgot one" is the only way this feature fails, and thirteen separate conditionals is thirteen chances to forget.
6Why the whole tab is gated
Every other module uses a free-tier cap: ten habits, ten todos, five tracked activities. Money is all-or-nothing — the tab is either fully available or shows a lock screen.
A cap needs a unit to count, and money doesn't have a sensible one. Ten transactions is useless after a week. Ten budgets is nearly unlimited. Capping by months of history means deleting the past, which is the one thing financial records are for. There's no cap here that's both meaningful and not hostile, so there isn't one.
The trade-off is honest in the other direction too: the money card on the dashboard and the money section in the day view are hidden entirely for free users, not shown as teasing locked previews. A permanent advert in the middle of your day, for a feature you've declined, is a bad way to treat someone.