import uuid from decimal import Decimal from typing import TYPE_CHECKING, List from barker.models.product import Product from sqlalchemy import ( Boolean, ForeignKey, Integer, Numeric, UniqueConstraint, case, func, text, ) from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import Mapped, mapped_column, relationship from ..db.base_class import reg if TYPE_CHECKING: from .inventory_modifier import InventoryModifier from .kot import Kot from .tax import Tax @reg.mapped_as_dataclass(unsafe_hash=True) class Inventory: __tablename__ = "inventories" __table_args__ = (UniqueConstraint("kot_id", "product_id", "is_happy_hour", "price"),) id: Mapped[uuid.UUID] = mapped_column( "id", UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"), insert_default=uuid.uuid4 ) kot_id: Mapped[uuid.UUID] = mapped_column( "kot_id", UUID(as_uuid=True), ForeignKey("kots.id"), nullable=False, index=True ) product_id: Mapped[uuid.UUID] = mapped_column( "product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False ) quantity: Mapped[Decimal] = mapped_column("quantity", Numeric(precision=15, scale=2), nullable=False) price: Mapped[Decimal] = mapped_column("price", Numeric(precision=15, scale=2), nullable=False) is_happy_hour: Mapped[bool] = mapped_column("is_happy_hour", Boolean, nullable=False) tax_rate: Mapped[Decimal] = mapped_column("tax_rate", Numeric(precision=15, scale=5), nullable=False) tax_id: Mapped[uuid.UUID] = mapped_column("tax_id", UUID(as_uuid=True), ForeignKey("taxes.id"), nullable=False) discount: Mapped[Decimal] = mapped_column("discount", Numeric(precision=15, scale=5), nullable=False) sort_order: Mapped[int] = mapped_column("sort_order", Integer, nullable=False) kot: Mapped["Kot"] = relationship(back_populates="inventories") tax: Mapped["Tax"] = relationship(back_populates="inventories") product: Mapped["Product"] = relationship(back_populates="inventories") modifiers: Mapped[List["InventoryModifier"]] = relationship(back_populates="inventory") def __init__( self, kot_id=None, product_id=None, quantity=None, price=None, discount=None, is_hh=None, tax_id=None, tax_rate=None, sort_order=None, product=None, tax=None, ): self.kot_id = kot_id if product_id is not None: self.product_id = product_id self.quantity = quantity self.price = price self.discount = discount self.is_happy_hour = is_hh if tax_id is not None: self.tax_id = tax_id self.tax_rate = tax_rate self.sort_order = sort_order if product is not None: self.product = product if tax is not None: self.tax = tax @hybrid_property def effective_price(self): return 0 if self.is_happy_hour else self.price @effective_price.inplace.expression @classmethod def _effective_price_expression(cls): return case((cls.is_happy_hour == True, 0), else_=cls.price) # noqa: E712 @hybrid_property def net(self): return self.effective_price * self.quantity * (1 - self.discount) @net.inplace.expression @classmethod def _net_expression(cls): return cls.effective_price * cls.quantity * (1 - cls.discount) @hybrid_property def tax_amount(self): return self.net * self.tax_rate @tax_amount.inplace.expression @classmethod def _tax_amount_expression(cls): return cls.net * cls.tax_rate @hybrid_property def amount(self): return round(Decimal(self.net * (1 + self.tax_rate)), 2) @amount.inplace.expression @classmethod def _amount_expression(cls): return func.round(cls.net * (1 + cls.tax_rate), 2)