Traducción. Autoritativo: ../../adr/adr-011-points-ledger.md.
Decisión
loyalty.ledger_transactionses append-only; tipos (paramétricos): earn|redeem|expire|revoke|adjust; estados pending→available (el earn se confirma tras la ventana de devolución configurada por el tenant). Una columna de saldo mutable está PROHIBIDA.- Dos monedas por programa como mínimo:
redeemableystatus(XP). Nunca mezcladas (modelo aerolínea). - Los earn crean
loyalty.point_lotsconexpires_at; los canjes consumen lotes FIFO. Modos de expiración por programa (sobreescribibles por nivel): rolling N días | fin de mes | fin de año. El escaneopoints.expiring_soonalimenta campañas de retención. loyalty.contact_balanceses una proyección actualizada en la MISMA transacción que la escritura del libro (casi en tiempo real, no negociable); la reconciliación nocturna detecta drift (el add-on de frescura de datos sube la frecuencia de la reconciliación, nunca la del saldo).- Los canjes son padre/hijo (solicitudes multi-recompensa); rollback a nivel de padre con modos
revert|keep;
Idempotency-Keyobligatorio.money_component(monto + moneda, nullable) reserva la costura del canje mixto puntos + dinero (DEC-H3) — sin usar en F1.
Consecuencias
- Pasivo auditable, saldos reproducibles, semántica estándar de la industria. − Más filas que un saldo ingenuo; mitigado por la evaluación de particionamiento y las proyecciones.