Небольшая CRM для соло-предпринимателя или менеджера, который ведёт клиентов и сделки в голове или в блокноте, а отчёт для себя/бухгалтера/партнёра собирает вручную раз в неделю. Здесь эта ручная сборка отчёта заменена одной кнопкой: приложение само считает сводку и кладёт готовую таблицу в Google Drive пользователя.
Собрано как демонстрация того, как быстро можно закрыть подобную задачу для небольшого бизнеса: рабочая база данных, API, веб-интерфейс и интеграция с Google — без тяжёлой CRM-платформы и её лицензий.
- Фрилансеру/менеджеру, который ведёт 1–2 источника правды (клиенты, сделки, задачи) и не хочет платить за Bitrix24/amoCRM ради трёх таблиц.
- Владельцу небольшого бизнеса, которому нужен понятный отчёт «что там с клиентами и сделками» в Google-таблице — чтобы переслать партнёру или бухгалтеру, а не открывать ему доступ в CRM.
- Клиенты: карточка, статус (активен/архив), поиск по имени/компании/email.
- Сделки: сумма, статус (открыта/выиграна/проиграна), привязка к клиенту.
- Задачи: срок, статус выполнения, привязка к клиенту или сделке.
- Кнопка «Выгрузить отчёт» в каждом разделе: создаёт Google-таблицу в папке пользователя на Drive, считает сводку (количество, суммы, средний чек, топ-показатели) и форматирует таблицу (жирная шапка, закреплённая строка, автоширина колонок).
- Авторизация в Google происходит от лица пользователя, в его браузере — не на сервере — и кешируется, чтобы не логиниться заново при каждом запросе.
┌────────────┐ REST /api ┌──────────────┐
│ Веб-интерфейс │ ───────────────▶ │ FastAPI │
│ (vanilla JS) │ ◀─────────────── │ backend │
└────────────┘ └──────┬───────┘
│ SQLite
▼
data/crm.db
│
(по кнопке "Выгрузить отчёт")
▼
google_integration/
├─ google_auth.py OAuth2, кеш токена
├─ google_drive.py создание файла в папке
└─ google_sheets.py запись + форматирование
│
▼
Google Drive пользователя
Интерфейс — не Tkinter, а обычная веб-страница поверх того же FastAPI: тот
же API можно дергать и из Swagger (/docs), и из своего фронтенда, и из
другого клиента.
- Правильный расчёт средних значений. В отчёте по сделкам средняя сумма
сделки считается как
sum(amount) / count, независимо от общей суммы — это код вbackend/app/reports.py, вынесенный отдельно от вьюх именно чтобы такие расчёты было легко проверить и покрыть тестами. - OAuth2 в контейнере. Установленное приложение (installed app flow) не
может открыть браузер внутри Docker-контейнера — поэтому ссылка для
авторизации печатается в лог контейнера, а обратный вызов принимается на
фиксированном порту (
OAUTH_REDIRECT_PORT), который явно опубликован вdocker-compose.yml. Токен сразу же кешируется на диск, поэтому повторного логина при каждом запросе не происходит. - Один Docker Compose, hot reload. Код примонтирован в контейнер, а
uvicorn --reloadперезапускает сервер при изменении файлов — не нужно пересобирать образ на каждую правку.
- В Google Cloud Console создайте
проект, включите Google Drive API и Google Sheets API, создайте
OAuth-клиент типа «Desktop app» и добавьте redirect URI
http://localhost:8765/. Скачайтеclient_secret.json. - Положите
client_secret.jsonв папкуdata/в корне проекта — она примонтирована в контейнер, это единственный способ достать файл с хоста внутрь Docker. - Скопируйте
.env.exampleв.env(порт для OAuth-редиректа можно не менять). - Запустите:
docker compose up --build
- Откройте
http://localhost:8000— это интерфейс. Раздел Настройки Google → укажите путьdata/client_secret.jsonи (по желанию) ID папки на Google Drive для отчётов. - Нажмите «Выгрузить отчёт» в любом разделе — при первом запросе откроется ссылка для авторизации в консоли контейнера, откройте её в браузере на своей машине.
Демо-данные:
pip install requests
python fill_test_data.py # наполнит клиентов/сделки/задачи тестовыми даннымиFastAPI · SQLite (без ORM) · google-api-python-client · google-auth-oauthlib · ванильный JS/CSS фронтенд · Docker Compose
- Права доступа/авторизация в самом приложении (сейчас однопользовательский сценарий).
- Формулы вместо предвычисленных сумм в самой Google-таблице — на случай, если данные потом будут править руками прямо в Drive.
- Экспорт по расписанию (например, через APScheduler) вместо ручной кнопки.