128 lines
3.7 KiB
Python
128 lines
3.7 KiB
Python
"""SQLModel table definitions for Ten31Portal."""
|
|
|
|
import enum
|
|
from datetime import date, datetime
|
|
from decimal import Decimal
|
|
from typing import Optional
|
|
|
|
from sqlmodel import Field, SQLModel, Column, String, JSON, UniqueConstraint
|
|
|
|
|
|
# --- Enums ---
|
|
|
|
class UserRole(str, enum.Enum):
|
|
approver = "approver"
|
|
cfo = "cfo"
|
|
fund_admin = "fund_admin"
|
|
viewer = "viewer"
|
|
|
|
|
|
class EntityType(str, enum.Enum):
|
|
fund = "fund"
|
|
spv = "spv"
|
|
gp = "gp"
|
|
mgmt_co = "mgmt_co"
|
|
|
|
|
|
class EntityStatus(str, enum.Enum):
|
|
active = "active"
|
|
closed = "closed"
|
|
|
|
|
|
class RoundStatus(str, enum.Enum):
|
|
draft = "draft"
|
|
submitted = "submitted"
|
|
approved = "approved"
|
|
returned = "returned"
|
|
|
|
|
|
# --- Tables ---
|
|
|
|
class User(SQLModel, table=True):
|
|
__tablename__ = "users"
|
|
|
|
id: int | None = Field(default=None, primary_key=True)
|
|
name: str
|
|
email: str = Field(sa_column=Column(String, unique=True, nullable=False))
|
|
password_hash: str
|
|
role: UserRole
|
|
is_active: bool = Field(default=True)
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
|
|
class Entity(SQLModel, table=True):
|
|
__tablename__ = "entities"
|
|
|
|
id: int | None = Field(default=None, primary_key=True)
|
|
name: str
|
|
type: EntityType
|
|
vintage_year: int | None = None
|
|
fund_size_cents: int | None = None
|
|
status: EntityStatus = Field(default=EntityStatus.active)
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
|
|
class Holding(SQLModel, table=True):
|
|
__tablename__ = "holdings"
|
|
|
|
id: int | None = Field(default=None, primary_key=True)
|
|
entity_id: int = Field(foreign_key="entities.id")
|
|
company_name: str
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
|
|
class Position(SQLModel, table=True):
|
|
__tablename__ = "positions"
|
|
|
|
id: int | None = Field(default=None, primary_key=True)
|
|
holding_id: int = Field(foreign_key="holdings.id")
|
|
security_name: str
|
|
investment_date: date
|
|
shares: str | None = None # Decimal stored as string
|
|
cost_cents: int
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
|
|
class ValuationRound(SQLModel, table=True):
|
|
__tablename__ = "valuation_rounds"
|
|
__table_args__ = (
|
|
UniqueConstraint("entity_id", "quarter_end", name="uq_round_entity_quarter"),
|
|
)
|
|
|
|
id: int | None = Field(default=None, primary_key=True)
|
|
entity_id: int = Field(foreign_key="entities.id")
|
|
quarter_end: date
|
|
status: RoundStatus = Field(default=RoundStatus.draft)
|
|
submitted_by: int | None = Field(default=None, foreign_key="users.id")
|
|
submitted_at: datetime | None = None
|
|
approved_by: int | None = Field(default=None, foreign_key="users.id")
|
|
approved_at: datetime | None = None
|
|
return_note: str | None = None
|
|
is_seed: bool = Field(default=False)
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
|
|
class Valuation(SQLModel, table=True):
|
|
__tablename__ = "valuations"
|
|
__table_args__ = (
|
|
UniqueConstraint("round_id", "position_id", name="uq_valuation_round_position"),
|
|
)
|
|
|
|
id: int | None = Field(default=None, primary_key=True)
|
|
round_id: int = Field(foreign_key="valuation_rounds.id")
|
|
position_id: int = Field(foreign_key="positions.id")
|
|
value_cents: int
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
|
|
|
|
class AuditLog(SQLModel, table=True):
|
|
__tablename__ = "audit_log"
|
|
|
|
id: int | None = Field(default=None, primary_key=True)
|
|
actor_user_id: int | None = Field(default=None, foreign_key="users.id")
|
|
action: str
|
|
object_type: str
|
|
object_id: int | None = None
|
|
detail: str | None = Field(default=None, sa_column=Column(JSON, nullable=True))
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|