Умные кассы для «ВкусВилла»
С точки зрения пользователя процесс онлайн-покупки продуктов у «ВкусВилла» выглядит просто — открыл приложение (самого «ВкусВилла» или партнеров доставки и агрегаторов), выбрал нужные товары, оплатил, дождался, получил. В этом посте мы немного расскажем о том, как работают кассы «ВкусВилла» изнутри, как они участвуют в процессе онлайн-заказа и как они связаны со всей остальной экосистемой магазина.
Изначально у нас было довольно классическое софтовое решение для продаж в магазине с помощью различных кассовых устройств, которые есть у «ВкусВилла», включая особенности продуктовой сети, в частности, системы лояльности. Но из-за пандемии коронавируса магазинам пришлось довольно оперативно добавить в работу онлайн-заказы и доставку товаров. Соответственно, обработку и оформление этих заказов нужно было как-то интегрировать в существующую экосистему.
Мы доработали кассу таким образом, что помимо выполнения своих основных функций, она стала еще и самостоятельным терминалом по сборке (ну, разве что сама пока не собирает товары в пакет) и смогла получать заказы, регистрировать товары для сборки, подтверждать завершение заказа и выдавать все нужные чеки.
Для начала расскажем про архитектуру нашего кассового решения. С точки зрения взаимодействия решение имеет клиент-серверную архитектуру. С точки зрения развертывания оно ближе к десктопному, т.к. обе части (и клиент, и сервер) располагаются локально, на каждой отдельно взятой кассовой машине. Ядро написано на Java, а фронт — на JavaScript (React). Для хранения данных используется PostgreSQL. Если говорить про обвязку, то всё, что касается оборудования, написано на Go, API — на Python.
Клиент-серверная архитектура была выбрана не случайно — такая реализация позволяет очень оперативно и гибко дорабатывать видимую для покупателя часть кассы, при этом сохраняя основную логику «под капотом» без изменений. За счет этого получаем более легковесные обновления при возможности реализации кастомных дизайнов интерфейсов. Также можем иметь на кассе одновременно несколько графических интерфейсов, и в течение нескольких минут, путем конфигурации, можем изменить отображаемый интерфейс, например, превратить кассу с кассиром в кассу самообслуживания.
Помимо этого, такая архитектура позволяет (при размещении нашего кассового решения удаленно) подключаться с разных «клиентов» для проведения покупок, например, с планшетов курьеров. В таком случае чек будет получен по месту нахождения серверной части и может быть отправлен покупателю через удаленные каналы связи. Это может использоваться для организации небольших кассовых ферм.
Стек технологий, который мы использовали при реализации, позволил нам сделать кроссплатформенное решение. И на большинстве точек в качестве ОС используется Linux Ubuntu, что позволяет дополнительно экономить на лицензиях.
Также на сервере кассы есть внутренняя база на PostgreSQL, где хранится информация, необходимая для работы кассы в режиме оффлайн (о номенклатурах, текущей смене, чеках за последний месяц и т.п.). Информация о продажах в реальном времени передается через интеграционные потоки во «ВкусВилл» для отслеживания остатков в магазинах и выводе информации о чеках и остатках покупателям.
Адаптация кассы для работы с заказами
Для того чтобы понять объем доработок на кассе, нужно рассмотреть процесс онлайн-заказа покупателя.
Под процессом мы понимаем, собственно, путь заказа с момента оформления и до его доставки покупателю.
Вот как это выглядит на схеме:
1. Оформление заказа покупателем
Содержимое заказа попадает на отдельный сервер «ВкусВилла», в котором под запрос доставки была изменена структура БД. Когда заказ оформлен, после попадания на сервер его данные аккуратно раскладывается в БД.
2. Отображение заказов на кассе
Для вывода данных о заказах на кассе был реализован особый режим отображения, со скрытым доступом от покупателей. Это особенно актуально для касс самообслуживания, т.к. обычный покупатель в магазине не должен иметь возможности зайти в заказы и произвести какие-либо действия с ними. При переходе в данный режим касса забирает информацию о заказах с сервера и выводит её продавцу. В этом случае касса выступает в роли терминала обслуживания на стороне магазина: она видит, какие заказы поступили от клиентов в данный магазин, приоритезацию этих заказов, на периодической основе обновляет список заказов и отображает данные по ним.
3. Печать сборочного чека
Когда сборщик выбирает себе конкретный заказ, умная касса печатает список в соответствии с местом выкладки товара в данном магазине. В таком списке все отмечено именно по местам выкладки, с актуальным на момент печати списка количеством товара.
Это решение позволяет собрать заказ в конкретном магазине максимально быстро, без блужданий по отделам туда-сюда за дополнительной вкусняшкой по кругу.
4. Регистрация собранных товаров
После сборки корзины кассир-сборщик возвращается к кассе и сканирует штрих-код со сборочного чека. После этого корректирует позиции заказа в соответствии с собранными товарами и согласует это с покупателем. Затем прикладывает к сканеру уже свой собственный бейджик — это решает сразу несколько задач. Во-первых, мы знаем, кто какой заказ собирал. Помните, как раньше на некоторых видах пищевой продукции можно было встретить штамп ОТК и пометку «Сборщик Иванов П.В.». Здесь что-то подобное, только видим это лишь мы в системе, и можем повлиять на качество сборки конкретного сотрудника, в случае появления большого количества рекламаций на обработанные им заказы. Во-вторых, на качество каждой сборки подвязана система мотивации сотрудников — это обеспечивает добавочный контроль качества.
После подтверждения сборки касса возвращает на сервер статус заказа ГОТОВ, и наступает очередь курьеров. Пеший курьер или таксист могут видеть заказы ещё на этапе сборки и заранее забронировать их под себя для доставки. После перевода заказа в статус ГОТОВ они могут брать данный заказ в работу. Если же покупатель решил забрать заказ самостоятельно, то он на этом этапе получает уведомление о готовности.
5. Выдача заказа
В зависимости от типа доставки выдача заказа может осуществляться курьеру (пешему, такси или одной из служб доставки) или непосредственно покупателю. При заказе покупатель может выбрать один из вариантов оплаты — онлайн или оффлайн-оплату (картой или наличными). Варианты доставки и оплаты по-разному комбинируются между собой, и тут есть нюансы. Например, нельзя оформить доставку через Деливери и при этом выбрать оффлайн-оплату, из-за особенностей взаиморасчетов с агрегаторами.
Если покупатель в мобильном приложении выбрал онлайн-оплату, то в момент выдачи заказа (вне зависимости от типа доставки) касса инициирует списание суммы финального чека. Предварительно, при оформлении заказа, средства на карте блокируются на сумму заказа.
Списывать сразу сумму набранной пользователем корзины нельзя — каких-то товаров может не оказаться, а от каких-то пользователь откажется сам. Это как раз те случаи, когда вы заказываете во «ВкусВилле» еду через приложение, а вам перезванивают и уточняют, что каких-то позиций нет, кефира осталось всего 4, а вы по случаю личного праздника хотели 7, а у хлеба срок годности через 2 дня закончится.
В таких случаях сборщик согласует всё с покупателем, а потом финализирует заказ в соответствии с полученными данными. А касса, видя расхождения между первоначальным заказом и финальным, выдает предупреждение, и сборщик должен указать причину для каждого товара — почему так произошло, и подтвердить результат. Только после этого сборка заказа считается завершенной.
Если в течение двух часов за заказом не приходит покупатель, оформивший самовывоз, заказ попадает в режим разборки. И весь праздничный кефир отправляется обратно на полки магазина.
Умные тележки
А ещё у «ВкусВилла» есть такая штука как «умная тележка». Такая тележка позволяет пользователю собирать товары в магазине, сканируя их с помощью встроенного сканера, а затем, подъехав к кассе (самообслуживания или с кассиром), быстро вывести на её экран список того, что насобирал. На тележке есть специальный планшет, на котором покупатель может быстро авторизоваться при помощи бонусной карты, если захочет использовать все возможности программы лояльности.
Тому принципу, по которому тележки сейчас взаимодействуют с кассой, предшествовали несколько попыток, о которых мы скажем ниже.
Но сначала расскажем, как в принципе происходит авторизация покупателя на кассе с помощью QR-кода.
- Покупатель подходит к кассе и нажимает «Сгенерировать код».
- Касса отправляет запрос на центральный сервер «ВкусВилла» и сообщает свой уникальный номер и запрос на регистрацию нового QR-кода.
- На сервере регистрируется этот код, в таблице БД создается соответствующая запись, что в таком-то магазине создан такой-то QR-код.
- Покупатель видит сгенерированный QR-код на мониторе кассы, сканирует его с помощью мобильного приложения.
- Приложение (в котором пользователь уже залогинен) распознает зашитый в QR-код номер и посылает запрос на сервер.
- На сервере запись с распознанным QR-кодом дополняется данными о покупателе из приложения.
- Касса всё это время мониторит эту таблицу на предмет изменений и появления новых данных. Как только замечает их — выдает оповещение на мониторе об успешной авторизации.
- При этом, если за 30 секунд касса так и не обнаружила информацию о покупателе в таблице, то выводит сообщение об ошибке авторизации.
Так вот, авторизация тележек на кассах работает примерно так же, но со своими особенностями.
Теперь расскажем непосредственно о тех вариантах интеграции тележек с кассами, которые мы реализовали.
1 попытка. QR-коды
У тележек есть встроенный сканер штрих-кодов, с помощью которого покупатель сканирует товары. Это же сканер решено было использовать для того, чтобы считывать QR-код, выдаваемый кассой для авторизации покупателя. Касса в этом случае точно так же, как и покупателя, авторизует конкретную тележку, но в качестве информации о пользователе подхватывает список покупок, которые уже добавлены в тележку.
И вроде бы все здорово. Но проблема в том, что сам QR-код на мониторе кассы всегда выводится в одном и том же месте экрана. Сканер на тележке статичный, встроенный в корпус примерно на высоте 50 см от земли. А вот кассы в магазинах стоят на разной высоте — отличаться может и высота прилавков на обычных кассах, и размещение касс самообслуживания. Так что нормально считывать код во всех магазинах сети разом таким способом не вышло — сканер не выносной, его специально делали одним целым с тележкой. А поднимать вручную тележку в попытках правильно считать код — тоже так себе идея.
Поэтому решили попробовать вариант с RFID-метками.
2 попытка. RFID-метки
К кассе подключен считыватель RFID-меток. На каждой телеге закреплена собственная метка.
Когда покупатель подъезжает с тележкой к конкретной кассе, касса замечает метку и принимает данные с этой тележки, выдавая на экран список покупок.
Тут проблема была в том, что тестировалось это всё в условиях, близким к идеальным. На деле же тележку можно не очень ровно подвезти к кассе, особенно, когда посетителей много. Или в магазине кассы могут стоять чуть ближе друг к другу, и пользователь должен подъезжать к нужной кассе максимально близко. В условиях деятельности нормального магазина это работает с перебоями уже при средней загруженности.
3 попытка. Авторизация с ручным вводом номера телеги
Рабочим вариантом стал такой. Тележка сканирует товары покупателя, эти данные сразу записываются на сервер (напомним, у тележки, кроме сканера, есть специальный планшет). А ещё у каждой тележки есть уникальный номер, который хорошо видно покупателю, с этим бортовым номером она уже и авторизована в системе.
Поэтому, когда тележка передает данные о списке товаров на сервер, она передает еще и этот номер — например, тележка номер 33782 из такого-то магазина и список просканированных товаров.
Далее покупатель идет к кассе и выбирает опцию загрузки товаров из телеги, вводя лишь номер своей тележки. И всё, с кассы уходит запрос на сервер, а там уже есть данные о списке товаров в тележке с этим номером. Всё это выводится на экран кассы.
Конечно, покупатель может ввести номер тележки какого-то другого покупателя, стоящего рядом в очереди, и получить список его товаров. Это возможно, если мы говорим о кассе самообслуживания — в случае с кассиром подобное исключается.
О сложностях
Может показаться, что реализация кассового решения проста — прокачал основную логику, поддерживаешь API, и всё как-то работает само собой. Но всё немного не так.
Во-первых, специфика разработки для «ВкусВилла» такова, что от какой-то базовой идеи до рабочего внедрения проходит минимальное количество времени.
Во-вторых, почти каждый день у нас появляются какие-то новые запросы на модернизацию существующих функций и методов. К примеру, на старте кассир иногда ошибался в процессе сборки. Упаковывал что-то не то, чего-то меньше, чего-то больше. Как понимаете, на лояльность покупателя это влияет довольно сильно и всегда только в одну сторону. Поэтому мы прописали дополнительные проверки на этом этапе, чтобы фактическое содержимое пакета в собранном заказе соответствовало тому, что, собственно, хотел заказать покупатель.
Или подключение сторонних систем для заказа продуктов. Там у ребят тоже свои требования — например, в сумме заказа не должно быть копеек. В принципе, это важно. Поэтому мы сели и для сторонних доставок переписали всё так, чтобы в наших чеках не было этих копеек. Хотя, когда вы в магазине покупаете весовой товар по конкретной цене, это очень часто приводит к тем самым копейкам в чеке. Но раз для сторонней доставки так нельзя — значит, нельзя. О сложностях именно интеграции систем «ВкусВилла» с другими компаниями мы писали в этом посте.
Затем допилили оплату бонусами. На кассе при покупке в магазине человек же может использовать бонусы с карты лояльности или промокоды. При онлайн-заказах через приложение такого не было. Значит, что? Верно, надо сделать.
Если говорить не только про сложности доставки, а про разработку вообще, у «ВкусВилла» очень разношерстное оборудование по всем магазинам. И нам изначально нужно было закладываться таким образом, чтобы можно было максимально просто запуститься на всем этом парке оборудования.
Обычно кассовое решение поставляется в комплексном виде: вот оборудование, а вот ПО для него. Перед нами же стояла задача создать ПО под оборудование, которое не специфицировано в одном ключе: описание того, сколько на устройстве памяти и сколько ядер, плавает от магазина к магазину, у каждой модели свои особенности. Где-то стоит моноблок одного производителя, а где-то системник и тач-монитор — от другого.
Планы развития
Параллельно мы готовим запуск касс самообслуживания нового поколения, оснащенных мультитач-экраном, 2D-сканером для распознавания штрих-кодов, стереодинамиками, микрофоном для звуковых приложений и голосовых сервисов, а также 3D-камерой и другими системами помощи покупателям.
Кстати, мы сейчас активно расширяем команду, все описанные кейсы, доделки, работоспособность систем и интеграций — ее работа. Планируем использовать максимально свежие решения и идеи, добавлять новые возможности как в приложение для покупателей, так и во внутренние системы. А вот тут можно посмотреть полный перечень вакансий — hh.ru / наш карьерный сайт.
Буду рад ответить на ваши вопросы.