114 lines
6.5 KiB
Python
114 lines
6.5 KiB
Python
from datetime import datetime
|
|
from decimal import Decimal
|
|
|
|
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, Numeric, String, Text, UniqueConstraint, func
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.db.database import Base
|
|
|
|
|
|
class InventoryCategory(Base):
|
|
__tablename__ = "inventory_categories"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
name: Mapped[str] = mapped_column(String(160), unique=True, index=True)
|
|
slug: Mapped[str] = mapped_column(String(180), unique=True, index=True)
|
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
|
|
|
items: Mapped[list["InventoryItem"]] = relationship(back_populates="category")
|
|
|
|
|
|
class InventoryLocation(Base):
|
|
__tablename__ = "inventory_locations"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
name: Mapped[str] = mapped_column(String(160), unique=True, index=True)
|
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
|
|
|
items: Mapped[list["InventoryItem"]] = relationship(back_populates="location")
|
|
|
|
|
|
class InventorySupplier(Base):
|
|
__tablename__ = "inventory_suppliers"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
name: Mapped[str] = mapped_column(String(180), unique=True, index=True)
|
|
contact_name: Mapped[str | None] = mapped_column(String(180), nullable=True)
|
|
email: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
phone: Mapped[str | None] = mapped_column(String(80), nullable=True)
|
|
website: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
customer_number: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
|
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
|
|
|
items: Mapped[list["InventoryItem"]] = relationship(back_populates="supplier")
|
|
|
|
|
|
class InventoryItem(Base):
|
|
__tablename__ = "inventory_items"
|
|
__table_args__ = (UniqueConstraint("sku", name="uq_inventory_items_sku"),)
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
sku: Mapped[str] = mapped_column(String(40), unique=True, index=True)
|
|
name: Mapped[str] = mapped_column(String(255), index=True)
|
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
category_id: Mapped[int | None] = mapped_column(ForeignKey("inventory_categories.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
manufacturer: Mapped[str | None] = mapped_column(String(180), nullable=True, index=True)
|
|
manufacturer_part_number: Mapped[str | None] = mapped_column(String(180), nullable=True, index=True)
|
|
supplier_id: Mapped[int | None] = mapped_column(ForeignKey("inventory_suppliers.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
supplier_part_number: Mapped[str | None] = mapped_column(String(180), nullable=True, index=True)
|
|
compatible_devices: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
location_id: Mapped[int | None] = mapped_column(ForeignKey("inventory_locations.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
quantity_on_hand: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
|
quantity_reserved: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
|
quantity_available: Mapped[int] = mapped_column(Integer, default=0, server_default="0", index=True)
|
|
reorder_level: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
|
unit: Mapped[str] = mapped_column(String(40), default="Stk.", server_default="Stk.")
|
|
purchase_price_cents: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
selling_price_cents: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
currency: Mapped[str] = mapped_column(String(3), default="EUR", server_default="EUR")
|
|
tax_rate_percent: Mapped[Decimal] = mapped_column(Numeric(5, 2), default=Decimal("19.00"), server_default="19.00")
|
|
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, server_default="true", index=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
|
|
|
category: Mapped[InventoryCategory | None] = relationship(back_populates="items", lazy="joined")
|
|
supplier: Mapped[InventorySupplier | None] = relationship(back_populates="items", lazy="joined")
|
|
location: Mapped[InventoryLocation | None] = relationship(back_populates="items", lazy="joined")
|
|
movements: Mapped[list["InventoryStockMovement"]] = relationship(
|
|
back_populates="item",
|
|
cascade="all, delete-orphan",
|
|
order_by="InventoryStockMovement.created_at.desc()",
|
|
)
|
|
|
|
|
|
class InventoryStockMovement(Base):
|
|
__tablename__ = "inventory_stock_movements"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
item_id: Mapped[int] = mapped_column(ForeignKey("inventory_items.id", ondelete="CASCADE"), index=True)
|
|
movement_type: Mapped[str] = mapped_column(String(40), index=True)
|
|
quantity: Mapped[int] = mapped_column(Integer)
|
|
reason: Mapped[str] = mapped_column(String(255), default="", server_default="")
|
|
reference_type: Mapped[str | None] = mapped_column(String(80), nullable=True, index=True)
|
|
reference_id: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True)
|
|
note: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
actor_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
item: Mapped[InventoryItem] = relationship(back_populates="movements")
|
|
actor = relationship(
|
|
"User",
|
|
foreign_keys="InventoryStockMovement.actor_user_id",
|
|
lazy="joined",
|
|
)
|
|
|
|
@property
|
|
def actor_username(self) -> str:
|
|
return self.actor.username if self.actor is not None else ""
|