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

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 areapin-manager
Feature groupfixations
Статусdraft
Документdocs/06-services/crm/api/pin-manager/list-fixations.md

Актор и доступ

ПроверкаЗначение
Actor typeUser (Менеджер ПИН; Служба заботы — read-only арбитраж)
Auth policy / Permissionscrm.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 отсекаются
Auditread (не sensitive: осознанно переданные для фиксации данные); в логи — только Id/счётчики/фильтры, телефон/ФИО не пишутся
Доступ

Особый случай tenancy: у сущности нет единого TenantId — у каждой заявки свой (TenantId = DeveloperCompanyId). Автоматический tenant-предикат отключён; единственный гейт — platform-scope permission crm.fixations.view. Кросс-tenant доступ есть только у Менеджера ПИН и Службы заботы (read-only); инвестор/агент/застройщик доступа не имеют.

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/pin-manager/crm/fixations
Success status200
Idempotency headerне применяется (безопасный read)
Correlationсквозной trace через заголовок traceparent

Входные данные / параметры запроса

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
ActorUserIdidentity claimsGuidДаRequiredAudit; проверка platform-scope
OriginAgencyCompanyIdqueryGuid?Нет!= Guid.Empty если задан; несуществующий → пустая страница, без 404Фильтр по агентству-инициатору (атрибуция)
DeveloperCompanyIdqueryGuid?Нет!= Guid.Empty если задан (= TenantId сущности)Фильтр по застройщику
OriginAgentUserIdqueryGuid?Нет!= Guid.Empty если заданФильтр по агенту-инициатору (иммутабельная атрибуция)
StatusqueryFixationStatus?Нетenum строкой; неизвестное значение → VALIDATION_FAILED
UniquenessqueryFixationUniqueness?Нетenum строкой; быстрый фильтр «только дубли» (HasDuplicates)
SearchTermquerystring?Нетtrim, 1..128 после trim; полнотекстовый поиск (multi-match, fuzzy), не raw-DSLPII (ФИО/телефон): в логи не пишется
PagingqueryPagedQueryНетTake ∈ [1..200] (default 20), Offset >= 0 (default 0)
SortquerySortQuery<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.

Алгоритм

  1. Контекст и доступ. Операцию выполняет Менеджер ПИН с platform-scope permission crm.fixations.view; если платформенного права нет → CRM_FIXATIONS_FORBIDDEN. Единого TenantId нет (кросс-tenant).
  2. Проверка входа и предусловий. 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).
  3. Что читаем. Заявки на фиксацию по составленному предикату сквозной выборкой (без 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 (default CreatedAt desc → Id desc). Постранично.
  4. Бизнес-правила и маппинг. Переходов состояний нет. Маппинг поле-в-поле — по общей модели FixationListItemModel; Uniqueness берётся из уже сохранённого результата авто-проверки (правило детекта — fixation-request.md, «Дубль-детект»), в списке не пересчитывается.
  5. Результат. Постраничный список FixationListItemModel + метаданные (Total, Take, Offset, HasMore) → HTTP 200. Пустой результат — 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_FAILED400Take/Offset вне диапазона; неизвестный Status/Uniqueness/Sort.Field; SearchTerm > 128; Guid.Empty в фильтре«Проверьте параметры запроса»no retry
CRM_FIXATIONS_FORBIDDEN403нет platform-scope permission crm.fixations.view (актор не Менеджер ПИН/Служба заботы)«Недостаточно прав для сквозного просмотра фиксаций»no retry
UNAUTHORIZED401нет/невалидный 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 descId desc.
  • Быстрый фильтр дублей. Когда: uniqueness=HasDuplicates. Тогда: только заявки с подсвеченным дублем (по сохранённому результату авто-проверки).
  • Комбинация фильтров (AND). Когда: developerCompanyId=D&status=Pending. Тогда: только Pending-заявки застройщика D.
  • Поиск по ФИО/телефону. Когда: search=+7999…. Тогда: 200, отбор по FTS, порядок по релевантности; только что поданная заявка может отсутствовать до индексации.
  • Несуществующий фильтр-id. Когда: originAgencyCompanyId без совпадений. Тогда: 200 c пустой страницей (не 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; dashboard pin-crm.

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

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

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