Files
xenia-pos-local/local_backend/schemas/reservation.py
bonamin 79bd3b0f41 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>
2026-06-07 20:25:22 +03:00

94 lines
2.5 KiB
Python

from pydantic import BaseModel, field_validator
from typing import Optional
from datetime import datetime, timezone
class ReservationCreate(BaseModel):
guest_name: str
party_size: int
phone: Optional[str] = None
email: Optional[str] = None
note: Optional[str] = None
reserved_for: datetime
table_id: Optional[int] = None
source: str = "manager_app"
online_customer_id: Optional[str] = None
@field_validator("reserved_for", mode="before")
@classmethod
def strip_timezone(cls, v):
if isinstance(v, str):
dt = datetime.fromisoformat(v.replace("Z", "+00:00"))
elif isinstance(v, datetime):
dt = v
else:
return v
# Store as naive local — strip any tz info so SQLite keeps it as-is
if dt.tzinfo is not None:
dt = dt.astimezone().replace(tzinfo=None)
return dt
class ReservationUpdate(BaseModel):
guest_name: Optional[str] = None
party_size: Optional[int] = None
phone: Optional[str] = None
email: Optional[str] = None
note: Optional[str] = None
reserved_for: Optional[datetime] = None
table_id: Optional[int] = None
status: Optional[str] = None
@field_validator("reserved_for", mode="before")
@classmethod
def strip_timezone(cls, v):
if v is None:
return v
if isinstance(v, str):
dt = datetime.fromisoformat(v.replace("Z", "+00:00"))
elif isinstance(v, datetime):
dt = v
else:
return v
if dt.tzinfo is not None:
dt = dt.astimezone().replace(tzinfo=None)
return dt
class ReservationStatusUpdate(BaseModel):
status: str # arrived | cancelled | no_show | pending
class ReservationOut(BaseModel):
id: int
guest_name: str
party_size: int
phone: Optional[str] = None
email: Optional[str] = None
note: Optional[str] = None
reserved_for: datetime
table_id: Optional[int] = None
status: str
source: str
created_by_user_id: Optional[int] = None
online_customer_id: Optional[str] = None
created_at: datetime
updated_at: datetime
# Denormalized for convenience
table_label: Optional[str] = None
created_by_name: Optional[str] = None
model_config = {"from_attributes": True}
# Compact summary embedded in TableOut for the RESERVED badge
class ReservationSummary(BaseModel):
id: int
guest_name: str
party_size: int
reserved_for: datetime
status: str
model_config = {"from_attributes": True}