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

GET /api/developer/crm/purchase-program — Программа покупки застройщика

Менеджер застройщика получает по этому методу единый read-only агрегат конфигурации покупки своего тенанта — для отрисовки и редактирования админ-таба «Программы покупки» ЛК застройщика. Агрегат один на застройщика (INV-PPR-1): 100% оплата (скидка), ипотека (ставка/срок), рассрочки (пресеты с milestone-платежами). Дом полей — purchase-program.md; здесь — только проекция и алгоритм её сборки. Расчёт графика платежей — отдельный метод ../get-payment-schedule.md (будущий путь по конвенции), не этот endpoint.

Назначение

Отдать менеджеру застройщика единый объект конфигурации покупки его тенанта: доступность и параметры 100% оплаты и ипотеки + список пресетов рассрочки с промежуточными платежами — для отрисовки/редактирования админ-таба «Программы покупки».

Metadata

ПолеЗначение
Сервис/контекстcrm
API areadeveloper
Feature grouppurchase-programs
Статусdraft
Документdocs/06-services/crm/api/developer/get-purchase-program.md

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

ПроверкаЗначение
Actor typeUser (Менеджер застройщика; администратор платформы — ADR-0053)
Auth policy / Permissionscrm.purchase-programs.manage (JWT identity)
Scope (RBAC)организация (застройщик) — TenantId = DeveloperCompanyId
Record-levelне применяется (единственный живой агрегат тенанта; фильтрация только по TenantId)
Tenant isolationTenantId = DeveloperCompanyId в каждом запросе
Auditread (не sensitive); PII отсутствует (конфигурация без персональных данных)
Доступ

Публичное чтение конфигурации с карточки объекта недвижимости — отдельный публичный метод; этот endpoint — привилегированный админ-таб под crm.purchase-programs.manage.

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/developer/crm/purchase-program (singleton по тенанту — без id в маршруте)
Success status200
Idempotency headerне применяется (безопасный GET)
Correlationсквозной trace через заголовок traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
TenantIdidentity claimsGuidДаRequired, != Guid.Empty (DeveloperCompanyId)Tenant isolation
ActorUserIdidentity claimsGuidДаJWT менеджера застройщикаaudit/логи

Параметров маршрута/query нет: агрегат определяется тенантом (INV-PPR-1, unique partial ux_purchase_programs_tenant).

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

Проекция агрегата; дом полей и семантика — purchase-program.md (enum MilestoneKind; правило расчёта графика). Ниже — только дельта проекции; все enum сериализуются строкой.

ПолеТипОбязательностьИсточникMaskingОписание
IdGuidДаPurchaseProgramEntity.IdИдентификатор конфигурации (для upsert-команд).
IsFullPaymentEnabledboolДаIsFullPaymentEnabledДоступна ли 100% оплата у застройщика.
FullPaymentDiscountPercentdecimalДаFullPaymentDiscountPercentСкидка при 100% оплате, % (0..100).
IsMortgageEnabledboolДаIsMortgageEnabledДоступна ли ипотека.
MortgageAnnualRatePercentdecimalДаMortgageAnnualRatePercentГодовая ставка ипотеки для калькулятора, %.
MortgageTermMonthsintДаMortgageTermMonthsСрок ипотеки, месяцы (1..600).
InstallmentPresetsIReadOnlyList<InstallmentPresetModel>Да (может быть пустым)live InstallmentPresetEntity (шаг 3)Пресеты рассрочки, SortOrder asc.
UpdatedAtDateTimeOffsetДаUpdatedAtПоследнее изменение конфигурации.
VersionlongДаVersionОптимистичный контроль для редактирования админ-табом.

InstallmentPresets — коллекция дочерних сущностей единичной кардинальности («единицы строк», purchase-program.md); не отдельный paginated список, а именованная проекция-массив в составе агрегата (паттерн Members/Documents в get-deal.md).

Важное условие

Пустой InstallmentPresets = [] — валидный ответ 200 (у застройщика ещё нет пресетов рассрочки); 404 возвращается только когда не сидирован сам агрегат программы покупки (INV-PPR-1).

InstallmentPresetModel

Проекция InstallmentPresetEntity (дом — purchase-program.md); только дельта.

ПолеТипОбязательностьИсточникОписание
IdGuidДаIdИдентификатор пресета (на него ссылается Deal.InstallmentPresetId).
NamestringДаNameНазвание («Рассрочка 12 месяцев»).
Codestring?НетCodeDisplay-код пресета (не доменный ключ).
InitialPaymentPercentdecimalДаInitialPaymentPercentПервоначальный взнос, % от цены (0..100).
MonthlyFixedPaymentdecimalДаMonthlyFixedPaymentФиксированный ежемесячный платёж, RUB (>= 0).
TermMonthsintДаTermMonthsСрок рассрочки, месяцы (1..600).
DiscountPercentdecimalДаDiscountPercentСкидка пресета от цены объекта недвижимости, % (0..100).
MilestonePaymentsIReadOnlyList<MilestonePaymentModel>Да (может быть пустым)typed jsonb MilestonePayments.ItemsПромежуточные обязательные платежи.
SortOrderintДаSortOrderПорядок показа опций (>= 0).

MilestonePaymentModel

Проекция элемента typed jsonb MilestonePayment (ContractVersion = 1, дом — purchase-program.md); 1:1.

ПолеТипОбязательностьИсточникОписание
MonthintДаMonthМесяц платежа (1..TermMonths).
KindMilestoneKind (enum строкой)ДаKindpercent / fixed / remainder.
ValuedecimalДаValueЗначение по Kind (percent: %; fixed: RUB; remainder: 0).

Алгоритм

  1. Контекст и доступ. Читает менеджер застройщика (User, permission crm.purchase-programs.manage, иначе CRM_PURCHASE_PROGRAMS_FORBIDDEN; администратор платформы — ADR-0053); tenant — TenantId = DeveloperCompanyId. Безопасный GET, идемпотентность не требуется.
  2. Проверка входа. TenantId != Guid.Empty — иначе VALIDATION_FAILED. Параметров маршрута/query нет; существование агрегата — на шаге 3; конфигурация читается всегда (статусной машины нет); доступ ограничен tenant, record-level не применяется.
  3. Что читаем.
    • живой агрегат программы покупки тенанта — единственный (INV-PPR-1, unique partial ux_purchase_programs_tenant), deleted_at IS NULL. Если его нет → CRM_PURCHASE_PROGRAM_NOT_FOUND (застройщик ещё не сидирован);
    • живые пресеты программы (SortOrder asc → lower(Name) asc) — ограниченный набор строк (без пагинации); MilestonePayments читаются из typed jsonb вместе с пресетом.
  4. Бизнес-правила и маппинг. Переходов состояний нет (read-only). Маппинг поле-в-поле program → PurchaseProgramModel; presets → InstallmentPresetModel[]; MilestonePayments.Items → MilestonePaymentModel[]. Enum Kind сериализуется строкой. Расчётов нет — график платежей строит отдельный метод (../get-payment-schedule.md, будущий путь).
  5. Результат. PurchaseProgramModel → HTTP 200 + тело; ошибки → ProblemDetails (404/403/400). Актуальность: программа и пресеты согласованы на момент запроса. Изменения конфигурации отражают команды upsert (поднимают доменный факт PurchaseProgramChanged, purchase-program.md).

Диаграмма

Ошибки и failure modes

Ошибки
КодHTTP statusУсловиеСообщениеRetry
VALIDATION_FAILED400TenantId пустой«Некорректный запрос»no retry
UNAUTHORIZED401нет/просрочен JWT«Требуется авторизация»no retry
CRM_PURCHASE_PROGRAMS_FORBIDDEN403нет permission crm.purchase-programs.manage«Недостаточно прав»no retry
CRM_PURCHASE_PROGRAM_NOT_FOUND404нет живого агрегата у тенанта (застройщик не сидирован)«Программа покупки не найдена»no retry

Совместимость и наблюдаемость

  • Новые optional поля PurchaseProgramModel/InstallmentPresetModel — additive (без breaking change); удаление/переименование/смена типа поля или значения enum MilestoneKind — breaking (migration + rollout). Enum сериализуется строкой; клиент обязан толерантно обрабатывать неизвестное значение.
  • Logs: purchase-program viewed (TenantId, ActorUserId, счётчик пресетов); metrics: crm_purchase_program_requests_total{result}, latency p95 (< 1 c, SLO чтений README); traces — сквозной trace через traceparent; dashboard pin-crm; runbook — docs/06-services/crm/runbooks/ (будущий путь).

Acceptance criteria

  • Given сидированная программа покупки тенанта, When менеджер застройщика запрашивает конфигурацию, Then 200 с PurchaseProgramModel (100%-оплата, ипотека, пресеты рассрочки в порядке SortOrder asc).
  • Given у пресета заданы milestone-платежи, When GET, Then каждый пресет несёт свой MilestonePayments[] (из typed jsonb), а Kind сериализован строкой (percent/fixed/remainder).
  • Given у застройщика нет ни одного пресета рассрочки, When GET, Then 200, InstallmentPresets = [] (не 404).
  • Given застройщик ещё не сидирован (живого агрегата нет), When GET, Then 404 CRM_PURCHASE_PROGRAM_NOT_FOUND.
  • Given актор без permission crm.purchase-programs.manage, When GET, Then 403 CRM_PURCHASE_PROGRAMS_FORBIDDEN; без JWT — 401.
  • Given пустой TenantId (сбой identity-контекста), When GET, Then 400 VALIDATION_FAILED.

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

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

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

  • Сущности: purchase-program.md (дом полей PurchaseProgramEntity/ InstallmentPresetEntity, jsonb MilestonePayments, enum MilestoneKind/PaymentMethod, INV-PPR-1, правило расчёта графика), deal.md (потребитель PaymentMethod/InstallmentPresetId).
  • Соседние методы застройщика: list-deals.md, get-deal.md; график платежей — ../get-payment-schedule.md (будущий путь по конвенции).
  • Контекст: README; прототип — prototype-mapping.md; tenancy — ADR-0052.