Анализ ТЗ партнёра 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

  1. Таблицы Client + Deal (миграция через Alembic — пора ставить).
  2. CRUD клиентов в админке: список, карточка с историей сделок и оборотом.
  3. Создание сделки: форма с автокомплитом клиента, валютной парой, курсом.
  4. Авто-создание операций из сделки (give→cash_in_op, get→cash_out_op).
  5. Расчёт прибыли по сделке: пока без TG-парсинга курса, market_rate вводится вручную (или из placeholder FX_TO_USDT).
  6. Мультивалютная касса: страница «Касса» с балансами по валюте × отделение, кнопка «Внести/Изъять» (cash_movement).
  7. Дашборд обновить: KPI «активные сделки», «прибыль за день», «топ-3 клиента дня».

Уточнённый план E1 (с зафиксированными решениями)

1. Миграция: ставим Alembic. Первая миграция — добавить clients, deals, cash_movements, cash_shift (открытие смены), markups. В operationsdeal_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

  1. Список реальных направлений (валютная пара → менеджер). Пример: «USD↔EUR Варшава → Дмитрий», «USD↔TRY Стамбул → Mert». Без этого маршрутизация в E3 будет неточная — но в E1 это пригодится для определения "кто ведёт сделку".
  2. Категории клиентов — порог по обороту для VIP. Например: «VIP с оборота 100k USDT», «постоянный с 3+ сделок».
  3. Источник market_rate — пока он будет ручной или из FX_TO_USDT. На E2 подключим Binance API / TG-каналы.
  4. Бэкапы: ежедневный pg_dump в S3 (Hetzner Object Storage?) или локально? Пока сделаю локальный + объясню как S3.

6. Зафиксированные решения (от 2026-05-14)

  1. UI: Главный экран = Сделки. Операции уходят в раздел «Audit» — служебный журнал для бухгалтера/админа. Меню сайдбара переделать: «Сделки» наверху, «Операции» внизу под аудитом.
  2. Открытие смены обязательно. Кассир при входе вводит начальный остаток по каждой валюте. Балансы = начальный остаток смены + дельта операций. Это закрывает проблему «минусовых балансов» из critique-v0.4.
  3. Прибыль = (applied_rate − indicative_rate) × amount, оба курса вводятся вручную на форме сделки. Индикативный курс — это референс с хеджа/биржи, который менеджер знает (или ему скажет трейдер). До E2 не парсим автоматически — менеджер вводит руками. На E2 подключим автозаполнение из Binance/TG.
  4. Бэкапы локально: pg_dump в ~/backups/obmenka/ ежедневно через systemd timer, retention 30 дней. Single-server SPOF осознан — расширим до S3 позже.
  5. Реальные направления (валютная пара → менеджер) — пользователь соберёт у руководителя. На 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.