feat: Add dedicated buyer dashboard, buyer role assignment, Bavarian White-Blue light/dark mode and mobile drawer
This commit is contained in:
+100
-1
@@ -527,6 +527,82 @@ def download_calendar_ics(event_id: int, request: Request, db: Session = Depends
|
||||
)
|
||||
|
||||
|
||||
# --- EINKAUFS- & KASSEN-ZENTRALE (/einkauf) ---
|
||||
|
||||
@app.get("/einkauf", response_class=HTMLResponse)
|
||||
def buyer_dashboard(
|
||||
request: Request,
|
||||
event_id: Optional[int] = None,
|
||||
success: Optional[str] = None,
|
||||
error: Optional[str] = None,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
tenant = resolve_tenant(request, db)
|
||||
if not tenant:
|
||||
return templates.TemplateResponse(request, "not_found_tenant.html", context={})
|
||||
|
||||
current_user = get_current_user(request, db)
|
||||
if not current_user:
|
||||
return RedirectResponse(url="/login", status_code=status.HTTP_303_SEE_OTHER)
|
||||
|
||||
# Alle Events des Mandanten
|
||||
all_events = db.query(Event).filter(Event.tenant_id == tenant.id).order_by(Event.event_date.desc(), Event.id.desc()).all()
|
||||
|
||||
# Ausgewähltes Event bestimmen:
|
||||
# 1. Prio: explizit übergebenes event_id
|
||||
# 2. Prio: Event, bei dem current_user als buyer zugewiesen ist und status == 'open'
|
||||
# 3. Prio: erstes offenes Event
|
||||
# 4. Prio: neuestes Event
|
||||
selected_event = None
|
||||
if event_id:
|
||||
selected_event = db.query(Event).filter(Event.id == event_id, Event.tenant_id == tenant.id).first()
|
||||
|
||||
if not selected_event:
|
||||
for ev in all_events:
|
||||
if ev.buyer_id == current_user.id and ev.status == "open":
|
||||
selected_event = ev
|
||||
break
|
||||
if not selected_event:
|
||||
for ev in all_events:
|
||||
if ev.status == "open":
|
||||
selected_event = ev
|
||||
break
|
||||
if not selected_event and all_events:
|
||||
selected_event = all_events[0]
|
||||
|
||||
orders = []
|
||||
item_sums = {}
|
||||
total_revenue = Decimal("0.00")
|
||||
total_paid = Decimal("0.00")
|
||||
|
||||
if selected_event:
|
||||
orders = db.query(Order).filter(Order.event_id == selected_event.id).order_by(Order.created_at.asc()).all()
|
||||
for ord_obj in orders:
|
||||
total_revenue += ord_obj.total_price
|
||||
if ord_obj.is_paid:
|
||||
total_paid += ord_obj.total_price
|
||||
|
||||
for it in ord_obj.items:
|
||||
pname = it.product.name if it.product else "Artikel"
|
||||
unit = it.product.unit if it.product else "Stk"
|
||||
if pname not in item_sums:
|
||||
item_sums[pname] = {"quantity": 0, "unit": unit}
|
||||
item_sums[pname]["quantity"] += it.quantity
|
||||
|
||||
return templates.TemplateResponse(request, "buyer.html", context={
|
||||
"tenant": tenant,
|
||||
"current_user": current_user,
|
||||
"event": selected_event,
|
||||
"all_events": all_events,
|
||||
"orders": orders,
|
||||
"item_sums": item_sums,
|
||||
"total_revenue": total_revenue,
|
||||
"total_paid": total_paid,
|
||||
"success": success,
|
||||
"error": error,
|
||||
})
|
||||
|
||||
|
||||
# --- ADMIN COCKPIT (/admin) ---
|
||||
|
||||
@app.get("/admin", response_class=HTMLResponse)
|
||||
@@ -801,6 +877,7 @@ def admin_create_event(
|
||||
deadline_date: str = Form(...),
|
||||
deadline_time: str = Form(...),
|
||||
sponsor_name: Optional[str] = Form(None),
|
||||
buyer_id: Optional[str] = Form(None),
|
||||
allow_guests: bool = Form(True),
|
||||
force: bool = Form(False),
|
||||
db: Session = Depends(get_db)
|
||||
@@ -827,6 +904,13 @@ def admin_create_event(
|
||||
status_code=status.HTTP_303_SEE_OTHER
|
||||
)
|
||||
|
||||
buyer_id_int: Optional[int] = None
|
||||
if buyer_id and buyer_id.strip():
|
||||
try:
|
||||
buyer_id_int = int(buyer_id.strip())
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
new_event = Event(
|
||||
tenant_id=tenant.id,
|
||||
title=title.strip(),
|
||||
@@ -834,6 +918,7 @@ def admin_create_event(
|
||||
deadline=deadline_dt,
|
||||
status="open",
|
||||
sponsor_name=sponsor_name.strip() if sponsor_name and sponsor_name.strip() else None,
|
||||
buyer_id=buyer_id_int,
|
||||
allow_guests=allow_guests,
|
||||
guest_token=secrets.token_urlsafe(16)
|
||||
)
|
||||
@@ -854,6 +939,7 @@ def admin_edit_event(
|
||||
deadline_time: str = Form(...),
|
||||
status_val: str = Form(...),
|
||||
sponsor_name: Optional[str] = Form(None),
|
||||
buyer_id: Optional[str] = Form(None),
|
||||
allow_guests: bool = Form(False),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
@@ -871,11 +957,19 @@ def admin_edit_event(
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=f"Ungültiges Datumsformat: {e}")
|
||||
|
||||
buyer_id_int: Optional[int] = None
|
||||
if buyer_id and buyer_id.strip():
|
||||
try:
|
||||
buyer_id_int = int(buyer_id.strip())
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
event.title = title.strip()
|
||||
event.event_date = ev_date
|
||||
event.deadline = deadline_dt
|
||||
event.status = status_val
|
||||
event.sponsor_name = sponsor_name.strip() if sponsor_name and sponsor_name.strip() else None
|
||||
event.buyer_id = buyer_id_int
|
||||
event.allow_guests = allow_guests
|
||||
if not event.guest_token:
|
||||
event.guest_token = secrets.token_urlsafe(16)
|
||||
@@ -1179,7 +1273,12 @@ def toggle_paid(order_id: int, request: Request, db: Session = Depends(get_db)):
|
||||
if order:
|
||||
order.is_paid = not order.is_paid
|
||||
db.commit()
|
||||
return RedirectResponse(url=f"/admin?event_id={order.event_id}", status_code=status.HTTP_303_SEE_OTHER)
|
||||
|
||||
ev_id = order.event_id if order else ""
|
||||
referer = request.headers.get("referer", "")
|
||||
if "/einkauf" in referer:
|
||||
return RedirectResponse(url=f"/einkauf?event_id={ev_id}", status_code=status.HTTP_303_SEE_OTHER)
|
||||
return RedirectResponse(url=f"/admin?event_id={ev_id}&tab=orders", status_code=status.HTTP_303_SEE_OTHER)
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
|
||||
Reference in New Issue
Block a user