---
id: CAP-MBR-STR-013
title: Bundle grants and package-specific unit requirements
feature: FEAT-MBR-STR-04
product_area: membership
module: structure
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes:
  - react-console acceptance inventory CAP-MBR-CFG-006
  - react-console acceptance inventory CAP-MBR-CFG-007
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources: []
purpose: Administrators describe the additional memberships and affiliation requirements accompanying an offering.
outcomes:
  - One offering can describe grants in other membership programs.
  - Package requirements can specialize program-level affiliation counts.
concepts:
  - CON-MBR-STR-004
actors:
  - ACT-MBR-001
surfaces:
  - api
  - job
preconditions: []
postconditions: []
permissions:
  - key: membership.configuration.manage
    where_checked: api
    scope: tenant
  - key: membership.application.access
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-MBR-STR-013-01
    proved_by: [ATS-MBR-STR-013-01]
    statement: 'A package accepts at most ten submitted grants, and rejects a submitted grant naming its own program.'
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:packageResource
    origin: none
  - id: BR-CAP-MBR-STR-013-02
    proved_by: [ATS-MBR-STR-012-11, ATS-MBR-STR-013-02, TST-MBR-STR-095]
    statement: Each grant must pair a membership type with its own program.
    kind: validation
    enforced_at:
      - database
    must_prove: true
    provenance: test
    source: >-
      services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches, through
      the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column refuses ›
      refuses a grant whose type is not of the granted program 400 naming grants: membership_type_id, and
      writes no package
    origin: none
  - id: BR-CAP-MBR-STR-013-03
    proved_by: [ATS-MBR-STR-013-03, ATS-MBR-STR-013-08, ATS-MBR-STR-015-11]
    statement: A grant uses the primary term’s dates or a separately validated rule that the term engine can compute.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:packageResource
    origin: none
  - id: BR-CAP-MBR-STR-013-04
    proved_by: [ATS-MBR-STR-013-06]
    statement: >-
      Submitted grants replace the whole grant list and receive newly allocated identities on each
      replacement.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/persistence/membership-packages.ts:replacePackageChildren
    origin: none
  - id: BR-CAP-MBR-STR-013-05
    proved_by: [ATS-MBR-STR-013-04, TST-MBR-STR-044]
    statement: >-
      A package accepts at most twenty unit rules, each naming one existing affiliation-enabled kind with a
      maximum no lower than its minimum.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:unitRuleProblems
    origin: none
  - id: BR-CAP-MBR-STR-013-06
    proved_by: [ATS-MBR-STR-010-05, ATS-MBR-STR-013-05, TST-MBR-STR-002, TST-MBR-STR-044]
    statement: >-
      Package counts and dues choices override the corresponding program counts, while program primary, portal
      and suggestion choices remain; package-only kinds are allowed.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-membership:modules/memberships/services/affiliations/unit-rules.ts'
    origin: none
  - id: BR-CAP-MBR-STR-013-07
    proved_by: [ATS-MBR-STR-013-07]
    statement: >-
      Fixed ends, grants and unit requirements change only when their lists are submitted, with an empty list
      removing their rows.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-membership:modules/configuration/services/configuration/configuration-records.ts:packageResource
    origin: none
  - id: BR-CAP-MBR-STR-013-08
    proved_by: []
    statement: >-
      Grant configuration does not verify the target program’s activity, admitted owner kind or compatibility
      with the owner’s other holdings.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: documented
    source: 'react-console:acceptance/membership/inventory/business-rules.json#RUL-MBR-CFG-019'
    origin: none
scenarios:
  - id: SC-CAP-MBR-STR-013-01
    given: A student offering includes membership in the Section program.
    when: The student completes the purchase.
    then: The resulting holdings include both the primary program and Section.
    provenance: test
    source: >-
      services-membership:tests/acceptance/configuration/MBR-ACC-0111.acceptance.ts#MBR-ACC-0111: The
      well-known FAA configuration every developer and integration run starts from reads back exactly as the
      seed describes it, its packages' products exist with the right audiences, and a second seed run changes
      nothing
edge_cases:
  - id: SC-CAP-MBR-STR-013-02
    given: A package adds a category absent from its program requirements.
    when: A term is established under that package.
    then: The category is permitted using its package counts and default program-specific choices.
    provenance: test
    source: >-
      services-membership:tests/acceptance/configuration/MBR-ACC-0104.acceptance.ts#MBR-ACC-0104: A package's
      unit rules override the program's counts: the Regular package demands a chapter the program leaves
      optional, a Section-only package names a type the program does not list, a region is refused, and
      clearing the rules hands control back to the program
constraints: []
depends_on:
  - CAP-MBR-STR-012
  - CAP-MBR-STR-010
related:
  - CAP-MBR-STR-015
journeys: []
open_questions: []
evidence:
  - >-
    services-membership: Examination of package request schema, child replacement and the effective unit rules
    the memberships module reads.
  - >-
    services-membership: Examination of database refusals end-to-end, acceptance journeys MBR-ACC-0103, 0104
    and 0111.
  - 'react-console:acceptance/membership/inventory/business-rules.json'
  - 'services-membership:modules/configuration/services/configuration/configuration-records.ts'
  - 'services-membership:modules/configuration/services/persistence/membership-packages.ts'
  - 'services-membership:modules/memberships/services/affiliations/unit-rules.ts'
  - 'services-membership:tests/e2e/database-refusals.e2e.spec.ts'
controls: [TC-AVL-002, TC-AVL-003, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, 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-007, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-PI-001, TC-PI-003, TC-SEC-001, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003]
exercised_by: [FAS-MBR-STR-04-01, FAS-MBR-STR-04-02, FAS-MBR-STR-04-03, FAS-MBR-STR-04-04, FAS-MBR-STR-04-05]
suites: [TST-MBR-STR-002, TST-MBR-STR-044, TST-MBR-STR-095]
---

# Bundle grants and package-specific unit requirements

## Purpose

Administrators describe the additional memberships and affiliation requirements accompanying an offering.

## How It Works

An administrator configures the additional memberships associated with a package. A grant identifies another program and one of its classifications. Its dates can match the primary term or use a separate term rule. Staff also specify package-level affiliation counts and whether local dues accompany the offering. These choices specialize the program’s defaults for this package. The service validates submitted categories, counts and grant date rules. Each submitted list replaces its previous rows. Omitting a list retains it. Newly saved grants receive new identities, so consumers cannot treat a grant row as a permanent reference across replacements. Several target-program compatibility checks are absent. The product should define those checks and the handling of retained grants when a package’s program changes.

## Relationships

- The package owns the grant and unit-rule lists.
- Program affiliation settings continue to supply primary and portal choices.
- The term engine validates independently dated grants.
