From 3cd44c8b9f5a3dd40abb1418f4a3702ce2c6ed3f Mon Sep 17 00:00:00 2001 From: Leopoldadmin Date: Thu, 4 Jun 2026 00:06:20 +0000 Subject: [PATCH] Upload app/models/user.py --- app/models/user.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 app/models/user.py diff --git a/app/models/user.py b/app/models/user.py new file mode 100644 index 0000000..3fcdc35 --- /dev/null +++ b/app/models/user.py @@ -0,0 +1,50 @@ +"""User model with role enum and soft-delete.""" + +from __future__ import annotations + +from enum import Enum +from typing import TYPE_CHECKING, Optional + +from sqlalchemy import Boolean, String, UniqueConstraint +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from app.models.base import Base, OrgScopedMixin, SoftDeleteMixin, TimestampMixin + +if TYPE_CHECKING: + from app.models.org import Org + + +class UserRole(str, Enum): + """User role for RBAC.""" + + admin = "admin" + sales_manager = "sales_manager" + sales_rep = "sales_rep" + + +class User(Base, TimestampMixin, SoftDeleteMixin, OrgScopedMixin): + __tablename__ = "users" + __table_args__ = ( + UniqueConstraint("org_id", "email", name="uq_users_org_email"), + ) + + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + email: Mapped[str] = mapped_column(String(255), nullable=False, index=True) + password_hash: Mapped[str] = mapped_column(String(255), nullable=False) + name: Mapped[str] = mapped_column(String(255), nullable=False) + role: Mapped[UserRole] = mapped_column( + String(32), + nullable=False, + default=UserRole.sales_rep, + server_default=UserRole.sales_rep.value, + ) + avatar_url: Mapped[Optional[str]] = mapped_column(String(1024), nullable=True) + email_notifications: Mapped[bool] = mapped_column( + Boolean, nullable=False, default=True, server_default="1" + ) + + # Relationship back to org (string reference avoids circular import at runtime) + org: Mapped["Org"] = relationship(back_populates="users", lazy="joined") + + def __repr__(self) -> str: + return f""