import uuid from datetime import datetime, date from decimal import Decimal from sqlalchemy import String, Text, Integer, TIMESTAMP, Date, Numeric, Boolean, ForeignKey, text from sqlalchemy.dialects.postgresql import UUID, ARRAY from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base class Product(Base): __tablename__ = "products" __table_args__ = {"schema": "shopping"} id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name: Mapped[str] = mapped_column(String(150), nullable=False) brand: Mapped[str | None] = mapped_column(String(100)) category: Mapped[str | None] = mapped_column(String(50)) description: Mapped[str | None] = mapped_column(Text) image_path: Mapped[str | None] = mapped_column(String(255)) thumbnail_path: Mapped[str | None] = mapped_column(String(255)) default_unit: Mapped[str | None] = mapped_column(String(20)) barcode: Mapped[str | None] = mapped_column(String(50)) price: Mapped[Decimal | None] = mapped_column(Numeric(8, 2)) quantity_per_unit: Mapped[Decimal | None] = mapped_column(Numeric(8, 3)) default_store_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("shopping.stores.id", ondelete="SET NULL")) frequency_score: Mapped[int] = mapped_column(Integer, server_default=text("0")) last_purchased_at: Mapped[date | None] = mapped_column(Date) avg_interval_days: Mapped[Decimal | None] = mapped_column(Numeric(8, 1)) tags: Mapped[list[str] | None] = mapped_column(ARRAY(Text()), nullable=True) owner_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True)) created_at: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=True), server_default=text("now()")) class Store(Base): __tablename__ = "stores" __table_args__ = {"schema": "shopping"} id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name: Mapped[str] = mapped_column(String(100), nullable=False) location: Mapped[str | None] = mapped_column(Text) url: Mapped[str | None] = mapped_column(Text) store_type: Mapped[str | None] = mapped_column(String(50)) owner_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True)) class PriceHistory(Base): __tablename__ = "price_history" __table_args__ = {"schema": "shopping"} id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) product_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("shopping.products.id", ondelete="CASCADE"), nullable=False) store_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("shopping.stores.id", ondelete="SET NULL")) price: Mapped[Decimal] = mapped_column(Numeric(8, 2), nullable=False) unit: Mapped[str | None] = mapped_column(String(20)) quantity: Mapped[Decimal | None] = mapped_column(Numeric(8, 3)) source: Mapped[str] = mapped_column(String(20), server_default="manual") recorded_at: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=True), server_default=text("now()")) class ShoppingList(Base): __tablename__ = "lists" __table_args__ = {"schema": "shopping"} id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name: Mapped[str | None] = mapped_column(String(100)) store_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("shopping.stores.id", ondelete="SET NULL")) week_date: Mapped[date | None] = mapped_column(Date) status: Mapped[str] = mapped_column(String(20), server_default="draft") owner_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True)) created_at: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=True), server_default=text("now()")) items: Mapped[list["ListItem"]] = relationship("ListItem", back_populates="shopping_list", cascade="all, delete-orphan") class ListItem(Base): __tablename__ = "list_items" __table_args__ = {"schema": "shopping"} id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) list_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("shopping.lists.id", ondelete="CASCADE"), nullable=False) product_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("shopping.products.id", ondelete="SET NULL")) custom_name: Mapped[str | None] = mapped_column(String(150)) quantity: Mapped[Decimal | None] = mapped_column(Numeric(8, 3)) unit: Mapped[str | None] = mapped_column(String(20)) is_checked: Mapped[bool] = mapped_column(Boolean, server_default=text("false")) price_recorded: Mapped[Decimal | None] = mapped_column(Numeric(8, 2)) carried_over: Mapped[bool] = mapped_column(Boolean, server_default=text("false")) sort_order: Mapped[int | None] = mapped_column(Integer) shopping_list: Mapped["ShoppingList"] = relationship("ShoppingList", back_populates="items") product: Mapped["Product | None"] = relationship("Product", lazy="select")