Multi-entity & FX
Consolidation and currency are where finance UIs quietly produce wrong numbers, because both involve summing things that look summable and aren't.
1. The consolidation model
type Entity = {
id: string; name: string; legalName: string;
functionalCurrency: string; // the currency it primarily operates in
parentId?: string;
ownershipPct: number; // of the immediate parent's interest
consolidationMethod: "full" | "equity" | "proportional" | "none";
fiscalCalendar: FiscalCalendar; // subsidiaries can differ from the parent
};
| Ownership | Method | Treatment |
|---|---|---|
| Control (presumed > 50% of votes; a VIE or IFRS 10 power-and-returns test can override) | Full consolidation | Line-by-line, with non-controlling interest for the minority share |
| 20–50% | Equity method | One line: investment in associate, moving with the share of profit |
| < 20% | Cost / fair value | An investment asset |
| Joint control | Equity method for a joint venture | IFRS 11 joint operations recognise their share of assets and liabilities; US GAAP allows proportionate consolidation only in narrow industries |
UI rules:
- The consolidation scope is always stated in the statement header. "Northwind Holdings Ltd. and subsidiaries" versus "Northwind Holdings Ltd. (standalone)" are different statements and must never be confused.
- Entity selection is explicit, never inferred from a user's default. A user who thinks they're looking at the group and is looking at one subsidiary is being misled.
- Non-controlling interest appears on both statements — as a component of equity on the balance sheet, and as an allocation of net income on the P&L. Omitting it overstates the parent's share.
- Subsidiaries with different fiscal year-ends require either aligned reporting or a stated adjustment. The UI flags the mismatch rather than silently summing.
2. Intercompany
Transactions between group entities must eliminate on consolidation. They are real for each entity and unreal for the group.
Entity A sells $100k of services to Entity B
─────────────────────────────────────────────
A: Dr IC receivable 100k / Cr Revenue 100k
B: Dr Expense 100k / Cr IC payable 100k
Group elimination: Dr Revenue 100k / Cr Expense 100k
Dr IC payable 100k / Cr IC receivable 100k
Group effect: zero
UI rules:
- The intercompany matrix must net to zero.
IntercompanyMatrixrenders an entity × entity grid of balances; every cell must have an equal and opposite counterpart. Non-zero cells are the exception queue, and they're the most common cause of a consolidation that doesn't tie. - Common breaks the UI must name: timing differences (one side booked in a later period), FX differences (each side translated at a different rate), and one-sided entries (the other entity never booked it). These have different fixes, so the matrix labels the break type rather than showing a bare difference.
- Unrealized profit in inventory — goods sold intercompany and still on hand — must be eliminated too. Flagged, not computed, because it requires knowing what's still in stock.
- Elimination entries are
source: "elimination"and exist only at the consolidated level. They never appear in an entity's standalone statements, and the UI makes that boundary visible.
3. Functional vs. presentation currency
The distinction that causes the most confusion, and the one that decides whether an FX difference hits the P&L or equity.
| Definition | |
|---|---|
| Transaction currency | What the invoice was denominated in |
| Functional currency | The currency of the entity's primary economic environment |
| Presentation currency | What the statements are shown in — IAS 21's term; ASC 830 calls it the reporting currency |
Two different processes, two different destinations:
| Remeasurement | Translation | |
|---|---|---|
| Converts | Transaction → functional | Functional → presentation |
| FX difference goes to | P&L (FX gain/loss — ASC 830-20-35-1) | Equity (CTA, in OCI — ASC 830-30-45-12) |
| Applies to | Monetary items held in a foreign currency | An entire subsidiary's statements |
This is not a detail. A EUR receivable held by a USD-functional entity produces a P&L gain or loss when rates move. That same entity's statements translated into a GBP-presentation group produce a CTA in OCI that bypasses the P&L until the foreign operation is sold or substantially liquidated, when it is reclassified to earnings. Confusing them misstates earnings.
4. Translation rates
| Item | Rate |
|---|---|
| Assets and liabilities | Closing rate at the balance sheet date |
| Income and expenses | Average rate for the period (or transaction-date rates if volatile) |
| Equity | Historical rate at the date of each contribution |
| Retained earnings | Rolled forward, not re-translated |
Because these use different rates, the translated balance sheet does not balance — and the plug is the cumulative translation adjustment, recorded in other comprehensive income.
UI rules:
- CTA is displayed as its own equity line, never buried. It is the arithmetic consequence of the method, and hiding it makes the balance sheet look inexplicable.
- Every translated statement shows the rates used — closing, average, and the source. A translated number without its rate is unverifiable.
- Average rate methodology is stated (simple average of daily, monthly weighted, transaction-date). Different methods give different results and comparability depends on knowing which.
- Hyperinflationary economies differ by framework: IFRS (IAS 29) restates for inflation, then translates; US GAAP (ASC 830) remeasures as if the reporting currency were functional, with differences in the P&L. The UI flags the jurisdiction and the framework rather than attempting it.
5. FX gain and loss
Three events, three rates, on a single foreign-currency invoice:
1. Invoice issued 1 Sep €10,000 @ 1.0850 = $10,850 → AR recorded
2. Period end 30 Sep €10,000 @ 1.0920 = $10,920 → unrealized gain $70
3. Payment received 12 Oct €10,000 @ 1.0790 = $10,790 → Sep unrealized gain reverses (−$70)
realized loss $60 vs. invoice rate
(October P&L −$130; cumulative −$60)
UI rules:
- Realized and unrealized are separate lines and never combined. Unrealized reverses; realized doesn't. Presenting one number destroys the reader's ability to tell what actually happened.
- The revaluation entry at period end is a system entry with the rate and source shown, reversing at the start of the next period.
- Only monetary items are revalued. Cash, receivables, payables, and debt — yes. Inventory, fixed assets, and prepaids — no, they stay at historical rate. Revaluing a non-monetary item is a common and material error, and the UI derives this from the account classification rather than trusting a flag.
- The rate source and timestamp are recorded on every conversion. A rate without provenance is not auditable.
6. Rate management
type ExchangeRate = {
from: string; to: string;
rate: number;
asOf: Date;
type: "spot" | "closing" | "average" | "historical" | "budget";
source: string; // "ECB" | "OANDA" | "manual"
enteredBy?: string; // manual rates need an actor
};
- Rate type is not optional. The same pair on the same date has a different closing rate and average rate, and using the wrong one is a silent misstatement.
- Manual rate overrides require a reason and an approver, and are visibly distinguished from sourced rates everywhere they're used.
- A missing rate is a blocking error, never a silent 1.0. Defaulting to parity is the worst possible failure mode here.
- Inverse rates are computed, never stored separately — storing both invites them to drift.
- Rounding: rates carry 5–6 significant figures; converted amounts round once, at the end, per the currency's minor unit.
7. Multi-entity UI rules
- Entity is a required dimension on every transaction.
- Every number states its entity scope and its currency. Both, always.
- Never sum across currencies without a stated conversion, with the rate, the type, and the as-of date reachable.
- A converted value is marked with
≈and its basis in a tooltip. - Entity switching is a full context change, so nothing animates and every cached figure is invalidated. A stale figure from the previous entity displayed for even one frame is a serious error.
- Consolidated and standalone views are visually distinct, not just differently labeled — the consolidated view carries a persistent scope chip in the header.
- A user with access to a subset of entities sees totals for that subset only, and the UI says so explicitly rather than presenting a partial total as a group total.
8. Component set
| Component | Purpose |
|---|---|
EntitySelector |
Explicit scope, with standalone/consolidated distinction |
ConsolidationView |
Entity columns + eliminations + group total |
IntercompanyMatrix |
Entity × entity, with break types named |
EliminationEntry |
Consolidation-level only, clearly scoped |
NciPanel |
Non-controlling interest on both statements |
TranslationSummary |
Rates used, by type, with sources |
CtaRollForward |
Opening CTA → movement → closing |
FxRevaluation |
Period-end revaluation preview and posting |
FxGainLossPanel |
Realized and unrealized, separated |
RateTable |
By pair, date, and type, with source and overrides flagged |