Skip to content

Mappings and master data contract

Mappings convert validated source identity into approved Accountify policy and Odoo targets. They are business configuration, not connector transformation code. A worker never searches Odoo and silently selects the first plausible record.

Mapping types

Type Source key Target
company source connection + market/legal entity allowed Odoo company
product source platform + variant ID and/or SKU Odoo product and unit of measure
customer source customer or B2C strategy key Odoo partner or approved anonymous partner strategy
tax jurisdiction + tax code/title + rate + inclusive flag + supply class Odoo tax and fiscal position
shipping source shipping/service code Odoo service product and tax treatment
discount discount type/funding party allocation rule or Odoo discount product
payment provider + transaction type + currency Odoo payment journal, method, clearing account
fee provider + fee/adjustment type + currency account, tax, analytic dimensions
warehouse source location/fulfillment service Odoo warehouse, picking type, locations
return_disposition source location + reason/disposition Odoo return destination and handling rule
dispute provider + dispute type/stage clearing/expense account and review rule

Data model

create table mapping_sets (
  tenant_id uuid not null,
  id uuid not null default gen_random_uuid(),
  version integer not null,
  status text not null check (status in ('draft','approved','retired')),
  effective_from timestamptz not null,
  effective_to timestamptz,
  approved_by uuid,
  approved_at timestamptz,
  created_at timestamptz not null default now(),
  primary key (tenant_id, id),
  unique (tenant_id, version),
  check (effective_to is null or effective_to > effective_from)
);

create table mapping_rules (
  tenant_id uuid not null,
  id uuid not null default gen_random_uuid(),
  mapping_set_id uuid not null,
  mapping_type text not null,
  source_connection_id uuid,
  company_id bigint not null,
  precedence integer not null check (precedence > 0),
  source_key jsonb not null,
  source_key_sha256 char(64) not null,
  target jsonb not null,
  evidence jsonb not null,
  status text not null check (status in ('active','blocked','retired')),
  created_at timestamptz not null default now(),
  primary key (tenant_id, id),
  unique (tenant_id, mapping_set_id, mapping_type, source_key_sha256),
  foreign key (tenant_id, mapping_set_id)
    references mapping_sets(tenant_id, id)
);

create table mapping_usage (
  tenant_id uuid not null,
  mapping_rule_id uuid not null,
  posting_intent_id uuid not null,
  resolved_at timestamptz not null,
  target_fingerprint char(64) not null,
  primary key (tenant_id, mapping_rule_id, posting_intent_id)
);

Approved mapping sets and rules are immutable. A correction creates a new set version. Effective ranges for approved sets may not overlap for the same tenant and posting profile.

Resolution algorithm

  1. Select the approved mapping set captured by the tenant posting policy and operation date.
  2. Restrict rules to the tenant, source connection where applicable, allowed Odoo company, mapping type, and active status.
  3. Canonicalize the source key and compute its hash.
  4. Match exact keys before any approved pattern rule.
  5. Apply the lowest numeric precedence only when exactly one rule remains.
  6. Validate the target through current source/Odoo capability evidence.
  7. Persist the rule ID, set version, target fingerprint, and evidence in the posting intent.
  8. Return a stable exception when no rule or multiple rules remain.

Resolution is deterministic: the same canonical inputs, policy version, mapping set, and capability evidence produce the same result.

Matching constraints

  • Product mapping prefers immutable source variant ID. SKU may be an additional exact key but is not assumed globally unique.
  • Customer matching may use a verified source customer ID. Email or name alone cannot auto-link a financial partner.
  • Tax mapping requires jurisdiction/scope and inclusive behavior; rate alone is insufficient.
  • Account, journal, tax, product, warehouse, and location targets must be active and company-correct.
  • Currency-specific mappings never apply to another currency.
  • A generic fallback product, customer, tax, account, or warehouse requires explicit accounting approval and a visible exception marker.
  • Fuzzy matching can rank suggestions but cannot create an active rule without reviewer approval.

Approval and separation of duties

Mapping Preparer Approver
Product/customer Integration preparer Integration reviewer
Tax/fiscal position Accounting preparer Accounting approver
Journal/account/payment/fee/dispute Accounting preparer Accounting approver
Warehouse/return disposition Operations preparer Inventory/accounting approver

The same user cannot approve a mapping set containing their own accounting-sensitive rule changes. Approval records the diff, reason, evidence, affected tenants/workflows, and effective time.

Drift

Target fingerprints include relevant Odoo write_date, active state, company, currency, account type, tax use/type, journal type, and warehouse/location identity. Source fingerprints include stable external identity and required semantic fields.

Drift marks the rule blocked or the mapping set degraded when:

  • the Odoo target is archived, deleted, moved to another company, or materially reconfigured;
  • source identity becomes ambiguous or a duplicate SKU appears;
  • a tax rate/scope or fiscal position changes;
  • a journal/account/payment method no longer supports the policy;
  • a warehouse/location route or valuation configuration changes;
  • the required capability evidence expires.

New posting intents fail with MAPPING_TARGET_DRIFTED. Confirmed historical operations keep their immutable mapping evidence.

API

GET  /api/v1/tenants/{tenant_id}/mapping-sets
POST /api/v1/tenants/{tenant_id}/mapping-sets
GET  /api/v1/tenants/{tenant_id}/mapping-sets/{mapping_set_id}
POST /api/v1/tenants/{tenant_id}/mapping-sets/{mapping_set_id}/rules
POST /api/v1/tenants/{tenant_id}/mapping-sets/{mapping_set_id}/validate
POST /api/v1/tenants/{tenant_id}/mapping-sets/{mapping_set_id}/approve
POST /api/v1/tenants/{tenant_id}/mapping-sets/{mapping_set_id}/retire
GET  /api/v1/tenants/{tenant_id}/mapping-exceptions
POST /api/v1/tenants/{tenant_id}/mapping-exceptions/{exception_id}/resolve

Writes use If-Match, reject unknown properties, and require Idempotency-Key for approval/retirement commands. Validation returns a complete diff and does not mutate Odoo.

Stable errors

Code Meaning
MAPPING_REQUIRED No approved rule matches
MAPPING_AMBIGUOUS Multiple rules have equal effective precedence
MAPPING_SET_NOT_APPROVED Policy references a draft/retired set
MAPPING_SET_OUTSIDE_EFFECTIVE_RANGE No approved set applies to the operation date
MAPPING_TARGET_INACTIVE Target is archived/deleted
MAPPING_TARGET_COMPANY_MISMATCH Target is outside the allowed Odoo company
MAPPING_TARGET_DRIFTED Target fingerprint differs from approved evidence
MAPPING_CAPABILITY_EXPIRED Required source/Odoo evidence is stale

Acceptance criteria

  • Database constraints prevent overlapping approved effective ranges and duplicate canonical keys.
  • Property tests prove deterministic resolution independent of database row order.
  • Cross-company, cross-currency, inactive, ambiguous, and stale targets fail closed.
  • A mapping change cannot alter a previously approved posting intent.
  • Approval separation, audit, rollback-of-release, and drift detection are tested.