Fix order saving, isMyOrder, blocked waiters, options pricing; add preferences, table groups, product images
Backend:
- OrderItemInput accepts option objects {id,name,price_delta} instead of int IDs
- extra_cost from selected options added to unit_price snapshot
- GET /api/products/?all=true for manager (includes unavailable)
- PUT /api/products/{id} now replaces options, ingredients, preference_sets
- POST /api/products/{id}/image — persistent image upload to /app/data/product_images
- New models: ProductPreferenceSet, ProductPreferenceChoice, TableGroup
- tables: group_id FK, hard delete (?hard=true), batch create POST /api/tables/batch
- GET /api/tables/groups + POST/PUT/DELETE groups endpoints
- POST /api/auth/me endpoint for token rehydration
- Auto-migration on startup for new columns
PWA:
- AuthRehydrator: fetches /auth/me on load so isMyOrder works after page reload
- 401 response force-logs out (covers blocked waiters)
- ItemOptionsModal: uses extra_cost correctly, shows preferences as radio buttons
Manager:
- ProductsPage: shows unavailable products greyed out, category color picker + reorder,
full option/ingredient/preference editing, image upload
- TablesPage: table groups, auto-increment, deactivate vs hard delete, batch add
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from database import engine, Base
|
||||
from middleware.license_check import LicenseCheckMiddleware
|
||||
@@ -16,9 +18,34 @@ import models.order # noqa: F401
|
||||
from routers import auth, tables, products, orders, waiters, reports, system
|
||||
|
||||
|
||||
def _run_migrations():
|
||||
"""Apply additive schema changes that create_all won't handle."""
|
||||
from sqlalchemy import text
|
||||
with engine.connect() as conn:
|
||||
# Add extra_cost to product_ingredients if missing
|
||||
try:
|
||||
conn.execute(text("ALTER TABLE product_ingredients ADD COLUMN extra_cost REAL NOT NULL DEFAULT 0.0"))
|
||||
conn.commit()
|
||||
except Exception:
|
||||
pass
|
||||
# Add image_url to products if missing
|
||||
try:
|
||||
conn.execute(text("ALTER TABLE products ADD COLUMN image_url VARCHAR"))
|
||||
conn.commit()
|
||||
except Exception:
|
||||
pass
|
||||
# Add group_id to tables if missing (added in Phase 3 table groups)
|
||||
try:
|
||||
conn.execute(text("ALTER TABLE tables ADD COLUMN group_id INTEGER REFERENCES table_groups(id)"))
|
||||
conn.commit()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
Base.metadata.create_all(bind=engine)
|
||||
_run_migrations()
|
||||
sync_task = await start_cloud_sync()
|
||||
yield
|
||||
sync_task.cancel()
|
||||
@@ -34,6 +61,11 @@ app.add_middleware(
|
||||
)
|
||||
app.add_middleware(LicenseCheckMiddleware)
|
||||
|
||||
# Serve product images as static files
|
||||
IMAGE_DIR = "/app/data/product_images"
|
||||
os.makedirs(IMAGE_DIR, exist_ok=True)
|
||||
app.mount("/static/product_images", StaticFiles(directory=IMAGE_DIR), name="product_images")
|
||||
|
||||
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
|
||||
app.include_router(tables.router, prefix="/api/tables", tags=["tables"])
|
||||
app.include_router(products.router, prefix="/api/products", tags=["products"])
|
||||
|
||||
Reference in New Issue
Block a user