Overview
The Accounts Receivable system manages the complete resident billing lifecycle: assessment generation, invoice delivery, payment receipt, late fee enforcement, payment plan management, and aging analysis. Every financial operation in this lifecycle flows through the JournalEngine with full enforcement guard evaluation.
The ARService and ARAgingService together provide the operational and analytical layers of AR management.
Invoice Generation
Assessment Scheduling
The generate_assessments method creates invoices from assessment schedules for all applicable units. Key controls:
- Per-unit duplicate checking: Before creating an invoice, the service checks for existing invoices matching the same unit, schedule, and period. This prevents double-billing on re-runs and automatically handles new units added after initial generation.
- Voided invoice handling: Previously voided invoices are excluded from duplicate checking, allowing regeneration when needed.
- Schedule tracking: The schedule's
last_generated_dateandlast_generated_periodare updated for reporting.
Journal Entry Creation
Every invoice creates a journal entry through JournalEngine:
DR: Accounts Receivable (assessment amount)
CR: Income Account (assessment amount)
This records revenue at invoice creation (accrual accounting). The entry includes subledger references linking to both the invoice and unit for drill-down capability.
Payment Processing
Payment Receipt
The receive_payment method records incoming payments with mandatory controls:
- Stripe fee linkage: When a payment originates from Stripe (
stripe_payment_intentis set), the receipt must link to the sourcePaymentobject. This is enforced at the method level -- calling with a Stripe intent but no source payment raises aValueError.
Payment Application
The apply_payment method supports three allocation strategies:
| Strategy | Behavior |
|---|---|
| FIFO (default) | Apply to oldest outstanding invoices first |
| Specific | Apply to explicitly specified invoices with specified amounts |
| Proportional | Apply proportionally across all outstanding invoices |
Defense-in-Depth Fee Verification
Before any Stripe payment can be applied to invoices, two conditions are enforced:
- Fee reconciliation check: If
fee_reconciliation_failedisTrueon the source payment, application is permanently blocked until manual review. - Fee confirmation check: The source payment must have
fee_confirmed=True, meaning the actual Stripe fee has been retrieved from the Balance Transaction API.
These checks are unskippable -- they evaluate the FK on the receipt, not an optional parameter.
Payment Journal Entry
DR: Cash/Bank Account (net amount received)
DR: Bank Charges (Stripe processing fee, if applicable)
CR: Accounts Receivable (gross invoice amount)
This three-line entry properly separates the fee from the payment, ensuring accurate expense tracking.
Late Fee Enforcement
The calculate_and_apply_late_fees method follows a priority hierarchy for fee configuration:
- HOAConfig settings (HOA-level):
enable_late_fee,late_fee_amount,late_invoice_day - AssessmentSchedule settings (schedule-level):
late_fee_enabled,late_fee_grace_days,calculate_late_fee() - System defaults: $25 flat fee, 15-day grace period
Late fees are applied only once per invoice (late_fee_applied flag prevents duplication). Each late fee creates its own journal entry:
DR: Accounts Receivable
CR: Late Fee Income (account 4040)
A UnitLedger entry of type LATE_FEE is also created for the unit's statement.
Payment Plans
For delinquent accounts, the service supports structured payment arrangements:
- Plan creation: Links multiple outstanding invoices into a single payment plan with configurable monthly amounts, number of payments, start date, payment day, optional down payment, and interest rate.
- Default detection: The
check_payment_plan_defaultsmethod monitors active plans for missed scheduled payments and transitions plans toDEFAULTEDstatus when tolerance is exceeded.
Aging Analysis
The ARAgingService provides institutional-standard aging analysis:
Standard Aging Buckets
| Bucket | Days Past Due |
|---|---|
| Current | Not yet due (due_date >= as_of_date) |
| 1-30 Days | 1-30 days past due |
| 31-60 Days | 31-60 days past due |
| 61-90 Days | 61-90 days past due |
| Over 90 Days | 91+ days past due |
Summary Report
The get_ar_aging_summary method produces:
- Bucket totals and percentages
- Per-unit aging detail with owner names
- Top 10 delinquent units ranked by total amount
- Severely delinquent list (units with balances in 90+ bucket)
Collection Effectiveness Metrics
The get_collection_effectiveness method calculates for any fiscal year:
- Collection rate: Total payments / total charges
- Days Sales Outstanding (DSO): Ending AR balance / average daily charges
- Net change: Ending balance vs. beginning balance
Aging Trend Analysis
The get_ar_aging_trend method provides month-over-month aging snapshots for up to 12 months, using stored ARSnapshot records when available or reconstructing from invoice data for historical periods.
AR Snapshots
The generate_ar_snapshot method creates point-in-time AR records including:
- Bucket totals and percentages
- Delinquent unit counts and delinquency rate
- Average days delinquent (weighted by amount)
- Per-unit detail with owner information
Unit Ledger
Every AR event (charge, payment, late fee) creates a UnitLedger entry with a running balance. The get_statement_data method dynamically rebuilds statements from invoice and payment records, ensuring accuracy even if ledger entries are missing.
CARI Integration
AR lifecycle metrics — delinquency rates, collection effectiveness, and aging distributions — feed into the CARI Financial Health and Payment Behavior sub-scores. Delinquency rate serves as both an L5 eligibility rule signal and a CARI scoring input, creating consistent treatment across enforcement and external reporting. Collection effectiveness and Days Sales Outstanding trends are weighted inputs to the Financial Health component.
For published methodology and component weights, see CARI Methodology and Scoring Framework.
How CommunityPay Enforces This
- All invoice and payment journal entries flow through JournalEngine with guard evaluation
- Per-unit duplicate checking prevents double-billing for the same assessment period
- Stripe payments require fee_confirmed=True before journal posting (defense-in-depth)
- Institutional-standard aging buckets: Current, 1-30, 31-60, 61-90, 90+ days