* feat(goals): earmark a portion of an account toward a goal
Goals currently count each linked account's whole balance, so an account
shared across goals double-counts and one account can't fund several goals
in distinct slices. Add a per-account earmark — the "GoalBacking" the v1
model already foreshadowed (goal.rb).
- goal_accounts.allocated_amount (nullable). NULL = "dedicate the whole
balance" (the v1 default: no backfill, existing goals unchanged); a set
amount reserves a fixed slice.
- Goal#current_balance is now the single chokepoint computing each account's
backing under a family-wide shared pool: fixed earmarks take their slice,
an unallocated link takes the remainder, and when fixed earmarks exceed the
balance every slice is scaled down pro-rata so the goals' shares can never
sum past the account balance (no double-counting).
- Account#free_to_earmark / #goal_earmarked_total (mirror Budget's
available_to_allocate) back a soft, non-blocking over-allocation hint.
- GoalsController threads a goal[allocations] hash through create/update.
Phase 1 of the goals earmarking work; investment-backed goals follow.
* feat(goals): earmark UI on the goal form + backing-aware funding breakdown
- Goal form: a per-account "earmark amount" input (blank = whole balance)
next to each funding-account checkbox, prefilled from the saved
allocation on edit.
- Goal#account_backing exposes a single linked account's share so the
funding-accounts breakdown shows each account's earmarked contribution
and percent instead of its whole balance — keeping the show page
consistent with the (now allocation-aware) progress ring.
- English strings for the earmark controls and the "earmarked of balance"
breakdown line.
* fix(goals): address review on the earmark shared-pool math
- Overdrawn (<= 0 balance) accounts now back nothing on both the fixed and
whole-balance paths. The fixed path previously produced negative backing and
let a goal claim money the account doesn't hold.
- An archived goal reads its OWN earmark from its own goal_accounts instead of
the shared pool (which excludes archived goals), so it no longer mis-reports
the whole account balance for itself.
- goals#index injects one family-wide earmark pool into every card
(Goal.pooled_allocations_for) instead of querying once per goal (N+1), and
preloads goal_accounts.
- The projection chart scales its whole-account historical series by the
backing ratio so the saved line meets current_balance at "today" rather than
dropping off a cliff for earmarked goals.
- Honest comments: free_to_earmark no longer claims a form warning that doesn't
exist yet; pace documents its deliberate whole-account basis.
* fix(goals): widen the earmark input so the 'Whole balance' placeholder isn't clipped
* fix(goals): address review on #2490
- autosave: true on goal_accounts so earmark edits to already-linked accounts
persist through goal.save! (Rails only auto-saves newly built children, so
changing/clearing an existing earmark was silently dropped). + test.
- Reset the balance/progress memos on AASM transitions, not just the status
memos, so a same-instance render after complete!/archive! isn't stale. + test.
- backing_ratio is 0 (not 1) when the linked-account total is non-positive, so
the projection saved series ends at 0 to match the forced-zero current_balance.
- Localize the funding-row subtype label via goals.form.subtypes.*.
- Add the earmark strings to zh-CN (the maintained second locale; goals has no
ca locale, so Catalan keeps falling back to en like the rest of goals).
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>