TorbatYar/backend/services/loyalty/app/repositories/rewards.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

106 lines
3.7 KiB
Python

"""Reward redemption repositories — Phase 7.3."""
from __future__ import annotations
from uuid import UUID
from sqlalchemy import func, select
from app.models.rewards import RewardRedemption
from app.models.types import RedemptionStatus
from app.repositories.base import TenantBaseRepository
_NON_BLOCKING_STATUSES = (RedemptionStatus.CANCELLED,)
class RewardRedemptionRepository(TenantBaseRepository[RewardRedemption]):
model = RewardRedemption
async def get_by_idempotency(
self, tenant_id: UUID, idempotency_key: str | None
) -> RewardRedemption | None:
if not idempotency_key:
return None
stmt = select(RewardRedemption).where(
RewardRedemption.tenant_id == tenant_id,
RewardRedemption.idempotency_key == idempotency_key,
)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
async def count_for_member_reward(
self,
tenant_id: UUID,
member_id: UUID,
reward_id: UUID,
*,
exclude_statuses: tuple[RedemptionStatus, ...] = _NON_BLOCKING_STATUSES,
) -> int:
stmt = (
select(func.count())
.select_from(RewardRedemption)
.where(
RewardRedemption.tenant_id == tenant_id,
RewardRedemption.member_id == member_id,
RewardRedemption.reward_id == reward_id,
RewardRedemption.status.notin_(exclude_statuses),
)
)
result = await self.session.execute(stmt)
return int(result.scalar_one())
async def list(
self,
tenant_id: UUID,
*,
reward_id: UUID | None = None,
member_id: UUID | None = None,
status: RedemptionStatus | None = None,
offset: int = 0,
limit: int = 50,
):
clauses = [RewardRedemption.tenant_id == tenant_id]
if reward_id is not None:
clauses.append(RewardRedemption.reward_id == reward_id)
if member_id is not None:
clauses.append(RewardRedemption.member_id == member_id)
if status is not None:
clauses.append(RewardRedemption.status == status)
stmt = (
select(RewardRedemption)
.where(*clauses)
.order_by(RewardRedemption.created_at.desc())
.offset(offset)
.limit(limit)
)
result = await self.session.execute(stmt)
return result.scalars().all()
async def count(
self,
tenant_id: UUID,
*,
reward_id: UUID | None = None,
member_id: UUID | None = None,
status: RedemptionStatus | None = None,
) -> int:
clauses = [RewardRedemption.tenant_id == tenant_id]
if reward_id is not None:
clauses.append(RewardRedemption.reward_id == reward_id)
if member_id is not None:
clauses.append(RewardRedemption.member_id == member_id)
if status is not None:
clauses.append(RewardRedemption.status == status)
stmt = select(func.count()).select_from(RewardRedemption).where(*clauses)
result = await self.session.execute(stmt)
return int(result.scalar_one())
async def list_for_member(
self, tenant_id: UUID, member_id: UUID, *, offset: int = 0, limit: int = 50
):
return await self.list(tenant_id, member_id=member_id, offset=offset, limit=limit)
async def list_for_reward(
self, tenant_id: UUID, reward_id: UUID, *, offset: int = 0, limit: int = 50
):
return await self.list(tenant_id, reward_id=reward_id, offset=offset, limit=limit)