feat: Phase 2B — staff payroll / hourly rate
- Users: add hourly_rate (Float, nullable) — manager-only, not shown to waiter - WaiterShift: add hourly_rate_snapshot — copied from user.hourly_rate at shift open, never updated - Shift start (both /start and /manager/start): snapshot rate at shift creation - compute_shift_pay(): calculates worked hours (minus breaks) × rate_snapshot; returns None if unset - _enrich_shift(): now includes hourly_rate_snapshot, duration_hours, shift_pay - shifts_report in reports.py: delegates to _enrich_shift so all shift endpoints are consistent - Shifts CSV export: adds duration_hours, hourly_rate, shift_pay columns - StaffTab: hourly_rate field in waiter edit modal (Μισθοδοσία section, col 3) - ShiftsOverview report: Αμοιβή/ώρα + Αποδοχές columns; untracked shows 'Δεν παρακολουθείται' Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -872,7 +872,7 @@ def shifts_report(
|
||||
db: Session = Depends(get_db),
|
||||
user: User = Depends(require_manager),
|
||||
):
|
||||
from routers.shifts import compute_shift_total
|
||||
from routers.shifts import _enrich_shift
|
||||
|
||||
q = db.query(WaiterShift)
|
||||
if waiter_id:
|
||||
@@ -887,28 +887,7 @@ def shifts_report(
|
||||
q = q.filter(WaiterShift.ended_at == None)
|
||||
|
||||
shifts = q.order_by(WaiterShift.started_at.desc()).all()
|
||||
waiters_db = {u.id: u for u in db.query(User).all()}
|
||||
|
||||
result = []
|
||||
for shift in shifts:
|
||||
w = waiters_db.get(shift.waiter_id)
|
||||
wname = (w.full_name or w.username) if w else f"#{shift.waiter_id}"
|
||||
total = compute_shift_total(shift.id, db) if shift.ended_at is None else (shift.total_collected or 0.0)
|
||||
result.append({
|
||||
"id": shift.id,
|
||||
"waiter_id": shift.waiter_id,
|
||||
"waiter_name": wname,
|
||||
"business_day_id": shift.business_day_id,
|
||||
"started_at": _dt(shift.started_at),
|
||||
"ended_at": _dt(shift.ended_at),
|
||||
"starting_cash": shift.starting_cash,
|
||||
"total_collected": total,
|
||||
"net_to_deliver": round(total + (shift.starting_cash or 0.0), 2),
|
||||
"is_active": shift.ended_at is None,
|
||||
"notes": shift.notes,
|
||||
})
|
||||
|
||||
return {"shifts": result}
|
||||
return {"shifts": [_enrich_shift(s, db) for s in shifts]}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1513,6 +1492,9 @@ def shifts_export(
|
||||
"waiter": s["waiter_name"],
|
||||
"started_at": s["started_at"],
|
||||
"ended_at": s["ended_at"] or "",
|
||||
"duration_hours": s.get("duration_hours", ""),
|
||||
"hourly_rate": s.get("hourly_rate_snapshot", ""),
|
||||
"shift_pay": s.get("shift_pay", ""),
|
||||
"starting_cash": s["starting_cash"],
|
||||
"total_collected": s["total_collected"],
|
||||
"net_to_deliver": s["net_to_deliver"],
|
||||
|
||||
Reference in New Issue
Block a user