TorbatYar/backend/services/communication/app/repositories/base.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

67 lines
2.8 KiB
Python

"""Tenant-aware base repository with soft-delete helpers."""
from __future__ import annotations
from datetime import datetime, timezone
from typing import Generic, Sequence, TypeVar
from uuid import UUID
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import Base
ModelT = TypeVar("ModelT", bound=Base)
class TenantBaseRepository(Generic[ModelT]):
model: type[ModelT]
def __init__(self, session: AsyncSession) -> None:
self.session = session
def _not_deleted_clause(self):
if hasattr(self.model, "is_deleted"):
return self.model.is_deleted.is_(False) # type: ignore[attr-defined]
return True
async def get(self, tenant_id: UUID, entity_id: UUID) -> ModelT | None:
clauses = [
self.model.tenant_id == tenant_id, # type: ignore[attr-defined]
self.model.id == entity_id, # type: ignore[attr-defined]
]
if hasattr(self.model, "is_deleted"):
clauses.append(self.model.is_deleted.is_(False)) # type: ignore[attr-defined]
stmt = select(self.model).where(*clauses)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
async def add(self, entity: ModelT) -> ModelT:
self.session.add(entity)
await self.session.flush()
return entity
async def soft_delete(self, entity: ModelT, *, deleted_by: str | None = None) -> None:
entity.is_deleted = True # type: ignore[attr-defined]
entity.deleted_at = datetime.now(timezone.utc) # type: ignore[attr-defined]
if deleted_by is not None and hasattr(entity, "deleted_by"):
entity.deleted_by = deleted_by # type: ignore[attr-defined]
await self.session.flush()
async def list_by_tenant(
self, tenant_id: UUID, *, offset: int = 0, limit: int = 20
) -> Sequence[ModelT]:
clauses = [self.model.tenant_id == tenant_id] # type: ignore[attr-defined]
if hasattr(self.model, "is_deleted"):
clauses.append(self.model.is_deleted.is_(False)) # type: ignore[attr-defined]
stmt = select(self.model).where(*clauses).offset(offset).limit(limit)
result = await self.session.execute(stmt)
return result.scalars().all()
async def count_by_tenant(self, tenant_id: UUID) -> int:
clauses = [self.model.tenant_id == tenant_id] # type: ignore[attr-defined]
if hasattr(self.model, "is_deleted"):
clauses.append(self.model.is_deleted.is_(False)) # type: ignore[attr-defined]
stmt = select(func.count()).select_from(self.model).where(*clauses)
result = await self.session.execute(stmt)
return int(result.scalar_one())