Intercompany
Trade between two entities of one organization (a Swiss parent and a German subsidiary, two Swiss companies in one VAT group). The design is ADR-0025; slices 1 and 2 of it are built: the relationship with its VAT group and transfer-pricing record, the group parties, the group accounts, the mirror an invoice creates in the counterpart, loans with their interest, group dividends, the settlement of interest and dividends, the reconciliation and the close checks. Settlement by netting and the group's own VAT return are not built (see what is not here). The controls are in the control matrix (section "Intercompany", IC1 to IC15).
Everything below is a REST operation first (/v1/ic, tag "intercompany" in the
generated reference); the web UI (Finance → Intercompany) and the MCP read
tools call the same operations.
The idea in one flow
CH01 Demo AG (CHF) invoices DE01 Demo GmbH (EUR) a management fee, CHF 10,000.00 net.
| Step | Who | What happens |
|---|---|---|
| 1 | Dana | Posts the invoice to the customer IC-DE01 in CH01. It debits the group receivable (ic_ar_control), not the ordinary one; Swiss VAT: none (the supply is abroad). |
| 2 | the system | The posting tells a listener (hooks.register_posted): the invoice is linked (ic_link) and a draft purchase bill is made in DE01 from vendor IC-CH01: the same currency, dates, description, quantities, unit prices, the invoice number as the vendor's number. DE01's VAT (reverse charge) is determined from DE01's side. |
| 3 | Carl | Opens the draft in DE01, recodes the cost account if he wants, and submits it: accepted. Dana, who posted the origin, may not. The bill credits the group payable (ic_ap_control) at the rate of the day. |
| 4 | the system | GET /v1/ic/reconciliation shows both sides equal to the cent in CHF; the close checks ic_reconciled of both entities pass. |
| 5 | each side | Settles by an ordinary payment (a receipt in CH01, a payment in DE01); a payment that one side has booked and the other not is a timing item until it does. |
The relationship
POST /v1/ic/relationships (human, draft scope, Idempotency-Key) asks for intercompany trade
between two entities; a second person approves it (POST /v1/approvals/{approval_id}/approve). The
relationship is pending until then. Its terms, which the approvers see in the request before they
decide:
| Term | Meaning |
|---|---|
mirror_mode |
draft (the default): the counterpart is a draft a person there accepts. post: the system posts it at once under the system policy, a standing flow the approval authorises (a monthly management fee); never the default |
tolerance_amount, tolerance_currency |
what the reconciliation counts as agreed: rounding only (default 1.00 in the currency of the first entity named) |
vat_group, vat_group_from, vat_group_reference |
none, ch_vat_group (two Swiss entities, Art. 13 MWSTG) or de_organschaft (two German ones, § 2 para. 2 no. 2 UStG), the date from which it applies and the registration or documentation; a VAT group across the border is refused (422, and by a trigger) |
pricing_method, markup_percent, pricing_basis, pricing_documented_on |
the transfer-pricing record: cup, resale_price, cost_plus (needs a mark-up), tnmm, profit_split or other; the basis in words (the benchmark or study and its date, mandatory); the date of the documentation (§ 1 para. 1 AStG, OECD Model Convention Art. 9, § 90 para. 3 AO) |
The ERP records the pricing record and never judges a price. The approval is bound to the
terms by a digest (the pair, the mirror mode, the tolerance, the VAT group and the pricing record, a
SHA-256 over a canonical form): changing any of them is a new request (PATCH), and approving applies
exactly the terms the request carries (409 ic_terms_changed otherwise). On the first approval, in the
same transaction, the relationship becomes active and the four group parties are created.
PATCH /v1/ic/relationships/{id}:
status: "suspended"with areason: stops new intercompany postings at once (one person); open items stay and settle normally. A document of a group entity becomes posted only while the relationship is active (triggererp_check_ic_document_party).status: "active"on a suspended relationship, or new terms: an approval request. The terms in force stay until a second person approves; one request waits at a time (409). A relationship that was never approved takes new terms at once and asks again; after a rejection any PATCH (even{}) asks again with the terms as they are, so a request is never stuck.refresh_parties: true(alone): reads the name, address, VAT number, country and bank accounts of the two entities again into the four group parties.
A relationship cannot be requested or approved while an entity lacks the group accounts:
409 role_missing names the entity, the role and the code the chart template suggests.
Group parties and group accounts
Each entity gets a customer and a vendor that is the other entity (IC-<code>, group_entity_id set,
never changed): name, address, VAT number and country from the other entity's record, the other entity's
functional currency, and its own registered bank accounts as their payment accounts. They are active
at once: the relationship's approval is their activation (the per-party four eyes of
ADR-0010 would ask the same two people the same
question twice). Nobody edits them (409 pointing at refresh_parties), and a payment to a group
entity goes only to a bank account registered for the other entity, read when the payment is released,
so a bill cannot redirect it.
The documents of such a party post to two optional control roles an entity assigns to its own
accounts (account roles): ic_ar_control (receivables from group companies) and
ic_ap_control (payables to group companies), with the suggested accounts below (the Swiss numbers are
proposals in the right group of the Kontenrahmen KMU, not confirmed against the full publication,
B-66). They are control accounts: only their subledger posts to them. The
balance sheet shows them on their own statement lines, bs.assets.current.receivables_group and
bs.liabilities.current.payables_group (Art. 959a para. 4 OR; § 266 HGB "gegen verbundene
Unternehmen"). trade.control_role(party) is the one place that picks the control account by party:
the document lines, the functional total, payments and the FX revaluation all ask it, and the aging and
the control-account close checks include the group account beside the ordinary one.
| Role | Swiss KMU | Generic IFRS |
|---|---|---|
ic_ar_control |
1110 Forderungen aus L+L gegenüber Konzerngesellschaften | 1210 Trade receivables, group |
ic_ap_control |
2010 Verbindlichkeiten aus L+L gegenüber Konzerngesellschaften | 2010 Trade payables, group |
The loan, other-receivable, interest and dividend roles of the ADR belong to slice 2 and are added with it.
The partner of a posted line is never typed: it is the party's group entity, read through the view
ic_journal_line, which the reconciliation (and later the consolidation) reads.
Opening balances with group entities
The open-items import (open_receivables, open_payables) refuses a group party: it posts to the
ordinary control accounts and books one side only, so a balance with a group entity would be on one
entity's books and unknown to the other. Bring intercompany opening balances in through the flow that
books both sides: post the invoices through the relationship (the mirror makes the counterpart), or
enter a journal in each entity against the group accounts (ic_ar_control, ic_ap_control) as part of
the opening entry. The reconciliation then compares the two sides like any other.
The mirror
At submit of an invoice or credit note to a group entity (submit check ic_mirror), the mirror is
dry-run through the same construction; if it would fail, the submit is refused
(409 ic_mirror_impossible with the reason: the relationship is not active, an entity lacks its group
account, the receiving side cannot determine its tax) and so is a goods line
(422 ic_goods_not_supported: stock transfers, customs and profit in stock are not built; lines linked to
a revenue contract will be refused when contracts exist). An invoice that cannot be mirrored does not post.
When the origin posts (directly or after approval), in the same transaction and a savepoint, the
listener writes the link and makes the counterpart as a draft through trade.create_document. The
mirror copies currency, document and due date, notes, quantities, unit prices and descriptions;
external_number is the origin's number (so the vendor-number rule applies); the expense account is the
receiver's default_expense until it recodes it; VAT is determined afresh. Failure does not undo the
origin's posting (it is immutable): the link is stored pending with the reason, and
POST /v1/ic/links/{id}/retry repeats it. A credit note mirrors as a purchase credit note crediting the
mirror bill once that bill is posted; until then its link is waiting.
| Link status | Meaning |
|---|---|
pending |
the counterpart could not be made (error); retry |
waiting |
a credit note whose invoice's mirror is not posted yet |
drafted |
the counterpart is a draft a person in the receiving entity accepts |
accepted |
the counterpart was posted (by a person, or by the system in mode post) |
rejected |
the receiver refused it (POST /v1/ic/links/{id}/reject with a reason voids the draft); the sender sees why and corrects with a credit note |
mirror_rejected |
a credit note of an invoice whose mirror was rejected (also set when the invoice's mirror is rejected while the credit note waits): there is no bill to credit, so the credit note has no counterpart. The sender's invoice and credit note cancel and the receiver booked neither; the reconciliation agrees |
Acceptance is the counterpart's normal submit (POST /v1/documents/{id}/submit). The receiver may change
what only it can decide (account, tax category, dates, text); 409 ic_mirror_locked refuses a change of
quantity, unit price, currency, net total or vendor, and a draft that mirrors is rejected, not voided. At
posting the trigger erp_check_ic_match requires the mirror's currency and net total to equal the
origin's, also with the application bypassed.
Segregation across entities. One person usually has access to both entities, so the rule compares
persons: the person who created or posted the origin does not accept (or reject) its mirror, whichever key
or session (accept_own_ic_document, people.check_segregation, default deny; a one-person group
allows it in its own policy and the refusal says so); an agent never accepts (agents_no_group_controls);
a key the system issued belongs to no known person and never accepts; the system actor posts a mirror
only in mode post. The check runs at submit and again where the posting happens (entry guard
intercompany), so an approval cannot be a way round it.
Locks. A relationship with mode post posts into the other entity from the listener, so the origin's
posting first takes both entities' locks in the order of their ids (hooks.lock_entities): an A to B and
a B to A posting queue instead of crossing. A mirror is never mirrored back (it is a document of a link
already), and the registry refuses a nesting deeper than two levels.
GET /v1/documents/{id} carries an ic block on a document of a group party: the link, the role (origin
or mirror), its status and the document at the other end.
VAT group
From vat_group_from, a document between the two members is created with every line
vat_scope = outside and the reason vat_group or organschaft (ADR-0029,
X7): no tax determination, rate 0, no tax tag, so no VAT report or return has it, and the document
prints "Leistung innerhalb einer Mehrwertsteuergruppe, nicht steuerbar (Art. 13 MWSTG)" or "Innenumsatz einer
Organschaft (§ 2 Abs. 2 Nr. 2 UStG)" in its own language. The mirror is built the same way. Documents
dated before the start date carry VAT as usual. The group's own VAT return is filed by the group
representative (Swiss) or the Organträger (German) and is not built: the supplies are only kept out of the
entity returns. A VAT group exists inside one country, never between CH01 and DE01.
Loans
A loan is an agreement between the two entities of an active relationship, approved by a second
person like the relationship itself (policy action approve_ic_agreement: human only, by default
require_approval). POST /v1/ic/loans (human, draft scope, Idempotency-Key) writes it draft with
its terms: the reference, lender and borrower, the currency (left out: the lender's) and the facility (what may be drawn at most), a
fixed annual rate with its arm's-length interest_basis in words (the benchmark, its source and date:
recorded, never judged), the day count (act/360, act/365 fixed, 30/360 European), the start and
maturity dates, the bank account of each side (an asset account of its chart) and net_investment (IAS
21.15: settlement is neither planned nor likely). The approval is bound to the terms by a digest; once the
loan is active the database refuses any change of them, and a loan moves draft → active → closed
only, and is never deleted.
The lender needs accounts with the roles ic_loan_receivable, ic_other_receivable and
ic_interest_income; the borrower ic_loan_payable, ic_other_payable and ic_interest_expense. The
chart templates have them (ch_kmu: 1445, 1150, 6952 and 2455, 2160, 6902; ifrs_generic: 1810, 1220,
7010 and 2610, 2020, 7110); 409 role_missing names a missing one.
Every movement posts both entities' entries in one transaction, each through its entity's posting
policy, and is recorded once, append-only (ic_movement, GET /v1/ic/movements), with the two entries:
| Movement | REST | Lender | Borrower |
|---|---|---|---|
| Drawdown | POST /v1/ic/loans/{id}/drawdowns |
Dr ic_loan_receivable / Cr bank |
Dr bank / Cr ic_loan_payable |
| Repayment | POST /v1/ic/loans/{id}/repayments |
Dr bank / Cr ic_loan_receivable |
Dr ic_loan_payable / Cr bank |
| Interest | POST /v1/ic/loans/{id}/interest |
Dr ic_other_receivable / Cr ic_interest_income |
Dr ic_interest_expense / Cr ic_other_payable |
| Settlement | POST /v1/ic/settlements |
Dr bank / Cr ic_other_receivable |
Dr ic_other_payable / Cr bank |
The lines on the group accounts name the other entity as the partner (counterparty_type = 'entity', only
the intercompany module and the appropriation may post one) and the loan as their subledger, so the
reconciliation, the FX revaluation and the consolidation read them. A side in a foreign currency books at
the ECB rate of the day; a repayment or settlement relieves the balance at its carrying amount and the bank
at the rate of the day, and the difference is the realised FX result (detail.realised). The open balances
of the loan, the accrued interest and a declared dividend are revalued at the period end like any
monetary item (the FX provider ic_balances).
A drawdown, a repayment or a settlement that takes the paying bank account below zero in the books is
made, with a warning (warnings, warning_reasons), by the same rule as a payment (see
ledger): the balance of the account in the currency it holds.
lender_bank_balance and lender_bank_currency on the loan give that balance, so the payout dialog warns
before the payout where the account holds the loan's currency.
A drawdown is dated within the term and may not exceed what is left of the facility. A loan is closed
when its principal and its open interest are both zero and the interest up to the last repayment is
accrued: after the final repayment the loan stays active until the stub from the last accrual to that day
is accrued (through the repayment date) and settled, or that interest could never be booked. A stub that
rounds to nothing does not hold the loan open. There is no waiver: interest the lender forgoes is accrued
and settled like any other, and the waiver booked separately.
Interest
POST /v1/ic/loans/{id}/interest with through accrues from the day after the last accrual (or from the
start) through that day, on the principal outstanding each day: a drawdown or a repayment cuts a segment,
each segment is principal × rate × days / basis by the day count, and the sum is rounded once, half to
even (detail.segments shows them). A day's interest belongs to the period that ends on it: from a
drawdown on 1 October to 31 October are 30 days, so EUR 500,000.00 at 3 % act/360 is EUR 1,250.00. A
movement of principal dated on or before the last accrual is refused (accrue first, or the days would be
counted twice). The close check ic_interest warns while a loan with principal outstanding is not accrued
through the period's end.
Settlement
POST /v1/ic/settlements pays accrued interest (loan) or a declared group dividend (dividend: the id of
its dividend_declared movement), at most what is open; the bank accounts default to the loan's (for a
dividend to the accounts with the role bank).
Group dividends
A subsidiary's appropriation (the year-end close) may name a destination
of kind group_dividend with the shareholder (an entity code) instead of an account. Its line goes to
the declaring entity's ic_other_payable with the shareholder as the partner. When the appropriation
posts, in the same transaction, the movement dividend_declared is recorded and the shareholder's
entry is made as a draft (Dr ic_other_receivable / Cr ic_dividend_income, in the declaring entity's
currency at the ECB rate of the resolution date). A person of the shareholder accepts it by submitting
it, as a mirror bill is accepted: not the person who proposed, posted or approved the appropriation
(accept_own_ic_document), never an agent. Until then the reconciliation lists the dividend as a timing
item. The payment is a settlement. An appropriation that declared a group dividend is not reversed (409):
the shareholder's side would stay.
When the declaring entity deducts the Swiss withholding tax (withholding_tax: deduct,
year-end close), the payable is the net 65 % and the
shareholder's draft is Dr ic_other_receivable (65 %), Dr withholding_tax_receivable (35 %) / Cr
ic_dividend_income (gross): the income is the gross dividend and the tax is a claim on the tax
administration, not on the subsidiary. The movement records withholding_tax, the procedure and net, and
a settlement pays at most the net. Under notification nothing is withheld and both sides carry the gross.
Reconciliation
GET /v1/ic/reconciliation?as_of= (read; optional relationship, entity). For each relationship that is
not pending and each currency of its open items it returns, in the document currency (so the FX
revaluation cannot disturb it): entity A's receivable from and payable to B, B's from and to A, the nets
and difference = a_net + b_net, zero when both sides agree. Open means what the books hold: the total
less the posted payments and credit notes dated up to the day. The same figures are listed per link
(origin open, counterpart open, difference, reason) and for every group document nobody linked; the
items add up to the difference. The carrying amounts in each functional currency, and the difference
converted to the tolerance currency at the ECB rate of the day, are for information; the tolerance applies
to that figure.
| Reason | Explained? | Means |
|---|---|---|
mirror_missing |
yes (timing) | the counterpart was not made (pending, waiting) |
mirror_draft |
yes | the counterpart is a draft |
mirror_pending_approval |
yes | the counterpart waits for approval |
payment_in_transit |
yes | one side booked a payment, the other has not |
cutoff |
yes | a document is dated on the other side of the day |
mirror_rejected |
no | the receiver rejected the mirror: a dispute |
unlinked_document |
no | a group document nobody linked (a bill typed by hand) |
amount_differs |
no | the two documents disagree, or a remainder nothing explains |
Loans, interest and dividends are reconciled by movement (balances: per kind loan, interest,
dividend, item and currency, what each side holds in the document currency): loan_movement_missing
(explained) when one side's entry of a movement has not posted yet, amount_differs (unexplained)
otherwise.
status: pass when everything is within the tolerance, warn when what differs is wholly timing items,
fail when an unexplained part exceeds the tolerance, n/a for a pending relationship.
Close check
ic_reconciled joins the close checklist (order 160, after recurring_generated): for every relationship of the entity, as of the
period's end date (by entry date, so the counterpart's fiscal calendar does not matter), pass / warn /
fail as above, n/a when the entity has no relationship. An unexplained difference means one side holds
a balance the other cannot see, which is exactly the error the check exists to find; a timing item is the
counterpart's to resolve, and holding one entity's close hostage to another team's backlog would make
people close around the check. The group will be stricter (the consolidation refuses a difference beyond the
tolerance). The existing ar_control_reconciled and ap_control_reconciled include the group accounts.
ic_interest (order 170, a warning, never a failure): for every loan the entity lends or borrows that has
started by the period's end, warn when principal is outstanding at the end of the day and the interest is
accrued through an earlier day, pass otherwise, n/a without a loan. Both sides of an accrual post
together, so the books still agree; only the period's interest result is short.
Web UI
Finance → Intercompany (/intercompany, Tell me: "intercompany", "konzern", "intragroupe"). Tiles
(each opens the list it counts, with the same count): relationships, mirrors to accept, counterparts
not made, rejected mirrors. The mirror documents list (?links=drafted|pending|waiting|rejected|mirror_rejected|accepted,
&relationship=) has Review and accept (opens the draft's document card, where the receiver recodes
and submits it), Reject (a reason) and Try again. A card per relationship shows its terms and
transfer-pricing record, whether it waits for approval, and its reconciliation as of a day (?as_of=): the
net positions per currency, the items with their reasons, the carrying amounts. The Approvals page lists
the requests with the pricing basis. The close checklist links its check to this page.
Audit
| Event | When |
|---|---|
ic.relationship.create, .request, .approve, .reject, .change, .suspend, .refresh |
the life of a relationship, with the terms and the digest |
customer.create, vendor.create (via the relationship) |
the four group parties |
ic.mirror.create, .accept, .reject, .retry, .failed, .post_failed, .ended |
the counterpart, with the object ids on both sides |
document.void |
the draft a rejection voids |
ic.loan.create, .request, .approve, .reject |
the life of a loan, with the terms and the digest |
ic.loan.movement |
a drawdown, repayment, accrual or interest settlement, with both entries |
ic.dividend.declare, ic.dividend.settle |
a group dividend declared (with the shareholder's draft) and paid |
What is not here
Variable rates (a loan's rate is fixed; a new rate is a new loan), a repayment schedule (each repayment is
recorded as it happens), the web UI of loans (the API and the MCP read tools list_intercompany_loans and
list_intercompany_movements have them). Later: bilateral netting, goods and profit in stock, the VAT return of a group,
withholding tax on interest (a dividend's is built), per-entity permissions (acceptor and originator differ by
person, not by entity access). The consolidation reads the links, the view and the reconciliation
(ADR-0026).