generated from gilles/template-webapp
98 lines
3.3 KiB
Python
98 lines
3.3 KiB
Python
"""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"<Location(id={self.id}, name='{self.name}', type={self.type.value})>"
|
|
|
|
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}"
|