GET /api/agent/crm/deals/{dealId} — Карточка сделки (агент)
Read-модель карточки сделки агентской стороны (/crm/deals/[dealId], вкладки «Условия/Об объекте/О проекте/
Документы»): агент (или руководитель его агентства) открывает сделку любого своего клиента и видит финансовую
сводку, участников, текущую бронь и сводку документов (US-AGT-02/03). Только чтение: дом полей агрегата —
deal.md. Таймлайн («Ход сделки») и полный список документов — отдельные paged-методы
(см. «Побочные эффекты»).
Назначение
Дать агенту карточку одной сделки по dealId: финансовые условия (цена, банк/эскроу, способ оплаты,
предупреждение о неоплаченной брони), активный состав участников, текущее бронирование и индикатор «Требуется
загрузка» по документам (US-AGT-06).
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | agent |
| Feature group | deals |
| Статус | draft |
| Документ | docs/06-services/crm/api/agent/get-deal.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Агент — свои сделки; Руководитель агентства — весь OriginAgencyCompanyId; Менеджер ПИН — platform-scope) |
| Auth policy / Permissions | crm.deals.view (JWT) |
| Scope (RBAC) | организация (агентство) — OriginAgencyCompanyId |
| Record-level | актор — активный участник сделки (обычно Executor) или руководитель агентства-владельца (DealEntity.OriginAgencyCompanyId = actor.CompanyId) |
| Tenant isolation | Запрос не tenanted (Deal.TenantId = DeveloperCompanyId ≠ актор): чтение агентской стороны по явному предикату OriginAgencyCompanyId + членство участника-исполнителя (ADR-0052) |
| Audit | read; EscrowAccountNumber — маска ****NNNN (Sensitive); телефоны/PII в логи не пишутся |
Сделка живёт в tenant застройщика (Deal.TenantId = DeveloperCompanyId), а не агентства актора — запрос не
tenanted. Агентская сторона читает сделку по явному предикату OriginAgencyCompanyId (ADR-0052) плюс
record-level: рядовой агент должен быть активным участником-исполнителем сделки, руководитель агентства
(IsAgencyManager) видит любую сделку своего OriginAgencyCompanyId без этого условия.
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/agent/crm/deals/{dealId} |
| Success status | 200 |
| Idempotency header | не применяется (GET — безопасный) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
DealId | route | Guid | Да | != Guid.Empty | — |
ActorUserId | identity claims | Guid | Да | JWT агента | record-level (владелец) |
ActorCompanyId | identity claims | Guid | Да | OriginAgencyCompanyId сделки — предикат record-level | — |
IsAgencyManager | identity claims (scope permission) | bool | Да | вычисляется из permission; снимает фильтр участия (актор — Executor) | — |
Модель ответа DealCardModel
Проекция агрегата; дом полей — deal.md (VO Money; enum DealStatus/PaymentMethod/
DealContractType/EscrowStatus/DealChatStatus). Ниже — только дельта.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
DealId | Guid | Да | Id | — | Идентификатор сделки. |
Title | string | Да | Title | — | Заголовок карточки. |
Status | DealStatus (enum строкой) | Да | Status | — | Application/Booking/Sale/Completed/Cancelled. |
StepId | Guid | Да | StepId | — | Текущий этап канбана застройщика. |
StepGroup | PipelineStepGroup (enum строкой) | Да | StepGroup | — | Системная группа этапа. |
ClientId | Guid | Да | ClientId | — | Карточка клиента агентства (client.md). |
PrimaryInvestorUserId | Guid | Да | PrimaryInvestorUserId | — | Основной инвестор сделки (возможны дополнительные InvestorSecondary в Members). |
PropertyObjectId | Guid | Да | PropertyObjectId | — | Объект (display-данные — API chessboard). |
ProjectId | Guid | Да | ProjectId | — | Проект объекта. |
BuildingId | Guid? | Нет | BuildingId | — | Здание/литер. |
Finance | DealFinanceModel | Да | computed | — | Финансовая сводка (цена, банк/эскроу, оплата). |
Members | IReadOnlyList<DealMemberModel> | Да | активные DealMemberEntity (RemovedAt is null) | — | Состав участников (bounded, ≤4). |
CurrentBooking | DealBookingModel? | Нет | бронь по BookingId | — | Текущая бронь; null — активной брони нет. |
Documents | DealDocumentsSummaryModel | Да | computed по crm.deal_documents | — | Сводка вкладки «Документы» (статусы + индикатор US-AGT-06). |
ChatStatus | DealChatStatus (enum строкой) | Да | ChatStatus | — | Готовность чата сделки (проекция messenger). |
TermsAcknowledgedAt | DateTimeOffset? | Нет | TermsAcknowledgedAt | — | Отметка инвестора «Ознакомлен» (US-INV-18). |
CancelReason | string? | Нет | CancelReason | — | Причина отказа (при Status = Cancelled). |
CreatedAt | DateTimeOffset | Да | CreatedAt | — | Создание сделки. |
UpdatedAt | DateTimeOffset | Да | UpdatedAt | — | Дата последнего обновления (US-INV-04). |
DealFinanceModel
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Price | Money (owned: Amount,Currency) | Да | Deal.Price (snapshot chessboard) | — | Полная стоимость объекта (US-INV-05). |
PaymentMethod | PaymentMethod? (enum строкой) | Нет | Deal.PaymentMethod | — | Способ оплаты (purchase-program.md). |
InstallmentPresetId | Guid? | Нет | Deal.InstallmentPresetId | — | Пресет рассрочки (если Installment). |
ContractType | DealContractType? (enum строкой) | Нет | Deal.ContractType | — | ДДУ/ДКП (US-INV-08). |
BankId | Guid? | Нет | Deal.BankId | — | Банк сделки; null → UI «Не указан» (US-INV-07). |
EscrowStatus | EscrowStatus (enum строкой) | Да | Deal.EscrowStatus | — | None/Provided (US-INV-19). |
EscrowAccountNumber | string? | Нет | Deal.EscrowAccountNumber | маска ****NNNN (последние 4) | Эскроу-счёт; полный — только SellManager/PinManager (deal.md). |
PaymentWarning | bool | Да | computed | — | true, если CurrentBooking != null и CurrentBooking.PaymentStatus = Pending — предупреждение «бронь не оплачена». |
DealMemberModel
Дом — DealMemberEntity (deal.md); дельты нет.
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
UserId | Guid | Да | DealMemberEntity.UserId | Пользователь-участник. |
Role | ParticipantRole (enum строкой) | Да | DealMemberEntity.Role | InvestorPrimary/Executor/SellManager/PinManager (доп. CoAgent/Assistant/Lawyer/InvestorSecondary/Responsible). |
DealBookingModel
Дом — booking.md; ниже проекция текущей брони (US-INV-06/10).
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
BookingId | Guid | Да | BookingEntity.Id | Идентификатор брони. |
Status | BookingStatus (enum строкой) | Да | Status | Reserved/AwaitingPayment/Booked/… |
ProgramName | string | Да | ProgramTerms.Name | Название программы бронирования (snapshot). |
IsPaid | bool | Да | ProgramTerms.IsPaid | Платная ли программа. |
PaymentStatus | BookingPaymentStatus (enum строкой) | Да | PaymentStatus | NotRequired/Pending/Paid (источник PaymentWarning). |
PaymentAmount | Money? (owned) | Нет | PaymentAmount | Сумма платной брони. |
ReserveExpiresAt | DateTimeOffset | Да | ReserveExpiresAt | Дедлайн резерва. |
BookingExpiresAt | DateTimeOffset? | Нет | BookingExpiresAt | Дедлайн подтверждённой брони. |
DealDocumentsSummaryModel
Дом — deal-document.md; скалярная сводка вкладки (полный список — отдельный paged-метод).
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
RequiresUpload | bool | Да | есть документы Status = Requested | Индикатор «Требуется загрузка» (US-AGT-06). |
RequestedCount | int | Да | число Status = Requested | Запрошено, файла нет. |
UploadedCount | int | Да | число Status ∈ {Uploaded, UnderReview} | Загружено / на проверке. |
AcceptedCount | int | Да | число Status = Accepted | Принято. |
RejectedCount | int | Да | число Status = Rejected | Отклонено (нужна повторная загрузка). |
Алгоритм
- Контекст и доступ. Операцию выполняет агент (permission
crm.deals.view); признакIsAgencyManagerснимает фильтр по владельцу. - Проверка входа.
DealId != Guid.Empty— иначе ошибка валидации. Существование и доступ проверяются при чтении (шаги 3–4). - Что читаем. Сделку (агентская сторона выбирается по явному предикату, у актора нет tenant застройщика)
с активными участниками (
RemovedAt is null); если нет/soft-deleted →CRM_DEAL_NOT_FOUND. Текущую бронь (еслиBookingId != null) — проекция вDealBookingModel. Скалярную сводку документов по статусам — полный список документов и таймлайн здесь не грузятся (отдельные вкладки). - Маппинг и правила доступа. Record-level:
IsAgencyManager && OriginAgencyCompanyId = actor.CompanyIdили актор — активный участник сделки (Executor) — иначеFORBIDDEN(403, чужая сделка не раскрывается). ВычислитьFinance.PaymentWarning = бронь != null && PaymentStatus == Pending;EscrowAccountNumber→ маска****NNNN. Остальные поля маппятся 1:1. - Результат. Карточка сделки → HTTP
200. Freshness — актуально на момент запроса (ChatStatus/ФИО — eventual-проекции). Ошибки →ProblemDetails(404/403/400).
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Интеграционные события | none (read-only GET) |
| Внешние вызовы | none (display-данные объекта/банка — отдельные запросы клиента к chessboard/catalog) |
| Проекции / поиск | none (прямое чтение агрегата, брони, скалярных счётчиков документов) |
| Уведомления | none |
| Отдельные запросы вкладок | «Ход сделки» — таймлайн (deal-timeline-event.md); полный список документов по DealId. В этот ответ не входят. |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | DealId = Guid.Empty | «Некорректный идентификатор сделки» | no retry |
FORBIDDEN | 403 | не владелец сделки и не руководитель агентства-владельца | «Нет доступа к сделке» | no retry |
CRM_DEAL_NOT_FOUND | 404 | нет сделки с таким Id (или soft-deleted) | «Сделка не найдена» | no retry |
Совместимость и наблюдаемость
- Новые optional поля
DealCardModel/подмоделей — additive (без breaking change); удаление/переименование/смена типа — breaking (migration + rollout). Enum-поля (DealStatus/PaymentMethod/BookingStatus/BookingPaymentStatus/EscrowStatus/…) сериализуются строкой; клиент обязан толерантно обрабатывать неизвестное значение. - Logs:
deal-card requested(DealId,OriginAgencyCompanyId,ExecutorAgentUserId; эскроу/телефоны — не пишутся); metrics:crm_deal_card_requests_total{result}, latency p95 (< 1 с, SLO чтений README); traces — сквозной trace черезtraceparent; dashboardpin-crm; runbook —docs/06-services/crm/runbooks/.
Acceptance Criteria
- Happy path. Given агент-владелец и его сделка; When
GET; Then200сDealCardModel: финансовая сводка (вычисленPaymentWarning), активные участники, текущая бронь, сводка документов (RequiresUpload), эскроу — маска****NNNN. - Руководитель. Given руководитель агентства и сделка его
OriginAgencyCompanyIdдругого агента; WhenGET; Then200(record-level по владельцу снят). - Неоплаченная бронь. Given активная бронь в статусе оплаты
Pending; WhenGET; ThenFinance.PaymentWarning = true. - Чужая сделка. Given актор не владелец и не руководитель агентства-владельца; When
GET; Then403 FORBIDDEN(сделка не раскрывается). - Не найдена. Given несуществующая или soft-deleted сделка; When
GET; Then404 CRM_DEAL_NOT_FOUND. - Валидация. Given
DealId = Guid.Empty; WhenGET; Then400 VALIDATION_FAILED. - Маска эскроу. Given заполненный
EscrowAccountNumber; WhenGETагентом; Then возвращается маска****NNNN(полный номер — толькоSellManager/PinManager). - Без брони. Given сделка без активной брони; When
GET; ThenCurrentBooking = null,PaymentWarning = false.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (2): POST /api/developer/crm/deals/{dealId}/rosreestr — Подтвердить регистрацию Росреестра, POST /api/investor/crm/deals/{dealId}/acknowledge-terms — Отметить «Ознакомлен»
- Поиск (1): DealSearchDocument
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: deal.md (дом полей, VO
Money, эскроу-маскирование), booking.md (текущая бронь,PaymentStatus), deal-document.md (статусы документов, US-AGT-06), client.md. - Смежные операции воронки агента: get-fixation.md, get-client.md; карта областей — README контекста.
- Событие снятия предупреждения оплаты: booking-paid.md (
pin.crm.booking-paid.v1,PaymentStatus → Paid). Tenancy/record-level: ADR-0052.