import uuid from datetime import date from decimal import Decimal from typing import TYPE_CHECKING, Optional from barker.models.product import Product from sqlalchemy import ( Boolean, Date, ForeignKey, Integer, Numeric, Unicode, case, func, text, ) from sqlalchemy.dialects import postgresql 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 .menu_category import MenuCategory from .sale_category import SaleCategory @reg.mapped_as_dataclass(unsafe_hash=True) class ProductVersion: __tablename__ = "product_versions" id: Mapped[uuid.UUID] = mapped_column( "id", UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"), insert_default=uuid.uuid4 ) product_id: Mapped[uuid.UUID] = mapped_column( "product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False ) name: Mapped[str] = mapped_column("name", Unicode(255), nullable=False) units: Mapped[str] = mapped_column("units", Unicode(255), nullable=False) menu_category_id: Mapped[uuid.UUID] = mapped_column( "menu_category_id", UUID(as_uuid=True), ForeignKey("menu_categories.id"), nullable=False, ) sale_category_id: Mapped[uuid.UUID] = mapped_column( "sale_category_id", UUID(as_uuid=True), ForeignKey("sale_categories.id"), nullable=False, ) price: Mapped[Decimal] = mapped_column("price", Numeric(precision=15, scale=2), nullable=False) has_happy_hour: Mapped[bool] = mapped_column("has_happy_hour", Boolean, nullable=False) is_not_available: Mapped[bool] = mapped_column("is_not_available", Boolean, nullable=False) quantity: Mapped[Decimal] = mapped_column("quantity", Numeric(precision=15, scale=2), nullable=False) sort_order: Mapped[int] = mapped_column("sort_order", Integer, nullable=False) valid_from: Mapped[Optional[date]] = mapped_column("valid_from", Date(), nullable=True) valid_till: Mapped[Optional[date]] = mapped_column("valid_till", Date(), nullable=True) menu_category: Mapped["MenuCategory"] = relationship(back_populates="products") sale_category: Mapped["SaleCategory"] = relationship(back_populates="products") product: Mapped["Product"] = relationship(back_populates="versions") __table_args__ = ( postgresql.ExcludeConstraint( (name, "="), (units, "="), (func.daterange(valid_from, valid_till, text("'[]'")), "&&"), ), postgresql.ExcludeConstraint( (product_id, "="), (units, "="), (func.daterange(valid_from, valid_till, text("'[]'")), "&&"), ), ) def __init__( self, product_id=None, name=None, units=None, menu_category_id=None, sale_category_id=None, price=None, has_happy_hour=None, is_not_available=None, quantity=None, valid_from=None, valid_till=None, sort_order=0, id_=None, ): self.product_id = product_id self.name = name self.units = units self.menu_category_id = menu_category_id self.sale_category_id = sale_category_id self.price = price self.has_happy_hour = has_happy_hour self.is_not_available = is_not_available self.quantity = quantity self.valid_from = valid_from self.valid_till = valid_till self.sort_order = sort_order self.id = id_ @hybrid_property def full_name(self): return f"{self.name} ({self.units})" if self.units else self.name @full_name.inplace.expression def _full_name_expression(cls): return cls.name + case((cls.units != "", " (" + cls.units + ")"), else_="") def can_delete(self, advanced_delete): if self.is_fixture: return False, f"{self.name} is a fixture and cannot be edited or deleted." if self.is_active: return False, "Product is active" # if len(self.inventories) > 0 and not advanced_delete: # return False, "Product has entries" return True, ""