Разработка сайта ресторана на 1С-Битрикс
Сайт ресторана — это не визитка с адресом и фотографией зала. Это система, через которую проходят заказы на доставку, бронирования столиков, обновления меню и синхронизация с кассой. Всё это завязано на инфоблоки, REST API кассовых систем и кастомные компоненты. Если на старте не продумать связку между инфоблоком меню и POS-системой, через три месяца окажется, что официант добавил новую позицию в iiko, а на сайте её нет — потому что синхронизация работает в одну сторону, и никто не написал обратный обработчик.
Инфоблок «Меню»: структура данных
Меню ресторана — это инфоблок с разделами-категориями и элементами-блюдами. Разделы: «Завтраки», «Салаты», «Горячее», «Десерты», «Напитки», «Винная карта». Вложенность — один уровень, для подкатегорий (например, «Красное вино» внутри «Винная карта») используется второй уровень разделов.
Свойства элемента (блюда):
-
WEIGHT— числовое, грамм. Выводится на карточке и в Schema.org разметке -
CALORIES— числовое, ккал. Опционально — расширенный блок: белки, жиры, углеводы (три отдельных свойстваPROTEINS,FATS,CARBS) -
ALLERGENS— множественный список: глютен, лактоза, орехи, морепродукты, яйца, соя. Фильтрация по аллергенам черезCIBlockElement::GetList()сPROPERTY_ALLERGENSв фильтре -
PRICE— числовое. Не через модульcatalog, если не нужна корзина — обычное свойство инфоблока. Если нужна онлайн-оплата — подключение к торговому каталогу черезCCatalog::Add() -
PHOTO— файл. Основное фото блюда. Дополнительные фото — множественное свойствоMORE_PHOTOS -
IS_NEW— чекбокс. Отметка «Новинка» для выделения в списке -
IS_SPICY— чекбокс. Пометка острого блюда -
STOP_LIST— чекбокс. Блюдо временно недоступно (закончился ингредиент). Элемент не удаляется, а скрывается по фильтру в шаблоне компонента -
SORT_ORDER— числовое. Порядок внутри раздела, позволяет шеф-повару через админку выставить фирменные блюда первыми
Для ресторанов с сезонным меню добавляется свойство SEASON (множественный список: весна, лето, осень, зима) и фильтрация по текущему сезону в component.php.
Онлайн-заказ и интеграция с POS-системами
Это технически самая нагруженная часть проекта. Ресторан работает с кассовой системой — iiko, r_keeper или Poster. Сайт должен не просто принимать заказы, а передавать их в кассу в реальном времени и получать обратную связь: подтверждение, время приготовления, статус.
Архитектура взаимодействия с iiko:
iiko предоставляет iiko Transport API (ранее iiko Biz API). Авторизация — по apiLogin, получение токена через POST /api/1/access_token. Токен живёт 60 минут, кешируется в $_SESSION или в Highload-блоке с TTL.
Создание заказа — POST /api/1/deliveries/create. Тело запроса содержит:
{
"organizationId": "...",
"order": {
"phone": "+375...",
"orderTypeId": "...", // delivery или self-pickup
"items": [
{
"productId": "iiko-product-uuid",
"amount": 2,
"modifiers": [...]
}
],
"address": {
"street": "...",
"house": "...",
"flat": "..."
},
"comment": "Без лука"
}
}
Критический момент — маппинг productId. В инфоблоке Битрикс у каждого блюда хранится свойство IIKO_PRODUCT_ID (строка, UUID из iiko). При синхронизации меню через GET /api/1/nomenclature загружается полный каталог iiko и сопоставляется с элементами инфоблока по этому UUID. Синхронизация запускается агентом CAgent раз в 15 минут или по вебхуку из iiko.
Что синхронизируется из iiko в Битрикс:
- Наличие блюда (stop-лист). iiko отправляет
POSTна webhook-endpoint/api/iiko-stoplist/. Обработчик обновляет свойствоSTOP_LISTу соответствующего элемента инфоблока черезCIBlockElement::SetPropertyValuesEx() - Цена. Если ресторан меняет цены в кассе, они должны приехать на сайт. Обработчик в агенте сравнивает цены из
/api/1/nomenclatureсPRICEв инфоблоке и обновляет расхождения - Модификаторы (добавки, гарниры). Хранятся в отдельном инфоблоке «Модификаторы» со свойством
IIKO_MODIFIER_ID
Что отправляется с сайта в iiko:
- Заказ с позициями, адресом, комментарием
- Тип оплаты (онлайн или при получении)
- Промокод, если есть — скидка рассчитывается на стороне iiko
Интеграция с r_keeper:
r_keeper использует UCS DeliveryPOS API. Принцип аналогичен, но протокол — XML-RPC вместо JSON REST. Запросы обёрнуты в XML-конверт, ответы парсятся через SimpleXMLElement. Маппинг товаров — по MenuItemID. Основная сложность — r_keeper требует VPN-туннель к серверу ресторана, тогда как iiko работает через облако.
Интеграция с Poster POS:
Poster предоставляет REST API с OAuth-авторизацией. Создание заказа — POST /api/incomingOrders.createIncomingOrder. Poster проще в интеграции: JSON API, облачное развёртывание, webhook для обновления статуса заказа. Маппинг — по product_id из Poster.
Обработка статусов заказа:
После создания заказа в POS сайт должен отслеживать статус. Два подхода:
-
Polling — агент или cron-задача раз в 60 секунд опрашивает API кассы по
orderId. Статусы: «Принят», «Готовится», «В пути», «Доставлен». Обновляет свойствоSTATUSв Highload-блоке «Заказы» -
Webhook — POS отправляет POST на
/api/order-status/при смене статуса. Предпочтительный вариант для iiko и Poster, но не всегда доступен для r_keeper
Статус отображается клиенту на странице /my-orders/ через AJAX-опрос каждые 30 секунд или через WebSocket (если инфраструктура позволяет).
Бронирование столиков
Кастомный компонент project:table.reservation с формой: дата, время, количество гостей, имя, телефон, комментарий.
Логика бронирования:
- Данные записываются в Highload-блок «Бронирования»:
DATE,TIME,GUESTS,NAME,PHONE,STATUS,TABLE_ID - Столики — отдельный Highload-блок:
TABLE_NUMBER,CAPACITY,ZONE(зал, терраса, VIP) - При бронировании компонент проверяет доступность: выборка из Highload-блока бронирований по
DATE+TIMEс окном ±2 часа, сопоставление с вместимостью свободных столиков - Если свободных столиков нет — предложение ближайшего доступного времени
Интеграция с CRM Битрикс24:
Каждое бронирование создаёт лид через CRest::call('crm.lead.add', [...]). Параметры:
$leadData = [
'TITLE' => 'Бронь столика: ' . $date . ' ' . $time,
'NAME' => $name,
'PHONE' => [['VALUE' => $phone, 'VALUE_TYPE' => 'WORK']],
'SOURCE_ID' => 'WEB',
'UF_CRM_TABLE' => $tableNumber,
'UF_CRM_GUESTS' => $guests,
'COMMENTS' => $comment
];
CRest::call('crm.lead.add', ['fields' => $leadData]);
Хостесс видит бронирования в CRM и подтверждает их. Статус лида «Подтверждено» → обработчик обновляет STATUS в Highload-блоке → клиенту приходит SMS через модуль messageservice или через внешний SMS-шлюз.
Фотогалерея и оптимизация изображений
Фуд-фотография — тяжёлые файлы. Исходники от фотографа — 5-10 МБ на снимок. На сайте нужны три размера: thumbnail для списка меню (400x300), средний для карточки блюда (800x600), full-size для лайтбокса (1600x1200).
Ресайз через CFile::ResizeImageGet() с BX_RESIZE_IMAGE_PROPORTIONAL. Результат кешируется в /upload/resize_cache/. Для WebP — конвертация через imagewebp() в обработчике OnBeforeFileResize или через Nginx-модуль ngx_http_image_filter_module.
srcset для Retina-дисплеев:
<img
src="/upload/resize_cache/menu/800x600/dish.webp"
srcset="/upload/resize_cache/menu/400x300/dish.webp 400w,
/upload/resize_cache/menu/800x600/dish.webp 800w,
/upload/resize_cache/menu/1600x1200/dish.webp 1600w"
sizes="(max-width: 640px) 400px, (max-width: 1024px) 800px, 1600px"
loading="lazy"
alt="Название блюда"
>
Атрибут loading="lazy" — нативная ленивая загрузка. Для старых браузеров — IntersectionObserver в JS. На странице меню с 50+ блюдами это экономит 30-40 МБ начальной загрузки.
Mobile-first: 80% трафика с телефонов
Ресторанный сайт ищут с телефона — «ресторан рядом», «меню доставки». Шаблон строится mobile-first:
- Меню категорий — горизонтальный скролл с
overflow-x: auto, не выпадающий список - Карточка блюда — фото на всю ширину, название, вес, цена. Кнопка «Добавить» зафиксирована внизу экрана через
position: sticky - Форма заказа — минимум полей. Телефон + адрес. Имя и комментарий — опционально. Автозаполнение адреса через Dadata API (
POST https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address) - Форма бронирования — нативные
<input type="date">и<input type="time">вместо кастомных datepicker-ов
Адаптивность — CSS Grid + Flexbox в шаблоне компонента. Breakpoint-ов три: 375px (телефон), 768px (планшет), 1280px (десктоп). Тестирование через Lighthouse: целевые показатели Performance > 90, LCP < 2.5s.
Мультиязычное меню
Для ресторанов в туристических зонах — меню на нескольких языках. В 1С-Битрикс мультиязычность реализуется через:
- Отдельный сайт в системе многосайтовости (
LID=s1для русского,s2для английского). Инфоблоки привязаны к обоим сайтам, свойстваNAME_EN,DESCRIPTION_EN— дополнительные текстовые свойства - Или через свойство
LANGUAGE(список: ru, en, de) и фильтрацию в компоненте по текущему языкуLANGUAGE_ID
Первый вариант надёжнее: разные URL (/menu/ и /en/menu/), корректные hreflang-теги, независимые SEO-настройки.
Schema.org: Restaurant + Menu
В result_modifier.php формируется JSON-LD разметка:
{
"@context": "https://schema.org",
"@type": "Restaurant",
"name": "Название ресторана",
"servesCuisine": "Итальянская",
"address": {
"@type": "PostalAddress",
"streetAddress": "...",
"addressLocality": "Минск"
},
"openingHoursSpecification": [...],
"menu": {
"@type": "Menu",
"hasMenuSection": [
{
"@type": "MenuSection",
"name": "Горячее",
"hasMenuItem": [
{
"@type": "MenuItem",
"name": "Стейк рибай",
"description": "...",
"nutrition": {
"@type": "NutritionInformation",
"calories": "850 cal"
},
"offers": {
"@type": "Offer",
"priceCurrency": "BYN"
}
}
]
}
]
}
}
Разметка выводится через $APPLICATION->AddHeadString(). Типы Restaurant, Menu, MenuItem — отдельные сущности Schema.org, Google распознаёт их для Rich Snippets в поисковой выдаче.
Акции и спецпредложения
Инфоблок «Акции» (тип promotions). Свойства: DATE_START, DATE_END, PROMO_TYPE (бизнес-ланч, happy hour, сезонное), DISCOUNT_PERCENT, LINKED_DISHES (множественная привязка к элементам инфоблока «Меню»).
Вывод на главной через news.list с фильтром по датам: >=DATE_START и <=DATE_END относительно текущей даты. Истекшие акции автоматически скрываются без участия администратора.
Для бизнес-ланча — отдельный раздел меню с ограничением по времени: компонент проверяет серверное время и показывает блок «Бизнес-ланч» только с 12:00 до 16:00.
Мероприятия ресторана
Инфоблок «Мероприятия» — для анонсов: живая музыка, тематические вечера, дегустации. Свойства: EVENT_DATE, EVENT_TIME, DESCRIPTION, COVER_CHARGE (чекбокс — вход платный/бесплатный), POSTER (изображение).
Вывод — лента на главной (три ближайших мероприятия) и отдельная страница /events/ со списком. Прошедшие мероприятия перемещаются в архив автоматически по EVENT_DATE < now().
Интеграция с агрегаторами доставки
Яндекс.Еда и Delivery Club предоставляют API для ресторанов-партнёров. Интеграция двусторонняя:
- Выгрузка меню — формирование XML/JSON-фида с позициями, ценами, фото, стоп-листом. Фид генерируется агентом раз в 30 минут из инфоблока «Меню»
-
Приём заказов — webhook от агрегатора на
/api/aggregator-order/. Обработчик создаёт заказ в Highload-блоке и передаёт в POS-систему
Это избавляет администратора от ручного обновления меню в личных кабинетах агрегаторов.
Этапы разработки
- Проектирование (1-2 недели) — структура инфоблоков, схема интеграции с POS, прототипы страниц, маппинг данных между Битрикс и кассовой системой
- Дизайн (1-2 недели) — макеты: главная, меню (список + карточка), бронирование, доставка, акции
- Бэкенд (2-4 недели) — инфоблоки, компоненты меню и бронирования, интеграция с POS-системой, CRM, обработка заказов
- Фронтенд (1-3 недели) — адаптивные шаблоны, оптимизация изображений, формы заказа и бронирования, AJAX-обновление статусов
- Интеграции (1-2 недели) — POS-система, агрегаторы доставки, SMS-уведомления, платёжная система
- Тестирование (1-2 недели) — функциональное, тестовые заказы через POS, мобильное тестирование, нагрузочное
- Запуск (3-5 дней) — деплой, мониторинг синхронизации с кассой, проверка на реальных заказах
| Масштаб проекта | Ориентировочные сроки |
|---|---|
| Сайт-витрина с меню и бронированием | 3-5 недель |
| Сайт с онлайн-заказом и интеграцией POS | 6-9 недель |
| Полная система: заказ, POS, агрегаторы, мультиязычность | 8-12 недель |
Сроки зависят от выбранной POS-системы (iiko интегрируется быстрее r_keeper за счёт облачного API), количества языков и требований к личному кабинету клиента.







