---
id: CAP-CRM-ENT-019
title: Resolve the forms and layouts used for CRM records
product_area: crm
module: entities
revision: 1
status: approved
product_intent: proposed
implementation_evidence: observed
source_sha:
  services-crm: 7ea48a414c42eade10d60753b8bcc323b1dd726f
  react-console: da63075876b2c5f4a6af07d457bb24ea8bf4c0c7
intent_sources: []
purpose: >-
  Staff obtain the applicable CRM forms and layouts, and administrators
  configure the defaults.
outcomes:
  - Each screen asks which definition is in force and gets one answer.
  - Each section of the settings is changed only by its own administrator.
actors:
  - ACT-CRM-001
  - ACT-CRM-003
  - ACT-CRM-004
surfaces:
  - console
  - portal
  - api
preconditions: []
postconditions: []
permissions:
  - key: crm.contact-layouts.read
    where_checked: api
    scope: tenant
  - key: crm.contact-layouts.update
    where_checked: api
    scope: tenant
  - key: crm.organization-layouts.read
    where_checked: api
    scope: tenant
  - key: crm.organization-layouts.update
    where_checked: api
    scope: tenant
  - key: crm.contacts.read
    where_checked: api
    scope: tenant
  - key: crm.organizations.read
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-CRM-ENT-019-01
    proved_by: [ATS-CRM-ENT-017-18, ATS-CRM-ENT-019-01, ATS-CRM-ENT-019-09, ATS-CRM-ENT-019-11, TST-CRM-ENT-786]
    statement: >-
      CRM settings reads return only authorized sections, omit forbidden
      sections rather than returning null and govern the default affiliation
      type with affiliation-type permissions.
    kind: permission
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    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
    origin: none
  - id: BR-CAP-CRM-ENT-019-02
    proved_by: [ATS-CRM-ENT-019-02, ATS-CRM-ENT-019-11, TST-CRM-ENT-786]
    statement: >-
      Settings updates require every named section’s update permission and make
      no change when any requested section is unauthorized.
    kind: permission
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      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
    origin: none
  - id: BR-CAP-CRM-ENT-019-03
    proved_by: [ATS-CRM-ENT-019-03, ATS-CRM-ENT-019-08, TST-CRM-ENT-786]
    statement: >-
      Settings updates check the read version, preserve unnamed sections and
      reject unknown properties.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#refuses a
      save from a stale version (409)
    origin: none
  - id: BR-CAP-CRM-ENT-019-04
    proved_by: [ATS-CRM-ENT-019-04, ATS-CRM-ENT-019-08, TST-CRM-ENT-786, TST-CRM-ENT-791]
    statement: >-
      Tenant or type layout assignments require tenant-owned definitions
      matching the record purpose and console or portal layout or form slot,
      with invalid assignments identified by section and field.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      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
    origin: none
  - id: BR-CAP-CRM-ENT-019-05
    proved_by: [ATS-CRM-ENT-018-15, ATS-CRM-ENT-019-05, ATS-CRM-ENT-019-10, TST-CRM-ENT-786]
    statement: >-
      Type replacements preserve their five omitted layout and form assignments,
      while removing or nulling one assignment through a patch clears only that
      slot.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      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
    origin: none
  - id: BR-CAP-CRM-ENT-019-06
    proved_by: [ATS-CRM-ENT-019-06, TST-CRM-ENT-786]
    statement: >-
      Effective definitions resolve each slot from the type override to the
      tenant default to not configured, distinguish create and edit forms,
      support console-only results and reject unknown types or missing record
      kinds.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      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
    origin: none
  - id: BR-CAP-CRM-ENT-019-07
    proved_by: [ATS-CRM-ENT-019-07, ATS-CRM-ENT-019-11, TST-CRM-ENT-786]
    statement: >-
      Record readers can retrieve effective definitions, while tenant-default
      administration requires the separate layout permissions.
    kind: permission
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      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
    origin: none
scenarios:
  - id: SC-CAP-CRM-ENT-019-01
    given: >-
      A tenant with a default contact layout and a Member type with its own edit
      form.
    when: >-
      A caller requests the effective Member edit-form and contact-layout
      definitions.
    then: >-
      The API returns the type’s edit-form assignment and the tenant’s default
      contact layout.
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#answers
      the type’s own definition for the kind the type assigns
edge_cases: []
constraints: []
depends_on: []
related:
  - CAP-CRM-ENT-017
  - CAP-CRM-ENT-018
journeys: []
open_questions: []
feature: FEAT-CRM-ENT-06
concepts:
  - CON-CRM-ENT-033
evidence:
  - >-
    `services-crm` at `7ea48a414c42eade10d60753b8bcc323b1dd726f`: examined
    `aws/api/crm-settings.routes.ts`, `crm-settings.schemas.ts`, `effective-definitions.routes.ts`,
    `effective-definitions.schemas.ts`, `services/persistence/crm-settings.ts`,
    `layout-definitions.ts`, `services/effective-definitions/resolve-effective-definitions.ts`,
    `db/crm-settings.table.ts`, `db/layout-slots.ts`, `docs/entities-api.md` ("Page layouts and
    forms"); tests `e2e/page-layouts.e2e.spec.js`,
    `integration/services/persistence/crm-settings.integration.spec.js`,
    `integration/services/effective-definitions/resolve-effective-definitions.integration.spec.js`.
  - '`react-console` at `da63075876b2c5f4a6af07d457bb24ea8bf4c0c7`: examined journey CRM-ACC-0146.'
approved_by: Andrew
approved_at: 2026-10-22
controls: [TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-DAT-001, TC-DAT-003, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-003, TC-TEN-001, TC-TEN-002, TC-TEN-003]
narrative:
  status: verified
  review: NARRATIVE-REVIEW-2026-10-23
narrative_sources:
  - section: Why It Works This Way
    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
    note: >-
      Operational rationale inferred from the cited behavior; no personal motive or historical
      decision is asserted.
  - section: Example
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/page-layouts.e2e.spec.js#refuses a save from a stale
      version (409); 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 type_id the tenant
      does not have (404) rather than answering the defaults
  - section: How It Works
    provenance: observed
    source: >-
      services-crm:modules/entities/aws/api/crm-settings.routes.ts:patchCrmSettings;
      services-crm:modules/entities/services/effective-definitions/resolve-effective-definitions.ts:resolveEffectiveDefinitions
repositories:
  - repo: services-crm
    role: owner
    paths:
      - modules/entities/
      - modules/entities/tests/e2e/page-layouts.e2e.spec.js
      - >-
        modules/entities/tests/integration/services/persistence/crm-settings.integration.spec.js
exercised_by: [FAS-CRM-ENT-06-01, FAS-CRM-ENT-06-02, FAS-CRM-ENT-06-03, FAS-CRM-ENT-06-04, FAS-CRM-ENT-06-05]
suites: [TST-CRM-ENT-786, TST-CRM-ENT-791]
---

# Resolve the forms and layouts used for CRM records

## Purpose

Staff need the appropriate record presentation without each CRM screen inventing its own selection logic. This capability resolves Platform-owned forms and layouts from record-type overrides and workspace defaults, and controls administration of those assignments. It distinguishes an unset presentation from a section the caller has no authority to inspect.

## How It Works

Settings reads expose only authorized sections; a forbidden section is omitted. An update checks every requested section's authority and the read settings version before writing, preserving unnamed sections. Unauthorized combined changes make no partial update, and unsupported properties are refused.

Assignments must refer to workspace definitions with the correct record purpose and presentation slot. Contact or organization type overrides take precedence over workspace defaults for each layout or form; a slot without either remains unconfigured. Create and edit forms are resolved separately, and callers can request console-only results. Type replacements preserve omitted assignments, while a targeted patch can clear an individual slot.

Record readers can retrieve effective definitions for their work without acquiring authority to administer the workspace defaults. CRM stores the assignment and resolution policy; Platform metadata owns the forms and layouts themselves.

## Example

An association assigns a default contact page layout and gives the Member contact type a different edit form. A coordinator opens a Member record and receives the type-specific edit form with the default page layout for the slot the type leaves unset. Clearing only the edit-form override falls back to the workspace choice without disturbing other assignments. A reader cannot use this retrieval permission to change those defaults.

The administrator checks the effective result after clearing the override, rather than assuming that clearing it means no form will be used. The returned choice identifies whether the type or workspace default supplies it; a slot with no candidate is unconfigured. This lets staff distinguish inheritance from an absent assignment before investigating the presentation itself.

If two administrators read the same settings and one saves first, the other's older settings version conflicts. She rereads before deciding which remaining change to submit. A request combining an authorized contact section with an unauthorized organization section is refused as a whole, preserving both prior defaults. Naming an unknown record type is also refused instead of silently returning the workspace's untyped answer. These cases are covered by the capability test specification and keep configuration mistakes distinct from ordinary empty slots.

## Why It Works This Way

Our reading is that per-slot fallback lets a type customize only what differs without duplicating every workspace assignment. Separating create and edit supports different entry tasks, and validating purpose and kind prevents a plausible-looking definition from being used in the wrong context. An unconfigured result is more honest than inventing a form.

Omitting unauthorized sections keeps forbidden information distinct from a genuinely unset default. All-section authorization before mutation prevents a combined settings request from succeeding halfway. Version checks protect concurrent administrators, and preservation of omitted type assignments prevents an older classification client from wiping presentation it does not understand. Effective-definition read authority supports everyday record work without giving readers configuration authority.

## Relationships

CRM Settings holds defaults, and Contact Type and Organization Type hold overrides. Platform metadata owns definition identity, purpose, and content through its contract. Record creation and record pages consume effective results. Classification administration preserves these assignments during ordinary type replacement, while Platform security evaluates each named section's authority rather than treating settings as one unrestricted record.
