Per-staff dashboards с обслужени поръчки, средна сметка, on-time коефициент, void-нати продукти и обратна връзка от клиенти — rolled up нощно от данните на живо за поръчки, не от отделен POS експорт.
Представянето на персонала в Ordering.Tools е дневен roll-up на всяка метрика, която връзва служител с клиент или смяна: поръчки, които е обслужил, продукти, които е въвел, приходи, за които е отговорен, средна сметка, on-time срещу late срещу no-show смени, void-нати продукти и средна оценка от клиенти. Всяка метрика идва от данни, които платформата вече улавя (Order.assignedWaiterId, OrderFeedback, ClockEvent, StaffShiftAssignment). Roll-up-ът работи нощно чрез cron:nightly runner-а; мениджърите могат да replay-нат всеки прозорец от admin UI.
Целта не е следене — а coaching. Per-staff dossiers изплуват pattern-и, които имат значение: кой е постоянно подранил; кой има най-висока средна сметка; кой void-ва повече от колегите си; кой се появява навреме дори в събота след сватба. Сдвоено с rota grid-а мениджърите знаят кого да насрочват за обедния пик срещу късната тераса. Данните са read-only за мениджъри (без редактиране на минали метрики); raw събития живеят в audit log, ако някой иска да верифицира.
Всяка метрика се изчислява от редове, които вече имате — Order, OrderFeedback, ClockEvent, StaffShiftAssignment. Без допълнителни sensors, без отделен POS експорт, без third-party time-tracking интеграция. Order.assignedWaiterId е join ключът.
Roll-up-ът работи в 03:00 UTC (cron:nightly) и записва един StaffMetricsDaily ред на (заведение, служител, дата). Ако трябва да реизчислите прозорец — например след backfill — натиснете /api/admin/staff/metrics/replay с from/to обхват; upsert-ът е идемпотентен.
Сравняваме CLOCK_IN на персонала срещу планираното StaffShiftAssignment.startTime. В рамките на 5 минути = on-time; над 5 минути = late; без clock-in до края на смяната = no-show. Per-staff rolling averages изплуват pattern-а, не само лошата вечер.
Метриките не са редактируеми — мениджърите не могат да „поправят“ ред да изглежда по-ласкаещо. Raw събитията живеят в audit log и са reproducible. Ако забележите ред, който изглежда грешен, replay endpoint-ът го реизчислява; ако реизчислението съвпада, данните са реални.
Превключете enableStaffMetrics в Персонал → Настройки. Нощният cron започва да emit-ва rolled-up метрики в рамките на 24 часа; per-staff dossier таб в /admin/profile става видим за служителя.
В 03:00 UTC cron:nightly runner-ът изпълнява staff-metrics стъпката. Той групира Order редове по assignedWaiterId за всяко активирано заведение, съединява ClockEvents за часове, агрегира OrderFeedback за оценки и upsert-ва един ред на (заведение, потребител, дата).
Отворете Персонал → Членове → кликнете служител. Dossier-ът показва дневни метрики през последните 30/90 дни със sparklines за поръчки, приходи и средна оценка. Кликнете всеки ден да видите подлежащите StaffShiftAssignment + ClockEvents + Orders.
Всеки служител вижда метриките си от My Stats екрана на mobile waiter shell — read-only. Coaching разговорите се рамкират от данни, които и двете страни могат да видят, не от анекдоти.
Идемпотентен upsert — re-running на същия прозорец произвежда идентични редове. Roll-up-ът е част от cron-system-migration grouped runner архитектурата; нови метрики се добавят чрез разширяване на една стъпка, не създаване на нов cron ред.
Сравняваме първия CLOCK_IN на служителя за деня със StaffShiftAssignment.startTime. Late прагът е конфигурируем (по подразбиране 5 минути); no-show е без clock-in до края на смяната. Класификацията записва onTimeShifts / lateShifts / noShowShifts брояци.
OrderFeedback редове, съединени с Order.assignedWaiterId, дават per-staff customer оценки. Dossier-ът показва feedbackAvg + feedbackCount; кликването разкрива индивидуални коментари (когато са налични), така че coaching има конкретни примери.
Ако backfill или бъг означава, че минали метрики са грешни, натиснете /api/admin/staff/metrics/replay с from/to. Endpoint-ът re-run-ва същата логика, която нощният cron използва; upsert-ът презаписва stale редове с пресни.
Сервитьор, нает преди три месеца има feedbackAvg = 3.8 срещу средно за екипа 4.4. Отворете dossier-а — лошите оценки са групирани в съботни късни смени. Мениджърът ги сдвоява със senior на тези смени; средното се качва на 4.3 през следващия месец.
noShowShifts на Петър се покачват през последните 30 дни. Отворете dossier-а — всеки no-show е неделя сутрин. Разговор: „Неделя сутрин са трудни — нека те преместим на вечерни“. Pattern-ът се решава без уволнение.
Двама кандидати за head-server роля. Dossier-ите им показват идентични средни сметки, но единият има 92% on-time срещу 78% на другия. Първият получава промоцията; вторият получава coaching по точност преди да бъде преразглеждан.
Нов кухненски наемник има voidedItemCount 3× средното на екипа. Повечето void-ове са грешно сготвени стекове. Разговор: „Нека минем през chart-а за doneness заедно“ — void-овете падат до средното на екипа за две седмици.
Всяко заведение има сервитьора, който upsell-ва естествено и този, който не. avgTicketCents колоната ги ранкира. Навиците на най-добрия сервитьор (препоръчване на специалитета на шефа, предлагане на гарнитура) се coach-ват в останалия екип.
Персоналът вижда статистиките си и се самокоригира без мениджърска интервенция. Сервитьор забелязва, че средната сметка във вторник е паднала — започва да споменава новата паста; средната се възстановява. Подобрението става без coaching среща.
Повечето ресторант analytics платформи агрегират на ниво заведение — общи покрития, общи приходи, общи сметки — и спират там. Трудната стъпка е да минете от средни на заведение към per-staff insight, защото това изисква staff-attribution колона на всяка поръчка. Ordering.Tools има Order.assignedWaiterId откакто waiter модулът беше пуснат, така че per-staff roll-up е SQL group-by, не нов instrumentation проект. Добавете ClockEvent за часове, OrderFeedback за оценки и StaffShiftAssignment за on-time класификация и имате пълен dossier без нови sensors.
Изчисляването на per-staff метрики live на всяко dashboard зареждане означава, че home page на 50-staff заведение прави 50-row агрегация през 30 дни поръчки — на всяко зареждане. Това е бавно дори с indexes и мащабира лошо. Нощният roll-up записва един StaffMetricsDaily ред на (заведение, служител, дата) и dashboards четат тези редове директно. P95 dossier load times падат от секунди до милисекунди, database load пада с порядък, и данните са точно толкова свежи колкото трябва (вчерашни числа, изчислени в 03:00 UTC).
Има напрежение между да си толерантен (30 минути late = on-time) и да си строг (1 минута late = late). Различни култури, различни роли. Ordering.Tools по подразбиране използва 5 минути — щедро достатъчно, че трафик и автобусна аритметика да не маркират добри работници като late, строго достатъчно, че постоянни 20-минутни late starts да бъдат флагнати. Прагът е venue-конфигурируем. Класификацията е rolling — една лоша вечер не разваля 30-дневно средно — и time-off-approved no-shows не се броят, по дизайн.
Редактируеми performance метрики раждат недоверие. Ако мениджър може да „поправи“ нискорейтингов ден да изглежда по-добре, персоналът спира да вярва на dashboard-а и започва да пита „какво беше реалното число?“. Ordering.Tools експонира метриките като read-only — единственият начин да се променят е replay endpoint, който ре-изчислява от raw събития. Самите raw събития са append-only (ClockEvent има MANUAL_ADJUST pattern; поръчките не са редактируеми след завършване). Dashboard-ът е каквото данните казват, че е станало, не интерпретация на мениджъра.