feat(loyalty): Phase 6 — admin GDPR, bulk ops, point restore, cascade
Some checks failed
Some checks failed
Admin operations for production management:
- GDPR anonymization: DELETE /admin/loyalty/cards/customer/{id}
Nulls customer_id, deactivates cards, scrubs PII from transaction
notes. Keeps aggregate data for reporting.
- Bulk deactivate: POST /admin/loyalty/merchants/{id}/cards/bulk/deactivate
and POST /store/loyalty/cards/bulk/deactivate (merchant_owner only).
Deactivates multiple cards with audit trail.
- Point restore: POST /admin/loyalty/cards/{id}/restore-points
Creates ADMIN_ADJUSTMENT transaction with positive delta. Reuses
existing adjust_points service method.
- Cascade restore: POST /admin/loyalty/merchants/{id}/restore-deleted
Restores all soft-deleted programs and cards for a merchant.
Service methods: anonymize_cards_for_customer, bulk_deactivate_cards,
restore_deleted_cards, restore_deleted_programs.
342 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1127,5 +1127,30 @@ class ProgramService:
|
||||
return stats
|
||||
|
||||
|
||||
def restore_deleted_programs(self, db: Session, merchant_id: int) -> int:
|
||||
"""Restore all soft-deleted programs for a merchant.
|
||||
|
||||
Returns number of programs restored.
|
||||
"""
|
||||
from sqlalchemy import update
|
||||
|
||||
result = db.execute(
|
||||
update(LoyaltyProgram)
|
||||
.where(
|
||||
LoyaltyProgram.merchant_id == merchant_id,
|
||||
LoyaltyProgram.deleted_at.isnot(None),
|
||||
)
|
||||
.values(deleted_at=None, deleted_by_id=None)
|
||||
.execution_options(include_deleted=True)
|
||||
)
|
||||
db.commit()
|
||||
count = result.rowcount
|
||||
if count:
|
||||
logger.info(
|
||||
f"Restored {count} soft-deleted programs for merchant {merchant_id}"
|
||||
)
|
||||
return count
|
||||
|
||||
|
||||
# Singleton instance
|
||||
program_service = ProgramService()
|
||||
|
||||
Reference in New Issue
Block a user