GET /api/developer/crm/deals/{dealId} — Карточка сделки (застройщик)
Read-модель карточки сделки для стороны застройщика (/crm/deals/[dealId], US-DEV-02):
менеджер застройщика открывает любую сделку своего тенанта и видит объект (объект недвижимости/проект), контакты клиента,
финансовые условия (цена, способ оплаты, тип договора), эскроу-реквизиты и документы сделки с их статусами
(US-DEV-05, US-DEV-06). Только чтение: дом полей сделки — deal.md, документов —
deal-document.md; здесь описана лишь проекция и алгоритм её сборки.
Назначение
Дать менеджеру застройщика единую карточку одной сделки по dealId в границах своего DeveloperCompanyId:
статус/этап канбана, снимок объекта и цены, ФИО/телефон клиента-покупателя, условия оплаты и договора, банк и
статус эскроу (полный номер счёта — только SellManager, иначе маска, US-DEV-06), а также перечень документов
сделки с их статусами проверки (US-DEV-05). Вкладки «О объекте недвижимости»/«О проекте» — данные chessboard, отдельные
запросы.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | developer |
| Feature group | deals |
| Статус | draft |
| Документ | docs/06-services/crm/api/developer/get-deal.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Менеджер застройщика; SellManager — полный эскроу; Менеджер ПИН — platform-scope) |
| Auth policy / Permissions | crm.deals.view (JWT) |
| Scope (RBAC) | организация (застройщик) — TenantId = DeveloperCompanyId |
| Record-level | не применяется (US-DEV-02: любая сделка своего тенанта; фильтрация только по TenantId) |
| Tenant isolation | TenantId = DeveloperCompanyId в каждом запросе |
| Audit | read; EscrowAccountNumber — Sensitive: наружу маска, в логи запрещён; телефон — маска в логах |
Читает менеджер застройщика в границах своего TenantId = DeveloperCompanyId — любая сделка тенанта, без
record-level ограничения (US-DEV-02). Полный номер эскроу-счёта видит только SellManager (роль/
crm.deals.decide); остальным — маска ****NNNN. Сделка чужого tenant не раскрывается: 404 CRM_DEAL_NOT_FOUND, а не 403.
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/developer/crm/deals/{dealId} |
| Success status | 200 |
| Idempotency header | не применяется (безопасный GET) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims | Guid | Да | Required, != Guid.Empty | Tenant isolation (DeveloperCompanyId) |
ActorUserId | identity claims | Guid | Да | JWT менеджера застройщика | audit/логи |
ActorIsSellManager | identity claims (роль/участие) | bool | Да | вычисляется из роли SellManager/crm.deals.decide | управляет раскрытием EscrowAccountNumber (шаг 4) |
DealId | route | Guid | Да | != Guid.Empty; сделка своего TenantId (шаг 2–3) | — |
Модель ответа DealCardModel
Проекция агрегата; дом полей — deal.md (VO Money; enum DealStatus/DealContractType/
PaymentMethod/EscrowStatus/DealChatStatus; группа этапа — pipeline-step.md).
Ниже — только дельта проекции; все enum сериализуются строкой.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
DealId | Guid | Да | Id | — | Идентификатор сделки (dealId маршрута). |
Title | string | Да | Title | — | Заголовок карточки («Объект №…, ЖК …»). |
Status | DealStatus | Да | Status | — | Application/Booking/Sale/Completed/Cancelled (US-INV-03). |
StepId | Guid | Да | StepId | — | Текущий этап канбана застройщика. |
StepGroup | PipelineStepGroup | Да | StepGroup | — | Системная группа этапа (денормализация). |
PropertyObjectId | Guid | Да | PropertyObjectId | — | Объект сделки (chessboard); display-данные — отдельный запрос. |
ProjectId | Guid | Да | ProjectId | — | Проект объекта (фильтры/статистика застройщика). |
BuildingId | Guid? | Нет | BuildingId | — | Здание/литер. |
Price | MoneyView | Да | Price (owned VO Money) | — | Снимок стоимости объекта недвижимости (Amount/CurrencyId + display CurrencyCode). |
PaymentMethod | PaymentMethod? | Нет | PaymentMethod | — | Способ оплаты (purchase-program.md); влияет на этапы. |
InstallmentPresetId | Guid? | Нет | InstallmentPresetId | — | Пресет рассрочки (при PaymentMethod = Installment). |
ContractType | DealContractType? | Нет | ContractType | — | Ddu/Dkp (US-INV-08). |
BankId | Guid? | Нет | BankId | — | Банк сделки/эскроу (справочник chessboard); null — «Не указан». |
EscrowAccountNumber | string? | Нет | EscrowAccountNumber | SellManager — полный; иначе маска ****NNNN (шаг 4) | Номер эскроу-счёта (Sensitive, US-DEV-06). |
EscrowStatus | EscrowStatus | Да | EscrowStatus | — | None/Provided («Эскроу забронирован», US-INV-19). |
BookingId | Guid? | Нет | BookingId | — | Текущее бронирование сделки (booking.md). |
ContractsPreparedAt | DateTimeOffset? | Нет | ContractsPreparedAt | — | «Договоры подготовлены» (US-DEV-04). |
RosreestrRegisteredAt | DateTimeOffset? | Нет | RosreestrRegisteredAt | — | Регистрация в Росреестре (ТЗ.md:407). |
CompletedAt | DateTimeOffset? | Нет | CompletedAt | — | Завершение сделки (ТЗ.md:409). |
CancelledAt | DateTimeOffset? | Нет | CancelledAt | — | Момент отказа. |
CancelReason | string? | Нет | CancelReason | — | Причина отказа (при Status = Cancelled). |
TermsAcknowledgedAt | DateTimeOffset? | Нет | TermsAcknowledgedAt | — | «Ознакомлен» инвестора (US-INV-18). |
ChatStatus | DealChatStatus | Да | ChatStatus | — | Проекция готовности чата (messenger, ADR-0055). |
PrimaryInvestorUserId | Guid | Да | PrimaryInvestorUserId | — | Основной инвестор-покупатель (допускаются соинвесторы InvestorSecondary в составе участников). |
ExecutorAgentUserId | Guid | Да | ExecutorAgentUserId | — | Текущий агент-исполнитель сделки (получатель комиссии). |
OriginAgentUserId | Guid | Да | OriginAgentUserId | — | Агент-инициатор (историческая атрибуция, иммутабелен). |
OriginAgencyCompanyId | Guid | Да | OriginAgencyCompanyId | — | Агентство агента-инициатора на момент создания (атрибуция). |
ClientId | Guid | Да | ClientId | — | Карточка клиента агентства. |
ClientName | FullNameModel | Да | ClientEntity.Name (снимок) | — | ФИО покупателя (client.md). |
ClientPhoneNumber | string | Да | ClientEntity.PhoneNumber (снимок) | телефон — маска в логах | Телефон покупателя (E.164, PII). |
Members | DealMemberModel[] | Да | активные DealMemberEntity (шаг 3) | — | Состав участников сделки. |
Documents | DealDocumentSummaryModel[] | Да | документы сделки (шаг 3) | — | Документы и их статусы (US-DEV-05). |
CreatedAt | DateTimeOffset | Да | CreatedAt | — | Создание сделки. |
UpdatedAt | DateTimeOffset | Да | UpdatedAt | — | Последнее обновление (US-INV-04). |
MoneyView
Read-проекция VO Money (дом — deal.md).
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
Amount | decimal | Да | Money.Amount | Сумма. |
CurrencyId | short | Да | Money.CurrencyId | Типизированная ссылка на справочник валют handbook. |
CurrencyCode | string | Да | CurrencySnapshot.IsoCode справочника по CurrencyId (snapshots.md) | Display-код валюты (RUB) — чтобы UI не делал отдельный lookup. |
DealMemberModel
Проекция DealMemberEntity (дом — deal.md), только активные (RemovedAt is null).
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
UserId | Guid | Да | DealMemberEntity.UserId | Пользователь-участник. |
Role | ParticipantRole (enum строкой) | Да | DealMemberEntity.Role | Executor/Responsible/CoAgent/Assistant/Lawyer/InvestorPrimary/InvestorSecondary/SellManager/PinManager. |
DealDocumentSummaryModel
Проекция DealDocumentEntity (дом — deal-document.md); только дельта.
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
DocumentId | Guid | Да | Id | Идентификатор документа. |
Type | DealDocumentType (enum строкой) | Да | Type | Тип документа (паспорт/договор брони/ДДУ/ДКП/…). |
Status | DealDocumentStatus (enum строкой) | Да | Status | Requested/Uploaded/UnderReview/Accepted/Rejected (US-INV-14). |
Title | string | Да | Title | Отображаемое название. |
FileId | Guid? | Нет | FileId | Файл в storage/CDN; null — только запрошен. |
UploadedAt | DateTimeOffset? | Нет | UploadedAt | Момент загрузки. |
ReviewedAt | DateTimeOffset? | Нет | ReviewedAt | Момент решения проверки. |
RejectReason | string? | Нет | RejectReason | Причина отклонения (при Status = Rejected). |
DueAt | DateTimeOffset? | Нет | DueAt | Срок предоставления (индикация «просрочено»). |
Алгоритм
- Контекст и доступ. Читает менеджер застройщика (
User) с permissioncrm.deals.view; tenant —TenantId = DeveloperCompanyId. Полный номер эскроу-счёта видит толькоSellManager(роль/crm.deals.decide), остальным — маска. Безопасный GET, идемпотентность не требуется. - Проверка входа и предусловий.
DealId != Guid.Empty,TenantId != Guid.Empty— иначеVALIDATION_FAILED. Существование, tenant и состояние проверяются при чтении (шаг 3); чтение разрешено во всех статусах, включая терминальныеCompleted/Cancelled. - Что читаем.
- Сделку в границах
TenantId = DeveloperCompanyId; если её нет, tenant не совпал или она soft-deleted →CRM_DEAL_NOT_FOUND(чужой tenant не раскрываем); - активных участников сделки (
RemovedAt is null); - карточку клиента (по
ClientId) — только display-снимок ФИО/телефона покупателя (карточка живёт в tenant агентства, читаются лишь контактные поля); - документы сделки (по
deal_id,type), не удалённые — перечень с их статусами проверки (US-DEV-05); - display-данные объекта/проекта/банка не загружаются (владелец —
chessboard/catalog, отдельные вкладки); в ответе — только id + снимокPrice.
- Сделку в границах
- Бизнес-правила и маппинг. Переходов состояний нет (read-only). Маппинг поле-в-поле
deal→DealCardModel(таблица выше);Money→MoneyView(display-код валюты — из справочника поCurrencyId); клиент →ClientName/ClientPhoneNumber; участники →DealMemberModel[]; документы →DealDocumentSummaryModel[]. Маскирование эскроу:SellManagerвидит полныйEscrowAccountNumber, остальным — маска****NNNN(последние 4 цифры; US-DEV-06, deal.md). Enum-поля сериализуются строкой. - Результат.
DealCardModel→ HTTP200+ тело; ошибки →ProblemDetails(404/400). Актуальность: агрегат, участники и документы согласованы на момент запроса; ФИО клиента — eventual-реплика изidentity. Статусы документов и этапов меняют отдельные команды застройщика (публикуют document-status-changed и deal-stage-changed); эта операция лишь отражает их результат.
Диаграмма
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | DealId/TenantId пустой | «Некорректный идентификатор сделки» | no retry |
CRM_DEAL_NOT_FOUND | 404 | сделки нет, TenantId не совпал или soft-deleted | «Сделка не найдена» | no retry |
Совместимость и наблюдаемость
- Новые optional поля
DealCardModel— additive (без breaking change); удаление/переименование/смена типа — breaking (migration + rollout). Enum-поля (DealStatus/ContractType/PaymentMethod/EscrowStatus/DealDocumentStatus/…) сериализуются строкой; клиент обязан толерантно обрабатывать неизвестное значение. - Logs:
deal-card viewed(DealId,TenantId,ActorUserId;EscrowAccountNumberне пишется, телефон — маской); 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
- Given сделка своего
TenantId, When менеджер застройщика запрашивает её поdealId, Then200сDealCardModel(снимок объекта/цены, ФИО/телефон покупателя, участники, документы со статусами). - Given актор с ролью
SellManager/crm.deals.decide, When GET, ThenEscrowAccountNumberвозвращается полностью; иначе (обычный менеджер) — маска****NNNN(последние 4 цифры, US-DEV-06). - Given сделка чужого
TenantId, несуществующая или soft-deleted, When GET, Then404 CRM_DEAL_NOT_FOUND(чужой tenant неотличим от отсутствующего). - Given сделка в терминальном статусе (
Completed/Cancelled), When GET, Then200— чтение разрешено во всех статусах. - Given ФИО клиента ещё не реплицировано из
identity, When GET, Then карточка отдаётся200, а контактные поля берутся из последнего снимка (ClientName/ClientPhoneNumber— eventual-реплика). - Given пустой
DealId/TenantId, When GET, Then400 VALIDATION_FAILED. - Given ответ отдан, When пишутся логи, Then
EscrowAccountNumberв логи не попадает, телефон — только маской.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (2): GET /api/developer/crm/purchase-program — Программа покупки застройщика, GET /api/pin-manager/crm/deals/{dealId} — Карточка любой сделки (Менеджер ПИН)
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: deal.md (дом полей, VO
Money, маска эскроу), deal-document.md (документы и статусы), client.md (VOFullName), booking.md, pipeline-step.md. - События контекста (результат отражается в карточке): document-status-changed, deal-stage-changed, deal-completed.
- Карта областей — README контекста; прототип — prototype-mapping.md; tenancy/record-level — ADR-0052.