feat(crud): async CRUD layer for all entities
- Restaurants: create / update / get / get_by_slug / get_by_wallets / get_all / delete (with ordered cascade through dependent rows) - Categories + subcategories with cascade - Menu items with adjust_stock helper for atomic decrement - Modifier groups + modifiers with cascade - Availability windows - Orders + order items (id := payment_hash so the invoice listener can look up by payment_hash with zero metadata round-trip) - Print jobs queue - Settings (single-row config table) JSON columns are passed through pydantic pre-validators on read so nested models (OpenHours, lists, etc.) round-trip cleanly across SQLite + Postgres.
This commit is contained in:
parent
52f1ad1bb1
commit
5f4b416f5f
1 changed files with 621 additions and 0 deletions
621
crud.py
Normal file
621
crud.py
Normal file
|
|
@ -0,0 +1,621 @@
|
||||||
|
"""
|
||||||
|
Async CRUD layer for the restaurant extension.
|
||||||
|
|
||||||
|
All functions are coroutines that hit the Database singleton initialized
|
||||||
|
at module import time. Pydantic models are passed to db.insert/update so
|
||||||
|
nested objects (OpenHours, SocialLinks, lists, etc.) are JSON-serialized
|
||||||
|
consistently across SQLite + Postgres backends.
|
||||||
|
|
||||||
|
A note on JSON columns: db.insert() / db.update() handle serialization,
|
||||||
|
but db.fetchone(model=Model) / db.fetchall(model=Model) reverse it via
|
||||||
|
the model's pre-validators (defined in models.py).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from lnbits.db import Database
|
||||||
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
||||||
|
from .models import (
|
||||||
|
AvailabilityWindow,
|
||||||
|
Category,
|
||||||
|
CreateAvailabilityWindow,
|
||||||
|
CreateCategory,
|
||||||
|
CreateMenuItem,
|
||||||
|
CreateModifier,
|
||||||
|
CreateModifierGroup,
|
||||||
|
CreateRestaurant,
|
||||||
|
CreateSubcategory,
|
||||||
|
MenuItem,
|
||||||
|
Modifier,
|
||||||
|
ModifierGroup,
|
||||||
|
Order,
|
||||||
|
OrderItemRow,
|
||||||
|
PrintJob,
|
||||||
|
Restaurant,
|
||||||
|
RestaurantSettings,
|
||||||
|
SelectedModifier,
|
||||||
|
Subcategory,
|
||||||
|
)
|
||||||
|
|
||||||
|
db = Database("ext_restaurant")
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Restaurants #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
async def create_restaurant(wallet: str, data: CreateRestaurant) -> Restaurant:
|
||||||
|
restaurant = Restaurant(
|
||||||
|
id=urlsafe_short_hash(),
|
||||||
|
wallet=wallet,
|
||||||
|
time=datetime.now(timezone.utc),
|
||||||
|
**{k: v for k, v in data.dict().items() if k != "wallet"},
|
||||||
|
)
|
||||||
|
await db.insert("restaurant.restaurants", restaurant)
|
||||||
|
return restaurant
|
||||||
|
|
||||||
|
|
||||||
|
async def update_restaurant(restaurant: Restaurant) -> Restaurant:
|
||||||
|
await db.update("restaurant.restaurants", restaurant)
|
||||||
|
return restaurant
|
||||||
|
|
||||||
|
|
||||||
|
async def get_restaurant(restaurant_id: str) -> Optional[Restaurant]:
|
||||||
|
return await db.fetchone(
|
||||||
|
"SELECT * FROM restaurant.restaurants WHERE id = :id",
|
||||||
|
{"id": restaurant_id},
|
||||||
|
Restaurant,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_restaurant_by_slug(slug: str) -> Optional[Restaurant]:
|
||||||
|
return await db.fetchone(
|
||||||
|
"SELECT * FROM restaurant.restaurants WHERE slug = :slug",
|
||||||
|
{"slug": slug},
|
||||||
|
Restaurant,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_restaurants(wallet_ids: str | list[str]) -> list[Restaurant]:
|
||||||
|
if isinstance(wallet_ids, str):
|
||||||
|
wallet_ids = [wallet_ids]
|
||||||
|
q = ",".join([f"'{w}'" for w in wallet_ids])
|
||||||
|
return await db.fetchall(
|
||||||
|
f"SELECT * FROM restaurant.restaurants WHERE wallet IN ({q}) ORDER BY time DESC",
|
||||||
|
model=Restaurant,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_all_restaurants() -> list[Restaurant]:
|
||||||
|
return await db.fetchall(
|
||||||
|
"SELECT * FROM restaurant.restaurants ORDER BY time DESC",
|
||||||
|
model=Restaurant,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_restaurant(restaurant_id: str) -> None:
|
||||||
|
# Cascade by app logic — relational FKs aren't enforced cross-backend,
|
||||||
|
# so we manually clean dependent rows in the right order.
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
DELETE FROM restaurant.print_jobs
|
||||||
|
WHERE order_id IN (
|
||||||
|
SELECT id FROM restaurant.orders WHERE restaurant_id = :rid
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
DELETE FROM restaurant.order_items
|
||||||
|
WHERE order_id IN (
|
||||||
|
SELECT id FROM restaurant.orders WHERE restaurant_id = :rid
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.orders WHERE restaurant_id = :rid",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
DELETE FROM restaurant.modifiers
|
||||||
|
WHERE group_id IN (
|
||||||
|
SELECT mg.id FROM restaurant.modifier_groups mg
|
||||||
|
JOIN restaurant.menu_items mi ON mg.menu_item_id = mi.id
|
||||||
|
WHERE mi.restaurant_id = :rid
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
DELETE FROM restaurant.modifier_groups
|
||||||
|
WHERE menu_item_id IN (
|
||||||
|
SELECT id FROM restaurant.menu_items WHERE restaurant_id = :rid
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
DELETE FROM restaurant.availability_windows
|
||||||
|
WHERE menu_item_id IN (
|
||||||
|
SELECT id FROM restaurant.menu_items WHERE restaurant_id = :rid
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.menu_items WHERE restaurant_id = :rid",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
DELETE FROM restaurant.subcategories
|
||||||
|
WHERE category_id IN (
|
||||||
|
SELECT id FROM restaurant.categories WHERE restaurant_id = :rid
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.categories WHERE restaurant_id = :rid",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.restaurants WHERE id = :id",
|
||||||
|
{"id": restaurant_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Categories / subcategories #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
async def create_category(data: CreateCategory) -> Category:
|
||||||
|
cat = Category(
|
||||||
|
id=urlsafe_short_hash(),
|
||||||
|
time=datetime.now(timezone.utc),
|
||||||
|
**data.dict(),
|
||||||
|
)
|
||||||
|
await db.insert("restaurant.categories", cat)
|
||||||
|
return cat
|
||||||
|
|
||||||
|
|
||||||
|
async def update_category(category: Category) -> Category:
|
||||||
|
await db.update("restaurant.categories", category)
|
||||||
|
return category
|
||||||
|
|
||||||
|
|
||||||
|
async def get_category(category_id: str) -> Optional[Category]:
|
||||||
|
return await db.fetchone(
|
||||||
|
"SELECT * FROM restaurant.categories WHERE id = :id",
|
||||||
|
{"id": category_id},
|
||||||
|
Category,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_categories(restaurant_id: str) -> list[Category]:
|
||||||
|
return await db.fetchall(
|
||||||
|
"""
|
||||||
|
SELECT * FROM restaurant.categories
|
||||||
|
WHERE restaurant_id = :rid
|
||||||
|
ORDER BY sort_order, time
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
model=Category,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_category(category_id: str) -> None:
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.subcategories WHERE category_id = :cid",
|
||||||
|
{"cid": category_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.categories WHERE id = :id",
|
||||||
|
{"id": category_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def create_subcategory(data: CreateSubcategory) -> Subcategory:
|
||||||
|
sub = Subcategory(
|
||||||
|
id=urlsafe_short_hash(),
|
||||||
|
time=datetime.now(timezone.utc),
|
||||||
|
**data.dict(),
|
||||||
|
)
|
||||||
|
await db.insert("restaurant.subcategories", sub)
|
||||||
|
return sub
|
||||||
|
|
||||||
|
|
||||||
|
async def update_subcategory(subcategory: Subcategory) -> Subcategory:
|
||||||
|
await db.update("restaurant.subcategories", subcategory)
|
||||||
|
return subcategory
|
||||||
|
|
||||||
|
|
||||||
|
async def get_subcategories(category_id: str) -> list[Subcategory]:
|
||||||
|
return await db.fetchall(
|
||||||
|
"""
|
||||||
|
SELECT * FROM restaurant.subcategories
|
||||||
|
WHERE category_id = :cid
|
||||||
|
ORDER BY sort_order, time
|
||||||
|
""",
|
||||||
|
{"cid": category_id},
|
||||||
|
model=Subcategory,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_subcategory(subcategory_id: str) -> None:
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.subcategories WHERE id = :id",
|
||||||
|
{"id": subcategory_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Menu items #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
async def create_menu_item(data: CreateMenuItem) -> MenuItem:
|
||||||
|
item = MenuItem(
|
||||||
|
id=urlsafe_short_hash(),
|
||||||
|
time=datetime.now(timezone.utc),
|
||||||
|
**data.dict(),
|
||||||
|
)
|
||||||
|
await db.insert("restaurant.menu_items", item)
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
async def update_menu_item(item: MenuItem) -> MenuItem:
|
||||||
|
await db.update("restaurant.menu_items", item)
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
async def get_menu_item(item_id: str) -> Optional[MenuItem]:
|
||||||
|
return await db.fetchone(
|
||||||
|
"SELECT * FROM restaurant.menu_items WHERE id = :id",
|
||||||
|
{"id": item_id},
|
||||||
|
MenuItem,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_menu_items(restaurant_id: str) -> list[MenuItem]:
|
||||||
|
return await db.fetchall(
|
||||||
|
"""
|
||||||
|
SELECT * FROM restaurant.menu_items
|
||||||
|
WHERE restaurant_id = :rid
|
||||||
|
ORDER BY sort_order, time
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
model=MenuItem,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_menu_item_by_nostr_event(event_id: str) -> Optional[MenuItem]:
|
||||||
|
return await db.fetchone(
|
||||||
|
"SELECT * FROM restaurant.menu_items WHERE nostr_event_id = :nid",
|
||||||
|
{"nid": event_id},
|
||||||
|
MenuItem,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_menu_item(item_id: str) -> None:
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
DELETE FROM restaurant.modifiers
|
||||||
|
WHERE group_id IN (
|
||||||
|
SELECT id FROM restaurant.modifier_groups WHERE menu_item_id = :mid
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
{"mid": item_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.modifier_groups WHERE menu_item_id = :mid",
|
||||||
|
{"mid": item_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.availability_windows WHERE menu_item_id = :mid",
|
||||||
|
{"mid": item_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.menu_items WHERE id = :id",
|
||||||
|
{"id": item_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def adjust_stock(item_id: str, delta: int) -> Optional[MenuItem]:
|
||||||
|
"""Decrement (negative delta) or increment stock atomically."""
|
||||||
|
item = await get_menu_item(item_id)
|
||||||
|
if not item or item.stock is None:
|
||||||
|
return item
|
||||||
|
item.stock = max(0, item.stock + delta)
|
||||||
|
return await update_menu_item(item)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Modifier groups + modifiers #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
async def create_modifier_group(data: CreateModifierGroup) -> ModifierGroup:
|
||||||
|
grp = ModifierGroup(
|
||||||
|
id=urlsafe_short_hash(),
|
||||||
|
time=datetime.now(timezone.utc),
|
||||||
|
**data.dict(),
|
||||||
|
)
|
||||||
|
await db.insert("restaurant.modifier_groups", grp)
|
||||||
|
return grp
|
||||||
|
|
||||||
|
|
||||||
|
async def update_modifier_group(grp: ModifierGroup) -> ModifierGroup:
|
||||||
|
await db.update("restaurant.modifier_groups", grp)
|
||||||
|
return grp
|
||||||
|
|
||||||
|
|
||||||
|
async def get_modifier_groups(menu_item_id: str) -> list[ModifierGroup]:
|
||||||
|
return await db.fetchall(
|
||||||
|
"""
|
||||||
|
SELECT * FROM restaurant.modifier_groups
|
||||||
|
WHERE menu_item_id = :mid
|
||||||
|
ORDER BY sort_order, time
|
||||||
|
""",
|
||||||
|
{"mid": menu_item_id},
|
||||||
|
model=ModifierGroup,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_modifier_group(group_id: str) -> None:
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.modifiers WHERE group_id = :gid",
|
||||||
|
{"gid": group_id},
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.modifier_groups WHERE id = :id",
|
||||||
|
{"id": group_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def create_modifier(data: CreateModifier) -> Modifier:
|
||||||
|
mod = Modifier(
|
||||||
|
id=urlsafe_short_hash(),
|
||||||
|
time=datetime.now(timezone.utc),
|
||||||
|
**data.dict(),
|
||||||
|
)
|
||||||
|
await db.insert("restaurant.modifiers", mod)
|
||||||
|
return mod
|
||||||
|
|
||||||
|
|
||||||
|
async def update_modifier(mod: Modifier) -> Modifier:
|
||||||
|
await db.update("restaurant.modifiers", mod)
|
||||||
|
return mod
|
||||||
|
|
||||||
|
|
||||||
|
async def get_modifiers(group_id: str) -> list[Modifier]:
|
||||||
|
return await db.fetchall(
|
||||||
|
"""
|
||||||
|
SELECT * FROM restaurant.modifiers
|
||||||
|
WHERE group_id = :gid
|
||||||
|
ORDER BY sort_order, time
|
||||||
|
""",
|
||||||
|
{"gid": group_id},
|
||||||
|
model=Modifier,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_modifier(modifier_id: str) -> None:
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.modifiers WHERE id = :id",
|
||||||
|
{"id": modifier_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Availability windows #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
async def create_availability_window(
|
||||||
|
data: CreateAvailabilityWindow,
|
||||||
|
) -> AvailabilityWindow:
|
||||||
|
win = AvailabilityWindow(
|
||||||
|
id=urlsafe_short_hash(),
|
||||||
|
time=datetime.now(timezone.utc),
|
||||||
|
**data.dict(),
|
||||||
|
)
|
||||||
|
await db.insert("restaurant.availability_windows", win)
|
||||||
|
return win
|
||||||
|
|
||||||
|
|
||||||
|
async def get_availability_windows(menu_item_id: str) -> list[AvailabilityWindow]:
|
||||||
|
return await db.fetchall(
|
||||||
|
"""
|
||||||
|
SELECT * FROM restaurant.availability_windows
|
||||||
|
WHERE menu_item_id = :mid
|
||||||
|
ORDER BY weekday NULLS FIRST, start_time
|
||||||
|
""",
|
||||||
|
{"mid": menu_item_id},
|
||||||
|
model=AvailabilityWindow,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_availability_window(window_id: str) -> None:
|
||||||
|
await db.execute(
|
||||||
|
"DELETE FROM restaurant.availability_windows WHERE id = :id",
|
||||||
|
{"id": window_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Orders + order items #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
async def create_order(order: Order) -> Order:
|
||||||
|
"""Insert an Order row. Caller must construct the Order with id set
|
||||||
|
(typically id = payment_hash so we can look it up from the invoice
|
||||||
|
listener with no extra metadata)."""
|
||||||
|
await db.insert("restaurant.orders", order)
|
||||||
|
return order
|
||||||
|
|
||||||
|
|
||||||
|
async def update_order(order: Order) -> Order:
|
||||||
|
await db.update("restaurant.orders", order)
|
||||||
|
return order
|
||||||
|
|
||||||
|
|
||||||
|
async def get_order(order_id: str) -> Optional[Order]:
|
||||||
|
return await db.fetchone(
|
||||||
|
"SELECT * FROM restaurant.orders WHERE id = :id",
|
||||||
|
{"id": order_id},
|
||||||
|
Order,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_order_by_payment_hash(payment_hash: str) -> Optional[Order]:
|
||||||
|
return await db.fetchone(
|
||||||
|
"SELECT * FROM restaurant.orders WHERE payment_hash = :ph",
|
||||||
|
{"ph": payment_hash},
|
||||||
|
Order,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_orders(
|
||||||
|
restaurant_id: str,
|
||||||
|
statuses: Optional[list[str]] = None,
|
||||||
|
limit: int = 200,
|
||||||
|
) -> list[Order]:
|
||||||
|
if statuses:
|
||||||
|
placeholders = ",".join([f"'{s}'" for s in statuses])
|
||||||
|
return await db.fetchall(
|
||||||
|
f"""
|
||||||
|
SELECT * FROM restaurant.orders
|
||||||
|
WHERE restaurant_id = :rid AND status IN ({placeholders})
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT {int(limit)}
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
model=Order,
|
||||||
|
)
|
||||||
|
return await db.fetchall(
|
||||||
|
f"""
|
||||||
|
SELECT * FROM restaurant.orders
|
||||||
|
WHERE restaurant_id = :rid
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT {int(limit)}
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
model=Order,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def create_order_item(item: OrderItemRow) -> OrderItemRow:
|
||||||
|
await db.insert("restaurant.order_items", item)
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
async def get_order_items(order_id: str) -> list[OrderItemRow]:
|
||||||
|
rows = await db.fetchall(
|
||||||
|
"SELECT * FROM restaurant.order_items WHERE order_id = :oid ORDER BY time",
|
||||||
|
{"oid": order_id},
|
||||||
|
)
|
||||||
|
out: list[OrderItemRow] = []
|
||||||
|
for row in rows:
|
||||||
|
d = dict(row)
|
||||||
|
# selected_modifiers comes back as JSON string from db; parse here.
|
||||||
|
sm = d.get("selected_modifiers")
|
||||||
|
if isinstance(sm, str):
|
||||||
|
try:
|
||||||
|
d["selected_modifiers"] = [
|
||||||
|
SelectedModifier(**m) for m in (json.loads(sm) if sm else [])
|
||||||
|
]
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
d["selected_modifiers"] = []
|
||||||
|
out.append(OrderItemRow(**d))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Print jobs #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
async def create_print_job(restaurant_id: str, order_id: str) -> PrintJob:
|
||||||
|
job = PrintJob(
|
||||||
|
id=urlsafe_short_hash(),
|
||||||
|
restaurant_id=restaurant_id,
|
||||||
|
order_id=order_id,
|
||||||
|
time=datetime.now(timezone.utc),
|
||||||
|
)
|
||||||
|
await db.insert("restaurant.print_jobs", job)
|
||||||
|
return job
|
||||||
|
|
||||||
|
|
||||||
|
async def update_print_job(job: PrintJob) -> PrintJob:
|
||||||
|
await db.update("restaurant.print_jobs", job)
|
||||||
|
return job
|
||||||
|
|
||||||
|
|
||||||
|
async def get_print_jobs(
|
||||||
|
restaurant_id: str, status: Optional[str] = None
|
||||||
|
) -> list[PrintJob]:
|
||||||
|
if status:
|
||||||
|
return await db.fetchall(
|
||||||
|
"""
|
||||||
|
SELECT * FROM restaurant.print_jobs
|
||||||
|
WHERE restaurant_id = :rid AND status = :status
|
||||||
|
ORDER BY time DESC
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id, "status": status},
|
||||||
|
model=PrintJob,
|
||||||
|
)
|
||||||
|
return await db.fetchall(
|
||||||
|
"""
|
||||||
|
SELECT * FROM restaurant.print_jobs
|
||||||
|
WHERE restaurant_id = :rid
|
||||||
|
ORDER BY time DESC
|
||||||
|
""",
|
||||||
|
{"rid": restaurant_id},
|
||||||
|
model=PrintJob,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Settings #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
async def get_settings() -> RestaurantSettings:
|
||||||
|
row = await db.fetchone("SELECT * FROM restaurant.settings WHERE id = 1")
|
||||||
|
if row:
|
||||||
|
d = dict(row)
|
||||||
|
d.pop("id", None)
|
||||||
|
return RestaurantSettings(**d)
|
||||||
|
return RestaurantSettings()
|
||||||
|
|
||||||
|
|
||||||
|
async def update_settings(settings: RestaurantSettings) -> RestaurantSettings:
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
UPDATE restaurant.settings
|
||||||
|
SET nostr_publish_enabled = :npe,
|
||||||
|
nostr_orders_enabled = :noe,
|
||||||
|
invoice_expiry_seconds = :ies,
|
||||||
|
auto_accept_orders = :aao
|
||||||
|
WHERE id = 1
|
||||||
|
""",
|
||||||
|
{
|
||||||
|
"npe": settings.nostr_publish_enabled,
|
||||||
|
"noe": settings.nostr_orders_enabled,
|
||||||
|
"ies": settings.invoice_expiry_seconds,
|
||||||
|
"aao": settings.auto_accept_orders,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return settings
|
||||||
Loading…
Add table
Add a link
Reference in a new issue