Rent Schedule Engine
Rent Schedule Engine
The rent schedule engine lets agents generate, preview, and manage rent payment schedules for tenancy terms. Schedules can be auto-calculated from term parameters or built manually entry by entry.
Overview
Each tenancy term can have a set of rent_schedule_payments — individual payment entries that together form the complete schedule. Each entry records:
- Amount (in pence, integer)
- Due date
- Period start / end
- Label (e.g. "Month 1", "Pro-rata (partial week)")
- Type flags:
isProRata,isCustom - Sort order for display
- Notes for agent annotations
UI Panel
The <RentSchedulePanel> component is embedded on the tenancy term detail page. It exposes three tabs:
Schedule Tab
Displays all saved payment entries in a table with:
| Column | Description |
|---|---|
| # | Sort order |
| Label | Entry name and optional note |
| Due Date | Formatted payment due date |
| Amount | Payment amount (£) |
| Running Total | Cumulative total at this row |
| Type | Standard, Pro-rata, or Custom badge |
| Actions | Remove individual entry |
A summary bar at the top shows the overall total and payment count. A Clear All button removes the entire schedule after confirmation.
If payments do not sum correctly against the expected total, a discrepancy indicator is shown.
Generate Tab
Auto-computes a schedule from the tenancy term's parameters.
Configurable options:
- Frequency: Monthly, Weekly, or Custom
- Due day: Day of the month/week the payment falls on
- Start date / End date: Override or inherit from the tenancy term
Preview before saving: The panel calls the preview procedure first so the agent can review the calculated entries before they are committed to the database. Generating a schedule replaces any existing one.
Custom Tab
Manually define individual payment entries.
- Add entries with amount, due date, label, and optional notes.
- Choose Replace mode to overwrite the existing schedule, or Append mode to add entries to it.
Calculation Logic
The generateRentSchedule() utility produces entries based on frequency:
Monthly
- Pro-rata first period — from the start date to the end of the first calendar month (if the start date is not the 1st).
- Full monthly entries — one entry per full calendar month.
- Pro-rata last period — from the start of the final partial month to the end date (if the end date is not the last day of the month).
Weekly
- Weekly rent is derived from the monthly figure:
monthlyRent × 12 ÷ 52. - Partial weeks at the start and end of the term are pro-rated.
Custom
- Returns an empty schedule. The agent is expected to populate entries manually using the Custom tab.
All amounts are stored and calculated as pence (integers) to avoid floating-point rounding errors.
Validation
validateScheduleTotal() sums all entry amounts and compares the result to the expected total. A discrepancy of up to £1 is tolerated to account for rounding across pro-rata periods.
tRPC API Reference
All procedures live under the rentSchedule router.
rentSchedule.preview
Stateless calculation — returns computed entries without persisting.
const result = await trpc.rentSchedule.preview.query({
tenancyTermId: string,
frequency: "monthly" | "weekly" | "custom",
startDate: string, // ISO date
endDate: string, // ISO date
monthlyRentPence: number,
dueDay?: number,
});
rentSchedule.generate
Computes and persists a schedule, replacing any existing entries.
await trpc.rentSchedule.generate.mutate({
tenancyTermId: string,
frequency: "monthly" | "weekly" | "custom",
startDate: string,
endDate: string,
monthlyRentPence: number,
dueDay?: number,
});
rentSchedule.list
Fetches saved entries sorted by sortOrder, along with a running total and entry count.
const { entries, totalPence, entryCount } =
await trpc.rentSchedule.list.query({ tenancyTermId: string });
rentSchedule.saveCustomEntries
Saves manually defined entries. Supports replace (overwrites) or append (adds to existing).
await trpc.rentSchedule.saveCustomEntries.mutate({
tenancyTermId: string,
mode: "replace" | "append",
entries: Array<{
label: string,
amountPence: number,
dueDate: string,
periodStart?: string,
periodEnd?: string,
notes?: string,
}>,
});
rentSchedule.deleteEntry
Removes a single payment entry by ID.
await trpc.rentSchedule.deleteEntry.mutate({ id: string });
rentSchedule.clearSchedule
Bulk-deletes all entries for a given tenancy term.
await trpc.rentSchedule.clearSchedule.mutate({ tenancyTermId: string });
rentSchedule.validate
Checks whether saved entries sum to the expected total (£1 tolerance).
const { isValid, discrepancyPence } =
await trpc.rentSchedule.validate.query({
tenancyTermId: string,
expectedTotalPence: number,
});
Audit Logging
All mutating procedures (generate, saveCustomEntries, deleteEntry, clearSchedule) emit audit log entries so changes to a rent schedule are fully traceable.