Building Fabric: Section-by-Section Replacement Forecasting
Building Fabric: Section-by-Section Replacement Forecasting
Available from: v0.13.16
Building Fabric assets now support per-section replacement forecasting. Instead of modelling a whole-asset replacement as a single event, each discrete section of a building fabric asset (e.g. North elevation render, Flat roof — main block, Guttering — east wing) can carry its own install date, lifespan estimate, and auto-calculated replacement year.
This means partial replacement events — where one section is replaced while others remain intact — can be planned and tracked accurately without creating duplicate top-level asset records.
How It Works
Per-Section Fields
When editing a Building Fabric asset, each section now includes three additional fields:
| Field | Description |
|---|---|
| Section Install / Last Replaced Date | The date this specific section was installed or last replaced. Accepts a calendar date. |
| Section Lifespan (years) | The expected service life for this section in years. |
| Projected Replacement Year | Automatically calculated from the install date + lifespan. Displayed as a read-only badge but stored for timeline and reporting use. |
These fields are stored in the existing fabricSections JSON column — no database changes are required and existing section data is unaffected.
Auto-Calculation
The projected replacement year is recalculated automatically whenever the install date or lifespan changes:
Projected Replacement Year = Year(installDate) + estimatedLifespanYears
For example, a section installed in March 2010 with a 25-year lifespan will show a projected replacement year of 2035.
Section Replacement Timeline
The Building Fabric read view now includes a Section Replacement Timeline — a vertical chronological view of all scheduled section replacements.
Colour Coding
| Status | Colour | Condition |
|---|---|---|
| Overdue | 🔴 Red | Projected year ≤ current year |
| Due soon | 🟡 Amber | Projected year within 5 years |
| Future | 🔵 Blue / default | Projected year > 5 years away |
Each year group shows:
- The projected replacement year and a status label (Overdue, This Year)
- Aggregated cost for all sections due that year
- Individual section cards with condition rating and estimated cost
Sections that have no install date or lifespan are listed separately at the bottom of the timeline with a prompt to add the missing data.
Section Read View
Each section card in the read view now shows additional metadata at a glance:
- Installed date
- Lifespan (e.g. 25-year lifespan)
- Replace YYYY badge — colour-coded by urgency
- Section lifespan progress bar — fills from green to red as the section ages through its estimated lifespan
Statistics Panel
The statistics panel above the section list now includes a fourth card: Scheduled Replacements, showing X / Y sections — how many of the total sections have a projected replacement year recorded.
Reserve Fund Integration
The per-section replacement year and cost data feeds directly into the reserve fund projection. Section-level capex is aggregated by replacement year and surfaced in the 10-year capital expenditure forecast.
The fabricSectionsSummary API endpoint provides this aggregated data programmatically. See the API Reference below.
fabricSectionsSummary Endpoint
The asset.fabricSectionsSummary tRPC query returns aggregated section-level data for reserve fund and reporting use.
Input
{
blockId?: string // Optional. Scopes results to a single block.
}
Output
{
totalBuildingFabricAssets: number;
totalSections: number;
scheduledSections: number; // Sections with a projected replacement year
totalSectionCostPence: number;
avgSectionCondition: number | null; // 1–5 scale, rounded to 1 decimal place
poorSections: number; // Sections rated condition 1 or 2
overdueSectionCount: number; // Replacement year ≤ current year
dueSoonSectionCount: number; // Replacement year within 5 years
sectionCostsByYear: Record<number, number>; // { [year]: totalCostPence }
sections: Array<{
assetId: string;
assetName: string;
blockId: string;
section: string;
condition: number | null;
estimatedCostPence: number | null;
projectedReplacementYear: number | null;
installDate: string | null;
estimatedLifespanYears: number | null;
}>;
}
Usage Example
const summary = await trpc.asset.fabricSectionsSummary.query({
blockId: "block_abc123",
});
// Cost aggregated by replacement year — suitable for reserve fund charts
console.log(summary.sectionCostsByYear);
// e.g. { 2027: 1250000, 2031: 4800000, 2035: 720000 }
// Sections needing immediate attention
console.log(summary.overdueSectionCount);
console.log(summary.dueSoonSectionCount);
Data Model
Section data is stored as a JSON array in the fabricSections column of the assets table. Each section object may now include:
{
"section": "North elevation render",
"condition": 3,
"estimatedCostPence": 1250000,
"quantityM2": 320.5,
"installDate": "2012-06-01",
"estimatedLifespanYears": 25,
"projectedReplacementYear": 2037,
"notes": "Repointed 2019, minor cracking noted south corner"
}
All new fields are optional. Existing section records without these fields continue to work as before.
Tips
- Partial replacements: When one section is replaced but others are not, update only that section's
installDate. The projected replacement year recalculates automatically. The other sections retain their original dates. - Unscheduled sections: Any section without an install date and lifespan appears in the timeline's unscheduled list. Work through these to ensure complete reserve fund coverage.
- Overdue sections: Sections where the projected replacement year has already passed are flagged in red throughout the UI. Review and update these to confirm whether replacement has occurred or is still pending.