Skip to main content
All Docs
FeaturesagentOS Block ManagerUpdated April 12, 2026

Block Settings — Management Fee & Apportionment

Block Settings — Management Fee & Apportionment

Every block can now be individually configured through a dedicated settings page. Settings are grouped into three areas: management fee structure, RMC/RTM company details, and service charge apportionment.

Accessing Block Settings

Open a block from the Blocks list, then click the Settings button in the block detail view. This navigates to /dashboard/blocks/{blockId}/settings.

Permission required: Reading settings is available to all authenticated users. Saving any changes requires the admin role.


Tab 1 — Management Fee

Configures how the managing agent's fee is calculated and invoiced for this block.

Fee Type

OptionDescription
Fixed Annual FeeA fixed amount in pounds charged once per year.
Percentage of Service Charge BudgetA percentage of the total service charge budget, entered as a decimal (e.g. 15.00 for 15%). Stored internally as basis points.

Management fees are always kept isolated from client and block funds in compliance with the "Management Fee Income Isolation" requirement.

Billing Schedule

  • Invoice Frequency — Monthly, Quarterly, or Annual.
  • Financial Year Start — The month the financial year begins. Defaults to April (standard UK financial year).
  • Auto-generate invoices — When enabled, management fee invoices are automatically created on the configured schedule.

Tab 2 — RMC/RTM Details

Stores company information for blocks that have a Resident Management Company (RMC) or Right to Manage (RTM) company.

Company Details

FieldDescription
Company NameFull legal name (e.g. Maple Court (RMC) Ltd)
Company NumberCompanies House registration number

Registered Address

Address line 1, address line 2, city, and postcode.

Directors

Directors can be added individually with the following fields:

FieldRequiredDescription
NameYesFull legal name
EmailNoContact email address
RoleNoe.g. Director, Secretary

Directors with an empty name are ignored on save.


Tab 3 — Apportionment

Defines how service charge costs are divided between units in the block.

Apportionment Methods

MethodDescription
Equal SplitEach unit pays an identical share. Basis points are distributed evenly, with any rounding remainder assigned to the first unit.
By Floor AreaProportional to each unit's floor area. Calculated server-side from unit floor area records.
Custom PercentagesManually specify each unit's share using the basis point editor.

Basis Points

Apportionment is stored and edited in basis points, where 10,000 = 100%. The UI displays the equivalent percentage alongside each input.

  • The total must equal exactly 10,000 basis points (100%) before a custom apportionment can be saved.
  • A running total row shows the current sum and any shortfall or excess in real time.
  • The Distribute Equally button resets all units to an equal split at any time.

Saving Apportionment Changes

Before saving, an optional reason field can be filled in. This reason is stored with the version record for audit purposes (e.g. Updated following lease extension on Flat 3).

For equal and floor area methods, the server calculates and applies the distribution. For custom, the per-unit basis point values entered in the editor are submitted directly.


Apportionment Version History

Every saved apportionment change creates an immutable versioned snapshot. Versions are numbered sequentially per block.

Each version record contains:

FieldDescription
Version numberSequential integer per block
MethodThe apportionment method used
Effective dateWhen the schedule takes effect
ReasonOptional free-text reason for the change
SnapshotFull per-unit breakdown (unit number + basis points) at time of change

The version history panel in the UI is collapsed by default. Click to expand it and view all past versions. Each version has an expandable snapshot showing every unit's percentage at that point in time.

Version records are never updated after creation. They form a permanent audit trail suitable for financial compliance review.


Data Model Reference

block_settings table

One record per block. All fields except id, orgId, blockId, and autoGenerateInvoices are nullable — settings can be partially configured.

block_settings
├── id                                 text (PK)
├── org_id                             text
├── block_id                           text (unique)
├── management_fee_type                enum: fixed_annual | percentage_of_budget
├── management_fee_amount_pence        integer (pence)
├── management_fee_percentage_basis_points  integer (basis points)
├── billing_schedule                   enum: monthly | quarterly | annual
├── auto_generate_invoices             boolean (default: false)
├── financial_year_start_month         integer 1–12 (default: 4)
├── rmc_company_name                   text
├── rmc_company_number                 text
├── rmc_address_line_1                 text
├── rmc_address_line_2                 text
├── rmc_city                           text
├── rmc_postcode                       text
├── rmc_directors                      text (JSON: [{name, email, role}])
├── apportionment_method               enum: equal | floor_area | custom (default: custom)
├── notes                              text
├── created_at                         timestamp
└── updated_at                         timestamp

apportionment_versions table

Append-only. Never updated after creation.

apportionment_versions
├── id              text (PK)
├── org_id          text
├── block_id        text
├── version         integer
├── method          enum: equal | floor_area | custom
├── snapshot        text (JSON: [{unitId, unitNumber, basisPoints}])
├── reason          text
├── created_by      text
├── effective_date  timestamp
└── created_at      timestamp

API Reference (tRPC)

blockSettings.get

Returns the settings record for a block, or null if none exists yet.

trpc.blockSettings.get.useQuery({ blockId: string })

blockSettings.upsert

Creates or updates block settings. All fields except blockId are optional.

trpc.blockSettings.upsert.useMutation()
// Input:
{
  blockId: string;
  managementFeeType?: "fixed_annual" | "percentage_of_budget" | null;
  managementFeeAmountPence?: number | null;
  managementFeePercentageBasisPoints?: number | null;
  billingSchedule?: "monthly" | "quarterly" | "annual" | null;
  autoGenerateInvoices?: boolean;
  financialYearStartMonth?: number;
  rmcCompanyName?: string | null;
  rmcCompanyNumber?: string | null;
  rmcAddressLine1?: string | null;
  rmcAddressLine2?: string | null;
  rmcCity?: string | null;
  rmcPostcode?: string | null;
  rmcDirectors?: { name: string; email?: string; role?: string }[];
}

Requires admin role.

blockSettings.updateApportionment

Updates the apportionment schedule. For custom method, all unitApportionments must sum to exactly 10,000 basis points or the mutation will fail with a validation error.

trpc.blockSettings.updateApportionment.useMutation()
// Input:
{
  blockId: string;
  method: "equal" | "floor_area" | "custom";
  unitApportionments?: { unitId: string; basisPoints: number }[]; // required when method is "custom"
  reason?: string;
}

On success: creates an apportionment_versions snapshot and updates all affected unit records.

Requires admin role.

blockSettings.listApportionmentVersions

Returns all version history records for a block, ordered newest first.

trpc.blockSettings.listApportionmentVersions.useQuery({ blockId: string })

blockSettings.getCurrentApportionment

Returns current live apportionment from unit records, with a completion indicator.

trpc.blockSettings.getCurrentApportionment.useQuery({ blockId: string })
// Returns:
{
  units: { id, unitNumber, leaseholderName, apportionmentBasisPoints }[];
  totalBasisPoints: number;
  isComplete: boolean; // true when totalBasisPoints === 10000
  unitCount: number;
}