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:
@@ -1,19 +1,24 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Body
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
from database import get_db
|
||||
from models.table import Table, TableGroup
|
||||
from models.order import Order
|
||||
from models.reservation import Reservation
|
||||
from models.user import User, WaiterZone
|
||||
from schemas.table import (
|
||||
TableCreate, TableUpdate, TableFloorplanUpdate, TableOut,
|
||||
TableCreate, TableUpdate, TableFloorplanUpdate, TableOut, UpcomingReservation,
|
||||
TableGroupCreate, TableGroupUpdate, TableGroupOut,
|
||||
TableBatchCreate, MAX_TABLE_NAME_LENGTH,
|
||||
)
|
||||
from routers.deps import get_current_user, require_manager
|
||||
from services.sse_bus import broadcast_sync
|
||||
|
||||
# Tables with a pending reservation due within this many hours get the RESERVED badge
|
||||
RESERVATION_LOOKAHEAD_HOURS = 4
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -98,10 +103,32 @@ def list_tables(include_inactive: bool = False, db: Session = Depends(get_db), u
|
||||
).all()
|
||||
}
|
||||
|
||||
# Fetch pending reservations due within the lookahead window for the RESERVED badge
|
||||
now = datetime.now(timezone.utc)
|
||||
lookahead = now + timedelta(hours=RESERVATION_LOOKAHEAD_HOURS)
|
||||
upcoming_reservations = (
|
||||
db.query(Reservation)
|
||||
.filter(
|
||||
Reservation.status == "pending",
|
||||
Reservation.table_id.isnot(None),
|
||||
Reservation.reserved_for >= now,
|
||||
Reservation.reserved_for <= lookahead,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
# Map table_id → soonest upcoming reservation
|
||||
reservation_by_table: dict[int, Reservation] = {}
|
||||
for res in sorted(upcoming_reservations, key=lambda r: r.reserved_for):
|
||||
if res.table_id not in reservation_by_table:
|
||||
reservation_by_table[res.table_id] = res
|
||||
|
||||
result = []
|
||||
for t in tables:
|
||||
out = TableOut.model_validate(t)
|
||||
out.has_active_order = t.id in active_table_ids
|
||||
res = reservation_by_table.get(t.id)
|
||||
if res:
|
||||
out.upcoming_reservation = UpcomingReservation.model_validate(res)
|
||||
result.append(out)
|
||||
return result
|
||||
|
||||
|
||||
Reference in New Issue
Block a user