TorbatYar/docs/loyalty-phase-7-1.md
Mortezakoohjani e41ecfad4c Sync platform docs, infra, and module services with Accounting integration.
Include Loyalty/Communication/Sports Center backends and registry updates alongside production nginx and compose wiring.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-25 22:35:23 +03:30

4.1 KiB

Phase 7.1 — Membership Engine

Field Value
Identifier loyalty-7.1
Status Complete
Module loyalty
Service loyalty-service
Version 0.7.1.0
Database loyalty_db
Depends On Phase 7.0
ADR(s) ADR-001, ADR-003, ADR-006, ADR-011
Manifest phase-manifest.yaml

Objective

Deliver the Membership Engine: lifecycle state machine, eligibility transitions, append-only lifecycle history, program cascade policy, and lifecycle APIs/events — without points ledger, rewards redemption, or campaign engines.

Scope

In Scope

  • Member lifecycle: activate, renew, freeze, resume, cancel, expire, transfer
  • Enroll path hardened with term / expiry and lifecycle events
  • MembershipLifecycleEvent append-only history
  • Cascade policy: reject program soft-delete while blocking members exist
  • Permissions, events, validators, migration 0002_phase_71_membership
  • Tests + documentation + handover

Out of Scope

  • Point ledger / balances (7.2)
  • Rewards redemption (7.3)
  • Campaign engine (7.4+)
  • Wallet / gift cards / partner network

Cascade Policy

Soft-deleting a LoyaltyProgram is rejected (409 program_has_active_members) when any non-deleted member exists in statuses: pending, active, frozen, suspended, expired. Cancelled / transferred / closed members do not block.

State Machine

Action Allowed from To
enroll / activate pending (activate also expired) active
renew active, expired, frozen, suspended active
freeze active frozen
resume frozen, suspended active
cancel pending, active, frozen, suspended, expired cancelled
expire active, frozen, suspended expired
transfer active, frozen, suspended transferred (+ new active member in target program)

Terminal: cancelled, transferred, closed.

Models

Entity Soft delete Audit Tenant
Member (extended fields) Yes Yes Yes
MembershipLifecycleEvent No (append-only) Via fields Yes

New member fields: activated_at, membership_started_at, membership_expires_at, frozen_at, freeze_reason, cancelled_at, cancel_reason, expired_at, transferred_to_member_id.

APIs

Method Path Permission
POST /api/v1/members/{id}/activate loyalty.members.activate
POST /api/v1/members/{id}/renew loyalty.members.renew
POST /api/v1/members/{id}/freeze loyalty.members.freeze
POST /api/v1/members/{id}/resume loyalty.members.resume
POST /api/v1/members/{id}/cancel loyalty.members.cancel
POST /api/v1/members/{id}/expire loyalty.members.expire
POST /api/v1/members/{id}/transfer loyalty.members.transfer
GET /api/v1/members/{id}/lifecycle loyalty.members.lifecycle.view

Existing create / enroll / update / delete remain compatible (additive fields).

Events

Event When
loyalty.member.activated Activate / enroll activation
loyalty.member.renewed Renew
loyalty.member.frozen Freeze
loyalty.member.resumed Resume
loyalty.member.cancelled Cancel
loyalty.member.expired Expire
loyalty.member.transferred Transfer (source)

Migration

Item Detail
Alembic 0002_phase_71_membership (down_revision 0001_initial)
Breaking None — additive columns + new table
Backfill None required

Tests

Command: cd backend/services/loyalty && pytest -q59 passed

Known Limitations

  • No automatic expire scheduler (API-driven expire only)
  • Transfer copies profile/contact; does not move point accounts (7.2)
  • JWT↔tenant binding remains platform-wide

Next Phase

Phase 7.2 — Point Engine (immutable ledger)