"""Modèle SQLAlchemy pour les objets de l'inventaire. Les objets (items) sont l'entité centrale de l'application. Chaque objet appartient à une catégorie et est situé à un emplacement. """ from datetime import date, datetime from decimal import Decimal from typing import TYPE_CHECKING from sqlalchemy import Date, DateTime, Enum, ForeignKey, Integer, Numeric, String, Text 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.category import Category from app.models.document import Document from app.models.location import Location import enum class ItemStatus(str, enum.Enum): """Statut d'un objet.""" IN_STOCK = "in_stock" # En stock (non utilisé) IN_USE = "in_use" # En cours d'utilisation BROKEN = "broken" # Cassé/HS SOLD = "sold" # Vendu LENT = "lent" # Prêté class Item(Base): """Objet de l'inventaire domestique. Attributes: id: Identifiant unique auto-incrémenté name: Nom de l'objet (ex: "Perceuse sans fil Bosch") description: Description détaillée optionnelle quantity: Quantité en stock (défaut: 1) price: Prix d'achat optionnel purchase_date: Date d'achat optionnelle status: Statut de l'objet (in_stock/in_use/broken/sold/lent) brand: Marque optionnelle (ex: "Bosch", "Samsung") model: Modèle optionnel (ex: "PSR 18 LI-2") serial_number: Numéro de série optionnel notes: Notes libres category_id: ID de la catégorie (FK) location_id: ID de l'emplacement (FK) created_at: Date/heure de création (auto) updated_at: Date/heure de dernière modification (auto) category: Relation vers la catégorie location: Relation vers l'emplacement documents: Relation vers les documents (photos, notices, factures) """ __tablename__ = "items" # Colonnes principales id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) name: Mapped[str] = mapped_column(String(200), nullable=False, index=True) description: Mapped[str | None] = mapped_column(Text, nullable=True) quantity: Mapped[int] = mapped_column(Integer, nullable=False, default=1) status: Mapped[ItemStatus] = mapped_column( Enum(ItemStatus, native_enum=False, length=20), nullable=False, default=ItemStatus.IN_STOCK, ) # Informations produit brand: Mapped[str | None] = mapped_column(String(100), nullable=True) model: Mapped[str | None] = mapped_column(String(100), nullable=True) serial_number: Mapped[str | None] = mapped_column(String(100), nullable=True, unique=True) url: Mapped[str | None] = mapped_column(String(500), nullable=True) # Lien vers page produit # Informations achat price: Mapped[Decimal | None] = mapped_column(Numeric(10, 2), nullable=True) purchase_date: Mapped[date | None] = mapped_column(Date, nullable=True) # Notes notes: Mapped[str | None] = mapped_column(Text, nullable=True) # Foreign Keys category_id: Mapped[int] = mapped_column( Integer, ForeignKey("categories.id", ondelete="RESTRICT"), nullable=False, index=True ) location_id: Mapped[int] = mapped_column( Integer, ForeignKey("locations.id", ondelete="RESTRICT"), nullable=False, index=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 category: Mapped["Category"] = relationship("Category", back_populates="items") location: Mapped["Location"] = relationship("Location", back_populates="items") documents: Mapped[list["Document"]] = relationship( "Document", back_populates="item", cascade="all, delete-orphan" ) def __repr__(self) -> str: """Représentation string de l'objet.""" return f""