This card covers cross-ERP multi-entity architecture patterns applicable across SAP S/4HANA, Oracle NetSuite OneWorld, Microsoft Dynamics 365 Finance, Oracle ERP Cloud, and Salesforce. It addresses how each platform models legal entities, how intercompany transactions flow, and how currency conversion and consolidation work.
| System | Entity Model | Multi-Currency | Intercompany | Max Entities |
|---|---|---|---|---|
| SAP S/4HANA | Company Code (Bukrs) | Multi-currency per company code; parallel ledgers | ICMR + Advanced IC Sales | Unlimited (~500 practical) |
| NetSuite OneWorld | Subsidiary | Native multi-currency; auto-revaluation | Built-in IC transactions; auto-elimination | 300 subsidiaries; 10 levels |
| D365 Finance | Legal Entity (DataAreaId) | Multi-currency per legal entity; exchange rate types | IC accounting with Due-to/Due-from | Unlimited (typically <200) |
| Oracle ERP Cloud | Business Unit / Legal Entity | Multi-currency; Global Accounting Engine | Automated IC eliminations; transfer pricing | Unlimited |
| Salesforce | Org (single/multi-org) | CurrencyIsoCode field; dated exchange rates | No native IC — requires ERP | Single org recommended |
One ERP instance serving all legal entities worldwide. All company codes, subsidiaries, or legal entities exist within a single system.
+---------------------------+
| Single ERP Instance |
| +---------+---------+ |
| | Entity | Entity | |
| | US (USD)| DE (EUR)| |
| +---------+---------+ |
| | Entity | Entity | |
| | JP (JPY)| BR (BRL)| |
| +---------+---------+ |
| Unified COA |
| Central FX Rate Table |
| Built-in IC Elimination |
+---------------------------+
Strengths: Single source of truth. Native intercompany elimination. One chart of accounts. Simplest consolidation. Lowest integration cost.
Weaknesses: All regions share release cycles and downtime windows. Regional customization constrained. Data residency violations if hosted in a single region. 30-40% of local requirements may require compromise. [src1]
Best for: Organizations with high process standardization, <50 entities, and no sovereign cloud mandates.
Separate ERP instances per region or major subsidiary group. Each instance is optimized for local requirements.
+------------------+ +------------------+ +------------------+
| Americas ERP | | EMEA ERP | | APAC ERP |
| (US, BR, MX) | | (DE, UK, FR) | | (JP, SG, AU) |
| USD primary | | EUR primary | | JPY primary |
+--------+---------+ +--------+---------+ +--------+---------+
| | |
+----------+------------+-----------+-----------+
| |
+------+-------+ +-----------+-----------+
| COA Mapping | | Consolidation Layer |
| Layer | | (HFM/BPC/FC/manual) |
+--------------+ +-----------------------+
Strengths: Each region controls its own release cycle. Local regulatory compliance is native. Data residency satisfied by design. Regional autonomy.
Weaknesses: IC transactions cross system boundaries. COA must be harmonized or mapped. Duplicate master data. Separate consolidation tool required. [src4]
Best for: Organizations with low process standardization, sovereign cloud mandates, >50 entities, multi-vendor ERP, or M&A-driven heterogeneous landscapes.
Core financials in a global instance; regional front-office or operational systems connected via integration layer.
+--------------------------------------------------+
| Global Financial Hub (ERP) |
| Unified COA | Consolidation | IC Elimination |
+----------+-------------------+--------------------+
| |
+----------+------+ +-------+----------+
| Regional CRM/ | | Regional Ops/ |
| Front Office | | Manufacturing |
+-----------------+ +------------------+
Strengths: Financial consolidation stays simple. Regional operational autonomy. Data residency manageable by keeping PII in regional systems.
Weaknesses: Requires robust integration layer. Master data synchronization complexity. More complex than either pure pattern. [src6]
Best for: Organizations with standardized financial processes but diverse operational needs. Most common real-world pattern for 20-200 entities.
START -- Choosing multi-entity ERP architecture
+-- How standardized are business processes across entities?
| +-- Highly standardized (same processes globally)
| | +-- Data residency mandates (China, Russia, sovereign cloud)?
| | | +-- NO -> Single Global Instance
| | | +-- YES -> Federated Hybrid (global financial hub + local operational)
| | +-- > 200 entities?
| | +-- NO -> Single Global Instance
| | +-- YES -> Single Global Instance with performance optimization
| +-- Moderately standardized (shared finance, diverse ops)
| | +-- Federated Hybrid
| +-- Low standardization (each entity operates independently)
| +-- < 10 entities? -> Regional Instances with consolidation tool
| +-- Already multi-vendor ERP? -> Regional Instances with iPaaS
| +-- M&A-driven growth? -> Regional Instances (fastest integration)
+-- Currency complexity
| +-- < 5 stable currencies -> any pattern works
| +-- 5-20 currencies -> ensure centralized FX rate management
| +-- > 20 currencies with high volatility -> Single Global or Federated
+-- Intercompany volume
| +-- < 100 IC/month -> manual reconciliation acceptable
| +-- 100-10,000/month -> automated IC matching required
| +-- > 10,000/month -> Single Global Instance strongly recommended
+-- Compliance
+-- GDPR only -> any pattern (EU hosting for EU entities)
+-- GDPR + China PIPL -> Federated or Regional
+-- Multiple sovereign mandates -> Regional Instances only
Every multi-entity ERP integration must solve three currency problems: transaction currency conversion, period-end revaluation, and financial translation for consolidation.
| Rate Type | When Used | Source | Sync Frequency | ERP Field |
|---|---|---|---|---|
| Spot rate | Transaction recording | Treasury/ECB/OANDA | Real-time or daily | SAP: TCURR; NS: Currency Exchange Rate; D365: Exchange rate type |
| Closing rate | Balance sheet translation (ASC 830 / IAS 21) | Central treasury | Monthly at period close | Used in consolidation |
| Average rate | P&L translation (ASC 830 / IAS 21) | Calculated from daily rates | Monthly weighted average | Used in consolidation |
| Budget rate | Planning/forecasting | Finance team | Quarterly or annually | SAP: parallel valuation; D365: Budget exchange rate type |
| Historical rate | Equity accounts, fixed assets | Locked at acquisition | One-time | Stored per account/entity |
| Capability | SAP S/4HANA | NetSuite OneWorld | D365 Finance | Oracle ERP Cloud |
|---|---|---|---|---|
| Multi-currency transactions | Yes (per company code) | Yes (per subsidiary) | Yes (per legal entity) | Yes (per business unit) |
| Parallel ledgers | Up to 3 | Multi-book (2024+) | Secondary currency per ledger | Subledger accounting rules |
| Auto FX revaluation | FAGL_FC_VAL | Automated schedule | Revaluation journal | Period close process |
| FX gain/loss | Configurable per COA | Configurable per subsidiary | Configurable per main account | Configurable per GL account |
| Exchange rate API | TCURR table update | Built-in daily auto-update | Data Entity import | Manual or integration |
| Triangulation | Yes | Yes (automatic) | Yes (cross-rate) | Yes |
# Input: ECB exchange rate feed + target ERP API endpoint
# Output: Updated exchange rates in ERP for all active currencies
import requests
from datetime import date
class ExchangeRateSyncer:
"""Sync ECB rates to ERP exchange rate tables."""
ECB_URL = "https://data-api.ecb.europa.eu/service/data/EXR/D..EUR.SP00.A"
def fetch_ecb_rates(self, reference_date=None):
if not reference_date:
reference_date = date.today().isoformat()
params = {"startPeriod": reference_date, "endPeriod": reference_date, "format": "jsondata"}
resp = requests.get(self.ECB_URL, params=params, timeout=30)
resp.raise_for_status()
data = resp.json()
rates = {}
series = data.get("dataSets", [{}])[0].get("series", {})
dimensions = data.get("structure", {}).get("dimensions", {}).get("series", [])
currency_dim = next(d for d in dimensions if d["id"] == "CURRENCY")
for key, series_data in series.items():
currency_idx = int(key.split(":")[1])
currency_code = currency_dim["values"][currency_idx]["id"]
obs = series_data.get("observations", {})
if obs:
rate = list(obs.values())[0][0]
rates[currency_code] = float(rate)
return rates # {"USD": 1.0842, "GBP": 0.8601, "JPY": 161.52}
def sync_to_sap(self, rates, exchange_rate_type="M"):
for currency, rate in rates.items():
bapi_params = {
"RATE_TYPE": exchange_rate_type,
"FROM_CURR": "EUR", "TO_CURRNCY": currency,
"VALID_FROM": date.today().strftime("%Y%m%d"),
"EXCH_RATE": rate, "FROM_FACTOR": 1, "TO_FACTOR": 1
}
# Call via pyrfc or SAP OData endpoint
| IC Transaction Type | Seller Entry | Buyer Entry | Elimination Entry |
|---|---|---|---|
| IC Sale/Purchase | Revenue + AR | COGS + AP | DR Revenue, CR COGS; DR AP, CR AR |
| IC Loan | Loan receivable + interest income | Loan payable + interest expense | DR Interest income, CR Interest expense |
| IC Service fee | Service revenue + AR | Service expense + AP | DR Revenue, CR Expense; DR AP, CR AR |
| IC Inventory transfer | Transfer out | Inventory in | Eliminate unrealized profit in inventory |
| IC Dividend | Dividend income | Retained earnings reduction | DR Dividend income, CR Investment |
SAP S/4HANA: Company codes paired with IC Business Partner records. ICMR provides real-time matching. Advanced Intercompany Sales automates sales-procurement-delivery-billing. Elimination in Group Reporting or BPC. [src1, src4]
NetSuite OneWorld: Built-in IC transactions auto-generate corresponding entries in counterpart subsidiary. Elimination journal entries automated via elimination schedules. [src2]
D365 Finance: Legal entity pairs with Due-to/Due-from main accounts. Reciprocal relationships auto-created. Cross-company data sharing for shared COA. [src3]
Oracle ERP Cloud: Business units map to legal entities. Auto-generated IC entries. Financial Consolidation Hub handles elimination.
// Input: IC transaction details (selling/buying subsidiary, amount, currency)
// Output: Paired IC journal entries in both subsidiaries
define(['N/record'], function(record) {
function createICJournalPair(params) {
var je = record.create({
type: record.Type.INTER_COMPANY_JOURNAL_ENTRY,
isDynamic: true
});
je.setValue({ fieldId: 'subsidiary', value: params.sellingSubsidiary });
je.setValue({ fieldId: 'currency', value: params.currency });
je.setValue({ fieldId: 'tosubsidiary', value: params.buyingSubsidiary });
// Debit: IC Receivable (seller side)
je.selectNewLine({ sublistId: 'line' });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'account', value: params.icAccount });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'debit', value: params.amount });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'linesubsidiary', value: params.sellingSubsidiary });
je.commitLine({ sublistId: 'line' });
// Credit: IC Revenue (seller side)
je.selectNewLine({ sublistId: 'line' });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'account', value: params.revenueAccount });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'credit', value: params.amount });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'linesubsidiary', value: params.sellingSubsidiary });
je.commitLine({ sublistId: 'line' });
// Debit: IC Expense (buyer side)
je.selectNewLine({ sublistId: 'line' });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'account', value: params.expenseAccount });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'debit', value: params.amount });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'linesubsidiary', value: params.buyingSubsidiary });
je.commitLine({ sublistId: 'line' });
// Credit: IC Payable (buyer side)
je.selectNewLine({ sublistId: 'line' });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'account', value: params.icAccount });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'credit', value: params.amount });
je.setCurrentSublistValue({ sublistId: 'line', fieldId: 'linesubsidiary', value: params.buyingSubsidiary });
je.commitLine({ sublistId: 'line' });
return je.save();
}
return { createICJournalPair: createICJournalPair };
});
| Pattern | Description | Complexity | Best For |
|---|---|---|---|
| Unified COA | One global COA; local extensions via segments | Low | Single-instance; high standardization |
| Mapped COA | Local COAs; mapping table to group COA | Medium | Regional instances; multi-vendor |
| Parallel COA | Both local and group COA simultaneously | High | SAP (multiple ledgers); Oracle |
| Segmented COA | Single COA with entity/region segments | Medium | D365 (dimensions); Oracle (segments) |
| Source (Local) | Target (Group) | Type | Transform | Gotcha |
|---|---|---|---|---|
| Local GL account | Group COA account | String | Mapping table lookup | Many-to-one mappings lose local detail |
| Local currency amount | Group currency amount | Currency | FX conversion at applicable rate | Rate type matters: spot for TX, closing for BS, average for P&L |
| Local tax code | Group tax classification | Enum | Mapping table | Local VAT codes don't map 1:1 |
| Subsidiary/entity ID | Group entity hierarchy | Reference | Hierarchy mapping | Acquired entities need new mapping |
| Local cost center | Group cost center | String | Direct or mapped | May not exist in group structure |
| Posting date | Posting date | Date | Timezone conversion | SAP: user TZ; NetSuite: company TZ; D365: UTC |
| IC partner code | Group IC partner | Reference | Cross-entity mapping | Mismatch blocks elimination |
| Jurisdiction | Law | Requirement | Architecture Impact |
|---|---|---|---|
| EU/EEA | GDPR (Art. 44-49) | PII must stay in EU/EEA or adequate countries | EU entity data hosted in EU region; aggregates flow globally |
| China | PIPL + DSL + CSL | Personal + important data stored in China | Separate instance for China common |
| Brazil | LGPD | Similar to GDPR; Brazilian jurisdiction | Host in Brazil or GDPR-adequate region |
| India | DPDP Act 2023 | Localization for certain categories | Indian PII hosted in India |
| Russia | Federal Law 242-FZ | Russian citizen data stored in Russia | Separate instance required |
| Saudi Arabia | PDPL | Certain categories require in-kingdom processing | Middle East regional instance |
| Indonesia | GR 71/2019 | Strategic systems use local data centers | Local hosting for regulated industries |
PATTERN: Federated Hybrid with Regional Data Boundaries
+--------------------+ +-------------------+ +------------------+
| EU Region (GDPR) | | China (PIPL) | | US / Global |
| EU ERP Instance | | China ERP Instance| | HQ ERP Instance |
| PII stays here | | PII stays here | | PII stays here |
+---------+----------+ +---------+---------+ +--------+---------+
| | |
| Financial aggregates | Financial aggregates |
| (no PII) only | (no PII) only |
+-----------+--------------+----------+--------------+
| |
+-------+-------------------------+--------+
| Global Consolidation Layer |
| Aggregated financials, no personal data |
+-------------------------------------------+
Key rule: Only financial aggregates (account balances, totals, journal summaries) should cross regional boundaries. Individual transaction records containing PII remain in the originating region. [src7]
| Error Type | Symptom | Cause | Resolution |
|---|---|---|---|
| IC imbalance | Non-zero IC elimination line | One side posted, other failed or wrong period | IC matching before close; same-period posting |
| FX rate gap | "Exchange rate not found" error | No rate for transaction date + currency pair | Daily auto-sync; fallback to nearest date |
| COA mapping miss | "Target account not found" | No group COA mapping defined | Default to suspense account; exception report |
| Cross-region timeout | API calls time out at 30s+ | Network latency between regions | Async/event-driven for cross-region |
| Subsidiary mismatch | Journal entry rejected | Wrong subsidiary internal ID | Maintain mapping table; validate before posting |
| Currency precision | Rounding variance accumulates | Different decimal precision between systems | Standardize precision; banker's rounding |
Load rates at 5 PM CET; use previous business day's rate with tolerance flag for gap period. [src5]Paired IC transaction pattern — both sides commit atomically or neither (saga with compensation).Async event-driven pattern (Kafka, SAP Event Mesh, Azure Service Bus) with regional consumers. [src7]COA governance process; new mappings require approval and validation against consolidation rules. [src3]Universal UTC cutoff time for IC transactions enforced via integration layer.// BAD -- same config for all regions: replicates PII globally, ignores local needs
const SYNC_CONFIG = {
interval: "15min", // Germany doesn't need 15-min for monthly journals
dataModel: "full_record", // Full records to all regions violates GDPR
currencies: ["USD"], // Forcing single currency
coa: "US_GAAP_COA" // Ignoring local statutory requirements
};
regions.forEach(region => syncAllData(region, SYNC_CONFIG));
// GOOD -- per-region config respecting local requirements and data residency
const REGION_CONFIGS = {
"EMEA": {
interval: "1h",
dataModel: "financial_aggregates", // No PII crosses border
currencies: ["EUR", "GBP", "CHF"],
coa: "local_to_group_mapped",
dataResidency: "eu-west-1",
piiReplication: false
},
"APAC": {
interval: "30min",
dataModel: "financial_aggregates",
currencies: ["JPY", "SGD", "AUD", "CNY"],
coa: "local_to_group_mapped",
dataResidency: "ap-northeast-1",
chinaIsolated: true
}
};
# BAD -- single rate source for all entities
def sync_exchange_rates(source="ECB"):
rates = fetch_rates(source)
for entity in all_entities:
entity.update_rates(rates) # India requires RBI; China requires PBOC
# GOOD -- each jurisdiction uses legally mandated rate source
RATE_SOURCES = {
"EU": {"provider": "ECB"},
"US": {"provider": "FRB"},
"India": {"provider": "RBI"},
"China": {"provider": "PBOC"},
}
def sync_by_jurisdiction():
for jurisdiction, source in RATE_SOURCES.items():
entities = get_entities_by_jurisdiction(jurisdiction)
rates = fetch_rates(source["provider"])
for entity in entities:
entity.update_rates(rates, source=source["provider"])
# BAD -- synchronous call across regions; if buyer fails, seller already committed
def post_ic(seller, buyer, amount):
seller.api.post_journal(debit_ic(amount)) # Committed
buyer.api.post_journal(credit_ic(amount)) # If this fails = IC IMBALANCE
# GOOD -- async with compensation for failures
def post_ic_saga(seller, buyer, amount, currency):
saga_id = generate_saga_id()
seller.api.post_journal(debit_ic(amount), status="PENDING", saga_id=saga_id)
publish_event("ic.transaction.initiated", {
"saga_id": saga_id, "buyer": buyer.id,
"amount": amount, "currency": currency, "timeout": "5min"
})
# Buyer consumer confirms or fails; saga orchestrator finalizes or compensates
Design entity structure before ERP implementation; include 3-5 year M&A projection. [src1]Separate exchange rate types: spot for transactions, closing for BS, average for P&L. [src5]IC matching/reconciliation process BEFORE auto-elimination; validate >98% matching rate first. [src4]Define arm's-length transfer pricing methodology per IC type; embed in integration logic.Load test with 6 months of production-volume data before go-live. [src5]Rolling close with IC cutoff at defined UTC timestamp; T+1 adjustment window.# SAP: Check intercompany balances across company codes
curl -X GET "https://{sap-host}/sap/opu/odata4/sap/api_journalentryitembasic/srvd_a2x/\
A_JournalEntryItemBasic?\$filter=IsIntercompanyTransaction eq true and FiscalYear eq '2026'" \
-H "Authorization: Bearer {token}" -H "Accept: application/json"
# NetSuite: List IC transactions pending elimination (SuiteQL)
curl -X POST "https://{account-id}.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql" \
-H "Authorization: OAuth ..." -H "Content-Type: application/json" \
-d '{"q": "SELECT id, trandate, subsidiary, tosubsidiary, amount FROM transaction WHERE type = '\''InterCompJrnl'\'' AND status = '\''pendingApproval'\''"}'
# D365: Check intercompany accounting setup
curl -X GET "https://{d365-host}/data/IntercompanyAccounting?\$filter=IsActive eq true" \
-H "Authorization: Bearer {token}"
# Check exchange rate gaps (SAP TCURR table query)
# SE16 > TCURR: SELECT * FROM TCURR WHERE GDATU BETWEEN start AND end AND FCURR = 'USD'
# Verify COA mapping completeness
# SELECT local_account FROM coa_mapping WHERE group_account IS NULL
| Capability | SAP S/4HANA | NetSuite OneWorld | D365 Finance | Oracle ERP Cloud | Salesforce |
|---|---|---|---|---|---|
| Entity model | Company Code | Subsidiary | Legal Entity | BU / Legal Entity | Org |
| Max entities | Unlimited | 300 (10 levels) | Unlimited | Unlimited | N/A |
| Multi-currency | Native + parallel ledgers | Native + multi-book | Native + secondary | Native + subledger | CurrencyIsoCode |
| IC transactions | Advanced IC Sales + ICMR | Built-in IC | Due-to/Due-from | Automated IC | None |
| IC reconciliation | ICMR (real-time) | Manual or scripted | Manual / Power Automate | FC Hub | N/A |
| IC elimination | Group Reporting / BPC | Elimination schedules | Consolidation company | FCCS | N/A |
| FX revaluation | FAGL_FC_VAL | Automated | Reval journal | Period close | N/A |
| COA approach | Parallel COA | Segmented per subsidiary | Financial dimensions | Flex Value Sets | N/A |
| Data residency | Regional deployments | Data center selection | Azure regions | OCI regions | Hyperforce |
| Consolidation | Group Reporting + BPC | Built-in + NSPB | Financial Reporter | FCCS / EPM | N/A |
| Use When | Don't Use When | Use Instead |
|---|---|---|
| 5+ entities across 3+ countries with IC transactions | Single entity, single currency | System-specific API capability card |
| Consolidated financials across heterogeneous ERP landscape | All entities on same instance with built-in consolidation | ERP vendor's native consolidation docs |
| Data residency requirements constrain deployment | No PII in ERP data | Standard integration patterns |
| M&A requires integrating acquired entities | Stable entity structure, no acquisitions | System-specific integration playbook |
| Multi-currency with 5+ volatile currencies | Single-currency or pegged currencies | Basic currency configuration guide |