Модуль «Партнёры» — внутренний учёт в CRM Obmenka
Версия: 1.0 (после уточнения цели)
Дата: 2026-05-16
Заменяет: affiliate-prd-v2.md в части реализации (исследования остаются как backup-материал)
1. Цель модуля
Внутренний CRM-модуль для учёта того, кто привёл каждого клиента и сколько мы должны этому человеку по каждой сделке.
Это НЕ публичная affiliate-программа с кабинетом, ref-ссылками и маркетингом. Это учётная книга партнёров в нашей CRM. Все данные ведут менеджеры вручную, партнёры в систему не заходят.
2. Бизнес-задача
2.1. Что болит сейчас
У руководителя (и менеджеров) нет единого места, где зафиксировано: - Кто привёл каждого клиента - Контакты этого «привёдшего» (телефон, TG) - На каких условиях мы работаем с этим клиентом (например: «10% от маржи отдаём Ахмету» или «$5 с каждой сделки») - Сколько всего начислено партнёру по этому клиенту - Сколько уже выплачено, сколько остаётся должны - Статистика по партнёру в целом: сколько клиентов привёл, какой оборот, общая комиссия
Сейчас это всё в голове, в WhatsApp-переписках, в Excel-табличке менеджера. При смене менеджера информация теряется. При споре с партнёром («ты мне должен $400, нет, $250») — невозможно проверить.
2.2. Какая задача решается
После внедрения модуля: 1. У каждого клиента в карточке видно «привёл партнёр Ахмет, условия 10% от маржи» 2. Каждая сделка клиента → автоматически генерится начисление партнёру 3. В карточке партнёра видно: «должны Ахмету 425 USDT за май, выплачено 200, баланс 225» 4. При выплате партнёру (наличными в офисе / USDT-переводом) — фиксируется в системе 5. Аудит-история: кто и когда менял условия, кто начислил, кто выплатил
2.3. Почему важно сейчас
- Объём операций растёт (149 сделок за 15 дней в seed, реально ~300/мес сейчас) — учёт «в голове» не масштабируется
- Партнёры спрашивают «сколько я заработал?» — без модуля ответ занимает часы Excel-работы
- При смене кассира/менеджера привязки и условия теряются
- Это базис для будущего: когда созреем до настоящей программы с кабинетом — данные уже есть
3. Сценарии использования (user stories)
3.1. Менеджер заводит нового партнёра
«Мне Ахмет, владелец туристического агентства в Лимане, говорит — буду присылать тебе своих клиентов, дай мне 1% с их оборотов».
→ Менеджер в админке: «Партнёры» → «+ Новый партнёр»
→ Заполняет: имя «Ахмет Йылмаз», TG @ahmet_lara, телефон +90 555 ..., дефолтные условия: 1% от volume USDT
→ Сохраняет → партнёр в системе
3.2. Менеджер привязывает нового клиента к партнёру
Клиент пришёл, кассир оформляет сделку.
→ В форме новой сделки (или карточке клиента) — поле «Партнёр» с autocomplete → Кассир выбирает «Ахмет Йылмаз» → Условия по клиенту = дефолт партнёра (1% volume) — можно override на этом клиенте → Сохраняет
3.3. Менеджер ставит особые условия на конкретного клиента
«Ахмет привёл крупного клиента — для него договорились 0.5% volume, не 1%».
→ В карточке клиента → блок «Партнёр» → кнопка «Изменить условия для этого клиента» → Заполняет: 0.5% от volume USDT → С момента изменения новые сделки этого клиента считаются по 0.5% → Старые начисления не пересчитываются (snapshot)
3.4. Сделка автоматически генерит начисление партнёру
Сделка клиента переходит в done → система:
1. Видит у клиента поле partner_id → есть привязка
2. Берёт условия по этому клиенту (или дефолт партнёра если override нет)
3. Считает комиссию = 0.5% × volume_usdt
4. Создаёт запись partner_commission в статусе pending
5. Через hold-период (по умолчанию 0 — пользователь решит) → accrued (должны выплатить)
3.5. Руководитель смотрит сколько должен партнёру
→ Админка → «Партнёры» → карточка «Ахмет Йылмаз» → Видит: «4 клиента, 23 сделки, оборот $84 000, накопленная комиссия $420, выплачено $200, должны $220» → Список всех клиентов Ахмета с агрегатами → Журнал начислений по каждой сделке → Журнал выплат
3.6. Менеджер фиксирует выплату партнёру
Руководитель отдал Ахмету $200 наличными в офисе.
→ Карточка партнёра → «Зафиксировать выплату» → 200 USDT, метод «cash», комментарий «Отдали в Лиман-офисе, расписка #134»
→ В системе: 200 USDT помечается как paid (списывается с накопленной комиссии)
→ Опционально: связать с кассовой cash_movement (расход с кассы)
4. Сущности модуля
4.1. Партнёр (Partner)
Кто-то, кто приводит нам клиентов. Может быть физлицо, может быть юрлицо, может быть «организация» (типа турбюро). В систему НЕ заходит — только данные о нём.
Поля:
- ID, external_ref (например P-0001)
- Display name («Ахмет Йылмаз», «Travel-агентство Mavi»)
- Контакты: TG-username, телефон, email
- Заметка (свободный текст: «Знакомый Дмитрия, лояльный, платит ему наличкой раз в месяц»)
- Default условия комиссии (применяются к новым клиентам без override):
- Модель: pct_of_volume / pct_of_profit / fixed_per_deal
- Значение: 0.01 = 1% или 5 USDT и т.д.
- Статус: active / paused (не начисляем) / archived
- Optional: parent_partner_id (для multi-level, фаза 2)
- Кто завёл, когда
4.2. Условия по клиенту (ClientPartnerLink)
Привязка клиента к партнёру + конкретные условия именно по этому клиенту.
Поля: - ID - Client (FK) - Partner (FK) - attached_at, detached_at (история привязок) - Условия (могут отличаться от дефолтных партнёра): - Модель: pct_of_volume / pct_of_profit / fixed_per_deal - Значение - Кто привязал (user), причина
Логика: у клиента в каждый момент времени одна активная привязка (partial UNIQUE). Если клиента переключили на другого партнёра — старая запись detached_at заполняется, создаётся новая.
4.3. Начисление комиссии (PartnerCommission)
Одна запись на каждую сделку клиента, привязанного к партнёру.
Поля:
- ID
- Deal (FK)
- Partner (FK, snapshot)
- ClientPartnerLink (FK, snapshot условий)
- Расчётные данные:
- base_value_usdt (volume или profit на момент расчёта)
- rate (% или фикс)
- amount_usdt (итоговая сумма)
- Статус: pending (только что начислено) / accrued (готово к выплате) / paid / voided / reversed
- payout_id (FK на Выплату, если оплачено)
- Создано когда, кем (system / manual recalc)
Идемпотентность: UNIQUE по (deal_id, partner_id) — на одну сделку максимум одна запись на партнёра.
4.4. Выплата (PartnerPayout)
Факт того, что мы отдали партнёру деньги.
Поля:
- ID
- Partner (FK)
- Amount USDT
- Method: cash / usdt-trc20 / bank / other (свободный)
- Дата выплаты
- Кто провёл (user)
- Комментарий («Расписка #134», «TX hash 0xabc...»)
- Optional: cash_movement_id (FK) — если связано с расходом из кассы
- Optional: operation_id (FK) — если выплата прошла как crypto_out через нашу Operation
Когда фиксируется выплата на сумму X:
- Покрываем accrued начисления партнёра в порядке FIFO
- Помечаем их paid, заполняем payout_id
- Если X больше чем accrued — записываем избыток как «predoplata» (отрицательный баланс)
5. Расчёт комиссии
5.1. Trigger
Момент Deal.status → done. Post-commit hook (после успешного коммита сделки):
1. Если у deal.client.id есть активный ClientPartnerLink → берём условия (override клиента ИЛИ дефолт партнёра)
2. Если партнёр paused/archived → не начисляем
3. Считаем сумму:
- pct_of_volume: volume_usdt × rate
- pct_of_profit: profit_usdt × rate
- fixed_per_deal: rate (фиксированная сумма)
4. Создаём PartnerCommission(status='accrued') (без hold на MVP — будем использовать сразу)
5. UNIQUE(deal_id, partner_id) защищает от дублирования
5.2. Reversal (отмена сделки)
Если сделка перешла в cancelled уже после начисления:
- Запись помечается voided (если ещё accrued)
- Если уже paid — создаётся reverse_entry с отрицательной суммой (баланс партнёра уйдёт в минус)
- Минус-баланс разрешён: при следующих начислениях покрывается, либо при выплате учитывается
5.3. Пересчёт при изменении условий
Если менеджер изменил условия (override) или дефолт партнёра — старые записи не пересчитываются. Snapshot условий хранится в PartnerCommission. Только будущие сделки идут по новым условиям.
Опционально: admin может вручную дать команду «пересчитать комиссии по сделке X» — voidает существующие записи и создаёт новые.
5.4. Edge-cases
| Случай | Поведение |
|---|---|
| У клиента нет привязки к партнёру | Не начисляем (нормальная ситуация) |
Партнёр paused |
Не начисляем, но привязка клиента остаётся |
Партнёр archived |
Не начисляем, скрыт из autocomplete |
| Override условий = 0% | Начисляем 0 (запись для аудита, видно что «условия 0»), статус voided: zero_rate |
Сделка cancelled после начисления |
voided если ещё accrued, reverse если paid |
Сделка в excluded_asset партнёра (если задано) |
0 |
| Деление partner в системе (archived вместо удаления) | Старые записи остаются, нельзя удалить партнёра с историей |
6. Выплаты и баланс
6.1. Балансы
Вычисляются на лету:
accrued_total(partner) = SUM(amount) WHERE status='accrued'
paid_total(partner) = SUM(amount) WHERE status='paid'
balance_owed(partner) = accrued_total - 0 (то что должны)
Если был reversal на paid:
reversed_total(partner) = SUM(|amount|) WHERE status='reversed'
balance_owed(partner) = accrued_total - reversed_total
6.2. Фиксация выплаты
Кнопка «Зафиксировать выплату» в карточке партнёра:
Поля формы:
- Сумма USDT
- Метод (cash / usdt-trc20 / bank / other)
- Дата (по умолчанию сегодня)
- Комментарий (свободный)
- Optional: «Списать с кассы X» — создаёт связанный CashMovement(kind='collection_out', reason='Выплата партнёру Ахмет')
- Optional: «Прикрепить tx hash» — если выплата USDT-TRC20, ввести hash для аудита
После сохранения:
1. Создаётся PartnerPayout(status='completed')
2. Покрываем accrued записи по FIFO до суммы выплаты
3. Каждая накрытая → status='paid', payout_id заполнен
4. Если сумма больше чем accrued — избыток хранится как prepayment (можно показать как «авансом выплачено $30», уменьшит следующие начисления)
6.3. Никакого hold-периода на MVP
В моём предыдущем PRD был hold 7 дней. Тут он не нужен: - Это внутренний учёт, не публичная программа - Партнёр всё равно деньги получит когда менеджер их физически передаст - Если сделка отменилась после выплаты — отдельный процесс (reverse + договариваться с партнёром)
Опция «отложенное начисление» (delay) может быть добавлена позже как настройка партнёра.
7. Multi-level (опционально, фаза 2)
Партнёр A привёл партнёра B. Когда клиенты B делают сделки → B получает свою комиссию, а A получает % от комиссии B.
Реализация (фаза 2):
- Поле Partner.parent_partner_id (self-FK)
- При начислении B → дополнительно создаём PartnerCommission на A с указанием tier=2
- Cтавка tier 2 — отдельное поле на программе/партнёре (например, 10% от того что получил B, либо 0.05% от volume — два варианта расчёта)
- НЕ делаем 3+ уровней (риск пирамиды, нет реальной бизнес-выгоды)
На MVP не делаем. Сначала запускаем single-level, смотрим на реальное использование, потом решаем нужен ли каскад.
8. Модель данных (SQLAlchemy)
8.1. Новые таблицы
class Partner(Base):
__tablename__ = "partners"
id: Mapped[int] = mapped_column(primary_key=True)
external_ref: Mapped[str] = mapped_column(String(32), unique=True, index=True) # "P-0001"
name: Mapped[str] = mapped_column(String(255))
tg_username: Mapped[str | None]
phone: Mapped[str | None]
email: Mapped[str | None]
note: Mapped[str | None] = mapped_column(Text)
# Дефолтные условия — применяются к новым клиентам без override
default_commission_model: Mapped[str] = mapped_column(String(32), default="pct_of_volume")
# 'pct_of_volume' / 'pct_of_profit' / 'fixed_per_deal'
default_commission_rate: Mapped[Decimal] = mapped_column(Numeric(10, 6), default=0)
# для pct — десятичная дробь (0.01 = 1%); для fixed — сумма в USDT
status: Mapped[str] = mapped_column(String(16), default="active") # active/paused/archived
parent_partner_id: Mapped[int | None] = mapped_column(ForeignKey("partners.id")) # фаза 2
created_at, created_by_id, ...
class ClientPartnerLink(Base):
__tablename__ = "client_partner_links"
id: Mapped[int] = mapped_column(primary_key=True)
client_id: Mapped[int] = mapped_column(ForeignKey("clients.id"), index=True)
partner_id: Mapped[int] = mapped_column(ForeignKey("partners.id"), index=True)
# Условия именно по этому клиенту (если NULL — используем default партнёра)
commission_model: Mapped[str | None]
commission_rate: Mapped[Decimal | None]
attached_at: Mapped[datetime] = mapped_column(default=now_utc)
detached_at: Mapped[datetime | None]
attached_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"))
reason: Mapped[str | None]
# partial UNIQUE: на клиента максимум одна активная привязка
# CREATE UNIQUE INDEX uq_active_link ON client_partner_links(client_id) WHERE detached_at IS NULL
class PartnerCommission(Base):
__tablename__ = "partner_commissions"
id: Mapped[int] = mapped_column(primary_key=True)
deal_id: Mapped[int] = mapped_column(ForeignKey("deals.id"), index=True)
partner_id: Mapped[int] = mapped_column(ForeignKey("partners.id"), index=True)
link_id: Mapped[int | None] = mapped_column(ForeignKey("client_partner_links.id")) # snapshot какая привязка
tier: Mapped[int] = mapped_column(Integer, default=1) # 1=прямой, 2=upline
# Snapshot расчёта
commission_model: Mapped[str] # как считали
commission_rate: Mapped[Decimal] # ставка применённая
base_value_usdt: Mapped[Decimal] # volume или profit на момент
amount_usdt: Mapped[Decimal] # итог
status: Mapped[str] = mapped_column(String(16), default="accrued")
# accrued / paid / voided / reversed
payout_id: Mapped[int | None] = mapped_column(ForeignKey("partner_payouts.id"))
void_reason: Mapped[str | None]
reverses_id: Mapped[int | None] = mapped_column(ForeignKey("partner_commissions.id"))
created_at: Mapped[datetime] = mapped_column(default=now_utc)
__table_args__ = (UniqueConstraint("deal_id", "partner_id", "tier", name="uq_commission"),)
class PartnerPayout(Base):
__tablename__ = "partner_payouts"
id: Mapped[int] = mapped_column(primary_key=True)
partner_id: Mapped[int] = mapped_column(ForeignKey("partners.id"), index=True)
amount_usdt: Mapped[Decimal]
method: Mapped[str] # cash / usdt-trc20 / bank / other
paid_at: Mapped[datetime] = mapped_column(default=now_utc)
paid_by_user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
note: Mapped[str | None]
# Связи с уже существующими сущностями системы
cash_movement_id: Mapped[int | None] = mapped_column(ForeignKey("cash_movements.id"))
operation_id: Mapped[int | None] = mapped_column(ForeignKey("operations.id"))
tx_hash: Mapped[str | None] # для криптовыплат
8.2. Изменения существующих таблиц
# Client — добавляем denormalized указатель на текущего партнёра (для скорости)
clients.referrer_partner_id: int | None # FK на partners
clients.referrer_set_at: datetime | None
# Deal — добавляем snapshot для аудита (вдруг привязку поменяют после)
deals.commission_calculated_at: datetime | None
referrer_partner_id — кеш. Истина — в client_partner_links (последняя без detached_at). Обновляется триггером или в коде при создании/изменении link.
9. UI экраны (только админка, без партнёрского кабинета)
9.1. Новые экраны
/partners — Список партнёров
| Колонки | |
|---|---|
| ID, имя, контакты | TG/телефон/email |
| Статус | active/paused/archived (бейдж) |
| Клиентов | сколько привёл |
| Сделок | сколько прошло по его клиентам |
| Должны | balance_owed в USDT (красный если > 100) |
| Действия | edit, archive |
Фильтры: статус, поиск по имени/TG. Кнопка «+ Новый партнёр».
/partners/new — Форма создания
Поля: имя, TG, телефон, email, заметка, дефолтная модель комиссии + ставка, статус.
/partners/{id} — Карточка партнёра
Header: имя, контакты, статус.
Tabs: - Обзор: 4 KPI (клиентов / сделок / должны USDT / выплачено всего), последние 10 начислений - Клиенты: таблица с псевдонимом, датой привязки, условиями, кол-вом сделок, оборотом, начислено-выплачено - Начисления: журнал PartnerCommission с фильтрами (статус, период) - Выплаты: журнал PartnerPayout с TX/cash_movement ссылками - Настройки: редактирование дефолтных условий, статуса, заметки
Кнопка «Зафиксировать выплату» (modal с формой).
9.2. Изменения существующих экранов
Карточка клиента /clients/{id} — добавляется блок «Партнёр»:
- Если привязан — показ имени, условий, истории, кнопка «Изменить условия» и «Отвязать»
- Если нет — кнопка «Привязать к партнёру» (autocomplete)
- История привязок (если перепривязывался)
Список клиентов /clients — колонка «Партнёр» + фильтр.
Карточка сделки /deals/{id} — блок «Партнёрская комиссия»:
- Read-only: «По этой сделке начислено партнёру Ахмет: 4.20 USDT (0.5% от volume, статус: accrued)»
- Если в reverse — показано
Форма новой сделки /deals/new — если у клиента есть привязка, в подсказке: «Будет начислено партнёру Ахмет (1%)».
Дашборд (для admin/manager) — виджет: - Активных партнёров: N - Должны партнёрам всего: X USDT - Начислено за период: Y USDT
9.3. Удалено из старого PRD
- ❌ Партнёрский кабинет (
/aff/*) - ❌ Партнёрский логин и роль
affiliate - ❌ 2FA для партнёра
- ❌ Реферальные ссылки
/r/{code} - ❌ Cookie tracking
- ❌ Self-signup
- ❌ Маркетинговые материалы
- ❌ Tax-report (это уровень всей системы, не модуля)
- ❌ Public landing pages
10. RBAC
| Роль | Что может |
|---|---|
admin |
CRUD партнёров, изменение условий, фиксация выплат, recalc комиссий |
manager |
CRUD партнёров, привязка клиентов, фиксация выплат, изменение условий клиента |
accountant |
Read-only по всему, экспорт CSV (начисления/выплаты по периоду) |
cashier |
В форме сделки и карточке клиента — видит и может привязать к партнёру (autocomplete) |
viewer |
Без модуля партнёров (как и сейчас) |
Никакой новой роли. Никакого внешнего доступа.
11. Audit log
Новые действия:
- partner_created, partner_updated, partner_archived
- partner_link_attached, partner_link_detached, partner_link_terms_changed
- commission_accrued (системно), commission_voided, commission_recalc (по сделке)
- payout_recorded
Всё пишется через существующий record_audit().
12. Связь с существующей системой
| Точка | Что меняется | Сложность |
|---|---|---|
models.py |
+4 модели (Partner, ClientPartnerLink, PartnerCommission, PartnerPayout) + ALTER на clients, deals |
средняя |
| Alembic | Одна миграция со всеми изменениями | низкая |
routes/deals.py |
Post-commit hook вызывает commission_engine.accrue_for_deal(deal) |
низкая |
routes/clients.py |
Блок «Партнёр» в карточке + поле в форме создания | средняя |
routes/partners.py |
НОВЫЙ — список, карточка, CRUD, выплаты | высокая (основная работа) |
templates/partners_*.html |
НОВЫЕ шаблоны (list, card, new, payout-modal) | средняя |
templates/base.html |
Пункт «Партнёры» в сайдбаре | низкая |
templates/client_card.html |
Блок партнёра | низкая |
templates/deal_card.html |
Блок «комиссия по сделке» | низкая |
| Дашборд | Виджет «партнёры» для admin/manager | низкая |
Никаких новых cron-job, никаких новых cookie, никаких новых auth-flows. Минимально-инвазивно.
13. Фазы реализации
Фаза 1 — MVP (1.5–2 недели)
В скоупе:
- Все 4 модели + миграция
- CRUD партнёров (/partners, /partners/new, /partners/{id})
- Карточка партнёра с tabs (обзор / клиенты / начисления / выплаты / настройки)
- Блок «Партнёр» в карточке клиента и форме сделки
- Авторасчёт комиссии при Deal.status → done (post-commit hook)
- Фиксация выплаты партнёру (modal)
- Аудит всех действий
- Виджет на дашборде
Без: - Multi-level - Связь выплаты с cash_movement (можно вручную потом) - Massive recalc tools для admin - CSV экспорт (накопится в фазе 2)
Фаза 2 — Расширение (1 неделя)
- Multi-level (parent_partner_id, tier 2 начисления)
- Связь PartnerPayout с CashMovement / Operation (автосписание с кассы)
- CSV экспорт начислений/выплат по периоду
- Massive recalc по сделке/клиенту (admin tool)
- Hold-период как настройка партнёра (если нужен будет)
- Excluded assets per partner
14. Открытые вопросы (требуют ответа)
14.1. Бизнес
-
Multi-level нужен? Если да — это фаза 2. Если нет вообще — снимаем поле
parent_partner_id. По описанию из первого сообщения «аффилиатов может быть несколько уровней» — нужен. Подтверди что в MVP single-level, фаза 2 — multi-level. -
Override условий клиента — перекрывает или плюсуется к дефолту партнёра? Мой выбор: перекрывает целиком (если override стоит — используем его, дефолт партнёра не смотрим). Альтернатива «плюсуется» сложна и редко нужна.
-
Hold-период на MVP — 0 (всё сразу accrued)? Мой выбор: 0. Это внутренний учёт, не клиентские деньги. Если потом понадобится — добавим как настройку партнёра.
-
Выплата партнёру связана с кассой автоматически или ручной cash_movement? В MVP — ручной (просто запись PartnerPayout без связи). В фазе 2 — опция «списать с кассы X» автоматом создаёт cash_movement.
-
Условия по умолчанию для новых партнёров? Мой выбор: при создании партнёра ставка
0и модельpct_of_volume. Менеджер обязан явно задать ставку до того как привязать клиента. Альтернатива — какой-то общий дефолт (например 0.5% volume), но это слишком предположительно. -
Может ли один клиент быть привязан к нескольким партнёрам? Мой выбор: НЕТ (partial UNIQUE гарантирует одну активную привязку). Если есть совместная история — это переключение во времени.
-
Архивный партнёр — что с его историей? Мой выбор: историю не трогаем (записи остаются), просто не показываем в autocomplete и не позволяем привязывать новых клиентов.
14.2. Технические
-
Когда срабатывает accrual — на
doneили сразу при создании сделки? Мой выбор: на done (как сейчас работают операции). Если сделка вnewилиin_work— комиссии ещё нет. -
Что если менеджер забыл указать партнёра, а сделка прошла? Возможность: ретро-привязка. Менеджер открывает клиента → ставит партнёра → отдельная кнопка «Начислить за прошлые сделки» (с ограничением по дате). Не в MVP — добавим если понадобится.
15. Метрики успеха модуля
Через 1 месяц после внедрения: - 100% активных клиентов с партнёром имеют корректную привязку в системе (а не в Excel) - 0 случаев «не знаем сколько должны партнёру X» - Время на ответ на вопрос партнёра «сколько я заработал?» — < 1 минуты - Журнал выплат — все за месяц зафиксированы в системе
Через 3 месяца: - Минимум 80% выплат партнёрам проходят через модуль (а не «отдал и не записал») - 0 спорных случаев по балансу с партнёрами (всё подтверждаемо аудит-логом)
16. Что НЕ делаем явно
| Что | Почему |
|---|---|
| Партнёрский кабинет | Не нужно по новой цели — это внутренний учёт |
| Ref-коды и публичные ссылки | Нет публичной программы |
| Self-signup партнёров | Менеджер их заводит |
| Маркетинговые материалы | Не в скоупе модуля |
| KYC партнёров в системе | Можно держать в свободной заметке, формальный KYC не нужен |
| Withholding tax расчёт | Это бухгалтерия в Excel/1С, не CRM |
| CMB compliance | Не публичная программа — не релевантно |
| 2FA для партнёров | Партнёров в системе нет |
| Cookie tracking | Нет публичного приёма клиентов |
| Hold-периоды на MVP | Внутренний учёт, не клиентские деньги |
| Multi-currency выплаты | Партнёры получают USDT (или наличку зафиксированную как USDT-эквивалент) |
| Smart-contracts | Overkill |
Приложение: соотношение со старым PRD
Старый PRD v2 (affiliate-prd-v2.md) — это PRD внешней публичной партнёрской программы с кабинетом, маркетингом, CMB-compliance.
Этот документ — это PRD внутреннего учётного модуля для CRM.
Что взято из старого PRD: - Базовая модель данных (упрощена, без программ-сущности, без ref-кодов) - Принцип snapshot при начислении (UNIQUE deal_id+partner_id) - Идея override условий per-клиент
Что выкинуто:
- Партнёрский кабинет и роль affiliate
- Все compliance/KYC/tax расчёты на уровне модуля (это уровень бизнеса в целом)
- Tracking, attribution, cookie
- Программы с разными ставками (упрощено: ставка на партнёре + override на клиенте)
- Tier-бонусы Bronze/Silver/Gold
- Hold-периоды, auto-payouts
- 3 модели вознаграждения остались (pct_of_volume / pct_of_profit / fixed_per_deal), но default = pct_of_volume