POST /api/developer/crm/deals/{dealId}/rosreestr — Подтвердить регистрацию Росреестра
Менеджер застройщика подтверждает регистрацию сделки в Росреестре — предпоследний шаг жизненного цикла сделки
перед завершением. Операция ставит RosreestrRegisteredAt, продвигает сделку на системный этап группы
Registration канбан-доски и опционально прикрепляет документ о регистрации (RosreestrDocument, дом —
deal-document.md). Порядок строгий (INV-DEAL-7): договоры подготовлены →
(для Ddu — эскроу Provided) → Росреестр → завершение; нарушение → CRM_DEAL_STAGE_ORDER_VIOLATION.
Дом полей сделки — deal.md.
Назначение
По кнопке «Подтвердить регистрацию» карточки сделки менеджер застройщика фиксирует регистрацию в Росреестре:
проставляет дату, переводит карточку в системный этап группы Registration, записывает событие таймлайна
RosreestrRegistered. Вызывает менеджер застройщика; на выходе — свежая карточка сделки. Публикуется
deal-stage-changed для чата/уведомлений участников.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | developer (JWT менеджера застройщика) |
| Feature group | deals |
| Статус | draft |
| Документ | docs/06-services/crm/api/developer/confirm-rosreestr.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Менеджер застройщика); Operator (Менеджер ПИН — platform-scope, арбитраж US-MGR) |
| Auth policy / Permissions | crm.deals.decide (решение застройщика по стадии сделки) |
| Scope (RBAC) | организация (застройщик) — TenantId = DeveloperCompanyId |
| Record-level | застройщик своего TenantId; Менеджер ПИН — platform-scope |
| Tenant isolation | TenantId = DeveloperCompanyId из claims; сделка читается/пишется только в этом tenant (ADR-0052) |
| Audit | update-lifecycle (UpdatedByUserId = actor.UserId, стамп RosreestrRegisteredAt); EscrowAccountNumber/PII в логи не пишутся |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | POST |
| Route | /api/developer/crm/deals/{dealId}/rosreestr |
| Success status | 200 (подтверждено; повтор при уже проставленном RosreestrRegisteredAt — тот же 200 с текущей карточкой, идемпотентно) |
| Idempotency header | не применяется (естественная идемпотентность: RosreestrRegisteredAt != null → no-op, шаг 4) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims | Guid | Да | Required; = DeveloperCompanyId актора | Tenant isolation |
DealId | route | Guid | Да | != Guid.Empty; существующая сделка в tenant (шаг 2) | — |
RegisteredAt | body | DateTimeOffset? | Нет | если задан — <= now (дата из выписки, не в будущем) и >= ContractsPreparedAt; null → серверный now | — |
Document | body | RosreestrDocumentInput? | Нет | если задан — валидируется как модель ниже; null → документ не прикрепляется | — |
ActorUserId | identity claims | Guid | Да | JWT менеджера застройщика; пишется в UpdatedByUserId/UploadedByUserId | — |
RosreestrDocumentInput
Единственная вложенная named-модель тела (container); при её наличии создаётся DealDocumentEntity типа
RosreestrDocument (загружает застройщик, проверяющего нет — deal-document.md).
| Поле | Тип | Обязательность | Валидация / Ограничения | Описание |
|---|---|---|---|---|
FileId | Guid | Да | != Guid.Empty; ссылка на storage/CDN (tenantId + fileId) | Загруженный файл выписки/зарегистрированного договора. |
FileName | string | Да | 1..512 | Оригинальное имя файла. |
ContentType | string | Да | application/pdf / image/jpeg / image/png (INV-DOC-3) | MIME-тип. |
SizeBytes | long | Да | > 0 and <= 52_428_800 (50 МБ) | Размер файла. |
Title | string? | Нет | 0..256; default «Документ о регистрации Росреестра» | Отображаемое название. |
Модель ответа DealCardModel
Дом модели и её подмоделей — get-deal.md («Модель ответа DealCardModel»);
возвращается свежая карточка сделки после мутации (та же проекция, что читает GET карточки). Дельты полей
нет — застройщик получает обновлённое состояние без второго запроса: StepId/StepGroup — новый системный
этап группы Registration, Documents — пересчитанная сводка (учтён прикреплённый RosreestrDocument),
UpdatedAt — момент операции. RosreestrRegisteredAt наружу несёт событие таймлайна RosreestrRegistered
(вкладка «Ход сделки», отдельный метод ленты — дом deal-timeline-event.md).
Алгоритм
-
Контекст и доступ. Выполняет менеджер застройщика (
User, permissioncrm.deals.decide, иначеCRM_DEALS_FORBIDDEN) либо менеджер ПИН (platform-scope, арбитраж); tenant —TenantId = DeveloperCompanyId. Естественная идемпотентность: еслиRosreestrRegisteredAt != null— no-op (шаг 4), отдельного ключа нет. -
Проверка входа и предусловий.
DealId != Guid.Empty;RegisteredAt(если задан)<= nowи>= ContractsPreparedAt;Document(если задан) —FileId != Guid.Empty,FileName 1..512,ContentTypeиз белого списка (application/pdf/image/jpeg/image/png, INV-DOC-3),SizeBytes ∈ 1..52_428_800— иначеVALIDATION_FAILED;- сделка существует в tenant застройщика — иначе
CRM_DEAL_NOT_FOUND(чужой tenant не раскрывается); - состояние:
Status == Sale(Росреестр — фаза продажи после брони); терминальныеCompleted/Cancelled→CRM_DEAL_ALREADY_TERMINAL; иной статус → нарушение порядка (шаг 4,CRM_DEAL_STAGE_ORDER_VIOLATION).
-
Что читаем. Сделку — с защитой от гонки с параллельным
Complete/MoveToStep. Целевой этап продвижения — системный этап группыRegistrationзастройщика (pipeline-step.md); если его нет →CRM_DEAL_STEP_FORBIDDEN. Прежниеdeal.StepId/deal.StepGroupфиксируются для payload перехода. -
Бизнес-правила и переходы (INV-DEAL-7).
- идемпотентность: если
RosreestrRegisteredAt != null— мутация/событие не выполняются, возвращается текущая карточка (шаг 8,200); - guard порядка (INV-DEAL-7):
ContractsPreparedAt != nullи (ContractType != DduилиEscrowStatus == Provided) — иначеCRM_DEAL_STAGE_ORDER_VIOLATION(для ДДУ эскроу обязателен); - маппинг:
RosreestrRegisteredAt = RegisteredAt ?? now,StepId = целевой этап,StepGroup = Registration(денормализация),UpdatedByUserId = ActorUserId,UpdatedAt = now.StatusостаётсяSale(вCompletedпереводит отдельный методComplete).
Фиксируется доменный факт
DealStageChanged(From: Sale, To: Sale; группа →Registration); подписчик материализует ленту (INV-TLE-2):RosreestrRegistered(TimelineEmptyPayload,Status = Done,OccurredAt = registeredAt,ActorRole = SellManager) иStageChanged(TimelineStageChangedPayload) — дом deal-timeline-event.md. При заданномDocumentсоздаётсяDealDocumentтипаRosreestrDocumentсо статусомUploaded(INV-DOC-5: сделка нетерминальна); фиксируется фактDocumentUploaded→ таймлайнDocumentUploaded. - идемпотентность: если
-
Что меняется. Сделка получает
RosreestrRegisteredAtи продвигается в системный этап группыRegistration; при заданном документе появляется записьRosreestrDocument; в ленту добавляются строкиRosreestrRegistered(+StageChanged, +DocumentUploaded). Конкурентный доступ — по версии строки: параллельныйComplete/MoveToStep→ ретрай с перечиткой. -
События. Интеграционное
DealStageChangedEvent→ топикpin.crm.deal-stage-changed.v1(ContractVersion = 1); payload:FromStepId,ToStepId,FromStepGroup,ToStepGroup = Registration,FromStatus = Sale,ToStatus = Sale,ChangedByUserId = ActorUserId,ChangedAt = now(дом — deal-stage-changed.md). При заданномDocumentдополнительно —DocumentStatusChangedEvent→ топикpin.crm.document-status-changed.v1(ToStatus = Uploaded, document-status-changed.md). Потребители:notifications(участникам «сделка на регистрации», инвестору — статус документа),messenger(карточка этапа в чате). -
Согласованность. Обновление сделки, опциональный документ, строки таймлайна и события фиксируются в одном согласованном изменении (атомарно).
-
Результат. Свежая карточка
DealCardModel(та же проекция, что get-deal.md) → HTTP200. Идемпотентный повтор (RosreestrRegisteredAtуже стоял) возвращает текущую карточку без мутаций/событий.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Integration events | DealStageChangedEvent → pin.crm.deal-stage-changed.v1 (переход в группу Registration); при Document — DocumentStatusChangedEvent → pin.crm.document-status-changed.v1 (ToStatus = Uploaded); оба через outbox |
| External calls | none (бинарь файла не читается — сохраняются метаданные и FileId storage/CDN) |
| Cache / projections / search | лента таймлайна получает RosreestrRegistered (+StageChanged, +DocumentUploaded); денормализованный Deal.StepGroup обновлён; проекция сделки (DealSearchDocument) обновляется по StepGroup (Title/ФИО прежние) |
| Notifications | опосредованно: notifications — консюмер deal-stage-changed (участникам «сделка на регистрации») и document-status-changed (инвестору US-INV-14.3); messenger рисует карточку этапа в чате сделки |
Ошибки и failure modes
Два 409 нетривиальны: CRM_DEAL_STAGE_ORDER_VIOLATION — нарушен порядок INV-DEAL-7 (договоры не подготовлены,
либо для Ddu эскроу не Provided, либо сделка не в статусе Sale); CRM_DEAL_STEP_FORBIDDEN — у застройщика не
настроен системный этап группы Registration (проблема конфигурации воронки, а не действия актора).
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | DealId = Guid.Empty, RegisteredAt в будущем, некорректные метаданные Document | «Проверьте корректность запроса» | no retry |
UNAUTHORIZED | 401 | нет/просрочен JWT | «Требуется авторизация» | no retry |
CRM_DEALS_FORBIDDEN | 403 | нет permission crm.deals.decide | «Недостаточно прав» | no retry |
CRM_DEAL_NOT_FOUND | 404 | сделки нет в tenant застройщика (или soft-deleted) | «Сделка не найдена» | no retry |
CRM_DEAL_ALREADY_TERMINAL | 409 | Status = Completed/Cancelled | «Сделка завершена — действие недоступно» | no retry |
CRM_DEAL_STAGE_ORDER_VIOLATION | 409 | INV-DEAL-7: договоры не подготовлены / для Ddu эскроу не Provided / статус не Sale | «Нельзя подтвердить регистрацию: не выполнены предыдущие шаги» | no retry |
CRM_DEAL_STEP_FORBIDDEN | 409 | у застройщика нет системного этапа группы Registration | «Этап регистрации не настроен» | no retry (конфиг воронки) |
Повтор при уже проставленном RosreestrRegisteredAt — не ошибка: 200 с текущей карточкой, без мутаций и без
повторной публикации событий (естественная идемпотентность по полю состояния, отдельного Idempotency-Key нет).
Предусловия и постусловия
Предусловия (истинны до сценария):
- актор — менеджер застройщика сделки (или Менеджер ПИН, арбитраж) с permission
crm.deals.decide; - сделка существует в
TenantId = DeveloperCompanyIdактора, нетерминальна и в статусеSale; - порядок стадий соблюдён (INV-DEAL-7): договоры подготовлены (
ContractsPreparedAt != null) и дляDdu— эскроуProvided; - у застройщика настроен системный этап группы
Registration.
Постусловия (гарантированы после успеха):
- проставлен
RosreestrRegisteredAt, сделка продвинута в системный этап группыRegistration(StepId/StepGroupденормализованы),StatusостаётсяSale(вCompletedпереводитComplete); - при заданном
DocumentсозданDealDocumentтипаRosreestrDocumentв статусеUploaded(INV-DOC-5); - записаны строки ленты
RosreestrRegistered(+StageChanged, при документе +DocumentUploaded); в outbox —deal-stage-changed.v1(+document-status-changed.v1при документе); - операция идемпотентна: если
RosreestrRegisteredAtуже стоял — мутаций и событий нет, возвращается текущая карточка.
Интеграционная последовательность (после коммита)
События обрабатываются асинхронно; актор в ответе 200 видит сделку в группе Registration сразу, эффекты
соседей доходят с задержкой (окно eventual-consistency — секунды):
DealStageChangedEvent → pin.crm.deal-stage-changed.v1(ToStepGroup = Registration):notifications— «сделка на регистрации» участникам;messenger— карточка этапа в чате сделки.- При заданном
Document—DocumentStatusChangedEvent → pin.crm.document-status-changed.v1(ToStatus = Uploaded):notifications— статус документа инвестору (US-INV-14.3).
Далее по цепочке следует завершение сделки (complete-deal.md) — оно требует именно
RosreestrRegisteredAt != null, установленный этой операцией (кросс-операционная предпосылка внутри контекста).
Что предоставляет: оба события — additive-only; unknown enum у консюмера трактуется как warning + «как есть».
Acceptance Criteria
- Happy path (ДДУ с эскроу). Дано: сделка
Sale, договоры подготовлены, эскроуProvided. Когда: менеджер подтверждает регистрацию. Тогда:200,RosreestrRegisteredAtпроставлен, группаRegistration, опубликованоdeal-stage-changed.v1. - Happy path (ДКП). Дано: сделка
SaleсContractType = Dkp, договоры подготовлены (эскроу не требуется). Когда: подтверждение. Тогда:200, продвижение вRegistration. - ДДУ без эскроу. Дано:
ContractType = Ddu,EscrowStatus != Provided. Когда: подтверждение. Тогда:409 CRM_DEAL_STAGE_ORDER_VIOLATION(INV-DEAL-7). - Договоры не подготовлены. Дано:
ContractsPreparedAt == null. Когда: подтверждение. Тогда:409 CRM_DEAL_STAGE_ORDER_VIOLATION. - Идемпотентный повтор. Дано:
RosreestrRegisteredAtуже стоял. Когда: повтор. Тогда:200с текущей карточкой, без мутаций и событий. - Терминальная сделка. Дано:
Status ∈ {Completed, Cancelled}. Когда: подтверждение. Тогда:409 CRM_DEAL_ALREADY_TERMINAL. - Нет этапа регистрации. Дано: у застройщика нет системного этапа группы
Registration. Когда: подтверждение. Тогда:409 CRM_DEAL_STEP_FORBIDDEN(настроить воронку). - С документом. Дано: задан валидный
Document. Когда: подтверждение. Тогда: созданRosreestrDocument(Uploaded) и дополнительно опубликованоdocument-status-changed.v1. - Дата в будущем. Дано:
RegisteredAt > now. Когда: подтверждение. Тогда:400 VALIDATION_FAILED.
Совместимость и наблюдаемость
RegisteredAt/Document— additive optional (при отсутствии — серверныйnowи без документа); удаление/ переименование полей или сменаSuccess status— breaking (migration + rollout). Контракты событий — additive-only (deal-stage-changed.md, document-status-changed.md); unknown enum у консюмера — warning + как есть.- Logs:
deal rosreestr confirmed(DealId,TenantId,ActorUserId,HasDocument; эскроу/ФИО — не пишутся); metrics:crm_deal_stage_confirm_total{stage="rosreestr",result}, latency p95 (< 500 мс, SLO команд); traces — OTel, correlation черезtraceparent; dashboardpin-crm; runbook —docs/06-services/crm/runbooks/(будущий путь).
Обратные ссылки
Автоссылки: где используется этот документ.
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: deal.md (дом полей,
ConfirmRosreestr, INV-DEAL-7,DealStatus/EscrowStatus/DealContractType), deal-timeline-event.md (RosreestrRegistered/StageChanged/DocumentUploaded), deal-document.md (RosreestrDocument, INV-DOC-3/5), pipeline-step.md (PipelineStepGroup.Registration). - События: deal-stage-changed.md, document-status-changed.md; модель ответа — get-deal.md; смежные стадии — confirm-booking-documents.md.
- Контекст: README; tenancy/record-level — ADR-0052.