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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

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

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

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

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

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

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

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

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

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

Через неделю после обсуждений и выявления потребностей заказчика я показал первую версию списка бронирования столов - функционал был на уровне таблицы в 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-проекты разработчикам?
100% Да 3
0% Нет 0
Проголосовали 3 пользователя. Воздержался 1 пользователь.
Источник: https://habr.com/ru/articles/768692/


Интересные статьи

Интересные статьи

Итак, представим, то вы находитесь в России и вдруг понимаете, что неплохо было бы купить виноградник в Италии. Безумие? Нет! Сегодня вполне возможно приобрести в метавселенной NFT токен, эквивалентны...
Один и тот же язык программирования одинаков в любой стране. Нет отличий Python в Китае от Python в Великобритании. Другое дело - модели ведения высокотехнологичного бизнеса. Они могут сильно различат...
Давным-давно в далекой Галактике, когда сестры Вачовски еще были братьями, искусственный разум в лице Архитектора поработил человечество и создал Матрицу… Всем привет, эт...
Привет, меня зовут Евгений. Я разрабатываю инфраструктуру поиска Яндекс.Маркета. Хочу рассказать, как graceful degradation помогает нам обрабатывать больше запросов, чем ...
Что случилось, когда венчурный инвестор рассказал всю правду о стартапе с поддержкой Марка Цукерберга Первое правило венчурного капитала Кремниевой долины – никогда не оскорблять стартапы. И...