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

UC-IDN-010. Агент выходит из агентства, сохраняя метрики

Назначение

Развязка членства (ADR-0061, аналогия «игрок меняет клан»): агент выходит из агентства и становится независимым, сохраняя UserId, роль agent (переезжает в scope Platform) и все личные метрики/достижения (user-centric, агрегация по AgentUserId). История членства не удаляется, а исторические сделки/лиды/фиксации остаются у прежней компании (снапшоты AgencyCompanyId/AgentUserId в crm иммутабельны, back-fill запрещён). Вызывает сам агент либо руководитель агентства. Связан с контекстом identity.

Metadata

ПолеЗначение
IDUC-IDN-010
Контекстidentity (см. docs/06-services/identity/)
Типосновной
Статусdraft

Актор

ПолеЗначение
РольАгент (сам) — self-действие; либо Агентство (руководитель) исключает агента с identity.users.manage
Permissions / scopeself (UserId из sub); либо identity.users.manage в scope Organization
Tenantчленство читается по AgentUserId (кросс-компанийно); мутация — в scope прежнего Organization
Record-levelактивное AgentMembership по AgentUserId

Предусловие

  • У агента есть активное AgentMembership (Status = Active); иначе он уже независимый.
  • UserEntity.Type = Agent, Status = Active.

Триггер

ПолеЗначение
ИсточникUI button
Триггерящий элементЛК агента → «Моё агентство» → «Выйти из агентства»

Основной Поток

  1. Агент нажимает «Выйти из агентства» и подтверждает (диалог поясняет: наработки и метрики сохранятся, доступ к клиентской базе агентства прекратится).
  2. Система принимает запрос выхода (leave-agency.md).
  3. Проверить: у агента есть активное AgentMembership (иначе IDENTITY_MEMBERSHIP_NOT_ACTIVE); UserEntity.Status = Active (иначе IDENTITY_USER_BLOCKED); найти активное назначение роли agent в Organization.
  4. В одном согласованном изменении (INV-AM6): закрыть членство (Active → Left, заполнить LeftAt, история сохраняется — INV-AM2); отвязать пользователя от агентства (зеркало CompanyId → null, тип остаётся Agent); отозвать назначение роли agent в Organization и создать RoleAssignment (agent, Platform) — роль переезжает Organization → Platform (INV-R3). Историческая привязка сделок/лидов/фиксаций не пересчитывается (ADR-0061 п.4). Гонка leave/accept разрешается оптимистичной блокировкой (CONCURRENCY_CONFLICT).
  5. Зафиксировать факты: agent-membership-changed (Left), user-upserted (CompanyId = null), user-access-changed.
  6. Агент видит статус «Независимый агент»; клиентская база агентства скрыта, личные метрики/достижения и лоты остаются. Руководитель по membership-событию видит убытие из ростера.

Альтернативные Потоки

A1. Руководитель исключает агента

  1. Та же операция, вызванная руководителем с identity.users.manage (дельта доступа — leave-agency.md); actor-stamping = руководитель. Итог как в основном потоке.

A2. Агент выходит и хочет полностью вернуться к инвестору

  1. Вместо leave агент вызывает become-investor (become-investor.md): снимается роль agent, закрывается членство, тип Agent → Investor (UC пересекается — см. become-investor).

A3. Нет активного членства (уже независимый)

  1. Шаг 3 → IDENTITY_MEMBERSHIP_NOT_ACTIVE (404); повтор идемпотентен для UI.

Постусловие

  • AgentMembership.Status = Left, LeftAt заполнен; запись сохранена (история найма).
  • UserEntity.Type = Agent (независимый), зеркало CompanyId = null.
  • Роль agent действует в scope Platform; назначение в Organization отозвано.
  • Личные метрики/достижения агента (user-centric) сохранены; исторические сделки/комиссии — у прежней компании.
  • Опубликованы agent-membership-changed (Left), user-upserted, user-access-changed.

Возможные Ошибки

КодHTTPУсловиеПоведение клиента
IDENTITY_MEMBERSHIP_NOT_ACTIVE404Нет активного членства (A3)«Вы не состоите в агентстве»
IDENTITY_USER_BLOCKED400Status != Active«Учётная запись заблокирована»
CONCURRENCY_CONFLICT409Гонка leave/accept«Повторите попытку»

Доменные События

СобытиеКогда поднимаетсяSubscribers
AgentLeftAgencyEventActive → Leftin-proc
RoleAssignmentRevokedEventОтзыв роли agent в Organizationin-proc аудит
RoleAssignmentGrantedEventВыдача роли agent в Platformin-proc аудит

Kafka Интеграционные События

СобытиеTopicКогда публикуетсяConsumers
AgentMembershipChangedKafkaEvent.v1pin.identity.agent-membership-changed.v1Active → Leftcrm (ростер, user-centric метрики), rewards (основания комиссий), notifications
UserUpsertedKafkaEvent.v1pin.identity.users.v1Зеркало CompanyId = nullcrm, messenger, profile
UserAccessChangedKafkaEvent.v1pin.identity.access.v1Роль Organization → Platformcrm, catalog, chessboard, gateway
Гарантии доставки и согласованность

События идут с ключом партиционирования UserId; потребители идемпотентны и сохраняют порядок изменений на пользователя. Членство закрывается в identity атомарно (INV-AM6), но доступ агента к клиентской базе агентства фактически прекращается после инвалидации кэшей прав по pin.identity.access.v1 (окно ≤ TTL, UC-IDN-008) — короткое время после выхода агент ещё может видеть данные агентства из кэша. crm по agent-membership-changed (Left) убирает агента из ростера, но не переписывает исторические снапшоты AgencyCompanyId/AgentUserId на сделках/лидах/фиксациях (back-fill запрещён, ADR-0061 п.4) — прежняя компания сохраняет основания комиссий. Гонка «leave против accept другого приглашения» разрешается оптимистичной блокировкой членства (CONCURRENCY_CONFLICT): победитель один, проигравший повторяет операцию на актуальном состоянии.

Acceptance Criteria

#КритерийПроверка
AC1Выход переводит membership в Left (не удаляет) — история сохранена (INV-AM2)тест: запись присутствует с LeftAt
AC2Роль agent сохраняется, переезжает Organization → Platformтест эффективных прав до/после
AC3Личные метрики/достижения агента не теряются (user-centric)тест агрегации по AgentUserId
AC4Исторические сделки/лиды/фиксации остаются у прежней компании (back-fill запрещён)тест: снапшоты AgencyCompanyId/AgentUserId не меняются
AC5Повторный выход без активного членства → 404 (A3)двойной вызов
AC6Атомарность (membership + user + роль + события)сбой между шагами → откат

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

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

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