"""Billing and subscription models.""" from datetime import datetime, date from decimal import Decimal from enum import Enum from uuid import UUID, uuid4 from sqlalchemy import String, ForeignKey, Date, Numeric, JSON from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base class SubscriptionPlan(Base): __tablename__ = "subscription_plans" id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4) name: Mapped[str] = mapped_column(String(100), nullable=False) slug: Mapped[str] = mapped_column(String(50), unique=True, nullable=False) description: Mapped[str | None] price_monthly: Mapped[Decimal] = mapped_column(Numeric(10, 2), nullable=False) price_yearly: Mapped[Decimal | None] = mapped_column(Numeric(10, 2)) features: Mapped[dict] = mapped_column(JSON, default=dict) limits: Mapped[dict] = mapped_column(JSON, default=dict) is_public: Mapped[bool] = mapped_column(default=True) created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow) # Relationships tenants: Mapped[list["Tenant"]] = relationship(back_populates="plan") def __repr__(self) -> str: return f"" class InvoiceStatus(str, Enum): DRAFT = "draft" SENT = "sent" PAID = "paid" OVERDUE = "overdue" CANCELLED = "cancelled" class Invoice(Base): __tablename__ = "invoices" id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4) tenant_id: Mapped[UUID] = mapped_column( ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False ) invoice_number: Mapped[str] = mapped_column(String(50), unique=True, nullable=False) period_start: Mapped[date] = mapped_column(Date, nullable=False) period_end: Mapped[date] = mapped_column(Date, nullable=False) amount: Mapped[Decimal] = mapped_column(Numeric(10, 2), nullable=False) tax_amount: Mapped[Decimal] = mapped_column(Numeric(10, 2), nullable=False) total_amount: Mapped[Decimal] = mapped_column(Numeric(10, 2), nullable=False) currency: Mapped[str] = mapped_column(String(3), default="SEK") status: Mapped[InvoiceStatus] = mapped_column(default=InvoiceStatus.DRAFT) paid_at: Mapped[datetime | None] stripe_invoice_id: Mapped[str | None] = mapped_column(String(100)) pdf_url: Mapped[str | None] due_date: Mapped[date] = mapped_column(Date, nullable=False) created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow) # Relationships tenant: Mapped["Tenant"] = relationship(back_populates="invoices") items: Mapped[list["InvoiceItem"]] = relationship( back_populates="invoice", cascade="all, delete-orphan" ) def __repr__(self) -> str: return f"" class InvoiceItem(Base): __tablename__ = "invoice_items" id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4) invoice_id: Mapped[UUID] = mapped_column( ForeignKey("invoices.id", ondelete="CASCADE"), nullable=False ) description: Mapped[str] = mapped_column(String(255), nullable=False) quantity: Mapped[Decimal] = mapped_column(Numeric(10, 2), nullable=False) unit_price: Mapped[Decimal] = mapped_column(Numeric(10, 2), nullable=False) amount: Mapped[Decimal] = mapped_column(Numeric(10, 2), nullable=False) metadata: Mapped[dict] = mapped_column(JSON, default=dict) # Relationships invoice: Mapped["Invoice"] = relationship(back_populates="items") def __repr__(self) -> str: return f""