← Back to Core Domains ← Back to Table of Contents

4.1 Member Lifecycle

Members are tenant-local identities. A member record holds login and profile data, branch affiliation, membership evidence, minor/parent relationships, and the inputs used to determine warrant eligibility. Authentication establishes the identity; CakePHP policies and scopes determine what that identity may do.

Status model

The source of truth is the constants on App\Model\Entity\Member.

Constant Stored value Meaning
STATUS_ACTIVE active Adult account without verified membership
STATUS_DEACTIVATED deactivated Account is disabled
STATUS_VERIFIED_MEMBERSHIP verified Adult membership has been verified
STATUS_UNVERIFIED_MINOR unverified minor Neither minor membership nor parent is verified
STATUS_MINOR_MEMBERSHIP_VERIFIED < 18 member verified Minor membership is verified; parent is not
STATUS_MINOR_PARENT_VERIFIED < 18 parent verified Parent relationship is verified; membership is not
STATUS_VERIFIED_MINOR verified < 18 Both required minor checks are complete

Do not reproduce these values in controllers, templates, or plugins. Use the entity constants.

Registration and verification

MemberRegistrationService owns registration support, membership-card validation/storage, and registration notification variables. Uploaded cards are stored through DocumentService; membership_card_document_id is the current association, while legacy file paths are supported only for transition compatibility.

Membership-card uploads accept JPEG/JFIF, PNG, GIF, and WebP raster images. Acceptance is based on server-detected file content and image metadata rather than the browser-provided MIME type or filename extension. Stored files use a canonical extension derived from the verified image type; SVG and other unapproved formats remain rejected.

Verification is an administrative workflow exposed through the member verification queue. Authorization still belongs in the controller policy path; a service call does not bypass it.

When a submitted membership card cannot be read, an authorized verifier can choose Request new upload. The request is bound to the card shown in the open form and stops if the member uploads a replacement before confirmation. Clearing the current card reference and dispatching the active membership-card-reupload-request workflow are one database transaction. The workflow reloads the member and sends the membership-card-reupload-requested email template to the member, with the configured account-verification contact address as the reply-to address. A workflow failure rolls the member record back so the existing card remains available. Stored-file cleanup runs after commit because filesystem deletion cannot be rolled back; a cleanup failure is logged and shown to the verifier without restoring a dangling card reference.

Member expiration imports

The Member Data Import screen updates either membership expiration dates or background-check expiration dates from CSV. The operator selects the date type, then uploads a two-column file containing Member Number and the corresponding expiration-date heading. MemberExpirationImportService validates the entire file—including exact row shape, duplicate membership numbers, and YYYY-MM-DD dates—before applying any updates through MembersTable; unmatched membership numbers are reported without blocking valid rows.

The narrow Can Import Member Data permission is granted to the shared Greater Officer of State role, but MemberPolicy permits that grant only while the member holds the current Kingdom Seneschal office assignment. The former broad member-management mapping does not grant access to this screen.

Automatic reviews

MembersTable::beforeSave() calls both entity reviews:

Because these checks occur on save, code that depends on a fresh result must persist the member through MembersTable; it should not update columns directly.

Roles and permissions

Members relate to MemberRoles, including current, upcoming, and previous windows. A role assignment alone is not a substitute for policy authorization: permissions, branch scope, active windows, and any required warrant are evaluated together. Use AuthorizationService, authorizeModel(), applyScope(), and entity policies instead of ad-hoc role-name checks.

Profile and privacy

MemberProfileService validates and replaces profile photos through DocumentService. Member policy and grid-column definitions protect profile and personally identifiable data. New read paths must be scoped before serialization or export; hiding a field in HTML is not an authorization control.

Multi-tenant rules

Primary code

For a compact table-layer reference, see MembersTable reference.