---
id: CAP-MBR-MEM-007
title: Maintain dated chapter and section affiliations
feature: FEAT-MBR-MEM-02
product_area: membership
module: memberships
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: partially-observed
supersedes:
  - 'inventory:CAP-MBR-AFF-001'
  - 'inventory:CAP-MBR-AFF-002'
  - 'inventory:CAP-MBR-AFF-003'
  - 'inventory:CAP-MBR-AFF-004'
  - 'inventory:CAP-MBR-AFF-005'
  - 'inventory:CAP-MBR-AFF-007'
  - 'inventory:CAP-MBR-AFF-008'
  - 'inventory:CAP-MBR-AFF-012'
  - 'inventory:CAP-MBR-AFF-013'
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-membership:README.md#2026-10-18 — Final acceptance'
  - >-
    services-membership:docs/migrations/membership-core/README.md#P2b: the merge with the structure module and
    the unit-bound parts
purpose: >-
  Staff and authorized officers maintain the units associated with each membership term under the applicable
  program and package rules.
outcomes:
  - Dated affiliations support chapter and ancestor rosters.
  - Affiliation operations apply configured eligibility and count restrictions.
  - Dues-bearing changes identify the purchase route that must handle the charge.
concepts:
  - CON-MBR-MEM-003
  - CON-MBR-MEM-002
actors:
  - ACT-MBR-007
  - ACT-MBR-009
surfaces:
  - console
  - portal
  - api
permissions:
  - key: membership.affiliations.manage
    where_checked: api
    scope: record
  - key: membership.members.read
    where_checked: api
    scope: record
rules:
  - id: BR-CAP-MBR-MEM-007-01
    proved_by: [ATS-MBR-MEM-007-01, TST-MBR-MEM-047]
    statement: Only affiliation-bound programs accept units on their terms or through subsequent affiliation additions.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-membership:tests/acceptance/affiliations/MBR-ACC-0263.acceptance.ts#MBR-ACC-0263: Ending a
      member's only chapter is refused, a cancelled term's chapters cannot be touched, and a program that does
      not bind by affiliation refuses chapters twice over
    origin: none
  - id: BR-CAP-MBR-MEM-007-02
    proved_by: [ATS-MBR-MEM-007-02, TST-MBR-MEM-024, TST-MBR-MEM-046]
    statement: >-
      A selected unit must be active, permit member affiliation through its type, and have a type allowed by
      the effective program and package rules.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:requireAffiliableUnit,
      refuseUnlessValid
    origin: none
  - id: BR-CAP-MBR-MEM-007-03
    proved_by: [ATS-MBR-MEM-001-14, ATS-MBR-MEM-003-09, ATS-MBR-MEM-006-14, ATS-MBR-MEM-007-03, ATS-MBR-MEM-007-14, TST-MBR-MEM-024, TST-MBR-MEM-038]
    statement: >-
      Counts stay within each type’s maximum across the term and meet its minimum from the operation’s
      effective day, with package counts overriding program counts.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/unit/unit-rules.spec.ts#refuses fewer affiliations of a
      type than its minimum from the day the operation takes effect
    origin: none
    examples:
      positive: A Regular package allowing one to three chapters accepts Atlanta and Savannah.
      negative: Ending the only required chapter is rejected for failing the minimum.
  - id: BR-CAP-MBR-MEM-007-04
    proved_by: [ATS-MBR-MEM-007-04, TST-MBR-MEM-038]
    statement: >-
      Unless multiple primaries are permitted, each type has at most one primary on a date; an addition
      without an explicit choice becomes primary only if none of that type is primary on its start day.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:addAffiliation;
      modules/memberships/services/affiliations/unit-rules.ts:affiliationSetProblems
    origin: none
  - id: BR-CAP-MBR-MEM-007-05
    proved_by: [ATS-MBR-MEM-007-05, TST-MBR-MEM-024]
    statement: A term cannot hold overlapping affiliations to the same unit.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/affiliations.integration.spec.ts#refuses
      the same unit twice on a day of a term (affiliation_overlap)
    origin: none
  - id: BR-CAP-MBR-MEM-007-06
    proved_by: [ATS-MBR-MEM-007-06, ATS-MBR-MEM-007-16, TST-MBR-MEM-024, TST-MBR-MEM-050]
    statement: >-
      Direct staff or officer additions and transfers into a unit with an active package-specific or catch-all
      dues product are refused with the required Commerce amendment kind.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/affiliations.integration.spec.ts#refuses
      to add a chapter that sells dues for the package, naming the Commerce amendment (requires_amendment)
    origin: none
  - id: BR-CAP-MBR-MEM-007-07
    proved_by: [ATS-MBR-MEM-002-08, ATS-MBR-MEM-007-07, ATS-MBR-MEM-007-13, TST-MBR-MEM-024, TST-MBR-MEM-054]
    statement: >-
      New staff terms use their named units from the term start; later terms with omitted affiliations inherit
      eligible active units held at the previous end, while an explicit empty list still faces the minimum
      rules.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:writeTermAffiliations,
      carriedAffiliations; modules/memberships/services/operations/staff-operations.ts:addTerm
    origin: 'RS-1115 (services-membership:docs/migrations/membership-core/README.md#P2b)'
  - id: BR-CAP-MBR-MEM-007-08
    proved_by: [ATS-MBR-MEM-007-08, ATS-MBR-MEM-007-15, ATS-MBR-MEM-008-10, ATS-MBR-MEM-020-12, TST-MBR-MEM-022, TST-MBR-MEM-048]
    statement: >-
      Ending defaults to yesterday and must shorten the current span; the day immediately before its start
      withdraws the row unless a later transfer continues it, while an earlier end date is invalid.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:endAffiliation'
    origin: none
  - id: BR-CAP-MBR-MEM-007-09
    proved_by: [ATS-MBR-MEM-007-09, ATS-MBR-MEM-007-18, ATS-MBR-MEM-015-11, TST-MBR-MEM-024]
    statement: >-
      Making a row primary clears overlapping same-type primary rows for their whole spans unless multiple
      primaries are allowed.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:losingPrimaryOf,
      setPrimaryAffiliation
    origin: none
  - id: BR-CAP-MBR-MEM-007-10
    proved_by: [ATS-MBR-MEM-007-10, TST-MBR-MEM-024]
    statement: >-
      Ending, transferring or changing the primary designation requires the current version of the affected
      affiliation row; additions do not submit an affiliation version.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:addAffiliationSchema,
      lockAffiliation
    origin: none
  - id: BR-CAP-MBR-MEM-007-11
    proved_by: [ATS-MBR-MEM-007-11, TST-MBR-MEM-021]
    statement: >-
      An affiliation addition waits for a concurrent unit merge holding the unit lock and then rejects a unit
      whose committed status is merged.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/affiliation-add-concurrency.integration.spec.ts#waits
      for a merge in flight and is then refused, because the unit it read is merged
    origin: none
  - id: BR-CAP-MBR-MEM-007-12
    proved_by: [ATS-MBR-MEM-007-12, ATS-MBR-MEM-007-17, ATS-MBR-MEM-014-12, TST-MBR-MEM-248]
    statement: >-
      Unit rosters apply dated affiliations and optional descendants from the requested date’s tree, returning
      a matching member only once.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-membership:tests/e2e/s05-multiple-chapters.e2e.spec.ts#returns Jo once in the Southeast Zone,
      Eastern Region and National rosters
    origin: none
  - id: BR-CAP-MBR-MEM-007-13
    proved_by: []
    statement: >-
      The product should accept a reduction that restores compliance after a package maximum is tightened
      below existing holdings.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: unknown
    source: FND-MBR-MEM-023
    origin: none
  - id: BR-CAP-MBR-MEM-007-14
    proved_by: []
    statement: >-
      The product should expose member chapter changes under the configured portal allowance while leaving
      staff free of that member-only restriction.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: unknown
    source: FND-MBR-MEM-022
    origin: none
scenarios:
  - id: SC-CAP-MBR-MEM-007-01
    given: 'Jo has a national membership affiliated to Atlanta and Savannah, with Atlanta primary.'
    when: Read chapter and ancestor rosters on a date covered by those affiliations.
    then: >-
      Both chapters and their ancestors include Jo once per roster, while the unrelated Western Region
      excludes her.
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/reads/membership-views.ts:membershipFiltersOf;
      modules/memberships/services/persistence/memberships.ts:listMembershipRows
  - id: SC-CAP-MBR-MEM-007-02
    given: Savannah has an active dues product for Jo’s Regular package.
    when: A coordinator requests a direct addition to Jo’s term.
    then: The refusal identifies the amendment needed to handle the chapter change and dues together.
    provenance: test
    source: >-
      services-membership:tests/acceptance/affiliations/MBR-ACC-0266.acceptance.ts#MBR-ACC-0266: A chapter
      that charges dues for the member's package cannot be added or moved into from the console, until its
      dues product is switched off
edge_cases: []
constraints:
  - A term-creation request accepts at most twenty named affiliations.
depends_on:
  - CAP-MBR-MEM-001
related:
  - CAP-MBR-MEM-008
  - CAP-MBR-MEM-015
  - CAP-MBR-MEM-009
journeys:
  - JNY-MBR-MEM-006
open_questions:
  - OQ-MBR-MEM-001
evidence:
  - repository: services-membership
    source_sha: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
    paths:
      - modules/memberships/services/affiliations/affiliation-operations.ts
      - modules/memberships/services/affiliations/unit-rules.ts
      - modules/memberships/services/persistence/term-affiliations.ts
      - modules/memberships/db/membership-term-organizational-units.table.ts
      - modules/memberships/db/membership-term-organizational-units-versions.table.ts
      - modules/memberships/tests/unit/unit-rules.spec.ts
      - modules/memberships/tests/integration/services/affiliations.integration.spec.ts
      - modules/memberships/tests/integration/services/affiliation-add-concurrency.integration.spec.ts
      - modules/memberships/tests/integration/services/affiliation-withdrawal-continued.integration.spec.ts
      - tests/e2e/s05-multiple-chapters.e2e.spec.ts
      - tests/acceptance/affiliations
  - repository: react-console
    source_sha: baf6dae382d6f41dce976c7f848667ccc37e076f
    paths:
      - acceptance/membership/reports/defects/affiliations.md
      - acceptance/membership/inventory/business-rules.json
controls: [TC-AVL-002, TC-AVL-003, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-006, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-007, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-003, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-SEC-001, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004]
exercised_by: [FAS-MBR-MEM-02-01, FAS-MBR-MEM-02-02, FAS-MBR-MEM-02-03, FAS-MBR-MEM-02-04, FAS-MBR-MEM-02-05]
suites: [TST-MBR-MEM-021, TST-MBR-MEM-022, TST-MBR-MEM-024, TST-MBR-MEM-038, TST-MBR-MEM-046, TST-MBR-MEM-047, TST-MBR-MEM-048, TST-MBR-MEM-050, TST-MBR-MEM-054, TST-MBR-MEM-248]
---

# Maintain dated chapter and section affiliations

## Purpose

Chapter and section ties place a membership period within the association’s structure. Staff use the dated ties to maintain rosters and make membership changes understandable to officers.

## How It Works

Staff supply affiliations when creating a term or use separate operations to adjust an existing effective period. An authorized officer uses the same operations within the units their authority reaches. The program and package together supply the rules for the resulting set. A later renewal distinguishes leaving affiliations unspecified from deliberately supplying an empty list. Staff can inspect the dates and primary designation of each row before ending it or changing its primary status. Transfers retain a connection between the old row and its continuation. A refusal directs dues-bearing changes to Commerce rather than completing a direct change. Portal self-service and repair of over-limit holdings remain proposed work recorded in findings.

## Relationships

- Membership creation and extension establish the terms carrying these affiliations.
- Organizational structure supplies units, unit types, ancestry and dues products.
- Officer scope uses these relationships to determine access to membership records.
