Skip to content

Business Rules

Each page in this section catalogues the rules that apply to one part of Formation — an entity (Schemes, Investment Events, Portfolios…) or a cross-cutting concern (Permissions, Unknown markers, Concurrent edits). Rules are described in plain language: what the system checks, when it checks it, and what you see when something doesn’t pass.

If you’re trying to find out why a form won’t save, or what counts as a “complete” record, or how the auto-rebalance on a portfolio sector breakdown works, this is the right section.

If you’re trying to find out how a rule is implemented (which file, which validator, which SQL function), each page ends with a Where this lives section linking the canonical source files. The Technical section covers the underlying patterns — controller layout, CQRS flow, validation framework — but not the rules themselves.

  • One page per entity: Schemes, Developments, Addresses, Companies, Investment Events, Occupier Events, Portfolios, Market Boundaries.
  • A Cross-cutting subsection for rules that apply to many entities at once — Unknown markers, Notes and Tags, Concurrent edits, Permissions, Completeness scoring, Units and display, Currency, Search matching.
  • For each rule: when it fires (typing, on save, after save), what fails, and what the user sees.
  • Wire-format details — JSON Patch path transforms, OData query semantics, REST headers. These live in Technical → API.
  • Implementation mechanics — how recursive validation walks navigation properties, how the OData store synchronises with the URL, how the audit interceptor wraps writes. The user-facing rule is documented here; the mechanism is documented under Technical.
  • The full lists behind summarised rules (e.g. the complete table of search-term synonyms, or every ScoredProperty weight). Where the list is long enough to rot quickly, each page summarises and links the source file.
  • Anything not yet enforced. This is a description of the system as it stands. Future or aspirational rules go in design docs, not here.

Every page follows the same skeleton so you can scan or compare:

  • Required fields — what you must provide for a record to be created or updated.
  • Field-level rules — constraints on individual fields (ranges, lengths, formats).
  • Cross-field rules — rules that involve two or more fields together (date orderings, currency couplings).
  • Conditional UI — what appears, disappears, or auto-fills as you edit.
  • On save — the checks the server runs after the form accepts your input.
  • After save — downstream effects (audit log entry, completeness recalc, cache invalidation).
  • Related rules — links to cross-cutting pages and to other entities that share a rule.
  • Where this lives — the canonical source files for the rules on the page.

Three conventions show up almost everywhere:

  • Unknown markers. Many optional fields can be marked Unknown rather than left blank — that’s a statement that the data is missing rather than absent. See Unknown and optional fields.
  • Concurrent edits. Save fails with a concurrency error if someone else has updated the same record since you loaded it. See Concurrent edits.
  • Save is only enabled once you’ve changed something. Edit forms detect unchanged inputs and disable the Save button.

Terms used throughout Business Rules pages. Linked entries point to their detailed pages.

  • Operator vs Owner — on a scheme, an operator runs the property (e.g. a hotel chain managing a hotel), an owner holds title. Most building types require at least one non-operator company; Hotel schemes are the exception.
  • Whole vs Share ownership — on an investment event, Whole means one party holds the entire interest, Share means the interest is split (typically across multiple investors). The Share % field on each party becomes visible whenever the role has at least one entry. See Investment Events.
  • Net vs Gross area — the same physical building reports a smaller net floor area (lettable space) and a larger gross area (including circulation, plant, etc.). Users pick a preferred basis under Settings; display components fall back automatically if the chosen basis isn’t available. See Units and display.
  • sqm / sqft — square metres / square feet. Conversion is handled by display components, not by the user.
  • psf / psm pa — per square foot / per square metre per annum. Rent units; the suffix pa distinguishes annual from monthly rent.
  • Local currency — for a market or scheme, the currency the deal was struck in. Conversions to a user’s preferred display currency happen via warehouse-supplied exchange rates; the local value is always available as a fallback.
  • Market boundary — a polygon that represents a market area (e.g. London City, Greater Manchester Office). Boundaries are hierarchical (a boundary can have a parent boundary) and addresses inside a boundary are auto-linked. See Market Boundaries.
  • Completeness score — a 0-100 number reflecting how much of a record is filled in. Calculated nightly per entity, weighted by [ScoredProperty] attributes on the model. See Completeness score.
  • Polymorphic collection — Notes, Tags, External Links — they attach to many different entity types via a single shared mechanism. See Notes and tags.
  • Verified / unverified — records that have passed manual data review carry a Verified flag. The list pages can default-filter to verified-only under Settings.

These rules are extracted from the code that enforces them. Each page’s Where this lives section points at the source file. When a rule changes, the corresponding page should be updated in the same change — the engineer touching the rule is the person best placed to do that. If you spot a discrepancy between the doc and the app, please raise it.