# Портал технической службы (чек-листы, наряды, инструмент) FastAPI + SQLAlchemy, веб-интерфейс на чистом JS, JWT-API для будущего Android-приложения. Документация API (Swagger): `/docs`. ## Модель - **Должность** (`technician` / `engineer`) имеет **график** (по дням недели или цикл N/M, дневная/ночная смена). **Пользователь** привязывается к должности администратором. - **Чек-лист** — список задач; тип ответа: статус (в работе/выполнено), список, число (с границами), текст. - **Шаблон наряда** — единожды настроенный наряд + периодичность (каждый день / дни недели / раз в месяц / каждые N дней). Планировщик внутри приложения создаёт из него наряды. - **Наряд** — копия чек-листа с оборудованием, местом, исполнителем и проверяющим. Исполнитель: конкретный человек **или** должность (тогда наряд видят те, кто сейчас на смене; кто первый начал — тот исполнитель). Статусы: `new → in_progress → review → completed` (проверяющий может вернуть на доработку), `cancelled`. - Права: техник — только свои наряды; инженер — всё про наряды/чек-листы/оборудование + проверка; админ — ещё пользователи, должности, графики. ## Документация - **[Руководство пользователя (PDF)](docs/user-guide.pdf)** — для техников, инженеров, модераторов и администраторов: скриншоты с пометками, работа без связи, проверка, графики, отчёты, FAQ. Пересборка: [tools/guide](tools/guide/README.md). - [docs/DEPLOY.md](docs/DEPLOY.md) — установка на свой сервер (Docker Compose); [docs/API.md](docs/API.md) — описание API для разработчика клиентов; [android/README.md](android/README.md) — сборка приложения. ## Роли - **Администратор** — всё, включая «Настройки сервера» (каталог фото, лимит размера, контакт push). - **Модератор** — панель «Администрирование»: пользователи, должности, графики, чек-листы, оборудование, наряды; не меняет администраторов, не выдаёт права, не видит настройки сервера, сам наряды не выполняет. - **Инженер / техник** — по типу должности (см. выше). ## Android-приложение Нативное приложение (Kotlin + Compose) в каталоге [android/](android/README.md): список и выполнение нарядов, фото с камеры, комментарии, проверка для инженеров, **полноценная работа без связи** (накопленное уходит на сервер само), уведомления. Распространяется APK-файлом: готовая версия лежит в `release/` и раздаётся по `https://<домен>/app.apk` (ссылка на экране входа); новый файл можно положить в `DATA_DIR/apk/` — он важнее. ## Мобильная версия в браузере (PWA) Запасной вариант, если приложение недоступно (например, iPhone): **`https://<домен>/m`**. Устанавливается на экран «Домой» (Android: «Установить приложение»; iPhone: «Поделиться → На экран Домой»), работает **полностью без связи**: наряды и чек-листы хранятся в браузере (IndexedDB), ответы, фото, комментарии и сдача копятся в очереди и уходят сами (фото с `Idempotency-Key`, ответы пачкой через `/orders/{id}/sync`). Все экраны: список с фильтрами и поиском, чек-лист с крупными кнопками, фото с камеры, комментарии, проверка для инженеров, уведомления (Web Push), выход. - С телефона на основном адресе предлагается перейти на `/m` (выбор «Остаться» запоминается); на ПК остаётся полная версия. - Файлы: `app/static/m/` (m.js, m.css, manifest, иконки); service worker — `app/static/sw.js` (кэш оболочки + push). - Ограничения iOS: нет фоновой синхронизации (отправка идёт, пока приложение открыто), push работает только у установленной PWA (iOS 16.4+) и только по HTTPS. Данные браузера iOS может очистить, если не открывать сайт долго. ## Демонстрационный режим (личный) «Администрирование → Демонстрационный режим». Демо включается **только для того, кто его включил**: его запросы идут в отдельную SQLite-базу с тестовыми данными (сотрудники, оборудование и краны, чек-листы, наряды и задачи за 14 дней, карты, склады, инциденты), остальные пользователи продолжают работать с рабочей базой. Администратор может включить или выключить демо любому сотруднику. Тестовые учётные записи (пароль `demo1234`) работают параллельно с рабочими. Приложение для Android и мобильная версия при смене режима очищают локальные данные. Подробнее — docs/API.md, раздел «Демонстрационный режим». ## Графики и отчёты - Графики: по дням недели, простой цикл N через M или **конструктор цикла из шагов** (смена/выходной, любая длительность, дневные, ночные и суточные смены, метки «отсыпной» и т. п.) с календарём-предпросмотром на 6 недель. - Отчёты → «Выполненные наряды»: машина, чек-лист, кто выполнил, должность, когда. Период (дата+время) обязателен; фильтры по машине, должности, исполнителю; экспорт в PDF и CSV. Шрифт DejaVu лежит в `app/fonts` (кириллица в PDF). ## Регламентное обслуживание и «Текущее состояние» - **Узлы машины.** Карточка машины («Оборудование» → «Узлы и наработка») хранит наработку (моточасы) и список обслуживаемых узлов: масло, фильтры, ремни и т. п. Для каждого узла выбирается, как он обслуживается: **по наработке** (каждые N м/ч), **по календарю** (раз в N дней / недель / месяцев) или **что наступит раньше**; задаётся порог предупреждения «Скоро» (% от интервала). При добавлении указывается, когда и при какой наработке узел обслуживался в последний раз. - **Чек-лист «Регламентное обслуживание».** Тип чек-листа в редакторе. В наряд подставляются: показание наработки машины, ваши обычные задачи и по пункту «обслужено?» на каждый узел выбранной машины (можно только «Требуется/Скоро»). Оборудование обязательно. Когда наряд завершён (после проверки, если она есть), отмеченные узлы сбрасываются, а наработка машины обновляется. Работает и в автозапуске (шаблоны нарядов). Android-приложение показывает пункты узлов с остатком и содержит экран «Состояние узлов» (наработка, сброс); мобильная версия — как обычные задачи. - **Сброс узла** вручную — если замена или обслуживание выполнены отдельно либо при внештатной ситуации (дата, наработка, комментарий; всё в истории узла). Показание счётчика не может уменьшаться, кроме «коррекции» (замена/обнуление счётчика). - **Отчёт «Текущее состояние»** (Отчёты): узлы всех машин с состоянием **Требуется / Скоро / В норме**, остатком по наработке и календарю, сроком и **прогнозом даты** (по средней наработке в сутки за 30 дней). Фильтры по машине, месту и состоянию; экспорт PDF и CSV. - API: `/equipment/{id}/service-units`, `/service-units/{id}[/reset|/events]`, `/equipment/{id}/meter`, `/reports/equipment-state` (см. [docs/API.md](docs/API.md)). ## Портал: задания, инциденты, карта, инструмент, сотрудники - **Боковое меню** вместо шапок: на компьютере всегда видно, на телефоне (сайт, /m, Android 1.1.0) — по кнопке ☰. - **Суточные задания** (`/tasks`): назначен / в работе / приостановлен / выполнен / отменён; для трёх последних комментарий обязателен; фото, привязка к машине или узлу. Другим ставят инженер, модератор, админ; техник — себе. - **Инциденты** (`/incidents`): авария на машине — описание, затронутые узлы, была ли замена (замена сбрасывает ресурс узла). Открытый инцидент — состояние машины «Авария». Сводка состояний: `/equipment-health`. - **Иконки оборудования**: встроенный набор `app/static/icons` (генерируется `tools/make_icons.py`) или своё фото. - **Карта завода** (`/map`): картинку и расстановку иконок ведёт админ; «!» — авария, жёлтое свечение — скоро/пора ТО. **Карта складов** — зоны складов на том же плане (ведёт администратор склада), клик — содержимое склада. - **Инструмент и склады** (`/inventory/*`, `/warehouses`): каталог, остатки, выдача лично сотруднику, возврат, перемещение, списание, журнал. Роль **«Администратор склада»** (`is_storekeeper`). Сотрудник видит только выданное ему (`/me/inventory`). - **Заявки на инструмент** (`/tool-requests`): что купить, зачем, ссылка на товар; статусы и ответ ответственного. - **Карточка сотрудника** (`/users/{id}/card`): ФИО, фото, должность, ближайшие смены, «что умею», текущие задания и наряды, инструмент. - **Передача задания** (`POST /tasks/{id}/transfer`): исполнитель, поставивший задание или инженер; причина обязательна, видна в истории. - **Инциденты извне.** Публичная форма **`/report`** без входа: место → только машины этого места (и вложенных) → описание и контакты (обязательно); лимит отправок по адресу, ловушка для ботов, выключается в «Настройках сервера». API для внешних систем: `POST /external/incidents` с заголовком `X-API-Key` (машина по id или инвентарному номеру, `external_id` защищает от дублей), справочник `GET /external/equipment`; ключи выпускает и отзывает админ в «Настройках сервера» (в БД — только хэш). - **История машины** (`GET /equipment/{id}/history`, вкладка в карточке машины): инциденты, обслуживание и замены узлов, наработка, наряды, задания; фильтры по типу, периоду и тексту. - **Отделы** (`/departments`): вложенность, руководитель; отдел в карточке пользователя, фильтр в «Сотрудниках». - **Мастер первичной настройки** (Администрирование → 🧭): графики, должности, отделы, места, оборудование, сотрудники (пакетный ввод строками, логины и временные пароли — таблицей для печати), карта, склады и остатки, чек-листы, внешние обращения, копии. - **Резервные копии** (Администрирование → «Резервные копии», `/backups`): база в переносимом формате + конфигурация (+ фото по желанию), автокопии по расписанию, скачивание, загрузка, восстановление. - **AI-помощник** (включается в «Настройках сервера»: провайдер, адрес, модель, ключ): кнопка в правом нижнем углу на всех экранах; отвечает только о работе в портале, учитывает права пользователя и читает данные только от его имени. - **Отчёт «Выполнение работ»** (`/reports/execution`): % выполнения суточных осмотров, чек-листов, ТО и заданий по машинам, отклонения; PDF/CSV. ## Комментарии, фото, уведомления - Комментарии к наряду видят все, кто видит наряд; участникам (исполнитель, проверяющий, назначенный) уходит уведомление. - Фото: у пункта чек-листа режим «без фото / по желанию / обязательно». Браузер уменьшает снимок до 1600 px. Файлы лежат в каталоге из настроек (по умолчанию `data/photos`, в Docker — `./photos` на хосте), в БД — только путь. API: `POST /api/orders/{id}/items/{item_id}/photos` (тело — сам файл, `Content-Type: image/jpeg|png|webp`), `GET /api/photos/{id}` (с токеном). - Уведомления — Web Push: кнопка «🔔» в шапке. Нужен **HTTPS** (через Nginx Proxy Manager подойдёт). Приходят при назначении наряда (человеку или тем, кто в этот момент на смене в должности), на проверку, при возврате и при комментариях. ## API Версионированный REST: `/api/v1`, Swagger на `/docs`. Короткий access-токен + refresh с ротацией, пакетная офлайн-синхронизация (`POST /orders/{id}/sync`), курсор `updated_since`, идемпотентная загрузка фото, FCM для Android. **Полное описание для разработчика приложения: [docs/API.md](docs/API.md).** Переменные: `ACCESS_TTL_MIN` (60), `REFRESH_TTL_DAYS` (90), `LOGIN_MAX_FAILS` (5), `LOGIN_WINDOW_SEC` (300). ## Локальный запуск ``` python -m venv .venv && .venv\Scripts\pip install -r requirements.txt .venv\Scripts\python -m uvicorn app.main:app --reload ``` Вход: `admin` / `admin` (создаётся при первом старте, **смените**). Тесты: `pytest tests`. ## Установка без Coolify (Docker Compose) Пошаговая инструкция для любого Linux-сервера с Docker: **[docs/DEPLOY.md](docs/DEPLOY.md)** (шаблон настроек — `.env.example`). Коротко: `cp .env.example .env` → заполнить → `docker compose up -d --build` → обратный прокси с HTTPS на `APP_PORT`. ## Деплой (Coolify + Nginx Proxy Manager) Приложение разворачивается из `docker-compose.yml` (сервис приложения + PostgreSQL, тома `pgdata`, `photos`, `appdata`). 1. В Coolify: Project → New → Application из Git-репозитория, **Build Pack = Docker Compose**, файл `docker-compose.yml`. 2. Переменные окружения (Coolify → Environment Variables): `DB_PASSWORD`, `SECRET_KEY` (случайная строка), `ADMIN_PASSWORD` (пароль администратора `admin`, применяется при первом старте), `FORWARDED_ALLOW_IPS` (**IP вашего NPM**, например `192.168.1.10`), при желании `APP_TZ` (по умолчанию Europe/Moscow) и `APP_PORT` (порт хоста, по умолчанию **8080**). В боевом режиме (`APP_ENV=production`, уже прописан в compose) приложение **не запустится**, если `SECRET_KEY` короче 32 символов или пуст, а `ADMIN_PASSWORD` слабее 10 символов (проверяется при первом создании администратора). Пароли пользователей — от 10 символов. 3. Домен в Coolify не задаётся: прокси — NPM. Proxy Host → Forward `http://:8080`, SSL (Let's Encrypt), Force SSL. **В Advanced добавьте `client_max_body_size 20m;`** (иначе фото больше 1 МБ вернут 413). Порт приложения обязательно закройте файрволом для всех, кроме адреса NPM, а в `FORWARDED_ALLOW_IPS` укажите этот адрес: только от него принимаются заголовки `X-Forwarded-For`. Проверка: «Администрирование → Настройки сервера» показывает адрес клиента, каким его видит сервер. 4. Запуск строго в **одном процессе** (так и настроено): планировщик нарядов и демо-режим живут внутри процесса. 5. APK: готовый подписанный файл лежит в репозитории (`release/`) и входит в образ, поэтому ссылка «Скачать приложение для Android» на экране входа и адрес `/app.apk` работают сразу после деплоя. Новую версию можно выложить без передеплоя: положить файл в том `appdata` по пути `apk/` (он важнее файла из образа): `docker cp checklists.apk <контейнер приложения>:/srv/data/apk/`. 6. Резервные копии: `pg_dump` базы по расписанию + тома `photos` и `appdata`.