Bills soft-delete and are retained 30 days, but the only way back was the
transient "Undo" toast — dismiss it and a bill deleted an hour ago was
unrecoverable from the UI (even though the API and retention kept it).
- GET /api/bills/deleted lists soft-deleted bills still inside the recovery
window, newest first, with days_left (declared before /:id). User-scoped.
- BillsPage shows a "Recently deleted (N)" button when any exist, opening a
dialog to restore each one; restoring refreshes the active list too.
- The list fetch is non-blocking (never blanks the page); restore is
try/catch + toast; dialog has empty and per-row busy states.
Tests: tests/billsDeletedRoute.test.js (window filter, ordering, days_left,
money serialization, user isolation). Server 116 pass; client 46; build clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- updateCheckService: gate the external request on `update_check_enabled`
(default on); when off, no network call, returns { disabled: true }
- aboutAdmin: GET/PUT /update-check-setting (admin-only) to toggle it
- StatusPage: a Switch on the admin System Status card to enable/disable
- privacy.js: state that an admin can disable it (was called "optional" with
no actual opt-out)
- tests/updateCheckOptOut.test.js: proves no external fetch when disabled
- docs: archive QA-B16-01, B16 ✅
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- extract buildUserDbExportFile() from routes/export.js so the SQLite user-DB
export is testable (route behavior unchanged)
- tests/exportImportRoundTrip.test.js: export user A (bill/payment/override) →
import into fresh user B → assert all money survives exactly in cents. Confirms
the export(fromCents)/import(toCents) conversion is symmetric — no 100x drift —
and guards it from regressing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- routes/summary buildBankTracking: fetch unpaid candidates and filter by
resolveDueDate in JS so annual / off-month quarterly bills don't inflate the
SimpleFIN "unpaid this month" metric (completes the occurrence-gating family)
- add tests/summaryBankTracking.test.js (isolated route test)
- docs: archive QA-B5-02; Active Findings Log now empty (0 open)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- routes/summary: filter the expense list by resolveDueDate so annual and
off-month quarterly bills no longer inflate the monthly total / "monthly
result" — the Summary now agrees with the Tracker for the same month (QA-B5-01)
- add a Tracker<->Summary reconciliation guard in e2e/api.probe.spec.js
- docs: archive QA-B5-01; track QA-B5-02 (SimpleFIN unpaid_this_month residual)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>