---
id: CON-CRM-ENT-020
name: CRM Settings
feature: FEAT-CRM-ENT-06
product_area: crm
module: entities
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
source_sha:
  services-crm: 7ea48a414c42eade10d60753b8bcc323b1dd726f
  react-console: da63075876b2c5f4a6af07d457bb24ea8bf4c0c7
definition: 'CRM settings hold the workspace’s defaults for CRM forms, layouts and affiliation processing.'
owned_by: MOD-CRM-ENT
facets: []
evidence:
  - >-
    `services-crm` at `7ea48a414c42eade10d60753b8bcc323b1dd726f`:
    `modules/entities/db/crm-settings.table.ts` records this concept.
narrative:
  status: verified
  review: NARRATIVE-REVIEW-2026-10-23
narrative_sources:
  - section: Why It Exists
    provenance: inferred
    source: >-
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#is not answered the section
      it may not read: it is ABSENT, not null, so "forbidden" and "unset" never look alike;
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#writes NEITHER section for
      that refused save: the decision is made before anything is written;
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#refuses a save from a stale
      version (409);
      services-crm:modules/entities/tests/integration/services/persistence/crm-settings.integration.spec.js#refuses
      a definition of the wrong kind: a console form in the console page layout slot;
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#KEEPS all five through a PUT
      that does not mention them: an older client cannot wipe a tenant’s assignments;
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#falls back to the tenant’s
      default for a kind the type leaves empty;
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#is refused the tenant’s
      defaults (403): reading a record does not make one an administrator of layouts;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#answers 400 naming the
      field for a create whose name is nothing but spaces;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#answers 400 for an id
      that is not a configuration id, saying what an id may hold;
      services-crm:modules/entities/tests/integration/services/persistence/contact-roles.integration.spec.js#refuses
      it with the legacy words, naming the organization types whose rules depend on it;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0196.spec.ts#CRM-ACC-0196;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#refuses the delete (409)
      while an affiliation of a contact is of that type;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#refuses a caller who may
      only read affiliation types the change of the default (403);
      services-crm:modules/entities/tests/integration/services/persistence/affiliation-types.integration.spec.js#keeps
      the mappings, as an omitted collection means unchanged;
      services-crm:modules/entities/tests/e2e/contact-roles.e2e.spec.js#answers 400 for a reorder
      that leaves a role of the tenant out, naming it;
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#D-11;
      services-crm:modules/entities/tests/e2e/contact-roles.e2e.spec.js#answers a portal user of the
      tenant;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0199.spec.ts#CRM-ACC-0199;
      services-crm:modules/entities/tests/integration/services/persistence/contact-affiliations.business-dates.integration.spec.js#refuses
      a primary starting on the tenant's tomorrow, though it is already UTC's and Sydney's today;
      services-crm:modules/entities/tests/integration/services/persistence/contact-affiliations.business-dates.integration.spec.js#reads
      a tie ending on the tenant's today as current;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0186.spec.ts#CRM-ACC-0186;
      services-crm:modules/entities/tests/unit/services/affiliations/affiliation-date-window.spec.js#spans
      every day of a skipped weekend, so the next night makes them up;
      services-crm:modules/entities/tests/integration/services/affiliations/affiliation-date-reconciliation.integration.spec.js#takes
      the organization out of compliance the next morning, with no save of the contact;
      services-crm:modules/entities/tests/integration/services/affiliations/affiliation-date-reconciliation.integration.spec.js#finds
      the day settled and changes nothing, the organization’s version included;
      services-crm:modules/entities/tests/e2e/affiliation-date-reconciliation.e2e.spec.js#refuses a
      real run that names a day, 400 naming it; services-crm:docs/affiliations/README.md#5.6b The
      overnight date reconciliation (D-46; CRM-M2 A6 and A7)
    note: Business need inferred from the sourced behavior; no historical motivation is attributed.
  - section: What It Controls
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#is not answered the section
      it may not read: it is ABSENT, not null, so "forbidden" and "unset" never look alike;
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#writes NEITHER section for
      that refused save: the decision is made before anything is written;
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#refuses a save from a stale
      version (409);
      services-crm:modules/entities/tests/integration/services/persistence/crm-settings.integration.spec.js#refuses
      a definition of the wrong kind: a console form in the console page layout slot;
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#KEEPS all five through a PUT
      that does not mention them: an older client cannot wipe a tenant’s assignments;
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#falls back to the tenant’s
      default for a kind the type leaves empty;
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#is refused the tenant’s
      defaults (403): reading a record does not make one an administrator of layouts;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#answers 400 naming the
      field for a create whose name is nothing but spaces;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#answers 400 for an id
      that is not a configuration id, saying what an id may hold;
      services-crm:modules/entities/tests/integration/services/persistence/contact-roles.integration.spec.js#refuses
      it with the legacy words, naming the organization types whose rules depend on it;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0196.spec.ts#CRM-ACC-0196;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#refuses the delete (409)
      while an affiliation of a contact is of that type;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#refuses a caller who may
      only read affiliation types the change of the default (403);
      services-crm:modules/entities/tests/integration/services/persistence/affiliation-types.integration.spec.js#keeps
      the mappings, as an omitted collection means unchanged;
      services-crm:modules/entities/tests/e2e/contact-roles.e2e.spec.js#answers 400 for a reorder
      that leaves a role of the tenant out, naming it;
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#D-11;
      services-crm:modules/entities/tests/e2e/contact-roles.e2e.spec.js#answers a portal user of the
      tenant;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0199.spec.ts#CRM-ACC-0199;
      services-crm:modules/entities/tests/integration/services/persistence/contact-affiliations.business-dates.integration.spec.js#refuses
      a primary starting on the tenant's tomorrow, though it is already UTC's and Sydney's today;
      services-crm:modules/entities/tests/integration/services/persistence/contact-affiliations.business-dates.integration.spec.js#reads
      a tie ending on the tenant's today as current;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0186.spec.ts#CRM-ACC-0186;
      services-crm:modules/entities/tests/unit/services/affiliations/affiliation-date-window.spec.js#spans
      every day of a skipped weekend, so the next night makes them up;
      services-crm:modules/entities/tests/integration/services/affiliations/affiliation-date-reconciliation.integration.spec.js#takes
      the organization out of compliance the next morning, with no save of the contact;
      services-crm:modules/entities/tests/integration/services/affiliations/affiliation-date-reconciliation.integration.spec.js#finds
      the day settled and changes nothing, the organization’s version included;
      services-crm:modules/entities/tests/e2e/affiliation-date-reconciliation.e2e.spec.js#refuses a
      real run that names a day, 400 naming it; services-crm:docs/affiliations/README.md#5.6b The
      overnight date reconciliation (D-46; CRM-M2 A6 and A7)
---

# CRM Settings

## What It Is

CRM Settings holds workspace defaults used across contact and organization work. It includes default presentation assignments, the default affiliation type, and the date-reconciliation watermark. These are workspace-level choices and processing state, not values describing one person or one organization. Their scope lets staff share a baseline while type-specific presentation can override it.

The stored settings record is versioned, but its sections have different reading and updating authority. A caller receives only the sections it may inspect. Platform owns the definitions referenced by presentation settings; CRM owns the assignments and effective resolution behavior.

## Why It Exists

Our reading is that shared defaults prevent every coordinator or integration from making the same configuration choice independently. Workspace-wide presentation can serve the common case while classifications customize exceptions. A default affiliation type makes the Organization command usable without inventing a relationship category in each request, and a settled-day marker lets boundary processing resume reliably after missed work.

## What It Is Not

CRM Settings is not an organization record, a complete catalogue, or a Platform form or layout definition. It selects existing definitions and types rather than owning their content. The reconciliation watermark is processing state, not the effective date of every affiliation. Reading contact records or effective presentation does not give a staff member unrestricted authority over all settings sections.

## What It Controls

Presentation defaults supply the fallback after a type leaves a particular form or layout slot unset. Each slot resolves independently, including distinctions between create and edit. An unconfigured slot remains unconfigured rather than producing an invented definition. The default affiliation type supplies supported Organization-command creation when no explicit type is given.

The watermark identifies the last settled reconciliation day so later runs can include missed boundaries and avoid repeating completed effects. Settings updates preserve unnamed sections and require authority for every section named. A stale settings version prevents overwriting another administrator's changes, and forbidden sections are absent on reads rather than represented as empty defaults.

## How It Is Used

The CRM Administrator chooses baseline forms, layouts, and relationship defaults. The CRM Editor and CRM Reader retrieve effective definitions for everyday record work under their record authority. The CRM System uses settled-date state during Keep dated relationships current. Good use distinguishes a type override from a workspace default and an authorized unset value from information the caller cannot read.

## Example

An association chooses a default contact page layout and the Other affiliation type for ordinary Organization commands. A Member contact type overrides its edit form but leaves the page-layout slot empty, so staff receive the workspace layout. Another administrator concurrently changes organization defaults; a stale combined update is refused, preserving the newer settings rather than silently writing over them.

## How It Relates

Contact Type and Organization Type hold presentation overrides that resolve ahead of CRM Settings. Affiliation Type supplies the default relationship vocabulary. Platform metadata owns the selected forms and layouts, and Platform security evaluates section authority. Affiliation date reconciliation uses the watermark, but deployment configuration separately controls whether scheduled processing runs; the settings record does not imply an enabled scheduler.
