from __future__ import annotations from datetime import date, datetime from sqlalchemy import Boolean, Date, DateTime, Float, ForeignKey, Index, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from app.db.session import Base class ThroughputProduct(Base): __tablename__ = "throughput_products" __table_args__ = ( Index("ix_throughput_products_tenant_item", "tenant_id", "item_id", unique=True), Index("ix_throughput_products_tenant_name", "tenant_id", "name"), ) id: Mapped[int] = mapped_column(primary_key=True) tenant_id: Mapped[str] = mapped_column(String(64), default="default", index=True) item_id: Mapped[str | None] = mapped_column(String(64), nullable=True) name: Mapped[str] = mapped_column(String(255)) client_name: Mapped[str | None] = mapped_column(String(255), nullable=True) default_bag_size: Mapped[float | None] = mapped_column(Float, nullable=True) is_bulka_default: Mapped[bool] = mapped_column(Boolean, default=False) active: Mapped[bool] = mapped_column(Boolean, default=True) is_stock_item: Mapped[bool] = mapped_column(Boolean, default=True) notes: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) entries: Mapped[list["ProductionThroughput"]] = relationship(back_populates="product") class ProductionThroughput(Base): __tablename__ = "production_throughput_entries" __table_args__ = ( Index("ix_throughput_entries_tenant_date", "tenant_id", "production_date"), ) id: Mapped[int] = mapped_column(primary_key=True) tenant_id: Mapped[str] = mapped_column(String(64), default="default", index=True) production_date: Mapped[date] = mapped_column(Date) product_id: Mapped[int | None] = mapped_column(ForeignKey("throughput_products.id"), nullable=True, index=True) product_name_snapshot: Mapped[str] = mapped_column(String(255)) bag_size: Mapped[float | None] = mapped_column(Float, nullable=True) scales_checked: Mapped[bool] = mapped_column(Boolean, default=True) label_correct: Mapped[bool] = mapped_column(Boolean, default=True) bag_sealed: Mapped[bool] = mapped_column(Boolean, default=True) pallet_good_condition: Mapped[bool] = mapped_column(Boolean, default=True) # Where the run is destined. A run can be for a client order, for stock, or # split across both. job_number records the Order Circle job for the order # portion; stock_quantity records how much of a split goes into stock (in the # same unit as `quantity`). for_order: Mapped[bool] = mapped_column(Boolean, default=False) for_stock: Mapped[bool] = mapped_column(Boolean, default=False) job_number: Mapped[str | None] = mapped_column(String(64), nullable=True) stock_quantity: Mapped[float | None] = mapped_column(Float, nullable=True) sample_box_no: Mapped[str | None] = mapped_column(String(64), nullable=True) test_weight_1: Mapped[float | None] = mapped_column(Float, nullable=True) test_weight_2: Mapped[float | None] = mapped_column(Float, nullable=True) test_weight_3: Mapped[float | None] = mapped_column(Float, nullable=True) test_weight_4: Mapped[float | None] = mapped_column(Float, nullable=True) test_weight_5: Mapped[float | None] = mapped_column(Float, nullable=True) quantity: Mapped[float] = mapped_column(Float, default=0.0) quantity_type: Mapped[str] = mapped_column(String(8), default="bags") calculated_kg: Mapped[float] = mapped_column(Float, default=0.0) staff_name: Mapped[str | None] = mapped_column(String(255), nullable=True) notes: Mapped[str | None] = mapped_column(Text, nullable=True) created_by: Mapped[str | None] = mapped_column(String(255), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) product: Mapped[ThroughputProduct | None] = relationship(back_populates="entries")