"""Vela Platform — SQLAlchemy models.""" from datetime import datetime, timezone from sqlalchemy import ( Boolean, Column, DateTime, Float, ForeignKey, Integer, String, Text, UniqueConstraint, create_engine, ) from sqlalchemy.orm import DeclarativeBase, relationship class Base(DeclarativeBase): pass class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, autoincrement=True) email = Column(String(255), unique=True, nullable=False) password_hash = Column(String(255), nullable=False) active = Column(Boolean, default=True, nullable=False) created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) class Service(Base): __tablename__ = "services" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(100), unique=True, nullable=False) description = Column(Text, default="") sell_price = Column(Float, default=0.0) cost_price = Column(Float, default=0.0) # sum of component unit_cost * quantity active = Column(Boolean, default=True, nullable=False) sort_order = Column(Integer, default=0) created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) updated_at = Column(DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) components = relationship("ServiceComponent", back_populates="service", cascade="all, delete-orphan") class ServiceComponent(Base): __tablename__ = "service_components" id = Column(Integer, primary_key=True, autoincrement=True) service_id = Column(Integer, ForeignKey("services.id", ondelete="CASCADE"), nullable=False) name = Column(String(100), nullable=False) unit_cost = Column(Float, default=0.0) unit_sell = Column(Float, default=0.0) quantity = Column(Integer, default=1) notes = Column(Text, default="") created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) service = relationship("Service", back_populates="components") class Transaction(Base): """Frozen transaction — snapshots prices at time of sale.""" __tablename__ = "transactions" __table_args__ = ( UniqueConstraint("source_type", "source_id", name="uq_transaction_source"), ) id = Column(Integer, primary_key=True, autoincrement=True) source_type = Column(String(20), default="manual") # "manual" or "order" source_id = Column(Integer, nullable=True) service_id = Column(Integer, ForeignKey("services.id"), nullable=True) service_name_snapshot = Column(String(100), nullable=False) quantity = Column(Integer, default=1) unit_sell_price_snapshot = Column(Float, nullable=False) unit_cost_snapshot = Column(Float, nullable=False) revenue_total = Column(Float, nullable=False) cogs_total = Column(Float, nullable=False) gross_profit_total = Column(Float, nullable=False) month_start_date = Column(String(10), nullable=False) # "2026-06" payment_status = Column(String(20), default="unpaid") # unpaid, partial, paid, refunded notes = Column(Text, default="") created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) class Expense(Base): __tablename__ = "expenses" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(200), nullable=False) amount = Column(Float, nullable=False) category = Column(String(100), default="general") month_start_date = Column(String(10), nullable=False) # "2026-06" notes = Column(Text, default="") created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) deleted_at = Column(DateTime, nullable=True) class ExpenseTemplate(Base): """Recurring expense templates — generate actual expenses each month.""" __tablename__ = "expense_templates" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(200), nullable=False) amount = Column(Float, nullable=False) category = Column(String(100), default="general") active = Column(Boolean, default=True, nullable=False) created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))