Смена открывается на месте
Сотрудник открывает смену на объекте, приложение проверяет, что он действительно там, ведёт паузы, собирает чек-листы с фото и считает зарплату. Три части: приложение, панель управления и сервер, который никому из них не верит на слово.
Что там внутри, если по-честному?
Не трекер, а рабочий день целиком
Организации и роли, объекты с зонами на карте, графики, смены с паузами, переработки и штрафы, чек-листы с фотоподтверждением, база знаний, тесты для сотрудников, расчёт зарплаты с выгрузкой в таблицу. В приложении четыре раздела и около полусотни экранов, в панели — пятнадцать разделов, между ними сервер: 157 операций в API и 19 таблиц в базе.
Пушей нет намеренно: уведомления забирает само приложение. Одной внешней зависимостью меньше — и одной причиной «а почему не пришло» тоже.
- Адресов в API
- 157 в 22 разделах
- Миграции базы
- 32 ревизии
- Тесты
- 86 файлов на сервере, 35 в приложении
- Проверка типов
- mypy в строгом режиме
И что здесь было самым важным?
Сервер не верит на слово
- Объект выбирает сервер, а не приложение. Клиент присылает координаты, а не «я на объекте номер три»: сервер сам считает расстояние до всех зон организации и берёт ближайшую в радиусе. Промах — смена не открывается. Подделать выбор объекта из приложения нельзя.
- Удаление последней зоны выключает геопроверку. Иначе организация, стёршая свои объекты, теряет возможность открыть смену вообще — и вместо настройки получает сломанный рабочий день.
- Дата и координаты вжигаются в само фото. Хранилище стирает служебные данные снимка, поэтому подпись наносится на пиксели в фоновом потоке — и остаётся на кадре, куда бы его потом ни переслали. Кадр, который не удалось обработать, отклоняется, а не уходит «как есть».
- Забытая смена закрывается сама. Раз в минуту фоновая задача ищет зависшие смены, плюс та же проверка идёт перед открытием новой. Закрывается временем планового конца, а не «сейчас», и всё пишется в журнал — чтобы спор о часах решался записью, а не памятью.
- Ставка — это история, а не поле. У каждой ставки есть дата, с которой она действует, поэтому пересчёт прошлого месяца не переписывает прошлое. Деньги считаются в копейках, округление одно и в конце.
А сопровождение?
Слияние в основную ветку — и оно в бою
На каждый push проходят тесты, линтер и строгая проверка типов, образ сканируется на уязвимости, состав зависимостей фиксируется отдельным файлом. После слияния сборка уезжает на сервер сама. База копируется в объектное хранилище по расписанию — потому что «у нас всё в облаке» не значит «у нас есть бэкап».