SPEC 009 — Finance Dashboard
Field Value Status DRAFT Priority P1 — Core Product Backend External: HH Finance Agent API (separate service, not equa-server) API equa-web/src/modules/hh-finance/services/api.ts (client-side API layer)Frontend equa-web/src/modules/hh-finance/
1. Feature Purpose
The Finance Dashboard provides a real-time financial overview for organizations, displaying key metrics (assets, liabilities, equity), accounts receivable/payable aging, bank account balances, open invoices and bills, P&L reports, and scheduled CFO automation jobs. Unlike other Equa modules, this feature connects to an external HH Finance Agent REST API rather than the core equa-server backend. The finance agent syncs with QuickBooks Online and provides aggregated financial data.
2. Current State (Verified)
2.1 Architecture
The finance dashboard is a frontend-only module within equa-web that communicates with an external microservice:
No equa-server entities — The finance agent is a standalone service; no tables in schema.ts
No authentication — API calls from the frontend carry no Authorization header, API key, or token
No route-level permission gating — Accessible to any authenticated organization member
2.2 Frontend
Component Path Module root equa-web/src/modules/hh-finance/Components equa-web/src/modules/hh-finance/components/React hooks equa-web/src/modules/hh-finance/hooks/useFinanceData.tsAPI service equa-web/src/modules/hh-finance/services/api.tsType definitions equa-web/src/modules/hh-finance/types/index.tsFormatters equa-web/src/modules/hh-finance/utils/formatters.ts
2.3 External API Configuration
const API_BASE_URL = process . env . REACT_APP_HH_FINANCE_API_URL || 'http://localhost:19792' ;
The base URL defaults to http://localhost:19792 and must be set via environment variable for deployed environments.
3. Data Model (TypeScript Interfaces)
Since there are no database entities, the data model is defined entirely as TypeScript interfaces in types/index.ts.
3.1 Core Financial Types
FinancialDashboard:
Field Type Description totalAssets number Sum of all asset accounts totalLiabilities number Sum of all liability accounts totalEquity number Assets minus liabilities netIncome number optional, P&L net income cashOnHand number optional, liquid cash balance
KeyMetrics:
Field Type Description currentRatio number Current assets / current liabilities quickRatio number (Current assets - inventory) / current liabilities workingCapital number Current assets - current liabilities daysReceivable number Average days to collect receivables daysPayable number Average days to pay payables
3.2 Account Types
BankAccount:
Field Type accountId string accountName string accountType string balance number
AccountSummary:
Field Type id string name string type string subType string (optional) balance number active boolean
3.3 Customer and Vendor Types
CustomerSummary / VendorSummary (same structure):
Field Type id string displayName string companyName string (optional) email string (optional) phone string (optional) balance number active boolean
3.4 Transaction Types
InvoiceSummary:
Field Type Description id string Invoice ID docNumber string (optional) Invoice number date string Issue date dueDate string (optional) Payment due date customerName string Customer display name customerId string Customer ID total number Invoice total amount balance number Outstanding balance status InvoiceStatus 'paid' | 'partial' | 'open' | 'overdue'
BillSummary:
Field Type Description id string Bill ID docNumber string (optional) Bill number date string Issue date dueDate string (optional) Payment due date vendorName string Vendor display name vendorId string Vendor ID total number Bill total amount balance number Outstanding balance status BillStatus 'paid' | 'partial' | 'open' | 'overdue'
3.5 Aging
AgingBucket:
Field Type Description current number Not yet due 1-30 number 1–30 days past due 31-60 number 31–60 days past due 61-90 number 61–90 days past due 90+ number Over 90 days past due total number Sum of all buckets
3.6 Dashboard Summary (Composite)
DashboardSummary:
Field Type financials FinancialDashboard bankAccounts BankAccount[] receivables { aging: AgingBucket; openInvoices: InvoiceSummary[] }payables { aging: AgingBucket; openBills: BillSummary[] }generatedAt string (ISO timestamp)
3.7 Scheduler Types
ScheduledJob:
Field Type id string name string description string skillId string schedule { cronExpression: string; enabled: boolean }
JobExecution:
Field Type jobId string executionId string startTime string endTime string (optional) status 'pending' | 'running' | 'completed' | 'failed'result unknown (optional) error string (optional)
SchedulerStatus:
Field Type running boolean jobCount number recentExecutions JobExecution[]
HealthStatus:
Field Type status string service string version string timestamp string
4. API Endpoints (External HH Finance Agent)
All endpoints are on the external HH Finance Agent service (33 total).
Health
Method Path Response Type GET /healthHealthStatus
Dashboard
Method Path Response Type GET /api/dashboard{ dashboard: FinancialDashboard; metrics: KeyMetrics }GET /api/dashboard/summaryDashboardSummary
Accounts
Method Path Response Type GET /api/accounts{ accounts: AccountSummary[] }GET /api/accounts/bank{ accounts: BankAccount[] }
Customers / Accounts Receivable
Method Path Response Type GET /api/customers{ customers: CustomerSummary[] }GET /api/customers/with-balance{ customers: CustomerSummary[] }GET /api/ar/total{ totalAccountsReceivable: number }GET /api/ar/aging{ aging: AgingBucket }
Vendors / Accounts Payable
Method Path Response Type GET /api/vendors{ vendors: VendorSummary[] }GET /api/vendors/with-balance{ vendors: VendorSummary[] }GET /api/ap/total{ totalAccountsPayable: number }GET /api/ap/aging{ aging: AgingBucket }
Invoices
Method Path Response Type GET /api/invoices?limit={n}{ invoices: InvoiceSummary[] }GET /api/invoices/open{ invoices: InvoiceSummary[] }GET /api/invoices/overdue{ invoices: InvoiceSummary[] }
Bills
Method Path Response Type GET /api/bills?limit={n}{ bills: BillSummary[] }GET /api/bills/open{ bills: BillSummary[] }GET /api/bills/overdue{ bills: BillSummary[] }GET /api/bills/due-this-week{ bills: BillSummary[] }
Reports
Method Path Response Type GET /api/reports/pnl?startDate={s}&endDate={e}{ report: unknown; period: { startDate; endDate } }GET /api/reports/balance-sheet{ report: unknown }GET /api/reports/aging{ report: unknown }GET /api/reports/top-expenses?limit={n}{ expenses: unknown[] }GET /api/reports/top-income?limit={n}{ income: unknown[] }
Report endpoints return unknown types — the response schemas are not fully typed in the frontend. This is a gap that should be addressed when report rendering is implemented.
Scheduler
Method Path Response Type GET /api/scheduler/statusSchedulerStatus GET /api/scheduler/jobs{ jobs: ScheduledJob[] }POST /api/scheduler/jobs/{jobId}/run{ execution: JobExecution }PUT /api/scheduler/jobs/{jobId}/toggle{ success: boolean; enabled: boolean }POST /api/scheduler/start{ started: boolean }POST /api/scheduler/stop{ stopped: boolean }
Method Path Response Type GET /api/tools{ tools: unknown[] }POST /api/tools/{toolName}/execute{ result: unknown }
5. Frontend Components
Component Hierarchy
Components
Component File Purpose FinanceDashboard components/FinanceDashboard.tsxMain dashboard: header, metrics grid, tables, scheduler MetricCard components/MetricCard.tsxSingle financial metric with label and formatted value AgingSummary components/AgingSummary.tsxAR or AP aging buckets display BankAccountsTable components/BankAccountsTable.tsxBank accounts with names, types, and balances TransactionsTable components/TransactionsTable.tsxInvoices or bills table (configurable, limit 5 per section) SchedulerStatus components/SchedulerStatus.tsxScheduled job list with run/toggle actions
React Hooks
Hook Return Type Source useFinanceDashboard() { summary, scheduler, jobs, loading, error, refetch }Combines summary + scheduler + jobs via Promise.all useHealthStatus() HealthStatus API health check useDashboardSummary() DashboardSummary Full dashboard data useBankAccounts() BankAccount[] Bank accounts list useOpenInvoices() InvoiceSummary[] Open (unpaid) invoices useOverdueInvoices() InvoiceSummary[] Past-due invoices useOpenBills() BillSummary[] Open (unpaid) bills useBillsDueThisWeek() BillSummary[] Bills due within 7 days useSchedulerStatus() SchedulerStatus Scheduler running state useScheduledJobs() ScheduledJob[] Job definitions list
Hook pattern: Generic useAsync<T> with data: T | null, loading: boolean, error: string | null, refetch().
Function Output Example Description formatCurrency(amount) $1,234USD, no decimals formatCurrencyPrecise(amount) $1,234.56USD, 2 decimals formatDate(dateStr) Jan 15, 2024Full date formatDateShort(dateStr) Jan 15No year formatNumber(num) 1,234Comma-separated formatPercent(value, decimals) 12.5%Percentage formatRatio(value) 1.232 decimal places formatRelativeTime(dateStr) 2 hours agoRelative timestamp
Routes
Route Component /organization/:organization/hh-financeFinanceDashboard (wrapped with withOrganizationHeader)
Dashboard Layout
Header: Title, subtitle with generatedAt timestamp, Refresh button
Metrics Grid (5 cards): Total Assets, Total Liabilities, Total Equity, Accounts Receivable total, Accounts Payable total
Bank Accounts: Table of bank accounts with balances
Two-Column Grid: AR Aging Summary | AP Aging Summary
Two-Column Grid: Open Invoices (limit 5) | Open Bills (limit 5)
Scheduler (conditional — shown only when scheduler exists and has jobs): Job list with run/toggle controls
State Management
Loading: Shows “Loading financial data…” message
Error: Alert banner with error message and Retry button
Empty: “No data available” message
Actions: handleRunJob(jobId) triggers manual job execution; handleToggleJob(jobId, enabled) enables/disables a job; refetch() refreshes all data
6. Business Rules and Validation
Rule Description Enforcement External API dependency Dashboard is non-functional if HH Finance Agent is unreachable Loading/error states in UI No authentication API calls carry no credentials — the finance agent must be network-restricted Deployment configuration No permission gating Route accessible to all authenticated org members, regardless of role withOrganizationHeader HOC (no explicit permission check)Report types untyped P&L, balance sheet, aging report, top expenses, top income return unknown Renders must handle arbitrary structures Scheduler controls Running/stopping the scheduler and executing jobs are admin-level operations with no auth Agent-side access control needed Data freshness generatedAt timestamp indicates when the summary was last computedDisplayed in dashboard header Error handling Hooks catch errors as strings; no retry logic or request timeouts Errors displayed in UI; manual retry via Refresh
7. Acceptance Criteria
Dashboard loads and displays 5 metric cards (total assets, liabilities, equity, AR, AP)
Bank accounts table shows all accounts with names, types, and balances
AR and AP aging summaries display correct bucket totals (current, 1-30, 31-60, 61-90, 90+)
Open invoices table shows up to 5 invoices with status indicators
Open bills table shows up to 5 bills with status indicators
Refresh button fetches fresh data from the finance agent
Loading state is displayed while data is fetching
Error state displays the error message with a Retry option
Scheduler section appears only when jobs exist
Scheduled jobs can be manually run and toggled on/off
All currency values are formatted as USD
Relative timestamps display correctly (e.g., “2 hours ago”)
REACT_APP_HH_FINANCE_API_URL environment variable configures the API base URL
Dashboard is accessible from the organization navigation
8. Risks and Edge Cases
Risk Impact Mitigation No API authentication Unauthorized access to financial data if agent is network-exposed Restrict agent to localhost or private network; add API key auth No route permission gating Non-admin members see financial data Add canEditBilling or a new viewFinance permission guard Finance agent downtime Dashboard is completely non-functional Show clear “service unavailable” message; cache last-known-good data Untyped report responses Report rendering may fail on unexpected data shapes Define report response schemas; validate before rendering No request timeouts Slow agent responses block the UI indefinitely Add timeout to apiFetch (e.g., 30 second abort controller) No retry logic Transient failures require manual page refresh Add automatic retry with exponential backoff in hooks QuickBooks sync lag Dashboard may show stale data from QBO Display generatedAt timestamp prominently; add manual sync trigger Large data sets Hundreds of invoices/bills may slow the agent response Pagination via ?limit= parameter; default to 5 on dashboard
9. Dependencies
Dependency Type Status 001-Authentication (user sessions) Required before DRAFT 002-Organization Management Required before DRAFT External: HH Finance Agent service Required — separate deployment Running locally on port 19792 External: QuickBooks Online Required — finance agent data source Connected via finance agent
The HH Finance Agent (Spec 009) and the FFM Sync Engine (FFM Spec 007) are related but separate systems that both interact with QuickBooks Online.
System Purpose Stack QBO Interaction HH Finance Agent (this spec)Real-time financial dashboard — reads QBO data for display Standalone REST API on port 19792 Read-only: fetches accounts, invoices, bills, reports FFM Sync Engine (Spec 007 )Automated data pipeline — writes financial data to QBO Next.js 16, Prisma 7, PostgreSQL 15 Read + Write: imports IIF/CSV data, creates journal entries
Integration Architecture
Future Integration Points
The Finance Dashboard could display FFM sync health status via the GET /api/sync/health endpoint
Both systems share the same QBO realm for Hills & Hollows, LLC (qboRealmId)
The FFM circuit breaker state could inform the dashboard’s data freshness indicator
FFM Documentation