feat: add payroll + tables served to shift detail and workday staff views
- business_day summary: waiter entries now include hourly_rate_snapshot, duration_hours, shift_pay (cumulative across day's shifts), tables_served (unique table count) - ShiftDetailModal: 4-KPI row replaced with 2×4 grid — Row 1: Διάρκεια | Μισθός/Ώρα | Πληρωμή Βάρδιας | Αρχικά Μετρητά Row 2: Σύνολο Παραγγελιών | Εισπράξεις | Προς Παράδοση | Εξ. Τραπεζιών - WorkdaySummaryModal WaitersTab: collapsed row uses ΠΑΡΑΓΓΕΛΙΕΣ | ΕΞ.ΤΡΑΠΕΖΙΩΝ | ΑΝΤΙΚ.ΕΙΣΠΡ. | ΕΙΣΠΡΑΞΗ; expanded grid is 2 rows: Row 1: ΕΝΑΡΞΗ | ΛΗΞΗ | ΜΙΣΘΟΣ/ΩΡΑ | ΜΙΣΘΟΣ (εως τώρα) Row 2: ΑΡΧΙΚΑ ΜΕΤΡΗΤΑ | ΤΑΜΕΙΟ ΤΩΡΑ | ΑΝΤΙΚ.ΕΙΣΠΡ. | ΕΞ.ΤΡΑΠΕΖΙΩΝ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -246,14 +246,25 @@ def business_day_summary(
|
||||
"shift_started_at": None,
|
||||
"shift_ended_at": None,
|
||||
"starting_cash": None,
|
||||
# Phase 2B payroll
|
||||
"hourly_rate_snapshot": None,
|
||||
"duration_hours": None,
|
||||
"shift_pay": None,
|
||||
# distinct tables served (not order count)
|
||||
"tables_served": 0,
|
||||
}
|
||||
return waiter_stats[uid]
|
||||
|
||||
# track unique tables per waiter (opened_by)
|
||||
waiter_tables: dict = {} # uid → set of table_ids
|
||||
|
||||
for o in orders:
|
||||
if o.status == "cancelled":
|
||||
continue
|
||||
e = _waiter_entry(o.opened_by)
|
||||
e["orders_opened"] += 1
|
||||
if o.table_id:
|
||||
waiter_tables.setdefault(o.opened_by, set()).add(o.table_id)
|
||||
|
||||
for item in all_items:
|
||||
if item.status == "cancelled":
|
||||
@@ -281,6 +292,7 @@ def business_day_summary(
|
||||
e["other"] += val
|
||||
|
||||
for shift in shifts:
|
||||
from routers.shifts import compute_shift_pay
|
||||
e = _waiter_entry(shift.waiter_id)
|
||||
if e["shift_started_at"] is None or (shift.started_at and _dt(shift.started_at) < e["shift_started_at"]):
|
||||
e["shift_started_at"] = _dt(shift.started_at)
|
||||
@@ -290,6 +302,18 @@ def business_day_summary(
|
||||
ended = _dt(shift.ended_at)
|
||||
if e["shift_ended_at"] is None or ended > e["shift_ended_at"]:
|
||||
e["shift_ended_at"] = ended
|
||||
# payroll — take the most recent non-null snapshot
|
||||
if shift.hourly_rate_snapshot is not None:
|
||||
e["hourly_rate_snapshot"] = shift.hourly_rate_snapshot
|
||||
pay_data = compute_shift_pay(shift)
|
||||
e["duration_hours"] = round((e["duration_hours"] or 0) + pay_data["duration_hours"], 2)
|
||||
if pay_data["shift_pay"] is not None:
|
||||
e["shift_pay"] = round((e["shift_pay"] or 0) + pay_data["shift_pay"], 2)
|
||||
|
||||
# write tables_served counts
|
||||
for uid, table_set in waiter_tables.items():
|
||||
if uid in waiter_stats:
|
||||
waiter_stats[uid]["tables_served"] = len(table_set)
|
||||
|
||||
# manager gets shift = day open/close
|
||||
manager_opener = users_map.get(day.opened_by_id)
|
||||
|
||||
Reference in New Issue
Block a user