TorbatYar/backend/services/beauty_business/app/models/base.py
Mortezakoohjani d579d0b142 feat(loyalty): add Loyalty Platform Frontend module
Add frontend/modules/loyalty with types, API client, design system, feature pages and thin App Router routes under app/loyalty/. BFF proxy at app/api/loyalty/. Include loyalty frontend docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 10:50:55 +03:30

52 lines
1.5 KiB
Python

"""Shared model mixins."""
from __future__ import annotations
import uuid
from datetime import datetime
from sqlalchemy import Boolean, DateTime, Integer, String, func
from sqlalchemy.orm import Mapped, mapped_column
from app.models.types import GUID
class UUIDPrimaryKeyMixin:
id: Mapped[uuid.UUID] = mapped_column(
GUID(), primary_key=True, default=uuid.uuid4
)
class TimestampMixin:
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)
class TenantMixin:
"""Row-level tenancy (ADR-003). Tenant comes from request context, never hardcoded."""
tenant_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
class SoftDeleteMixin:
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
deleted_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
deleted_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
class ActorAuditMixin:
created_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
updated_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
class OptimisticLockMixin:
version: Mapped[int] = mapped_column(Integer, default=1, nullable=False)