Защита от одиночных мест

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

Как включить

Нужна роль администратора организации. Права глобального администратора не требуются.

Откройте Настройки > Организация. У поля Защита от одиночных мест стоит подсказка Запрещает выбор, после которого в ряду остаётся одно свободное место. и три значения. Включено применяет правило ко всем событиям организации. Выключено отключает его для всех событий. Наследовать (сейчас включено) или Наследовать (сейчас выключено) передаёт решение уровню выше и показывает, что этот уровень отвечает сейчас. Значение сохраняется сразу после выбора.

Страница настроек организации в редакторе площадок с полем «Защита от одиночных мест» в значении «Включено»

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

Как переопределить для одного события

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

Диалог события в редакторе площадок где поле «Защита от одиночных мест» оставлено на «Наследовать (сейчас включено)» — организация уже включила защиту

Когда изменение вступает в силу

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

Как правило читает ряд

Правило работает с геометрией ряда. Оно сортирует места по координатам, поэтому ряд с нумерацией от 23 до 12 читается так же, как ряд перед ним с нумерацией от 1 до 11. Названия мест в расчёт не входят.

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

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

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

Что видит покупатель

Рендерер обводит пунктирным контуром ту пару мест, которую придётся взять вместе. Контур только подсказывает. Места внутри контура остаются кликабельными, покупатель может нажать на любое из них.

Карта мест в рендерере бронирования с пунктирным контуром вокруг пары мест, которые бронируются вместе

Клик по любому месту из такой пары по умолчанию проходит. Рендерер сам добавляет второе место, и в корзине оказываются оба. Удаление места из корзины, наоборот, отклоняется, потому что поправка убрала бы места, уже выбранные покупателем. Клик при groupSize больше единицы отклоняется по той же причине.

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

Для интеграций

Реализуйте колбэк onOrphanSeatsBlocked, если хотите решать самостоятельно вместо поведения по умолчанию. Колбэк получает места, которые попытался взять покупатель, места, которые останутся в одиночестве, и поправку, снимающую проблему. Верните true, чтобы принять поправку, и false, чтобы отклонить изменение.

Методы корзины, которые вы вызываете сами, сообщают то же самое в возвращаемом значении. initCart, addSeatsToCart, removeSeatsFromCartByIds и removeSeatsFromCartByKeys теперь возвращают ICartChangeResult вместо пустого значения. Поле applied отвечает, изменилась ли корзина, а orphaned показывает, оставляет ли изменение место в одиночестве. Удаление возвращает applied: true вместе с непустым orphaned, потому что оно проходит и всё равно оставляет место одиноким.

Если ваше оформление заказа блокирует места по одному, передавайте в поле cartSeatIds запроса v2 идентификаторы мест, которые покупатель уже держит. Сервер судит эти места и места из запроса как один выбор, и правильно собранная корзина не отклоняется на полпути. Поле принимает до 1000 идентификаторов, его читают lock, sale и directsale.

Полную сигнатуру колбэка и примеры кода смотрите в разделе Programmatic Cart Changes and Orphan Prevention документации по рендереру.

Что отвечает оформление заказа

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

На маршруте бронирования v2 запрос падает целиком. lock, sale и directsale отвечают HTTP 422 с кодом ORPHAN_SEATS_REFUSED и называют отклонённые места. Ничего не блокируется и ничего не продаётся.

На пути продажи v1 вызов отвечает HTTP 200, а спорные места возвращаются в списке неуспешных выборов. Остальная часть выбора продаётся. Этот список надо читать, потому что 200 здесь не означает, что прошло каждое место.

Сервер проверяет только места, переходящие в LOCKED или SOLD. Освобождение проходит нетронутым.

Если сама проверка падает

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