V

viko-system2

ViKo System

Модульный backend для грузоперевозок, автосервиса, финансов, CMS, маркетинга, склада, трекинга и др. по ТЗ.

Основной запуск: Python + локальный PostgreSQL (localhost:5432), без Docker.

Stack

  • Python 3.12
  • FastAPI
  • PostgreSQL (локально)
  • SQLAlchemy 2.0, Alembic, Pydantic v2
  • JWT (python-jose), passlib/bcrypt
  • WebSocket, openpyxl, reportlab; APScheduler (фон: опрос трекеров каждые 30 с, плановый бэкап по расписанию из БД), qrcode
  • По ТЗ основной путь без Docker — только Python и локальный PostgreSQL.

Project Structure

  • app/core — конфиг, БД, auth, idempotency, seed, CompanyDetails, назначения водителей, акты приёма-передачи, ежедневные осмотры, документы сотрудников, HATEOAS-хелпер, планировщик
  • app/modules/logistics — заказы, рейсы, аренда
  • app/modules/service — клиенты, проблемы, наряды, осмотры, чек-листы, логи, заявки с QR
  • app/modules/accounting — счета, платежи, расходы, P&L, CashAccount / CashFlow / DailyBalance, журнал ДДС GET /finance/money-journal (периоды день/неделя/месяц/год, печать PDF), синхронизация проводок из платежей, расходов и зарплаты в cash_flows
  • app/modules/payroll — ставки, учёт времени, транзакции, SalaryComponent / SalaryRule
  • app/modules/cms — сайты, страницы, услуги, тарифы, акции, отзывы, FAQ, CompanyAdvantage, PartnerCertificate
  • app/modules/notifications — in-app, шаблоны, SmsLog, правила и подписки
  • app/modules/reports — шаблоны, документы, PrintTemplate
  • app/modules/internship — стажировки: статусы, CRUD, этапы, оценки, наставничество, шаблоны, назначение ТС (/internships/{id}/car-assignments)
  • app/modules/marketing + corporate — лиды, B2B, рассылки (dispatch заданий — фиксация sent_at/счётчиков), промо, реферальные настройки (чтение диспетчером, правка — админ)
  • app/modules/uniform — спецодежда: номенклатура, выдачи/возврат, поставки, списания (/admin/uniform/write-offs)
  • app/modules/tracking — трекеры, геозоны (каркас)
  • app/modules/backup — настройки в БД, история; POST /admin/backup/runpg_dump -Fc (PostgreSQL), горячая копия файла (SQLite на диске) или симуляция; GET /admin/backup/status, GET /admin/backup/files/{id}, GET /admin/backup/history/{id}/listing (pg_restore -l); восстановление: POST /admin/backup/restore/run (pg_restore или замена файла SQLite, аварийный снимок перед операцией); плановый бэкап по cron при is_enabled; UI: /backup
  • app/modules/warehouse — склады, зоны хранения, StockPart, движения (receipt / issue / adjust / reserve / release), контроль низкого остатка
  • app/modules/equipment — учёт инструмента и оборудования (список, карточка, создание)
  • app/modules/public — публичные заявки без JWT

Полное ТЗ (все эндпоинты из п.3) наращивается итерациями: схема БД и заготовки роутов заложены; бизнес-логика части разделов — stub.

Run

Локально без Docker

  1. Установите PostgreSQL (например 16), запустите службу.

  2. Создайте пользователя и базу (пароль viko должен совпадать с .env):

    psql -U postgres -f scripts/create_local_db.sql

    Если psql не в PATH, используйте SQL Shell (psql) из меню Пуск или pgAdmin → Query Tool и выполните команды из scripts/create_local_db.sql вручную.

  3. Скопируйте .env.example в .env. В .env для локального запуска должно быть
    DATABASE_URL=postgresql://viko:viko@localhost:5432/viko.env.example это уже так по умолчанию).

  4. Python 3.12 (рекомендуется). На Python 3.14+ под Windows часто нет готовых сборок psycopg2-binary и pydantic-core — установка потребует Visual Studio Build Tools с компонентом «Разработка классических приложений на C++». Чтобы не возиться с компилятором, поставьте 3.12 с python.org и создайте .venv им.

    Зависимости:

    Проще всего — один скрипт (из папки проекта; активация .venv не нужна):

    cd "c:\projects\viko-system 2"
    powershell -ExecutionPolicy Bypass -File .\scripts\run_local.ps1

    Вручную без Activate.ps1 (если скрипт не подошёл): сначала зайдите в каталог проекта, создайте venv и вызывайте всё через python.exe из .venv:

    cd "c:\projects\viko-system 2"
    py -3.12 -m venv .venv
    .\.venv\Scripts\python.exe -m pip install -r requirements.txt
    .\.venv\Scripts\python.exe -m alembic upgrade head
    .\.venv\Scripts\python.exe -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000

    Если нет py, замените первую строку на: python -m venv .venv

  5. Откройте http://127.0.0.1:8000/docs.

Если ругается на Activate.ps1 — можно не активировать venv и использовать команды с .\.venv\Scripts\python.exe -m ..., как выше. Политика выполнения:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.

Через Docker (если доступен)

  1. Скопировать .env.example в .env, в DATABASE_URL указать хост db:
    postgresql://viko:viko@db:5432/viko
  2. Запустить:
docker compose up --build

Если установлен старый CLI, можно также docker-compose up --build.

Приложение будет доступно по http://localhost:8000.

Swagger:

  • http://localhost:8000/docs

Проверка БД:

  • GET /health — процесс жив
  • GET /health/db — соединение с PostgreSQL

Migrations

Локально:

alembic upgrade head

Цепочка включает 20260410_0003_tz_schema_expand: создание новых таблиц и ALTER TABLE ... IF NOT EXISTS для расширения employees, cars, service_clients, work_orders, car_inspections, trips, reviews (PostgreSQL).

В Docker миграции запускаются при старте контейнера приложения (см. Dockerfile).

После обновления зависимостей: pip install -r requirements.txt (в т.ч. APScheduler, qrcode).

Test Data

При запуске uvicorn подставляются только категории расходов (seed_system_reference) — без демо-машин, тестовых сотрудников и контрагентов; удалённые записи не появляются снова.

Демо-данные (seed_initial_data с SEED_DEMO_DATA=true) используются в pytest и скриптах (scripts/shipment_bulk_form_smoke.py), не при обычном старте приложения.

Чтобы вручную или в тестах заполнить стенд демо-пользователями (телефоны 7000000000170000000006, машины из seed.py, тестовые контрагенты), задайте:

SEED_DEMO_DATA=true

и вызовите seed_initial_data (как в tests/conftest.py).

После включения:

  • admin: 70000000001
  • dispatcher: 70000000002
  • driver: 70000000003
  • mechanic: 70000000004
  • supplier_logist: 70000000005
  • logist: 70000000006

Dev login code для всех этих пользователей:

  • 1234

Резервные копии и восстановление после переустановки Windows

Почему «пропали» заказы и журнал ДДС, а сотрудники и машины есть.
После чистой установки PostgreSQL вы создаёте новую пустую базу (CREATE DATABASE), затем alembic upgrade head. Заказы, рейсы, проводки журнала и т.п. в ней изначально пусты.
Сотрудники и автомобили, которые вы видите сразу после старта, чаще всего приходят из демо-seed (app/core/seed.py при запуске приложения), а не из «старой» базы — это не восстановление, а шаблонные записи.

Восстановить «как было» можно только из резервной копии, например:

  • файл *.dump, снятый через pg_dump -Fc (так делает встроенный модуль бэкапа при BACKUP_PG_DUMP_ENABLED=true, либо скрипт ниже);
  • старый каталог данных PostgreSQL с того же major-версии (сложнее и реже нужно);
  • любой архив, который вы сохранили до переустановки ОС.

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

Автоматический снимок на диск (рекомендуется)

Из корня проекта (пароль БД берётся из DATABASE_URL в .env):

powershell -ExecutionPolicy Bypass -File .\scripts\backup_postgres.ps1

Файл появится в backups/ (каталог в .gitignore). Копируйте .dump на другой диск или в облако. В Планировщике заданий Windows можно вызывать этот скрипт, например, раз в сутки.

Встроенный UI и API бэкапа

В приложении: страница /backup, API POST /admin/backup/run. Для реального pg_dump в PostgreSQL в .env должно быть BACKUP_PG_DUMP_ENABLED=true (и настроен каталог в настройках бэкапа). Восстановление через API — только при явных флагах безопасности в .env (BACKUP_RESTORE_ENABLED / BACKUP_PG_RESTORE_ENABLED), см. .env.example.

Дополнительно в этом модуле:

  • Проверка после копии (BACKUP_VERIFY_AFTER_RUN): для PostgreSQL вызывается pg_restore -l, для SQLite — PRAGMA integrity_check; при ошибке проверки файл копии всё равно сохраняется, детали — в поле backup_meta записи журнала.
  • Архив generated/: в настройках бэкапа можно включить упаковку каталога GENERATED_DIR в ZIP рядом с дампом; скачивание: GET /admin/backup/files/{id}?artifact=generated.
  • Минимум свободного места: BACKUP_MIN_FREE_DISK_MB (0 = не проверять).
  • Плановый бэкап: после сохранения настроек расписание перечитывается планировщиком (без обязательного перезапуска uvicorn).
  • Параллельный запуск копии и восстановления с одного процесса блокируется ответом 409 backup_or_restore_busy (в т.ч. между разными администраторами).

Ручное восстановление из *.dump (кратко)

Остановите приложение. Под суперпользователем PostgreSQL пересоздайте базу viko (или восстановите в новое имя и поменяйте DATABASE_URL), затем:

pg_restore -h localhost -p 5432 -U postgres -d viko --no-owner --role=viko путь\к\файлу.dump

Точные ключи (--clean, владелец объектов) зависят от вашей политики; при ошибках прав смотрите лог pg_restore.

Подгрузка данных без полного дампа

  • Журнал ДДС: в веб-интерфейсе учёта — импорт Excel, эндпоинты GET /finance/money-journal/import-hints и POST /finance/money-journal/import-excel (формат описан в подсказках).
  • Заказы: массовая загрузка логистом — POST /logist/orders/bulk (.xlsx, см. README в блоке Assumptions / Limitations и bulk_upload в коде).

Переменные окружения (опционально)

  • PDF с кириллицей (журнал ДДС, заказ-наряд): используются системные TTF (Windows: Arial/Segoe; Linux: DejaVu/Liberation) или файл static/fonts/DejaVuSans.ttf в корне проекта.
  • Отправка заказ-наряда по e-mail (POST /work-orders/{id}/send-to-client): задайте SMTP_HOST, SMTP_PORT (по умолчанию 587), при необходимости SMTP_USER, SMTP_PASSWORD, SMTP_FROM_EMAIL, SMTP_USE_TLS (по умолчанию true). Без SMTP_HOST письмо не уходит (остальные каналы — как реализованы в коде).
  • OCR чека (POST /driver/receipt-ocr): pip install pytesseract Pillow уже в requirements.txt; нужен установленный Tesseract в PATH и языковые данные (rus, eng или rus+eng).

Main Endpoint Groups

  • /auth/login
  • /users, /employees, /cars, /counterparties, /counterparty-logists
  • /orders, PATCH /orders/{id} (черновик / согласование), /trips, /orders/public, /logist/orders, /logist/orders/bulk, /dispatcher/orders/*
  • /supplier/rental-requests, approve/reject, /supplier/rental-requests/{id}, /driver/trips, /driver/issues, /issues, /work-orders
  • Списки: query-параметры limit (1–200, по умолчанию 50) и offset (по умолчанию 0) на основных GET-списках (в т.ч. core: пользователи, сотрудники, машины, контрагенты; заказы, рейсы, финансы, сервис, payroll, уведомления).
  • Логистика: назначение машины только для заказа в статусе approved и без уже созданного активного рейса (отменённые рейсы не мешают повторному назначению); согласование — из draft / pending_approval; отклонение заказа недоступно для уже назначенных/завершённых.
  • Отмена: POST /dispatcher/trips/{id}/cancel (диспетчер/админ) — рейс в cancelled, заказ из car_assigned возвращается в approved, связанные заявки аренды в pending/approvedcancelled. POST /supplier/rental-requests/{id}/cancel — отмена только pending (поставщик по своей заявке или диспетчер/админ).
  • CORS: переменная CORS_ORIGINS* или список через запятую, например https://vikoauto.ru,https://perevoz35.ru.
  • Сервис: POST/GET /parts, строки запчастей в заказ-наряде POST/GET /work-orders/{id}/parts, осмотр POST /work-orders/{id}/inspection; списание со склада при добавлении запчасти в наряд.
  • /payroll/* — ставки, учёт времени, начисления, подтверждения зарплаты
  • /invoices, /payments, /expenses (списки и GET по id), /reports/pnl*
  • /orders/{id}, /trips/{id}, /mechanic/issues/{id}, /work-orders/{id}
  • /cms/*, /admin/cms/*
  • /notifications/send, /notifications/me, PATCH /notifications/{id}/read, /ws/notifications
  • /reports/*, /admin/reports/templates

Test Scenario

Интеграционный сценарий:

python test_scenario.py

По умолчанию используется http://localhost:8000.

Юнит/интеграционные проверки API на SQLite in-memory:

pytest tests -q

Assumptions / Limitations

  • Авторизация по коду 1234 реализована как dev-only MVP.
  • Email / SMS / push выполнены как stub-каналы, основная рабочая реализация - in-app уведомления.
  • bulk upload: для .xlsx первая строка — заголовки колонок source, destination, опционально weight_kg, volume_m3, client_phone, client_price. Иные расширения — позже.
  • PDF и Excel генерируются в простом базовом формате.
  • P&L в MVP считается как payments - expenses.
  • Idempotency-Key поддержан для driver-confirm/complete trip и создания issue.
  • payroll: REST API для ставок, учёта времени, начислений и подтверждений зарплаты (admin/dispatcher); расчёт ведомостей можно наращивать отдельно.
  • Админский CMS CRUD сделан в MVP-формате: полный рабочий create/list плюс generic update/delete.
  • WorkOrder из online appointment создается в упрощенном виде с машиной по умолчанию car_id=1.
  • Логист контрагента в GET /orders и GET /orders/{id} видит заказы, привязанные к его контрагентам (counterparty_logists), и заказы, созданные им самим.
  • В seed добавлены демо-связи: поставщик и логист привязаны к демо-контрагентам; у логиста — контрагент Logist Partner Demo.