Extracted the check computation into buildReconChecks() and exposed it
to the report exporter (exportFlat), so the Reconciliation Checks report
now downloads as a branded PDF/CSV (Check · Residual · Status) like the
other reports.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Banking deposit/payment + bulk-categorize and the reconcile add-transaction
dialog previously limited the category to income (deposits) or expense
(payments). Now every account type is selectable, grouped by Income /
Expense / Assets / Liabilities / Equity (with codes); the direction's
natural type is listed first.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wraps the page in tabs: Workbook (assumptions/components/projection) and
a new Reserve Spending tab. The spending tab auto-imports every GL line
on reserve-flagged (is_reserve) expense accounts — total + reserve
balance, spending-by-year, and a full imported-expenditure detail list
(date, account, description, ref, amount). It shares the live GL query
so it feeds the projection's actual expenses in real time; Re-import
button refreshes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
B/S line items now render as '3011 SIRS Reserves - 3066 Painting/
Waterproofing' — leaf code+name prefixed by parent code+name when the
account has a parent, sorted so siblings group under their parent. Adds
parent_account_id to the reports accounts fetch. Codes always show
(baked into the label). Budget vs Actuals already shows codes + parent
hierarchy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New /dashboard/reserve-workbook: per-association capital reserve study.
- Manually-entered components with computed total, inflated replacement
cost, replacement year, fully-funded balance, % funded.
- Autoloads the current reserve fund balance and actual reserve
expenditures by year from reserve-flagged (is_reserve) GL accounts.
- 30-year Reserve Plan Summary projection (starting balance,
contribution w/ change %, interest, scheduled+actual expenses, ending
balance, fully-funded, % funded) with inflation/interest assumptions.
- Certify-as-approved + bold red preliminary disclaimer (same pattern as
the budget workbook); Save + landscape PDF export.
New accounting.reserve_workbooks / reserve_components tables (RLS mirrors
budget_workbooks). Nav links added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a 'Certify as Approved' toggle (accounting.budget_workbooks.certified
/certified_at/certified_by). Until certified, show a bold red note that
the projections are preliminary and not subject to annual increases or
inflation; once certified, show an approved/certified banner. Both states
render in the PDF export too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an edit (pencil) action to each row in the reconciliation working
list, reusing the add dialog in edit mode. On edit, category/vendor are
optional (so imported GL register rows can be edited without forcing
re-categorization); category/coa are only overwritten when a category
is chosen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reconciled items were hard-blocked from editing. Replace the block with
a confirmation warning that editing may unbalance the completed
reconciliation. The edit save preserves reconciliation_id, so the item
stays reconciled.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
generateLedgerStatement now prints every active owner under
'ACCOUNT HOLDER(S):' (primary first) and flows the property address
and tables below the owner block so it never overlaps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Owner card on the unit account dashboard showed only the primary
owner. List every active owner (primary first, tagged), with each
owner's email and account number; label becomes 'Owners' when >1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1) AccountingBillsPage: add an 'Apply existing payment' mode to the
Record Payment dialog. Instead of creating a new withdrawal, pick an
unmatched outgoing bank transaction already in the register; it links
to the bill (bill_id + coa cleared => Dr A/P / Cr Bank) and marks it
paid. Same-vendor / exact-amount candidates surface first.
2) BillDetailPage (also used by the board view): redesign the bill
detail layout to match Buildium - vendor-name header with status +
amount/due subline, grey-headed 'Bill details' / 'Item details'
(with Total row) / 'Approval' cards, and a right-hand 'Bill amount'
(Remaining) card. All editing/approval/comment functionality intact.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The pdf.js worker was set via new URL("pdfjs-dist/build/pdf.worker.min.mjs",
import.meta.url) — Vite only resolves relative paths there, so a bare
node_modules specifier 404s in production and react-pdf never renders the PDF
(both the field-placement step and the recipient signing page). Switch to a
Vite `?url` import so the worker is emitted as a hashed same-origin asset.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Match the violations changes on the Inspections map: add a green "New" badge for
violations recorded in the last 7 days, both in the map pin popups and the unit
list, so new vs old is distinguishable. (Newest-first ordering and
auto-advance-to-next-stage on re-recording a property already existed here.)
Also fixed a misleading stage fallback that showed "New" as a notice level.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Certificate of Validation claimed "cryptographic proof ... and integrity"
but showed no hash. Add a real SHA-256 of the original document to the
certificate page so the integrity claim is backed by a verifiable digest.
(Deploy via MCP alongside the base-flow fix.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "Violation Updated" entries rendered raw change diffs: a "→" arrow the PDF
font can't draw (showed as "!'"), snake_case field names, "—" for empty, and a
dense single-paragraph wall.
- Render-time (fixes existing reports): normalizeTimelineText now maps "→"→"to"
and smart quotes/dashes/ellipsis to ASCII, prettifies snake_case field labels
(violation_type → "Violation Type", etc.), and puts each change on its own
line for proper spacing.
- Logger (future entries): logViolationUpdated/logBulkUpdate now write
professional notes — "Description / Notes changed from "X" to "Y".",
"… set to …", "… cleared." — with Title-Case labels, long values truncated to
80 chars, and one change per line. No arrows or raw field names.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auto-escalation was broken: calculateNextNoticeLevel queried unit_id but the
dialog passed the owner id, so it always returned "First Notice." Rewrote it to
advance one step past the property's highest CURRENTLY-OPEN stage (First →
Second → Third & Final, capped), identifying the property by unit, else owner,
else association+address; the dialog now passes all of those and re-runs when
the unit/address changes. So re-recording a violation on the same property
auto-selects the next stage.
New vs old by date: list now loads newest-first, and cards show a "New" badge
for violations recorded in the last 7 days (violation_date was already captured
and shown, plus the timeline group-by-date view).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parse-invoice: guard oversized PDFs (>18MB → clear "too large, saved for manual
entry" message) and surface the AI gateway's actual error instead of a bare
status code.
inbound-bill-email: route to an association by the recipient alias
(<alias>@bills.avriamail.com, via associations.inbound_alias) in addition to the
sender's vendor mapping; fix extractEmail (bare addresses were mis-split, e.g.
invoices@x → s@x); surface parse-invoice's real error in the inbox. Deployed via
MCP; migration associations_inbound_alias adds + populates the aliases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Render an itemized table (Description / Qty / Unit Price / Amount) from the
linked invoice's line_items, with a Total row (the authoritative bill amount)
and a subtle note if the line items don't sum to it. Falls back to a single
row when no line items exist. Fetch invoices.line_items in the bills query and
pass it through to the generator.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a "Download PDF" action to each bill's row menu that generates a clean
one-page bill summary (vendor, amount, invoice #, bill/due dates, expense
account, description, status, and board approval statuses). New generator
src/pages/accounting/lib/billSummaryPdf.ts (jsPDF), styled like the other
report PDFs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The list-row trash is always-visible now, but add an unmissable "Delete"
button in the open-conversation header (ChatView) too. Deletes the whole 1:1
conversation for everyone and clears the selection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New "Add Bill" action on the bank reconciliation screen: enter a bill (bill
date, payment date, vendor, amount, expense account), and it creates the bill
(accrual expense on the bill date) plus a withdrawal that pays it (Dr A/P /
Cr Bank on the payment date) and clears into the current reconciliation. The
expense lands in the bill's period for reporting; the cash leaves on the
payment date.
Fixes import-mode posting: app-created bills now carry gl_post_override=true
(new accounting.bills column) so post_bill_gl posts their expense even for
import-mode (gl_auto_post=false) companies. The previous external_source-based
gate didn't work because the bill back-sync trigger stamps every app bill with
external_source='acmacc_bill', making it indistinguishable from Buildium
imports; the explicit flag set only by the app's bill creators (Bills page +
reconciliation) is reliable, while Buildium-import/auto-reconcile bills stay
gated and never double-count the GL pull. Migration:
bills_gl_post_override_for_inapp_bills.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Whole board where users actually look: the Direct "New Message" dialog now
lists "Whole Board — <Association>" rows for staff (All/Board tabs). Clicking
one opens the group-topic composer prefilled with that board's members (the
composer is now reusable: hideTrigger + controlled open + initialSelected,
wired through MessagesPage).
- Delete discoverability: the trash control on Direct conversations and Topic
threads is now always visible (subtle, bottom-right) instead of hover-only,
which read as "no delete option."
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Whole-board messaging: staff starting a Topic can add an entire association's
board in one click ("Whole Board — <Association>") in NewTopicThreadDialog;
create_topic_thread already permits staff to add board members.
- Delete for everyone: trash control on Direct conversations (deletes the 1:1
both directions) and on Topic threads (deletes the thread for the creator or
admin/manager — cascade). Backed by new RLS delete policies on direct_messages
and message_threads (migration allow_delete_conversations_and_threads).
- Fix New Message dialog overflow for real: the grid child needed min-w-0 (+
overflow-hidden on the card); applied the same guard to the Topic dialog.
- Fix board members showing as "Unknown User": fall back to board_members.member_name.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The recipient row's inner name+badge flex lacked min-w-0, so a long name
(e.g. a full association name) couldn't truncate — it widened the row,
pushed the badge out, and stretched the dialog (and its full-width search
field) past max-w-md. Add min-w-0 so the name truncates and the dialog
stays within bounds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a "Topics" tab to the Messages page so board members can start a
topic-scoped group conversation with management and their own-association
board members. Topic = predefined category + free-text subject; recipients
are multi-selected; replies are visible to all participants.
- New public tables message_threads / message_thread_participants /
thread_messages (migration board_topic_message_threads), added to the
realtime publication; RLS scopes reads/writes to thread participants via
is_thread_participant().
- create_topic_thread() RPC (SECURITY DEFINER) enforces recipient
eligibility server-side: management (admin/manager) or a board member of
the caller's own association; rejects anyone else.
- Frontend: messageTopics constant, useMessageThreads/useThreadMessages
hooks (realtime), TopicThreadList/TopicThreadView/NewTopicThreadDialog,
and a tabbed MessagesPage. Recipients notified via insert_notification
with a /dashboard/messages?thread= deep link. Existing 1:1 DMs unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The diagonal semi-transparent "RECOMMENDED FOR FINING" stamp looked poor over
the report content. Remove it (drawRecommendedForFiningStamp + both call sites)
and instead print a bold red "RECOMMENDED FOR FINING" line just beneath the
navy header bar on both the Summary and Timeline reports, shifting the owner
block down to make room. Non-fining reports are unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Budget vs Actuals: shade the Budget / Actual / Variance / Variance % columns
in a light→dark grey gradient with left vertical borders so they're easy to
tell apart (shared BVA_COL styles applied to header, group-total and detail
rows; variance text bumped to emerald/red-700 for contrast on the darker fills).
Reconciliation R2: reconcile() classified GL lines via the active-only account
list and dropped lines on archived accounts, so the "Assets = Liabilities +
Equity (incl. net income)" check went out of balance by the archived balance
(e.g. VW's archived 4016 Renters Insurance Income, -$75). Feed reconcile any
account referenced by glCumulative but missing from the active list (joined
metadata), matching the Balance Sheet builder; once every line is typed, R2
reduces to R1 and passes whenever the ledger balances. Also tightens R3/R4.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fetchOwnerLedger pulled all entries regardless of is_archived, so the
accounting AR Aging / Prepaid / batch reports still counted archived
(voided/duplicate) charges — e.g. 2455-VL showed $4K in violations
instead of $3K. Exclude archived entries at the source.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Archived (voided/duplicate) owner_ledger_entries were still counted in
the per-unit account breakdown, Collections, and Outstanding Balances —
inflating totals (e.g. 2455-VL showed $4K in violations vs the real
$3K after a duplicate was archived). Filter out archived entries in all
three, matching the canonical Unit Ledger view.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a PDF upload to the bid/quote dialog (stored in the bid-attachments
bucket, saved to document_url/document_name) and shows the attachment in
the detail view. Board members with can_upload can attach PDFs — table
RLS and the storage bucket already permit it; only the UI was missing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "Staff full access on status_updates" policy was missing from the
live DB (dropped outside migrations), so all inserts/updates failed with
"new row violates row-level security policy". Recreate it (admin/manager).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lets management post internal status updates that don't appear in the
board portal. Adds status_updates.hidden_from_board and re-creates the
association-scoped RLS SELECT policy so board members can't read hidden
rows (staff still see all). Dialog gains a "Hide from board" toggle, the
board view filters hidden updates, and management cards show a badge.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Budget column overlap-weighted partial months, so a YTD range
ending mid-month showed a fractional budget. Now include each budget
period's full amount whenever the selected window touches it, for both
the main and comparison windows — exactly as entered on the budget.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The accounting Budget vs. Actuals report computed actuals from
operational tables (bill_items + payment transactions + a
budget-weighted paid-invoice plug), which double-counted (a bill
counted as its line item AND its payment, plus redundant imported
bills) and diverged from the posted books — especially for Buildium
GL-import / import-mode associations whose activity lives only in
journal entries.
Now fetch GL lines via fetchAllGLLines and net per account
(income = credit - debit, expense = debit - credit), matching the
Income Statement. Budget side was already correct (reads active
accounting.budgets + budget_entries).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An archived COA account that still carried a GL balance was dropped from the
Balance Sheet (fetchAllGLLines filtered is_archived=false), so the report went
out of balance by exactly that amount. Now the financial-report GL fetches
include archived accounts (those without activity never appear, since this
queries journal lines), and buildBalanceSheet surfaces archived asset/liability
/equity accounts that carry a balance as line items; archived income/expense
flow into Net Income. Fixes the Village Woods $170.30 out-of-balance and the
same latent issue in Bent Oak and Casuarina.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Buildium often records vendor payments as direct checks (Dr Expense/Cr Bank)
with no bill. For import-mode companies post_bill_gl no-ops, so a bill is a
record only. New fn accounting.autocreate_nobill_vendor_bills() creates a paid
record-only bill for each such buildium_gl payment (vendor resolved by payee,
idempotent by external_id); buildium-gl-sync calls it per import-mode company
after each pull. No GL impact, no double-count.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Below the Submit vendor profile button, instruct vendors to email bills to
ap@avriacam.com or mail them to the association's mailing address, c/o Avria
Community Management, LLC. The address is resolved from the vendor's
association (association_id, falling back to association_ids[0]).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
post_transaction_gl now skips posting a register row whose exact bank
movement (company + bank account + date + amount + direction) is already
in buildium_gl, so categorizing an ad-hoc-materialized register row can no
longer duplicate the Buildium GL pull. Also freezes (exclude_from_gl) any
remaining unguarded buildium-overlapping register rows across all
import-mode companies. Genuinely-missing manual items (no GL match) still
post normally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The retired no-reply@avriamail.com had no email_senders row, so the
queue dispatcher 500'd on every run. Point DEFAULT_AUTOMATED_FROM at
notifications@avriamail.com (the sole remaining sender). The
AUTOMATED_EMAIL_FROM secret was set to match and the function deployed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
partymap mode now also resolves each Buildium transaction's dominant
income/expense account (from its Journal lines) to a local category, staged
alongside the party. Used to backfill accounting.transactions.category on the
materialized bank register. Applied to Village Woods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds "partymap" mode to buildium-payee-backfill: resolves each Buildium GL
transaction to a local vendor (by name) / customer (by unit_number) and stages
it in accounting.buildium_party_map. The materialized bank register
(accounting.transactions) is then backfilled in SQL by bridging each register
row to its journal-entry bank line (bijective row-number pairing within
date/amount/side groups, so same-amount/same-day payments each get a distinct
owner) and pulling the resolved vendor/customer + name from staging.
Applied to all five Buildium-imported associations (Village Woods, Bent Oak,
Village Grove, Bridgewater, Casuarina).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
account mode lists a single account's ledger entries; txn mode reconstructs
one transaction's full double-entry across accounts. Used to trace the VW
5098 discrepancy to a missing 2025-12-31 reserve-funded reclass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
report mode pulls /v1/generalledger signed balances per account for the
window so imported books can be diffed against Buildium's authoritative GL
to verify completeness.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-pulls Buildium GL transactions and backfills payor/payee names onto
imported journal_entries.description (matched by external_id). Vendor for
Bill/Check/EFT via PaymentDetail.Payee; owner for Charge/Payment/ApplyDeposit
resolved from UnitId via /v1/associations/owners. Idempotent; modes
sample/dry/apply. Used to backfill Village Woods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a 'Recurring' button to the Journal Entries and Bills page headers
that links to the Recurring (Bills & Journals) page, and rename the
sidebar item so it's clear it covers journals too. Improves discovery —
users looking for recurring journals on the Journal Entries page now have
a direct link.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Swap the bank-linking + transaction-download integration from Plaid to
Stripe Financial Connections (you're already on Stripe for payments).
- accounting.stripe_bank_connections table (mirrors plaid_connections)
- stripe-financial-connections edge function: create_session / save_account
/ sync / disconnect, using per-association keys from stripe_account_mappings
(handles connected accounts via Stripe-Account + stripeAccount on the client)
- lib/stripeBank.ts: client wrappers + the Stripe.js collectFinancialConnections
Accounts flow
- AccountingBankingPage: Connect/Sync/Disconnect now drive Stripe FC; removed
react-plaid-link usage and the PlaidLinkButton
- Integrations page wording updated
- Imported transactions land uncategorised in the bank feed (credit/debit by
amount sign) for matching, same as before
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add accounting.recurring_templates (bill|journal) with a schedule
(frequency/interval/day-of-month/start/end). New generate_due_recurring()
materialises real bills (-> A/P) and journal entries on cadence — catching
up any missed periods — posting through existing triggers. Runs nightly via
pg_cron ('accounting-recurring-daily') and on demand from a new Recurring
page ('Generate due now'). Page lists templates with pause/resume/edit/
delete; create dialog handles both kinds (vendor + line items for bills,
signed balanced lines for journals). Wired into routes + accounting nav.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>