Внедрение pet-проекта в реальный бизнес или как в одиночку написать ERP-систему для общепита

от автора

Всем добра! Данную статью меня побудило написать сильное желание, во-первых, зафиксировать некоторые результаты своего профессионального развития и личного опыта помимо основной работы, а также впервые на столь широкой сцене как Хабр, рассказать вам о нюансах развития своего небольшого pet-проекта, который впоследствии стал частью автоматизации сети заведений. Чтобы статья несла практический смысл и не выглядела как реклама, в ней не будут упомянуты ни название проекта, ни ссылки на его сайт и прочие сервисы.

C чего все началось

У меня есть брат, он с ребятами развивает сеть заведений общественного питания. Все началось в марте 2018 с его сообщения о том, насколько сложно мне будет написать веб-приложение, в котором будет отражен список заявок на бронирование столиков в баре. Стояла задача заменить функционал приведенной ниже Google-таблицы во что-то более приятное и расширяемое:

Исходная таблица с записями о бронировании столов

Исходная таблица с записями о бронировании столов

Мы обсудили основные необходимые моменты, которые были бы удобны работникам бара, и сошлись на написании MVP (фактически todo-листа) за ближайшие несколько дней. Думал ли я тогда, что из этого todo-листа вырастет целая система? Скорее нет. Но выросло что-то подобное.

Проектирование архитектуры

Из опыта у меня был лишь мучительный опыт фриланса (в основном JavaScript и верстка) и разработки сайтов на WordPress, а по основной работе я был начинающим Frontend-разработчиком в организации, которая в то время начала активно переносить фронт своих проектов на Angular Framework.

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

В качестве фронтенда для веб-приложения я выбрал Angular актуальной тогда версии v.4 с системой компонентов Angular Material. Опыта написания бэкенда, как и времени на его изучения под эту задачу не было, поэтому было решено поэкспериментировать с Firebase — ключевую роль в этом сыграло мое посещение с коллегами фронтенд-митапа в офисе Google летом 2017, где выступал докладчик из Google (нашлась ссылка на тот самый митап).

Первые версии, первый опыт и первые проблемы

Через неделю после обсуждений и выявления потребностей заказчика я показал первую версию списка бронирования столов — функционал был на уровне таблицы в Excel, а данные уже хранились в Realtime Database у Firebase:

Отображение структуры БД в виде дерева

Отображение структуры БД в виде дерева

Никакой аутентификации пользователей, логирования и прочего тогда даже не подразумевалось- это была обычная веб-страница, которую выложили в сеть и дали к ней доступ персоналу заведения (в плане архитектуры приложения — один Angular Module, один Injectable-сервис и пара-тройка компонентов). А поскольку в заведении назревала острая необходимость в использовании хоть какого-то средства для ведения заявок на бронирования, эту сырую версию с косяками и ошибками сразу же взяли в работу.

Одна из самых первых реализаций списка бронирования

Одна из самых первых реализаций списка бронирования

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

Также одна из ранних реализаций

Также одна из ранних реализаций

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

Для удобства работы, помимо списка столов было решено визуализировать схему рассадки гостей в заведении в виде схемы — по аналогии с тем, как она выглядела в системе Quick Resto (в этих же заведениях она уже использовалась для учета заказов и работы с кассовым терминалом):

Конструктор схемы зала (на скриншоте приведен режим редактирования)

Конструктор схемы зала (на скриншоте приведен режим редактирования)

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

Проблема 1: «один телефон на всех»

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

Проблема 2: Ошибки при внесении данных о бронировании

Со временем ребята столкнулись с еще одной проблемой — постоянное внесение номера телефона и данных клиента в заявку на бронь затратно по времени и влечет ошибки/опечатки. Мне предложили подумать о том, чтобы  в приложении была возможность доступа к контактам с мобильного телефона.

Но! это же обычная вэб-страница, на тот момент даже не PWA-приложение, и ни о какой интеграции с нативными функциями телефона не могло быть. В резульате обсуждений было принято вынести данные клиентов в отдельный справочник. Так созрела идея добавить второй модуль — «Клиенты», а при создании очередной заявки на бронь появилась возможность выбора из ранее добавленных данных.

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

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

Время шло, проект начал разрастаться

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

В период карантина и почти полного простоя общепита из-за COVID-19, я принял решение расширять возможности проекта, доведя его до полноценной системы учета и управления ресурсами заведения. К этому времени как разработчик я уже набрался большего опыта и посчитал это посильной задачей.

Работу над расширением проекта я начал с базовых модулей для учета ресурсов.

  • Что? Сами ресурсы: в быстрые сроки была реализована первая версия модуля «Номенклатура» с подразделами для учета «Ингредиентов», «Полуфабрикатов», «Модификаторов», а позднее там добавились «Стоп-листы».

  • Где? Где будут вести учет: для этого я выделил в структуре проекта логическую сущность «Предприятие», в которое вошли «Места реализации» (заведения), «Места приготовления» (кухня) и «Склады».

  • Когда? Списание ресурсов происходит через привязку связку модуля «POS-терминал» с данынми «Места реализации», который позволяет осуществить продажу готовых блюд и напитков (по аналогии с тем как работают системы rKeeper, iiko, Quick Resto).

В связи с тем, что другие модули в процессе работы требовали большого количества переиспользуемых данных, я вынес работу с подобными данными в «Справочники» — такие как «Типы оплат», «Фасовки», «Ставки НДС» и прочие.

Мне повезло, что при добавлении каждого из модулей сразу же была возможность применения его в реальных бизнес-процессах у тех заведений, где использовался проект. С того момента для себя я начал гордо называть этот проект «Системой», однако в действительности до системы было еще совсем далеко.

Механика взаимодействия с БД

Вкратце для того, чтобы внести представление о взаимодействии фронтенда с данными из Firebase Realtime Database, перечислю несколько примеров методов, которые используются в сервисах Angular-проекта для общего понимания:

Пример получения данных из Realtime Database:

/**  * Возвращаем список всех единиц бронировния по заведению в компании  *  * @param {string} companyId  * @param {string} divisionId  * @return Observable<ReservationItem[]>  */  public getReservationItemsByDivision(companyId: string, divisionId: string): Observable<ReservationItem[]> {      return this._db.list<ReservationItem>(`__идентификатор_ноды_дерева__${companyId}`, (ref: DatabaseReference) => {          return ref.orderByChild('divisionId').equalTo(divisionId);      }).snapshotChanges()        .pipe(           map((changes: SnapshotAction<ReservationItem>[]) =>              changes.map((c: SnapshotAction<ReservationItem>) => {                 const data: ReservationItem = c.payload.val() as ReservationItem;                 const key: string = c.payload.key;                 return {key, ...data};               })            )         )     );  }

Пример обновления данных в Realtime Database:

/**  * Обновление заявки на бронирование  *   * @param {ReservationOrder} reservationOrder  */ public updateReservationOrder(reservationOrder: ReservationOrder): Promise<void> {    return this._db.object(`__идентификатор_ноды_дерева__`)       .update(reservationOrder); }

Пример удаления данных из Realtime Database:

/**  * Удаление заявки на бронирование  *  * @param reservationOrder  * @return Promise<void>  */ public deleteReservationOrder(reservationOrderId: string): Promise<void> {    return this._db.object(`__идентификатор_ноды_дерева__`).remove(); }

Firebase наше всё? (нет)

C ростом количества модулей произошла некоторая неизбежность (из-за лени и отсутствия времени) — в проекте значительно увеличился уровень их интеграции с Google Firebase. Ниже приведу основные, задействованные в ходе разработки:

  • Firebase Authorization
    Авторизация пользователей по номеру телефона, email

  • Firebase Realtime Database
    Хранение всех данных в виде структурированной БД (древовидной), все сервисы по каждой сущности хранятся в дереве, а запрашиваются из БД через сервисы внутри ядра системы

  • Firebase Functions
    Функции обработки загруженных изображений (создание 3 разных превью в различных размерах при загрузке фото), сам код функций выполняется на удаленных серверах Google

  • Firebase Cloud Messages (ранее Google Cloud Messages)
    Отправка push-уведомлений (используется внутри MessagingService в проекте), работает в браузере и на Android-устройствах

  • Firebase Filestorage
    Хранение статичных файлов изображений

Полагаться только лишь на один сервис от Google было бы опрометчиво, поэтому постепенно в проекте начал появляться бэкенд. Так, модуль эквайринга для взаимодействия с банком и проведения платежей был написан на PHP (Symfony) моим товарищем бэкендером, а интеграция с ККТ на текущий момент запланирована через внешний сервис kkmserver.

Сейчас бизнес-логика и структура БД стабильна по своей структуре, однако вижу смысл в постепенном и частичном уходе от Firebase в силу ряда ограничений в гибкости при разработке — начиная от реализации структуры новых «веток» в структуре баз данных, заканчивая отсутствием полного контроля над данными в приложении (всегда есть опосредованный провайдер).

На текущий момент сам Google Firebase в России работает стабильно (за исключением республики Крым — там сервис не доступен), но в силу ряда известных всем ограничений, привязка российских карт к платежному аккаунту Google невозможна.

Развитие отдельных сервисов

Появление учета ресурсов позволило создать отдельное веб-приложение в общем доступе для просмотра меню заведения по QR-коду и помогло барам быстрее адаптироваться к условиям новой реальности после COVID-19 через внедрение электронного меню. Так появился первый отдельный сервис на базе «Системы» — назову его условно LINK. С его помощью гости заведения или пользователи сайта могут перейти по короткой ссылке и посмотреть информацию и цены блюд и напитков. Со временем удалось внедрить в LINK возможность оплаты через эквайринг банка.

На появление новых способов взаимодействия в гостями общепита меня вдохновляло и развитие технологий — в 2021 я взялся за изучение кастомных веб-компонентов и Angular Elements, и решил «завернуть» функционал бронирования столика со стороны гостя в виджет на сайте — наподобие тех, которые позволяют внедрить онлайн-мессенджеры на сайт или форму записи в салон красоты. Это занятие привело к появлению нового сервиса, назовем его WIDGETS с набором виджетов для размещения на сторонних сайтах.

Текущее состояние проекта

На сегодняшний день мой pet-проект («Система») состоит из 65 модулей, 11 репозиториев и по потреблению ресурсов (на 5 заведений) еще умещается в бесплатном тарифе Firebase — суммарный объем хранилища на момент написания статьи порядка 4,5 Гб. На текуший день в проекте есть система управления доступом, с возможностью создания учетных записей для персонала и разграничением прав работника в зависимости от его роли (должности), не исключаю что со временем придется перейти на IDM-систему вроде Keykloak.

Так сейчас выглядит основной экран навигации после входа в проект

Так сейчас выглядит основной экран навигации после входа в проект

В состав проекта так же вошли мобильные приложения под iOS и Android для гостей и персонала заведений, которые реализованы по принципу One Codebase через использование Ionic Capactitor. Кодовую базу пока еще удается бережно хранить в актуальном состоянии (пару недель назад состоялся успешный переезд на Angular 16.2.5, частично переписал управление состоянием в проекте при помощи Angular Signals).

Конечно же, нельзя без ложки дёгтя

Надо понимать, что в основе проекта сейчас используется Firebase — если работу со статикой (изображениями) можно переписать с «Functions + Storage» на PHP или NodeJS (express.js), то с основными данными сложнее — так как данные хранятся в виде дерева — типичный NoSQL со своими плюсами, бубном и минусами, и к сожалению отсутствием адекватной возможности создания сложных запросов. Вероятно, для более оптимальной работы на широкие массы, со временем придется мигрировать на MySQL/PostgreSQL.

На текущем этапе понимаю, что в голове уже все перестаёт умещаться, поэтому большое внимание уделяю пользовательской и технической документации по проекту — для написания инструкций по использованию обратился к специалисту, а техническую документацию по проекту пока позволяет реализовать npm-пакет @compodoc/compodoc.

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

Выводы и планы на будущее

Во время работы мне удалось пощупать и популярные на текущий день в РФ системы управления ресторанами и посмотреть как они устроены, и очевидно, что стремиться моему проекту еще есть к чему — важнейшную роль тут играет не столько мое желание, сколько потребности в автоматизации бизнес-процессов у заведений.

Что будет интересно доработать — полноценно встроить работу с онлайн-кассами, интегрироваться с крупными игроками либо внедрить импорт/экспорт основных данных с проектом, реализовать монетизацию (идей много, но не в рамках этой статьи).

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

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Нужны ли pet-проекты разработчикам?

98.04% Да100
1.96% Нет2

Проголосовали 102 пользователя. Воздержались 5 пользователей.

ссылка на оригинал статьи https://habr.com/ru/articles/768692/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *