"""POS Pro aggregates — Phase 12.5. Full-featured POS additions: payments, discounts, tax lines, floor plans, and stations. No accounting posting — payment records are local only. """ from __future__ import annotations import uuid from sqlalchemy import Index, Integer, String, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column from app.core.database import Base from app.models.base import ( ActorAuditMixin, SoftDeleteMixin, TenantMixin, TimestampMixin, UUIDPrimaryKeyMixin, ) from app.models.types import GUID, LifecycleStatus, PosDiscountKind, PosPaymentMethod class PosPayment( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): """A payment recorded against a POS ticket (local record only, no accounting posting).""" __tablename__ = "pos_payments" venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) ticket_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) method: Mapped[PosPaymentMethod] = mapped_column( default=PosPaymentMethod.CASH, nullable=False ) amount: Mapped[int] = mapped_column(Integer, default=0, nullable=False) currency_code: Mapped[str] = mapped_column(String(3), default="IRR", nullable=False) external_payment_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) status: Mapped[LifecycleStatus] = mapped_column( default=LifecycleStatus.ACTIVE, nullable=False ) __table_args__ = ( Index("ix_pos_payments_venue", "tenant_id", "venue_id"), Index("ix_pos_payments_ticket", "tenant_id", "ticket_id"), Index("ix_pos_payments_tenant_deleted", "tenant_id", "is_deleted"), ) class PosDiscount( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): """A discount applied to a POS ticket.""" __tablename__ = "pos_discounts" venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) ticket_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) code: Mapped[str] = mapped_column(String(50), nullable=False) kind: Mapped[PosDiscountKind] = mapped_column( default=PosDiscountKind.PERCENT, nullable=False ) value: Mapped[int] = mapped_column(Integer, default=0, nullable=False) name: Mapped[str | None] = mapped_column(String(255), nullable=True) __table_args__ = ( Index("ix_pos_discounts_venue", "tenant_id", "venue_id"), Index("ix_pos_discounts_ticket", "tenant_id", "ticket_id"), Index("ix_pos_discounts_tenant_deleted", "tenant_id", "is_deleted"), ) class PosTaxLine( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): """A tax line applied to a POS ticket.""" __tablename__ = "pos_tax_lines" venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) ticket_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) code: Mapped[str] = mapped_column(String(50), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) rate_bps: Mapped[int] = mapped_column(Integer, default=0, nullable=False) amount: Mapped[int] = mapped_column(Integer, default=0, nullable=False) __table_args__ = ( Index("ix_pos_tax_lines_venue", "tenant_id", "venue_id"), Index("ix_pos_tax_lines_ticket", "tenant_id", "ticket_id"), Index("ix_pos_tax_lines_tenant_deleted", "tenant_id", "is_deleted"), ) class PosFloorPlan( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): """A floor plan grouping tables within a venue / dining area.""" __tablename__ = "pos_floor_plans" venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) code: Mapped[str] = mapped_column(String(50), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) dining_area_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) status: Mapped[LifecycleStatus] = mapped_column( default=LifecycleStatus.ACTIVE, nullable=False ) __table_args__ = ( UniqueConstraint( "tenant_id", "venue_id", "code", name="uq_pos_floor_plans_tenant_venue_code" ), Index("ix_pos_floor_plans_venue", "tenant_id", "venue_id"), Index("ix_pos_floor_plans_tenant_deleted", "tenant_id", "is_deleted"), ) class PosStation( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): """A service station (bar / counter / terminal) optionally tied to a floor plan.""" __tablename__ = "pos_stations" venue_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) floor_plan_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) code: Mapped[str] = mapped_column(String(50), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) status: Mapped[LifecycleStatus] = mapped_column( default=LifecycleStatus.ACTIVE, nullable=False ) __table_args__ = ( UniqueConstraint( "tenant_id", "venue_id", "code", name="uq_pos_stations_tenant_venue_code" ), Index("ix_pos_stations_venue", "tenant_id", "venue_id"), Index("ix_pos_stations_floor_plan", "tenant_id", "floor_plan_id"), Index("ix_pos_stations_tenant_deleted", "tenant_id", "is_deleted"), )