core PK: id 8 required 1 unique

Description

Junction entity linking a contact to an organization unit (chapter). A contact in NHF may belong to up to 5 local chapters simultaneously. This relationship is critical for multi-chapter membership handling and for cross-chapter duplicate activity detection to prevent double-counting in Bufdir reporting.

10
Attributes
6
Indexes
8
Validation Rules
23
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Surrogate primary key for the membership record. Used for stable foreign-key references in audit logs and duplicate-warning event logs.
PKrequiredunique
contact_id uuid Foreign key referencing the contact who holds this chapter membership. Part of the composite unique constraint preventing duplicate memberships.
required
organization_unit_id uuid Foreign key referencing the organization unit (local chapter) the contact belongs to. Must resolve to a unit of type 'chapter' in the organization hierarchy.
required
role_in_chapter string Optional free-text role label describing the contact's function within this specific chapter (e.g., 'peer_mentor', 'board_member', 'volunteer'). Not normalized; used for display and reporting only.
-
joined_at datetime Timestamp (UTC) recording when the contact joined or was added to this chapter. Used to order affiliations chronologically and for audit purposes.
required
is_primary boolean Indicates whether this is the contact's primary chapter for reporting attribution. Exactly one membership per contact per organization may be primary. Enforced by a partial unique index at the database layer.
required
status enum Membership lifecycle state. Active memberships are used for activity queries and Bufdir aggregation. Inactive records are retained for audit history.
required
created_by uuid User ID of the coordinator or admin who created this membership record. Stored for audit trail and coordinator-scope validation.
-
created_at datetime Server-side creation timestamp. Set automatically on insert; never updated.
required
updated_at datetime Server-side last-modification timestamp. Updated via Supabase trigger on every row mutation.
required

Database Indexes

idx_contact_chapter_unique_membership
btree unique

Columns: contact_id, organization_unit_id

idx_contact_chapter_contact_id
btree

Columns: contact_id

idx_contact_chapter_org_unit_id
btree

Columns: organization_unit_id

idx_contact_chapter_primary_per_contact
btree unique

Columns: contact_id

idx_contact_chapter_status_active
btree

Columns: contact_id, status

idx_contact_chapter_joined_at
btree

Columns: contact_id, joined_at

Validation Rules

contact_id_required_and_exists error

Validation failed

organization_unit_id_required_and_exists error

Validation failed

max_active_memberships_count error

Validation failed

role_in_chapter_max_length error

Validation failed

joined_at_not_future warning

Validation failed

status_transition_validity error

Validation failed

reactivation_respects_max_limit error

Validation failed

multi_chapter_affiliation_chip_ui_limit info

Validation failed

Business Rules

max_five_chapters_per_contact
on_create

A single contact may belong to at most 5 local chapters simultaneously. This is an NHF-specific constraint reflecting the maximum simultaneous chapter memberships allowed by the organization. Any attempt to add a 6th active membership must be rejected with a clear user-facing error.

single_primary_chapter_per_contact
on_create

Each contact may have at most one primary chapter (is_primary = true) across all their active memberships. When a new primary is designated, any existing primary membership for that contact must be demoted to is_primary = false atomically within the same transaction.

no_duplicate_chapter_membership
on_create

The combination of contact_id and organization_unit_id must be unique across active memberships. Re-activating a previously inactive membership is permitted by updating status rather than inserting a new row.

chapter_unit_type_enforcement
on_create

The referenced organization_unit_id must correspond to a unit with type 'chapter' (leaf-level node) in the organizational hierarchy. Regional or national units cannot be used as chapter membership targets.

cross_chapter_activity_deduplication
always

When a contact belongs to multiple chapters and an activity is submitted, the cross-chapter-activity-query must be used to detect if the same activity (same contact_id, activity_type, and date) already exists in any of the contact's other chapters before allowing submission. This prevents Bufdir double-counting.

coordinator_scope_enforcement
on_create

A coordinator may only add or remove chapter memberships for contacts within chapters they are assigned to. The Supabase RLS policy and service-layer validation both enforce this restriction.

soft_delete_on_removal
on_delete

Chapter memberships are never hard-deleted from the database. Removing a contact from a chapter sets status to 'inactive' and records updated_at. Historical membership data is preserved for audit and Bufdir reporting traceability.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage

Entity Relationships

contact
incoming one_to_many

A contact (especially in NHF) may belong to up to 5 local chapters simultaneously through the contact_chapter junction

optional cascade delete
organization_unit
outgoing many_to_one

Each contact-chapter membership links to a specific organization unit (local chapter) in the hierarchy

required