"""Modèle SQLAlchemy pour les emplacements de stockage. Les emplacements sont organisés de manière hiérarchique : pièce → meuble → tiroir → boîte Exemple : Garage → Étagère A → Tiroir 2 → Boîte visserie """ from datetime import datetime from typing import TYPE_CHECKING from sqlalchemy import DateTime, Enum, ForeignKey, Integer, String from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from app.core.database import Base if TYPE_CHECKING: from app.models.item import Item import enum class LocationType(str, enum.Enum): """Type d'emplacement dans la hiérarchie.""" ROOM = "room" # Pièce (ex: Garage, Cuisine) FURNITURE = "furniture" # Meuble (ex: Étagère, Armoire) DRAWER = "drawer" # Tiroir BOX = "box" # Boîte/Bac de rangement class Location(Base): """Emplacement de stockage hiérarchique. Attributes: id: Identifiant unique auto-incrémenté name: Nom de l'emplacement (ex: "Garage", "Étagère A") type: Type d'emplacement (room/furniture/drawer/box) parent_id: ID du parent (None si racine) path: Chemin complet calculé (ex: "Garage > Étagère A > Tiroir 2") description: Description optionnelle created_at: Date/heure de création (auto) updated_at: Date/heure de dernière modification (auto) parent: Relation vers le parent children: Relation vers les enfants items: Relation vers les objets à cet emplacement """ __tablename__ = "locations" # Colonnes id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) name: Mapped[str] = mapped_column(String(100), nullable=False, index=True) type: Mapped[LocationType] = mapped_column( Enum(LocationType, native_enum=False, length=20), nullable=False ) parent_id: Mapped[int | None] = mapped_column( Integer, ForeignKey("locations.id", ondelete="CASCADE"), nullable=True, index=True ) path: Mapped[str] = mapped_column(String(500), nullable=False, index=True) description: Mapped[str | None] = mapped_column(String(500), nullable=True) # Timestamps created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False, ) # Relations parent: Mapped["Location | None"] = relationship( "Location", remote_side=[id], back_populates="children" ) children: Mapped[list["Location"]] = relationship( "Location", back_populates="parent", cascade="all, delete-orphan" ) items: Mapped[list["Item"]] = relationship( "Item", back_populates="location", cascade="all, delete-orphan" ) def __repr__(self) -> str: """Représentation string de l'emplacement.""" return f"" def calculate_path(self) -> str: """Calcule le chemin complet de l'emplacement. Returns: Chemin complet (ex: "Garage > Étagère A > Tiroir 2") """ if self.parent is None: return self.name return f"{self.parent.calculate_path()} > {self.name}"