Leases
Overview
A Lease ties a Tenant to a Unit (or, for single-unit properties, a Property directly) for a stated term at a stated rent, with a stated deposit. The Lease record drives three automations:
- Rent schedule generation — on create/update the server materialises a RentSchedule row for each period in the term at the current
baseRent. - Scheduled escalations — pre-planned bumps written into the lease (e.g. “+3% on each anniversary”) apply automatically via the nightly cron.
- Monthly rent invoice generation — on the 1st of each month the cron turns the next due RentSchedule row into an actual Invoice.
Each lease has its own detail page with three tabs: Rent Schedule, Rent History, and Documents.
Header
The page header carries:
- Tenant name (click to open the tenant)
- Property / Unit (click to open either)
- Term:
startDate → endDate(or “Month-to-month” if no end date) baseRent+ frequency (monthly / semi-monthly / bi-weekly / weekly)depositamount + held-in-account hint- Status badge: Pending / Active / Ended / Terminated
Header actions: Record deposit intake, Compute interest, Deposit disposition, Edit, Terminate.
Tabs
Rent Schedule
Shows every future + current RentSchedule row on the lease. Columns: Period (start → end), Amount, Due date, Status (Pending / Invoiced / Skipped), Invoice (link when issued).
Rows with status = pending and invoiceId = null are repriced automatically when a RentChange or Escalation applies, so the figures always match the current baseRent.
Runs the rent-invoice generator for this lease on demand. Useful for retries when the monthly cron skipped a row (JE post failed, feature flag off, etc.). Idempotent — already-invoiced rows are left alone.
Row action. Marks a pending period as skipped (e.g. rent-free month). No invoice will be generated. Reversible while the row remains pending.
Rent History
Chronological log of every rent movement:
- Applied Rent changes (mid-lease notices — see Rent changes guide)
- Applied Escalations (pre-planned bumps written into the lease at signing)
Each entry shows date, source (change / escalation), previous rent, new rent, delta, and who applied it.
Documents
Generic attachments panel — lease PDF, addenda, signed renewals, condition reports. Accepted categories for this tab: lease, inspection, notice, other.
Max 10 MB per file; MIME allowlist enforced server-side (PDF, common image types, docx/xlsx).
Escalations
Escalations are pre-planned bumps stored inline on the lease (not as separate RentChange records). Types:
| Type | value means | Example |
|---|---|---|
| fixed-amount | Absolute dollars added | value: 50 → +$50 |
| percentage | Percent increase | value: 3 → +3% |
| manual | New absolute rent | value: 2100 → baseRent becomes $2,100 |
| cpi-linked | Treated as percentage in v1; operator enters realised CPI | value: 2.8 → +2.8% |
The daily cron sweeps leases with any escalations.status === 'scheduled' and effectiveDate <= today, applies them oldest-first (so multiple past-due escalations compound correctly), flips the entry to applied, and reprices unissued RentSchedule rows.
Deposit flows
Three buttons in the header move the deposit through its lifecycle. Each posts journal entries via the shared createAndPostJournalEntry helper — idempotent on (sourceType, sourceId).
Record deposit intake
Posts on move-in:
Dr Cash (chosen cash account)Cr Tenant deposits held (workspace default)Both lines tagged with leaseId. Safe to re-run — idempotent on lease:<id>:intake.
- Click Record deposit intake in the header.
- Confirm Amount (defaults to
lease.deposit). - Pick the Cash account the deposit is going into.
- Optional: override the Tenant deposits held account.
- Click Record & post JE.
Compute interest
Pure helper — computes the annual interest at the supplied rate and shows the number. No JE is posted in v1; the user posts manually once they’ve reviewed the accrual.
- Click Compute interest in the header.
- Enter the rate % (e.g. Ontario statutory rate, landlord policy).
- Review the calculated amount.
- Post a manual JE: Dr Interest expense, Cr Tenant deposits held.
Deposit disposition
Run at move-out to allocate the deposit between deductions (cleaning, damages, unpaid rent) and the tenant refund. Posts a single JE:
For each deduction: Dr Tenant deposits held Cr <target account the user picked>
If refund > 0: Dr Tenant deposits held Cr CashAll lines carry leaseId so the Property P&L picks up the writedowns.
- Click Deposit disposition in the header.
- Add deduction rows: description, amount, target account (expense, income, or AR account).
- Enter the Refund amount remaining.
- If refund > 0, pick the Cash account the refund is drawn from.
- Click Post disposition. The whole JE posts atomically.
Overdue disposition widget: the Dashboard flags any lease ended > 10 days ago with deposit > 0 and no disposition JE — it uses endDate when set, falling back to updatedAt so month-to-month roll-offs without explicit end dates still get caught.
Actions
Create a lease
- From the Tenant detail or Property/Unit detail, click Add lease.
- Pick the Tenant and Unit (for multi-unit properties) — or Property for single-unit.
- Enter Start date, optional End date (leave blank for month-to-month), baseRent, and Frequency.
- Optional: add Escalations (type + value + effectiveDate).
- Enter Deposit amount (recorded separately via the intake button above).
- Click Save. A RentSchedule row is materialised for each period until
endDate(or 24 months out for month-to-month).
Terminate a lease
- Click Terminate in the header.
- Enter the Termination date.
- Click Confirm. Status flips to
terminated; future pending RentSchedule rows after the termination date are skipped; any escalations withscheduledstatus past the termination date are voided.
Terminated leases are preserved for audit; the Rent History tab still shows their applied changes.