Consolidation
A group of entities reports as one: the parent and its subsidiaries, each in its own currency and books, are translated into the presentation currency, added up, and what they owe and sell each other is eliminated. The design is ADR-0026; the controls are in the control matrix (section "Consolidation", CN1 to CN14).
Slices 1 to 3 of four are built (slice 3 without the opening position of an existing group). What exists:
- the group structure: groups, member versions, and their approval by a second person;
- the framework data (
ifrs,fer,hgb) with the verification gate: all three run, each rule cited from its standard (Swiss GAAP FER and HGB since 2026-10-12, ADR-0075); - rates from data-vNext (the ECB reference rates, and only those);
- the translation to the reporting currency (IAS 21.39 to 21.41) and the roll-forward of the translation reserve that proves it;
- aggregation by report line and the group balance sheet and profit and loss;
- the intragroup eliminations of balances (E1), loans (E2), income and expense (E3) and dividends (E6), read from what the ledgers say about each other through the intercompany view (intercompany), with the exceptions when the two sides do not agree (slice 2, and E2 and E6 with intercompany slice 2);
- the capital consolidation (slice 3, ADR-0051): the parent's investment against the subsidiary's equity at acquisition with goodwill or a bargain purchase or a negative difference as the framework treats it (E4), the non-controlling interests and their share of what the subsidiary earned since (E5), goodwill amortisation where the framework's policy requires it (E7), each entry with its basis;
- the group close checklist (slice 2), which can now be ready.
What does not exist yet: the opening position of a member consolidated after its acquisition, purchase
price allocation (fair-value adjustments), ownership changes and disposals (each a blocking exception,
never a guess); manual adjustments, finalising a run, verify, the reopen guard and the worksheet export
(slice 4).
Everything is a REST operation first (/v1, tag "consolidation" in the
generated reference); the web UI (Finance, Consolidation) calls the same
operations.
The group structure
POST /v1/consolidation-groups creates a group: a parent entity, the reporting currency and the
framework. A human does it: the policy action approve_group_structure denies agents (403 policy_denied). The group is written once; a later change of parent, currency or framework is an
approved structure change and belongs to a later slice. In this version the reporting currency is the
parent's functional currency (the parent is carried over unchanged; translating the parent too needs a
starting point it does not have). The group's code may be left out: it is then made from the name
(Demo Group is DEMO-GROUP, -2 when taken), as the New group form does while you type; the
form's Suggest with AI proposes the framework and the goodwill treatment, and saves nothing.
A subsidiary enters through a member version: POST /v1/consolidation-groups/{group}/members with
the ownership share, the control assessment (IFRS 10.5 to 10.7, Art. 963 para. 2 OR: why the parent
controls it, mandatory text), when control was acquired and from when the entity is consolidated.
- By default the request waits for a second person, who sees in the approval's
summarythe group, its parent, framework and reporting currency, the subsidiary with its name and currency and the version as proposed (including the control assessment) (status: pending_approval, an approval request of the actionapprove_group_structure);POST /v1/approvals/{approval_id}/approveby a person with theapprovescope who is not the requester writes the version. A one-person organization allowsapprove_group_structurein its approval policy; the version is then in force at once. A key the system issued belongs to no known person: it may request, never approve. - Versions are append-only and exist only once approved (
group_member); a change is a new version (version2,effective_fromnot before the one it replaces). A run takes the version in force at its date and records it. - A version can correct the one before it (ADR-0073;
corrects_versionon the version, shown on the structure screen and in the approval): a version that takes effect on the same date replaces the one before entirely (a share or a date typed wrong), and a version that keeps the share and the acquisition date but states other purchase data (cost, net assets at acquisition, investment account) replaces that data from the acquisition on, whatever date it takes effect from. So a subsidiary first approved without its purchase data is completed by asking for a new version with them. The corrected version stays in the history; a run records which version's purchase data it used (purchase_data_version). No correction once a final run used the version (409 member_version_closed). - The subsidiary's functional currency must be one the rate source publishes: otherwise
409 rate_source_missingnames the currency, at the request and not at the first run. - Only direct subsidiaries of the parent are supported; the parent is not a member.
Frameworks and the verification gate
The framework is a choice of the group and it is data: seed/consolidation_frameworks.yaml holds
the group's report-line vocabulary (with the labels of the statements in en, de, fr and it) and, per
framework, the switches where frameworks differ (the goodwill policies, the useful life of goodwill, what
a negative difference becomes, the presentation of the translation reserve, the statement set), every
row with its source. The mechanics are the same for every framework.
A framework runs only when its sources have been read: verified: true needs verified_by,
verified_on and sources_read, and no row may still say "to verify". A run under a framework that is
not verified is refused (422 framework_unverified, "group statements under ... are not available yet:
the rules of the standard have not been checked against its text"), one that is not built too (422 framework_not_implemented); nothing is stored. Today all three run:
ifrs |
fer (Swiss GAAP FER 30, 2022) |
hgb (§§ 290 to 315e HGB, DRS 23, DRS 25) |
|
|---|---|---|---|
Goodwill policies (goodwill_policy) |
impairment_only |
offset_equity (default) or amortise |
amortise |
Useful life (goodwill_years) |
none | 1 to 20 years, 5 when left empty (30/16); also for offset_equity, for the notes |
1 to 20 years, 10 when left empty (§ 253 para. 3) |
| A negative difference | a gain (IFRS 3.34) | offset against equity, or a liability released over five years (30/17) | the "Unterschiedsbetrag aus der Kapitalkonsolidierung" after equity, kept, with a warning (§ 301 para. 3, § 309 para. 2) |
| Translation reserve | other comprehensive income | equity (30/24) | "Eigenkapitaldifferenz aus Währungsumrechnung" (§ 308a) |
| Non-controlling interests | proportionate share | proportionate share (30/10) | "Nicht beherrschende Anteile", their share of the equity (§ 307) |
The translation is the same under all three (closing rate, historical rate for equity, monthly
averages for the profit and loss: IAS 21.39 to 21.41, FER 30/24, § 308a HGB). The resolved switches and
their sources, with who verified them, are copied into every run (policy), so a later change of the
seed or of the group cannot reinterpret an old snapshot.
How the paragraphs were checked. The ifrs rows rest on the paragraph numbers of the ADR, checked
by the project lead from knowledge on 2026-10-03. The fer and hgb rows were checked on 2026-10-12 by
an AI assistant against search extracts of Swiss GAAP FER 30 (2022, free on fer.ch) and its summaries
by PwC, KPMG and OBT, the statute at gesetze-im-internet.de and DRS 23 and 25; the full pages could
not be opened from the build environment. No licensed or full text has been read by a person: the
founder reads them before the first final run (backlog B-78).
What a run reads
POST /v1/consolidation-groups/{group}/runs with {"as_of": "2026-09-30"} (a month end, because
the rates are calendar months) makes a draft run in one transaction and never edits an earlier one.
The ledger is read without the year-end closing entries (source close; ADR-0023), posted entries
only, by entry date, whatever the entity's fiscal calendar. A closing entry would move the year's
profit and loss into equity on one date and, translated at that date's rate, replace the monthly average
rates of IAS 21.40. Without it the profit and loss accounts hold the cumulative result, which the
translation splits by month. The appropriation of the result (source appropriation) is an ordinary
equity movement and is read like any other. A ledger with and without the closing entries therefore gives
the same inputs digest, statements and reserve (CN5).
The group year is the calendar year of the date: the profit and loss of a run is 1 January to its date, whatever fiscal year an entity keeps.
Rates: the ECB, and only the ECB
Rates are units of the reporting currency per unit of the entity's functional currency, from
data-vNext's ECB reference rates: the month's closing rate (last fixing), the month's average
(the mean of the fixings) and the ECB rate on a date. A rate data-vNext does not serve (a currency
the ECB does not publish, a month with incomplete fixings or one that has not ended yet) is not
invented and no manual rate is accepted: it is the blocking exception rate_missing (named by
currency and month), the entity is left out of the run and the run says so. Every rate used is stored
with its source, its fixing dates and the data-vNext dataset version, so the run can be checked against
the same pinned data (GET /v1/consolidation-runs/{id}/rates).
Translation (IAS 21.39 to 21.41)
For a subsidiary with functional currency F, consolidated from the date T0, at the group date T:
| Item | Rate |
|---|---|
| Assets and liabilities at T | closing rate of the month of T |
| Income and expenses of the group year | each month's movement at that month's average rate, summed (IAS 21.40) |
| Equity at T0 | the ECB rate on T0 (the historical rate) |
| Equity movements after T0 (a contribution, a dividend, an appropriation) | the ECB rate on the entry date |
| Results of earlier years, and what was earned before T0 | one line: the result at T0 at the historical rate and each later month at its average rate |
Translation reserve (bs.equity.cta) |
the balancing amount of the translated trial balance (IAS 21.41) |
Every translated amount is rounded half-even to the minor unit of the reporting currency, so the translated trial balance balances exactly. An entity in the reporting currency (the parent, a CHF subsidiary) is carried over unchanged: no rates, no reserve. The reserve starts at zero at T0; an opening position with a carried reserve (an existing group) is a later slice.
The roll-forward proves the reserve
The reserve is not only a plug. For each subsidiary the run also computes
CTA(T) - CTA(T0) = NA(T0) x (R_T - R_T0) + sum over months of P x (R_T - A) + sum over equity movements of D x (R_T - R_k)
(NA net assets in F, P the profit of a month, A the month's average rate, R_T the closing rate, R_k the
rate of an equity movement) and compares it with the plug (GET .../cta) exactly, with no tolerance.
Each translated amount is rounded to the minor unit; the run keeps the sum of those rounding residuals
(rounded less exact, signed like the amount: rounding) and the comparison is
plug - roll-forward (unrounded) - rounding = 0
to the last digit, in a Decimal context wide enough to hold every product (60 digits). A difference of one
unit in the last place is not rounding but a defect, and is the blocking exception cta_unreconciled.
The roll-forward's own terms are shown rounded for reading, so a displayed sum can differ from the
displayed total by a cent; the check is made on the unrounded figures. In the worked example every
product is exact, rounding is zero and the difference is zero.
The worked example (the ADR's numbers)
CH01 (CHF) is the parent; DE01 (EUR) holds EUR 1,000,000 of share capital since 1 January 2026 and earns EUR 120,000 over January to September (EUR 20,000 in each of three months, EUR 10,000 in each of six). EUR 1 = CHF 0.9400 on 2 January (T0), the closing rate of September is 0.9350 and every month's average is 0.9380. The parent holds CHF 2,000,000 of capital, CHF 800,000 invested in DE01 and CHF 50,000 of revenue.
| DE01 | EUR | Rate | CHF |
|---|---|---|---|
| Cash (asset) | 1,120,000 | 0.9350 closing | 1,047,200.00 |
| Share capital (equity) | 1,000,000 | 0.9400 historical | 940,000.00 |
| Revenue, year to date | 120,000 | 0.9380 average, month by month | 112,560.00 |
| Translation reserve (balancing) | -5,360.00 | ||
| Roll-forward | 1,000,000 x (0.9350 - 0.9400) + 120,000 x (0.9350 - 0.9380) | -5,000.00 - 360.00 = -5,360.00 |
Group balance sheet before eliminations: cash 2,297,200.00 (1,250,000 + 1,047,200) and the investment
800,000.00 make assets 3,097,200.00; capital 2,940,000.00, translation reserve -5,360.00 and the
result of the year 162,560.00 (112,560 + 50,000) make equity 3,097,200.00. The two entities hold
nothing towards each other, so nothing is eliminated. The investment (800,000 in the parent's assets)
and DE01's equity are both still in the totals: setting one against the other is the elimination of
slice 3. tests/erp/features/consolidation/test_consolidation.py and tests/unit/test_consolidation_* run these numbers.
Aggregation and the statements
Each entity's translated trial balance is added line by line on account.report_line, the vocabulary
the charts share (the lines of both chart templates, the generic "other" lines and the lines
consolidation adds). The consolidated lines equal the sum of the translated trial balances, so the group
trial balance balances. A line outside the vocabulary, or one whose kind (asset, liability, equity,
revenue, expense) is not the kind of the account on it, is the blocking exception
unmapped_report_line: it is still added, nothing is dropped.
GET .../balance-sheet and GET .../profit-and-loss are built from the stored lines of the run,
never from the live ledger: assets, liabilities, equity (capital, retained earnings, the results of earlier
years not yet appropriated, the translation reserve, the non-controlling interests) and the result of the
year attributable to the owners of the parent; balanced says assets equal liabilities plus equity. The
profit and loss's net_income is the group's result before its attribution, split into
attributable_to_owners (the balance sheet's result) and attributable_to_nci.
The translation reserve is labelled per framework (other comprehensive income under ifrs, equity under
fer, "Eigenkapitaldifferenz aus Währungsumrechnung" under hgb). The statement of comprehensive
income (.../oci) comes with the non-controlling interests.
Intragroup eliminations (E1, E2, E3, E6)
After the aggregation, a run reverses what the entities of the run hold towards each other
(IFRS 10.B86(c); §§ 303 and 305 HGB). The partner is never typed: it is read from the view
ic_journal_line, which resolves it from the line's group party (ADR-0025). The ledger is read as for
the translation: posted entries by entry date, without the closing entries. A partner that is not in
the run (not a member, or consolidated after the date) is external: nothing is eliminated.
| Rule | What is reversed | At | What is left over goes to |
|---|---|---|---|
| E1 intragroup balances | each entity's balance on its group accounts (ic_ar_control, ic_ap_control, and ic_other_receivable, ic_other_payable: accrued interest and declared dividends) towards each partner |
the closing rate the entity was translated at | sides that agree in the document currency: profit or loss (pl.financial.expense or .income): an exchange difference on an intragroup monetary item stays in consolidated profit or loss (IAS 21.45). It is what an entity that has not revalued its foreign-currency balance has not booked yet |
| E2 intragroup loans | each entity's balance on its loan accounts (ic_loan_receivable, ic_loan_payable) towards each partner |
the closing rate | as E1, profit or loss; for a loan that is part of the net investment (IAS 21.15) the translation reserve, and the exchange differences the entities' revaluations took to profit or loss on it in the group year move there too (IAS 21.32): the fx_unrealized line reversed at its month's average rate |
| E3 intragroup income and expense | each revenue and expense line with a partner (interest included), in the group year and from the date both entities are consolidated (before that the partner was not in the group), month by month; FX results (fx_gain, fx_loss, fx_unrealized) are not intragroup income and stay (IAS 21.45); dividend income is E6 |
each month's average rate | sides that agree in the document currency: the translation reserve (bs.equity.cta): one side translated at an average rate against the other at another rate is a translation difference (IAS 21.41), not profit |
| E6 intragroup dividends | the shareholder's ic_dividend_income from another entity of the run, against the equity the declaring entity's appropriation debited (the movement dividend_declared names the entry), in the group year |
the income at its month's average rate, the equity at the rate of the resolution date (as the translation took it) | sides that agree: the translation reserve (IAS 21.41). A dividend the shareholder has not accepted yet is a difference (ic_difference) |
Each line is translated as the translation translates it, so an account that holds one partner only is emptied to the cent: the group receivables and payables lines are zero when both sides are booked.
Two sides that do not agree in the document currency (a mirror not accepted yet, a bill typed by
hand for another amount) are still reversed in full. The part the disagreement explains, each
currency's difference at the closing rate, goes to a line of its own,
bs.assets.current.ic_unreconciled (balances) or pl.ic_unreconciled (income and expense), so nothing
is hidden in another line; the rest is treated as for agreed sides. The run carries ic_difference,
blocking when that part exceeds the group's ic_tolerance_amount (in the reporting currency,
default 1.00), a warning within it. A difference in a currency no entity of the run keeps cannot be
converted: all of the residual is then unreconciled. The intercompany reconciliation
(GET /v1/ic/reconciliation) names the documents behind it.
A payment in transit (ADR-0073):
when one entity has booked the payment of a group invoice and the other has not yet (the
reconciliation's timing item payment_in_transit) and those payments are the whole of an E1
pair's disagreement in every document currency, the part goes to cash in transit
(bs.assets.current.cash_in_transit, "Cash in transit between group companies"), each currency at its
closing rate: Dr cash in transit, Cr the receiver's group receivable. The pair's treatment is
cash_in_transit (with in_transit), the run warns (ic_cash_in_transit) and is complete. A
disagreement the payments explain only in part stays unreconciled as a whole. Example: DE01 pays the
EUR 10,000.00 bill of CH01 on 28 September, CH01 has not booked the receipt on the 30th: E1 reverses
CH01's receivable of CHF 9,438.00 and puts EUR 10,000.00 x 0.9438 = CHF 9,438.00 on cash in transit;
E3 leaves CHF 5.23 to the translation reserve (9,438.00 of revenue against 10,000.00 x 0.9432772727 =
9,432.77 of expense) (tests/erp/features/consolidation/test_consolidation_cash_in_transit_pure.py,
tests/erp/features/consolidation/test_consolidation_cash_in_transit.py).
An intragroup cost capitalised by the buyer (a line with a partner on an asset account that is not a
group account or input VAT) would leave the seller's profit in the asset: the blocking exception
ic_capitalised, which a manual adjustment resolves (slice 4).
GET /v1/consolidation-runs/{id}/eliminations lists per rule and pair what each side holds in every
document currency, whether they agree, every line with its rate and where the residual went; every pair
balances. The header of the run keeps the same record (eliminations: the rules applied, the rules
pending and the pairs) and eliminations_applied is true. A run made before slice 2 has none.
The ADR's flow (CH01 invoices DE01 CHF 10,000.00; DE01 books EUR 10,638.30 of expense and revalues
its payable to EUR 10,695.19; closing 0.9350, September's average 0.9390): E1 reverses CHF 10,000.00 of
receivable against 10,695.19 x 0.9350 = 10,000.00 of payable, nothing left; E3 reverses CHF 10,000.00 of
revenue against 10,638.30 x 0.9390 = 9,989.36 of expense, and CHF 10.64 goes to the translation reserve
(tests/erp/features/consolidation/test_consolidation_eliminate.py). The loans and dividends are worked the same way in
tests/erp/features/consolidation/test_consolidation_eliminate_loans.py.
Capital consolidation (E4, E5, E7)
For each subsidiary acquired inside the ledger (consolidated_from = acquired_on), with the
consideration (acquisition_cost, in the reporting currency), the parent's investment account and the
net assets at acquisition stated on the member version (approved by a second person):
| Rule | What it does | Basis |
|---|---|---|
| E4 | reverses the subsidiary's whole equity at acquisition at the historical rate (as the translation translated it) against the parent's investment at the consideration; credits the non-controlling interest at its proportionate share of that equity; the difference is goodwill. As an asset (impairment_only, amortise) it is kept in the subsidiary's currency and translated at the closing rate, its translation difference to the owners' reserve (under IFRS not amortised; the run warns that the system does not test it for impairment). Under offset_equity it is debited to retained earnings at its amount at acquisition, and the basis shows the theoretical capitalisation over the group's useful life (the amortisation of the year and the carrying amount the notes show, FER 30/43). A negative difference is, by the framework: a bargain purchase gain on pl.bargain_purchase in the acquisition year (IFRS, with a warning to reassess); credited to retained earnings (FER offset); a liability bs.liabilities.noncurrent.negative_goodwill released straight line over five years to pl.negative_goodwill (FER capitalised); or kept at its amount after equity as the "Unterschiedsbetrag aus der Kapitalkonsolidierung" (bs.after_equity.consolidation_difference, its own section of the balance sheet, neither liabilities nor equity; HGB, the warning negative_goodwill_not_released) |
IFRS 10.B86(b), 3.18 to 3.36; Swiss GAAP FER 30/10, 30/16 to 30/19, 30/43; §§ 301, 307, 309 HGB, DRS 23.137, 23.139 ff. |
| E5 | gives the non-controlling interests their share of the subsidiary's profit of the year (pl.nci_share, shown as the attribution of the net income, not as an expense), of its translation reserve and of its other equity movements since the acquisition (earlier years' results, capital, dividends), all credited to bs.equity.nci |
IFRS 10.22, 10.B94; IAS 1.81B |
| E7 | under amortise: straight-line amortisation over goodwill_years from the month of the acquisition, in the subsidiary's currency, each month at its average rate; the accumulated amount off the goodwill at the closing rate, the difference to the reserve |
Swiss GAAP FER 30/16; § 309 para. 1 with § 253 para. 3 HGB, DRS 23.118, DRS 25.61 |
The net assets at acquisition are taken at their book value as their fair value: the figure on the
member version must equal what the subsidiary's ledger holds at the acquisition date, else the blocking
exception fair_value_adjustment_unsupported (no purchase price allocation in this version). A dividend
from the subsidiary to the parent is E6; the outside shareholders' part of it leaves their interest
through E5.
GET /v1/consolidation-runs/{id}/eliminations returns acquisitions: per subsidiary every figure
(consideration, net assets, rates, equity and non-controlling interest at acquisition, goodwill in both
currencies and its translation difference, the three parts of E5 and the interest at the date, its exact
share and the rounding), the paragraphs each rule follows, and the lines. The worked example of ADR-0026
(80 % of DE01 for CHF 800,000): goodwill CHF 48,000 = EUR 51,063.83, at 0.935 = 47,744.68; NCI 188,000 +
22,512 - 1,072 = 209,440 (tests/erp/features/consolidation/test_consolidation_capital_pure.py, tests/erp/features/consolidation/test_consolidation.py).
The group close checklist
GET /v1/consolidation-groups/{group}/checklist?as_of= (read) has the shape of an entity's close
checklist (pass, warn, fail, n/a; ready when nothing fails). Some checks read the books as they
are, the others the latest run at the date:
| Check | Reads | Fails or warns when |
|---|---|---|
run |
the runs | fail: no run at the date; warn: an entity's ledger chain moved since the run (make a new one) |
periods_closed |
the books | fail: a period of the parent or a member that starts on or before the date is open |
ic_reconciled |
the books | the intercompany reconciliation of a relationship between two entities of the group: its own warn (timing items) or fail |
rates_available |
the run | fail: rate_missing |
report_lines |
the run | fail: unmapped_report_line |
ic_eliminated |
the run | fail: ic_difference above the tolerance or ic_capitalised; warn: ic_difference within it, ic_exchange_difference or ic_cash_in_transit, or a run made before the eliminations |
cta_reconciled |
the run | fail: a reserve that does not tie to its roll-forward |
balanced |
the run | fail: consolidated assets differ from liabilities plus equity |
opening_position |
the members | fail: a member consolidated after its acquisition has no opening position |
capital_consolidated |
the run | fail: a capital exception (below) or a subsidiary without its capital consolidation; warn: a bargain purchase, goodwill not tested for impairment or an HGB negative difference not released |
nci_attributed |
the run | fail: net income is not the owners' plus the non-controlling interests' share |
adjustments_approved, final_run_current |
n/a: later slices |
Exceptions
| Code | Severity | Meaning |
|---|---|---|
rate_missing |
blocking | a rate the rate source does not publish; the entity is not in the run |
unmapped_report_line |
blocking | an account on a line outside the vocabulary or of another kind |
cta_unreconciled |
blocking | the reserve is not exactly its roll-forward plus the rounding of the translated amounts |
period_open |
warning | a period of the entity that starts on or before the date is still open (a preview) |
member_not_consolidated |
warning | a member starts after the date and is left out |
opening_position_missing |
blocking | a member is consolidated from a date after it was acquired: its investment, goodwill, non-controlling interest and reserve at the start cannot be computed from the books (the opening position is a later version) |
acquisition_basis_incomplete |
blocking | the member version (or the version that corrects its purchase data) lacks the consideration (in the reporting currency), the investment account or the net assets at acquisition: a new version with the same share and acquisition date completes it |
ownership_change_unsupported |
blocking | the versions in force that no later version corrects differ in the share or the acquisition date (a step acquisition or a partial disposal) |
investment_differs_from_cost |
blocking | the parent's investment account does not hold the consideration of the members on it |
fair_value_adjustment_unsupported |
blocking | the stated net assets at acquisition differ from the subsidiary's books |
bargain_purchase |
warning | a gain on a bargain purchase: reassess (IFRS 3.36) |
goodwill_impairment_not_tested |
warning | goodwill is carried without amortisation; the system does not test it for impairment |
negative_goodwill_not_released |
warning | HGB: a negative difference after equity, which the group releases as its cause allows (§ 309 para. 2 HGB) by a manual adjustment |
ic_difference |
blocking above the tolerance, else warning | two entities' intragroup balances, or income and expense, do not agree in the document currency; the part that disagrees is shown apart |
ic_capitalised |
blocking | an intragroup cost sits in an asset of the buyer |
ic_exchange_difference |
warning | agreed balances differ in the reporting currency by more than the tolerance: a balance was probably not revalued; the difference stays in profit or loss |
ic_cash_in_transit |
warning | two entities' group balances disagree only by payments one side booked and the other not yet: shown as cash in transit |
eliminations_pending, cta_starts_at_zero |
only on runs made before the capital consolidation |
complete on a run, a statement and the run list means: no blocking exception.
Operations
| Operation | Scope | Does |
|---|---|---|
POST /v1/consolidation-groups |
draft, human | creates a group (Idempotency-Key) |
GET /v1/consolidation-groups, GET .../{group} |
read | groups with their latest member versions and pending requests |
POST .../{group}/members |
draft, human | requests a member version (second person approves) |
GET .../{group}/members |
read | every approved version |
POST .../{group}/runs |
draft | makes a draft run (Idempotency-Key) |
GET .../{group}/runs |
read | the group's runs |
GET /v1/consolidation-runs/{id} |
read | the header: framework and switches, members and versions, digest, chain heads |
GET .../{id}/trial-balance?entity= |
read | the entity's translated trial balance with rates |
GET .../{id}/balance-sheet, .../profit-and-loss |
read | the group statements, intragroup items eliminated |
GET .../{id}/eliminations |
read | the eliminations by rule and pair |
GET .../{id}/cta |
read | the translation reserve roll-forward |
GET .../{id}/rates, .../exceptions |
read | the rates used, the exceptions |
GET /v1/consolidation-groups/{group}/checklist?as_of= |
read | the group close checklist |
curl -X POST "$ERP_URL/v1/consolidation-groups/DEMO/members" \
-H "Authorization: Bearer $ERP_KEY" -H "Idempotency-Key: demo-member-1" \
-H "Content-Type: application/json" \
-d '{"entity": "DE01", "ownership_percent": "80", "control_basis": "80 % of the voting rights.",
"acquired_on": "2026-01-02", "consolidated_from": "2026-01-02",
"acquisition_cost": "800000", "acquisition_cost_currency": "CHF",
"investment_account": "1485", "net_assets_at_acquisition": "1000000"}'
curl -X POST "$ERP_URL/v1/consolidation-groups/DEMO/runs" \
-H "Authorization: Bearer $ERP_KEY" -H "Idempotency-Key: demo-run-1" \
-H "Content-Type: application/json" -d '{"as_of": "2026-09-30"}'
The snapshot
A run is evidence. consolidation_run keeps everything it was computed from (the members and versions,
the framework's switches, every rate, the chain position of each entity's ledger, the digest of the
inputs, the reserve roll-forward, the record of the eliminations) and only its state can move (draft to final or superseded, with who and
when, by a trigger and a restricted grant); consolidation_line and consolidation_exception are
append-only, and only an elimination line names a partner (partner_entity_id, a check); at most one
run per group and date can be final. inputs_digest is the SHA-256 of the canonical JSON of the entity
balances and monthly movements, what each entity holds towards the others, the member versions, the
rates, the policy and the group's tolerance: the same inputs give the same digest. No ledger entry is written by consolidation: there is no
second posting path.
Not in this version, and how the design reads
- Parent in another currency: refused at group creation (the parent would need a starting point).
- Realised exchange differences on a net-investment loan (a repayment of it) stay in profit or loss; only the revaluation's differences move to the reserve. Reclassifying the reserve on disposal (IAS 21.48) comes with disposals.
- The reserve's starting point is zero at T0; the opening position with its basis (
cta_basis), the acknowledgement of exceptions, adjustments, finalise and supersede,verify, the reopen guard and the worksheet export are later slices of the ADR. - Hyperinflation, other rate sources and multi-level groups stay out of scope as in the ADR.