GET /api/pin-manager/crm/fixations/{fixationId} — Карточка любой фиксации (Менеджер ПИН)
Назначение
Даёт Менеджеру ПИН сквозную (арбитражную) карточку одной заявки на фиксацию: platform-scope актор открывает
любую фиксацию любого тенанта по fixationId и видит обе стороны сразу — снимок ФИО/телефона клиента,
эффективные хотелки (override заявки либо «шапка» лида), результат авто-проверки дублей у застройщика
(DuplicateOfFixationId/DuplicateMatchKind), решение (DecidedAt/RejectReason), созданную сделку
(DealId) и того, кто подал заявку (CreatedByUserId — Менеджер ПИН может подать фиксацию от имени агента).
Нужна для арбитража спора «агент ↔ застройщик» (US-MGR-01..06). В отличие от агентской
(../agent/get-fixation.md) и застройщичьей сторон, карточка Менеджера ПИН не
ограничена ни OriginAgencyCompanyId, ни DeveloperCompanyId и раскрывает дубль-ссылку. Инвестор фиксаций не видит.
Только чтение; дом полей — fixation-request.md.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | pin-manager |
| Feature group | fixation |
| Статус | draft |
| Документ | docs/06-services/crm/api/pin-manager/get-fixation.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | Operator(Admin) — Менеджер ПИН (platform-scope); Служба заботы — read-only арбитраж |
| Auth policy / Permissions | crm.fixations.view в platform-scope (роль «Менеджер ПИН»); политика проверяется до бизнес-логики |
| Scope (RBAC) | платформа (кросс-tenant; ADR-0052: кросс-tenant — только platform-scope) |
| Record-level | не применяется (platform-scope видит любую фиксацию любого агентства и застройщика) |
| Tenant isolation | Запрос не tenanted: TenantId фиксации (= DeveloperCompanyId) не сравнивается с актором; чтение сквозное по platform-scope-политике |
| Audit | read (арбитраж); телефон клиента в логи — только маской; ActorUserId фиксируется |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/pin-manager/crm/fixations/{fixationId} |
| Success status | 200 |
| Idempotency header | не применяется (безопасный GET) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
FixationId | route | Guid | Да | != Guid.Empty | — |
ActorUserId | identity claims | Guid | Да | JWT Менеджера ПИН (platform-scope) | audit/логи |
Модель ответа FixationCardModel
Проекция агрегата; дом полей — fixation-request.md (VO FullName —
client.md; VO LeadWishes — lead.md). Все enum сериализуются
строкой.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
FixationId | Guid | Да | Id | — | Идентификатор заявки (fixationId маршрута). |
Status | FixationStatus | Да | Status | — | Pending/FixedUnique/FixedNonUnique/Rejected/Cancelled. |
DeveloperCompanyId | Guid | Да | TenantId | — | Застройщик-адресат заявки. |
OriginAgencyCompanyId | Guid | Да | OriginAgencyCompanyId | — | Агентство агента-инициатора на момент подачи (иммутабельная атрибуция). |
OriginAgentUserId | Guid | Да | OriginAgentUserId | — | Агент-инициатор на момент подачи: историческая атрибуция и антифрод (иммутабелен). |
ExecutorAgentUserId | Guid | Да | ExecutorAgentUserId | — | Текущий агент-исполнитель фиксации (получатель комиссии при одобрении); мутабелен — переназначается без переписывания Origin. |
InvestorUserId | Guid | Да | InvestorUserId | — | Инвестор (клиент). |
LeadId | Guid | Да | LeadId | — | Лид-основание (переход в карточку лида). |
Client | ClientRefModel ({ ClientId: Guid, ClientName: FullName }) | Да | ClientId + ClientName (snapshot) | — | Клиент фиксации; ФИО — snapshot на момент подачи (иммутабельно), карточка — ClientId. |
ClientPhoneNumber | string | Да | ClientPhoneNumber (snapshot) | телефон — маска в логах | Телефон клиента на момент подачи (E.164). |
Wishes | LeadWishes (owned) | Да | вычисление: WishesOverride ?? Lead.Wishes | — | Эффективные хотелки для показа (шаг 3–4). |
WishesSource | WishesSource (enum) | Да | вычисление | — | Override (снимок заявки) / Lead (актуальная «шапка» лида). |
Uniqueness | FixationUniqueness | Да | Uniqueness | — | Unknown/NoDuplicates/HasDuplicates (авто-проверка застройщика). |
DuplicateOfFixationId | Guid? | Нет | DuplicateOfFixationId | — | Активная фиксация-дубль у застройщика (арбитраж); null — совпадений нет. |
DuplicateMatchKind | DuplicateMatchKind? | Нет | DuplicateMatchKind | — | Чем совпало: Phone/Name/PhoneAndName. |
DecidedByUserId | Guid? | Нет | DecidedByUserId | — | Кто одобрил/отклонил (менеджер застройщика); null — решения нет. |
DecidedAt | DateTimeOffset? | Нет | DecidedAt | — | Момент решения застройщика. |
RejectReason | string? | Нет | RejectReason | — | Причина отклонения (при Status = Rejected). |
CancelledAt | DateTimeOffset? | Нет | CancelledAt | — | Момент отмены агентом. |
DealId | Guid? | Нет | DealId | — | Сделка, созданная при одобрении (deal.md); null — до одобрения. |
CreatedByUserId | Guid? | Нет | CreatedByUserId | — | Кто подал заявку: совпадает с OriginAgentUserId, либо Менеджер ПИН — при подаче от имени агента. |
CreatedAt | DateTimeOffset | Да | CreatedAt | — | Подача заявки. |
UpdatedAt | DateTimeOffset | Да | UpdatedAt | — | Последнее изменение. |
Алгоритм
- Контекст и доступ. Операцию выполняет Менеджер ПИН с permission
crm.fixations.viewв platform-scope (проверено до бизнес-логики). Доступ сквозной, кросс-tenant. - Проверка входа и предусловий.
FixationId != Guid.Empty→ иначеVALIDATION_FAILED. Чтение разрешено во всех статусах, включая терминальныеRejected/Cancelled/Fixed*. - Что читаем. Фиксацию по
FixationIdсквозным доступом (TenantIdфиксации не фильтруется по актору); если фиксации нет или она soft-deleted →CRM_FIXATION_NOT_FOUND. Если у заявки нет собственного снимка хотелок (WishesOverride == null), дополнительно читаем лид-основание поLeadId— для актуальнойLead.Wishes. Дубль-фиксация поDuplicateOfFixationIdнаружу не разворачивается — в ответе только её id иDuplicateMatchKind. - Бизнес-правила и маппинг. Record-level не применяется (platform-scope видит любую сторону — арбитраж).
Эффективные хотелки:
Wishes = WishesOverride ?? Lead.Wishes,WishesSource = Override, если снимок заявки есть, иначеLead. Остальные поляFixationCardModel— 1:1 из фиксации (таблица выше), включая дубль-ссылку иCreatedByUserId. Переходов состояний нет (чтение). - Результат. Карточка
FixationCardModel→ HTTP200. Данные согласованы на момент запроса; ФИО клиента — иммутабельный snapshot заявки. Решение застройщика, отражённое в карточке, публикуют отдельные операции (fixation-approved, fixation-rejected); эта операция лишь показывает их результат.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Внешние вызовы | display-ФИО участников/решивших по их id — отдельный запрос к identity |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | FixationId = Guid.Empty | «Некорректный идентификатор фиксации» | no retry |
CRM_FIXATION_NOT_FOUND | 404 | нет фиксации с таким Id (или soft-deleted) | «Фиксация не найдена» | no retry |
Отсутствие platform-scope-права (403 FORBIDDEN) отсекается политикой до бизнес-логики метода.
Предусловия и постусловия
Предусловия (истинны до сценария):
- актор — Менеджер ПИН (или Служба заботы, read-only) с permission
crm.fixations.viewв platform-scope; кросс-tenant,TenantIdфиксации с актором не сравнивается; - чтение разрешено во всех статусах, включая терминальные
Rejected/Cancelled/FixedUnique/FixedNonUnique.
Постусловия (после запроса):
- состояние не изменяется (read-only); факт арбитражного чтения PII логируется, телефон — маской;
- возвращена карточка обеих сторон с раскрытой дубль-ссылкой (
DuplicateOfFixationId+DuplicateMatchKindкак id/тип, сама дубль-фиксация не разворачивается) и вычисленными эффективными хотелками.
Кросс-сервисные ожидания (eventual-реплики). Display-ФИО участников/решивших по их id резолвятся отдельным запросом к identity (имена могут отставать, идентификаторы присутствуют всегда). Решение застройщика, отражённое в карточке (DecidedAt/Status/DealId), публикуют отдельные операции (fixation-approved/fixation-rejected); эта операция лишь показывает их результат.
Acceptance Criteria
- Happy path (override-хотелки). Дано: заявка со снимком
WishesOverride. Когда:GET …/fixations/{id}. Тогда:200,Wishes= снимок заявки,WishesSource = Override(лид-основание не читается). - Хотелки из лида. Дано:
WishesOverride = null. Когда:GET. Тогда:200,Wishes= актуальныеLead.Wishes,WishesSource = Lead. - Есть дубль. Дано:
Uniqueness = HasDuplicates. Когда:GET. Тогда:200, заполненыDuplicateOfFixationIdиDuplicateMatchKind(Phone/Name/PhoneAndName); дубль-фиксация не разворачивается. - Терминальный статус. Дано:
Status = Rejected/Cancelled/Fixed*. Когда:GET. Тогда:200(чтение во всех статусах). - Кросс-tenant. Дано: фиксация чужого застройщика. Когда:
GET. Тогда:200(platform-scope не ограничен tenant). - Не найдена/soft-deleted. Когда: фиксации с таким
Idнет. Тогда:404 CRM_FIXATION_NOT_FOUND. - Пустой идентификатор. Когда:
FixationId = Guid.Empty. Тогда:400 VALIDATION_FAILED. - Нет platform-scope-права. Когда: актор без
crm.fixations.view@ platform-scope. Тогда:403 FORBIDDEN(отсекается политикой до бизнес-логики).
Совместимость и наблюдаемость
- Новые optional поля
FixationCardModel— additive (без breaking change); удаление/переименование/смена типа — breaking (migration + rollout). Enum-поля (FixationStatus/FixationUniqueness/DuplicateMatchKind/WishesSource) сериализуются строкой; клиент обязан толерантно обрабатывать неизвестное значение. - Logs:
pin-manager fixation-card requested(FixationId,DeveloperCompanyId,OriginAgencyCompanyId,ActorUserId; телефон — маской); metrics:crm_fixation_card_requests_total{area=pin-manager,result}, latency p95 < 1 с (SLO чтений); traces — сквозной trace черезtraceparent; dashboardpin-crm.
Обратные ссылки
Автоссылки: где используется этот документ.
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: fixation-request.md (дом полей, дубль-детект, VO
LeadWishesoverride), lead.md (актуальная «шапка» хотелок), client.md (VOFullName), deal.md (сделка из одобрения). - Агентская сторона той же карточки — ../agent/get-fixation.md.
- События контекста (результат отражается в карточке): fixation-approved, fixation-rejected.
- Карта областей — README контекста; tenancy/platform-scope — ADR-0052.