import enum import uuid from datetime import datetime from typing import Any, List, Optional from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, text, Numeric, BigInteger from sqlalchemy.dialects.postgresql import ENUM as PG_ENUM, UUID as PG_UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func # MB 2.0: A központi aszinkron adatbázis motorból húzzuk be a Base-t from app.database import Base class OrgType(str, enum.Enum): individual = "individual" service = "service" service_provider = "service_provider" fleet_owner = "fleet_owner" club = "club" business = "business" class OrgUserRole(str, enum.Enum): OWNER = "OWNER" ADMIN = "ADMIN" FLEET_MANAGER = "FLEET_MANAGER" DRIVER = "DRIVER" MECHANIC = "MECHANIC" RECEPTIONIST = "RECEPTIONIST" class Organization(Base): """ Szervezet entitás. Lehet flotta (user) és szolgáltató (service) egyszerre. Minden üzleti adat a 'data' sémába kerül. """ __tablename__ = "organizations" __table_args__ = {"schema": "data"} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) # Kapcsolat a címekkel (szintén a data sémában) address_id: Mapped[Optional[uuid.UUID]] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("data.addresses.id")) is_anonymized: Mapped[bool] = mapped_column(Boolean, default=False, server_default=text("false")) anonymized_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) full_name: Mapped[str] = mapped_column(String, nullable=False) name: Mapped[str] = mapped_column(String, nullable=False) display_name: Mapped[Optional[str]] = mapped_column(String(50)) folder_slug: Mapped[str] = mapped_column(String(12), unique=True, index=True) default_currency: Mapped[str] = mapped_column(String(3), default="HUF") country_code: Mapped[str] = mapped_column(String(2), default="HU") language: Mapped[str] = mapped_column(String(5), default="hu") address_zip: Mapped[Optional[str]] = mapped_column(String(10)) address_city: Mapped[Optional[str]] = mapped_column(String(100)) address_street_name: Mapped[Optional[str]] = mapped_column(String(150)) address_street_type: Mapped[Optional[str]] = mapped_column(String(50)) address_house_number: Mapped[Optional[str]] = mapped_column(String(20)) address_hrsz: Mapped[Optional[str]] = mapped_column(String(50)) tax_number: Mapped[Optional[str]] = mapped_column(String(20), unique=True, index=True) reg_number: Mapped[Optional[str]] = mapped_column(String(50)) org_type: Mapped[OrgType] = mapped_column( PG_ENUM(OrgType, name="orgtype", schema="data"), default=OrgType.individual ) status: Mapped[str] = mapped_column(String(30), default="pending_verification") is_deleted: Mapped[bool] = mapped_column(Boolean, default=False) subscription_plan: Mapped[str] = mapped_column(String(30), server_default=text("'FREE'"), index=True) base_asset_limit: Mapped[int] = mapped_column(Integer, server_default=text("1")) purchased_extra_slots: Mapped[int] = mapped_column(Integer, server_default=text("0")) notification_settings: Mapped[Any] = mapped_column(JSON, server_default=text("'{\"notify_owner\": true, \"alert_days_before\": [30, 15, 7, 1]}'::jsonb")) external_integration_config: Mapped[Any] = mapped_column(JSON, server_default=text("'{}'::jsonb")) # KRITIKUS: A júzer az 'identity' sémában van! owner_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id")) is_active: Mapped[bool] = mapped_column(Boolean, default=True) is_verified: Mapped[bool] = mapped_column(Boolean, default=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), onupdate=func.now()) is_ownership_transferable: Mapped[bool] = mapped_column(Boolean, server_default=text("true")) # Kapcsolatok (Relationships) assets: Mapped[List["AssetAssignment"]] = relationship("AssetAssignment", back_populates="organization", cascade="all, delete-orphan") members: Mapped[List["OrganizationMember"]] = relationship("OrganizationMember", back_populates="organization", cascade="all, delete-orphan") owner: Mapped[Optional["User"]] = relationship("User", back_populates="owned_organizations") financials: Mapped[List["OrganizationFinancials"]] = relationship("OrganizationFinancials", back_populates="organization", cascade="all, delete-orphan") service_profile: Mapped[Optional["ServiceProfile"]] = relationship("ServiceProfile", back_populates="organization", uselist=False) branches: Mapped[List["Branch"]] = relationship("Branch", back_populates="organization", cascade="all, delete-orphan") class OrganizationFinancials(Base): __tablename__ = "organization_financials" __table_args__ = {"schema": "data"} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) organization_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.organizations.id"), nullable=False) year: Mapped[int] = mapped_column(Integer, nullable=False) turnover: Mapped[Optional[float]] = mapped_column(Numeric(18, 2)) profit: Mapped[Optional[float]] = mapped_column(Numeric(18, 2)) employee_count: Mapped[Optional[int]] = mapped_column(Integer) source: Mapped[Optional[str]] = mapped_column(String(50)) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) organization: Mapped["Organization"] = relationship("Organization", back_populates="financials") class OrganizationMember(Base): __tablename__ = "organization_members" __table_args__ = {"schema": "data"} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) organization_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.organizations.id"), nullable=False) # KRITIKUS: User és Person az identity sémában lakik! user_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id")) person_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("identity.persons.id")) role: Mapped[OrgUserRole] = mapped_column( PG_ENUM(OrgUserRole, name="orguserrole", schema="data"), default=OrgUserRole.DRIVER ) permissions: Mapped[Any] = mapped_column(JSON, server_default=text("'{}'::jsonb")) is_permanent: Mapped[bool] = mapped_column(Boolean, default=False) is_verified: Mapped[bool] = mapped_column(Boolean, default=False) organization: Mapped["Organization"] = relationship("Organization", back_populates="members") user: Mapped[Optional["User"]] = relationship("User") person: Mapped[Optional["Person"]] = relationship("Person", back_populates="memberships") class OrganizationSalesAssignment(Base): __tablename__ = "org_sales_assignments" __table_args__ = {"schema": "data"} id: Mapped[int] = mapped_column(Integer, primary_key=True) organization_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("data.organizations.id")) # KRITIKUS: Az ügynök (agent) júzer az identity sémában van agent_user_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id")) assigned_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) is_active: Mapped[bool] = mapped_column(Boolean, default=True)