TorbatYar/backend/services/loyalty/app/repositories/foundation.py
Mortezakoohjani e41ecfad4c Sync platform docs, infra, and module services with Accounting integration.
Include Loyalty/Communication/Sports Center backends and registry updates alongside production nginx and compose wiring.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-25 22:35:23 +03:30

214 lines
6.9 KiB
Python

"""Loyalty foundation repositories — Phase 7.0."""
from __future__ import annotations
from uuid import UUID
from sqlalchemy import select
from app.models.foundation import (
Campaign,
LoyaltyAuditLog,
LoyaltyProgram,
Member,
MembershipLifecycleEvent,
MembershipTier,
PointAccount,
Reward,
)
from app.repositories.base import TenantBaseRepository
class LoyaltyProgramRepository(TenantBaseRepository[LoyaltyProgram]):
model = LoyaltyProgram
async def get_by_code(self, tenant_id: UUID, code: str) -> LoyaltyProgram | None:
stmt = select(LoyaltyProgram).where(
LoyaltyProgram.tenant_id == tenant_id,
LoyaltyProgram.code == code,
LoyaltyProgram.is_deleted.is_(False),
)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
class MembershipTierRepository(TenantBaseRepository[MembershipTier]):
model = MembershipTier
async def get_by_code(
self, tenant_id: UUID, program_id: UUID, code: str
) -> MembershipTier | None:
stmt = select(MembershipTier).where(
MembershipTier.tenant_id == tenant_id,
MembershipTier.program_id == program_id,
MembershipTier.code == code,
MembershipTier.is_deleted.is_(False),
)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
async def list_by_program(
self, tenant_id: UUID, program_id: UUID, *, offset: int = 0, limit: int = 50
):
stmt = (
select(MembershipTier)
.where(
MembershipTier.tenant_id == tenant_id,
MembershipTier.program_id == program_id,
MembershipTier.is_deleted.is_(False),
)
.order_by(MembershipTier.rank.asc(), MembershipTier.created_at.desc())
.offset(offset)
.limit(limit)
)
result = await self.session.execute(stmt)
return result.scalars().all()
class MemberRepository(TenantBaseRepository[Member]):
model = Member
async def get_by_membership_number(
self, tenant_id: UUID, membership_number: str
) -> Member | None:
stmt = select(Member).where(
Member.tenant_id == tenant_id,
Member.membership_number == membership_number,
Member.is_deleted.is_(False),
)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
async def get_by_external_ref(
self, tenant_id: UUID, program_id: UUID, external_customer_ref: str
) -> Member | None:
stmt = select(Member).where(
Member.tenant_id == tenant_id,
Member.program_id == program_id,
Member.external_customer_ref == external_customer_ref,
Member.is_deleted.is_(False),
)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
async def count_blocking_for_program(
self, tenant_id: UUID, program_id: UUID, statuses: list
) -> int:
from sqlalchemy import func
stmt = (
select(func.count())
.select_from(Member)
.where(
Member.tenant_id == tenant_id,
Member.program_id == program_id,
Member.is_deleted.is_(False),
Member.status.in_(statuses),
)
)
result = await self.session.execute(stmt)
return int(result.scalar_one())
class PointAccountRepository(TenantBaseRepository[PointAccount]):
model = PointAccount
async def get_by_member(
self, tenant_id: UUID, member_id: UUID
) -> PointAccount | None:
stmt = select(PointAccount).where(
PointAccount.tenant_id == tenant_id,
PointAccount.member_id == member_id,
PointAccount.is_deleted.is_(False),
)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
async def get_by_account_number(
self, tenant_id: UUID, account_number: str
) -> PointAccount | None:
stmt = select(PointAccount).where(
PointAccount.tenant_id == tenant_id,
PointAccount.account_number == account_number,
PointAccount.is_deleted.is_(False),
)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
class RewardRepository(TenantBaseRepository[Reward]):
model = Reward
async def get_by_code(
self, tenant_id: UUID, program_id: UUID, code: str
) -> Reward | None:
stmt = select(Reward).where(
Reward.tenant_id == tenant_id,
Reward.program_id == program_id,
Reward.code == code,
Reward.is_deleted.is_(False),
)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
class CampaignRepository(TenantBaseRepository[Campaign]):
model = Campaign
async def get_by_code(
self, tenant_id: UUID, program_id: UUID, code: str
) -> Campaign | None:
stmt = select(Campaign).where(
Campaign.tenant_id == tenant_id,
Campaign.program_id == program_id,
Campaign.code == code,
Campaign.is_deleted.is_(False),
)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
class LoyaltyAuditLogRepository(TenantBaseRepository[LoyaltyAuditLog]):
model = LoyaltyAuditLog
async def list_for_entity(
self,
tenant_id: UUID,
entity_type: str,
entity_id: UUID,
*,
limit: int = 100,
):
stmt = (
select(LoyaltyAuditLog)
.where(
LoyaltyAuditLog.tenant_id == tenant_id,
LoyaltyAuditLog.entity_type == entity_type,
LoyaltyAuditLog.entity_id == entity_id,
)
.order_by(LoyaltyAuditLog.created_at.desc())
.limit(limit)
)
result = await self.session.execute(stmt)
return result.scalars().all()
class MembershipLifecycleEventRepository(
TenantBaseRepository[MembershipLifecycleEvent]
):
model = MembershipLifecycleEvent
async def list_for_member(
self, tenant_id: UUID, member_id: UUID, *, limit: int = 100
):
stmt = (
select(MembershipLifecycleEvent)
.where(
MembershipLifecycleEvent.tenant_id == tenant_id,
MembershipLifecycleEvent.member_id == member_id,
)
.order_by(MembershipLifecycleEvent.created_at.desc())
.limit(limit)
)
result = await self.session.execute(stmt)
return result.scalars().all()