"""SQLAlchemy models: User, Service, ServiceComponent, Transaction.""" from datetime import datetime, timezone from sqlalchemy import ( Column, Integer, String, Float, Boolean, DateTime, ForeignKey, ) from sqlalchemy.orm import relationship from database import Base class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) username = Column(String, unique=True, index=True, nullable=False) password_hash = Column(String, nullable=False) display_name = Column(String, nullable=False) role = Column(String, nullable=False, default="viewer") # admin | viewer transactions = relationship("Transaction", back_populates="creator") class Service(Base): __tablename__ = "services" id = Column(Integer, primary_key=True, index=True) name = Column(String, unique=True, nullable=False) description = Column(String, default="") sell_price = Column(Float, nullable=False) # MAD/month — auto-computed from components cost_price = Column(Float, nullable=False) # MAD/month — auto-computed from components active = Column(Boolean, default=True) transactions = relationship("Transaction", back_populates="service") components = relationship( "ServiceComponent", back_populates="service", cascade="all, delete-orphan", order_by="ServiceComponent.id", ) def recompute_prices(self): """Update sell_price and cost_price from component totals.""" self.sell_price = sum( (c.unit_sell * c.quantity) for c in self.components ) if self.components else 0.0 self.cost_price = sum( (c.unit_cost * c.quantity) for c in self.components ) if self.components else 0.0 class ServiceComponent(Base): """A cost/sell component of a service offer (e.g. RAM, HDD, Transport).""" __tablename__ = "service_components" id = Column(Integer, primary_key=True, index=True) service_id = Column(Integer, ForeignKey("services.id"), nullable=False) name = Column(String, nullable=False) unit_cost = Column(Float, nullable=False, default=0.0) # what we pay / unit unit_sell = Column(Float, nullable=False, default=0.0) # what we charge / unit quantity = Column(Integer, nullable=False, default=1) notes = Column(String, default="") service = relationship("Service", back_populates="components") class Transaction(Base): __tablename__ = "transactions" id = Column(Integer, primary_key=True, index=True) service_id = Column(Integer, ForeignKey("services.id"), nullable=False) quantity = Column(Integer, nullable=False, default=1) month = Column(String, nullable=False) # YYYY-MM format notes = Column(String, default="") created_by = Column(Integer, ForeignKey("users.id"), nullable=False) created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) service = relationship("Service", back_populates="transactions") creator = relationship("User", back_populates="transactions") @property def revenue(self) -> float: """Calculated: quantity * service.sell_price""" return self.quantity * self.service.sell_price @property def cost(self) -> float: """Calculated: quantity * service.cost_price""" return self.quantity * self.service.cost_price