Skip to main content
Wildo.ai Coming soon

Systems of record

Keep each subsidiary to its own company's records

When one ERP holds several companies, an operator maps each company to a unit of the organization, and people working in a unit see, change and act on that company's records only. Every mapping is justified and recorded.

Odoo companies map to Acme SA and Globex SARL separately; an Acme finance manager sees its invoices and receives Not found for a Globex invoice.

Keep each subsidiary to its own company's records

A group often runs several companies inside one ERP: one Odoo database holding the French, German and Spanish entities, for example. Without confinement, everybody in the application would see every company’s customers and invoices, because all of them belong to the same organization in the application.

An operator maps each company in the ERP to a unit of the organization. A person granted a role on a unit then works within that company: their lists show its records and the records that belong to no company, a record of another company answers as if it did not exist, a record they create lands in their company, and an action such as posting an invoice is refused on another company’s invoice. The restriction is sent to the ERP as part of every query, so totals and pages are correct and nothing is fetched only to be thrown away.

A company that nobody has mapped is read by nobody, not even by organization-wide administrators. Every mapping, move and removal is made by an application super-administrator, carries a written justification, and is recorded with the unit before and after.

Example — Finance managers per subsidiary

A group’s Odoo holds two companies. The operator maps company 1 to the unit “Acme SA” and company 2 to “Globex SARL”. A finance manager granted on the Acme unit lists invoices and sees Acme’s and the shared ones; opening a Globex invoice answers “not found”, and posting it is refused: the only call that reaches Odoo is the read that found the invoice belongs to Globex. A third company, created in Odoo later and not yet mapped, appears to nobody until the operator maps it.

For engineers

Partitioning is a property of the deployment, declared on the instance; how the vendor partitions is the facet’s. Wonder Todos’ declaration, comments omitted:

systemsOfRecord: {
  odoo: {
    vendor: 'odoo',
    dialect: HttpApiTransportDialect.ODOO_JSONRPC,
    partition: true,
    personalDataOrigin: SystemOfRecordPersonalDataOrigin.NO_PUBLICLY_ACCESSIBLE_SOURCES,
  },
},

The Odoo facet declares the partition as the company_id field, read as Odoo’s id-name pair and compared as an integer. Both were measured against a live multi-company Odoo: a domain comparing the id as text matches nothing and reports no error, so a wrong codec would have shown members empty lists that looked correct. A record whose company_id is unset is shared and belongs to every caller. partition: true on a vendor whose facet declares no partition is refused at startup, naming the vendor.

Map companies to units

Assignments are an application-scoped engine resource, systemOfRecordPartitionAssignments, reached at /applications/{applicationId}/system-of-record-partition-assignments by an application super-administrator. Each row names the instance, the remote key in canonical text and the unit, with a justification. One remote partition maps to at most one unit. A move is its own operation, which states the unit it expects to replace and is refused if another operator moved it first. Every transition is written to the audit trail with the unit it left.

Gate reads so confinement actually applies

Unit narrowing is additive: a role a person holds across the whole organization wins over one held on a unit. A read-through resource bound to a partitioned instance must therefore gate its reads on a role above the default member role, or startup refuses it, because every member would otherwise reach every company. Wonder Todos’ billableServices and customerInvoices gate reads, writes and the invoice action on ORG_MANAGER; a person granted that role on one unit only is confined to that unit’s company.

What each operation does under confinement

OperationConfinement
List, count, searchthe predicate is pushed to the ERP: for Odoo a domain matching the assigned company ids or an unset company_id, for OData an eq or-chain, for SuiteQL or SOQL a statement clause
Read by keya record of another company answers as not found
Createlands in the partition of the caller’s unit; the partition field is never accepted from the caller
Update, delete, record actionthe record is read first; another company’s record is refused as absent, and only that read leaves the application
Change to a shared recordallowed only to an organization-wide caller, since it affects every company
Copy-in pipelineeach copied row is stamped with the unit its company is assigned to (partitionUnitLocalField) and keeps the remote company key (partitionKeyLocalField); both are required on a partitioned instance. A row whose company is unassigned after it was copied is read by nobody until the company is assigned again
Outbound pipelinea created row is placed by its unit; an update is sent only if the record’s current company is the row’s unit’s

Building a B2B product or an internal tool?

Wildo is not self-service yet. Tell us what you have in mind and we will say plainly whether it fits, and what happens next.