Design Principles
The ledger is the financial source of truth for every property, unit, and tenant in DoorStax. It is designed around three core principles:
Append-Only
Entries are never modified or deleted. New entries are always appended.
Locked Entries
Once written, a ledger entry is immutable. Its fields cannot be changed.
Compensating Adjustments
Corrections are made by adding new offsetting entries, not editing existing ones.
Entry Types
Every ledger entry has a type that categorizes the nature of the transaction.
| Type | Direction | Description |
|---|---|---|
| CHARGE | Debit (+) | Rent, late fees, or other charges owed by tenant |
| PAYMENT | Credit (-) | Payment received from tenant |
| REVERSAL | Credit (-) | Reversal of a previous charge (compensating entry) |
| ADJUSTMENT | Either | Manual correction — debit or credit as needed |
Running Balance Calculation
Each ledger entry stores a runningBalance field that is computed at write time. The balance for a lease is the sum of all entry amounts in chronological order:
runningBalance = previousBalance + amount
Where:
CHARGE → amount > 0 (increases balance owed)
PAYMENT → amount < 0 (decreases balance owed)
REVERSAL → amount < 0 (decreases balance owed)
ADJUSTMENT → amount ≷ 0 (either direction)A running balance of 0 means the tenant is fully paid up. A positive balance means the tenant owes money. A negative balance means a credit exists on the account.
LedgerEntry Field Reference
| Field | Type | Description |
|---|---|---|
| id | String | Unique identifier (cuid) |
| leaseId | String | Associated lease |
| unitId | String | Associated unit |
| tenantId | String? | Associated tenant (nullable for charges) |
| type | EntryType | CHARGE | PAYMENT | REVERSAL | ADJUSTMENT |
| amount | Decimal | Signed amount in cents |
| runningBalance | Decimal | Balance after this entry |
| description | String | Human-readable description |
| referenceId | String? | Links to a payment or charge record |
| referenceType | String? | Type of referenced record (payment, charge, etc.) |
| metadata | Json? | Arbitrary metadata |
| createdAt | DateTime | Timestamp of entry creation |
| createdById | String | User who created this entry |
| isLocked | Boolean | Always true after creation |
Example Flow
Here is a typical sequence for a monthly rent cycle:
Rent Charge Created
CHARGE of +$1,500.00 — running balance: $1,500.00
Tenant Payment Received
PAYMENT of -$1,500.00 — running balance: $0.00
Late Fee (if payment was late)
CHARGE of +$50.00 — running balance: $50.00
Late Fee Waived
REVERSAL of -$50.00 — running balance: $0.00
{
"id": "led_abc123",
"leaseId": "lea_xyz789",
"unitId": "unt_456",
"tenantId": "tnt_012",
"type": "PAYMENT",
"amount": -150000,
"runningBalance": 0,
"description": "Rent payment for January 2025",
"referenceId": "pay_def456",
"referenceType": "payment",
"createdAt": "2025-01-05T10:30:00.000Z",
"createdById": "usr_tnt012",
"isLocked": true
}Audit Trail
Every ledger entry is linked to the user who created it via createdById. When a payment triggers a ledger entry, the referenceId links back to the originating payment record, creating a complete audit chain.
Because entries are immutable, the full history of every financial event is preserved and can be audited at any time. Reversals and adjustments maintain explicit references to the entries they compensate.
Reconciliation
DoorStax runs an automated reconciliation cron job that:
- •Verifies running balances match the sum of all entries per lease
- •Cross-references ledger entries with payment processor records (Kadima)
- •Flags discrepancies for manual review
- •Generates reconciliation reports for accounting teams
Note: The reconciliation cron runs daily at 2:00 AM UTC. Discrepancies are surfaced in the admin dashboard and via email notifications to accountants.