from datetime import UTC, datetime from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, LargeBinary, String, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from app.db import Base def utcnow() -> datetime: return datetime.now(UTC) class Box(Base): __tablename__ = "boxes" id: Mapped[int] = mapped_column(primary_key=True, index=True) name: Mapped[str] = mapped_column(String(100), nullable=False) note: Mapped[str | None] = mapped_column(Text, nullable=True) room: Mapped[str | None] = mapped_column(String(100), nullable=True) status: Mapped[str | None] = mapped_column(String(50), nullable=True) image_blob: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True) image_mime_type: Mapped[str | None] = mapped_column(String(50), nullable=True) image_width: Mapped[int | None] = mapped_column(Integer, nullable=True) image_height: Mapped[int | None] = mapped_column(Integer, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, nullable=False) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=utcnow, onupdate=utcnow, nullable=False, ) items: Mapped[list["Item"]] = relationship( back_populates="box", cascade="all, delete-orphan", order_by="Item.id", ) class Item(Base): __tablename__ = "items" id: Mapped[int] = mapped_column(primary_key=True, index=True) box_id: Mapped[int] = mapped_column(ForeignKey("boxes.id", ondelete="CASCADE"), nullable=False) name: Mapped[str] = mapped_column(String(100), nullable=False) note: Mapped[str | None] = mapped_column(Text, nullable=True) quantity: Mapped[int | None] = mapped_column(Integer, nullable=True) is_container: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) image_blob: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True) image_mime_type: Mapped[str | None] = mapped_column(String(50), nullable=True) image_width: Mapped[int | None] = mapped_column(Integer, nullable=True) image_height: Mapped[int | None] = mapped_column(Integer, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, nullable=False) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=utcnow, onupdate=utcnow, nullable=False, ) box: Mapped[Box] = relationship(back_populates="items") subitems: Mapped[list["SubItem"]] = relationship( back_populates="parent_item", cascade="all, delete-orphan", order_by="SubItem.id", ) class SubItem(Base): __tablename__ = "subitems" id: Mapped[int] = mapped_column(primary_key=True, index=True) parent_item_id: Mapped[int] = mapped_column( ForeignKey("items.id", ondelete="CASCADE"), nullable=False, ) name: Mapped[str] = mapped_column(String(100), nullable=False) note: Mapped[str | None] = mapped_column(Text, nullable=True) quantity: Mapped[int | None] = mapped_column(Integer, nullable=True) image_blob: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True) image_mime_type: Mapped[str | None] = mapped_column(String(50), nullable=True) image_width: Mapped[int | None] = mapped_column(Integer, nullable=True) image_height: Mapped[int | None] = mapped_column(Integer, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, nullable=False) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=utcnow, onupdate=utcnow, nullable=False, ) parent_item: Mapped[Item] = relationship(back_populates="subitems")