Files
boc/landvex-admin/backend/app/models/billing.py
T
Bernt aee0f09db8 landvex: Fixar och tester klara för alla komponenter
- Datafabrik: Dockerfile fix, agentorkestrering fungerar
- Vision: Identify-modell, FAISS, OCR alla testade
- API: Alla 7 integrationstester passerade
- Upplösare: Entitetsupplösning verifierad
2026-07-05 06:41:32 +00:00

91 lines
3.5 KiB
Python

"""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"<SubscriptionPlan {self.slug}>"
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"<Invoice {self.invoice_number}>"
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"<InvoiceItem {self.description}>"