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
This commit is contained in:
Bernt
2026-07-05 06:41:32 +00:00
parent f4f853d94b
commit aee0f09db8
19583 changed files with 1450867 additions and 1153 deletions
@@ -0,0 +1,90 @@
"""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}>"