GET /api/pin-manager/crm/fixations — Сквозной список всех фиксаций платформы
Назначение
Отдаёт Менеджеру ПИН (и Службе заботы — read-only) постранично заявки на фиксацию всей платформы (все
агентства × все застройщики) с фильтрами агентство/застройщик/статус, полнотекстовым поиском по ФИО+телефон и
подсветкой дублей — вход в арбитраж и контроль воронки платформы (US-MGR-01..06). Ключевая особенность
tenancy: у сущности TenantId = DeveloperCompanyId (владелец решения — застройщик, ADR-0052), но актор —
platform-scope, ему доступны заявки всех застройщиков; изоляция обеспечивается не record-level и не
tenant-предикатом, а исключительно platform-scope permission (кросс-tenant — только Менеджер ПИН/Служба
заботы). Каждая строка несёт результат сохранённой авто-проверки дублей (Uniqueness) для подсветки в UI.
Только чтение; дом агрегата — FixationRequestEntity.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | pin-manager |
| Feature group | fixations |
| Статус | draft |
| Документ | docs/06-services/crm/api/pin-manager/list-fixations.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Менеджер ПИН; Служба заботы — read-only арбитраж) |
| Auth policy / Permissions | crm.fixations.view в platform-scope (identity RBAC); инвестор/агент/застройщик кросс-tenant доступа не имеют |
| Scope (RBAC) | платформа (кросс-tenant): выборка по всем TenantId без ограничения |
| Record-level | не применяется: platform-scope видит все заявки всех тенантов; OriginAgencyCompanyId/DeveloperCompanyId/OriginAgentUserId — опциональные фильтры, не ограничение доступа |
| Tenant isolation | Особый случай: единого TenantId нет; tenant-предикат отключён, единственный гейт — platform-scope permission; soft-deleted отсекаются |
| Audit | read (не sensitive: осознанно переданные для фиксации данные); в логи — только Id/счётчики/фильтры, телефон/ФИО не пишутся |
Особый случай tenancy: у сущности нет единого TenantId — у каждой заявки свой (TenantId = DeveloperCompanyId).
Автоматический tenant-предикат отключён; единственный гейт — platform-scope permission crm.fixations.view.
Кросс-tenant доступ есть только у Менеджера ПИН и Службы заботы (read-only); инвестор/агент/застройщик доступа
не имеют.
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/pin-manager/crm/fixations |
| Success status | 200 |
| Idempotency header | не применяется (безопасный read) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
ActorUserId | identity claims | Guid | Да | Required | Audit; проверка platform-scope |
OriginAgencyCompanyId | query | Guid? | Нет | != Guid.Empty если задан; несуществующий → пустая страница, без 404 | Фильтр по агентству-инициатору (атрибуция) |
DeveloperCompanyId | query | Guid? | Нет | != Guid.Empty если задан (= TenantId сущности) | Фильтр по застройщику |
OriginAgentUserId | query | Guid? | Нет | != Guid.Empty если задан | Фильтр по агенту-инициатору (иммутабельная атрибуция) |
Status | query | FixationStatus? | Нет | enum строкой; неизвестное значение → VALIDATION_FAILED | — |
Uniqueness | query | FixationUniqueness? | Нет | enum строкой; быстрый фильтр «только дубли» (HasDuplicates) | — |
SearchTerm | query | string? | Нет | trim, 1..128 после trim; полнотекстовый поиск (multi-match, fuzzy), не raw-DSL | PII (ФИО/телефон): в логи не пишется |
Paging | query | PagedQuery | Нет | Take ∈ [1..200] (default 20), Offset >= 0 (default 0) | — |
Sort | query | SortQuery<FixationSortField>? | Нет | FixationSortField ∈ {CreatedAt, DecidedAt, Status}; default CreatedAt desc (при поиске — relevance/score desc) | — |
Все фильтры комбинируются по AND; OriginAgencyCompanyId/DeveloperCompanyId/OriginAgentUserId/Status/Uniqueness —
логические предикаты (отсутствие совпадений → пустая страница, не ошибка). Сортировка типизирована
FixationSortField (enum, не строка), tiebreak Id desc.
Модель ответа
Постраничный список: элементы FixationListItemModel + метаданные страницы (Total, Take, Offset,
HasMore). Строка — общая проекция FixationListItemModel, дом которой — агентский список
list-fixations.md (поля Id, ClientName, ClientPhoneNumber,
DeveloperCompanyId, Status, Uniqueness, DecidedAt, CreatedAt); поля-источник — агрегат
FixationRequestEntity. Здесь таблица не дублируется, фиксируется только
дельта платформенного использования:
- строки не ограничены одним
TenantId— в одной странице соседствуют заявки разных застройщиков; застройщик каждой строки —DeveloperCompanyId, агентство/агент используются как фильтры и резолвятся на карточке фиксации при drill-down; - подсветка дублей — по полю
Uniqueness(HasDuplicatesпри найденном совпадении в пределах того же застройщика); конкретная дубль-фиксация (DuplicateOfFixationId/DuplicateMatchKind) видна на карточке застройщика (developer/list-fixations.md); - все enum — строкой; дом значений и правило дубль-детекта — fixation-request.md.
Алгоритм
- Контекст и доступ. Операцию выполняет Менеджер ПИН с platform-scope permission
crm.fixations.view; если платформенного права нет →CRM_FIXATIONS_FORBIDDEN. ЕдиногоTenantIdнет (кросс-tenant). - Проверка входа и предусловий.
Paging.Take ∈ [1..200],Offset >= 0;SearchTerm(если задан) —1..128после trim;Status/Uniqueness/Sort.Field— известные члены enum (unknown →VALIDATION_FAILED, не «тихий» фильтр);OriginAgencyCompanyId/DeveloperCompanyId/OriginAgentUserId != Guid.Emptyпри заданных. Существование сущностей по id не проверяется (несуществующий/чужой id → пустая страница, без 404). Record-level и tenant-предикат не применяются (кросс-tenant, гейт — только platform-scope permission). - Что читаем. Заявки на фиксацию по составленному предикату сквозной выборкой (без tenant-предиката;
soft-deleted отсекаются). Опциональные фильтры (AND):
TenantId == DeveloperCompanyId,OriginAgencyCompanyId,OriginAgentUserId,Status,Uniqueness. При заданномSearchTerm— предварительный отбор идентификаторов заявок полнотекстовым поиском по проекцииFixationRequestSearchDocument(multi-match поSearchText, fuzzy; platform-scope, ADR-0059), затем ограничение выборки этими id и сортировка по relevance (score) desc; иначе порядок изSort(defaultCreatedAtdesc →Id desc). Постранично. - Бизнес-правила и маппинг. Переходов состояний нет. Маппинг поле-в-поле — по общей модели
FixationListItemModel;Uniquenessберётся из уже сохранённого результата авто-проверки (правило детекта — fixation-request.md, «Дубль-детект»), в списке не пересчитывается. - Результат. Постраничный список
FixationListItemModel+ метаданные (Total,Take,Offset,HasMore) → HTTP200. Пустой результат —200с пустым списком (не 404). PII:ClientPhoneNumber— в ответе без маски (platform-scope доступ), в логи — толькоId/Total.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Поиск | при поиске — чтение полнотекстовой проекции FixationRequestSearchDocument (platform-scope, ADR-0059) |
| Внешние вызовы | display-имена агентств/застройщиков/агентов и карточка дубль-фиксации резолвятся отдельно на фронте |
Ошибки и failure modes
Отсутствие совпадений по OriginAgencyCompanyId/DeveloperCompanyId/OriginAgentUserId/Status/Uniqueness
не считается ошибкой — возвращается пустая страница (200), не 404. 403 CRM_FIXATIONS_FORBIDDEN — только
при отсутствии platform-scope permission crm.fixations.view у актора.
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | Take/Offset вне диапазона; неизвестный Status/Uniqueness/Sort.Field; SearchTerm > 128; Guid.Empty в фильтре | «Проверьте параметры запроса» | no retry |
CRM_FIXATIONS_FORBIDDEN | 403 | нет platform-scope permission crm.fixations.view (актор не Менеджер ПИН/Служба заботы) | «Недостаточно прав для сквозного просмотра фиксаций» | no retry |
UNAUTHORIZED | 401 | нет/невалидный JWT | «Требуется авторизация» | no retry |
Предусловия и постусловия
Предусловия (истинны до сценария):
- актор — Менеджер ПИН (или Служба заботы, read-only) с permission
crm.fixations.viewв platform-scope; инвестор/агент/застройщик кросс-tenant доступа не имеют; - единого
TenantIdу выборки нет (TenantId = DeveloperCompanyIdу каждой строки свой);Uniquenessберётся из сохранённого результата авто-проверки дублей (в списке не пересчитывается).
Постусловия (после запроса):
- состояние не изменяется (read-only); в логи — только
Id/счётчики/фильтры, телефон/ФИО не пишутся; - возвращена сквозная страница заявок всех застройщиков;
ClientPhoneNumber— без маски (platform-scope доступ).
Кросс-сервисные ожидания (eventual-согласованность поиска). Без SearchTerm — прямой запрос PostgreSQL по фильтрам (актуально на момент запроса). С SearchTerm — предварительный отбор id по FTS-проекции FixationRequestSearchDocument (ADR-0059), индексируемой асинхронно: только что поданная заявка может не попасть в результаты поиска в течение окна индексации; порядок — по релевантности (score) desc. Конкретная дубль-фиксация (DuplicateOfFixationId/DuplicateMatchKind) в списке не разворачивается — видна на карточке фиксации/застройщика при drill-down.
Acceptance Criteria
- Happy path. Дано: заявки нескольких застройщиков. Когда:
GET …/fixationsбез фильтров. Тогда:200, в одной странице соседствуют заявки разных застройщиков, порядокCreatedAt desc→Id desc. - Быстрый фильтр дублей. Когда:
uniqueness=HasDuplicates. Тогда: только заявки с подсвеченным дублем (по сохранённому результату авто-проверки). - Комбинация фильтров (AND). Когда:
developerCompanyId=D&status=Pending. Тогда: толькоPending-заявки застройщикаD. - Поиск по ФИО/телефону. Когда:
search=+7999…. Тогда:200, отбор по FTS, порядок по релевантности; только что поданная заявка может отсутствовать до индексации. - Несуществующий фильтр-id. Когда:
originAgencyCompanyIdбез совпадений. Тогда:200c пустой страницей (не 404). - Неизвестный enum/длинный поиск. Когда:
status=Foo,uniqueness=BarилиsearchTerm> 128. Тогда:400 VALIDATION_FAILED. - Нет platform-scope-права. Когда: актор не Менеджер ПИН/Служба заботы. Тогда:
403 CRM_FIXATIONS_FORBIDDEN.
Совместимость и наблюдаемость
- Новые optional фильтры/поля ответа — additive (без breaking). Удаление/переименование полей или сужение
enum
FixationStatus/FixationUniqueness— breaking (миграция клиента + rollout); неизвестный член enum на клиенте — по правилу unknown-enum (толерантная трактовка). - Logs:
pin-manager-fixations listed(ActorUserId, набор фильтров,Total, флагfts; телефон/ФИО — не пишутся); metrics:crm_platform_fixations_list_total{fts,has_duplicates}, latency p95 < 1 c (SLO списков; кросс-tenant объём выше — контроль по индексам); traces — сквозной trace черезtraceparent; dashboardpin-crm.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (1): GET /api/pin-manager/crm/leads/{leadId} — Карточка любого лида (Менеджер ПИН)
- registries (1): Реестр API-методов PIN
Связанные документы
- Дом сущности, правило дубль-детекта/FTS и enum (
FixationStatus/FixationUniqueness/DuplicateMatchKind): fixation-request.md; VO ФИО — client.md; лид-основание — lead.md. - Решения по заявкам (не публикуются этим методом): fixation-approved.md, fixation-rejected.md.
- Дом общей строки
FixationListItemModel— агентский список list-fixations.md; застройщик-проекция того же дома — list-fixations.md; карта областей — README. - ADR: 0052 tenancy, 0014 repository-pipeline и scope-filters.