Галерея оптики «ОпАрт» (opart.ru) — сеть магазинов в трёх городах. Продукция: солнечные очки, оправы и очки для зрения, в том числе под собственным брендом, контактные линзы.
Задача — превратить сайт-визитку в интернет-магазин с интеграцией с ИТигрис. Реализовать возможность заказа очков по рецепту и онлайн-запись к врачу, каталог с разными типами фильтрации для разных товарных групп, контроль остатков на трёх складах, личный кабинет с историей рецептов.
Сайт делали на «1С-Битрикс». Весь учёт заказчика уже вёлся в CRM ИТигрис — узконишевой системе для оптик со своей логикой и ограничениями. Сайт нужно было сделать так, чтобы он работал с данными из этой CRM: товарами, ценами и скидками, остатками по трём магазинам и складу, клиентами и записью.
Сейчас в каталоге сайта 54 400 товаров. Из них 47 000 — линзы для очков; 6500 — позиции, представленные в каталоге как самостоятельные товары; 900 — SKU для контактных линз.
Очки для зрения это не обычная товарная позиция: покупатель выбирает оправу, а затем подбирает к ней две линзы — для правого и левого глаза — иногда с разными диоптриями, цилиндром и осью. Стандартный Битрикс под такую логику не заточен, поэтому корзину, оформление заказа и карточку товара пришлось перепроектировать под составной товар.
То же самое с фильтрацией. У «ОпАрт» четыре товарные группы: солнцезащитные очки, оправы, линзы и аксессуары — и у каждой свои характеристики для поиска. Мы спроектировали два синхронных фильтра, быстрый и полный, с динамической подгрузкой значений, а для цветов написали алгоритм группировки по базовым оттенкам, потому что из CRM приходило 10 вариантов одного синего.
Кроме того, ИТигрис оказалась системой со своими ограничениями: в ней нет API для создания заказа, нет интерфейса для удобного просмотра остатков с группировкой по срокам, а на один номер телефона может быть несколько профилей клиентов. Все это пришлось решать кастомными механизмами.
В результате проект потребовал больше 1100 часов программирования: мы сделали движок расчёта сроков поставки с учётом логистики между городами, Vue-приложение для записи к врачу с синхронизацией с ИТигрис и реализовали десятки других доработок.
Дизайн сайта также разработан нашей командой, о нем расскажем в отдельном кейсе.
В ИТигрис хранится все, что есть в учёте: и обычные товары для витрины, и заказы под конкретного клиента, и диагностические линзы, и уценённые позиции, которые ещё не прошли оформление. Нам нужно было настроить выгрузку в четыре разные категории (солнцезащитные очки, оправы, линзы и аксессуары) так, чтобы на сайт попадали только те товары, которые действительно можно продавать через интернет.
Для разных групп товаров критерии были разными, поэтому правила выгрузки мы прописывали индивидуально для каждой категории. Например, для линз в исключения попадали товары со словом «диаг» или «diag» в названии, что соответствовало диагностическим линзам. Для других товаров искали служебные метки в разных полях CRM.
Параллельно нужно было решить проблему с названиями товаров. В CRM названия не подходят для витрины: они заполняются товароведами в том виде, в котором удобно для учёта, а не для покупателя.
Поэтому названия на сайте собираются автоматически из параметров: бренд, модель, цвет, размер. Для линз — бренд, цвет, диоптрии, сфера.

Для четырёх товарных групп фильтры различаются. У аксессуаров он простой — только по типу аксессуара, для других товаров более сложный. Например, у контактных линз есть Сфера, Цилиндр, Ось, Аддидация, Радиус кривизны.
Клиент хотел, чтобы фильтрация работала как на современных маркетплейсах: есть быстрый фильтр в виде плашек над каталогом и полный фильтр в развёрнутой панели. Они должны быть синхронны — выбор в одном мгновенно применяется к другому.
Например, если для контактных линз пользователь в быстром фильтре выбрал модель, то в выпадайке «Сфера» система покажет только те диоптрии, которые есть в наличии от этого бренда. А если затем открыл полный фильтр, там уже будет применён ранее сделанный выбор. Реализовали это через динамическую подгрузку значений с отслеживанием зависимостей между полями.

В быстром фильтре при выборе модели линз в выпадайке «Сфера» некоторые значения становятся неактивными

Выбранная модель остаётся выбранной в полном фильтре
Кроме того, для контактных линз сделали вывод важных параметров прямо в карточках товаров в списке.
Когда мы занялись каталогом, в CRM было около 50 названий цветов оправ в стиле «Золотистый металл», «Шампань», «Тёмный сиреневый», «Коралловый». Мы сохранили эти названия цветов и выводим их в карточках товаров. Но выводить столько цветов в фильтре было бы нерационально.
У каждого товара в CRM есть код цвета. Мы создали справочник, где менеджер один раз сопоставляет код из ИТигрис с базовым цветом в формате hex. Дальше система работает автоматически: она определяет по спектру, к какому базовому цвету относится оттенок, и группирует похожие.
Пример: если в фильтре выбран синий, в выдачу попадают все оправы с близкими по спектру оттенками — синий, голубой, темно-синий, бирюзовый. Пользователь видит один кружок синего цвета. Выбор цвета сократился с 50 до 10 внятных вариантов.

Для клиента было важно, чтобы товары на сайте выводились в определённом порядке. Он предоставил алгоритм сортировки, который учитывает множество факторов и работает по‑разному для каждой товарной группы.
Например, для оправ и солнцезащитных очков в выдаче чередуются разные цвета, на первых страницах должен быть определённый процент товаров с фотографиями, определённый процент — с фотографиями на лице. Новинки и бестселлеры поднимаются выше.
Другой пример: на странице аксессуаров каждый следующий аксессуар должен быть из другой подгруппы, чтобы создавалось разнообразие (футляр, потом спрей, потом салфетка, потом снова футляр, но другого цвета).

При каждом обновлении каталога сортировка применяется автоматически. Система рассчитана на то, что ассортимент может вырасти до очень больших объёмов, и сортировка должна работать без ручного вмешательства.
У разных типов товаров различается устройство карточек товаров.
Для контактных линз сделали классическую структуру SKU. Есть основная карточка товара-группы, а у неё — несколько торговых предложений с разными параметрами: диоптрии, радиус кривизны, количество в упаковке и т.д. У каждого SKU своя цена и остаток. Это стандартный для e‑commerce подход, и он хорошо подходит для линз, потому что у одной модели может быть много вариаций.

Для оправ реализована другая логика. Цвета — это не SKU внутри одной карточки, а отдельные товары, которые сгруппированы визуально. У каждого цвета своя страница с фото, артикулом и своим URL. Поэтому, когда пользователь внутри карточки товара выбирает другой цвет, происходит переход на URL товара в нужном цвете.


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

Сейчас бы мы, наверно, решали эту задачу с помощью анализа изображений нейросетью, но на момент разработки сайта таких инструментов не было, поэтому, чтобы система правильно подбирала порядок фото, мы внедрили систему нейминга всех файлов с фотографиями. Сотрудник, который делает фотографии, называет файлы по шаблону: в названии указан код очков и тип фото — «основное», «на лице», «ракурс». Скрипт при загрузке считывает название и расставляет картинки в нужном порядке. По неймингу же определяется наличие фото с лицом — такие товары попадают в специальные блоки на главной странице. Блок обновляется при каждой загрузке каталога автоматически.
В ИТигрис нет возможности хранить фотографии товаров, поэтому все фото всех товаров мы храним в Битриксе.
У клиента есть категория товаров, которые продаются по уценке из-за небольших дефектов. Раньше учёт вёлся в Гугл-таблице: там хранились цена со скидкой и причина уценки. Фотографии дефектов лежали в отдельной папке на Гугл-диске, названия файлов соответствовали коду товара.
Мы перевели весь процесс на сайт и полностью отказались от Гугл-таблицы. Теперь сотрудник ставит флаг «уценка» в карточке товара и указывает причину уценки и цену со скидкой прямо на сайте.
Фотографии дефектов система ищет в той же папке на Гугл-диске по коду товара и подгружает их в карточку, если находит. При этом фото дефектов не смешиваются с основными изображениями и открываются отдельно — по кнопке или ссылке.

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

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


Дальше система делит все доступные линзы на группы по потребностям: простые, тонкие, хамелеоны и так далее. Внутри выбранной группы пользователь на следующем шаге видит деление по ценовым сегментам: базовый, эконом, бизнес, премиум.
Когда пользователь завершает настройку, система собирает выбранные оправу и линзы в один составной товар и отправляет в корзину.
Стандартные компоненты Битрикса не поддерживают сборку товара из разных типов позиций с разными параметрами для каждого глаза — это полностью кастомная логика.
На складе заказчик держит только ходовые линзы для очков с распространёнными параметрами. Остальные покупают у поставщиков под конкретные заказы. Наличие таких линз в заказе увеличивает срок готовности очков на срок доставки линз до нужной оптики. Этот срок для разных линз различается. Чтобы покупатель, выбрав линзы, сразу видел срок готовности очков, нам надо было завести всю информацию о покупных линзах в систему.
Клиент предоставил Excel-файл из 20 строк, где каждая строка описывала «семейство» линз: бренд, тип, базовые характеристики. Но одна такая строка на практике превращается в сотни разных позиций, потому что одна модель линз выпускается с разной оптической силой, аддидацией, покрытием и другими параметрами.
Мы написали скрипт, который разворачивает каждую строку-семейство в полный каталог: перебирает все возможные (с заданными ограничениями) комбинации и создаёт отдельную позицию с ценой и параметрами. Использовали алгоритмы проверки на уникальность — с помощью ИИ, чтобы не плодить дубли. В итоге из 20 строк сгенерировано более 46 000 уникальных позиций.
Управление ценами сделали через группировку. Каждая строка в исходном Excel — это группа. У группы своя цена. Когда нужно обновить цены (поставщик поменял стоимость), менеджер меняет цифру в Excel-файле, и скрипт обновляет цены у всех сгенерированных позиций, принадлежащих этой группе.
По требованию ТЗ пользователь на этапе выбора товара должен видеть его срок доставки в выбранный салон оптики. У клиента один склад и 3 салона оптики в разных городах.
Для очков для зрения срок складывается из двух составляющих: наличие или срок поставки самой оправы и срок поставки линз. Мы сделали так, что срок показывается отдельно для оправы, а затем, на этапе подбора линз и выбора ценового сегмента, система показывает срок поставки уже для линз (то есть по сути для очков с выбранными линзами).
Срок поставки товара зависит от многих факторов. Например, оправа может быть в наличии в Ростове, а линзы — на складе в Пятигорске. Тогда линзы нужно сначала переместить со склада на магазин в Пятигорске, а потом отправить в Ростов. Мы должны автоматически учитывать эту логистику, когда показываем срок поставки очков.
Кроме того, на складе клиент держит только самые ходовые линзы — с популярными диоптриями. Если линза с редкими параметрами и её нет на складе, то она заказывается у поставщика. Срок доставки заказной линзы зависит от поставщика и конкретной модели.
Клиент предоставил нам информацию о сроках поставки разных линз от разных поставщиков, и на базе этого мы рассчитали срок доставки до нашего склада всех 46 000 заказных линз.


Чтобы правильно рассчитывать срок готовности очков даже с заказными линзами, мы написали движок расчёта сроков. При импорте товаров из CRM каждому товару один раз присваивается срок с учётом всех факторов: наличия на складах, логистических цепочек между городами, условий поставщика. Срок хранится в карточке товара и пересчитывается при изменении статуса. Например, если заказная линза появилась на нашем складе — система пересчитывает срок, и вместо 14 дней становится 2–3 дня.
В корзине заказы группируются по оптикам и по срокам готовности — как в маркетплейсах.


Типовая корзина Битрикса не поддерживает составной товар «оправа + две линзы с разными параметрами для каждого глаза». Поэтому мы написали отдельное приложение на Vue для оформления заказа с нуля.
При этом важно было не захардкодить логику. Вся штатная функциональность Битрикса — способы доставки, оплаты, свойства заказа — должна была сохраниться и управляться из админки. Мы сделали так, что менеджер добавляет новую доставку в админке Битрикса, и она автоматически появляется в кастомной корзине.
Мы сделали так, что в корзине можно менять параметры линз — диоптрии, толщину, покрытие и т.д. При изменении система пересчитывает цену без перезагрузки страницы.

Для выбора пункта выдачи использовали модуль доставки Яндекса. Модуль предназначен для работы со стандартным шаблоном оформления заказа в Битриксе, но мы интегрировали его в наше Vue-приложение, чтобы он работал внутри нашего кастомного интерфейса.
Первый — простая быстрая запись: короткая форма обратной связи, пользователь оставляет имя и телефон, менеджер перезванивает.
Второй — полноценное Vue-приложение, интегрированное с ИТигрис. Приложение забирает из CRM услуги, врачей и свободные слоты времени, а отправляет обратно готовую запись.
Главная техническая сложность — разная логика записи в CRM и на сайте. В ИТигрис запись завязана на врача: сначала выбирается специалист, потом его услуги и свободные слоты. На сайте по требованиям клиента порядок должен быть обратный: сначала услуга, потом врач. И при этом даты для записи должны показываться до выбора врача — хотя в CRM каждая дата жёстко привязана к конкретному специалисту.
Как решили: приложение собирает из CRM слоты всех врачей по выбранной услуге, объединяет их по датам и показывает пользователю единую сетку дат. Когда пользователь выбирает время, приложение находит, какому врачу принадлежит этот слот, и бронирует его.
Для выбора пункта выдачи использовали модуль доставки Яндекса. Модуль предназначен для работы со стандартным шаблоном оформления заказа в Битриксе, но мы интегрировали его в наше Vue-приложение, чтобы он работал внутри нашего кастомного интерфейса.


В API ИТигрис на момент начала работы не было чёткой структуры с идентификаторами товаров. Нельзя было просто взять ID из CRM и обновить по нему позицию на сайте. Нужно было придумать, по каким признакам определять, что товар из новой выгрузки — это тот же товар, что уже есть на сайте.
Решение: собрали составной ключ из нескольких полей: бренд, модель, цвет, размер. Если все эти поля совпадают — значит, это один и тот же товар, и его нужно обновить, а не создавать новый. При изменении любого из этих полей создаётся новый товар.
Заказ из корзины нельзя отправить напрямую в CRM. Это ограничение самой системы, и мы ничего не могли с ним сделать.
Решили так: заказ в виде письма уходит менеджеру нужной оптики. Менеджер вручную создаёт заказ в ИТигрис и прописывает в комментарий номер заказа с сайта. Сайт периодически проверяет выгрузку из CRM (используем копию базы). Если номер заказа с сайта появляется в выгрузке — система находит соответствующий заказ на сайте и синхронизирует его статус. Покупатель видит актуальную информацию: «заказ принят», «линзы заказаны у поставщика», «готово к выдаче».
В ИТигрис на один номер телефона может быть несколько профилей клиентов. Например, родитель и ребёнок — оба ходят в одну оптику, у обоих заказы на один телефон, но это разные люди с разными рецептами.
При регистрации на сайте система проверяет CRM по номеру телефона. Если найден ровно один профиль — автоматически синхронизирует нового пользователя сайта с этим профилем. Если найдено несколько — предлагает пользователю выбрать, к какому профилю привязаться. Если пользователь пропускает синхронизацию, система позволит сделать заказ, но при повторном входе будет предлагать синхронизацию.

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

Взаимодействие с ИТигрис было организовано через REST API. И на первых этапах работы нам сильно не хватало его возможностей. Позже нам удалось получить доступ к копии базы данных в Яндекс.Cloud, которая синхронизируется с определённой периодичностью и доступна только для чтения.
Мы использовали оба канала. API — для оперативных данных: остатки, цены, запись. Базу — для сложных выборок, где нужно много данных или нестандартные группировки: например, история заказов для личного кабинета или массовая выгрузка товаров с фильтрацией. Кроме этого, в базе есть идентификаторы, которые нужны нам для определённых задач при выводе товаров в личном кабинете пользователя на сайте.
Клиент получил инструмент, который позволяет продавать очки по рецепту онлайн — с подбором линз, расчётом сроков поставки с учётом логистики и интеграцией с существующей CRM ИТигрис.
Пользователи могут выбрать оправу, настроить линзы для каждого глаза по своему рецепту, увидеть реальный срок готовности в выбранном салоне и оформить заказ. Онлайн-запись к врачу работает через Vue-приложение, синхронизированное с CRM.
Проект потребовал более 1100 часов программирования.