feat(local_backend): reservations, print analytics, workday summary, zone-PIN management
- New reservations module: model, schema, router (CRUD + status updates + upcoming alerts) and background task for auto-expiring stale reservations - Reports: print_products, print_categories, print_tables analytics endpoints plus meta_products and business_day_summary for workday close/view flow - printer_service: configurable font sizes/weights, donut/bar chart print layout helpers, analytics print blocks per printer - tables/schemas: surfaced color, zone, and other new fields on Table, Product, User, Printer - demo_seed.py for quick dev DB population; wipe_database.py utility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ class Printer(Base):
|
||||
port = Column(Integer, default=9100, nullable=False)
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
protocol = Column(String, default="escpos_tcp", nullable=False)
|
||||
line_width = Column(Integer, default=48, nullable=False)
|
||||
|
||||
products = relationship("Product", back_populates="printer_zone")
|
||||
print_logs = relationship("PrintLog", back_populates="printer")
|
||||
|
||||
@@ -27,6 +27,7 @@ class Product(Base):
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(String, nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
category_id = Column(Integer, ForeignKey("categories.id"), nullable=True)
|
||||
base_price = Column(Float, nullable=False)
|
||||
is_available = Column(Boolean, default=True, nullable=False)
|
||||
|
||||
42
local_backend/models/reservation.py
Normal file
42
local_backend/models/reservation.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Text
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime, timezone
|
||||
from database import Base
|
||||
|
||||
|
||||
class Reservation(Base):
|
||||
__tablename__ = "reservations"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# Guest info
|
||||
guest_name = Column(String, nullable=False)
|
||||
party_size = Column(Integer, nullable=False)
|
||||
phone = Column(String, nullable=True)
|
||||
email = Column(String, nullable=True)
|
||||
note = Column(Text, nullable=True)
|
||||
|
||||
# When the customer wants the table
|
||||
reserved_for = Column(DateTime, nullable=False)
|
||||
|
||||
# Optional table assignment (assignable/editable later)
|
||||
table_id = Column(Integer, ForeignKey("tables.id"), nullable=True)
|
||||
|
||||
# Lifecycle: pending | arrived | cancelled | no_show
|
||||
status = Column(String, nullable=False, default="pending")
|
||||
|
||||
# Source: manager_app | waiter_app | online
|
||||
source = Column(String, nullable=False, default="manager_app")
|
||||
|
||||
# Who entered this reservation (staff user)
|
||||
created_by_user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
|
||||
# For future online reservations: cloud-side customer identifier
|
||||
online_customer_id = Column(String, nullable=True)
|
||||
|
||||
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
|
||||
updated_at = Column(DateTime, default=lambda: datetime.now(timezone.utc),
|
||||
onupdate=lambda: datetime.now(timezone.utc))
|
||||
|
||||
table = relationship("Table", foreign_keys=[table_id])
|
||||
created_by = relationship("User", foreign_keys=[created_by_user_id])
|
||||
@@ -21,6 +21,7 @@ class User(Base):
|
||||
full_name = Column(String, nullable=True)
|
||||
nickname = Column(String, nullable=True)
|
||||
mobile_phone = Column(String, nullable=True)
|
||||
note = Column(String, nullable=True)
|
||||
avatar_url = Column(String, nullable=True)
|
||||
created_at = Column(DateTime(timezone=True), default=_utcnow)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user