TKBK Studio
TKBK Studio
Выручка, заказы, возвраты, остатки с прогнозом и юнит-экономика трёх маркетплейсов на одной странице
Свой дашборд для селлера, который торгует на Wildberries, Ozon и Яндекс Маркете. Раз в час приложение забирает заказы, выкупы, возвраты и остатки по официальным API и складывает их в локальную базу SQLite — история копится, даже если площадка хранит данные недолго.
На странице — выручка, заказы и возвраты по дням на графике, топ товаров, остатки с прогнозом «закончится через N дней» и юнит-экономика по вашей себестоимости из costs.csv: комиссия, логистика, прибыль, маржа и ROI по каждому артикулу. Утром бот присылает сводку за вчера в Telegram.
В отличие от сервисов аналитики по подписке, данные остаются у вас, ключи — только на чтение, а логику расчёта можно поменять под себя. Площадки подключаются независимо: достаточно ключа хотя бы одной.
Для кого: Селлеры маркетплейсов, менеджеры и аналитики магазинов
Ключи и токены не вводятся на сайте — их кладут в файл .env на своём сервере.
WB_TOKENТокен API Wildberries — ЛК → Настройки → Доступ к API → Персональный токен, категории «Статистика» и «Аналитика», только чтениеOZON_CLIENT_IDClient-Id Ozon — ЛК Ozon → Настройки → API-ключиOZON_API_KEYAPI-ключ Ozon — ЛК Ozon → Настройки → API-ключи → роль Admin read onlyYM_API_KEYAPI-ключ Яндекс Маркета — Кабинет → Настройки → API и модули → токен с доступом на чтение заказов и товаровYM_BUSINESS_IDID кабинета Яндекс Маркета — Необязательно: найдётся автоматическиYM_CAMPAIGN_IDSID магазинов Яндекс Маркета через запятую — Необязательно: по умолчанию все магазины кабинетаTELEGRAM_BOT_TOKENТокен Telegram-бота для утреннего отчёта — @BotFather → /newbotTELEGRAM_CHAT_IDID чата для отчёта — Добавьте бота в чат; ID можно узнать у @getmyid_botDASHBOARD_PASSWORDПароль входа на дашборд — Пусто — временный пароль выводится в лог при запуске# {{SHOP_NAME}} — дашборд продаж WB + Ozon + Яндекс Маркет
Одна страница со всеми продажами: выручка, заказы и возвраты по дням, топ товаров, остатки с прогнозом
«закончится через N дней» и юнит-экономика по вашей себестоимости. Данные забираются по официальным API
раз в час и копятся в локальной базе SQLite — история не теряется, даже если площадка хранит её недолго.
Утром бот присылает в Telegram сводку за вчера.
## Что умеет
- **Wildberries** — заказы, выкупы и возвраты (Statistics API), остатки на складах WB и на своих складах
(отчёт «Остатки на складах» из Analytics API).
- **Ozon** — отправления FBS и FBO (`/v4/posting/fbs/list`, `/v3/posting/fbo/list`), остатки по всем схемам.
- **Яндекс Маркет** — заказы всего кабинета (`POST /v1/businesses/{id}/orders`), остатки каждого магазина.
- Каждая площадка включается сама, если в `.env` есть её ключ. Ошибка одной площадки не мешает остальным.
- Прогноз остатков: остаток ÷ средние заказы в день за последние N дней. Товары, которых хватит меньше
чем на заданное число дней, подсвечены красным.
- Юнит-экономика: себестоимость, комиссия и логистика из `costs.csv` → прибыль, маржа, ROI по каждому товару.
- Утренний отчёт в Telegram в заданный час (по Москве).
- Вход на дашборд по логину и паролю.
## Запуск за 5 минут
### С Docker
```bash
cp .env.example .env # впишите ключи площадок (ниже — где их взять)
docker compose up -d --build
```
Откройте http://localhost:8000 (логин `admin`, пароль из `DASHBOARD_PASSWORD`).
Если пароль не задан, временный пароль будет в логах: `docker compose logs dashboard`.
### Без Docker (Python 3.12)
```bash
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # Windows: copy .env.example .env
python -m app
```
Первая синхронизация стартует через 5 секунд после запуска и для Wildberries может занять пару минут
(у WB жёсткие лимиты: 1 запрос в минуту к статистике, 3 в минуту к отчёту об остатках). Кнопка
«Обновить сейчас» запускает синхронизацию вручную.
## Где взять ключи
### Wildberries
1. Личный кабинет продавца → **Настройки → Доступ к API**.
2. «Создать токен» → тип **Персональный** (для своих интеграций).
3. Отметьте категории **Статистика** и **Аналитика**, включите **«Только на чтение»**.
4. Скопируйте токен в `WB_TOKEN`. Токен действует 180 дней — поставьте напоминание.
Без категории «Аналитика» заказы будут, а остатки — нет (в статусе на странице появится ошибка 401/403).
### Ozon
1. Личный кабинет → **Настройки → API-ключи** (нужны права администратора).
2. «Сгенерировать ключ», роль **Admin read only** — дашборду не нужны права на запись.
3. `Client-Id` → `OZON_CLIENT_ID`, ключ → `OZON_API_KEY` (ключ показывается один раз).
### Яндекс Маркет
1. Кабинет продавца → **Настройки → API и модули** → блок «Авторизационные токены».
2. «Создать новый токен», доступ **«Обработка заказов и товаров»** или **«Все методы»** с пометкой
«только чтение».
3. Токен → `YM_API_KEY`. ID кабинета и магазинов приложение найдёт само; чтобы ограничить список,
укажите `YM_BUSINESS_ID` и `YM_CAMPAIGN_IDS` (их видно в адресной строке кабинета).
### Telegram (необязательно)
1. Напишите [@BotFather](https://t.me/BotFather) → `/newbot` → токен в `TELEGRAM_BOT_TOKEN`.
2. Добавьте бота в чат и узнайте ID чата (например, через @getmyid_bot) → `TELEGRAM_CHAT_ID`.
3. Отчёт приходит каждый день в {{REPORT_HOUR}}:00. Проверить сразу: `curl -u admin:ПАРОЛЬ -X POST http://localhost:8000/report`.
## Себестоимость: costs.csv
```
marketplace;sku;cost;commission_pct;logistics
*;*;;0;0
wb;*;;25;90
ozon;*;;22;80
*;ART-001;450;;
wb;ART-002;300;27;110
```
- `marketplace` — `wb`, `ozon`, `ym` или `*` (все), `sku` — артикул продавца или `*`.
- Каждое поле берётся из самого точного правила, где оно заполнено: (площадка, артикул) → (*, артикул) →
(площадка, *) → (*, *). Так можно задать комиссию площадки один раз, а себестоимость — по артикулам.
- Дробные числа можно писать через запятую. Файл перечитывается при каждом открытии страницы.
- В Docker файл подключён из папки проекта — правьте его, перезапуск не нужен.
## Как считаются цифры
| Показатель | Wildberries | Ozon | Яндекс Маркет |
|---|---|---|---|
| Заказы | строки заказов, по дате заказа | все отправления | все заказы |
| Выручка (выкупы) | продажи (saleID на S), по дате продажи | отправления в статусе `delivered` | статусы DELIVERED, PARTIALLY_RETURNED |
| Возвраты и отмены | возвраты (R) и отменённые заказы | статус `cancelled` | CANCELLED, RETURNED |
| Сумма | цена со скидкой продавца (`priceWithDisc`) | цена товара × количество | payment + cashback + subsidy |
Для Ozon и ЯМ выкупы и отмены привязаны к дате заказа (так их отдаёт API списка заказов). При каждой
синхронизации перезагружаются последние `REFRESH_DAYS` дней, поэтому смена статусов подтягивается.
## Настройки из мастера
Значения, выбранные в TKBK Studio, лежат в `app/config.py` (константы `WIZARD_*`) — их можно поменять там
или переопределить в `.env`: `SHOP_NAME`, `HISTORY_DAYS`, `FORECAST_DAYS`, `LOW_STOCK_DAYS`, `REPORT_HOUR`, `ACCENT`.
## Тесты
```bash
pip install pytest==9.1.1
pytest
```
Тесты не ходят в сеть: ответы API подменены `httpx.MockTransport`.
## FAQ
**Почему цифры не совпадают с отчётом о реализации?** Statistics API WB и списки заказов — оперативные
данные, а не бухгалтерия: комиссии и логистика здесь — ваши оценки из `costs.csv`. Для точного учёта
используйте финансовые отчёты площадок (идея для доработки ниже).
**WB отвечает 429.** Это лимит площадки. Приложение само ждёт и повторяет запрос (по заголовку
`X-Ratelimit-Retry`). Не ставьте `SYNC_MINUTES` меньше 30.
**У ЯМ ошибка 420.** Так Маркет сообщает о превышении лимита — приложение повторит запрос с паузой.
**Остатки WB показаны как «WB 123456».** В отчёте об остатках WB нет артикула продавца — он берётся из
истории заказов. После первых заказов по товару появится артикул.
**Сколько истории можно загрузить?** WB хранит заказы 90 дней, ЯМ отдаёт до 30 дней за запрос (приложение
само режет период), Ozon — до года. Дальше история копится в базе `data/sales.db`.
## Ограничения API
- WB Statistics: 1 запрос в минуту на метод, до 80 000 строк за запрос, данные обновляются раз в 30 минут.
- WB отчёт об остатках: 3 запроса в минуту; старый метод `/api/v1/supplier/stocks` WB отключил 23.06.2026.
- Ozon: списки отправлений — до 100 штук на страницу и до года на запрос.
- ЯМ: заказы — 50 на страницу, период до 30 дней; метод `GET /v2/campaigns/{id}/orders` объявлен устаревшим,
поэтому используется `POST /v1/businesses/{businessId}/orders`.
Оценка 0–10 после заказа: довольных — на Яндекс Карты, 2ГИС и WB, недовольных — сразу к менеджеру
Название, описание и характеристики под правила каждой площадки — по одному товару или сотнями из CSV
Претензии покупателей по артикулам — размер, качество, брак, упаковка — и что поправить в карточке