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

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 areadeveloper
Feature groupdeals
Статусdraft
Документdocs/06-services/crm/api/developer/get-deal.md

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

ПроверкаЗначение
Actor typeUser (Менеджер застройщика; SellManager — полный эскроу; Менеджер ПИН — platform-scope)
Auth policy / Permissionscrm.deals.view (JWT)
Scope (RBAC)организация (застройщик) — TenantId = DeveloperCompanyId
Record-levelне применяется (US-DEV-02: любая сделка своего тенанта; фильтрация только по TenantId)
Tenant isolationTenantId = DeveloperCompanyId в каждом запросе
Auditread; EscrowAccountNumber — Sensitive: наружу маска, в логи запрещён; телефон — маска в логах
Доступ

Читает менеджер застройщика в границах своего TenantId = DeveloperCompanyId — любая сделка тенанта, без record-level ограничения (US-DEV-02). Полный номер эскроу-счёта видит только SellManager (роль/ crm.deals.decide); остальным — маска ****NNNN. Сделка чужого tenant не раскрывается: 404 CRM_DEAL_NOT_FOUND, а не 403.

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/developer/crm/deals/{dealId}
Success status200
Idempotency headerне применяется (безопасный GET)
Correlationсквозной trace через заголовок traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
TenantIdidentity claimsGuidДаRequired, != Guid.EmptyTenant isolation (DeveloperCompanyId)
ActorUserIdidentity claimsGuidДаJWT менеджера застройщикаaudit/логи
ActorIsSellManageridentity claims (роль/участие)boolДавычисляется из роли SellManager/crm.deals.decideуправляет раскрытием EscrowAccountNumber (шаг 4)
DealIdrouteGuidДа!= Guid.Empty; сделка своего TenantId (шаг 2–3)

Модель ответа DealCardModel

Проекция агрегата; дом полей — deal.md (VO Money; enum DealStatus/DealContractType/ PaymentMethod/EscrowStatus/DealChatStatus; группа этапа — pipeline-step.md). Ниже — только дельта проекции; все enum сериализуются строкой.

ПолеТипОбязательностьИсточникMaskingОписание
DealIdGuidДаIdИдентификатор сделки (dealId маршрута).
TitlestringДаTitleЗаголовок карточки («Объект №…, ЖК …»).
StatusDealStatusДаStatusApplication/Booking/Sale/Completed/Cancelled (US-INV-03).
StepIdGuidДаStepIdТекущий этап канбана застройщика.
StepGroupPipelineStepGroupДаStepGroupСистемная группа этапа (денормализация).
PropertyObjectIdGuidДаPropertyObjectIdОбъект сделки (chessboard); display-данные — отдельный запрос.
ProjectIdGuidДаProjectIdПроект объекта (фильтры/статистика застройщика).
BuildingIdGuid?НетBuildingIdЗдание/литер.
PriceMoneyViewДаPrice (owned VO Money)Снимок стоимости объекта недвижимости (Amount/CurrencyId + display CurrencyCode).
PaymentMethodPaymentMethod?НетPaymentMethodСпособ оплаты (purchase-program.md); влияет на этапы.
InstallmentPresetIdGuid?НетInstallmentPresetIdПресет рассрочки (при PaymentMethod = Installment).
ContractTypeDealContractType?НетContractTypeDdu/Dkp (US-INV-08).
BankIdGuid?НетBankIdБанк сделки/эскроу (справочник chessboard); null — «Не указан».
EscrowAccountNumberstring?НетEscrowAccountNumberSellManager — полный; иначе маска ****NNNN (шаг 4)Номер эскроу-счёта (Sensitive, US-DEV-06).
EscrowStatusEscrowStatusДаEscrowStatusNone/Provided («Эскроу забронирован», US-INV-19).
BookingIdGuid?НетBookingIdТекущее бронирование сделки (booking.md).
ContractsPreparedAtDateTimeOffset?НетContractsPreparedAt«Договоры подготовлены» (US-DEV-04).
RosreestrRegisteredAtDateTimeOffset?НетRosreestrRegisteredAtРегистрация в Росреестре (ТЗ.md:407).
CompletedAtDateTimeOffset?НетCompletedAtЗавершение сделки (ТЗ.md:409).
CancelledAtDateTimeOffset?НетCancelledAtМомент отказа.
CancelReasonstring?НетCancelReasonПричина отказа (при Status = Cancelled).
TermsAcknowledgedAtDateTimeOffset?НетTermsAcknowledgedAt«Ознакомлен» инвестора (US-INV-18).
ChatStatusDealChatStatusДаChatStatusПроекция готовности чата (messenger, ADR-0055).
PrimaryInvestorUserIdGuidДаPrimaryInvestorUserIdОсновной инвестор-покупатель (допускаются соинвесторы InvestorSecondary в составе участников).
ExecutorAgentUserIdGuidДаExecutorAgentUserIdТекущий агент-исполнитель сделки (получатель комиссии).
OriginAgentUserIdGuidДаOriginAgentUserIdАгент-инициатор (историческая атрибуция, иммутабелен).
OriginAgencyCompanyIdGuidДаOriginAgencyCompanyIdАгентство агента-инициатора на момент создания (атрибуция).
ClientIdGuidДаClientIdКарточка клиента агентства.
ClientNameFullNameModelДаClientEntity.Name (снимок)ФИО покупателя (client.md).
ClientPhoneNumberstringДаClientEntity.PhoneNumber (снимок)телефон — маска в логахТелефон покупателя (E.164, PII).
MembersDealMemberModel[]Даактивные DealMemberEntity (шаг 3)Состав участников сделки.
DocumentsDealDocumentSummaryModel[]Дадокументы сделки (шаг 3)Документы и их статусы (US-DEV-05).
CreatedAtDateTimeOffsetДаCreatedAtСоздание сделки.
UpdatedAtDateTimeOffsetДаUpdatedAtПоследнее обновление (US-INV-04).

MoneyView

Read-проекция VO Money (дом — deal.md).

ПолеТипОбязательностьИсточникОписание
AmountdecimalДаMoney.AmountСумма.
CurrencyIdshortДаMoney.CurrencyIdТипизированная ссылка на справочник валют handbook.
CurrencyCodestringДаCurrencySnapshot.IsoCode справочника по CurrencyId (snapshots.md)Display-код валюты (RUB) — чтобы UI не делал отдельный lookup.

DealMemberModel

Проекция DealMemberEntity (дом — deal.md), только активные (RemovedAt is null).

ПолеТипОбязательностьИсточникОписание
UserIdGuidДаDealMemberEntity.UserIdПользователь-участник.
RoleParticipantRole (enum строкой)ДаDealMemberEntity.RoleExecutor/Responsible/CoAgent/Assistant/Lawyer/InvestorPrimary/InvestorSecondary/SellManager/PinManager.

DealDocumentSummaryModel

Проекция DealDocumentEntity (дом — deal-document.md); только дельта.

ПолеТипОбязательностьИсточникОписание
DocumentIdGuidДаIdИдентификатор документа.
TypeDealDocumentType (enum строкой)ДаTypeТип документа (паспорт/договор брони/ДДУ/ДКП/…).
StatusDealDocumentStatus (enum строкой)ДаStatusRequested/Uploaded/UnderReview/Accepted/Rejected (US-INV-14).
TitlestringДаTitleОтображаемое название.
FileIdGuid?НетFileIdФайл в storage/CDN; null — только запрошен.
UploadedAtDateTimeOffset?НетUploadedAtМомент загрузки.
ReviewedAtDateTimeOffset?НетReviewedAtМомент решения проверки.
RejectReasonstring?НетRejectReasonПричина отклонения (при Status = Rejected).
DueAtDateTimeOffset?НетDueAtСрок предоставления (индикация «просрочено»).

Алгоритм

  1. Контекст и доступ. Читает менеджер застройщика (User) с permission crm.deals.view; tenant — TenantId = DeveloperCompanyId. Полный номер эскроу-счёта видит только SellManager (роль/crm.deals.decide), остальным — маска. Безопасный GET, идемпотентность не требуется.
  2. Проверка входа и предусловий. DealId != Guid.Empty, TenantId != Guid.Empty — иначе VALIDATION_FAILED. Существование, tenant и состояние проверяются при чтении (шаг 3); чтение разрешено во всех статусах, включая терминальные Completed/Cancelled.
  3. Что читаем.
    • Сделку в границах 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.
  4. Бизнес-правила и маппинг. Переходов состояний нет (read-only). Маппинг поле-в-поле dealDealCardModel (таблица выше); MoneyMoneyView (display-код валюты — из справочника по CurrencyId); клиент → ClientName/ClientPhoneNumber; участники → DealMemberModel[]; документы → DealDocumentSummaryModel[]. Маскирование эскроу: SellManager видит полный EscrowAccountNumber, остальным — маска ****NNNN (последние 4 цифры; US-DEV-06, deal.md). Enum-поля сериализуются строкой.
  5. Результат. DealCardModel → HTTP 200 + тело; ошибки → ProblemDetails (404/400). Актуальность: агрегат, участники и документы согласованы на момент запроса; ФИО клиента — eventual-реплика из identity. Статусы документов и этапов меняют отдельные команды застройщика (публикуют document-status-changed и deal-stage-changed); эта операция лишь отражает их результат.

Диаграмма

Ошибки и failure modes

Ошибки
КодHTTP statusУсловиеСообщениеRetry
VALIDATION_FAILED400DealId/TenantId пустой«Некорректный идентификатор сделки»no retry
CRM_DEAL_NOT_FOUND404сделки нет, 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; dashboard pin-crm; runbook — docs/06-services/crm/runbooks/ (будущий путь).

Acceptance criteria

  • Given сделка своего TenantId, When менеджер застройщика запрашивает её по dealId, Then 200 с DealCardModel (снимок объекта/цены, ФИО/телефон покупателя, участники, документы со статусами).
  • Given актор с ролью SellManager/crm.deals.decide, When GET, Then EscrowAccountNumber возвращается полностью; иначе (обычный менеджер) — маска ****NNNN (последние 4 цифры, US-DEV-06).
  • Given сделка чужого TenantId, несуществующая или soft-deleted, When GET, Then 404 CRM_DEAL_NOT_FOUND (чужой tenant неотличим от отсутствующего).
  • Given сделка в терминальном статусе (Completed/Cancelled), When GET, Then 200 — чтение разрешено во всех статусах.
  • Given ФИО клиента ещё не реплицировано из identity, When GET, Then карточка отдаётся 200, а контактные поля берутся из последнего снимка (ClientName/ClientPhoneNumber — eventual-реплика).
  • Given пустой DealId/TenantId, When GET, Then 400 VALIDATION_FAILED.
  • Given ответ отдан, When пишутся логи, Then EscrowAccountNumber в логи не попадает, телефон — только маской.

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

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

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