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:
@@ -810,3 +810,39 @@ def adjust_points(
|
||||
)
|
||||
|
||||
return PointsAdjustResponse(**result)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Bulk Operations (Merchant Owner only)
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@router.post("/cards/bulk/deactivate")
|
||||
@rate_limit(max_requests=10, window_seconds=60)
|
||||
def bulk_deactivate_cards(
|
||||
request: Request,
|
||||
data: dict,
|
||||
current_user: User = Depends(get_current_store_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Bulk deactivate multiple loyalty cards (merchant_owner only)."""
|
||||
if current_user.role != "merchant_owner":
|
||||
raise AuthorizationException("Only merchant owners can bulk deactivate cards")
|
||||
|
||||
from app.modules.tenancy.services.store_service import store_service
|
||||
|
||||
store = store_service.get_store_by_id_optional(db, current_user.token_store_id)
|
||||
if not store:
|
||||
raise AuthorizationException("Store not found")
|
||||
|
||||
card_ids = data.get("card_ids", [])
|
||||
reason = data.get("reason", "Merchant bulk deactivation")
|
||||
|
||||
count = card_service.bulk_deactivate_cards(
|
||||
db,
|
||||
card_ids=card_ids,
|
||||
merchant_id=store.merchant_id,
|
||||
reason=reason,
|
||||
)
|
||||
|
||||
return {"cards_deactivated": count, "message": f"Deactivated {count} card(s)"}
|
||||
|
||||
Reference in New Issue
Block a user