diff --git a/HISTORY.md b/HISTORY.md index 08a4975..90c97a2 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -30,6 +30,8 @@ ### 🐛 Fixed +- **Mortgage and housing categories now auto-detected as debt** — `DEBT_LIKE_CLAUSES` in `routes/snowball.js` matched `%credit%`, `%loan%`, and `%debt%` category names but not `%mortgage%` or `%housing%`. A real user who created a bill under a "Mortgage" or "Housing" category would never see it on the Snowball page unless they manually toggled `snowball_include`. Demo data hid the bug because the seed bill has `snowball_include` pre-set. The frontend's `mortgageIncluded` warning in `SnowballPage.jsx` already checked for mortgage/housing in category and bill name — it just never fired because those bills were filtered out before reaching the page. Added both patterns to `DEBT_LIKE_CLAUSES`; the warning now works as intended, correctly flagging when a mortgage is present so users see the Ramsey Baby Step 2 note about excluding the house. + - **Imported payments now update debt balance** — Every payment creation path except spreadsheet import correctly computed `balance_delta` and updated `bills.current_balance`. Imported payments were permanently orphaned from debt tracking: the snowball page balance stayed wrong after an Excel import, delete/restore was broken (the restore path checks `balance_delta IS NULL` and silently skips the reversal), and any debt-related reporting was incorrect. Three paths were fixed. In `spreadsheetImportService.js` `createPaymentFromImport()`: added `computeBalanceDelta` import, fetches the bill fresh on every call (critical for sequential month imports so each payment sees the post-previous-payment balance), includes `balance_delta` in the INSERT, updates `bills.current_balance`, and sets `payment_source = 'import'` (was null). The `create_payment` action path in the same file had the identical gap and received the same treatment. `routes/matches.js` manual transaction confirm also had no `balance_delta` or `current_balance` update — fixed with the same `computeBalanceDelta` call inside the existing transaction block. - **Daily worker cycle range bugs for quarterly and annual bills** — `dailyWorker.js` had two bugs affecting non-monthly billing cycles. First, `getCycleRange(year, month)` was called without a bill argument, always producing the calendar-month range for payment lookups. A quarterly bill paid in January would be invisible to the autopay check in February and March because the worker only searched that calendar month — a payment that existed was treated as missing. Fixed by calling `getCycleRange(year, month, bill)` per-bill so quarterly bills look at their full 3-month window and annual bills look at the full year. Second, `buildTrackerRow()` returns `null` for bills whose cycle does not apply in the current month (quarterly/annual bills in non-due months), and the code immediately accessed `row.due_date` with no null check. JavaScript's `&&` short-circuit masked the crash for non-autopay bills, but any autopay-enabled quarterly or annual bill in a non-applicable month would throw `TypeError: Cannot read properties of null`. Fixed with an early `continue` when `getCycleRange` returns null and a defensive guard after `buildTrackerRow()`. The issue description incorrectly stated that `resolveDueDate()` and `getCycleRange()` ignore cycle types — both functions already handle quarterly, annual, biweekly, and weekly correctly; the tracker already filters non-applicable bills via `.filter(Boolean)`; no new scheduler was needed. diff --git a/client/components/BillModal.jsx b/client/components/BillModal.jsx index aa06a10..27ef6c2 100644 --- a/client/components/BillModal.jsx +++ b/client/components/BillModal.jsx @@ -46,7 +46,7 @@ const PAYMENT_METHODS = [ ]; const DEBT_KEYWORDS = ['credit', 'loan', 'mortgage', 'housing', 'debt']; -const SNOWBALL_KEYWORDS = ['credit', 'loan', 'debt']; +const SNOWBALL_KEYWORDS = ['credit', 'loan', 'debt', 'mortgage', 'housing']; const SUBSCRIPTION_TYPES = [ ['streaming', 'Streaming'], ['software', 'Software'], diff --git a/routes/snowball.js b/routes/snowball.js index b04da87..0c6435a 100644 --- a/routes/snowball.js +++ b/routes/snowball.js @@ -13,6 +13,8 @@ const DEBT_LIKE_CLAUSES = `( LOWER(c.name) LIKE '%credit%' OR LOWER(c.name) LIKE '%loan%' OR LOWER(c.name) LIKE '%debt%' + OR LOWER(c.name) LIKE '%mortgage%' + OR LOWER(c.name) LIKE '%housing%' ) ) )`;