feat: major dashboard & waiter PWA overhaul
- Manager dashboard: replaced monolithic DashboardTab/OperationsPage with new DashboardPage; added OrderDetailModal, ShiftDetailModal, DeleteConfirmModal, PaymentMethodModal; updated Sidebar routing and App navigation - Reports: reworked WorkDaySummary, OrderHistory, ShiftsOverview with detail modals - Backend routers: extended orders, reports, shifts, products, business_day endpoints; updated cloud_sync service - Waiter PWA: refreshed app icons, improved ConnectionLostModal UX, updated TableCard, SSEContext, connectionStore; added useProductCache hook; vite config tweaks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -120,14 +120,83 @@ def list_active_orders(db: Session = Depends(get_db), user: User = Depends(get_c
|
||||
]
|
||||
|
||||
|
||||
@router.get("/{order_id}", response_model=OrderOut)
|
||||
@router.get("/{order_id}")
|
||||
def get_order(order_id: int, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
|
||||
order = db.query(Order).filter(Order.id == order_id).first()
|
||||
if not order:
|
||||
raise HTTPException(status_code=404, detail="Order not found")
|
||||
if not _can_access_order(order, user, db):
|
||||
raise HTTPException(status_code=403, detail="Access denied")
|
||||
return order
|
||||
|
||||
# Resolve all user IDs referenced by this order in one query
|
||||
user_ids = set()
|
||||
user_ids.add(order.opened_by)
|
||||
if order.closed_by:
|
||||
user_ids.add(order.closed_by)
|
||||
for item in order.items:
|
||||
user_ids.add(item.added_by)
|
||||
if item.paid_by:
|
||||
user_ids.add(item.paid_by)
|
||||
for log in order.audit_logs:
|
||||
if log.waiter_id:
|
||||
user_ids.add(log.waiter_id)
|
||||
|
||||
users = db.query(User).filter(User.id.in_(user_ids)).all()
|
||||
name_map = {u.id: u.nickname or u.full_name or u.username for u in users}
|
||||
|
||||
def fmt_item(i):
|
||||
return {
|
||||
"id": i.id,
|
||||
"order_id": i.order_id,
|
||||
"product_id": i.product_id,
|
||||
"product": {"id": i.product.id, "name": i.product.name} if i.product else None,
|
||||
"added_by": i.added_by,
|
||||
"added_by_name": name_map.get(i.added_by),
|
||||
"quantity": i.quantity,
|
||||
"unit_price": i.unit_price,
|
||||
"selected_options": i.selected_options,
|
||||
"removed_ingredients": i.removed_ingredients,
|
||||
"notes": i.notes,
|
||||
"status": i.status,
|
||||
"added_at": i.added_at,
|
||||
"printed": i.printed,
|
||||
"paid_by": i.paid_by,
|
||||
"paid_by_name": name_map.get(i.paid_by) if i.paid_by else None,
|
||||
"paid_at": i.paid_at,
|
||||
"payment_method": i.payment_method,
|
||||
"paid_in_shift_id": i.paid_in_shift_id,
|
||||
}
|
||||
|
||||
def fmt_log(l):
|
||||
return {
|
||||
"id": l.id,
|
||||
"order_id": l.order_id,
|
||||
"event_type": l.event_type,
|
||||
"waiter_id": l.waiter_id,
|
||||
"waiter_name": name_map.get(l.waiter_id) if l.waiter_id else None,
|
||||
"item_ids": l.item_ids,
|
||||
"amount": l.amount,
|
||||
"payment_method": l.payment_method,
|
||||
"note": l.note,
|
||||
"created_at": l.created_at,
|
||||
"offline_at": l.offline_at,
|
||||
"is_duplicate": l.is_duplicate,
|
||||
}
|
||||
|
||||
return {
|
||||
"id": order.id,
|
||||
"table_id": order.table_id,
|
||||
"opened_by": order.opened_by,
|
||||
"opened_at": order.opened_at,
|
||||
"status": order.status,
|
||||
"closed_at": order.closed_at,
|
||||
"closed_by": order.closed_by,
|
||||
"notes": order.notes,
|
||||
"business_day_id": order.business_day_id,
|
||||
"items": [fmt_item(i) for i in order.items],
|
||||
"waiters": [{"waiter_id": w.waiter_id} for w in order.waiters],
|
||||
"audit_logs": [fmt_log(l) for l in order.audit_logs],
|
||||
}
|
||||
|
||||
|
||||
@router.post("/", response_model=OrderOut, status_code=status.HTTP_201_CREATED)
|
||||
@@ -310,10 +379,26 @@ def close_order(order_id: int, db: Session = Depends(get_db), user: User = Depen
|
||||
raise HTTPException(status_code=403, detail="Access denied")
|
||||
if order.status not in ("paid", "open", "partially_paid"):
|
||||
raise HTTPException(status_code=400, detail="Cannot close order in current status")
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
# Mark all still-active items as 'closed' — unpaid, closed by manager
|
||||
active_items = db.query(OrderItem).filter(
|
||||
OrderItem.order_id == order_id,
|
||||
OrderItem.status == "active",
|
||||
).all()
|
||||
closed_item_ids = []
|
||||
for item in active_items:
|
||||
item.status = "closed"
|
||||
closed_item_ids.append(item.id)
|
||||
|
||||
order.status = "closed"
|
||||
order.closed_at = datetime.now(timezone.utc)
|
||||
order.closed_at = now
|
||||
order.closed_by = user.id
|
||||
_audit(db, order_id, "ORDER_CLOSED", waiter_id=user.id)
|
||||
|
||||
note = f"Κλείσιμο από manager — {len(closed_item_ids)} απλήρωτα αντικείμενα" if closed_item_ids else None
|
||||
_audit(db, order_id, "ORDER_CLOSED", waiter_id=user.id,
|
||||
item_ids=closed_item_ids if closed_item_ids else None, note=note)
|
||||
db.commit()
|
||||
broadcast_sync("order_closed", {"order_id": order_id, "table_id": order.table_id})
|
||||
return {"status": "closed"}
|
||||
@@ -419,10 +504,26 @@ def cancel_order(order_id: int, db: Session = Depends(get_db), user: User = Depe
|
||||
order = db.query(Order).filter(Order.id == order_id).first()
|
||||
if not order:
|
||||
raise HTTPException(status_code=404, detail="Order not found")
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
# Cancel all still-active items
|
||||
active_items = db.query(OrderItem).filter(
|
||||
OrderItem.order_id == order_id,
|
||||
OrderItem.status == "active",
|
||||
).all()
|
||||
cancelled_item_ids = []
|
||||
for item in active_items:
|
||||
item.status = "cancelled"
|
||||
cancelled_item_ids.append(item.id)
|
||||
|
||||
order.status = "cancelled"
|
||||
order.closed_at = datetime.now(timezone.utc)
|
||||
order.closed_at = now
|
||||
order.closed_by = user.id
|
||||
_audit(db, order_id, "ORDER_CANCELLED", waiter_id=user.id)
|
||||
|
||||
note = f"Ακύρωση από manager — {len(cancelled_item_ids)} αντικείμενα ακυρώθηκαν" if cancelled_item_ids else None
|
||||
_audit(db, order_id, "ORDER_CANCELLED", waiter_id=user.id,
|
||||
item_ids=cancelled_item_ids if cancelled_item_ids else None, note=note)
|
||||
db.commit()
|
||||
broadcast_sync("order_closed", {"order_id": order_id, "table_id": order.table_id})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user