StabisoDocs

Accounting and finance

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 a reason: 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 (trigger erp_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).

Stabiso, the Enterprise System of Record · generated from the code and its guides · release 20b7dd5b0c4d