Korat
Модуль

Касса кафе, экран кухни и заказ прямо со стола

Система заказов для ресторана начинается со стола: гость сканирует QR и оказывается в меню. Заведение получает экран кухни, детализированный счёт, с которым согласна касса, и очередь доставки — и ни одна цена не доверена телефону, который её прислал. Ставить программу не нужно: экран кухни открывается в браузере на любом планшете. Всё это бесплатно, без абонентской платы и без сборов за стол или за заказ.

Софт для приёма заказов в ресторане обычно ломается в двух местах: там, где чужой человек может заказать на не свой стол, и там, где сумма на экране гостя расходится с суммой на кассе. Модуль еды в Korat проектировался, отталкиваясь от обоих этих мест. Сессию стола выпускает персонал, и она несёт одноразовый токен; счёт — те же строки, что читает касса; а place_dining_order считает каждую позицию на сервере. Опции, таймер буфета и разделение счёта построены поверх этих двух гарантий.

QR стола — это одноразовый токен, а не номер стола

Пути «введите номер стола» здесь нет намеренно. Это очевидная функция и причина, по которой множеством систем обслуживания за столом можно злоупотребить с парковки: если единственный пропуск — номер, напечатанный на столе, то любой, кто хоть раз здесь ел, может вечно заказывать на любой стол. Вместо этого персонал открывает стол из консоли, и это выпускает свежий одноразовый токен, встроенный в показанный на столе QR. Сфотографированный код, скриншот, пересланный другу, или распечатка с прошлого вторника несут токен, который уже не действует, и не работают.

Гость, подошедший к столу, не сотрудник заведения и не хозяин ещё не существующей сессии, поэтому по правилам защиты на уровне строк он не может прочитать таблицу dining_sessions вообще — ни своей строки, ни любой другой. RLS умеет судить о том, кто обращается; она не умеет судить о том, что запрос несёт действительный токен. Поэтому сканирование идёт через join_session_by_token — функцию SECURITY DEFINER, которая сверяет токен на сервере, делает первого сканировавшего хозяином и возвращает null, когда токен не подошёл, — не объясняя причину. Ошибка, отличающая «неверный токен» от «просроченного», — это оракул для подбора.

Внутри хозяин может позвать остальных за стол ссылкой или карточкой в чате, чтобы опоздавший друг вошёл в ту же сессию и его еда попала в тот же счёт.

Заказ: опции, комментарии и меню, знающее, что скоро закрытие

Меню разложено по категориям, и у каждой позиции могут быть группы опций — дополнительная порция эспрессо, острота, вид лапши — с надбавками к цене, признаком обязательности и максимумом выборов. Надбавка привязана к опции, а не вписана в комментарий, — поэтому чек на кухне и строка счёта согласны в том, что заказали. У позиции есть свободный комментарий и количество, а корзина остаётся редактируемой до отправки.

Часы работы, последний заказ и проверка возраста на алкоголь

Заведение задаёт часы работы с буфером последнего заказа. На подходе к нему меню предупреждает, что кухня скоро закроется; после него заказ блокируется, а не принимается, чтобы позже тихо отмениться. Позиции, помеченные как недоступные или закончившиеся, говорят об этом на карточке — до нажатия. У алкоголя свой признак: проверка возраста по дате рождения гостя, и персонал может подтвердить возраст сразу для всей сессии.

Очередь подтверждения: на кухню ничего не попадает, пока человек не сказал «да»

Отправленные заказы не идут прямо на раздачу. Они падают в очередь ожидания подтверждения на экране кухни, и сотрудник подтверждает или отклоняет каждый, прежде чем тот станет работой. Система, позволяющая телефону класть чеки прямо на кухню, отдаёт незнакомцу возможность пускать продукты заведения в расход и убирает момент, когда человек замечает, что шестой стол случайно заказал четыре одинаковых сета.

С экрана кухни персонал двигает заказы по состояниям, а консоль показывает живые значки, обновляющиеся каждые несколько секунд: активные заказы, столы, зовущие официанта, ожидающие брони, новые доставки — плюс красная полоса в тот же миг, когда стол нажал вызов. У гостей вторая половина этой петли: позвать официанта, попросить счёт или покинуть стол.

Живой детализированный счёт и его разделение через QR PromptPay

Живой счёт показывает ровно те строки, что читает касса, и обновляется по мере подтверждения блюд: никакого сюрприза в конце ужина и никакого восстановления по памяти. Когда стол закрывается, приложение выдаёт итоговый экран — вместе с предложением добавить в друзья тех, с кем вы делили сессию, — а потом напоминание об отзыве. Чеки остаются в личной истории счетов.

Разделение счёта: сколько должен каждый

Разделение счёта отвечает на вопрос, из-за которого люди действительно спорят: сколько с меня. Каждый видит свой подытог по блюдам плюс равную долю буфетного пакета, если он был. Для суммы можно сгенерировать PromptPay QR в тайском стандартном формате с контрольной суммой CRC-16.

Разделение счёта — это расчёт, а не отдельные платежи. Оно считает, кто сколько должен; оно не списывает четыре доли с четырёх карт. Деньги по-прежнему делят между собой люди за столом и заведение.

И запасного пути «введите номер стола» нет намеренно. Если QR у гостя не сканируется, персонал заново открывает стол из консоли. Ручной ввод вернул бы ровно ту дыру, ради закрытия которой токен и выпускается.

Буфет: пакет с человека, таймер и плата за остатки

У буфета своя модель, а не подделка через позицию меню с ценой за человека. У пакета есть цена с человека, таймер обратного отсчёта с продлением от персонала и раздельный подсчёт взрослых и детей. Позиции меню несут буфетную роль — включено, только а-ля-карт или включено с надбавкой, — так что одно меню обслуживает оба типа гостей. Поддерживается плата за недоеденное с указанием единиц: правило, которое большинство тайских буфетов уже печатает на столе, а большинство софта делает вид, что его нет.

Доставка, самовывоз и брони столиков

То же меню обслуживает доставку и самовывоз. У заказа есть стоимость доставки, служба, назначенное время выдачи и адрес, и он проходит состояния «новый», «готовится», «в пути», «выполнен» — с уведомлением обеих сторон на каждом переходе. Бронь столика принимает дату, время, число гостей и комментарий и ждёт подтверждения заведения: счётчик ожидающих виден на панели живым значком. Заведение, которое торгует ещё и фасованным товаром, берёт для этого модуль магазина, построенный на том же правиле расчёта цен на сервере.

Два частых варианта. Точка, обслуживающая у стойки и вызывающая по номеру, использует электронную очередь вместо столов. А если сначала хочется понять, чего вообще требует открытие заведения, это целиком описано на странице для бизнеса.

Каждая цена считается на сервере

Правило, которое делает безопасным всё остальное: клиент никогда не задаёт цену. place_dining_order и place_shop_order читают цены позиций, надбавки опций и стоимость доставки из базы и игнорируют присланное телефоном. Поле клиента, которому доверяют, — это скидка себе самому. Тот же принцип действует и с отзывами: написать может только гость, про которого база может доказать, что он ел здесь или заказывал доставку, а рейтинг пересчитывается триггером и никогда не вписывается руками.

Частые вопросы

Может ли гость заказать, не сканируя QR на столе?

Для обслуживания за столом — нет. Ручного ввода номера стола нет, потому что напечатанный номер рано или поздно становится пропуском для всего города. Персонал открывает стол из консоли, и это выпускает токен, который несёт QR.

С доставкой и самовывозом наоборот: их заказывают со страницы заведения, стол в этом не участвует.

Что мешает сфотографировать наш QR и заказать позже?

Токен в QR одноразовый и привязан к сессии, которую открыл персонал. Как только сессия закончилась, токен перестаёт совпадать и сканирование не даёт ничего. Проверка живёт в функции базы данных SECURITY DEFINER, а не в приложении, поэтому её нельзя обойти, обратившись к API напрямую.

Заказы уходят прямо на кухню?

Нет. Каждый заказ сначала падает в очередь ожидания подтверждения на экране кухни, и персонал принимает или отклоняет его. Этот шаг подтверждения — место, где ошибки и злоупотребления ловятся до того, как стоят продуктов.

Может ли каждый за столом оплатить свою долю картой?

Нет. Разделение считает, кто сколько должен — свои блюда плюс равная доля буфета, — а расчёт происходит с заведением. Korat не проводит отдельные карточные платежи по людям.

Что будет, если гость закажет прямо перед закрытием?

Заведение задаёт часы работы и буфер последнего заказа. Внутри буфера приложение предупреждает, что кухня скоро закрывается; после него заказ блокируется, а не принимается с последующей отменой.

Откройте стол и увидите петлю целиком

Сторона гостя и сторона консоли — одно приложение. Установите его, создайте бизнес и отсканируйте собственный QR.