TorbatYar/docs/loyalty-phase-7-2.md
Mortezakoohjani 071c484530 Ship Loyalty phases 7.2-7.6 (points through wallet) with production deploy.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 21:27:22 +03:30

3.3 KiB
Raw Permalink Blame History

Phase 7.2 — Point Engine (Immutable Ledger)

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

Objective

Deliver the Point Engine: append-only immutable ledger as the sole source of point balances, with earn / redeem / adjust / expire operations, idempotency, and balance APIs — without rewards redemption, campaigns, wallet, or gift cards.

Scope

In Scope

  • PointLedgerEntry append-only model (signed amounts; balance_after)
  • Balance = SUM(amount) — never stored on PointAccount
  • Earn, redeem, adjust, expire operations
  • Idempotency via optional idempotency_key (tenant-unique)
  • Insufficient-balance rejection on redeem/expire
  • Permissions, events, validators, migration 0003_phase_72_points
  • Tests + documentation + handover + service snapshot

Out of Scope

  • Reward catalog redemption flows (7.3)
  • Campaign rule engine (7.4)
  • Referral / wallet / gift cards / analytics / partner APIs (7.57.9)

Ledger Rules

Rule Detail
Append-only No UPDATE/DELETE of ledger rows
Signed amounts Earn/adjust+ positive; redeem/expire/adjust negative
Open accounts only Frozen/closed accounts reject mutations
Idempotency Same (tenant_id, idempotency_key) returns existing entry
Balance Computed from ledger; never a mutable column on PointAccount

Models

Entity Soft delete Audit Tenant
PointLedgerEntry No (append-only) Via fields + audit log Yes

APIs

Method Path Permission
GET /api/v1/point-accounts/{id}/balance loyalty.points.view
GET /api/v1/point-accounts/{id}/ledger loyalty.points.view
POST /api/v1/point-accounts/{id}/earn loyalty.points.earn
POST /api/v1/point-accounts/{id}/redeem loyalty.points.redeem
POST /api/v1/point-accounts/{id}/adjust loyalty.points.adjust
POST /api/v1/point-accounts/{id}/expire-points loyalty.points.expire

Events

Event When
loyalty.points.earned Earn
loyalty.points.redeemed Redeem
loyalty.points.adjusted Adjust
loyalty.points.expired Expire points

Migration

Item Detail
Alembic 0003_phase_72_points (down_revision 0002_phase_71_membership)
Breaking None — additive table
Backfill None required

Tests

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

Known Limitations

  • No automatic expire scheduler (API-driven expire only)
  • No FIFO lot tracking for expiration (bulk expire by amount)
  • Transfer still does not migrate ledger entries (explicit follow-up if needed)

Next Phase

Phase 7.3 — Rewards Engine