Skip to content

Units and display

Behind every size and every rent in Formation are two decisions: what basis to display (net or gross), and what unit to display it in (square metres vs square feet, per-foot vs per-metre, monthly vs annual rent). Both are driven by the user’s preferences under Settings, not by anything on the record itself — which means the same scheme renders different sizes for two users with different preferences, while the underlying data is identical.

This page describes how those decisions are made and what falls back to what when the preferred value isn’t available.

Every size on the system is stored in two versions: a Net value (lettable / usable area) and a Gross value (including circulation, plant, and other non-lettable space). Gross is always the larger figure for any given physical building.

Your Settings → Format → Area Calculation preference determines which one shows by default:

  • Net — Net values appear first; Gross values are still available as a secondary column.
  • Gross — Gross values appear first; Net is the secondary column.

This affects every list page (the Total Size column on Schemes, on Developments, on Investment Events) and every detail page.

When a record’s preferred basis is missing for some reason, the display falls back through a 3-step chain:

  1. The preferred basis (Net or Gross).
  2. The other basis.
  3. The local value — the raw size as entered, in the unit the user typed it in.

So a development with only a local size and no net/gross still shows a number; it just shows whichever it has. This typically only happens during the create flow before the SizeTriplet interceptor has filled in the other two.

Your Settings → Format → Measurement System preference picks between metric (square metres, sqm) and imperial (square feet, sqft). The system stores every size in canonical square metres regardless of preference; the conversion to imperial happens at display time.

The conversion ratio comes from the SizeUnit table itself (each unit row carries a RatioToMetric and RatioToImperial field). If the unit table hasn’t loaded yet — say, on the very first render after a hard reload — the system falls back to a hard-coded 10.7639 (the standard sqm-to-sqft conversion) so the UI doesn’t show a wrong-looking number.

When a field needs a unit and you haven’t picked one, the system picks a default based on your preferences:

  • Imperial + NetSqft Net.
  • Imperial + GrossSqft Gross.
  • Metric + NetSqm Net.
  • Metric + GrossSqm Gross.

If no exact match is found in the SizeUnit table for your combination, the system falls back through three further steps:

  1. Any unit with the right system (sqft / sqm) and the right basis (net / gross).
  2. Any unit with the right system, ignoring basis.
  3. Empty — no default selected, you’ll have to pick one yourself.

Rent isn’t just a number — it’s a number with a currency, a per-unit qualifier (per-foot, per-metre, per-month), and a period (per annum, per month). The format is driven by a DisplayFormat template on the SizeUnit row.

A unit’s template looks like £{value} psf pa. The system replaces {value} with the rent figure formatted by Intl.NumberFormat using the user’s locale, then renders the result.

When the user’s currency preference differs from the unit’s template currency, the system substitutes the currency symbol at display time — so a unit defined as £{value} psf pa renders to $1,234 psf pa for a USD-preference user.

Column headers that show rent (e.g. the Rent column on Occupier Events lists) include the time period as a suffix:

  • Annual (the default) → pa (per annum). Header reads e.g. Rent (£ psqm pa).
  • Monthlypm. Header reads e.g. Rent (£ psqm pm).

The time-period preference also lives under Settings → Format. Changing it rewrites the column header silently — the underlying values aren’t converted (annual rent stays annual rent in the data), but the display of those values updates.

When a value needs converting between bases (e.g. to compute Net from Local for a hotel use), the system looks up a scaler that says “how many sqm of net floor does a sqm of local-as-typed produce?” The scaler lookup goes through a precedence chain:

  1. Unit + Building Type + Country — the most specific scaler.
  2. Unit + Country — drop the building type.
  3. Unit + Building Type — drop the country.
  4. Unit only — drop both.
  5. Global — fall back to a default 1:1.

The first match in this chain wins. This lets us encode “in France, hotel net floor is 0.85 of gross” without affecting every other country.

A size or rent field that’s been deliberately set to unknown (via the Unknown checkbox where available) displays as “Unknown” in detail and list views rather than as a missing value. See Unknown and optional fields.

  • Frontend units module: src/services/web/src/lib/units.ts
  • Display components: src/services/web/src/lib/components/displays/ (AreaValue.svelte, AreaLabel.svelte, RentValue.svelte)
  • Preferences: src/services/web/src/lib/preferences/
  • Server-side scaler precedence: src/services/api/app/app.api/Services/Conversion/SizeTripletConversionService.cs
  • DisplayFormat templates: stored on the SizeUnit table; surfaced through the EnumService cache