From 3745ef79b718ea5bf0ba202aa7fa3512c53b792c Mon Sep 17 00:00:00 2001 From: null Date: Thu, 4 Jun 2026 02:48:32 -0500 Subject: [PATCH] docs: update HISTORY.md with bank tracking, merchant rules, and UI changes --- HISTORY.md | 14 ++++++++++++++ routes/summary.js | 5 +++++ services/trackerService.js | 5 +++++ 3 files changed, 24 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index e92e468..4f9974f 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -30,6 +30,20 @@ - **Bank tracking pending deduction corrected — no double-counting** — The pending payments window was subtracting all recent payments regardless of source, including bank-synced ones (`payment_source = 'provider_sync'`). Since the live bank balance already reflects those outgoing transactions, subtracting them again produced a balance ~$2k lower than reality. Fixed in both `trackerService.js` and `summary.js` by adding `AND (p.payment_source IS NULL OR p.payment_source != 'provider_sync')` to the pending query. Only manually-entered payments and transaction-matched payments are now counted as pending — those are payments the user recorded in the tracker before the bank has seen them. The `pending_cleared` badge on tracker rows was given the same fix. Additionally, `b.name` was missing from the `SELECT` in `getOverdueCount`, causing the tooltip to show `null` names — corrected. +- **Bank tracking error handling hardened** — `buildBankTracking()` in `trackerService.js` and `buildBankTrackingSummary()` in `summary.js` had no try/catch. A DB lock, schema inconsistency, or bad account state would throw an unhandled exception and crash the entire tracker or summary API response for the user. Both functions are now wrapped: `buildBankTracking` returns `{ enabled: false }` on error (tracker falls back to manual starting amounts gracefully), and `buildBankTrackingSummary` returns `null` (summary page omits bank data but still loads). Console error logged in both cases for debugging. + +- **Merchant rule word-boundary matching — fixes false positives** — The merchant matching logic used simple substring `.includes()` which caused "suno" (Suno AI music) to match "sunoco" (gas station) because "sunoco" contains "suno" as a substring. All four matching sites replaced with a word-boundary regex check: `(^|\s)rule(\s|$)` must match within the transaction string. Now "suno" does not match "sunoco" but still correctly matches "suno inc" and similar. DB migration `v0.83` adds `auto_attribute_late` column to `bill_merchant_rules`. + +- **Merchant rule: "Auto-fix month crossing" toggle** — Some bills consistently post to the bank 1–5 days after month end (e.g. AT&T due May 29, posts June 1). Previously this required a manual prompt every cycle. Each merchant rule now has an "Auto-fix" toggle in the Bill Modal Bank Matching Rules section. When on, payments that cross a month boundary are automatically moved to the last day of the prior month on sync — no popup, no manual intervention. The flag is stored as `auto_attribute_late` in `bill_merchant_rules` (migration v0.83) and persists. A `PATCH /api/bills/:id/merchant-rules/:ruleId/auto-attribute` endpoint controls it. + +- **Historical payment import dialog** — When a merchant rule is added in the Bill Modal, a dialog now opens automatically asking how to handle past transactions: "Import all N payments" (bank as truth), "Choose which ones" (checkbox list showing each transaction's date, amount, and current match status), or "Skip — future only". The "choose" view shows already-handled transactions dimmed at the bottom for context. Backend: `GET /api/bills/:id/merchant-rules/candidates` returns all matching transactions regardless of current match status; `POST /api/bills/:id/merchant-rules/import-historical` imports a selected list. + +- **Bank tracking status bar and card on TrackerPage** — When SimpleFIN bank tracking is enabled, the TrackerPage now shows: (B) a slim colored status bar between the header and the filter panel — pulsing live indicator, account name, balance, pending amount, and projected figure color-coded green/red; (C) the Starting summary card is replaced by a bank-styled card with an emerald top bar, account name label, `Landmark` icon, pulsing "Live" badge, effective balance as the main number, and projected-after-bills in the hint line. Number sizes unchanged from other summary cards. + +- **`refetch is not defined` in BillModal fixed** — Two `refetch?.()` calls were added to `BillModal` during an earlier session but `refetch` is not a prop or variable in that component's scope. JavaScript throws `ReferenceError: refetch is not defined` before optional chaining can evaluate. Both calls removed — `loadLinkedTransactions?.()` is sufficient since the parent's `onSave` callback handles any full tracker refresh. + +- **BillMerchantRules suggestion list now uses inline rendering** — The suggestions dropdown previously used `position: absolute` which was clipped by BillModal's `overflow-y-auto` container. Clicks on suggestions appeared to land on the scroll container behind them. Replaced with inline block rendering (no absolute/fixed positioning) so suggestions are part of the normal document flow and always clickable. `onMouseDown + e.preventDefault()` keeps the input focused during selection. + - **Bank tracking: projected month-end balance added** — The CalendarPage Monthly Money Map (bank mode) now shows a dedicated "Projected Month-End Balance" row below the four metrics. It displays the full equation inline — `$5,461 bank − $1,737 pending − $3,696 remaining bills` — so the source of the number is always visible. The row uses a green/red border depending on whether the projection is positive or negative. The TrackerPage Starting card hint also updated from `account_name · live balance` to `account_name · projected $X after bills` so the projection is visible without navigating to Calendar. - **Bank tracking settings and account picker fixed** — The `loadBankTracking` function in `BankSyncSection` called `api.getSettings()` but the API method is `api.settings()`. The wrong name threw `TypeError: api.getSettings is not a function`, silently caught by the outer `catch {}`, so neither the toggle state nor the account list ever loaded. Renamed to `api.settings()`. All 19 financial accounts now appear in the picker and the enabled/account/pending-days settings persist correctly across page loads. diff --git a/routes/summary.js b/routes/summary.js index 0a38912..01257b6 100644 --- a/routes/summary.js +++ b/routes/summary.js @@ -10,6 +10,7 @@ const DEFAULT_PENDING_DAYS = 3; // Build bank tracking summary when the user has enabled SimpleFIN bank tracking. // Returns null when disabled, the account isn't found, or bank sync isn't set up. function buildBankTrackingSummary(db, userId, year, month) { + try { const settings = getUserSettings(userId); if (settings.bank_tracking_enabled !== 'true') return null; @@ -88,6 +89,10 @@ function buildBankTrackingSummary(db, userId, year, month) { unpaid_this_month: unpaidDollars, remaining: money(effectiveDollars - unpaidDollars), }; + } catch (err) { + console.error('[buildBankTrackingSummary] Error:', err.message); + return null; + } } function parseYearMonth(source) { diff --git a/services/trackerService.js b/services/trackerService.js index 701ed9a..3cb502d 100644 --- a/services/trackerService.js +++ b/services/trackerService.js @@ -9,6 +9,7 @@ const { computeAmountSuggestion } = require('./amountSuggestionService'); const DEFAULT_PENDING_DAYS = 3; function buildBankTracking(db, userId, year, month) { + try { const settings = getUserSettings(userId); if (settings.bank_tracking_enabled !== 'true') return { enabled: false }; @@ -72,6 +73,10 @@ function buildBankTracking(db, userId, year, month) { remaining: roundMoney(effective - unpaid), last_updated: account.updated_at, }; + } catch (err) { + console.error('[buildBankTracking] Error computing bank tracking data:', err.message); + return { enabled: false }; + } } function validateTrackerMonth(query = {}, now = new Date()) {