Commit Graph

60 Commits

Author SHA1 Message Date
admin aa94622a78 Opening Balances: fix duplicate earnings accounts; recognize name variants
The auto-provision effect could fire twice and create duplicate "Current Year
Earnings" accounts (seen on Village Grove). Use the idempotent
coa_get_or_create RPC with a once-per-company ref guard, and match existing
name variants (Current Year Income / Net Income / Retained Earnings) so it
won't create redundant accounts. Balance sheet now also folds
"Current Year Income"/"Net Income" equity accounts into the Net Income line.
Removed the existing Village Grove duplicate (zero activity).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 19:04:51 -04:00
admin 7fd8ad2c52 Opening Balances: allow entering Retained Earnings + Current Year Earnings
Auto-provision "Retained Earnings" and "Current Year Earnings" equity accounts
per company so they appear as inputtable rows in the Chart of Accounts Opening
Balances grid. The Balance Sheet folds the posted "Current Year Earnings"
account into the Net Income line (already did this for Retained Earnings), so a
mid-year import can seed both equity figures without entering income/expense
detail, and Total Equity stays balanced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 18:11:01 -04:00
admin 5aa03d1cd6 Balance Sheet: fold posted Retained Earnings account into the prior-years line
A real "Retained Earnings" equity account is postable via journal entries, but
the balance sheet listed it separately from the calculated "Retained Earnings
(prior years)" line, so JE adjustments looked like they had no effect there.
Now the posted RE-account balance is added into the "Retained Earnings (prior
years)" line (and removed from the generic equity list). Total Equity is
unchanged — it already included that account.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 13:26:13 -04:00
admin 8f14877274 Balance Sheet: label the YTD earnings equity line "Net Income"
The equity section already carried a permanent current-year earnings line
(income − expenses for the fiscal year to date); rename it from "Current Year
Earnings" to "Net Income" to match the Movement of Equity report and the
requested terminology. No calc change — still YTD income minus expenses,
included in Total Equity.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 13:00:24 -04:00
admin f66165a8f5 Checks: Banking print now applies field positions + MICR gaps
Banking's generateCheckPDF opts omitted fieldPositions (and micr gaps), so the
per-field x/y position adjustments saved in Check Setup had no effect on checks
printed from Banking. Pass them through.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:50:38 -04:00
admin 3d264130b9 Checks: print the General Settings mailing address as the return address
Banking check printing hardcoded companyAddress: undefined, so the association
mailing/office address (accounting.companies.address, set in General Settings)
never printed below the name. Now loads and passes it. Bills check printing
falls back to the company name/address when no company_check_layouts payer is set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:15:48 -04:00
admin c66a11769c Checks: align Print Checks page with Check Setup generator
PrintChecksPage was still using the old generator (utils/checkPdfGenerator +
check_layouts), so it had the same bugs as Bill Approvals (wrong return address,
missing vendor address, ignored x/y offsets). Route it through
lib/checkPdf.generateCheckPDF using the accounting company (return address),
check_settings (layout/offsets/field positions/signature/MICR), the selected
bank account (routing/account), and vendor address as payee. DB side effects
unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:01:07 -04:00
admin dd4a088e9d Checks: print PDF now uses Check Setup settings (matches the sample)
Bulk "Print Checks" was rendering via a separate generator (utils/checkPdfGenerator
+ check_layouts), so the printed PDF ignored the accounting Check Setup: wrong
return address (generic company vs association name + mailing address), missing
vendor address, and ignored x/y field-position offsets.

Route the print through the same generator as the working sample
(lib/checkPdf.generateCheckPDF) fed by the accounting company (return address),
check_settings (style/offsets/field_positions/signature/MICR gaps), the chosen
bank account (routing/account), and the vendor address as payee. DB side effects
(checks, transactions, bill paid status, check numbering) unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 11:45:30 -04:00
admin a81e4f51ab RV/Boat Lots: map tab edits the association Map amenity + per-pin amount
The Map tab now binds to the association's active map-type amenity (the
reservation map) instead of a separate table — pins edited here are the public
reservation map. Adds a per-pin amount ($/mo) field (GoogleMapPicker showAmount
prop, backward-compatible). Shows a notice when no Map amenity is active and an
amenity picker when more than one exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:41:54 -04:00
admin 69f643a51e RV/Boat Lots: sync lots to public rental_calendar amenities
Add a "Sync to Public Page" button that creates/updates one rental_calendar
amenity per lot (name, size · rate · availability in the description, rate in
booking_config, shown on the public page). Idempotent via amenities.source_rv_lot_id;
removes synced amenities for deleted lots.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 21:06:21 -04:00
admin f549f21c21 RV/Boat Lots: waitlist size + more vehicle types
- Waitlist now captures a free-form Size (requested_size column) in place of
  the type field in the internal form/table.
- Lot type selector (add + bulk edit) expanded to RV, Boat, Travel Trailer,
  Fifth Wheel, Camper, Car, Truck, Trailer, Other.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 21:01:18 -04:00
admin 0c0300efce RV/Boat Lots: internal lot map with directory-unit links
Add a Map tab that reuses the reservation-map pin picker. Staff drop/label
pins per lot and optionally link a directory unit (pin.linked_amenity_id =
unit id). Config persists per association in rv_boat_lot_maps.

GoogleMapPicker generalized with optional linkLabel + allowLinkAnyStatus
(defaults preserve the amenity reservation-map behavior).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 20:40:52 -04:00
admin 10a9d31b94 RV/Boat Lots: insurance expiring-soon / expired indicator
Color-code each rental's insurance status like vendors: red when expired,
amber when expiring within 90 days (with days remaining), green otherwise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 20:23:07 -04:00
admin 308af20aa1 RV/Boat Lots: request renter insurance (vendor-style flow)
Phase 4. Mirror the vendor insurance request flow for RV/boat renters:
- Migration: insurance fields on rv_boat_lot_rentals + rv_renter_insurance_requests
  table + token-scoped lookup/submit SECURITY DEFINER RPCs (granted to anon).
- Edge fn send-rv-renter-insurance-request emails the renter a secure link
  (reuses the vendor-insurance-request email template).
- Public page /rv-insurance/:token to submit carrier/policy/expiration + COI upload.
- "Request Insurance" button on each active rental + insurance status display.

DB RPCs verified end-to-end (rolled-back txn): submit matches token, updates the
rental, marks the request submitted. Edge function deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 20:16:31 -04:00
admin d8465f2297 RV/Boat Lots: link rentals to owner/unit + Notice to Vacate toggle
Phase 2: rental form now has optional Owner and Unit selectors (auto-fills
renter info/unit from the chosen owner), persisting owner_id/unit_id.

Phase 3: add RvRentalVacateButton on Owner and Unit profiles — shows only
when that owner/unit has an active/vacating RV-boat rental, and toggles the
rental status between active (renting) and vacating. Active Rentals tab now
includes vacating rentals with a badge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 18:20:58 -04:00
admin 7c18576390 RV/Boat Lots: batch edit Lots and Active Rentals
Add row checkboxes + select-all to the Lots and Active Rentals tables with a
selection bar. Bulk edit lots (type, status, monthly rate) and bulk delete;
bulk edit rentals (status incl. end, owner/renter flag, monthly rate).
Phase 1 of the RV/Boat lots enhancements.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 17:54:43 -04:00
admin 4ecbdcfd4d Calendar: manage rental/meeting calendars from the dashboard
Add a "Manage Calendars" action (rental/meeting modes) that opens the full
AmenitiesManager for a chosen association in a dialog — so staff can create/
configure rental calendars, manage their bookings, and block/unblock
availability without going to the association overview page. Adding bookings
inline was already supported in these modes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 17:39:44 -04:00
admin 6932c5acbb Unit ledger: let amount input accept $0.00
The amount field had min="0.01", so the browser's native form validation
blocked a $0.00 charge before the JS check ran. Lower to min="0" so a $0.00
charge/note can be submitted; the JS still restricts $0 to charge-type
entries (payments require a positive amount).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 17:22:07 -04:00
admin 3f39bfbd70 Unit ledger: allow posting a $0.00 charge
Relax the amount validation so a Charge/Expense/WriteOff can be posted with a
$0.00 amount (used as a note/memo on the ledger). Payments and prepayments
still require a positive amount; negatives remain blocked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 17:04:25 -04:00
admin 271a6d2701 Nav: add Owner Ledger link under Finance → Receivables
The Owner Ledger page (/dashboard/owner-ledger) was a route with no menu
entry, so it was only reachable by URL. Add it to the Receivables group so
staff can reach it (and the new Add Note action).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:41:59 -04:00
admin 2503dc4846 Owner Ledger notes: show notes to homeowners
Reverts the homeowner-facing exclusion — ledger notes now appear on the
homeowner ledger and statements views as intended.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:18:00 -04:00
admin e641b05a91 Owner Ledger notes: hide internal notes from homeowner-facing views
Exclude transaction_type 'note' from the homeowner ledger and statements
pages so internal staff notes aren't shown to owners.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:16:53 -04:00
admin f53a0aaf46 Owner Ledger: add $0.00 note entries
Add an "Add Note" action that records a memo on an owner's ledger as a
$0.00 entry (transaction_type 'note', debit/credit 0). Notes work on any
ledger including paid-up ($0.00 balance) accounts, render as styled memo
rows, and don't affect the running balance. The accounting sync treats a
zero entry as a no-op, so no phantom AR is created.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:12:50 -04:00
admin 36787b193d Accounting COA: bulk edit + bulk delete of accounts
Add row checkboxes (with per-category select-all) to the accounting Chart of
Accounts. A selection bar exposes Edit/Delete/Clear. Bulk edit applies any of
type, parent account, bank flag, reserve flag to all selected (each field has
a "No change" option); bulk delete removes selected accounts. Mirrors the
existing bulk-edit UX on the per-association chart_of_accounts page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:54:19 -04:00
admin cb740c2521 Bill approvals: fix approver column always showing "None"
The list loaded all bills (~1,150) then fetched approvers with
.in("bill_id", [all ids]); that request URL exceeds server limits and
fails silently, so approvalsByBill was always empty. Fetch the small
bill_approvals table directly (RLS scopes per role) and group locally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 13:21:08 -04:00
admin fd7107290a Bill approvals: admins-only mark-paid + DB guard
Restrict marking a bill paid to admins only, per requirement.
- BillDetailPage: gate Mark Paid / Mark Unpaid on useAuth().isAdmin
  (was only hidden in board view).
- BillApprovalsPage: gate Print Checks (which sets bills to paid) on isAdmin.
- Migration: BEFORE INSERT/UPDATE trigger enforce_admin_marks_bill_paid()
  rejects the transition into 'paid' for authenticated non-admins. Service-role
  / system contexts (auth.uid() null: buildium-sync, accounting triggers,
  autopay) remain allowed. Verified: admin allowed, non-admin blocked (23514).

Note: the approver column showing "None" in production is a stale-deploy
issue — the DB column was renamed vendor_name->approver_name (Jun 4) but
prod still ran code querying vendor_name. Deploying current main resolves it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 12:28:03 -04:00
admin ea8cf6efb6 Merge remote-tracking branch 'origin/main' into accounting-sales-receipts-coa-sync-expenses
# Conflicts:
#	src/integrations/supabase/types.ts
#	src/pages/BillDetailPage.tsx
2026-06-06 23:24:45 -04:00
admin af67e2fb9a Accounting: rename bill_approvals.vendor_name→approver_name + COA import dialog
Rename the bill_approvals approver column from vendor_name to approver_name
across UI, hooks, types, and buildium-sync. Add a Chart of Accounts import
dialog (XLSX upload + column mapping + type-alias normalization) and a
"denied" status color.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 23:04:29 -04:00
admin 096cac6b0b Merge pull request #6 from renee-png/accounting-sales-receipts-coa-sync-expenses
Accounting: sales receipts, COA dashboard sync, accrual A/P, manual deposits, per-association COA, bill-approval sync fix
2026-06-05 20:52:43 -04:00
admin 84c8483169 Accounting: unify vendor roster + COA across bill-approvals and accounting bills
Single vendor source (public.vendors) and single COA source (accounting.accounts)
across both bill flows:

- Forward sync now carries public.bills.expense_account_id into the mirrored
  accounting.bill_items.account_id (when it resolves to accounting.accounts).
- Reverse trigger flows a GL change on a mirrored accounting bill line back to
  public.bills.expense_account_id (loop-guarded).
- New public.ensure_accounting_vendor RPC resolves a chosen public vendor to its
  accounting.vendors row; one-time backfill of mirrored line account_id.
- BillApprovalsPage GL pickers now use ChartOfAccountsDropdown (accounting.accounts).
- AccountingBillsPage vendor picker now lists public.vendors scoped to the
  company's association and maps to accounting.vendors on save.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 18:57:32 -04:00
admin 2c723410a4 Bill approvals: surface approvers, fix email path, schema cleanup
UI

- Dashboard BillApprovalsCard: render approver name chips (color-coded
  by vote status) per pending bill instead of leaving the approver
  identity invisible.
- BillDetailPage: collapse the duplicate "Requested Approvers" card
  into the existing "Approvers" table. Approve/deny handler now stamps
  approved_by = auth.uid() for audit trail.
- MasterBoardDashboardPage: the "pending approvals for me" count was
  filtering on a non-existent bill_approvals.approver_user_id column.
  Replaced with a board_members.member_name -> bill_approvals.approver_name
  join (matches the RLS policy).
- BillApprovalRequestDialog + AIInvoiceParserPage: bill_approvals inserts
  now set created_by.

Database

- Rename public.bill_approvals.vendor_name -> approver_name. RLS policies
  auto-rewritten by ALTER TABLE RENAME COLUMN; the column was misnamed
  (it stores the approver's board-member name, never a vendor).
- Restore the bill_approval_email_tokens table + lookup_/record_
  bill_approval_by_token RPCs. The original 20260520153409 migration
  was never applied successfully; rewrote it to use approver_name and
  to populate approved_by/created_by from board_members.user_id on
  token-driven votes. Added the v2 migration that matches the live DB
  state.
- accounting trigger: void on accounting.bills cascades to
  public.bills.status='cancelled' (existing forward sync then drops the
  accounting row per accounting.bill_should_mirror).

Edge function

- send-transactional-email: add bill-approval-request and
  bill-approval-vote-invite templates (caller paths in BillApprovalsPage
  + send-bill-approval-invites referenced templates that weren't in the
  registry, so every email 404'd). Restored the local copies of
  election-invite, board-vote-invite, and the missing registry.ts so the
  repo matches what's deployed. Deployed to send-transactional-email v35.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-04 17:17:05 -04:00
admin 6634907799 Per-association chart of accounts
Each association now owns an independent set of chart_of_accounts rows. Two
associations can both have a "5000" meaning different things, edited
independently — previously buildium rows were shared across associations via the
association_ids[] array, so editing one association's number edited it for all.

- Data migration: split each shared buildium row into one row per association in
  its association_ids (excluding nulls and ids of deleted associations). The
  original row stays as the per-association row for its own association_id;
  clones are added for the others — nothing is deleted, so no FK dangles.
  94 -> 370 buildium rows. References repointed by each record's association
  (bills, budgets, owner_ledger_entries, vendor_coa_mappings, units, vendors,
  journal_entries; budget_actuals_monthly is a view). parent_account_id remapped
  same-association; orphan-parent children become top-level. Pristine backup in
  public._coa_perassoc_backup. Ran in one transaction with in-line verification.
- Uniqueness: drop (account_number, accounting_system); add
  UNIQUE(association_id, account_number) WHERE accounting_system <> 'platform'
  (platform rows mirror accounting.accounts and carry blank/dup codes). This also
  finally backs the buildium importers' existing onConflict target.
- Keep association_ids as a single-element mirror of association_id during the
  transition so the admin COA page and direct array-contains callers keep working.
- App: fetchChartOfAccounts scopes buildium/zoho by association_id when an
  association is given (was system-wide). Importers and sync_account_to_public_coa
  were already per-association; no change needed.

Verified on live data: 370 singleton-array rows across 12 associations, zero
intra-association duplicates, zero cross-association references or parents, and a
live two-association "5000" independence test (create/rename/isolate) passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 13:38:50 -04:00
admin 8f1cbcd3af Accounting: selectable-source / multi-line manual deposits
Deposits no longer force the credit side through Undeposited Funds — the
structural cause of negative Undeposited balances. A deposit can now credit any
account(s): interest income, a refund, an insurance reimbursement, cash straight
to the bank, etc.

- Schema: add accounting.deposit_lines (deposit_id, company_id, account_id,
  amount, memo) for the credit side, plus deposits.source_account_id as a
  single-source fallback. RLS mirrors deposits (staff + company member).
- post_deposit_gl: Dr bank for the total; Cr each deposit_lines row's account
  for its amount; no lines -> Cr source_account_id; neither -> Cr Undeposited
  Funds (backward compatible — existing deposits stay Dr Bank / Cr Undeposited).
  Remainder safety net keeps the entry balanced. New trg_acct_deposit_line_gl
  re-posts when lines change (header trigger fires before lines exist).
- Make Deposit page: GL-driven submit writes the deposit header + deposit_lines
  and marks selected payments deposited. Adds an "Other deposit lines" grid
  (account + amount + memo) alongside the existing Undeposited selection, with a
  running grand total and a soft guard against over-crediting Undeposited.
  Drops the old bank/Undeposited register-transaction inserts and manual balance
  pokes (never exercised in production; carried a money-in sign bug). Deposits
  are GL-only, consistent with the sync-created deposits already in the DB.

Verified Dr/Cr for single-source and multi-line scenarios against the live GL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 13:00:41 -04:00
admin 7464d55b6c Accounting: enforce accrual A/P on bill payments (match rule + guard + cleanup)
Under accrual, a vendor expense is recognized once — when the bill is entered
(Dr Expense / Cr A/P). Paying a bill must only relieve the liability
(Dr A/P / Cr Bank) and never re-hit the expense account. This hardens the
existing Pay Bills flow against re-recognition and double counting.

- Strict bill matching: extract a pure, dependency-free matcher into
  lib/billMatch.ts (debitMatchesBill/matchOpenBills) — same vendor, status
  open/overdue/partially_paid, amount within $0.01 of remaining (or <= remaining
  for partials), debit date within ±30 days of due/issue date. Unit-tested in
  billMatch.test.ts (covers the identical-recurring-charge regression).
- AccountingBankingPage.saveTx uses the strict rule (was "any open bill"), so a
  thrice-paid identical charge only clears the in-window bill.
- Bank-feed categorizer (bulkSetCategory) matches open bills before assigning an
  expense COA: single match clears A/P + links the bill; multi-match is skipped
  with a prompt to resolve in Pay Bills; no match categorizes as a direct expense.
- DB guard: add accounting.transactions.bill_id (FK -> bills) and CHECK
  chk_bill_payment_no_coa (bill_id IS NULL OR coa_account_id IS NULL) so a
  bill-linked payment can never carry an expense category. Both writers set
  bill_id on single-bill payments; partial payments now write partially_paid.
- One-time cleanup: clear coa_account_id on Ashley Manor's 8 double-counted bill
  payments ($2,198.98) so the GL reposts them as Dr A/P / Cr Bank.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:30:44 -04:00
admin d82466f826 Accounting: Sales Receipts, COA sync to dashboard, vendor-expense recognition
- Add Sales Receipts page (dashboard/accounting/sales-receipts): records a
  cash sale (name, address, income account, price, qty) — deposits and books
  income in one step via a transaction. New accounting.sales_receipts table.
- Sync chart of accounts to the accounting dashboard: mirror accounting.accounts
  into public.chart_of_accounts for platform associations (one-way, same id) so
  Bill Approvals and every COA consumer use the dashboard's accounts. Legacy
  rows hidden; Bill Approvals made system-aware.
- Vendor-expense recognition: a vendor payment with no bill now books the
  expense directly (Dr Expense / Cr Bank) on the payment date instead of going
  to A/P; payments against open bills still clear A/P (applied FIFO). Backfill
  reclassifies unbilled payments stuck in A/P. Expense Summary report made
  GL-driven so it follows the same rule.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 10:01:18 -04:00
admin 3b220a3f26 Checks: drop "Authorized Signer", move signature label below the line
Removes the "Authorized Signer" text from the payee/address block, moves the
"AUTHORIZED SIGNATURE" label to just below the signature line, and raises the
signature image cap (0.42 -> 0.65 in) so the signature renders full size in
the now-clear space above the line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 01:12:52 -04:00
admin 7ccfc133f8 Bill-payment checks: per-element positioning + visibility
The bill-payment check generator (checkPdf.ts) now supports per-field
position offsets (X/Y inches) and show/hide for every element — check
number, return address, bank block, date, pay-to, amounts, payee address,
memo, signature, and MICR — layered on the existing layout (defaults render
identically). Edited in Settings → Check Setup ("Element positions").
Stored in accounting.check_settings.field_positions (jsonb). Also replaced a
"→" with ">" in the MICR placeholder to avoid the UTF-16 spacing artifact.

Migration applied: check_settings.field_positions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 01:05:10 -04:00
admin c3d1d86b07 Reports: account drill-down to GL; bids/quotes PDF attachments
- P&L and Balance Sheet account rows are now clickable and open the
  General Ledger filtered to that account (its transaction list for the COA).
  Adds StructuredRow.accountId, threaded from the report builders, with a
  clickable row in StructuredTable and an initialAccountId prop on
  GeneralLedgerReport.
- Bids/Quotes: PDF upload on the New Bid/Quote dialog (bid-attachments
  bucket + document_url/document_name columns), shown as a link on the
  bid Details dialog.

Migration applied: bids_quotes_pdf_attachments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 00:47:11 -04:00
admin c3a0682e57 Accounting: A/P-clearing payments, check return address + MICR gaps, dashboard fixes
Bill/vendor payments:
- Bill "Pay" and bank-register vendor debits now clear Accounts Payable
  (Dr A/P / Cr Bank) instead of re-debiting the expense, which had
  double-counted expenses in the P&L and never cleared A/P. The expense
  account is kept as a display-only category on the payment.

Checks:
- Bill-payment checks now print the return address (payer name/address)
  from the company check layout (was hardcoded blank).
- Per-segment MICR gap control (check# / routing / account) in both check
  generators, wired to Check Setup (bill payments) and the Check Layout
  editor (Print Checks). New columns: check_settings.micr_gap_1/2 and
  check_layouts/company_check_layouts.micr_gap_1/2.

Accounting Dashboard / Financial Overview:
- Date-range selector (presets + custom) drives the charts, top expenses,
  and recent transactions.
- PDF title renamed to "Financial Overview" and shows the period.
- Fixed amounts rendering as "$ 5 0 0. 0 0": the U+2212 minus sign forced
  jsPDF into a UTF-16 fallback; replaced with an ASCII hyphen (also in the
  report PDF out-of-balance line).

DB migrations applied to the project: repost_gl_on_line_item_change,
budget_actuals_accrual_owner_income, add_micr_gap_controls.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 00:35:30 -04:00
admin 7b54ddd40d Export the Accounting Dashboard as a vector PDF
Add a dashboard PDF generator that renders the metrics, an Income & Expenses
bar chart, a Top Expenses donut, the invoices overview, and recent transactions
as native PDF vector graphics + selectable text (not a screenshot), using the
shared branded header/footer. Wire an "Export PDF" button into the dashboard
header (association logo, ACM fallback).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:52:43 -04:00
admin e302fb91f0 Accounting platform: remove Zoho, unify reports, board access, vendor sharing
- Remove the Zoho Books integration (edge functions, sync libs, settings,
  reports/overview, banking links, fees tab, import dialog); preserve fee
  rules as a standalone FeesTab and the COA accounting_system classification.
- Financial Overview/Reports (staff + board) render the Accounting dashboard
  and reports; board reports mirror the rich Accounting Reports.
- New Reserve Fund Schedule report + an is_reserve flag on accounts.
- Unify all report exports to a branded format (logo + centered header +
  footer): shared ReportSheet (on-screen) and reportHeader (PDF). Budget vs
  Actuals and Bank Reconciliation PDFs now match the reference layout.
- Render financial reports inline (no preview pop-up).
- Budget Management mirrors Accounting Budgeting (staff-accessible) with SPA
  navigation; editable bills in the Accounting Bills page.
- Negative opening balances flow through to the GL and reports (allow negative
  input; keep non-zero on save; signed CSV import).
- Upload a per-account trial balance via CSV on Opening Balances.
- Board members: read-only RLS access to their association's accounting ledger;
  editable board-members panel on the association page; share vendor contacts
  with the board (toggle + directory section).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:29:31 -04:00
admin db20226d62 Movement of Equity GL-consistent; wire reconciliation checks R3/R5 (+R6/R9)
The Movement of Equity derived net income from sub-ledgers (calcNetIncome over
invoices/expenses/bills), while the P&L and Balance Sheet use the GL. With any
direct bank-categorized income/expense the two disagreed — Ashley Manor's SCE
was off from the Balance Sheet equity by 9,257.44. Rebuild Movement of Equity
from the GL (current-year earnings + GL equity balances) so all three statements
tie by construction.

Complete the §9 reconciliation matrix: R3 (P&L net income == raw-GL period net
income — guards the P&L builder) and R5 (Movement of Equity ending == Balance
Sheet total equity) are now computed by cross-checking the report builders against
the raw GL; checks render in numeric order. R6 (GL == TB/BS, satisfied by
construction) and R9 (direct vs indirect CFO, only indirect built) shown as N/A
for matrix completeness. Adds 5 reconcile unit tests (12 total).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 12:52:05 -04:00
admin 96de47496a Reconcile imported-GL companies: Bridgewater opening equity + scope R7/R8
Bridgewater's GL was imported as single-sided postings missing its opening fund
balance, leaving the trial balance off by 130,348.76 with an abnormal debit
equity balance. Record the gap as an Opening Fund Balance equity credit (migration
20260602150000); R1 and the Balance Sheet now tie out exactly.

A/R-A/P sub-ledger checks (R7/R8) only apply to platform-managed companies whose
invoices/bills post to the GL. Imported-GL companies (Bent Oak, Bridgewater) keep
their own AR/AP, so scope R7/R8 to gl_managed companies (new arApApplicable flag
on reconcile + gl_auto_post surfaced in useReportData). Every company now passes
the Reconciliation report.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 02:14:25 -04:00
admin f25a778230 PDF financial reports: add Comparative / Change / Change % columns
renderReportPdf now matches the on-screen comparison: when a comparison period
is active it renders Amount | Comparative | Change | Change % columns (was
Previous | Amount), switches to landscape for room, and underlines each numeric
column on totals. Applies to P&L, Cash Flow, Movement of Equity, and Balance
Sheet exports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 02:02:07 -04:00
admin 3d11980b8c Part B: Δ/Δ% columns, Balance Sheet comparison, Budget vs Actuals comparison
- StructuredTable now renders Comparative + Change + Change % columns when a
  comparison period is on (P&L, Cash Flow, Movement of Equity, Balance Sheet).
- Balance Sheet supports an as-of comparison period (prior as-of balances per
  account + totals); comparison toggle enabled for it (buildBalanceSheet takes
  prior dataset). Current Year Earnings stays independently computed.
- Budget vs Actuals: optional "Compare to" date range adds Compare + Δ-vs-Compare
  columns; actuals computation factored into a shared helper for both windows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 01:49:47 -04:00
admin 5383404bb0 Part B: Cash Flow comparison period + Budget vs Actuals proration
- Cash Flow Statement now supports a comparison period (prior-period/prior-year/
  custom): refactored into computeCashFlow() and renders the comparison column
  for Net Income, CFO/CFI/CFF, beginning/ending cash. Previously it ignored the
  compare toggle. (P&L and Movement of Equity already had comparison.)
- Budget vs Actuals: budget is now pro-rated to the selected actuals window
  (sum each budget period weighted by overlap with the range) instead of always
  using the full annual figure — honors monthly/quarterly/annual period_type.
  Noted in the on-screen card and the PDF header (B4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 01:22:35 -04:00
admin f74c61c9df Budget vs Actuals: custom actuals comparison date range
Add From/To date inputs to the Budget vs Actuals report (Accounting section
Reports) so actuals can be compared to the budget over any custom range,
independent of the page period preset. Defaults to the report period; drives
the actuals queries and CSV/PDF export labels.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:54:54 -04:00
admin 7a7435a8ee Cash Flow indirect method (R4) + reconciliation library & test suite
In the Accounting section Reports (AccountingReportsPage):
- Rebuild the Cash Flow Statement as an indirect-method report derived from the
  GL: Net Income + working-capital/non-cash movements, classified into
  CFO/CFI/CFF, with Beginning/Ending Cash. Ties to the change in Balance Sheet
  cash by construction (R4); surfaces an explicit residual row if it ever doesn't.
- Add R4 to the Reconciliation Checks report.
- Extract the reconciliation matrix into a pure, tested library
  (lib/reconcile.ts) and route the Reconciliation report through it.
- Add §10 synthetic-ledger vitest suite (lib/reconcile.test.ts): empty, single
  balanced entry, full period, partial settlement (open vs gross §1.5), and
  fault ledgers (single-sided → R1; payment-not-applied → R7). Verified R4 ties
  for Ashley Manor ($35,727.08 = ΔCash).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:07:16 -04:00
admin 03286f865a Conform reports to financial spec: A/R open-balance + reconciliation checks
Per the Financial Reports Master Spec:
- §1.5 A/R fix: invoice settlements now post to the GL (Dr Undeposited / Cr A/R
  from invoice.paid_amount); payments are the cash sub-ledger only and no longer
  separately credit A/R (avoids double-count). A/R control = open balance, so
  recon R7 passes for managed companies (Ashley Manor 39,248 -> 0). Bills already
  settled (R8 ok). Migration applied + backfilled managed companies.
- §9/§10: add a "Reconciliation Checks" report that surfaces R1/R2/R7/R8
  residuals (never plugged) so imbalances are visible — e.g. Bridgewater's
  imported GL is unbalanced (R1) and its sub-ledgers don't tie (R7/R8).

Imported companies (Bridgewater/Bent Oak) left untouched per decision; their
residuals now surface in the Reconciliation report.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 23:55:18 -04:00
admin a3a0b706a1 Board-member upload permission for documents & bids/quotes
Add a "Allow document & bid/quote uploads" toggle on board member profiles
(board_members.can_upload). When enabled, that board member can upload
association documents and create/manage bids & quotes for their association(s);
otherwise the board portal stays read-only for them.

- Migration (prod): board_members.can_upload column; tighten the documents
  insert + storage 'files' upload policies to require can_upload; add a
  bids_quotes board policy gated on can_upload.
- BoardMembersPage: permission switch (load/save).
- BoardAssociationContext: expose canUpload for the selected association.
- DocumentsPage: board upload gated by the flag (was always-on for board).
- BidsQuotesPage: permitted board members can add/manage bids (was hidden);
  board inserts target the board's association.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 23:33:09 -04:00