Перейти к основному содержимому

ADR-0061: Независимый агент, отвязка от агентства и приглашения по номеру телефона

Статус

Accepted

Дата

2026-07-14

Контекст

Текущая модель жёстко привязывает агента к компании и не даёт «уйти» из агентства, сохранив наработки:

  • Жёсткий INV-2 (identity/domain/user.md): Type ⇔ CompanyId, для Agent обязателен CompanyId != null. Агент не существует вне агентства.
  • Приглашение = сразу учётка (identity/api/company-user/invite-company-user.md): «invite = UserEntity в статусе Invited», без отдельной pending-сущности; приглашённый ничего не подтверждает, учётку создаёт руководитель, активация — первым входом. Для добора агента «со стороны» по номеру телефона (который может уже быть учёткой-инвестором или агентом другой компании) этого мало.
  • Бизнес-требование (аналогия «игрок меняет клан»): агент — самостоятельная фигура; его метрики/статистика/достижения принадлежат агенту, а не агентству, и сохраняются при смене агентства.
  • Историческая привязка сделок/лидов/фиксаций уже держится снапшотами AgencyCompanyId/AgentUserId (crm/domain/deal.md, lead.md, fixation-request.md) — эти поля record-level и не пересчитываются. Статистика агента сегодня считается agency-centric (record-level по AgencyCompanyId + AgentUserId, crm/api/agent/get-statistics.md) — в границах одного агентства.

Решение

1. Независимый агент (развязка членства)

  • Вводится независимый агент: Agent с CompanyId = null или через отдельную запись членства (AgentMembership) без активной компании. Жёсткий INV-2 ослабляется: тип Agent больше не требует CompanyId != null — членство в агентстве становится отдельной, развязываемой связью.
  • Отвязка от агентства (leave) не удаляет учётку и не трогает историю: агент сохраняет UserId, роль agent (для независимого — в scope Platform) и все метрики/достижения. Агент может выйти, стать независимым и позже вступить в другое агентство.
  • Конкретная реализация (nullable CompanyId против явной membership-сущности) фиксируется в identity-доменных доках; INV-2 и RoleAssignment scope переоформляются там же.
  • Множественное членство и active-agency (агент одновременно состоит в нескольких агентствах и выбирает активное) вводятся отдельно — ADR-0070: там инвариант единственного активного членства INV-AM1 снят (заменён на SingleActivePerCompany), появляется AgentMembershipEntity, RefreshSession.ActiveCompanyId и endpoint выбора активного агентства. Настоящий ADR остаётся в силе для развязки членства и независимого агента.

2. Приглашение по номеру телефона — новый агрегат CompanyInvitation

  • «Приглашение» отделяется от «создания учётки». Новый агрегат CompanyInvitation (контекст identity), Status: Pending / Accepted / Declined / Expired / Revoked.
  • Руководитель приглашает по номеру телефона (адресат может уже быть инвестором или агентом другой компании). Агент подтверждает (accept) или отклоняет (decline); только accept создаёт/меняет членство и назначает роль. Revoked — отзыв приглашающей стороной, Expired — по сроку жизни.
  • Существующий invite-company-user (invite = сразу Invited-учётка) остаётся для внутренних сотрудников без самоподтверждения; добор агента «со стороны» идёт через CompanyInvitation.

3. Переходы роли инвестор ↔ агент

  • become-agent: инвестор становится агентом (принял приглашение агентства или заявился независимым) — добавляется роль agent, при необходимости членство.
  • become-investor: агент возвращается к роли инвестора (снятие роли agent, выход из членства).
  • Переходы меняют роли/членство, но не создают новую учётку — один UserId на человека.

4. Историческая привязка: иммутабелен Origin-snapshot, текущий исполнитель — мутабелен

С введением модели участников CRM (ADR-0071) разделяются атрибуция происхождения (иммутабельна) и текущий исполнитель (мутабелен, производный от участников):

  • Иммутабелен именно Origin-snapshot сделки/лида/фиксации — OriginAgencyCompanyId / OriginAgentUserId. Это отпечаток «кто и от какого агентства завёл процесс» на момент создания; он не переписывается при смене агентом агентства и фиксирует происхождение записи.
  • Текущий исполнитель ExecutorAgentUserId — мутабелен: это производное поле от активного участника Executor (enum ParticipantRole), которого руководитель может переназначить (reassign) в ходе процесса. Ровно один активный Executor; процесс нельзя оставить без него.
  • Получатель комиссии = явно назначенный руководителем Executor на момент завершения (CommissionRecipientUserId в событиях pin.crm.deal-completed.v1 / fixation-approved), а не тот, кто когда-то завёл сделку. Правила смены получателя — в ADR-0071.
  • Back-fill, переписывающий OriginAgencyCompanyId / OriginAgentUserId исторических записей на новую компанию агента, запрещён: он исказил бы происхождение сделок, воронку агентства и основания комиссий (rewards). Смена агентства меняет только членство и текущего исполнителя (через reassign), но не Origin.

5. Метрики агента — user-centric

  • Личные метрики/достижения агента агрегируются по AgentUserId сквозь все компании, а не только по текущему агентству.
  • get-statistics остаётся agency-centric (якорь AgencyCompanyId) — отчётность агентства; личная статистика агента — отдельная user-centric проекция.
  • Опционально: проекции AgentPerformance / AgentAchievement (user-scoped сводка достижений), дом — identity либо rewards; вводятся при появлении экрана личного профиля агента, не обязательны сразу.

Область Действия

ОбластьВходитНе входит
Сервисыidentity (агрегат/переходы/членство), crm/rewards (user-centric метрики), notifications (уведомления)chessboard, catalog, messenger
КонтрактыCompanyInvitation API (invite/accept/decline/revoke), become-agent/become-investor; 2 новых Kafka-топикаконтракт invite-company-user не меняется
ДанныеCompanyInvitation, membership/CompanyId=null, опц. AgentPerformance/AgentAchievementOrigin-snapshot OriginAgencyCompanyId/OriginAgentUserId в crm (иммутабелен, не трогается); ExecutorAgentUserId — производное поле участников (ADR-0071)
Эксплуатациямиграция ослабления INV-2; запрет back-fill исторической привязки

Последствия

КонтекстПоследствие
identity+агрегат CompanyInvitation, +переходы become-agent/become-investor, ослабление INV-2, модель членства/независимого агента; дорабатываются user.md, role.md, invite-company-user.md.
crm / rewardsличная статистика агента — user-centric (агрегация по AgentUserId); Origin-snapshot (OriginAgencyCompanyId/OriginAgentUserId) не пересчитывается, back-fill запрещён; текущий исполнитель ExecutorAgentUserId мутабелен (reassign), получатель комиссии — Executor на момент завершения (ADR-0071).
notificationsновые уведомления по событиям приглашений и членства (пригласили / приняли / отклонили / вышел из агентства).

Затронутые контракты и совместимость

КонтрактВерсияСовместимостьМиграция
pin.identity.company-invitation-changed.v11новый топикadditive
pin.identity.agent-membership-changed.v11новый топикadditive
INV-2 (Type ⇔ CompanyId) в user.mdослабление (breaking для модели)миграция схемы + переоформление инвариантов в domain-доках

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы