Block Settings & Management Fee Configuration
Block Settings & Management Fee Configuration
Each block can now have a dedicated settings page that controls management fee structure, billing schedule, RMC/RTM company details, and service charge apportionment.
Navigate to a block and select Settings to access the page at:
/dashboard/blocks/{blockId}/settings
Authentication is required. Viewing settings requires org membership; saving changes requires admin access.
Tabs Overview
The settings page is split into three tabs.
1. Fees & Billing
Configure how and when your management fee is charged.
Fee Structure
| Fee Type | Description | Stored As |
|---|---|---|
| Fixed Annual | A fixed annual amount billed on the chosen schedule | Integer pence (e.g. £2,500.00 → 250000) |
| Percentage of Budget | A percentage of the total service charge budget | Basis points (e.g. 15% → 1500) |
Important — fee isolation: Per platform spec, management fee income must never be deposited into client money accounts. The settings page tracks fee configuration separately from block funds.
Billing Schedule
- Frequency — Monthly, quarterly, or annual.
- Financial Year Start — The calendar month the financial year begins (default: April). This drives billing period calculations.
Invoice Settings
| Field | Default | Description |
|---|---|---|
| Auto-generate invoices | Off | Automatically create a fee invoice at the start of each billing period |
| Days before due | 14 | How many days before the due date to send the invoice |
| Payment terms | 30 days | Days from invoice date until payment is due |
2. RMC / RTM
Store details for the Resident Management Company (RMC) or Right to Manage (RTM) company associated with this block.
| Field | Description |
|---|---|
| Company Name | Legal name of the RMC/RTM company |
| Company Number | Companies House registration number |
| Registered Address | Lines 1 & 2, city, postcode |
| Directors | JSON array of director objects |
Directors Format
Directors are stored as a JSON array. Each director object accepts:
[
{
"name": "Jane Smith",
"role": "Director",
"email": "jane@example.com"
}
]
3. Apportionment
Control how the service charge budget is split between units in the block.
Apportionment Method
| Method | Description |
|---|---|
| Equal | Each unit receives an equal share |
| By Floor Area | Shares are proportional to each unit's floor area |
| Custom | You set each unit's percentage manually |
Editing Percentages
The apportionment table lists every unit in the block with columns for unit number, floor, leaseholder name, and an editable percentage share.
- Percentages are stored internally as basis points (1% = 100 basis points; 100% = 10,000 basis points).
- A live total indicator shows the current sum and highlights any over- or under-allocation.
- Saving is blocked until the total equals exactly 100.00%.
Distribute Equally
Click Distribute Equally to automatically split 10,000 basis points across all units. Any rounding remainder is distributed one basis point at a time to the first units in the list.
Saving Apportionment Changes
- Adjust each unit's percentage in the table.
- Confirm the total reads 100.00%.
- Optionally enter a reason for the change (e.g. "Lease extension", "Remeasurement").
- Click Save Apportionment.
Each save creates an immutable, versioned record in the audit history.
Change History
Click History to open the apportionment change history panel. Each record shows:
- Version number and apportionment method in effect at the time
- Timestamp of the change
- Reason (if provided)
- A full snapshot of every unit's basis points at that version
History is read-only and cannot be modified or deleted.
Data Model
block_settings table
One record per block. Created on first save, updated on subsequent saves.
| Column | Type | Description |
|---|---|---|
management_fee_type | enum | fixed_annual or percentage_of_budget |
management_fee_fixed_amount_pence | integer | Annual fee in pence (fixed type only) |
management_fee_percentage_basis_points | integer | Fee as basis points (percentage type only) |
billing_schedule | enum | monthly, quarterly, or annual |
financial_year_start_month | integer | 1–12 |
auto_generate_invoices | boolean | Default false |
invoice_days_before_due | integer | Default 14 |
payment_terms_days | integer | Default 30 |
rmc_company_name | text | Optional |
rmc_company_number | text | Optional |
rmc_address_line_1 | text | Optional |
rmc_address_line_2 | text | Optional |
rmc_city | text | Optional |
rmc_postcode | text | Optional |
rmc_directors | text | JSON string; array of {name, role, email} |
apportionment_method | enum | equal, by_floor_area, or custom |
notes | text | Free-text notes |
apportionment_change_history table
Immutable append-only table. One record per apportionment save.
| Column | Type | Description |
|---|---|---|
block_id | text | Block the change belongs to |
changed_by | text | User ID who made the change |
apportionment_method | text | Method in effect at time of change |
snapshot | text | JSON array of {unitId, unitNumber, basisPoints} for all units |
reason | text | Optional reason text |
version | integer | Auto-incremented per block |
Both tables are protected by Row Level Security (RLS) for multi-tenant isolation.
API Reference (tRPC)
blockSettings.getByBlockId
Fetch the settings record for a block.
const settings = await trpc.blockSettings.getByBlockId.query({ blockId });
// Returns the settings object, or null if not yet configured.
blockSettings.upsert
Create or update block settings. Admin only.
await trpc.blockSettings.upsert.mutate({
blockId: "blk_abc123",
managementFeeType: "fixed_annual",
managementFeeFixedAmountPence: 250000, // £2,500.00
billingSchedule: "quarterly",
financialYearStartMonth: 4,
autoGenerateInvoices: true,
invoiceDaysBeforeDue: 14,
paymentTermsDays: 30,
apportionmentMethod: "custom",
});
All fields except blockId are optional on update — only supplied fields are changed.
blockSettings.getApportionment
Fetch apportionment data for all units in a block.
const data = await trpc.blockSettings.getApportionment.query({ blockId });
// data.units — array of units with apportionmentBasisPoints
// data.total — sum of all basis points
// data.isComplete — true if total === 10000
blockSettings.updateApportionment
Atomically update all unit apportionments. Validates the sum equals exactly 10,000 basis points (100%). Creates a versioned history record. Admin only.
await trpc.blockSettings.updateApportionment.mutate({
blockId: "blk_abc123",
entries: [
{ unitId: "unit_1", basisPoints: 5000 }, // 50%
{ unitId: "unit_2", basisPoints: 5000 }, // 50%
],
reason: "Initial configuration",
});
// Returns { version: 1 }
blockSettings.apportionmentHistory
Fetch versioned change history.
const history = await trpc.blockSettings.apportionmentHistory.query({
blockId: "blk_abc123",
limit: 10,
});
// Returns array of history records, newest first.
Compliance Notes
- Fee isolation — Management fee income is configured here but must never be deposited into a client money account. The platform enforces this separation.
- Apportionment audit trail — Every change to unit apportionments creates an immutable versioned record. History cannot be edited or deleted.
- Audit logging — All
upsertandupdateApportionmentcalls are recorded vialogAudit().