Анализ ТЗ партнёра vs текущая система v0.5
Дата: 2026-05-14 Источник ТЗ: партнёр пользователя, написано «когда-то» (старое видение). Цель документа: разложить ТЗ на закрытое vs новое, составить план реализации.
1. Главное расхождение в ментальной модели
| Наша архитектура v0.3 → v0.5 | ТЗ партнёра | |
|---|---|---|
| Центральная сущность | Операция (движение валюты) | Сделка (обмен с клиентом) |
| Что обязательно | Касса / кошелёк, сумма, актив | Клиент, валютная пара, курс, менеджер |
| Что измеряем | Расхождение касса ↔ блокчейн | Прибыль по сделке, оборот клиента |
| TG-роль | Контроль менеджеров (мониторинг диалогов) | Бот клиентам + парсинг курсов + автопостинг |
Вывод: обе модели валидны и совместимы. Сделка — это бизнес-уровень. Операция — это бухгалтерский уровень. 1 сделка = 1–2 операции (взяли у клиента + отдали клиенту). Объединяем: модель Deal поверх Operation со ссылкой Operation.deal_id.
2. Покрытие модулей ТЗ
| Модуль | Закрыто в v0.5 | Не закрыто |
|---|---|---|
| М1. Учёт сделок | у нас есть Operation с branch/amount/asset/note. Аудит. |
нет Deal, нет Client, нет курса сделки, нет статуса (new/in_work/done/cancel), нет привязки менеджера, нет комментариев. |
| М1. База клиентов | — | нет вообще. Нужна таблица clients с ФИО / @username / категорией (VIP/новый/постоянный) / историей / оборотом. |
| М2. Мультивалютная касса | агрегация наличных по operations есть (но косвенная) | нет таблицы cash_box, нет открытия смены, нет инкассации (cash_collection), нет автопересчёта после сделки как явного события. |
| М2. Прибыль по сделке | плейсхолдер 2% от оборота | нет applied_rate и market_rate в операции; нет таблицы курсов; нет расчёта дельты. |
| М2. Отчёты день/неделя/месяц | дашборд по периодам — есть | нет «отчёта по менеджеру» и «по валютной паре» (только по отделению). |
| М2. Журнал инкассации | — | нет вообще. Нужны cash_movement-операции типа collection_in/out. |
| М3. Парсинг курсов из TG | — | нет вообще. Нужны: tg_channels (source), парсер (regex/LLM), fx_rates_history. |
| М3. Своя наценка к курсу | — | нет. Нужно поле markup_pct на валютную пару / отделение. |
| М4. Автопостинг курсов в TG | — | нет. Нужны: tg_channels (broadcast), post_templates, post_schedule, post_log. |
| М5. Чат-бот для клиентов | — | нет. Нужен TG-бот с FSM (приветствие / FAQ / сбор данных). |
| М6. Подготовка заявки для менеджера ⭐ | — | нет. Это самый ценный модуль ТЗ: автокарточка для менеджера + маршрутизация + LLM-генерация ответа. |
| М7. Веб-интерфейс | 8 экранов, RBAC, темная тема, mobile — есть | нет: страницы клиентов, шаблонов автопостинга, настроек курсов и наценок. |
| НФТ. Скорость <200мс | сейчас 50мс на 200 строк | держим |
| НФТ. Бэкап БД ежедневно | — | нет cron pg_dump |
| НФТ. Логирование операций с кассой | login/logout пишутся, биз-действий нет (нечего логировать ещё) | надо записывать каждое движение по кассе |
| НФТ. 2FA для менеджеров | пароль + cookie | нет TOTP/SMS |
| НФТ. Адаптивный UI | сделано в v0.5 | держим |
Общий вывод: у нас закрыты ~25% ТЗ (учётная база + auth + аудит + дашборд + операции). Остальные 75% — это новая функциональность, разложенная по 4 фазам ТЗ.
3. Изменения в модели данных
Добавить таблицы
-- Клиенты
clients (
id PK, external_ref UNIQUE, -- внутренний идентификатор (например, "C-2204")
name, tg_username, phone, email,
category ENUM('new','regular','vip'),
total_volume_usdt NUMERIC, -- денормализованный кэш
first_seen_at, last_seen_at,
note
)
-- Сделки (обмен с клиентом, 1-2 операции)
deals (
id PK, client_id FK,
occurred_at TIMESTAMPTZ,
branch_id FK, -- город/офис
manager_user_id FK, -- кто вёл сделку
status ENUM('new','in_work','done','cancelled'),
give_asset, give_amount NUMERIC, -- что отдал клиент
get_asset, get_amount NUMERIC, -- что получил клиент
applied_rate NUMERIC, -- наш курс
market_rate NUMERIC, -- рыночный (с биржи или из TG)
profit_usdt NUMERIC, -- расчётная прибыль
comment TEXT
)
-- Операции связаны со сделкой
ALTER TABLE operations ADD COLUMN deal_id FK NULLABLE;
ALTER TABLE operations ADD COLUMN applied_rate NUMERIC NULLABLE;
-- История курсов
fx_rates (
id PK, asset_from, asset_to,
rate NUMERIC, source ENUM('market','tg-parsed','manual'),
source_ref TEXT, -- ссылка на сообщение / биржу
ts TIMESTAMPTZ
)
-- Наценки по валютной паре + отделению
markups (
id PK, asset_from, asset_to,
branch_id FK NULLABLE, -- NULL = глобальная
markup_pct NUMERIC,
is_active BOOLEAN
)
-- Движения по кассе (инкассация, расходы, открытие смены)
cash_movements (
id PK, branch_id FK,
asset, amount NUMERIC, -- + или −
kind ENUM('shift_open','collection_in','collection_out','expense','adjust'),
reason TEXT, user_id FK, ts
)
-- TG-каналы (источники и приёмники)
tg_channels (
id PK, tg_id BIGINT, title,
role ENUM('source','broadcast'),
parse_template TEXT NULLABLE, -- regex/спец-формат для source
is_active
)
-- Сырые сообщения из TG (для парсинга и аудита)
tg_messages_raw (
id PK, channel_id FK, msg_id,
text, ts, parsed_at NULLABLE
)
-- Лог автопостинга
post_log (
id PK, channel_id FK, template_id FK,
sent_at, status ENUM('ok','retry','failed'),
tg_msg_id NULLABLE, -- для edit вместо повторного post
error TEXT
)
-- Шаблоны (для автопостинга и для ответов клиенту)
templates (
id PK, name,
kind ENUM('broadcast_rates','client_reply','client_faq'),
content TEXT, -- с placeholder'ами {{usd_rate}} и т.п.
is_active
)
-- Маршрутизация заявок
routes (
id PK,
give_asset NULLABLE, get_asset NULLABLE,
city NULLABLE,
manager_user_id FK,
priority INT
)
-- Заявки от клиент-бота (lead)
leads (
id PK, client_id FK NULLABLE,
channel ENUM('tg-bot','manual'),
city, give_asset, give_amount, get_asset,
status ENUM('new','assigned','accepted','rejected','converted'),
assigned_to_user_id FK NULLABLE,
deal_id FK NULLABLE, -- если конвертирован в сделку
raw_dialog JSONB,
created_at, updated_at
)
Изменения существующих
operations.deal_id— связь со сделкой (для cash + crypto leg одной сделки).operations.applied_rate,operations.market_rate— для прибыли.branches.country— нужно для маршрутизации (сейчас толькоcity).tg_flags(E4-наша) остаётся как есть.
4. Маппинг фаз ТЗ на наши этапы
ТЗ задаёт 4 фазы по приоритету. Сопоставляю с нашими этапами:
| Фаза ТЗ | Эквивалент в нашей дорожной карте | Время |
|---|---|---|
| MVP (1–2 недели): сделки + касса + клиенты | E1 (расширенный): модель Deal+Client, ручной ввод сделок, прибыль, мультивалютная касса, инкассация, дашборд переделать под сделки | 1–2 недели |
| Фаза 2: парсинг + автопостинг курсов | E2 (новый): TG-парсер курсов + БД курсов + наценки + шаблоны и расписание автопостинга. Плюс наш E2 про блокчейн ETL — оставить параллельно как E2.5 | 2 недели |
| Фаза 3: клиент-бот + автоподготовка заявок | E3 (новый): FSM-бот, FAQ, сбор данных, маршрутизация, карточка-заявка менеджеру с кнопками | 2–3 недели |
| Фаза 4: аналитика, отчёты, расширения | E4 + E5: отчёты по менеджеру/паре/городу, knowledge base, 2FA, бэкапы | 1–2 недели |
| (наша добавка) | E6 (наш TG-мониторинг менеджеров) — оставляем, переносим в конец | 1 неделя |
Итоговая дорожная карта:
E0 ✅ Скелет + docs viewer + auth + admin v0.5
E1 (1-2 нед) Сделки + Клиенты + Касса + Инкассация + Прибыль ← MVP по ТЗ
E2 (2 нед) Парсинг курсов из TG + Автопостинг курсов
E2.5 (1-2 нед) Блокчейн ETL (наш — для on-chain сверки)
E3 (2-3 нед) Клиент-бот + Маршрутизация + Карточка-заявка ⭐
E4 (1-2 нед) Отчёты + 2FA + Бэкапы
E5 (1 нед) TG-мониторинг менеджеров (наша добавка)
E6 (опц) Knowledge base + полировка CRM
5. Что предлагаю делать ПЕРВЫМ (E1 расширенный)
Цель E1 (1.5–2 недели)
Менеджер открывает админку, видит «новая сделка», заполняет: клиент (выбор из базы или создание), отдаёт X USD, получает Y EUR, применил курс Z. Система сама создаёт две операции, считает прибыль (Z − market_rate) × amount, обновляет балансы кассы и оборот клиента.
Этапы внутри E1
- Таблицы Client + Deal (миграция через Alembic — пора ставить).
- CRUD клиентов в админке: список, карточка с историей сделок и оборотом.
- Создание сделки: форма с автокомплитом клиента, валютной парой, курсом.
- Авто-создание операций из сделки (give→cash_in_op, get→cash_out_op).
- Расчёт прибыли по сделке: пока без TG-парсинга курса, market_rate вводится вручную (или из placeholder FX_TO_USDT).
- Мультивалютная касса: страница «Касса» с балансами по валюте × отделение, кнопка «Внести/Изъять» (cash_movement).
- Дашборд обновить: KPI «активные сделки», «прибыль за день», «топ-3 клиента дня».
Уточнённый план E1 (с зафиксированными решениями)
1. Миграция: ставим Alembic. Первая миграция — добавить clients, deals, cash_movements, cash_shift (открытие смены), markups. В operations — deal_id, applied_rate, market_rate (для совместимости с прошлыми операциями — nullable).
2. Сделка (страница «Новая сделка»):
- Выбор клиента (autocomplete по @username / ФИО, или «+ новый клиент» прямо в форме).
- Город/отделение (для cashier — фиксируется).
- Валютная пара: отдаёт X (asset+amount) → получает Y (asset+amount).
- Applied_rate (наш курс): вычисляется как give_amount / get_amount или get_amount / give_amount в зависимости от пары — но можно ввести вручную и пересчитать сумму получения.
- Indicative_rate (хедж/биржа): вводится вручную менеджером.
- Прибыль в USDT считается автоматически и показывается ещё до сохранения.
- Комментарий, статус.
- При сохранении: создаются 2 операции (cash_in/out + crypto_out/in), линкуются на deal_id.
3. Открытие смены (страница «Касса»):
- Кассир видит «Смена закрыта» / «Открыта (с 09:15)».
- Кнопка «Открыть смену» → форма с начальными остатками по каждой валюте → запись в cash_shift.
- Балансы кассы во всех экранах = cash_shift.opening + sum(operations since shift_open) + sum(cash_movements since shift_open).
4. Журнал инкассации:
- Кнопка «Внести / Изъять» на странице «Касса» → форма (валюта, сумма, причина, направление).
- Запись в cash_movements.
5. Меню обновить:
Основное: Сделки (новый) | Касса (новый) | Клиенты (новый) | Дашборд
Справочники: Отделения | Кошельки | Шаблоны (новый)
Контроль: Сверка | Менеджеры
Система: Операции (бывшее main) | Аудит | Инструкции
6. Дашборд переделать: - KPI: оборот в USDT (есть), прибыль USDT (новый, считается из сделок), активные сделки (status in_work), флаги. - Виджет «Топ-3 клиента дня» (по обороту). - График — по сделкам, не операциям.
7. Бэкап:
- ~/bin/obmenka-backup.sh: pg_dump obmenka > ~/backups/obmenka/$(date +%Y%m%d-%H%M).sql.gz.
- systemd timer ежедневно в 03:00.
- Скрипт ротации: оставлять последние 30.
Что НЕ делаем в E1
- Парсинг и автопостинг курсов (E2).
- Клиент-бот (E3).
- Любая интеграция с реальным TG.
- Реальные блокчейн-данные (остаются seed).
- 2FA (E4).
- LLM для генерации ответов клиенту (E3).
Что нужно от тебя для старта E1
- Список реальных направлений (валютная пара → менеджер). Пример: «USD↔EUR Варшава → Дмитрий», «USD↔TRY Стамбул → Mert». Без этого маршрутизация в E3 будет неточная — но в E1 это пригодится для определения "кто ведёт сделку".
- Категории клиентов — порог по обороту для VIP. Например: «VIP с оборота 100k USDT», «постоянный с 3+ сделок».
- Источник market_rate — пока он будет ручной или из FX_TO_USDT. На E2 подключим Binance API / TG-каналы.
- Бэкапы: ежедневный pg_dump в S3 (Hetzner Object Storage?) или локально? Пока сделаю локальный + объясню как S3.
6. Зафиксированные решения (от 2026-05-14)
- ✅ UI: Главный экран = Сделки. Операции уходят в раздел «Audit» — служебный журнал для бухгалтера/админа. Меню сайдбара переделать: «Сделки» наверху, «Операции» внизу под аудитом.
- ✅ Открытие смены обязательно. Кассир при входе вводит начальный остаток по каждой валюте. Балансы =
начальный остаток смены + дельта операций. Это закрывает проблему «минусовых балансов» из critique-v0.4. - ✅ Прибыль =
(applied_rate − indicative_rate) × amount, оба курса вводятся вручную на форме сделки. Индикативный курс — это референс с хеджа/биржи, который менеджер знает (или ему скажет трейдер). До E2 не парсим автоматически — менеджер вводит руками. На E2 подключим автозаполнение из Binance/TG. - ✅ Бэкапы локально:
pg_dumpв~/backups/obmenka/ежедневно через systemd timer, retention 30 дней. Single-server SPOF осознан — расширим до S3 позже. - ⏸ Реальные направления (валютная пара → менеджер) — пользователь соберёт у руководителя. На E1 пока хардкодим маршрутизацию = «отделение менеджера» (он же — ответственный за свои сделки).
7. Риски и допущения
- Риск: Переход от «операционной» к «сделочной» модели — это инвазивная миграция. У нас есть 205 seed-операций без
deal_id. После миграции они становятся «осиротевшими». Решение: либо пометить какlegacy, либо удалить и перегенерировать seed под новые сделки. - Допущение: ТЗ написано «когда-то», некоторые требования могли устареть. Например, «новый пост или редактирование закреплённого» в М4 — нужно подтвердить, что edit pinned message всё ещё актуально.
- Допущение: «LLM-разбор» в М3 — это можно сделать на Claude API. Стоимость ~$0.001 за разбор сообщения с курсом — копейки.
- Риск: Клиент-бот на одном TG-аккаунте быстро упрётся в rate-limit, если каналов и пользователей много. На E3 решим — bot API или user API (Telethon).
- Допущение: Наша «контроль леваков» через MTProto менеджеров — фича из нашей инициативы, не из ТЗ. Оставляем как E5.
8. Что НЕ делать сейчас
Большой соблазн — начать «вакуумную уборку» всего. Этого не делаем:
- Не переписываем дашборд под «активные сделки» до того, как появится таблица deals.
- Не интегрируем TG до E2.
- Не делаем 2FA на старте E1 — это финальный полирующий шаг (E4).
- Не ставим React. Jinja + HTMX + Alpine.js потянут все интерактивы CRM. React оправдан только если UI станет полу-SPA — это не E1.