POST /api/agent/crm/leads/{leadId}/archive — Архивировать лид
Ручное закрытие лида агентом: клиент отказался, дубль или лид отработан. Перевод Active|Expired → Archived с
фиксацией причины (ArchiveReason) и момента (ArchivedAt).
Назначение
Дать агенту (или руководителю агентства / Менеджеру ПИН) явно убрать лид из рабочей воронки с указанием причины, сохранив ленту событий, фиксации и сделки как историю.
В отличие от архивирования клиента (INV-CLI-4, client.md), архивирование лида не
требует отсутствия активных сделок: порождённые сделки и заявки на фиксацию — исторические записи дочерних
агрегатов и продолжают жить своим циклом (INV-LEAD-4, lead.md). Единственный побочный
эффект на связанном агрегате — обнуление Client.ActiveLeadId (INV-CLI-2), если денормализованная ссылка
указывала на закрываемый лид.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | agent |
| Feature group | lead |
| Статус | draft |
| Документ | docs/06-services/crm/api/agent/archive-lead.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Агент; либо Руководитель агентства / Менеджер ПИН) |
| Auth policy / Permissions | crm.leads.manage |
| Scope (RBAC) | организация (агентство); Менеджер ПИН — платформа |
| Record-level | агент — свои лиды (ExecutorAgentUserId = actor); руководитель — весь TenantId; Менеджер ПИН — platform-scope (lead.md) |
| Tenant isolation | TenantId = AgencyCompanyId в каждом запросе; лид грузится с фильтром по TenantId |
| Audit | update (actor-stamping: UpdatedByUserId, Version) |
Круг действующих лиц сужен record-level: агент архивирует только свои лиды (ExecutorAgentUserId = actor);
руководитель агентства — любой лид своего TenantId; Менеджер ПИН — сквозной доступ (platform-scope). Лид вне
этого круга → 403 CRM_LEAD_FORBIDDEN.
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | POST |
| Success status | 204 (No Content) |
| Route | /api/agent/crm/leads/{leadId}/archive |
| Idempotency header | не применяется (терминальный статус: повтор на Archived → 409) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims | Guid | Да | Required; = AgencyCompanyId актора | Tenant isolation |
LeadId | route | Guid | Да | != Guid.Empty; существующий лид в TenantId | record-level по ExecutorAgentUserId |
ArchiveReason | body | string? | Нет | 0..1000 (varchar(1000)); trim; пустая строка → null | свободный текст, не PII |
ActorUserId | identity claims | Guid | Да | JWT агента | — |
Модель ответа
Тела нет: HTTP 204 без тела. Ошибки — ProblemDetails (таблица ниже).
Алгоритм
- Контекст и доступ. Операцию выполняет агент/руководитель агентства/Менеджер ПИН (permission
crm.leads.manage) в рамкахTenantId. - Проверка входа и предусловий.
- формат:
LeadId != Guid.Empty;ArchiveReason(если задан) ≤ 1000 — иначе ошибка валидации; - лид существует в
TenantId— иначеCRM_LEAD_NOT_FOUND; - допустимость состояния:
Status ∈ {Active, Expired}; ужеArchived→CRM_LEAD_ALREADY_ARCHIVED(повторный переход в терминал недопустим); - record-level: лид того же tenant и агент — владелец (кроме руководителя агентства и Менеджера ПИН) —
иначе
CRM_LEAD_FORBIDDEN.
- формат:
- Что читаем. Лид (для проверок выше) и его клиента-родителя (для поддержки INV-CLI-2).
- Бизнес-правила и переходы. Переход
Active|Expired → Archived(INV-LEAD-4: активные сделки и фиксации лида не трогаются, лента событий сохраняется append-only):ArchivedAt = now,ArchiveReason(trimmed,nullдля пустого),UpdatedByUserId = actor;ExpiredAt/ExpiresAtне меняются (историческая правда срока жизни). У клиента: еслиActiveLeadId == lead.Id— обнулить (INV-CLI-2); иначе клиент не мутируется. Доменных событийArchived-перехода нет (lead.md). - Что меняется. Статус лида →
Archivedи (опционально) обнулениеClient.ActiveLeadId— в одном согласованном изменении. - События. Интеграционных и доменных событий нет: закрытие лида внутреннее для агентства, внешние
контексты на него не реагируют (в списке produced контекста
lead-archivedнет, см. README; наружу — только lead-created.md и lead-expired.md). - Результат. HTTP
204 No Content. Повторный вызов на ужеArchivedлиде →CRM_LEAD_ALREADY_ARCHIVED.
Диаграмма
Предусловия и постусловия
Предусловия. Актор с crm.leads.manage и доступом к лиду (исполнитель ExecutorAgentUserId = actor / руководитель
агентства / Менеджер ПИН), TenantId = AgencyCompanyId. Лид существует в tenant, Status ∈ {Active, Expired}
(не Archived).
Постусловие (успех). Status = Archived, ArchivedAt = now, ArchiveReason (trim; null для пустого);
ExpiresAt/ExpiredAt сохранены (историческая правда срока жизни). Если Client.ActiveLeadId == lead.Id —
обнулён (INV-CLI-2), иначе клиент не мутируется. Дочерние сделки/фиксации продолжают жить (INV-LEAD-4), лента
событий append-only сохранена. Ни доменных, ни интеграционных событий не публикуется.
Постусловие (отказ). Лид не изменён, клиент не тронут.
Побочные эффекты
| Тип | Детали |
|---|---|
| Интеграционные события | none (закрытие лида интеграционных событий не порождает) |
| Внешние вызовы | none |
| Проекции / поиск | none (проекция клиента ClientSearchDocument не зависит от ActiveLeadId) |
| Уведомления | none (нет события → нет downstream-уведомления; лид пропадает из фильтра Active в списке агента) |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | ArchiveReason длиннее 1000 или LeadId = Guid.Empty | «Проверьте причину архивирования» | no retry |
CRM_LEAD_NOT_FOUND | 404 | лида нет в TenantId | «Лид не найден» | no retry |
CRM_LEAD_ALREADY_ARCHIVED | 409 | Status = Archived (повтор/гонка) | «Лид уже архивирован» | no retry |
CRM_LEAD_FORBIDDEN | 403 | actor не владелец лида и не руководитель/Менеджер ПИН | «Недостаточно прав на лид» | no retry |
CRM_CLIENT_ACTIVE_LEAD_MISMATCH | 409 | защита INV-CLI-2: обнуление ActiveLeadId с чужим leadId (рассинхрон) | «Повторите попытку» | retry (перечитка) |
CRM_LEAD_CONCURRENCY_CONFLICT | 409 | лид изменён конкурентно | «Лид изменён, повторите» | retry (перечитка) |
Совместимость и наблюдаемость
- Изменение статуса — терминальное и внутреннее; внешних контрактов метод не меняет (breaking-риска нет). Новые optional поля тела (напр. код причины) — additive.
- Logs:
lead-archived(LeadId,TenantId,ExecutorAgentUserId, наличие причины — без текста, если счесть чувствительным); metrics:crm_leads_archived_total{reason_present}, latency p95; traces — сквозной trace черезtraceparent; dashboardpin-crm; runbook —docs/06-services/crm/runbooks/.
Acceptance Criteria
- Happy path (Active). Given
Active-лид, у клиентаActiveLeadIdуказывает на него; Whenarchive; Then204,Status = Archived,Client.ActiveLeadId = null. - Happy path (Expired). Given
Expired-лид; Whenarchive; Then204,Status = Archived. - Активная сделка не блокирует. Given у лида незавершённая сделка; When
archive; Then204(в отличие от архивации клиента), сделка не изменена (INV-LEAD-4). - Повтор. Given
Status = Archived; Whenarchive; Then409 CRM_LEAD_ALREADY_ARCHIVED. - Рассинхрон
ActiveLeadId. Given денормализованная ссылка клиента указывает на другой лид; When обнуление; Then409 CRM_CLIENT_ACTIVE_LEAD_MISMATCH(retry, перечитка). - Доступ. Given чужой лид не-руководителю; When
archive; Then403 CRM_LEAD_FORBIDDEN.
Обратные ссылки
Автоссылки: где используется этот документ.
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: lead.md (переход
Archived,ArchiveReason/ArchivedAt, INV-LEAD-4), client.md (INV-CLI-2,ActiveLeadId). - Жизненный цикл лида (события, которые метод НЕ шлёт): lead-created.md, lead-expired.md.
- Карта областей и permissions: README; tenancy — ADR-0052.