← vitalness.ru Как работает Устройство Как делал Войти
Zapas

Для кофеен, пекарен и кафе на 1–5 точек

Продукты всегда в наличии

Склад, прогноз спроса и напоминания о заказе. Система сама считает, что и когда заказать, — и присылает сводку в Telegram.

Как это работает

Демо живёт 24 часа, регистрация не нужна.

2 критично 6 заказать 31 хватает 1 нет данных Молоко 3,2% 18 л заказать 2 кор. Стаканы 350 мл 40 шт критично 4 уп. Зерно Бразилия 6,4 кг хватает — Почему такой статус Сейчас 18 л, в пути 0. До поставки в пн нужно 31 л + запас 4 л. Не хватает 17 л → заказать 2 кор. (24 л)

Знакомо?

Закончилось посреди смены

Молоко кончается в разгар утреннего потока. Бариста бежит в магазин по розничной цене, очередь стоит.

Выбросили просрочку

Заказали с запасом «чтобы точно хватило» — половина ушла в списание. Деньги сгорели дважды: при закупке и при выбросе.

Всё держится в голове владельца

Учётная система показывает остаток, но не отвечает на главный вопрос: когда заказывать. Отпуск — и всё рассыпается.

Как это работает

  1. 1

    Учёт

    Приход, расход, списания и пересчёт. Остаток никогда не правится руками — он равен сумме движений, и любую цифру можно объяснить по журналу.

  2. 2

    Прогноз

    Система считает расход на 14 дней вперёд по истории, с учётом дня недели, и показывает собственную точность. Не «магия ИИ», а честные числа, которые видно.

  3. 3

    Напоминание

    Зная срок поставки и дни доставки поставщика, система считает, хватит ли запаса до следующего заказа. Если нет — присылает сводку с готовым количеством.

Промотайте неделю за секунду

В демо есть кнопка «+7 дней». Система проживает неделю: списывает расход, принимает поставки, пересчитывает прогноз и шлёт сводки.

Рядом — переключатель «автопилот». С ним система заказывает сама, и через неделю почти всё зелёное. Без него позиции краснеют. Это и есть сравнение «с системой и без».

Так приходит сводка в Telegram
Кофейня «Демо» · среда, 23 сентября

Заказать сегодня (3)
Молочная ферма — до 16:00, поставка в чт
  • Молоко 3,2% — 2 кор. (24 л), сейчас хватит до пт
  • Сливки 33% — 1 кор. (6 л)
Кофе-Импорт — до 18:00, поставка в пт
  • Зерно Бразилия — 2 × 1 кг

Критично (1)
  • Стаканы 350 мл — хватит только до завтра,
    ближайшая поставка в пт

Открыть дашборд →

Точность видна, а не обещана

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

85% медианная точность на демо-данных

Замер: пять свежих демо, 190 позиций, бэктест на 28 днях. В карточке позиции написано прямо: «80% за 28 дней, модель M2, занижает на 4%». Если неделю ничего не заказывать, позиции заканчиваются, и точность падает до 77%: дни с нулевым остатком честно портят прогноз, и это тоже видно.

— факт - - прогноз

Как это устроено

Этот раздел — для тех, кто смотрит на проект как на код, а не как на продукт.

Браузер nginx API · Go PostgreSQL 16 Redis Воркер · Go Telegram и почта кабинет на React /zapas/ → :8080 chi, pgx + sqlc RLS, роль без BYPASSRLS очередь, лимиты прогноз, сводки, outbox только из outbox Уведомление пишется в базу в одной транзакции с событием, отправляет воркер: Telegram не влияет на ответ API

Стек

Go (chi, pgx + sqlc, без ORM) · PostgreSQL 16 · Redis + asynq · React + TypeScript + Vite · Tailwind · systemd + Nginx

Ключевые решения

  • Журнал движений. Таблица только дописывается: права на UPDATE и DELETE отозваны у роли приложения. Ошибка исправляется сторно, а не правкой.
  • Изоляция тенантов на четырёх уровнях. Row-level security в базе, роль без BYPASSRLS, составные внешние ключи. Забытый WHERE tenant_id перестаёт быть уязвимостью.
  • Transactional outbox. Уведомление пишется в одной транзакции с событием, отправкой занимается воркер. Telegram не влияет на время ответа API.
  • Абстракция часов. time.Now() запрещён — время берётся из интерфейса. На этом держится «промотка времени» в демо.

Что проверено тестами

  • 50 горутин одновременно списывают одну позицию: остаток не уходит в минус и сходится с журналом.
  • Повтор запроса с тем же ключом не создаёт второе движение, повторная приёмка — второй приход.
  • Чужой тенант получает 404 на любой ресурс; запросы без тенанта возвращают ноль строк.
  • Точность прогноза на сгенерированных данных: WAPE ≤ 20% с фиксированным seed.

Как я это делал

Бизнес-логика почти не переделывалась: формулы из ТЗ легли в табличные тесты с первого раза. Время ушло на стыки с реальностью — сервер, nginx, сертификаты, CI и сами способы проверки. Каждый такой случай записан в журнал, а все вместе разложились на шесть классов.

A · Среда

Мир не такой, как в документе

ТЗ предполагало выделенный сервер под Docker. На деле — общий: 1 vCPU и 961 МБ на шесть проектов. Размещение переписано на systemd.

Ловится командой на целевой машине до кода.

B · Немой отказ

200 в логах, белый экран

Кабинет отдавал 200, а все его скрипты — 403: alias в regex-локации nginx. Логи чистые, сторож молчит.

Ловится браузером, а не кодом ответа.

C · Шов

Два модуля правы по отдельности

Приёмка заказа брала системные часы, склад сверял с часами организации. В промотанном демо приход оказывался «в прошлом».

Ловится сквозным тестом всего цикла.

D · Документ по памяти

Контракт разошёлся с кодом

Причины списания в OpenAPI не совпадали с кодом, раннбук отката советовал migrate down, который в проде запрещён.

Ловится документом из живого ответа.

E · Вырожденный вход

Правдоподобные данные проходят всегда

Excel на русской Windows сохраняет CSV в CP1251 — импорт падал. Ровный ряд продаж ломал обрезку выбросов: разброс равен нулю.

Ловится списком крайних случаев до кода.

F · Инструменты

Три среды — три набора умолчаний

Разработка под Windows, сборка в Linux-контейнере, прод на Ubuntu: CRLF в файлах Go, пути Git Bash, кодировка PowerShell.

Ловится правилами в файлах, а не в голове.

Цифры

22,7 мс
дашборд на миллионе движений, p95 при бюджете 300 мс
34,2 мс
запись движения, p95 при бюджете 150 мс
1,5 с
создание демо с 90 днями истории на проде
168
тестовых функций Go, конкурентность и изоляция — на настоящем PostgreSQL
8
проверок CI на каждый push: формат, линтер, тесты с детектором гонок, секреты, уязвимости, фронтенд, контракт, сборка
42
операции в контракте OpenAPI

Об авторе

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