TorbatYar/backend/services/loyalty/app/validators/referral.py
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

96 lines
3.1 KiB
Python

"""Phase 7.5 Referral Engine validators — program status, attribution rules, transitions."""
from __future__ import annotations
from uuid import UUID
from shared.exceptions import AppError
from app.models.types import (
ReferralAttributionStatus,
ReferralCodeStatus,
ReferralProgramStatus,
)
def validate_referral_program_status(
status: ReferralProgramStatus | str,
) -> ReferralProgramStatus:
if isinstance(status, ReferralProgramStatus):
return status
try:
return ReferralProgramStatus(status)
except ValueError as exc:
raise AppError(
"وضعیت برنامه معرفی نامعتبر است",
status_code=422,
error_code="invalid_referral_program_status",
) from exc
def ensure_referral_program_active(status: ReferralProgramStatus) -> None:
if status != ReferralProgramStatus.ACTIVE:
raise AppError(
"برنامه معرفی فعال نیست",
status_code=409,
error_code="referral_program_not_active",
)
def ensure_code_active(status: ReferralCodeStatus) -> None:
if status != ReferralCodeStatus.ACTIVE:
raise AppError(
"کد معرفی فعال نیست",
status_code=409,
error_code="referral_code_not_active",
)
def ensure_not_self_referral(referrer_member_id: UUID, referee_member_id: UUID) -> None:
if referrer_member_id == referee_member_id:
raise AppError(
"معرفی خود فرد مجاز نیست",
status_code=422,
error_code="referral_self_referral_forbidden",
)
def ensure_max_referrals_not_exceeded(
current_count: int, max_referrals: int | None
) -> None:
if max_referrals is not None and current_count >= max_referrals:
raise AppError(
"سقف تعداد معرفی‌های این عضو تکمیل شده است",
status_code=409,
error_code="referral_max_referrals_exceeded",
)
def ensure_attribution_pending(status: ReferralAttributionStatus) -> None:
if status != ReferralAttributionStatus.PENDING:
raise AppError(
"وضعیت معرفی برای تبدیل مناسب نیست",
status_code=409,
error_code="referral_not_pending",
)
def ensure_attribution_converted(status: ReferralAttributionStatus) -> None:
if status != ReferralAttributionStatus.CONVERTED:
raise AppError(
"معرفی باید ابتدا تبدیل شده باشد",
status_code=409,
error_code="referral_not_converted",
)
def ensure_attribution_cancellable(status: ReferralAttributionStatus) -> None:
if status in (
ReferralAttributionStatus.REWARDED,
ReferralAttributionStatus.CANCELLED,
):
raise AppError(
"این معرفی قابل لغو نیست",
status_code=409,
error_code="referral_not_cancellable",
)