01
Сначала перечислите всё, что клуб выдаёт и ждёт обратно
В фитнес-клубе возвратными бывают шкафчики, механические и электронные ключи, браслеты доступа, полотенца, халаты, спортивная форма, мячи, ракетки, пульсометры и другое оборудование. Часть предметов закрепляется на один визит, часть на весь срок абонемента, часть арендуется для занятия. Если команда учитывает только шкафчики, соседние операции быстро возвращаются в блокнот и чат.
Составьте реестр по филиалам. Для каждой категории укажите единицу учёта, способ идентификации, место хранения, типовой срок, состояние, стоимость замены и правило залога. Полотенца могут учитываться по партиям, но дорогой браслет или ключ требует уникального кода. Индивидуальный шкафчик также получает собственную карточку, даже если его номер уже написан на дверце.
Отдельно определите границу между возвратным предметом и основным средством. Беговая дорожка живёт в контуре оборудования, обслуживания и ремонта. Переносной пульсометр, который выдаётся клиенту, участвует и в эксплуатации, и в аренде. Система должна связывать эти процессы, сохраняя одну операционную ответственность за выдачу.
- шкафчики и ключи;
- браслеты и пропуска;
- полотенца, халаты и форма;
- инвентарь для игр и тренировок;
- переносное измерительное оборудование.
02
Дайте каждому предмету устойчивый код
Код должен пережить смену наклейки, сотрудника и программного интерфейса. Используйте короткую схему с филиалом, типом и номером, например CHL-LOCKER-014 или YUN-TOWEL-032. QR или штрихкод ускорит поиск, но текстовый код остаётся читаемым при разряженном сканере. Название объясняет расположение: шкафчик в мужской раздевалке, ключ от корта или комплект для настольного тенниса.
У карточки есть рабочее состояние. Available означает готовность к выдаче. Maintenance временно закрывает предмет для ремонта, чистки или проверки. Lost фиксирует подтверждённую потерю, retired завершает эксплуатацию. Статус in use лучше вычислять по активной выдаче, а не хранить отдельным переключателем. Иначе аварийное закрытие окна может оставить шкафчик занятым без связанной операции.
Справочник принадлежит конкретной организации и при необходимости филиалу. Одинаковый код в двух юридических контурах допустим, но внутри одной организации должен быть уникальным. Составные внешние ключи и row level security защищают сеть от случайной связи шкафчика одного клуба с клиентом другого.
03
Оформляйте выдачу как отдельный жизненный цикл
Выдача связывает предмет, клиента, опциональный абонемент, сотрудника, время и срок возврата. Дополнительно фиксируются состояние при выдаче и короткая заметка. Такой объект отвечает на четыре вопроса: что выдали, кому, кто выдал и когда ожидается возврат. Комментарий в карточке клиента не даёт этой структуры и легко теряется среди звонков и продаж.
Сотрудник выбирает только доступный предмет. Перед записью сервер блокирует ресурс и проверяет активную выдачу. Частичный уникальный индекс остаётся финальной защитой, если два запроса прошли проверку почти одновременно. Один из них завершится успешно, второй получит понятный конфликт и обновит список. Это важнее визуальной блокировки кнопки, которая работает только в одном браузере.
Каждая команда получает idempotency key. Если связь пропала после нажатия, интерфейс может безопасно повторить запрос. Тот же ключ и тот же набор данных возвращают созданную запись. Тот же ключ с другим клиентом, предметом, сроком или залогом вызывает conflict. Так retry не превращается в скрытую новую операцию.
04
Свяжите срок с реальным процессом клуба
Для шкафчика на время визита срок можно вывести из ожидаемого завершения посещения. Для абонементного хранения он совпадает с датой окончания договора. Полотенце обычно возвращается при выходе, а спортивный инвентарь после брони корта. Общая модель хранит явную due date и позволяет продукту подставить разумное значение из связанного процесса.
Просрочка должна быть видна смене как рабочая очередь. Уведомление без следующего действия создаёт шум. Ресепшен проверяет открытый визит и контактирует с клиентом, склад выполняет инвентаризацию, руководитель решает по залогу. Для дорогого оборудования можно автоматически создать задачу через BPM, сохранив ссылку на исходную выдачу.
Не закрывайте выдачу автоматически только потому, что закончился визит. Событие прохода подтверждает выход человека, но не физический возврат предмета. Автоматизация может подсветить расхождение и поставить задачу. Подтверждение состояния остаётся у сотрудника или устройства, которое действительно приняло вещь.
05
Разделите залог, оплату аренды и штраф
Плата за аренду является доходом клуба. Залог остаётся обязательством вернуть деньги при выполнении условий. Штраф или удержание возникает после отдельного решения. Эти суммы могут проходить через одну кассу, но имеют разный экономический смысл. Одна строка sale с названием аренда и залог затрудняет возврат, сверку и бухгалтерский учёт.
При выдаче система сохраняет сумму и валюту залога как снимок. Изменение тарифа предмета завтра не меняет обязательство перед сегодняшним клиентом. Для положительной суммы сотрудник указывает способ и доказательство: кассовую смену и чек, terminal reference, банковскую операцию или внешний документ. Сервер не должен считать деньги полученными только по выбранному пункту интерфейса.
Отмена залога тоже требует основания. Бесплатная выдача по привилегии, корпоративному договору или решению менеджера фиксируется как waived. Причина помогает отличить разрешённое правило от пропущенной операции. В аналитике достаточно статуса и суммы, а служебная ссылка на чек остаётся в защищённой карточке.
- аренда как выручка;
- залог как возвратное обязательство;
- удержание как отдельное решение;
- неизменяемая сумма на момент выдачи;
- доказательство кассовой или банковской операции.
06
Опишите возврат и отрицательные результаты
Обычный возврат фиксирует время, сотрудника и состояние предмета. Если залог был принят, сотрудник возвращает его сразу или передаёт решение кассиру. Подтверждение возврата денег связывается с кассовой или банковской операцией. Статус refunded появляется только после явной команды, а не вместе с физическим возвратом вещи.
Потеря и повреждение требуют причины. Сотрудник описывает недостающую часть, дефект или обстоятельства. Залог можно удержать полностью, вернуть или оставить pending до решения менеджера. Последний вариант полезен, когда стоимость ремонта ещё неизвестна. Кассир позже завершает расчёт отдельной идемпотентной командой.
Отмена подходит для ошибочно оформленной выдачи, когда предмет фактически не передавали. Она не заменяет потерю и не стирает историю. После закрытия появляется событие с прежним и новым статусом, решением по залогу, автором и временем. Старые события остаются неизменяемыми, корректировка добавляет следующий факт.
07
Разведите роли ресепшена, кассы и склада
Ресепшен видит справочник, выдаёт и принимает предметы, фиксирует потерю и состояние. Кассиру нужен просмотр операции и право завершить возврат или удержание залога. Склад управляет справочником, обслуживанием и физической инвентаризацией, но не принимает финансовые решения. Директор и владелец получают сводный доступ.
Три отдельных permission делают это разделение проверяемым: view для журнала, manage для предметов и жизненного цикла, settle для денег. Полный доступ владельца остаётся явным bypass. Соседние продукты Taska Core не получают права автоматически, потому что новые ключи добавляются только в fitness vertical и конкретные роли ProGym.
Интерфейс также учитывает права. Кассир видит ожидающий залог и кнопку расчёта, но не меняет состояние справочника. Склад может вывести предмет на обслуживание, но не вернуть деньги. Сервер повторяет каждую проверку, поэтому скрытая кнопка не является единственным барьером.
08
Собирайте метрики без персональных данных
Операционные показатели включают число доступных и активных предметов, просрочки, долю потерь, средний срок выдачи, сумму held и pending залогов, возвраты и удержания. Разрезы по филиалу, типу и периоду помогают увидеть нехватку полотенец, проблемную зону шкафчиков или смену с большим числом незакрытых операций.
Для таких отчётов не нужны имя клиента, телефон, membership ID, заметка, причина спора, idempotency key и ссылка на чек. Metric layer публикует только безопасные колонки. Drill-down в рабочую карточку выполняется через отдельный API и права роли. Такой подход снижает объём чувствительных данных в экспортах и BI.
Не оценивайте сотрудника только по числу потерь. Сравнивайте объём выдач, тип предмета, длительность и подтверждённые причины. Рост pending залогов показывает процессную проблему кассы, а увеличение maintenance может означать качественную профилактику. Метрика должна вести к проверяемому действию.
09
Свяжите возвратный инвентарь с соседними модулями
ClubVisit даёт контекст присутствия и помогает найти незакрытую выдачу при выходе. Membership подтверждает право на бесплатный шкафчик или полотенце. Checkout проводит платную аренду и залог по кассе. Inventory отвечает за закупку расходных партий, Asset Ops за обслуживание дорогого оборудования. Rental сохраняет физический жизненный цикл конкретного предмета.
Интеграция строится через идентификаторы и API, а не общую таблицу со всеми статусами. Ошибка платежа не должна автоматически потерять предмет. Закрытие визита не доказывает возврат. Списание складской партии не завершает клиентскую выдачу. Каждый модуль хранит свою истину и обменивается проверяемыми событиями.
Для полотенец с массовым оборотом позже можно добавить количественную модель партии. Уникальные ключи и шкафчики уже работают в текущем контуре. Расширение должно сохранить те же принципы: организация, блокировка, идемпотентность, доказательство финансового действия и безопасная аналитика.
10
Проведите приёмку на реальной смене
Создайте несколько предметов без залога и с разной суммой. Выдайте один клиенту, повторите запрос после имитации таймаута и получите прежнюю запись. Затем запустите две выдачи одного свободного предмета параллельно. Успешной должна остаться одна. Попробуйте вывести занятый предмет на обслуживание и убедитесь, что сервер отклоняет изменение.
Закройте обычный возврат с немедленным refund, потерю с удержанием и повреждение с pending решением. Кассир завершает pending отдельной командой. Повтор каждого действия не создаёт новое событие. Изменённый запрос с прежним ключом получает conflict. Проверьте список событий и отсутствие ключей, fingerprint и платёжной ссылки в ответе API.
Повторите сценарий под ролями ресепшена, кассира, склада и сотрудника без доступа. Проверьте две организации с одинаковым кодом и убедитесь, что каждая видит только свою запись. Выполните mobile QA на ширине 390 пикселей, клавиатурную навигацию, touch targets и русский, узбекский, английский интерфейс.
После технической приёмки проведите одну настоящую смену. Перенесите текущие ключи и шкафчики, назначьте ответственных, договоритесь о формате reference и обработке спорных случаев. В конце дня сверьте физический остаток, активные выдачи, кассу и pending залоги. Только после такой сверки процесс можно считать запущенным.
- одна активная выдача на предмет;
- точный retry и semantic conflict;
- возврат, потеря, порча и отмена;
- отдельный расчёт по pending залогу;
- роли, RLS, аудит и мобильный UX.