GET /api/pin-manager/crm/deals — Сквозной список всех сделок платформы
Отдаёт Менеджеру ПИН (и Службе заботы — read-only) сквозной постраничный список всех сделок платформы поверх
всех застройщиков и агентств: без tenant-ограничений, с фильтрами по агенту/застройщику/статусу/объекту и
владельческими колонками (US-MGR-01). Read-only проекция агрегата DealEntity.
Назначение
Отдаёт Менеджеру ПИН (и Службе заботы — read-only) постранично все сделки платформы поверх всех
застройщиков и агентств, без tenant-ограничений, с фильтрами по агенту, застройщику, статусу и объекту и
явными владельческими колонками (DeveloperCompanyId, OriginAgencyCompanyId, ExecutorAgentUserId, PrimaryInvestorUserId) —
чтобы понимать «чья сделка» без принадлежности к одному tenant (US-MGR-01: Менеджер ПИН — авто-участник и
наблюдатель всех сделок). Только чтение; дом агрегата — DealEntity.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | pin-manager |
| Feature group | deals |
| Статус | draft |
| Документ | docs/06-services/crm/api/pin-manager/list-deals.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Менеджер ПИН; Служба заботы — read-only арбитраж) |
| Auth policy / Permissions | permission crm.deals.view, выданный на scope «платформа» (каталог — identity/domain/permission.md) |
| Scope (RBAC) | платформа (кросс-tenant; org/project scope недостаточно) |
| Record-level | не применяется: сквозная видимость всех сделок всех сторон (US-MGR-01) |
| Tenant isolation | снята для этого запроса: TenantId в запрос не передаётся; выборка сквозная, гейт — только платформенный permission (ADR-0052) |
| Audit | read-sensitive (кросс-tenant просмотр): пишется факт запроса и набор фильтров; ФИО/телефон клиента и номер эскроу в логи не пишутся |
Tenant isolation для этого запроса снята: TenantId в запрос не передаётся, выборка идёт сквозным доступом
поверх всех застройщиков и агентств. Record-level также не применяется — единственный гейт — платформенный
permission crm.deals.view (org/project scope недостаточно, ADR-0052).
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/pin-manager/crm/deals |
| Success status | 200 |
| Idempotency header | не применяется (read-only, безопасный метод) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
Paging | query | PagedQuery | Нет | Take 1..200 (default 20), Offset >= 0 (default 0) | защита от unbounded |
Sort | query | SortQuery<DealSortField>? | Нет | поле — enum CreatedAt(default)/UpdatedAt/Price; direction default Desc | — |
ExecutorAgentUserId | query | Guid? | Нет | фильтр по текущему исполнителю сделки (!= Guid.Empty); несуществующий → пустая страница (не 404) | — |
DeveloperCompanyId | query | Guid? | Нет | фильтр по застройщику (= Deal.TenantId, != Guid.Empty) | — |
OriginAgencyCompanyId | query | Guid? | Нет | фильтр по агентству-владельцу (атрибуция, != Guid.Empty) | — |
PropertyObjectId | query | Guid? | Нет | фильтр по объекту (логическая ссылка chessboard, != Guid.Empty) | — |
Status | query | DealStatus? | Нет | enum строкой Application/Booking/Sale/Completed/Cancelled (deal.md); unknown → VALIDATION_FAILED | — |
Search | query | string? | Нет | trim, 2..128; полнотекстовый поиск (multi-match, fuzzy) | вход untrusted — не raw-DSL; PII не в логи |
Все id-фильтры комбинируются по AND. Сортировка типизирована DealSortField (enum, не строка); default
CreatedAt Desc, tiebreak Id desc.
Модель ответа
Постраничный список: элементы DealListItemModel + метаданные страницы (Total, Take, Offset,
HasMore). Элемент — полная проекция DealEntity, включая владельческие колонки
DeveloperCompanyId/OriginAgencyCompanyId, которые проекции developer/agent не отдают.
DealListItemModel
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | DealEntity.Id | — | Идентификатор сделки (навигация в карточку). |
Title | string | Да | DealEntity.Title | — | Заголовок карточки («Объект №…, ЖК …»). |
Client | ClientRefModel ({ ClientId: Guid, ClientName: string }) | Да | клиент по DealEntity.ClientId | PII (ФИО): не в логи | Клиент сделки (client.md). |
Status | DealStatus | Да | DealEntity.Status (enum строкой) | — | Application/Booking/Sale/Completed/Cancelled. |
StepId | Guid | Да | DealEntity.StepId | — | Текущий настраиваемый этап канбана застройщика. |
StepGroup | PipelineStepGroup | Да | DealEntity.StepGroup (enum строкой) | — | Системная группа этапа (pipeline-step.md). |
PropertyObjectId | Guid | Да | DealEntity.PropertyObjectId | — | Объект сделки (логическая ссылка chessboard). |
ProjectId | Guid | Да | DealEntity.ProjectId | — | Проект объекта. |
Price | MoneyView | Да | DealEntity.Price (Money) | — | Полная стоимость объекта недвижимости (snapshot; Amount,CurrencyCode,Formatted). |
ContractType | DealContractType? | Нет | DealEntity.ContractType (enum строкой) | — | Ddu/Dkp; null до группы Contract. |
PaymentMethod | PaymentMethod? | Нет | DealEntity.PaymentMethod (enum строкой) | — | Способ оплаты (purchase-program.md). |
DeveloperCompanyId | Guid | Да | DealEntity.TenantId | — | Застройщик-владелец сделки (кросс-tenant разрез, US-MGR-01). |
OriginAgencyCompanyId | Guid | Да | DealEntity.OriginAgencyCompanyId | — | Агентство-владелец сделки (иммутабельная атрибуция; кросс-tenant разрез). |
ExecutorAgentUserId | Guid | Да | DealEntity.ExecutorAgentUserId | — | Текущий агент-исполнитель сделки (получатель комиссии). |
PrimaryInvestorUserId | Guid | Да | DealEntity.PrimaryInvestorUserId | — | Основной инвестор сделки (возможны соинвесторы InvestorSecondary). |
CreatedAt | DateTimeOffset | Да | DealEntity.CreatedAt | — | Дата создания (ключ сортировки по умолчанию). |
UpdatedAt | DateTimeOffset | Да | DealEntity.UpdatedAt | — | Дата последнего обновления (US-INV-04). |
BookingPaymentStatus | BookingPaymentStatus? | Нет | статус оплаты активной брони (DealEntity.BookingId) | — | Статус оплаты брони строкой (booking.md); null — активной брони нет. |
EscrowAccountNumber и данные фиксаций в список не входят (Sensitive; карточка сделки — отдельная
операция). Все enum — строкой; дом значений — deal.md.
Алгоритм
- Контекст и доступ. Операцию выполняет Менеджер ПИН с permission
crm.deals.viewна scope «платформа»; если платформенного права нет →CRM_DEALS_PLATFORM_SCOPE_REQUIRED.TenantIdв запрос не передаётся (кросс-tenant). - Проверка входа и предусловий.
Paging.Take ∈ 1..200,Offset >= 0;Sort.Field— известное значение enum;Status— известное значение enum (unknown →VALIDATION_FAILED, не «тихий» фильтр);Search—2..128после trim;ExecutorAgentUserId/DeveloperCompanyId/OriginAgencyCompanyId/PropertyObjectId != Guid.Emptyпри заданных. Существование по id не проверяется — это фильтры списка (несуществующий id → пустая страница, без 404). Tenant/record-level предикаты не применяются (сквозной охват — замысел US-MGR-01). - Что читаем. Сделки по составленному предикату (опциональные фильтры
ExecutorAgentUserId,DeveloperCompanyId(=TenantId),OriginAgencyCompanyId,PropertyObjectId,Statusкомбинируются по AND) сквозным доступом (без tenant-предиката; soft-deleted отсекаются). При заданномSearch— предварительный отбор идентификаторов сделок полнотекстовым поиском по проекцииDealSearchDocument(multi-match, fuzzy; platform-scope, ADR-0059), затем ограничение выборки этими id. Порядок — поSort.Field(CreatedAt/UpdatedAt/Price), tiebreak поId; постранично. Для каждой строки —ClientNameклиента иBookingPaymentStatusактивной брони. - Бизнес-правила и маппинг. Переходов состояний нет. Маппинг поле-в-поле — таблица «Модель ответа»;
StepGroup— из денормализованной колонки сделки;DeveloperCompanyId = DealEntity.TenantId. Enum сериализуются строкой. - Результат. Постраничный список
DealListItemModel+ метаданные (Total,Take,Offset) → HTTP200. Пустой результат —200с пустым списком (не 404); эскроу/фиксации не отдаются.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Поиск | при Search — чтение полнотекстовой проекции DealSearchDocument (platform-scope, ADR-0059) |
| Внешние вызовы | нет (данные — своя схема crm; объект/проект/цена — snapshot из chessboard на момент создания сделки) |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | Take вне 1..200, неизвестный Status/Sort.Field, Search короче 2, Guid.Empty в фильтре | «Проверьте параметры запроса» | no retry |
UNAUTHORIZED | 401 | нет/просрочен JWT | «Требуется авторизация» | no retry |
CRM_DEALS_PLATFORM_SCOPE_REQUIRED | 403 | у актора нет permission crm.deals.view на scope «платформа» (org/project scope недостаточно для кросс-tenant) | «Недостаточно прав для сквозного просмотра» | no retry |
Неизвестное значение Status/Sort.Field — это 400 VALIDATION_FAILED, а не молчаливый пропуск фильтра.
Несуществующий ExecutorAgentUserId/DeveloperCompanyId/OriginAgencyCompanyId/PropertyObjectId,
напротив, не ошибка: это фильтр списка, результат — пустая страница 200 (не 404).
Предусловия и постусловия
Предусловия (истинны до сценария):
- актор — Менеджер ПИН (или Служба заботы, read-only) с permission
crm.deals.viewна scope «платформа»; org/project-scope недостаточно для кросс-tenant; - при заданном
Searchиспользуется Elasticsearch-проекцияDealSearchDocument(ADR-0059), обновляемая асинхронно после мутаций сделки.
Постусловия (после запроса):
- состояние не изменяется (read-only); кросс-tenant просмотр логируется (read-sensitive audit: набор фильтров,
Total); - возвращена сквозная страница сделок с владельческими колонками (
DeveloperCompanyId/OriginAgencyCompanyId/ExecutorAgentUserId/PrimaryInvestorUserId);EscrowAccountNumberи данные фиксаций не отдаются.
Кросс-сервисные ожидания (eventual-согласованность поиска). Без Search — прямой запрос PostgreSQL по фильтрам (актуально на момент запроса). С Search — предварительный отбор id по FTS-проекции DealSearchDocument, индексируемой асинхронно: только что созданная/переименованная сделка может не попасть в результаты поиска в течение окна индексации (секунды); фильтры и сортировка применяются к отобранным id.
Acceptance Criteria
- Happy path. Дано: сделки нескольких застройщиков и агентств. Когда:
GET …/dealsбез фильтров. Тогда:200, сквозная страница, порядокCreatedAt Desc(tie-breakId), заполнены владельческие колонки; эскроу/фиксации не отдаются. - Комбинация фильтров (AND). Когда:
executorAgentUserId=A&status=Sale. Тогда: только сделки с текущим исполнителемAв статусеSale. - Несуществующий фильтр-id. Когда:
developerCompanyIdбез совпадений. Тогда:200c пустой страницей (не 404). - Поиск. Дано: сделка проиндексирована в
DealSearchDocument. Когда:search=Иванов. Тогда:200, отбор по FTS, порядок по релевантности; только что созданная сделка может отсутствовать до завершения индексации (окно eventual-consistency). - Неизвестный enum/короткий поиск. Когда:
status=Foo,sort.field=Barилиsearchкороче 2. Тогда:400 VALIDATION_FAILED. - Пустой результат. Когда: фильтры не дали совпадений. Тогда:
200c пустым списком. - Нет платформенного права. Когда: у актора
crm.deals.viewтолько на org/project-scope. Тогда:403 CRM_DEALS_PLATFORM_SCOPE_REQUIRED.
Совместимость и наблюдаемость
- Новые optional поля ответа/фильтры — additive (без breaking); удаление/переименование/смена типа поля или
значения enum — breaking (migration + rollout). Unknown enum во входе —
VALIDATION_FAILED(не игнор).DealStatus/PipelineStepGroup/BookingPaymentStatus— enum строкой; неизвестное значение наружу не отдаётся. - Logs:
pin-manager all-deals listed(набор фильтров,Total, кросс-tenant флаг для аудита; ФИО клиента иSearch— не в логи); metrics:crm_all_deals_list_total{result}, latency p95 < 1 c (SLO списков); traces — сквозной trace черезtraceparent; dashboardpin-crm.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (1): GET /api/pin-manager/crm/leads/{leadId} — Карточка любого лида (Менеджер ПИН)
- Use Cases (1): UC-CRM-006. Вмешательство Менеджера ПИН
- Поиск (1): DealSearchDocument
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: deal.md (дом полей,
DealStatus/DealContractType/PaymentMethod,Money, индексы по агенту/агентству/объекту/tenant); поисковая проекция — ../../search/deals.md (DealSearchDocument, ADR-0059); pipeline-step.md (PipelineStepGroup), client.md (ClientName), booking.md (BookingPaymentStatus). - Соседние списки сделок: developer/list-deals.md, agent/list-deals.md, investor/list-deals.md.
- Контекст: README; tenancy и кросс-tenant платформенный доступ — ADR-0052.