Asset movements between own accounts (buy/sell cash leg, reconciling a
receivable or payable) were typed 'expense'/'income', so they showed up in
the spending and income metrics. They are now typed 'transfer'/'settlement',
which the spending queries already exclude.
Backend:
- funded-buy/sell bank leg now typed 'transfer' (linked to investment account)
instead of 'expense' — buys no longer inflate spending
- FundedBuyRequest accepts type 'sell' in addition to 'buy'; the sell leg
credits proceeds (shares × price − fee) to the bank account
- /reconcile legs retyped to 'settlement' — collecting a receivable no longer
inflates income, paying off a payable no longer inflates spending
- RSU vest picks currency from PriceCache instead of hardcoding 'USD'
- create_account writes account + opening balance in one atomic commit
- create_split_expense links the expense row to the transfer legs so deleting
any one of the three rows deletes the whole group
- delete_transaction follows linked_transaction_id in both directions to
collect and delete the full linked group atomically
- splitwise-i-paid rejects partner_share >= total_amount (422)
- Migration h8c9d0e1f2a3 retypes historical rows the same way
Frontend:
- StockTradeFlow: sell now shows bank-account selector ('Credit proceeds…');
selector shown for both buy and sell, hidden only for rsu_vest
- Portfolio Add Trade form: same buy/sell bank-account selector added,
routes to createFundedBuy when an account is chosen
- Portfolio price-fetch handlers guard against stale ticker closures
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Frontend:
- Derive transaction currency from the selected account instead of
hardcoding EUR in SplitwiseIPaid/SplitwiseSettle/SplitwiseShePayed/IOwe
flows; add currency-mismatch guards and dynamic labels (IAmOwed too)
- Replace toISOString().split('T')[0] with local-time todayLocalISO()
helper in Dashboard, Portfolio, Spending and all QuickAdd flows —
UTC dates booked trades/vests/settles to yesterday before ~1-2 AM
- SplitExpenseFlow now calls the new atomic /transactions/split-expense
endpoint instead of two sequential requests that could partially fail
- Round partner_share to cents and reject 0% split client-side
- Portfolio: don't render accounts when holdings fetch failed
- client.ts: deleteAccount returns the Account, not void
Backend:
- New POST /api/transactions/split-expense writing expense + both
transfer legs in one commit
- delete_transaction legacy fallback now matches date/type and closest
id, so it can no longer delete a leg of a different transfer between
the same two accounts
- delete_stock_transaction reverts any grant vested through the deleted
transaction to pending, fixing the undeletable vested-grant deadlock
(422 on grant delete, FK 500 on transaction delete)
- FundedBuyRequest.type constrained to Literal['buy'] (422 instead of 500)
- calculate_net_worth logs a warning before falling back to EUR/USD=1.0
- Scheduler engine applies PRAGMA foreign_keys=ON and uses proper URL
parsing via db_config helpers
- fetch_all_prices counts only real updates; stale retry exception no
longer logged after a clean no-data result
Infra:
- nginx: Cache-Control no-cache on index.html so deploys aren't served
from stale browser caches
- api Dockerfile: COPY --chown=app:app
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019uL1qrmNyCP8Bmybns1qTQ