Как мы учили кассу самообслуживания принимать деньги

Наша компания уже около двух лет занимается кассами самообслуживания, и по этому пути мы шли итеративно. В одном из своих предыдущих постов я рассказывал какие сложности возникли во время наших первых запусков — запусков двух магазинов SPAR в Калининграде. При этом и в SPAR, и в несколько последующих магазинов, мы установили кассы, которые принимают оплату только по банковской карте. Это решение имело плюсы как для нас (такое решение быстрее запустить), так и для магазинов (они смогли разделить потоки покупателей в соответствии с типом оплаты). Но мы понимали, что рано или поздно нам придется взяться за подключение модуля наличных к кассе самообслуживания. Всем, кому интересно узнать как это было, добро пожаловать под кат.20d8a02cd1f54f9c9bc1905a16f53022.jpg

Основным комплексом для приема наличных для касс шведской компании ITAB, разработкой под которые мы занимаемся, является комплекс японской компании Glory с пафосным названием Cash Infinity 10.

f4ae9700764a44caa3c621dcdb114d05.pngСостоит он из двух отдельных устройств — купюро- и монетоприемников, соединенных с контроллером.

Монетоприемник — это устройство с точками ввода и вывода монет, транспортером, валидатором и 8-ю одинаковыми монетницами. После того, как монета принята, она по транспортеру доставляется в валидатор, который определяет ее номинал и отправляет в соответствующую ей монетницу. Если номинал не определен, то монета возвращается через точку вывода монет. Точно так же возвращается и любой мусор — скрепки, ключи. Сами же монеты смело можно вносить горстью — монетоприемник совершенно спокойно обрабатывает большое количество монет разных номиналов. Монетницы работают и на input, и на output, соответственно сдача покупателю выдается теми же монетами, которые внесли в кассу предыдущие покупатели.

00ba1cee91fa4de68810f0cc8c386c4e.jpg

Купюроприемник устроен по-другому. Купюра — это тонкая бумажка, и если она сложена куда-то, то забрать ее уже не получится, а значит реализовать систему input/output для купюр сложнее. Сложнее, но можно. Для этого в купюроприемнике размещены специальные барабаны: распознанная купюра наматывается на такой барабан и может быть точно так же снята для сдачи. Проблема лишь в том, что пространство в устройстве ограничено, поэтому в нем размещено всего 3 барабана, на которые логично устанавливать самые ходовые купюры. Все остальные купюры (а также ходовые купюры в случае переполнения соответствующего барабана) попадают в кассету, которую сотрудник в любой момент может изъять в закрытом виде и внести все ее содержимое в главную кассу.

eedf7319a4bf479cae0a8b55e5292662.jpeg

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

1. Интерфейс покупателя для оплатыВершиной айсберга (и как впоследствии оказалось, одной из самых понятных задач) являлось создание интерфейса покупателя для приема наличных. Выбор наличных как способа оплаты переводит устройства из спящего режима (IDLE) в режим приема наличных (WAIT INTSERTATION), информация обо всех внесенных номиналах передается в нашу кассовую программу.ba05236cd55a401bb78e213d62e4f3c6.png

Мы столкнулись с трудностью — в тот момент, когда внесенная сумма становилась больше суммы чека, мы не могли послать команду выдачи сдачи на нужную сумму: такой команды в API просто нет. Сделано это потому, что обработка внесенной купюры и передача ее во внешнюю систему — длительный процесс, а значит в тот момент, когда внешняя система узнала о достаточности суммы часть купюр или монет еще могут быть на обработке. Соответсвенно мы отправляем в контроллер команду CASH REQUEST, которая говорит контроллеру о том, что когда все купюры будут внесены и обработаны, можно будет перейти к выдаче сдачи. И уже сам контроллер возвращает нам событие WAIT FOR CHANGE, после которого мы показываем экран выдачи сдачи.

80f8decd088e439bafc3ddf558a6f793.png

2. Настройка емкостей для всех номиналов С монетами все кажется просто: 8 емкостей, и как раз монеты 8 номиналов в ходу. Правда, копейку Glory не принимает, как не принимает и большие 10-рублевые монеты. Оставшуюся емкость можно либо отдать под один из ходовых номиналов, либо отдать её под излишек монет. Мы выбрали второй вариант. Излишек или миксер собирает в себя монеты разных номиналов в случае переполнения их емкостей. Для купюр все немного проще: предстояло выбрать, какие 3 номинала разместить на барабанах. После консультаций с клиентом мы выбрали купюры в 50, 100 и 500 рублей. Соответственно, тысячными купюрами касса сдачу не даёт.Один из ключевых вопросов настройки: при каких значениях блокировать оплату наличными на кассе самообслуживания? Не хочется допускать ситуации, когда мы в очередной раз отправим контроллеру команду CASH REQUEST, а он не сможет выдать сдачу по причине отсутствия необходимых номиналов. Не хотелось блокировать оплату сильно заранее, но и приближаться очень близко к проблемной ситуации не хотелось тем более. В итоге в силу опасений и более простого варианта реализации, мы выбрали более заблаговременное решение: оплата наличными блокируется, если хотя бы для одного номинала больше нет возможности выдать сдачу при внесении следующего по значению. То есть, если 500-рублевых купюр на барабане остается меньше 9 (именно столько нужно будет купюр для выдачи сдачи по маленькой покупке при внесении пятитысячной купюры) или 100-рублевых будет меньше 4-х, то оплата наличными автоматически будет заблокирована, а покупатель оповещен об этом на стартовом экране и на экране выбора способов оплаты.

43de5783b1ca4144b4e15cb61adda958.png

10663a844f1e4367816303fd7fb20543.png

Кроме того, оплата наличными автоматически блокируется при приближении переполнению кассеты с наличными или излишка монет.

3. Индикация о недостатке или избытке купюр и монет Задача системы индикации — оповестить сотрудника магазина о приближающейся проблемной ситуации. Механизмов оповещения у нас два: фонарь-индикатор над кассой и отображение состояния наличных на кассе в интерфейсе сотрудника магазина. Логика такая: как только помощник видит, что между покупками на кассе фонарь начинает мигать зеленым светом, то нужно проверить состояние наличных. Более детальную информацию он может посмотреть в режиме работы с наличными.b0c432282a184a09aa855897e489c7ea.png

В системе индикации состояния, не требующие внимания, отображаются серым, близкие к проблемным — розовым, а критические (в результате которых оплата наличными будет заблокирована) — красными. Кажется, что напрашивался светофор с зелеными, желтыми и красными состояниями, но в этом пестром сочетании выделить что-то наиболее значимое сложнее.

Цвет зависит и от типа емкости. Например, переполнение монетницы — это плохо, ведь теперь все монеты будут сыпаться в излишек, а его емкость совсем небольшая. Поэтому 10-рублёвая монетница подсвечена розовым. Переполнение барабана напротив — очень хорошая ситуация, ведь есть чем выдавать сдачу. Да, лишние купюры будут отправляться в кассету, но емкость кассеты достаточна для того, чтобы их вместить.

Много частных вопросов было при расстановке границы: к примеру, при какой заполненности кассеты она должна подсвечиваться розовым? После нескольких мозговых штурмов мы установили это значение на уровне 75 процентов. Соответственно, в этот же момент начинает мигать фонарь над кассой. Аналогичные решения требовалось принять по всем емкостям для купюр и монет.

4. Огромный блок работы с кассовой дисциплиной Внесение и изъятие из-за перечисленных особенностей оборудования оказались совсем не простыми задачами. Даже несмотря на то, что во всех кассовых решениях нашей компании внесение и изъятие присутствует, здесь многие из принципов приходилось практически полностью переосмысливать.С внесением все в целом довольно понятно: после входа в режим внесения купюро- и монетоприемники готовы к приему наличных. Номиналы всех внесенных купюр и монет отображаются рядом с соответствующими емкостями. А после того, как все внесено, сотрудник должен нажать «Завершить внесение», проведя эту транзакцию через фискальный регистратор, который и напечатает документ о внесении.

feff07c06f524546a223a7f6e87a5dab.png

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

dede5dda08754ce9b12977ea5098ec52.png

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

С изъятием пришлось повозиться. Изъятий может быть 4 типа (чего в принципе не может быть на обычной кассе):

изъятие монет определенного номинала изъятие излишка монет изъятие купюр с барабанов изъятие кассеты с купюрами 37c231c9c8664a3fb033adef8602c131.png

Для каждого из них требовалось реализовать различные варианты поведения.

При изъятия монет из одной из монетниц выбирается сумма изъятия, кратная номиналу. Излишек монет изымается только целиком, изъять часть монет определенной суммы просто нет возможности, ведь все монеты в нем перемешаны Деньги из монетниц возвращаются не сразу, а после нажатия кнопки «Завершить изъятие». В этот же момент печатается документ изъятия.Для купюр так же как и для монет выбирается сумма изъятия, кратная номиналу на барабане, но после консультаций мы решили не выдавать эту сумму через купюроприемник, а отправлять ее в кассету. Соответственно и документ изъятия в этом случае не печатается, ведь деньги остались в кассе. При изъятии кассеты практически вся последовательность действий задана производителем. Мы отправляем команду на изъятие кассеты, в купюроприемнике разблокируется дверца, которая ее запирает. Сотрудник открывает дверцу, вынимает кассету. Вставить обратно он обязан пустую кассету, иначе контроллер хоть и вернется в значальное состояние IDLE, но не вернет событие об инвентаризации кассеты. Соответственно наш кассовый модуль не сможет напечатать документ изъятия, не сможет узнать состояние кассеты, поэтому в этом случае мы решили блокировать оплату наличными и требуем вставить пустую кассету. Это разумно, ведь у него совершенно не будет возможности узнать какая сумма осталась в кассете после изъятия. Выбор в случае изъятия кассеты был только в том, в какой момент печатать документ об изъятии. Вначале мы решили печатать его в момент установки пустой кассеты обратно, но после анализа процессов магазина стали печатать документ сразу после изъятия кассеты. Причина в том, что документ остается в главной кассе после внесения в нее денег. Главная касса далеко от касс самообслуживания, и сотрудникам приходилось бегать 2 раза: вначале с деньгами, потом с документом. Мы просто оптимизировали их работу.5. Обработка ошибочных ситуаций Ошибочных и пограничных ситуаций оказалось настолько много, что выделять всю эту титаническую работу во всего лишь один пункт с моей стороны немного неправильно. Приведу лишь несколько вопросов, на которые приходилось отвечать как разработчикам и тестировщикам, так и мне как проектировщику интерфейсов.Как будет обработано застревание монеты / замятие купюры? Как поведет себя касса в режиме покупателя и в режиме помощника? Что будет, если после того, как помощник нажал «Завершить внесение», он вдогонку бросит в приемник еще несколько монет? Отсутствие копеек накладывает свой отпечаток: что делать в ситуации, если итоговая сумма получилась с копейками (например, если не сработала скидка на округление чека)? Сам контроллер может находиться далеко не только в состояниях IDLE, WAIT INSERTATION и WAIT FOR CHANGE. Состояния меняются от каждого действия: есть COUNTING, включающийся в момент подсчета внесенных денег, есть CALCULATING CHANGE AMOUNT — включается на доли секунды в момент подсчета сдачи, есть DISPENSING, который включается в любой момент выдачи денег. Само собой есть и состояние ERROR, которое содержит детали произошедшей ошибки. А всего таких состояний ровно 30, причем в каждом из них становятся доступны или недоступны свои функции. Все это нужно не только запрограммировать, но и протестировать. Ну, а теперь пришло время рассказать о главном: лучшей характеристикой проделанной работы является не ее описание, а факт того, что полученный результат используется и выполняет свою функцию.

Ровно месяц назад мы запустили остров самообслуживания из 4-х касс с модулем приема наличных в супермаркете О«КЕЙ, расположенном в торговом комплексе «Галерея» в Санкт-Петербурге.

eec17039a490490285b89fc3824bd46d.JPG

155e091bb9084c99a87f6eb83adeb2a7.JPG

5e825402e4e646cbb8b5844283fd3aec.JPG

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

Лучше один раз увидеть, а ещё лучше — попробовать. Приезжайте в Петербург, приходите в О«КЕЙ в «Галерее» и оцените наше решение.

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

© Habrahabr.ru