# Анализ ТЗ партнёра 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. Изменения в модели данных ### Добавить таблицы ```sql -- Клиенты 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`. В `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 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.