from datetime import datetime from enum import Enum import uuid from decimal import Decimal from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy import ( Column, Integer, Boolean, Unicode, DateTime, Numeric, ForeignKey, UniqueConstraint, case, ) from sqlalchemy.orm import relationship, backref, synonym from sqlalchemy.dialects.postgresql import UUID from .meta import Base class VoucherType(Enum): KOT = 0 REGULAR_BILL = 1 NO_CHARGE = 2 STAFF = 4 VOID = 5 class GuestBook(Base): __tablename__ = "guest_book" id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) customer_id = Column("customer_id", UUID(as_uuid=True), ForeignKey("customers.id"), nullable=False) pax = Column("pax", Numeric, nullable=False) date = Column("creation_date", DateTime(timezone=True), nullable=False) customer = relationship("Customer") def __init__(self, pax=None, id_=None, customer_id=None, customer=None): self.customer_id = customer_id self.pax = pax self.id = id_ self.date = datetime.utcnow() if customer is None: self.customer_id = customer_id else: self.customer = customer class Overview(Base): __tablename__ = "overview" id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) voucher_id = Column("voucher_id", UUID(as_uuid=True), ForeignKey("vouchers.id"), nullable=False, unique=True) food_table_id = Column("food_table_id", UUID(as_uuid=True), ForeignKey("food_tables.id"), nullable=False, unique=True,) guest_book_id = Column("guest_book_id", UUID(as_uuid=True), ForeignKey("guest_book.id"), unique=True) status = Column("status", Unicode(255), nullable=False) voucher = relationship("Voucher", backref=backref("status", uselist=False)) food_table = relationship("FoodTable", backref=backref("status", uselist=False)) guest = relationship("GuestBook", backref=backref("status", uselist=False)) def __init__(self, voucher_id, food_table_id, guest_book_id, status, id_=None): self.voucher_id = voucher_id self.food_table_id = food_table_id self.guest_book_id = guest_book_id self.status = status self.id = id_ class InventoryModifier(Base): __tablename__ = "inventory_modifiers" __table_args__ = (UniqueConstraint("inventory_id", "modifier_id"),) id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) inventory_id = Column("inventory_id", UUID(as_uuid=True), ForeignKey("inventories.id"), nullable=False) modifier_id = Column("modifier_id", UUID(as_uuid=True), ForeignKey("modifiers.id"), nullable=False) price = Column("price", Numeric, nullable=False) inventory = relationship("Inventory", backref="modifiers") modifier = relationship("Modifier") def __init__(self, inventory_id, modifier_id, price): self.inventory_id = inventory_id self.modifier_id = modifier_id self.price = price class Voucher(Base): __tablename__ = "vouchers" __table_args__ = (UniqueConstraint("bill_id", "voucher_type"),) id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) date = Column("date", DateTime, nullable=False, index=True) pax = Column("pax", Numeric, nullable=False) bill_id = Column("bill_id", Numeric) kot_id = Column("kot_id", Numeric, nullable=False, unique=True) creation_date = Column("creation_date", DateTime(timezone=True), nullable=False) last_edit_date = Column("last_edit_date", DateTime(timezone=True), nullable=False) food_table_id = Column("food_table_id", UUID(as_uuid=True), ForeignKey("food_tables.id"), nullable=False) customer_id = Column("customer_id", UUID(as_uuid=True), ForeignKey("customers.id")) narration = Column("narration", Unicode(1000), nullable=False) reason = Column("reason", Unicode(255)) _voucher_type = Column("voucher_type", Integer, nullable=False) user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) user = relationship("User", backref="vouchers") food_table = relationship("FoodTable", backref="vouchers") customer = relationship("Customer", backref="vouchers") kots = relationship("Kot", backref="voucher", cascade="delete, delete-orphan", cascade_backrefs=False,) settlements = relationship( "Settlement", backref="voucher", cascade="delete, delete-orphan", cascade_backrefs=False, ) reprints = relationship("Reprint", backref="voucher", cascade="delete, delete-orphan", cascade_backrefs=False,) def _get_voucher_type(self): return VoucherType(self._voucher_type) def _set_voucher_type(self, voucher_type): self._voucher_type = voucher_type.value voucher_type = property(_get_voucher_type, _set_voucher_type) voucher_type = synonym("_voucher_type", descriptor=voucher_type) def __init__( self, date, pax, bill_id, kot_id, food_table_id, customer_id, voucher_type, user_id, ): self.date = date self.pax = pax self.creation_date = date self.last_edit_date = date self.bill_id = bill_id self.kot_id = kot_id self.food_table_id = food_table_id self.customer_id = customer_id self.narration = "" self.voucher_type = voucher_type self.user_id = user_id @property def full_bill_id(self): if self.voucher_type == VoucherType.KOT: return "K-" + str(self.kot_id) if self.voucher_type == VoucherType.NO_CHARGE: return "NC-" + str(self.bill_id) if self.voucher_type == VoucherType.STAFF: return "ST-" + str(self.bill_id) if self.voucher_type == VoucherType.REGULAR_BILL: return str(self.bill_id // 10000) + "-" + str(self.bill_id % 10000) if self.voucher_type == VoucherType.VOID: return "K-" + str(self.kot_id) else: raise Exception @property def amount(self): return round(sum(i.amount for k in self.kots for i in k.inventories), 2) class Kot(Base): __tablename__ = "kots" id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) voucher_id = Column("voucher_id", UUID(as_uuid=True), ForeignKey("vouchers.id"), nullable=False, index=True) code = Column("code", Numeric, nullable=False, unique=True) food_table_id = Column("food_table_id", UUID(as_uuid=True), ForeignKey("food_tables.id"), nullable=False) date = Column("date", DateTime, nullable=False, index=True) user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) user = relationship("User", backref="kots") food_table = relationship("FoodTable", backref="kots") def __init__( self, voucher_id=None, code=None, food_table_id=None, date=None, user_id=None, id_=None, ): self.id = id_ self.voucher_id = voucher_id self.code = code self.food_table_id = food_table_id self.date = date self.user_id = user_id class Settlement(Base): __tablename__ = "settlements" __table_args__ = (UniqueConstraint("voucher_id", "settled"),) id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) voucher_id = Column("voucher_id", UUID(as_uuid=True), ForeignKey("vouchers.id"), nullable=False, index=True) settled = Column("settled", ForeignKey("settle_options.id"), nullable=False) amount = Column("amount", Numeric, nullable=False) settle_option = relationship("SettleOption") def __init__(self, voucher_id=None, settled=None, amount=None, id_=None): self.id = id_ self.voucher_id = voucher_id self.settled = settled self.amount = amount class Reprint(Base): __tablename__ = "reprints" id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) date = Column("date", DateTime, nullable=False, index=True) voucher_id = Column("voucher_id", UUID(as_uuid=True), ForeignKey("vouchers.id"), nullable=False, index=True) user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) user = relationship("User", backref="reprints") def __init__(self, voucher_id=None, user_id=None, id_=None): self.id = id_ self.date = datetime.now() self.voucher_id = voucher_id self.user_id = user_id class Inventory(Base): __tablename__ = "inventories" __table_args__ = (UniqueConstraint("kot_id", "product_id", "is_happy_hour", "price"),) id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) kot_id = Column("kot_id", UUID(as_uuid=True), ForeignKey("kots.id"), nullable=False, index=True) product_id = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False) quantity = Column("quantity", Numeric) price = Column("price", Numeric) is_happy_hour = Column("is_happy_hour", Boolean, nullable=False) tax_rate = Column("tax_rate", Numeric) tax_id = Column("tax_id", UUID(as_uuid=True), ForeignKey("taxes.id"), nullable=False) discount = Column("discount", Numeric) sort_order = Column("sort_order", Numeric, nullable=False) kot = relationship("Kot", backref="inventories") tax = relationship("Tax", foreign_keys=tax_id) product = relationship("Product", backref="inventories") def __init__( self, kot_id, product_id, quantity, price, discount, is_hh, tax_id, tax_rate, sort_order, ): self.kot_id = kot_id self.product_id = product_id self.quantity = quantity self.price = price self.discount = discount self.is_happy_hour = is_hh self.tax_id = tax_id self.tax_rate = tax_rate self.sort_order = sort_order @hybrid_property def effective_price(self): return 0 if self.is_happy_hour else self.price @effective_price.expression def effective_price(cls): return case([(cls.is_happy_hour == True, 0)], else_=cls.price) @hybrid_property def net(self): return self.effective_price * self.quantity * (1 - self.discount) @net.expression def net(cls): return cls.effective_price * cls.quantity * (1 - cls.discount) @hybrid_property def tax_amount(self): return self.net * self.tax_rate @tax_amount.expression def tax_amount(cls): return cls.net * cls.tax_rate @hybrid_property def amount(self): return Decimal(self.net * (1 + self.tax_rate)) @amount.expression def amount(cls): return cls.net * (1 + cls.tax_rate)