Feature: Table Booking in guestbook.
Feature: Guest book list shows the running cover count
This commit is contained in:
parent
1cb2677ad1
commit
ecea277e46
barker
alembic/versions
barker
bookie/src/app
180
barker/alembic/versions/7e9944b430d6_booking.py
Normal file
180
barker/alembic/versions/7e9944b430d6_booking.py
Normal file
@ -0,0 +1,180 @@
|
||||
"""booking
|
||||
|
||||
Revision ID: 7e9944b430d6
|
||||
Revises: 1d95eb10ea20
|
||||
Create Date: 2023-08-19 14:25:40.206187
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "7e9944b430d6"
|
||||
down_revision = "1d95eb10ea20"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column("customers", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"customers", "phone", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"customers", "address", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=True
|
||||
)
|
||||
op.alter_column("devices", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"food_tables", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"menu_categories", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"modifier_categories", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column("modifiers", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
overviewstatus = sa.Enum("running", "printed", name="overviewstatus")
|
||||
overviewstatus.create(op.get_bind())
|
||||
op.execute("ALTER TABLE overview ALTER COLUMN status TYPE overviewstatus USING status::text::overviewstatus;")
|
||||
op.alter_column(
|
||||
"overview",
|
||||
"status",
|
||||
existing_type=sa.VARCHAR(length=255),
|
||||
type_=overviewstatus,
|
||||
existing_nullable=False,
|
||||
)
|
||||
op.alter_column(
|
||||
"permissions", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column("printers", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"printers", "address", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"printers", "cut_code", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"product_versions", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"product_versions", "units", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column("regimes", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column("regimes", "header", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column("regimes", "prefix", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column("roles", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"sale_categories", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column("sections", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column("settings", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column("settings", "data", existing_type=postgresql.JSON(astext_type=sa.Text()), nullable=False)
|
||||
op.alter_column(
|
||||
"settle_options", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
|
||||
)
|
||||
op.alter_column("taxes", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column("users", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column("users", "password", existing_type=sa.VARCHAR(length=60), type_=sa.Text(), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"vouchers", "narration", existing_type=sa.VARCHAR(length=1000), type_=sa.Text(), existing_nullable=True
|
||||
)
|
||||
op.alter_column("vouchers", "reason", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=True)
|
||||
|
||||
guestbooktype = sa.Enum("walk_in", "booking", "arrived", name="guestbooktype")
|
||||
guestbooktype.create(op.get_bind())
|
||||
gbt = sa.table(
|
||||
"guest_book",
|
||||
sa.column("last_edit_date", sa.DateTime()),
|
||||
sa.column("arrival_date", sa.DateTime()),
|
||||
sa.column("type", guestbooktype),
|
||||
)
|
||||
|
||||
with op.batch_alter_table("guest_book") as batch_op:
|
||||
batch_op.add_column(sa.Column("booking_date", sa.DateTime(), nullable=True))
|
||||
batch_op.alter_column("creation_date", new_column_name="last_edit_date")
|
||||
batch_op.add_column(sa.Column("arrival_date", sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column("notes", sa.Text(), nullable=True))
|
||||
batch_op.add_column(sa.Column("type", guestbooktype, nullable=True))
|
||||
op.execute(gbt.update().values(arrival_date=sa.text("last_edit_date")))
|
||||
op.execute(gbt.update().values(type="walk_in"))
|
||||
with op.batch_alter_table("guest_book") as batch_op:
|
||||
batch_op.alter_column("type", existing_type=guestbooktype, nullable=False)
|
||||
batch_op.create_check_constraint("ck_guest_book_nulls", sa.text("num_nonnulls(booking_date, arrival_date) > 0"))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column("vouchers", "reason", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=True)
|
||||
op.alter_column(
|
||||
"vouchers", "narration", existing_type=sa.Text(), type_=sa.VARCHAR(length=1000), existing_nullable=True
|
||||
)
|
||||
op.alter_column("users", "password", existing_type=sa.Text(), type_=sa.VARCHAR(length=60), existing_nullable=False)
|
||||
op.alter_column("users", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column("taxes", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"settle_options", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.alter_column("settings", "data", existing_type=postgresql.JSON(astext_type=sa.Text()), nullable=True)
|
||||
op.alter_column("settings", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column("sections", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"sale_categories", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.alter_column("roles", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column("regimes", "prefix", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column("regimes", "header", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column("regimes", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"product_versions", "units", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"product_versions", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"printers", "cut_code", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"printers", "address", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.alter_column("printers", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"permissions", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"overview",
|
||||
"status",
|
||||
existing_type=sa.Enum("running", "printed", name="overviewstatus"),
|
||||
type_=sa.VARCHAR(length=255),
|
||||
existing_nullable=False,
|
||||
)
|
||||
op.alter_column("modifiers", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"modifier_categories", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"menu_categories", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.add_column("guest_book", sa.Column("creation_date", postgresql.TIMESTAMP(), autoincrement=False, nullable=False))
|
||||
op.drop_column("guest_book", "type_")
|
||||
op.drop_column("guest_book", "notes")
|
||||
op.drop_column("guest_book", "last_edit_date")
|
||||
op.drop_column("guest_book", "date")
|
||||
op.alter_column(
|
||||
"food_tables", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.alter_column("devices", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
op.alter_column(
|
||||
"customers", "address", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=True
|
||||
)
|
||||
op.alter_column(
|
||||
"customers", "phone", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
|
||||
)
|
||||
op.alter_column("customers", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
|
||||
# ### end Alembic commands ###
|
@ -16,6 +16,7 @@ from ..models.modifier import Modifier # noqa: F401
|
||||
from ..models.modifier_category import ModifierCategory # noqa: F401
|
||||
from ..models.modifier_category_product import ModifierCategoryProduct # noqa: F401
|
||||
from ..models.overview import Overview # noqa: F401
|
||||
from ..models.overview_status import OverviewStatus # noqa: F401
|
||||
from ..models.permission import Permission # noqa: F401
|
||||
from ..models.printer import Printer # noqa: F401
|
||||
from ..models.product import Product # noqa: F401
|
||||
|
@ -1,6 +1,6 @@
|
||||
import uuid
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from sqlalchemy import Boolean, ForeignKey, Integer, Text, Uuid, text
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
@ -25,7 +25,7 @@ class FoodTable:
|
||||
sort_order: Mapped[bool] = mapped_column(Integer, nullable=False)
|
||||
|
||||
section: Mapped["Section"] = relationship("Section", foreign_keys=section_id)
|
||||
status: Mapped["Overview" | None] = relationship(back_populates="food_table", uselist=False)
|
||||
status: Mapped[Optional["Overview"]] = relationship(back_populates="food_table", uselist=False)
|
||||
|
||||
@property
|
||||
def __name__(self):
|
||||
|
@ -1,9 +1,19 @@
|
||||
import uuid
|
||||
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, Integer, Uuid, text
|
||||
from barker.models.guest_book_type import GuestBookType
|
||||
from sqlalchemy import (
|
||||
CheckConstraint,
|
||||
DateTime,
|
||||
Enum,
|
||||
ForeignKey,
|
||||
Integer,
|
||||
Text,
|
||||
Uuid,
|
||||
text,
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from ..db.base_class import reg
|
||||
@ -17,20 +27,44 @@ if TYPE_CHECKING:
|
||||
@reg.mapped_as_dataclass(unsafe_hash=True)
|
||||
class GuestBook:
|
||||
__tablename__ = "guest_book"
|
||||
__table_args__ = (CheckConstraint("num_nonnulls(booking_date, arrival_date) > 0", name="ck_guest_book_nulls"),)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, server_default=text("gen_random_uuid()"))
|
||||
customer_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("customers.id"), nullable=False)
|
||||
pax: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
date: Mapped[datetime] = mapped_column("creation_date", DateTime(), nullable=False)
|
||||
booking_date: Mapped[Optional[datetime]] = mapped_column(DateTime(), nullable=True)
|
||||
arrival_date: Mapped[Optional[datetime]] = mapped_column(DateTime(), nullable=True)
|
||||
last_edit_date: Mapped[datetime] = mapped_column(DateTime(), nullable=False)
|
||||
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
type_: Mapped[GuestBookType] = mapped_column("type", Enum(GuestBookType), nullable=False)
|
||||
|
||||
customer: Mapped["Customer"] = relationship("Customer")
|
||||
status: Mapped["Overview" | None] = relationship(back_populates="guest", uselist=False)
|
||||
status: Mapped[Optional["Overview"]] = relationship(back_populates="guest", uselist=False)
|
||||
|
||||
def __init__(self, pax=None, id_=None, customer_id=None, customer=None, date_=None):
|
||||
self.customer_id = customer_id
|
||||
def __init__(
|
||||
self,
|
||||
customer_id=None,
|
||||
pax=None,
|
||||
booking_date=None,
|
||||
arrival_date=None,
|
||||
notes=None,
|
||||
type_=None,
|
||||
customer=None,
|
||||
id_=None,
|
||||
):
|
||||
if customer_id is not None:
|
||||
self.customer_id = customer_id
|
||||
self.pax = pax
|
||||
self.id = id_
|
||||
self.date = datetime.utcnow() if date_ is None else date_
|
||||
if booking_date is None and arrival_date is None:
|
||||
raise Exception("Both arrival and booking date cannot be null")
|
||||
|
||||
self.booking_date = booking_date
|
||||
self.arrival_date = arrival_date
|
||||
self.last_edit_date = datetime.utcnow()
|
||||
self.notes = notes
|
||||
self.type_ = type_
|
||||
if customer is None:
|
||||
self.customer_id = customer_id
|
||||
else:
|
||||
self.customer = customer
|
||||
self.id = id_
|
||||
|
10
barker/barker/models/guest_book_status.py
Normal file
10
barker/barker/models/guest_book_status.py
Normal file
@ -0,0 +1,10 @@
|
||||
import enum
|
||||
|
||||
|
||||
class GuestBookStatus(str, enum.Enum):
|
||||
running = "running"
|
||||
printed = "printed"
|
||||
walk_in = "walk_in"
|
||||
booking = "booking"
|
||||
arrived = "arrived"
|
||||
old = "old"
|
7
barker/barker/models/guest_book_type.py
Normal file
7
barker/barker/models/guest_book_type.py
Normal file
@ -0,0 +1,7 @@
|
||||
import enum
|
||||
|
||||
|
||||
class GuestBookType(str, enum.Enum):
|
||||
walk_in = "walk_in"
|
||||
booking = "booking"
|
||||
arrived = "arrived"
|
@ -83,7 +83,7 @@ class Inventory:
|
||||
|
||||
@hybrid_property
|
||||
def effective_price(self) -> Decimal:
|
||||
return 0 if self.is_happy_hour else self.price
|
||||
return Decimal(0) if self.is_happy_hour else self.price
|
||||
|
||||
@effective_price.inplace.expression
|
||||
@classmethod
|
||||
|
@ -1,8 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import ForeignKey, Text, Uuid, text
|
||||
from barker.models.overview_status import OverviewStatus
|
||||
from sqlalchemy import Enum, ForeignKey, Uuid, text
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from ..db.base_class import reg
|
||||
@ -31,7 +34,7 @@ class Overview:
|
||||
unique=True,
|
||||
)
|
||||
guest_book_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("guest_book.id"), unique=True, nullable=True)
|
||||
status: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
status: Mapped[OverviewStatus] = mapped_column(Enum(OverviewStatus), nullable=False)
|
||||
|
||||
voucher: Mapped["Voucher"] = relationship(back_populates="status")
|
||||
food_table: Mapped["FoodTable"] = relationship(back_populates="status")
|
||||
|
6
barker/barker/models/overview_status.py
Normal file
6
barker/barker/models/overview_status.py
Normal file
@ -0,0 +1,6 @@
|
||||
import enum
|
||||
|
||||
|
||||
class OverviewStatus(str, enum.Enum):
|
||||
running = "running"
|
||||
printed = "printed"
|
@ -1,7 +1,7 @@
|
||||
import uuid
|
||||
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, List
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
|
||||
from sqlalchemy import DateTime, Enum, ForeignKey, Integer, Text, Uuid, text
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
@ -63,7 +63,7 @@ class Voucher:
|
||||
back_populates="voucher",
|
||||
cascade="delete, delete-orphan",
|
||||
)
|
||||
status: Mapped["Overview" | None] = relationship(back_populates="voucher", uselist=False)
|
||||
status: Mapped[Optional["Overview"]] = relationship(back_populates="voucher", uselist=False)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -4,8 +4,10 @@ from datetime import date, datetime, timedelta
|
||||
|
||||
import barker.schemas.guest_book as schemas
|
||||
|
||||
from barker.models.guest_book_status import GuestBookStatus
|
||||
from barker.models.guest_book_type import GuestBookType
|
||||
from fastapi import APIRouter, Depends, HTTPException, Security, status
|
||||
from sqlalchemy import desc, select
|
||||
from sqlalchemy import and_, desc, func, or_, select
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
@ -47,8 +49,16 @@ def save(
|
||||
else:
|
||||
customer.name = data.name or customer.name
|
||||
customer.address = data.address or customer.address
|
||||
td = timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)
|
||||
booking_date = None if data.booking_date is None else data.booking_date - td
|
||||
arrival_date = None if data.arrival_date is None else data.arrival_date - td
|
||||
item = GuestBook(
|
||||
pax=data.pax, customer=customer, date_=data.date_ - timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)
|
||||
pax=data.pax,
|
||||
customer=customer,
|
||||
booking_date=booking_date,
|
||||
arrival_date=arrival_date,
|
||||
notes=data.notes,
|
||||
type_=data.type_,
|
||||
)
|
||||
db.add(item)
|
||||
db.commit()
|
||||
@ -73,7 +83,14 @@ def update_route(
|
||||
item.customer.phone = data.phone
|
||||
item.customer.address = data.address
|
||||
item.pax = data.pax
|
||||
item.date = data.date_ - timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)
|
||||
td = timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)
|
||||
booking_date = None if data.booking_date is None else data.booking_date - td
|
||||
arrival_date = None if data.arrival_date is None else data.arrival_date - td
|
||||
item.booking_date = booking_date
|
||||
item.arrival_date = arrival_date
|
||||
item.last_edit_date = datetime.now()
|
||||
item.notes = data.notes
|
||||
item.type_ = data.type_
|
||||
db.commit()
|
||||
return guest_book_info(item)
|
||||
except SQLAlchemyError as e:
|
||||
@ -103,9 +120,10 @@ def delete_route(
|
||||
|
||||
@router.get("", response_model=schemas.GuestBookIn)
|
||||
def show_blank(
|
||||
t: GuestBookStatus | None,
|
||||
user: UserToken = Security(get_user, scopes=["customers"]),
|
||||
) -> schemas.GuestBookIn:
|
||||
return blank_guest_book_info()
|
||||
return blank_guest_book_info(t or GuestBookStatus.walk_in)
|
||||
|
||||
|
||||
@router.get("/list", response_model=schemas.GuestBookList)
|
||||
@ -117,33 +135,39 @@ def show_list(
|
||||
d = date.today()
|
||||
else:
|
||||
d = datetime.strptime(q, "%d-%b-%Y")
|
||||
s = d + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
|
||||
f = d + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES, days=1)
|
||||
list_ = (
|
||||
select(GuestBook)
|
||||
.where(
|
||||
GuestBook.date >= d + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
|
||||
)
|
||||
.where(
|
||||
GuestBook.date
|
||||
< d
|
||||
+ timedelta(
|
||||
minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES,
|
||||
days=1,
|
||||
or_(
|
||||
and_(GuestBook.arrival_date >= s, GuestBook.arrival_date < f),
|
||||
and_(GuestBook.booking_date >= s, GuestBook.booking_date < f),
|
||||
)
|
||||
)
|
||||
.order_by(GuestBook.date)
|
||||
.order_by(func.coalesce(GuestBook.arrival_date, GuestBook.booking_date))
|
||||
)
|
||||
|
||||
guest_book: list[schemas.GuestBookListItem] = []
|
||||
with SessionFuture() as db:
|
||||
count = 0
|
||||
for i, item in enumerate(db.execute(list_).scalars().all()):
|
||||
status = (
|
||||
GuestBookStatus(item.type_.name) if item.status is None else GuestBookStatus(item.status.status.name)
|
||||
)
|
||||
if item.type_ != GuestBookType.booking:
|
||||
count += item.pax
|
||||
gbli = schemas.GuestBookListItem(
|
||||
id_=item.id,
|
||||
serial=i + 1,
|
||||
name=item.customer.name,
|
||||
phone=item.customer.phone,
|
||||
pax=item.pax,
|
||||
date_=item.date,
|
||||
status=None if item.status is None else item.status.status,
|
||||
count=count,
|
||||
booking_date=item.booking_date,
|
||||
arrival_date=item.arrival_date,
|
||||
last_edit_date=item.last_edit_date,
|
||||
status=status,
|
||||
table_id=None if item.status is None else item.status.food_table.id,
|
||||
voucher_id=None if item.status is None else item.status.voucher_id,
|
||||
table_name=None if item.status is None else item.status.food_table.name,
|
||||
@ -160,7 +184,7 @@ def show_list(
|
||||
.first()
|
||||
)
|
||||
if last is not None:
|
||||
gbli.status = "old"
|
||||
gbli.status = GuestBookStatus.old
|
||||
gbli.voucher_id = last.id
|
||||
gbli.table_name = last.food_table.name
|
||||
guest_book.insert(0, gbli)
|
||||
@ -178,21 +202,34 @@ def show_id(
|
||||
|
||||
|
||||
def guest_book_info(item: GuestBook) -> schemas.GuestBook:
|
||||
td = timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)
|
||||
booking_date = None if item.booking_date is None else item.booking_date + td
|
||||
arrival_date = None if item.arrival_date is None else item.arrival_date + td
|
||||
return schemas.GuestBook(
|
||||
id_=item.id,
|
||||
name=item.customer.name,
|
||||
phone=item.customer.phone,
|
||||
pax=item.pax,
|
||||
address=item.customer.address,
|
||||
date_=item.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES),
|
||||
booking_date=booking_date,
|
||||
arrival_date=arrival_date,
|
||||
last_edit_date=item.last_edit_date + td,
|
||||
notes=item.notes,
|
||||
type_=GuestBookStatus(item.type_.name),
|
||||
)
|
||||
|
||||
|
||||
def blank_guest_book_info() -> schemas.GuestBookIn:
|
||||
def blank_guest_book_info(type_: GuestBookStatus) -> schemas.GuestBookIn:
|
||||
now = datetime.utcnow() + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)
|
||||
booking_date = None if type_ != GuestBookStatus.booking else now
|
||||
arrival_date = None if type_ != GuestBookStatus.walk_in else now
|
||||
return schemas.GuestBookIn(
|
||||
name="",
|
||||
phone="",
|
||||
pax=0,
|
||||
address="",
|
||||
date_=datetime.utcnow() + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES),
|
||||
booking_date=booking_date,
|
||||
arrival_date=arrival_date,
|
||||
notes=None,
|
||||
type_=type_,
|
||||
)
|
||||
|
@ -1,10 +1,13 @@
|
||||
import uuid
|
||||
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Set
|
||||
|
||||
import barker.schemas.voucher as schemas
|
||||
|
||||
from barker.models.guest_book_type import GuestBookType
|
||||
from barker.models.overview_status import OverviewStatus
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
@ -34,7 +37,7 @@ def get_tax(tax, voucher_type):
|
||||
|
||||
|
||||
def do_update_table(item: Voucher, guest_book: GuestBook | None, db: Session):
|
||||
status_ = "running" if item.voucher_type == VoucherType.KOT else "printed"
|
||||
status_ = OverviewStatus.running if item.voucher_type == VoucherType.KOT else OverviewStatus.printed
|
||||
if item.status is None:
|
||||
item.status = Overview(
|
||||
voucher_id=item.id,
|
||||
@ -42,6 +45,9 @@ def do_update_table(item: Voucher, guest_book: GuestBook | None, db: Session):
|
||||
guest_book_id=guest_book.id if guest_book is not None else None,
|
||||
status=status_,
|
||||
)
|
||||
if guest_book is not None and guest_book.type_ == GuestBookType.booking:
|
||||
guest_book.type_ = GuestBookType.arrived
|
||||
guest_book.arrival_date = datetime.now()
|
||||
db.add(item.status)
|
||||
else:
|
||||
item.status.status = status_
|
||||
|
@ -2,6 +2,7 @@ import uuid
|
||||
|
||||
import barker.schemas.voucher as schemas
|
||||
|
||||
from barker.models.overview_status import OverviewStatus
|
||||
from fastapi import APIRouter, HTTPException, Security, status
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
@ -101,7 +102,10 @@ def void_and_issue_new_bill(
|
||||
if old.status is None:
|
||||
guest_book_id = None if guest_book is None else guest_book.id
|
||||
item.status = Overview(
|
||||
voucher_id=None, food_table_id=item.food_table_id, guest_book_id=guest_book_id, status="printed"
|
||||
voucher_id=None,
|
||||
food_table_id=item.food_table_id,
|
||||
guest_book_id=guest_book_id,
|
||||
status=OverviewStatus.printed,
|
||||
)
|
||||
db.add(item.status)
|
||||
else:
|
||||
|
@ -2,7 +2,15 @@ import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator
|
||||
from barker.models.guest_book_status import GuestBookStatus
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
Field,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
model_validator,
|
||||
)
|
||||
|
||||
from . import to_camel
|
||||
|
||||
@ -12,51 +20,117 @@ class GuestBookIn(BaseModel):
|
||||
phone: str
|
||||
address: str | None = None
|
||||
pax: int = Field(ge=0)
|
||||
date_: datetime
|
||||
booking_date: datetime | None = None
|
||||
arrival_date: datetime | None = None
|
||||
notes: str | None = None
|
||||
type_: GuestBookStatus
|
||||
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("date_", mode="before")
|
||||
@field_validator("booking_date", mode="before")
|
||||
@classmethod
|
||||
def parse_date(cls, value):
|
||||
def parse_booking_date(cls, value):
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("date_")
|
||||
def serialize_date(self, value: datetime, _info):
|
||||
return value.strftime("%d-%b-%Y %H:%M")
|
||||
@field_serializer("booking_date")
|
||||
def serialize_booking_date(self, value: datetime | None, _info):
|
||||
return None if value is None else value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
@field_validator("arrival_date", mode="before")
|
||||
@classmethod
|
||||
def parse_arrival_date(cls, value):
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("arrival_date")
|
||||
def serialize_arrival_date(self, value: datetime | None, _info):
|
||||
return None if value is None else value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def nulls(self) -> "GuestBookIn":
|
||||
if self.arrival_date is None and self.booking_date is None:
|
||||
raise ValueError("Both arrival and booking date cannot be null")
|
||||
return self
|
||||
|
||||
|
||||
class GuestBook(GuestBookIn):
|
||||
id_: uuid.UUID
|
||||
last_edit_date: datetime
|
||||
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("last_edit_date", mode="before")
|
||||
@classmethod
|
||||
def parse_last_edit_date(cls, value):
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("last_edit_date")
|
||||
def serialize_last_edit_date(self, value: datetime, _info):
|
||||
return value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
|
||||
class GuestBookListItem(BaseModel):
|
||||
id_: uuid.UUID
|
||||
serial: int
|
||||
count: int
|
||||
name: str
|
||||
phone: str
|
||||
pax: int
|
||||
date_: datetime
|
||||
status: str | None = None
|
||||
booking_date: datetime | None = None
|
||||
arrival_date: datetime | None = None
|
||||
last_edit_date: datetime
|
||||
|
||||
status: GuestBookStatus
|
||||
table_id: uuid.UUID | None = None
|
||||
voucher_id: uuid.UUID | None = None
|
||||
table_name: str | None = None
|
||||
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("date_", mode="before")
|
||||
@field_validator("booking_date", mode="before")
|
||||
@classmethod
|
||||
def parse_date(cls, value):
|
||||
def parse_booking_date(cls, value):
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("date_")
|
||||
def serialize_date(self, value: datetime, _info):
|
||||
@field_serializer("booking_date")
|
||||
def serialize_booking_date(self, value: datetime | None, _info):
|
||||
return None if value is None else value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
@field_validator("arrival_date", mode="before")
|
||||
@classmethod
|
||||
def parse_arrival_date(cls, value):
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("arrival_date")
|
||||
def serialize_arrival_date(self, value: datetime | None, _info):
|
||||
return None if value is None else value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
@field_validator("last_edit_date", mode="before")
|
||||
@classmethod
|
||||
def parse_last_edit_date(cls, value):
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("last_edit_date")
|
||||
def serialize_last_edit_date(self, value: datetime, _info):
|
||||
return value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
|
||||
|
@ -13,6 +13,7 @@ export class GuestBookDetailResolver {
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<GuestBook> {
|
||||
const id = route.paramMap.get('id');
|
||||
return this.ser.get(id);
|
||||
const account = route.queryParamMap.get('t') || 'walk_in';
|
||||
return this.ser.get(id, account);
|
||||
}
|
||||
}
|
||||
|
@ -41,10 +41,22 @@
|
||||
</div>
|
||||
<div class="flex flex-row justify-around content-start items-start">
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Notes</mat-label>
|
||||
<textarea matInput formControlName="notes"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="flex flex-row justify-around content-start items-start">
|
||||
<mat-form-field class="flex-auto basis-1/2 mr-5">
|
||||
<mat-label>Date</mat-label>
|
||||
<input matInput [matDatepicker]="date" (focus)="date.open()" formControlName="date" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="date"></mat-datepicker-toggle>
|
||||
<mat-datepicker #date></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto basis-1/4 mr-5">
|
||||
<mat-label>Hours</mat-label>
|
||||
<input matInput formControlName="hours" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-form-field class="flex-auto basis-1/4">
|
||||
<mat-label>Minutes</mat-label>
|
||||
<input matInput formControlName="minutes" />
|
||||
</mat-form-field>
|
||||
|
@ -3,6 +3,7 @@ import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import * as moment from 'moment';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
|
||||
|
||||
@ -25,6 +26,8 @@ export class GuestBookDetailComponent implements OnInit, AfterViewInit {
|
||||
phone: FormControl<string | Customer>;
|
||||
pax: FormControl<number>;
|
||||
address: FormControl<string | null>;
|
||||
notes: FormControl<string | null>;
|
||||
date: FormControl<Date>;
|
||||
hours: FormControl<number>;
|
||||
minutes: FormControl<number>;
|
||||
}>;
|
||||
@ -49,6 +52,8 @@ export class GuestBookDetailComponent implements OnInit, AfterViewInit {
|
||||
}),
|
||||
pax: new FormControl(0, { validators: Validators.required, nonNullable: true }),
|
||||
address: new FormControl<string | null>(null),
|
||||
notes: new FormControl<string | null>(null),
|
||||
date: new FormControl({ value: new Date(), disabled: false }, { nonNullable: true }),
|
||||
hours: new FormControl(0, { validators: [Validators.min(0), Validators.max(23)], nonNullable: true }),
|
||||
minutes: new FormControl(0, { validators: [Validators.min(0), Validators.max(59)], nonNullable: true }),
|
||||
});
|
||||
@ -79,13 +84,22 @@ export class GuestBookDetailComponent implements OnInit, AfterViewInit {
|
||||
|
||||
showItem(item: GuestBook) {
|
||||
this.item = item;
|
||||
const dt = (this.item.arrivalDate || this.item.bookingDate) as string;
|
||||
if (item.type === 'booking') {
|
||||
this.form.controls.date.enable();
|
||||
} else {
|
||||
this.form.controls.date.disable();
|
||||
}
|
||||
|
||||
this.form.setValue({
|
||||
name: item.name,
|
||||
phone: item.phone,
|
||||
pax: item.pax,
|
||||
address: item.address,
|
||||
hours: +item.date.substring(12, 14),
|
||||
minutes: +item.date.substring(15, 17),
|
||||
notes: item.notes,
|
||||
date: new Date(dt),
|
||||
hours: +dt.substring(12, 14),
|
||||
minutes: +dt.substring(15, 17),
|
||||
});
|
||||
}
|
||||
|
||||
@ -149,9 +163,16 @@ export class GuestBookDetailComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
this.item.pax = formModel.pax ?? 0;
|
||||
this.item.address = formModel.address ?? null;
|
||||
this.item.notes = formModel.notes ?? null;
|
||||
const hours = ('' + (formModel.hours as number)).padStart(2, '0');
|
||||
const minutes = ('' + (formModel.minutes as number)).padStart(2, '0');
|
||||
this.item.date = this.item.date.substring(0, 12) + hours + ':' + minutes;
|
||||
if (this.item.type === 'booking') {
|
||||
console.log(formModel.date);
|
||||
this.item.bookingDate = moment(formModel.date).format('DD-MMM-YYYY') + ' ' + hours + ':' + minutes;
|
||||
} else {
|
||||
this.item.arrivalDate = (this.item.arrivalDate as string).substring(0, 12) + hours + ':' + minutes;
|
||||
}
|
||||
|
||||
return this.item;
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,13 @@
|
||||
<mat-card-header>
|
||||
<mat-card-title-group>
|
||||
<mat-card-title>Guest List</mat-card-title>
|
||||
<a mat-button [routerLink]="['/guest-book', 'new']">
|
||||
<a mat-button [routerLink]="['/guest-book', 'new']" [queryParams]="{ t: 'booking' }">
|
||||
<mat-icon>add_box</mat-icon>
|
||||
Add
|
||||
Table Booking
|
||||
</a>
|
||||
<a mat-button [routerLink]="['/guest-book', 'new']" [queryParams]="{ t: 'walk_in' }">
|
||||
<mat-icon>add_box</mat-icon>
|
||||
Walk-in
|
||||
</a>
|
||||
</mat-card-title-group>
|
||||
</mat-card-header>
|
||||
@ -22,8 +26,8 @@
|
||||
<mat-table [dataSource]="dataSource" aria-label="Elements">
|
||||
<!-- SNo Column -->
|
||||
<ng-container matColumnDef="sno">
|
||||
<mat-header-cell *matHeaderCellDef>S. No</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{ row.serial }}</mat-cell>
|
||||
<mat-header-cell *matHeaderCellDef>S. No / Total</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{ row.serial }} / {{ row.count }}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Name Column -->
|
||||
@ -49,7 +53,9 @@
|
||||
<!-- Time Column -->
|
||||
<ng-container matColumnDef="date">
|
||||
<mat-header-cell *matHeaderCellDef>Time</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{ row.date | localTime }}</mat-cell>
|
||||
<mat-cell *matCellDef="let row"
|
||||
>{{ row.bookingDate | localTime }} // {{ row.arrivalDate | localTime }}</mat-cell
|
||||
>
|
||||
</ng-container>
|
||||
|
||||
<!-- Action Column -->
|
||||
@ -88,6 +94,7 @@
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row
|
||||
*matRowDef="let row; columns: displayedColumns"
|
||||
[class.grey300]="row.status === 'booking'"
|
||||
[class.accent]="row.status === 'running'"
|
||||
[class.strong-accent]="row.status === 'printed'"
|
||||
></mat-row>
|
||||
|
@ -19,3 +19,7 @@ table
|
||||
/* Read the 700 hue from the primary color palete.*/
|
||||
color: mat.get-color-from-palette($my-accent, '700-contrast')
|
||||
background: mat.get-color-from-palette($my-accent, 700)
|
||||
|
||||
.grey300
|
||||
background-color: #ede7f6
|
||||
color: #000000
|
||||
|
@ -22,10 +22,11 @@ export class GuestBookService {
|
||||
private log: ErrorLoggerService,
|
||||
) {}
|
||||
|
||||
get(id: string | null): Observable<GuestBook> {
|
||||
get(id: string | null, type: string): Observable<GuestBook> {
|
||||
const options = { params: new HttpParams().set('t', type) };
|
||||
const getUrl: string = id === null ? url : `${url}/${id}`;
|
||||
return this.http
|
||||
.get<GuestBook>(getUrl)
|
||||
.get<GuestBook>(getUrl, options)
|
||||
.pipe(catchError(this.log.handleError(serviceName, `get id=${id}`))) as Observable<GuestBook>;
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
export class GuestBook {
|
||||
id: string | undefined;
|
||||
serial: number;
|
||||
count: number;
|
||||
name: string;
|
||||
phone: string;
|
||||
pax: number;
|
||||
address: string | null;
|
||||
date: string;
|
||||
notes: string | null;
|
||||
bookingDate: string | null;
|
||||
arrivalDate: string | null;
|
||||
type: string | undefined;
|
||||
lastEditDate: string | undefined;
|
||||
status?: string;
|
||||
tableId?: string;
|
||||
voucherId?: string;
|
||||
@ -14,11 +19,15 @@ export class GuestBook {
|
||||
public constructor(init?: Partial<GuestBook>) {
|
||||
this.id = undefined;
|
||||
this.serial = 0;
|
||||
this.count = 0;
|
||||
this.name = '';
|
||||
this.phone = '';
|
||||
this.pax = 0;
|
||||
this.address = null;
|
||||
this.date = '';
|
||||
this.notes = null;
|
||||
this.bookingDate = null;
|
||||
this.arrivalDate = null;
|
||||
this.lastEditDate = '';
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import * as moment from 'moment';
|
||||
})
|
||||
export class LocalTimePipe implements PipeTransform {
|
||||
transform(value: string): string {
|
||||
if (value === undefined) {
|
||||
if (value === undefined || value === null) {
|
||||
return '';
|
||||
}
|
||||
if (value.length === 5) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user