About Dealsplit

Revenue recognition for real estate teams, simplified.

Last deployed June 1, 2026 at 5:28 PM EST

Changelog

Changes on branch `extract_correct`

Unreleased

0.6.0 - 2026-06-01

Added
  • Brokerage Setting per Agency: Agencies now have a brokerage selection (Compass, eXp, or KW) in Settings → Agency. This is the switch that turns on format-specific journal handling — Compass retention consolidation, eXp-specific fee mappings, KW disbursement detection. Without it set, deals fall back to generic per-party math regardless of what the PDF actually is
  • Risk Management Fee as Its Own GL Bucket: eXp's Risk Management Fee (E&O) is no longer lumped into "Other Deductions". Agencies can map it to a dedicated account in their chart of accounts or fold it into Brokerage Transaction Fees — set it under Settings → GL Account Mappings
  • Brokerage Cap Credits / Refunds: When the brokerage refunds part of company-dollar back to the office (KW post-cap, referral company-dollar reversals), the refund now books as a credit on the same Commissions to Office account that outgoing brokerage fees debit. The 12-month cap total reflects gross movement on that account (per accountant guidance), so the office's annual cap balance stays accurate. Detected automatically when the disbursement table shows the brokerage as a payee with negative net
  • Live Processing Indicator on Dashboard: Deals being extracted show a subtle "alive" animation while the AI is working, with a separate reduced-motion fallback that respects system accessibility settings
  • Auto-Linked vs User-Edited Corrections (Admin): The extraction-corrections view distinguishes name mappings the system auto-linked (longer roster name to its canonical short form) from corrections a user actually retyped. Real friction is no longer hidden under canonical-name normalization
  • Enterprise Subscription Overrides (Admin): Sales-onboarded enterprise customers can have a custom deal limit, custom feature set, custom plan label, and custom monthly price set directly on their subscription from the admin user detail page — no Stripe price object required. The resolved feature state shows inline so admins can see exactly what an account has access to
  • Per-Request Context on Error Reports: Production error events now carry which user, page, route, and LiveView triggered them — eliminating the "which user hit this?" guesswork on support tickets
  • Agent Performance in Insights: The Insights dashboard adds a per-agent performance breakdown with a drill-down detail page for each agent, alongside office KPIs (net revenue, deals closed) that now show the trend versus the prior period
  • Branded Account Emails: Sign-in magic links, email confirmation, and password/email-change notices are now branded HTML messages matching the app, with a plain-text fallback for every mail client
Changed
  • Compass Team-Deal Journals — Retention as One Line: Compass statements show GCI, then brokerage retention (typically 15% gross less a 5% team incentive = 10% net), then per-agent splits. Previously the journal pro-rated that retention across every party. Now it consolidates onto the principal as a single line — matching what every Compass agency books today and what their P&Ls expect (the brokerage retention recorded as one event, not pro-rated across each party). Both presentations balance; the new one matches the reading
  • Journal Entries Show the Actual Brokerage: Broker-tied components (broker fees, transaction fees, review fees, royalties, stock purchase, risk management) now carry the deal's actual brokerage and market center on the journal entry instead of a generic "EXP" placeholder. Names match the actual entity on every deal
  • Compass Concession Handling — No More Double-Count: Compass statements often show both a deal-level concession rollup AND per-agent concession rows; both are the same money reported two ways. The extractor now treats per-agent allocations as the source of truth and the deal-level rollup as reporting only — eliminating a class of double-count bugs on team deals
  • eXp Referral Detection Widened: Now identifies four structural patterns instead of one — "SVC FEE" headers, "Other Transaction Type" headers, all-zero standard fields, and documents missing a Sale/Listing Commission Base section. Fewer eXp referrals end up shoe-horned into listing or buyer
  • Compass Per-Agent Shape Recovery: When the model emits an agent's commission as their net check (instead of the gross share + retention pair the document actually shows), the extractor now derives the missing broker_fee from gross × split × retention so per-agent math stays uniform across all parties before the journal pass
  • Insights Numbers Match the Journal: The "net to office" figure on Insights now applies the same concession dedupe rule as the journal entry. Deal-level concession rollups no longer double-count against per-agent concession rows. What Insights shows is what the journal books — invariant, not coincidence
  • QBO Posting Errors Show Useful Messages: When QBO rejects a posting (auth expired, duplicate document number, account doesn't exist), users see a categorized message describing the actual problem instead of a raw exception inspect string. Retry decisions are also now driven by error category, not substring guesswork
  • eXp Deal-Level Referrals from the Statement Summary: When an eXp statement records a referral only in its summary totals rather than as a party line, the extractor now reads that printed summary line to recover the deal-level referral — and only from an explicit printed referral, so it never invents one to explain an unrelated gap
  • Team-Deal Agent Splits Book Consistently: Non-principal agent commission splits are booked from the gross allocation printed on the statement (falling back to the net check only when the gross is missing), so the office journal treats every team deal's agent splits the same way
  • Admin User Detail Reorganized: The admin user detail page is restructured into balanced zones — a metrics strip and Deals-by-Status up top, then condensed account and integrations cards — for a faster read on a single customer
Fixed
  • KW Disbursement Misfire on Compass and Solo Deals: The safety check that flags KW DA-format documents collapsing into the principal's net was firing on Compass deals and any solo-principal deal — neither was the actual problem case. Compass solo deals where "Outstanding Credits" legitimately equals the principal net are no longer flagged. Now only fires on the case it was designed for: KW multi-party deals
  • Agent Deductions Stay on the Agent's Card: In the deal wizard, a non-principal agent's brokerage deductions now stay on that agent's party card instead of appearing under the office's Deal Expenses. Agent costs are the agent's, not the office's — so office expense is no longer overstated on team deals
Tech Debt / Refactor
  • AI extractor broken up — the 1500-LOC monolith is now a thin facade over Client, Parser, Prompt, Normalizer (with BankDeposit / ComponentSemantics / Gross / SplitComponents sub-modules), Compass, PartyMatcher, ExtractionContext, and BrokerageProfile. Single-purpose modules, easier to test, calculator stays out of the LLM path
  • Context writes route through Repo.update_owned / Repo.delete_owned instead of hand-rolled user_id == current_user_id checks scattered across contexts. Single ownership boundary on the write side
  • QBO errors tagged at source — Qbo.Error.classify_string/1 and 14 substring patterns deleted; posting failures and worker retry classification both route through Revrec.Qbo.Error
  • ExtractionMemory correction attrs centralized through Correction.attrs/4 — replaces ad-hoc map building across 9 correction types
  • Extraction quality harness runs on every mix test (~0.1s, no exclude flag). Writes tmp/extraction_quality_report.md bucketing the 61-fixture corpus into Structural / External knowledge / Extraction quality / Possibly-extractable
  • Procedure for adding a new canonical GL key documented end-to-end (7 steps, worked example) in lib/revrec/deals/CLAUDE.md; AI extractor architecture documented in lib/revrec/ai_extractor/CLAUDE.md
Upgrade and Migration
  • Migration add_brokerage_name_to_agencies adds the brokerage field to agencies
  • Migration add_subscription_overrides adds custom deal-limit / features / label / monthly-price columns to subscriptions for enterprise overrides
  • Migration add_source_to_extraction_corrections adds an auto/user discriminator to correction rows (no backfill — old rows remain NULL)
  • Migration drop_who_pays_from_deal_parties removes an unused column
  • Migrations add_extraction_normalized_json_to_deals and add_pipeline_state_to_extraction_corrections add storage for normalized extraction output and correction pipeline state (no backfill required)
  • Action required: Set the brokerage on each agency in Settings → Agency. Without it, Compass retention consolidation, eXp-specific behavior, and brokerage-aware journal names all fall back to generic handling — even on a clearly-Compass or clearly-eXp document

0.5.0 - 2026-04-15

Added
  • Multi-PDF Dashboard Upload: Upload up to 10 PDFs at once from the dashboard. Each file gets its own progress indicator, extractions run concurrently, and single-file uploads auto-navigate to the wizard when extraction completes
  • Brokerage-Aware Extraction: The extractor now recognizes whether a statement is from KW, Compass, or eXp and applies format-specific rules — Company Dollar / CAP / Royalty lines on KW disbursement forms, Outstanding Credits and concession handling on Compass, Capped Fee / Review Fee / BPOL on eXp, plus correct treatment of agent split percentages before brokerage retention
  • Consistent Compass Math: After extraction, Compass split amounts are deterministically recalculated from the gross, split %, and fee amount on the statement — so re-running the same PDF no longer produces slightly different numbers due to model rounding
  • Referral-Only Deals: New "referral" deal side for deals where the office only received a referral fee with no property representation (common on eXp). Previously these had to be shoe-horned into listing/buyer/both
  • Extraction Corrections Analytics (Admin): When a user edits extraction output at deal approval, the specific corrections are logged — missed parties, wrong sides, component additions, agent-name mappings, and so on. Admins can see the most common correction patterns at /admin/extraction-corrections to prioritize which statement formats need prompt attention
  • Admin Infinite Scroll: Dashboard activity feed and per-user deal lists now load additional rows as you scroll, replacing paged navigation. Emails and deal addresses are clickable links (the separate "View" column is gone)
  • Searchable Vendor Dropdown: Vendor-name matching in the deal wizard uses a searchable select with keyboard navigation — much easier to find the right vendor in long lists
  • Custom 400 Error Page: Branded error page for malformed requests, with server-side logging that distinguishes bot probing from real client bugs
  • More Granular Fee Components: Transaction fees, review fees, and concessions are now their own component types with dedicated GL mappings (previously bundled into generic buckets). Transaction-coordinator names are captured as payees on their processing fees
Changed
  • GPT-5.4 Extraction Engine: Upgraded AI extraction from GPT-4o to GPT-5.4 with native PDF upload — PDFs are sent directly to the model instead of converting to images, improving accuracy especially on Compass statements and eXp notifications
  • Strict Schema Enforcement: Extraction output is now constrained by a strict JSON schema at the token level, eliminating malformed or missing fields
  • Better Bank Deposit Detection: Per-format bank-deposit strategy — KW pulls from the disbursement payee list, Compass prefers "Outstanding Credits" with a fee-amount fallback, eXp derives the net from the principal section. Twelve previously-failing cases now resolve correctly
  • Compass Statement Extraction: Further improvements to split handling, concession capture, multi-agent split parsing, and explicit exclusion of brokerage-direct admin fees from gross commission
  • Consistent Principal Handling: Principal-agent detection, GL account lookups, and display formatting consolidated into shared helpers so the wizard, journal preview, QBO, and Sheets stay in lockstep
Fixed
  • Quality-of-Life UX: Journal entry rendering, wizard scroll behavior, duplicate flash messages on upload failure, double-navigation during multi-file selection, and assorted small polish throughout the dashboard and wizard
  • Billing Quota on Multi-Upload: Per-file billing check now accounts for in-flight uploads, so users can't exceed their plan's deal quota by uploading multiple files at once
  • Orphan Deal Cleanup: Deals created during Drive ingestion are cleaned up if the background job fails to enqueue, preventing stranded rows. Storage cleanup failures are now logged
  • Upload Error Surfacing: Local storage upload failures become flash messages instead of crash pages
  • Decimal Precision Throughout: Monetary amounts are parsed and stored as exact decimals end-to-end. Floating-point math has been removed from QBO amount formatting, deal reset, and fee component validation — floating-point rounding in accounting software produces real bugs
  • Franchise Fee GL Mapping: Franchise fees and franchise taxes now map to separate GL accounts instead of sharing one
Security
  • Force HTTPS in Production: force_ssl enabled in prod with secure cookies. Health check endpoints excluded from the SSL redirect so Fly's internal checks still work over HTTP
  • Changeset Trust Boundary: Enforced split between user-editable and server-controlled fields on deal schemas — form submissions can no longer accidentally write to fields like ownership, status, or calculated values
Tech Debt / Refactor
  • Centralized GL account keys, principal-agent checks, and display formatting helpers
  • Unified Drive polling and dashboard upload onto the same ingest worker flow
  • New ExtractionMemory context with a Correction schema — capture-only analytics layer for extraction quality
  • Upload rate limit aligned with the dashboard uploader's max entries
  • .worktrees/ ignored at the repo level to support isolated feature development
  • docs/ops/ runbook consolidates operational documentation; MCP server architecture plan added
Upgrade and Migration
  • Migration create_extraction_corrections adds the extraction_corrections table for correction-pattern analytics
  • No manual operator steps required

0.4.0 - 2026-03-28

Added
  • Company Dollar / Broker Fee Extraction: AI extraction now recognizes broker fee variants — "Company Dollar", "Company Commission", "Commissions to Office", "CAP"/"Pre-Cap"/"Post-Cap", "Broker Fee", "Office Fee", "Franchise Fee". Treated as negative amounts (money flowing back to brokerage)
  • Compass Statement Handling: Extraction handles Compass split structures (e.g., 85% commission + 5% incentive = 90% to agent), auto-calculates full GCI from partial, and extracts net company commission as negative broker_fee
  • Searchable Select Hook: JS hook for agent matching dropdowns with keyboard navigation and filtering
  • Google Picker Hook: JS hook for Google Drive spreadsheet selection
  • Dimensional Tags: Selectable QBO class/division tags on deals, with auto-defaulting from agent configuration
  • Sitemap Controller: SEO sitemap endpoint
Changed
  • Google Integration Rework: Switched from broad folder access to per-file Google Picker with drive.file scope (non-sensitive, no CASA audit required). ConfigLive now separates Drive and Sheets connection flows
  • Component Polish: Prettier UI components with improved styling and selectable CSS classes throughout the app
  • Config Page Redesign: Better section organization and layout for the configuration page
  • Wizard Sticky Layout: Journal entry preview and PDF viewer now pin below the running tally header instead of overlapping it — both stay visible while deal content scrolls
  • Billing & Pricing: Billing banner and plan display improvements
  • QBO Token Refresh: Client reloads agency from DB before refreshing to avoid consuming stale single-use tokens
Fixed
  • Production discrepancies: Fixed feature flag evaluation and GL account mapping consistency in production
  • Stale GL mapping keys: Migration fixes stale GL mapping key references
  • GCI Stability: Adding non-principal agents no longer mutates gross commission
  • PDF Storage: Fixed upload controller PDF storage path handling
  • QBO Error Handling: Improved error categorization (retryable vs non-retryable)
Tech Debt / Refactor
  • Refactored deal wizard step names and component organization
  • Account key mapper improvements with new test coverage
Upgrade and Migration
  • Migration fix_stale_gl_mapping_keys corrects stale GL mapping key references

0.3.0 - 2026-03-10

Added
  • Admin Dashboard: Real-time business metrics at /admin — deal counts by status, posting success rates, destination breakdown (QBO/Sheets), recent activity feed with email filtering, and "Needs Attention" alerts for failed postings and stuck jobs. Auto-refreshes every 30 seconds
  • Office Insights Dashboard: User-facing analytics at /insights with period filters (month/quarter/year/all time) — office KPIs (GCI, net to office, deal count, avg deal size), agent-level breakdown, and trend indicators with % change vs previous period
  • Stripe Billing Integration: Full subscription lifecycle — Free (2 deals lifetime), Light (3/month, Sheets only), Pro (20/month, QBO + Sheets + Drive), Elite (200/month, all features + Insights). Feature flags gate deal creation and posting per plan
  • Google Drive Auto-Import: Automatic PDF ingestion from Google Drive — polling for new files, dedup via unique constraint, download→S3→deal→extract pipeline, PubSub broadcasts for real-time progress. Gated to Pro/Elite plans
  • Onboarding Product Tour: Driver.js-powered guided walkthrough on first login — branded step definitions targeting nav links and upload zone. Tracks completion via onboarded_at on user record
  • Cloudflare Turnstile CAPTCHA: Integrated on login and registration forms with auto-refresh on failed verification
  • Rate Limiting: Hammer ETS backend with IP-based and user-based rate checks, plug for controller pipelines, admin metrics tracking
  • Deal PubSub: Real-time updates for deal status changes (wizard posting progress) and dashboard list refresh on deal creation/update/deletion
  • Referral Fee Extraction: AI extraction now captures referral fee components with payee_name field — handles "Outside Referral" deductions across multiple agents contributing to the same referral partner
  • Golden Path Test Cases: Additional real-deal test fixtures for extraction validation
  • robots.txt and sitemap.xml: SEO infrastructure for search engine crawling
  • Google Analytics: GA measurement integration in app.js
Changed
  • Landing Page Redesign: New marketing site with pricing tiers, testimonials (embedded at compile time for release compatibility), and updated footer
  • Admin Routes: Double auth protection (plug + on_mount) at /admin with role-based access (:admin, :superuser)
  • Billing enforcement disabled in dev/test: Billing.enabled?/0 feature flag defaults false outside production
  • Swoosh email configuration: Updated for production delivery
Fixed
  • Referral fee amounts: Now correctly extracted with payee names and mapped to journal entries
  • Dimension handling: Fixed dimension tag application on deal entries
  • QBO token refresh: Client reloads agency from DB before refresh to avoid consuming stale single-use tokens
  • Running tally display: Various wizard UI fixes post-0.2.0
Tech Debt / Refactor
  • Revrec.Admin context module with cross-tenant queries (skip_user_id: true) for batch user/deal/subscription lookups
  • Revrec.Billing context with Features module as single source of truth for plan feature matrix
  • Revrec.Drive context with Client, SyncedFile schema, and folder polling pipeline
  • Revrec.Deals.Insights module for aggregation queries and KPI calculations
  • RevrecWeb.RateLimit helper and RateLimitPlug for rate limiting infrastructure
  • ADR-0005: DealSplit feature design documentation
Upgrade and Migration
  • Migration adds payee_name to fee_components, last_login_at to users, onboarded_at to users
  • Migration creates subscriptions table for Stripe billing
  • Migration adds Google Drive fields to agencies and creates drive_synced_files table
  • Migration renames plan tiers for billing consistency

0.2.0 - 2026-02-03

Added
  • Google Sheets: Added "Gross to Office" column for principal agent's gross commission
  • Google Sheets: Added Agent 3 Name and Agent 3 Split columns (now supports 3 non-principal agents)
  • Google Sheets: Agent overflow warning when deal has more agents than available sheet slots
  • QBO: Auto-create vendors when posting journal entries - agents without a QBO vendor ID get matched to existing vendors via fuzzy name matching, or created as new vendors
  • QBO: Qbo.VendorSync module with ensure_vendors_for_deal/2 for bulk vendor sync and sync_agent/2 for single-agent sync
  • QBO: Vendor sync summary included in posting audit logs (matched/created/skipped/errors counts)
  • Initialized tk ticket system for project task tracking
Changed
  • Google Sheets: Agent splits now show GROSS commission (base commission component) instead of NET (fee_amount) to match journal entries
  • Google Sheets: Agents now sorted by gross commission amount (highest first) instead of insertion order
  • Google Sheets: Principal agent separated from non-principal agents - principal goes to "Gross to Office", others fill Agent 1/2/3 slots in order of commission
  • QBO Poster: Now runs vendor sync before posting journal entries - preloads deal_parties with agents
  • Row formatter now preloads agent association to detect principal status via agent.role == :principal
Fixed
  • Google Sheets: Agent split amounts now match journal entry "Commissions Paid Out" values (gross, not net)
  • Google Sheets: Consistent ordering between UI display and sheet output
  • Deal Wizard: Running tally no longer overlaps the Progress sidebar
  • Deal Wizard: Journal Entry Preview now shows full account names on hover (tooltip)
  • Dark Mode: Fixed overly bright chips and badges throughout the app (status badges, fee component tags, dimension tags, role badges)
Tech Debt / Refactor
  • Extracted partition_agents/1 helper to cleanly separate principal from non-principal agents
  • Extracted get_gross_commission/1 helper to calculate base commission from fee components
  • Added Qbo.Client.query_vendors/1 and create_vendor/2 for vendor API operations
  • New Qbo.VendorSync module encapsulates fuzzy name matching (via NameMatcher) and vendor lifecycle
Upgrade and Migration
  • Users must add "Gross to Office" column to their Google Sheets and refresh mappings
  • Users may add "Agent 3 Name" and "Agent 3 Split" columns for 3-agent deals

0.1.0 - 2026-01-11

Added
  • Deal Wizard Redesign: Real-time accounting workbench with running tally showing Gross Commission, Agent Splits, Expenses, and Net to Office as you edit
  • Edit Tracking System: Visual badges (AI/Edited/User) show what came from extraction vs. user corrections
  • Collapsible Review Sections: Extraction Summary, Agent Matching, Deal Knowledge, Revenue Configuration, and Dimensional Tags organized by correction category
  • Golden Path Integration Tests: End-to-end test framework comparing AI extraction + corrections against expected journal entries for real deals
  • New account types: broker_review_fee, charitable_contributions, stock_purchase (equity account)
  • Brokerage name field on deals for NAME dimension in journal entries
  • About page: Accessible via header link, displays changelog with semantic styling
Changed
  • Wizard flow reduced from 5 to 4 steps: Upload and Extract merged into single step with auto-extraction
  • Journal entries now use deal-side-specific accounts: Listing deals → listing_income/agent_listing_splits, Buyer deals → buyer_income/agent_buyer_splits, etc.
  • Always per-agent entries: Each agent gets separate expense line (no more aggregation mode)
  • Consistent entry ordering: Revenue (credits) → Agent splits → Fee components → Referral fees → Undeposited Funds
Fixed
  • Principal handling: Principal commissions correctly stay with office, not recorded as expenses
Removed
  • Zero-balance placeholder entries removed from journal output
  • Simple/aggregated agent expense mode removed
  • Separate Extract step removed from wizard flow
Tech Debt / Refactor
  • FeeComponentEditor extracted as reusable LiveComponent
  • Edit state computation decoupled into EditTracker module
  • Running tally computed from deal state, not stored
Upgrade and Migration
  • Migration adds brokerage_name column to deals table

Built with care for real estate professionals who deserve better tools for managing their business.