organization.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. """
  2. Organization model - represents a client company.
  3. """
  4. from sqlalchemy import Boolean, String
  5. from sqlalchemy.orm import Mapped, mapped_column, relationship
  6. from app.core.encryption import decrypt_password, encrypt_password
  7. from app.models.base import Base
  8. class Organization(Base):
  9. """
  10. Organization (client company) model.
  11. Each organization can have multiple users and devices.
  12. Products (WiFi, BLE) are enabled/disabled per organization.
  13. """
  14. __tablename__ = "organizations"
  15. id: Mapped[int] = mapped_column(primary_key=True)
  16. name: Mapped[str] = mapped_column(String(255), nullable=False)
  17. contact_name: Mapped[str | None] = mapped_column(String(255))
  18. contact_email: Mapped[str] = mapped_column(String(255), nullable=False)
  19. contact_phone: Mapped[str | None] = mapped_column(String(50))
  20. # Product access (modular)
  21. wifi_enabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
  22. ble_enabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
  23. android_enabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
  24. # WiFi credentials (encrypted)
  25. wifi_ssid: Mapped[str | None] = mapped_column(String(100))
  26. _wifi_password_encrypted: Mapped[str | None] = mapped_column(
  27. "wifi_password_encrypted", String(500)
  28. )
  29. # Status: pending, active, suspended, deleted
  30. status: Mapped[str] = mapped_column(
  31. String(20), default="pending", nullable=False
  32. )
  33. # Admin notes
  34. notes: Mapped[str | None] = mapped_column(String)
  35. # Relationships
  36. users: Mapped[list["User"]] = relationship(
  37. "User", back_populates="organization", cascade="all, delete-orphan"
  38. )
  39. devices: Mapped[list["Device"]] = relationship(
  40. "Device", back_populates="organization"
  41. )
  42. @property
  43. def wifi_password(self) -> str | None:
  44. """Decrypt WiFi password for viewing by admin."""
  45. if not self._wifi_password_encrypted:
  46. return None
  47. return decrypt_password(self._wifi_password_encrypted)
  48. @wifi_password.setter
  49. def wifi_password(self, plain_password: str | None) -> None:
  50. """Encrypt WiFi password before storing."""
  51. if plain_password is None:
  52. self._wifi_password_encrypted = None
  53. else:
  54. self._wifi_password_encrypted = encrypt_password(plain_password)
  55. def __repr__(self) -> str:
  56. return f"<Organization {self.id}: {self.name}>"