Files
aura-crm/production/PROJECT.md
T

24 KiB
Raw Blame History

Сервисный центр — платформа: обзор проекта и руководство пользователя

Этот файл — человеко-читаемое описание всей платформы и её основного модуля (production), для владельца бизнеса и для себя на будущее. Техническая справка по API/деплою этого репозитория — в README.md, она не дублируется здесь.

1. Что это

Модульная CRM для сервисного центра (Севастополь): приём техники в ремонт, заправка картриджей, продажа/выкуп техники, склад и закупки, касса, аналитика. Заменяет разрозненные таблицы и мессенджеры одним рабочим местом для владельца, менеджеров и мастеров.

Разработка началась 2026-08-08. Основная дорожная карта (фазы 0–10: MVP, документы, картриджи, склад, AI-приёмка, касса, уведомления, аналитика, AI-диагностика) закрыта к 2026-08-10, после чего добавлен второй слой фич (лояльность, запись на приём, поставщики и закупки, гарантия/возвраты, трейд-ин, кастомные поля, шаблоны документов, PWA, управление модулями).

2. Архитектура платформы

Платформа состоит из независимых сервисов — каждый со своей БД, своим рантаймом, своим docker-compose. Общее — только staff-идентичность (JWT) и реестр модулей, оба живут в core.

Сервис Репозиторий Роль Порт (loopback)
core ~/service-center Auth (JWT owner/manager/master), реестр модулей, heartbeat/restart/maintenance backend 18090
production ~/production (этот репозиторий) Приём в ремонт, картриджи, склад, касса, аналитика — основной объём функциональности backend 18091, web 18092
site ~/site Публичный лендинг + форма лида backend 18093, web 18094
online-store отдельный прод-деплой (Next.js) Продажа техники, уже в проде, синкается вебхуком в production.sale

Модуль регистрируется в core (POST /api/modules), получает MODULE_TOKEN и шлёт heartbeat каждые 30с. core может дистанционно перезапустить модуль или перевести его в режим обслуживания (страница /modules, owner-only). production не имеет собственного логина — JWT выпускает core, оба сервиса проверяют его одним и тем же JWT_SECRET (HS256).

Все сервисы стоят за системным Caddy на хосте (домен ещё не выбран), сеть между контейнерами — общий docker-сеть platform_net.

3. Технологический стек

Backend: Go + Fiber, PostgreSQL (pgx/v5, собственные SQL-миграции — не Supabase/ORM), MinIO (файлы: фото заявок, документы). AI — Gemini (бесплатный тариф): structured output для AI-приёмки заявок и AI-диагностики по фото, свободный текст для аналитической сводки. Docker + Caddy для деплоя.

Frontend (web/): React 19 + Vite (без TypeScript, .jsx), React Router 7, @dnd-kit для drag-n-drop Kanban-досок, lucide-react для икон, vite-plugin-pwa для PWA-манифеста/service worker. Без Tailwind и без UI-фреймворка — своя лёгкая дизайн-система (раздел 9).

Аутентификация: JWT, роли owner / manager / master, токен хранится в localStorage, состояние — React Context (AuthContext).

4. Роли и права доступа

Роль Доступ
Владелец (owner) Всё. Плюс единственный, кто видит: Сотрудники, Поля приёмки, Справочники, Шаблоны документов, Модули, Настройки
Менеджер (manager) Всё операционное: канбан, картриджи, склад, поставщики, закупки, возвраты, гарантия, трейд-ин, клиенты. Плюс Касса и Аналитика
Мастер (master) Операционные разделы, но видит только заявки/партии, назначенные на него (или ещё неназначенные — чтобы взять в работу). Кассу и Аналитику как отдельные страницы не видит, но кассовая история конкретного своего заказа доступна внутри карточки заказа

Разграничение — не только на фронтенде (скрытие пункта меню), а на уровне API (internal/authz): чужой заказ/партия картриджей отдают 403 на чтение и запись для мастера, включая фото/комментарии/списание запчастей.

5. Руководство пользователя

Ниже — по каждому пункту левого меню (web/src/layout/Sidebar.jsx), в том порядке, в котором он идёт в интерфейсе.

Канбан (/kanban)

Главный экран. Доска приёма в ремонт с колонками по статусу: Новая → Диагностика → В ремонте → Ждём запчасти → Готово → Выдано (плюс Отменено). Карточки заказов перетаскиваются между колонками (@dnd-kit) — перетаскивание пишет новую запись в таймлайн заказа. Клик по карточке открывает модалку заказа: данные клиента и устройства, назначенный мастер, оценка/итоговая цена, гарантия (срок + связь с исходным заказом при повторном ремонте), таймлайн (смена статусов — всегда публична; комментарии и фото — публичность настраивается отдельно для каждой записи), кнопки генерации Счёта и Акта (PDF), кнопка AI-диагностики по фото, форма AI-приёмки нового заказа (Gemini разбирает свободный текст на структурированные поля). Обновляется поллингом раз в 20 секунд — realtime-канал (internal/realtime) уже реализован для уведомлений о новых заявках, полноценный live-канбан без поллинга — не сделано.

Заявки на запись (/bookings)

Заявки, оставленные клиентом с публичной страницы записи (/book, без логина) — желаемая дата/время, что за проблема. Владелец/менеджер/мастер переводит заявку в реальный заказ или отклоняет.

Картриджи (/cartridges)

Отдельная Kanban-доска для заправки картриджей — та же механика статусов, что и у заказов в ремонт, но свой домен (internal/cartridge): партия картриджей, а не устройство. Тоже поддерживает документы (Счёт/Акт), фото, комментарии, назначенного мастера и тот же ACL по назначению.

Склад (/parts)

Учёт запчастей и расходников: остатки, себестоимость, списание при ремонте (привязка расхода к конкретному заказу — для реальной маржинальности по заявке, не только по кассе в целом).

Поставщики (/suppliers)

Справочник поставщиков запчастей — контакты, кто у кого что заказывает.

Заказы поставщикам (/purchase-orders)

Цикл закупки: заказ поставщику → поступление (GRN) → приход на склад.

Возвраты поставщикам (/rma)

Брак/возврат запчастей поставщику — отдельный от клиентской гарантии поток.

Гарантийные случаи (/warranty-claims)

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

Выкуп техники (/trade-ins)

Приём техники от клиента на выкуп (trade-in) — отдельный поток от ремонта: оценка состояния, цена выкупа, статус сделки.

Касса (/cash) — только owner/manager

Ручная кассовая книга (cash_transactions) — приход/расход, привязка операции к заказу или партии картриджей. Отдельная страница — сводный журнал по всей кассе; по конкретному заказу кассовая история видна и мастеру внутри карточки этого заказа (см. раздел 4).

Аналитика (/analytics) — только owner/manager

Агрегированная отчётность: выручка, операционные метрики (сколько заказов в каком статусе, средний срок ремонта и т.п.), остатки склада, плюс AI-сводка на свободном тексте (Gemini интерпретирует цифры человеческим языком).

Клиенты (/clients)

Физ- и юрлица, поиск, карточка клиента с полной историей его заказов. Программа лояльности (internal/loyalty) считается здесь же.

Сотрудники (/staff) — только owner

Единственный способ управлять персоналом (у core, который реально владеет staff-аккаунтами, своего фронтенда нет — эта страница ходит напрямую в core API). Создание сотрудника, смена роли, отключение/включение аккаунта, сброс пароля (владелец задаёт новый пароль лично — self-service восстановления по почте нет, инфраструктуры email в проекте нет). Нельзя разжаловать или отключить последнего активного owner'а — защита от случайной блокировки.

Поля приёмки (/order-fields) — только owner

Кастомные поля для формы приёма заказа (текст/число/выбор/чекбокс, обязательное или нет, порядок отображения). Позволяет владельцу подстроить форму приёма под свою специфику без изменения кода. Архивация вместо удаления — старые значения в уже созданных заказах не теряются.

Справочники (/catalogs) — только owner

Каталог устройств (devicecatalog) — бренды/модели для автозаполнения формы приёма (плюс кнопка автозаполнения марки/модели телефона по IMEI).

Шаблоны документов (/document-templates) — только owner

Блочный редактор вёрстки Счёта и Акта: реордер блоков, скрытие необязательных блоков, свои текстовые вставки. Не freeform HTML/CSS — осознанное ограничение, чтобы нельзя было случайно сломать документ. Структура обязательных блоков и шрифты не редактируются.

Модули (/modules) — только owner

Панель управления всей платформой на уровне core: список зарегистрированных модулей (production, site, online-store) со статусом (healthy / unhealthy / maintenance), кнопки реального перезапуска контейнера и перевода в режим обслуживания (503 на все роуты, кроме /health).

Настройки (/settings) — только owner

Реквизиты бизнеса для PDF-документов, ключ/модель Gemini, токен и chat_id Telegram-бота, ключ IMEI-сервиса — всё редактируется владельцем без редеплоя (фоллбэк на .env, если поле в БД пустое). Инфраструктурные секреты (JWT_SECRET, пароли БД/MinIO, токены модулей) сюда сознательно не вынесены — только .env.

6. Публичные страницы (без логина)

  • /track/:token — трекинг заказа для клиента: статус, устройство, гарантия, только публичные события таймлайна (фото на этой странице пока не рендерятся, только факт "добавлено фото").
  • /book — форма записи на приём, попадает в раздел «Заявки на запись».

7. Backend: домены (backend/internal/)

Каждый пакет — одна зона ответственности:

Пакет Отвечает за
auth Верификация staff JWT, выпущенного core
authz Единое правило ACL: свой/чужой заказ или партия по assigned_master_id
order / cartridge Заказы в ремонт / партии картриджей — ядро домена
client Физ/юр клиенты
booking Публичные заявки на запись
cash Ручная кассовая книга
inventory Склад запчастей, списание, себестоимость
supplier / purchaseorder / rma Поставщики → закупки → возвраты браком
tradein Выкуп техники у клиента
loyalty Программа лояльности клиентов
customfields Owner-настраиваемые поля формы приёма
devicecatalog Каталог брендов/моделей устройств
imei Автозаполнение марки/модели по IMEI (HiCellTek TAC lookup)
aiintake AI-приёмка заявки: свободный текст → структурированные поля (Gemini)
diagnosis AI-диагностика по фото (Gemini vision + Google Search grounding)
analytics Агрегированная отчётность + AI-сводка
document / doctemplates / pdfgen / ordernum Генерация Счёта/Акта, блочные шаблоны, нумерация документов
file Хранение файлов в MinIO, реальный MIME-снифф по байтам (не доверяет заголовку клиента)
settings Owner-редактируемая конфигурация без редеплоя
notify / clientnotify / tgbot / smsgw Уведомления персоналу и клиентам (Telegram/SMS)
coreclient / modulecontrol Heartbeat в core, приём команд restart/maintenance от core
realtime Push-сигналы подключённым клиентам о изменениях
sale Записи о продажах, синкаемые из online-store
scheduler Фоновые задачи по расписанию
db / dbutil Подключение к БД и общие SQL-хелперы

8. Документы и уведомления

PDF (Счёт на оплату / Акт выполненных работ) генерируются на лету (internal/pdfgen, кириллица через встроенные шрифты DejaVu Sans), структура настраивается блоками в «Шаблонах документов», реквизиты — в «Настройках». Уведомления клиенту о смене статуса — Telegram/SMS (clientnotify), уведомления персоналу о новых заявках — Telegram (notify/tgbot).

9. Дизайн-система

Своя лёгкая дизайн-система, без Tailwind и без готового UI-кита — базовые токены в web/src/index.css, специфика конкретного компонента — инлайн style={{}} в самом компоненте (осознанный выбор для проекта такого размера, не технический долг).

Шрифты: заголовки — Montserrat (600800, letter-spacing: -0.02em — плотный, «инженерный» вид), текст — Roboto (300500). Оба через Google Fonts.

Палитра (светлая тема, единственная — тёмная не реализована):

Токен Значение Назначение
--bg-light #f8fafc Фон страницы
--bg-lighter #ffffff Фон карточек/панелей
--accent-blue #4361ee Основной акцент — активный пункт меню, кнопки, ссылки
--accent-orange #ea580c Вторичный акцент (предупреждения/ожидание)
--accent-green #22c55e Успех/готово
--accent-red #ef4444 Ошибка/отмена
--text-main #1e1e2d Основной текст
--text-muted #64748b Второстепенный текст
--border-light #e9ecef Границы, разделители

Статусы заказа (orders/statuses.js) имеют собственную, более широкую палитру для наглядности канбан-доски: новая #f59e0b (янтарный), диагностика #0ea5e9 (голубой), в ремонте #8b5cf6 (фиолетовый), ждём запчасти #f97316 (оранжевый), готово #22c55e (зелёный), выдано #64748b (серый), отменено #ef4444 (красный) — цвет статуса используется как акцент на карточке и бейдже, не просто текстовая метка.

Компоненты и приёмы:

  • .glass-panel — базовая карточка/панель: белый фон, тонкая граница border-light, скругление 16px, мягкая тень (0 8px 32px rgba(15,23,42,.08)) — лёгкий «стеклянный» эффект без размытия (blur не используется).
  • .btn-primary (заливка accent-blue, белый текст, radius 8px) / .btn-outline (белый фон, граница border-light, при hover обводка синеет) — два уровня действий, primary/secondary.
  • Sidebar фиксированный, 240px, белый, активный пункт — светло-синяя подложка (#eef2ff) + синяя левая полоса 3px + синий текст/иконка. На мобильном (isMobile) уезжает за экран и выезжает по transform: translateX, с крестиком закрытия.
  • Иконки — lucide-react, по одной на каждый пункт меню, семантически подобранные (гаечный ключ для ремонта, капля для картриджей, коробки для склада, кошелёк для кассы и т.д.).
  • Формы/модалки переиспользуют общие стили (orders/formStyles.js и аналоги) — не единый компонентный кит, а общие объекты стилей на домен.

Характер дизайна: утилитарный внутренний инструмент для персонала (канбан-доска, таблицы, модалки), не публичный маркетинговый сайт — приоритет на плотность информации и скорость сканирования глазами (статус через цвет, а не только текст), а не на декоративность. Единственная по-настоящему «клиентская» поверхность — /track/:token и /book.

10. Разработка и деплой

Кратко (полная версия — README.md):

docker network create platform_net   # один раз, общая сеть с core/site
cp .env.example .env                 # JWT_SECRET должен совпадать с core/.env
docker compose up -d --build
curl http://127.0.0.1:18091/health

cd web && cp .env.example .env && npm install && npm run dev

11. Текущий статус и известные ограничения

  • Полная дорожная карта (фазы 0–10) закрыта, плюс второй слой фич (см. раздел 1) — реализован и живьём протестирован.
  • Юрлицо бизнеса ещё не зарегистрировано → нет интеграции с Атол Онлайн (фискализация, 54-ФЗ) и с Т-Банк эквайрингом для самого сервисного центра.
  • Kanban обновляется поллингом (20с), не realtime (транспорт для realtime уже есть в internal/realtime, но не подключён к канбан-доске).
  • Публичный трекинг не показывает сами фото, только факт их добавления.
  • Юнит-тестов почти нет (core — 0; production — часть пакетов без тестов: auth, client, coreclient, document, file, order).
  • Секреты в «Настройках» хранятся plaintext без истории изменений — нельзя откатить или увидеть, кто менял банковские реквизиты.