- Add global WriteAccessGuard that blocks POST/PUT/PATCH/DELETE for viewer role
- Add @AllowViewer() decorator for endpoints viewers need (switch-org, intro-seen, AI recommendations)
- Add useIsReadOnly hook to auth store for frontend role checks
- Hide write UI (add/edit/delete/import buttons, inline editors) in all 13 data pages for viewers
- Disable inline NumberInputs on Budgets and Monthly Actuals pages for viewers
- Skip onboarding wizard for viewer role users
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Dashboard: Remove tenant name/role subtitle
- Cash Flow: Replace Operating/Reserve net cards with inflow vs outflow
breakdown showing In/Out amounts and signed net; replace Ending Cash
card with AI Financial Health status from saved recommendation
- Accounts: Auto-set first asset account per fund_type as primary on creation
- Investments: Add 5th summary card for projected annual interest earnings
- Sankey: Add Actuals/Budget/Forecast data source toggle and
All Funds/Operating/Reserve fund filter SegmentedControls with
backend support for budget-based and forecast (actuals+budget) queries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Feature 1 - How-To Intro Tour (react-joyride):
- 8-step guided walkthrough highlighting Dashboard, Accounts, Assessments,
Transactions, Budgets, Reports, and AI Investment Planning
- Runs automatically on first login, tracked via has_seen_intro flag on user
- Centralized step config in config/tourSteps.ts for easy text editing
- data-tour attributes on Sidebar nav items and Dashboard for targeting
Feature 2 - Tenant Onboarding Wizard:
- 3-step modal wizard: create operating account, assessment group + units,
import budget CSV
- Runs after tour completes, tracked via onboardingComplete in org settings JSONB
- Reuses existing API endpoints (POST /accounts, /assessment-groups, /units,
/budgets/:year/import)
Backend changes:
- Add has_seen_intro column to shared.users + migration
- Add PATCH /auth/intro-seen endpoint to mark tour complete
- Add PATCH /organizations/settings endpoint for org settings updates
- Include hasSeenIntro in login response, settings in switch-org response
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wrap account interest query in subquery to avoid SUM(SUM(...)) nesting
- Replace nonexistent interest_earned column with current_value - principal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix Accounts page: include investment accounts in Est. Monthly Interest calc,
add Fund column to investment table, split summary cards into Operating/Reserve
- Fix Cash Flow: ending balance now respects includeInvestments toggle
- Fix Budget Manager: separate operating/reserve income in summary cards
- Fix Projects: default sort by planned_date instead of name
- Add Vendors: last_negotiated date field with migration, CSV import/export
- New Quarterly Financial Report: budget vs actuals, over-budget flagging, YTD
- Enhance Dashboard: separate Operating/Reserve fund cards, expanded Quick Stats
with monthly interest, YTD interest earned, planned capital spend
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rate fetcher now scrapes CD, Money Market, and High Yield Savings rates
from Bankrate.com with pauses between fetches to avoid rate limiting
- Historical rate data is preserved (no longer deleted on each fetch)
- Database migration adds rate_type column and tenant ai_recommendations table
- Backend returns market rates grouped by type with latest-batch-only queries
- AI prompt now includes all three rate types for comprehensive analysis
- AI recommendations are saved per-tenant for retrieval on page load
- Frontend: "Market CD Rates" replaced with "Today's Market Rates" tabbed view
- Rates section is collapsible (expanded by default) to save screen space
- Saved recommendations load automatically with "Last Updated" timestamp
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enhancement 1 - Block suspended/archived org access:
- Add org status check in switchOrganization() (auth.service.ts)
- Filter suspended/archived orgs from login response (generateTokenResponse)
- Add org status guard with 60s cache in TenantMiddleware
- Frontend: filter orgs in SelectOrgPage, add 403 handler in api.ts
Enhancement 2 - Change tenant plan level:
- Add updatePlanLevel() to organizations.service.ts
- Add PUT /admin/organizations/:id/plan endpoint
- Frontend: clickable plan dropdown in Organizations table + confirmation modal
- Plan level Select in tenant detail drawer
Enhancement 3 - User impersonation:
- Add impersonateUser() to auth.service.ts with impersonatedBy JWT claim
- Add POST /admin/impersonate/:userId endpoint
- Frontend: Impersonate button in Users tab (disabled for admins)
- Impersonation state management in authStore (start/stop/persist)
- Orange impersonation banner in AppLayout header with stop button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TypeORM needs explicit type: 'varchar' for nullable string columns
to avoid reflecting the union type as Object.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Database: Add login_history, ai_recommendation_log tables; is_platform_owner
column on users; subscription fields on organizations (payment_date,
confirmation_number, renewal_date)
- Backend: New AdminAnalyticsService with platform metrics, tenant detail, and
health score calculations (0-100 based on activity, budget, transactions,
members, AI usage)
- Backend: Login/org-switch now records to login_history; AI recommendations
logged to ai_recommendation_log; platform owner protected from superadmin toggle
- Frontend: 4-tab admin panel (Dashboard, Organizations, Users, Tenant Health)
with tenant detail drawer, subscription management, health scoring visualization
- Platform owner account (admin@hoaledgeriq.com) auto-redirects to admin panel
- Seed data includes platform owner account and sample login history
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The AI was making incorrect liquidity warnings because it only saw
current cash positions and annual budget totals — it had no visibility
into the month-by-month cash flow forecast that includes:
- Special assessment collections (reserve fund income from homeowners)
- Monthly budget income/expense breakdown (not just annual totals)
- Investment maturity schedule (when CDs return cash)
- Capital project expense timing
Now the AI receives:
1. Full assessment schedule (regular + special, with frequency)
2. 12-month forward forecast with projected operating/reserve cash,
investment balances, and per-month income/expense drivers
3. Updated system prompt instructing the AI to use the forecast for
liquidity analysis rather than just current balances
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add toggle-able debug logging (AI_DEBUG env var) that logs prompts,
request metadata, raw responses, parsed output, and full error chains
- Replace Node.js native fetch() with https module for Docker Alpine
compatibility (fixes "fetch failed" error with large payloads)
- Reduce max_tokens from 16384 to 4096 (qwen3.5 doesn't need thinking
token budget)
- Strip <think> blocks from model responses
- Add AI_DEBUG to docker-compose.yml and .env.example
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
kimi-k2.5 is a thinking model that times out on complex prompts (>3min).
qwen3.5-397b-a17b responds in ~2s with clean JSON output.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Increase max_tokens from 4096 to 16384 to accommodate reasoning tokens
- Increase timeout from 90s to 180s for thinking model latency
- Add logging for response diagnostics (content length, reasoning, finish reason)
- Better error message when model exhausts tokens on reasoning
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add shared.cd_rates table for cross-tenant market data (CD rates from Bankrate)
- Create standalone Puppeteer scraper script (scripts/fetch-cd-rates.ts) for cron-based rate fetching
- Add investment-planning backend module with 3 endpoints: snapshot, cd-rates, recommendations
- AI service gathers tenant financial data (accounts, investments, budgets, projects, cash flow) and calls OpenAI-compatible API (NVIDIA endpoint) for structured investment recommendations
- Create InvestmentPlanningPage with summary cards, current investments table, market CD rates table, and AI recommendation accordion
- Add Investment Planning to sidebar under Planning menu
- Configure AI_API_URL, AI_API_KEY, AI_MODEL environment variables
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix monthly actuals showing same data for all months (SQL JOIN excluded
month filter from SUM — added je.id IS NOT NULL guard)
- Fix units displaying $0 assessment by reading from assessment group
instead of stale unit field; add special assessment column
- Replace proportional project funding with priority-based sequential
allocation — near-term items get fully funded first; add is_funding_locked
flag so users can manually lock a project's fund balance
- Remove post-creation opening balance UI (keep only initial balance on
account creation); remove redundant Fund filter dropdown from Accounts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CSV import/export for Units, Projects, and Vendors with match-on-name/number upsert
- Cash Flow report toggle for Cash Only vs Cash + Investments
- Per-account and bulk opening balance setting with as-of date
- Interest rate field on normal accounts with estimated monthly/annual interest display
- Mobile sidebar auto-close on navigation
- Shared CSV parsing/export utility extracted to frontend/src/utils/csv.ts
DB migration needed for existing tenants:
ALTER TABLE accounts ADD COLUMN IF NOT EXISTS interest_rate DECIMAL(6,4);
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove min={0} from NumberInput to allow negative actuals (refunds/corrections)
- Fix post() in journal-entries service: use id param directly instead of
RETURNING result which returns [rows, count] in TypeORM QueryRunner
- Handle negative amounts in saveActuals(): negative expense credits the
expense account, negative income debits the income account
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add spreadsheet-style Monthly Actuals page for entering monthly actuals
against budget with auto-generated journal entries and reconciliation flag.
Add file attachment support (PDF, images, spreadsheets) on journal entries
for receipts and invoices. Enhance Budget vs Actual report with month
filter dropdown. Add reconciled badge to Transactions page. Replace bcrypt
with bcryptjs to fix Docker cross-platform native binding issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move Settings from sidebar Admin section to User Profile dropdown menu
- Add User Preferences page (placeholder for future: dark mode, timezone,
notifications, feature visibility)
- Add Manage Members page for tenant admins to invite/manage board members:
- List all org members with roles, status, join date, last login
- Add new members (creates user account + org membership)
- Change member roles (president, treasurer, secretary, board member,
property manager, viewer)
- Remove members (soft-deactivate)
- Role-gated: only president, admin, treasurer can manage members
- Backend: new org member management endpoints on OrganizationsController
- GET /organizations/members
- POST /organizations/members
- PUT /organizations/members/:id/role
- DELETE /organizations/members/:id
- Bump version to 0.2.0 MVP_P2 (package.json + Settings page)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change account_number from INTEGER to VARCHAR(50) to support segmented codes
like 30-3001-0000 used by real HOA accounting systems. Budget CSV import now:
- Auto-creates income/expense accounts from CSV when they don't exist
- Infers account_type and fund_type from account number prefix conventions
- Parses currency-formatted values ($48,065.21, $(13,000.00), $-, etc.)
- Reports created accounts back to the user
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Show investment subtable under All accounts tab (was only under Operating/Reserve)
- Add "Withdraw from primary account" switch (on by default) when creating
investments; creates a journal entry to credit the primary asset account
and debit the equity offset, keeping the books balanced
- Prevent all account modals from closing on outside click to avoid data loss
- Replace login page text title with the SVG logo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The forecast was counting money twice: operating used asset accounts while
reserve used equity accounts, but both sides of the opening balance journal
entry represent the same funds. Changed all cash balance queries (current,
opening, and historical) to use asset accounts (debit - credit) for both
operating and reserve. Also fixed a LEFT JOIN bug where date filters on
journal_entries didn't prevent journal_entry_lines from being summed,
causing opening balances to include all entries regardless of date.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New feature: Cash Flow page under Financials showing stacked area chart of
operating/reserve cash and investment balances over time. Backend forecast
endpoint integrates assessment income schedules, budget expenses, capital
project costs, and investment maturities to project 24+ months forward.
Historical months show actual journal entry balances; future months are
projected. Includes Operating/Reserve/All fund filter, 12-month sliding
window navigation, forecast reference line, and monthly detail table.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Investment accounts now appear under their respective Operating/Reserve
tabs in the Accounts page, with a compact sub-table showing name, type,
institution, principal/value, rate, interest earned, and maturity info
- Investment values (current_value) are included in dashboard Total Cash KPI
- Reserve investment values are added to Reserve Fund Balance KPI and
project funded percentage calculations
- Year-end report reserve status now includes reserve investment values
- Tab counts updated to include investment accounts per fund type
- Summary cards show separate "asset (investments)" total for visibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Dashboard reserve fund KPI now uses reserve equity accounts (fund balance
position) instead of asset accounts, correctly showing the total reserve
fund balance regardless of how users categorize their reserve accounts
- Projects findAll() and findForPlanning() dynamically compute funded_percentage
and current_fund_balance from reserve equity account balances via CTE,
distributing the total reserve balance proportionally across projects
- Year-end summary reserve status now queries unified projects table instead
of deprecated reserve_components table
- Remove standalone Monthly Assessment field from Units form — assessment
amount is now inherited from the selected assessment group
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: multiple issues with balance computation across the system.
Accounts list:
- findAll() was doing SELECT * FROM accounts, returning the stale
denormalized `balance` column (always 0). Now computes balances from
journal entries using proper double-entry logic (debit-credit for
assets/expenses, credit-debit for liabilities/equity/income).
Dashboard KPIs:
- Total Cash filtered by name LIKE '%Cash%' which missed accounts not
named "Cash". Now queries ALL asset accounts regardless of name.
- Reserve Fund queried the legacy reserve_components.current_fund_balance
column. Now computes from journal entries on reserve asset accounts.
Opening balance journal entries:
- On blank tenants, equity offset accounts (3000/3100) don't exist, so
the balancing journal entry line was silently skipped, leaving entries
unbalanced. Now auto-creates Operating Fund Balance (3000) and Reserve
Fund Balance (3100) equity accounts when needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix account creation initial balance bug: the INSERT RETURNING result
was not being unwrapped correctly from TypeORM's array response, causing
account.id to be undefined and journal entry lines to have $0 amounts.
Now uses findOne() after insert for reliable ID retrieval.
- Add missing balancing equity entry line (debit/credit to account 3000/3100)
so opening balance journal entries are proper double-entry
- Fix account update to use findOne() instead of RETURNING * for consistent
response format
- Add investment account creation to Accounts page: account type dropdown now
shows a separator followed by investment types (CD, Money Market, Treasury,
Savings, Brokerage). Selecting an investment type expands the modal to show
investment-specific fields (institution, principal, interest rate, purchase
date, maturity date, account last 4, notes) and posts to /investment-accounts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Unify reserve_components + capital_projects into single projects model with
full CRUD backend and new Projects page frontend
- Rewrite Capital Planning to read from unified projects/planning endpoint;
add empty state directing users to Projects page when no planning items exist
- Add default designation to assessment groups with auto-set on first creation;
units now require an assessment group (pre-populated with default)
- Add primary account designation (one per fund type) and balance adjustment
via journal entries against equity offset accounts (3000/3100)
- Add computed investment fields (interest earned, maturity value, days remaining)
with PostgreSQL date arithmetic fix for DATE - DATE integer result
- Restructure sidebar: investments in Accounts tab, Year-End under Reports,
Planning section with Projects and Capital Planning
- Fix new tenant creation seeding unwanted default chart of accounts —
new tenants now start with a blank slate
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Admin panel: create tenants with org + first user, manage org status
(active/suspended/archived), contract number and plan level fields
- Units: delete with invoice check, assessment group dropdown binding
- Assessment groups: frequency field (monthly/quarterly/annual) with
income calculations normalized to monthly equivalents
- Sidebar: grouped nav sections (Financials, Assessments, Transactions,
Planning, Reports, Admin), renamed Chart of Accounts to Accounts
- Header: replaced text with SVG logo
- Capital projects: Kanban as default view, table-only PDF export,
Future category (beyond 5-year plan)
- Auth: block login for suspended/archived organizations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add hierarchical roles: SuperUser Admin (is_superadmin flag), Tenant Admin,
Tenant User with separate /admin route and admin panel
- Add Assessment Groups module for property type-based assessment rates
(SFHs, Condos, Estate Lots with different regular/special rates)
- Enhance Chart of Accounts: initial balance on create (with journal entry),
archive/restore accounts, edit all fields including account number & fund type
- Add Budget CSV import with downloadable template and account mapping
- Add Capital Projects Kanban board with drag-and-drop between year columns,
table/kanban view toggle, and PDF export via browser print
- Update seed data with assessment groups, second test user, superadmin flag
- Create repeatable reseed.sh script for clean database population
- Fix AgingReportPage Mantine v7 Table prop compatibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rename app from "HOA Financial Platform" to "HOA LedgerIQ" across all
frontend pages, backend API docs, package.json files, and seed data
- Add Cash Flow Statement report (GET /reports/cash-flow) with operating
and reserve fund activity breakdown, beginning/ending cash balances
- Add Aging Report (GET /reports/aging) with per-unit aging buckets
(current, 1-30, 31-60, 61-90, 90+ days), expandable invoice details
- Add Year-End Package (GET /reports/year-end) with income statement
summary, collection stats, 1099-NEC vendor report, reserve fund status
- Add Settings page showing org info, user profile, and system details
- Replace all PlaceholderPage references with real implementations
- Bump auth store version to 3 for localStorage migration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>