---
id: DOM-CRM-REG-003
title: Regional Placement
realises: CON-CRM-REG-003
product_area: crm
module: regions
revision: 2
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-CRM-REG-regions
readable_by:
  - SVC-CRM-ENT-entities
lifecycle: STATE-CRM-REG-003
custom_fields: none
identity: >-
  Placement is a facet of a contact or organization, identified by that owning record. It has no independent
  record key and may contain no regional reference.
attributes:
  - name: current region
    kind: reference
    required: false
    computed: false
    notes: >-
      The owning record references one workspace region or none. Staff editing and automated matching can both
      set it.
  - name: automatic-assignment override
    kind: enum
    required: false
    computed: false
    notes: >-
      When enabled on the record, this exception prevents automatic matching and recalculation from replacing
      its region.
  - name: remembered deciding input
    kind: computed
    required: false
    computed: true
    notes: >-
      Processing stores an address-source fingerprint. Null means no prior decision; a no-address fingerprint
      records removal. Ordinary record saves cannot write it.
  - name: current deciding address
    kind: computed
    required: false
    computed: true
    notes: >-
      The policy selects a preferred or typed address read at processing time. A contact’s effective
      affiliation can resolve it from an organization.
  - name: record version consequence
    kind: computed
    required: true
    computed: true
    notes: >-
      Only a changed regional reference advances the owning record’s version and modification stamps;
      fingerprint maintenance alone leaves them intact.
relationships:
  - to: DOM-CRM-REG-001
    cardinality: A placement selects no more than one catalogue region.
    ownership: The owning record stores the regional reference and prevents deletion of its target.
    cross_service: false
  - to: DOM-CRM-REG-002
    cardinality: Each placement is evaluated against its workspace’s policy.
    ownership: Placement processing consumes policy without modifying it.
    cross_service: false
  - to: DOM-CRM-ENT-001
    cardinality: Every contact carries one regional facet.
    ownership: >-
      Entities stores the contact’s regional values and addresses; Regions computes automatic decisions
      through Entities persistence.
    cross_service: false
  - to: DOM-CRM-ENT-002
    cardinality: Every organization carries one regional facet.
    ownership: >-
      Entities stores the organization’s regional values; Regions updates them through the owning persistence
      functions.
    cross_service: false
  - to: DOM-CRM-ENT-003
    cardinality: A linked contact address can resolve through an effective affiliation.
    ownership: Entities owns the affiliation and resolves the linked address for regional reads.
    cross_service: false
invariants:
  - id: INV-DOM-CRM-REG-003-01
    statement: >-
      Reconciliation reads policy, locks eligible records and reads their current deciding addresses rather
      than event snapshots; policy reads are not locked into one consistent snapshot.
    enforced_at:
      - service
      - database
    provenance: observed
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#region
      assignment deliveries (integration) › two messages delivered out of order › ends in the region of the
      address the contact has now, not of the one the older message described;
      services-crm:modules/regions/services/region-assignment/region-assignment.ts:reconcileRegionAssignments;
      services-crm:modules/regions/services/persistence/region-assignment-settings.ts:readRegionAssignmentSettings
  - id: INV-DOM-CRM-REG-003-02
    statement: An enabled override prevents automatic reconciliation and recalculation from changing the stored region.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#region
      assignment (integration) › manual regions and the override › never changes the region of a contact that
      overrides automatic assignment
  - id: INV-DOM-CRM-REG-003-03
    statement: >-
      Without a current or remembered deciding address, reconciliation preserves staff placement; removal of a
      previously remembered address can clear it.
    enforced_at:
      - service
    provenance: observed
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#region
      assignment (integration) › moving › clears the region when the deciding address is removed;
      services-crm:modules/regions/services/region-assignment/region-assignment.ts:reconcileRegionAssignments
  - id: INV-DOM-CRM-REG-003-04
    statement: >-
      Regional persistence increments record versions and modification stamps only when the regional reference
      changes.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#region
      assignment deliveries (integration) › a deciding-address change that leaves the region where it was ›
      bumps no version, so the contact stands at the version its save answered
  - id: INV-DOM-CRM-REG-003-05
    statement: >-
      Placement persistence changes no address field and therefore does not trigger another regional
      assignment.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#region
      assignment deliveries (integration) › one delivery of one address change › is not called again by its
      own write, so an assignment cannot set off another
  - id: INV-DOM-CRM-REG-003-06
    statement: Automatic placement excludes records carrying a lock against updates.
    enforced_at:
      - service
      - database
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/contacts.ts:lockContactRegionTargets;
      services-crm:modules/entities/services/persistence/organizations.ts:lockOrganizationRegionTargets
  - id: INV-DOM-CRM-REG-003-07
    statement: >-
      New regional references stay within the record’s workspace, while tolerated legacy references may
      persist until the regional value changes.
    enforced_at:
      - service
      - database
    provenance: observed
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#region
      assignment (integration) › refusals › refuses a contact naming a region the tenant does not have;
      services-crm:db/migrations/0019_regions.sql
  - id: INV-DOM-CRM-REG-003-08
    statement: >-
      Organization placement reads that organization’s own deciding address without inheriting a parent’s
      region.
    enforced_at:
      - service
    provenance: observed
    source: >-
      react-console:acceptance/crm/inventory/business-rules.json#RUL-CRM-ORG-042;
      services-crm:modules/entities/services/persistence/organizations.ts:readOrganizationRegionAddresses
physical:
  - crm.contacts
  - crm.organizations
evidence:
  - >-
    services-crm at a8d1364f: modules/regions/services/region-assignment/region-assignment.ts,
    region-matching.ts; modules/entities/services/persistence/contacts.ts (lockContactRegionTargets,
    readContactRegionAddresses, setContactRegionAssignments, listContactsLinkingOrganizationRecords);
    modules/entities/services/persistence/organizations.ts (the organization's three); docs/change-events.md
    (region handlers); tests/integration/services/region-assignment/*.
---

# Regional Placement

## What It Models

Placement is the regional value staff see on a person or organization. Staff can choose it directly, or Regions can calculate it from geographic coverage. The record-level override protects a deliberate exception from later matching and recalculation.

## Identity and Attributes

The owning record supplies identity. Its regional facet holds the current region, override and remembered address fingerprint. That fingerprint includes the deciding source and recognized address fields. It distinguishes unchanged input from a removed address and from a record for which no address has ever been decided.

## Relationships

Entities owns the person or organization and stores the placement fields. Regions supplies automatic decisions through the record owner’s persistence functions. Contacts can resolve deciding addresses through effective affiliations; organization changes therefore need a separate linked-contact reaction.

## Invariants

Record locking prevents concurrent record writes from interleaving with a decision. Repeated input is skipped, overrides remain protected, and fingerprint-only maintenance preserves the version. The record lock does not freeze policy reads or a linked organization’s address. Those concurrency limits qualify the convergence claim.

## How It Is Stored and Shared

Placement remains on the contact or organization and is available through its record surfaces. Regions keeps no duplicate table. Ordinary saves omit the fingerprint, leaving regional processing responsible for its maintenance. Changed regions enter the record’s normal change and history mechanisms.

## Open Design Questions

Affiliation expiry alone produces no regional reconciliation. A later organization-address event or recalculation can update a placement whose linked input stopped resolving. Policy read consistency and the reported inactive-choice display gap also remain open.
