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 area | developer |
| Feature group | purchase-programs |
| Статус | draft |
| Документ | docs/06-services/crm/api/developer/get-purchase-program.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Менеджер застройщика; администратор платформы — ADR-0053) |
| Auth policy / Permissions | crm.purchase-programs.manage (JWT identity) |
| Scope (RBAC) | организация (застройщик) — TenantId = DeveloperCompanyId |
| Record-level | не применяется (единственный живой агрегат тенанта; фильтрация только по TenantId) |
| Tenant isolation | TenantId = DeveloperCompanyId в каждом запросе |
| Audit | read (не sensitive); PII отсутствует (конфигурация без персональных данных) |
Публичное чтение конфигурации с карточки объекта недвижимости — отдельный публичный метод; этот endpoint —
привилегированный админ-таб под crm.purchase-programs.manage.
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/developer/crm/purchase-program (singleton по тенанту — без id в маршруте) |
| Success status | 200 |
| Idempotency header | не применяется (безопасный GET) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims | Guid | Да | Required, != Guid.Empty (DeveloperCompanyId) | Tenant isolation |
ActorUserId | identity claims | Guid | Да | JWT менеджера застройщика | audit/логи |
Параметров маршрута/query нет: агрегат определяется тенантом (INV-PPR-1, unique partial
ux_purchase_programs_tenant).
Модель ответа PurchaseProgramModel
Проекция агрегата; дом полей и семантика — purchase-program.md (enum
MilestoneKind; правило расчёта графика). Ниже — только дельта проекции; все enum сериализуются строкой.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | PurchaseProgramEntity.Id | — | Идентификатор конфигурации (для upsert-команд). |
IsFullPaymentEnabled | bool | Да | IsFullPaymentEnabled | — | Доступна ли 100% оплата у застройщика. |
FullPaymentDiscountPercent | decimal | Да | FullPaymentDiscountPercent | — | Скидка при 100% оплате, % (0..100). |
IsMortgageEnabled | bool | Да | IsMortgageEnabled | — | Доступна ли ипотека. |
MortgageAnnualRatePercent | decimal | Да | MortgageAnnualRatePercent | — | Годовая ставка ипотеки для калькулятора, %. |
MortgageTermMonths | int | Да | MortgageTermMonths | — | Срок ипотеки, месяцы (1..600). |
InstallmentPresets | IReadOnlyList<InstallmentPresetModel> | Да (может быть пустым) | live InstallmentPresetEntity (шаг 3) | — | Пресеты рассрочки, SortOrder asc. |
UpdatedAt | DateTimeOffset | Да | UpdatedAt | — | Последнее изменение конфигурации. |
Version | long | Да | Version | — | Оптимистичный контроль для редактирования админ-табом. |
InstallmentPresets — коллекция дочерних сущностей единичной кардинальности («единицы строк»,
purchase-program.md); не отдельный paginated список, а именованная
проекция-массив в составе агрегата (паттерн Members/Documents в get-deal.md).
Пустой InstallmentPresets = [] — валидный ответ 200 (у застройщика ещё нет пресетов рассрочки); 404
возвращается только когда не сидирован сам агрегат программы покупки (INV-PPR-1).
InstallmentPresetModel
Проекция InstallmentPresetEntity (дом — purchase-program.md); только дельта.
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
Id | Guid | Да | Id | Идентификатор пресета (на него ссылается Deal.InstallmentPresetId). |
Name | string | Да | Name | Название («Рассрочка 12 месяцев»). |
Code | string? | Нет | Code | Display-код пресета (не доменный ключ). |
InitialPaymentPercent | decimal | Да | InitialPaymentPercent | Первоначальный взнос, % от цены (0..100). |
MonthlyFixedPayment | decimal | Да | MonthlyFixedPayment | Фиксированный ежемесячный платёж, RUB (>= 0). |
TermMonths | int | Да | TermMonths | Срок рассрочки, месяцы (1..600). |
DiscountPercent | decimal | Да | DiscountPercent | Скидка пресета от цены объекта недвижимости, % (0..100). |
MilestonePayments | IReadOnlyList<MilestonePaymentModel> | Да (может быть пустым) | typed jsonb MilestonePayments.Items | Промежуточные обязательные платежи. |
SortOrder | int | Да | SortOrder | Порядок показа опций (>= 0). |
MilestonePaymentModel
Проекция элемента typed jsonb MilestonePayment (ContractVersion = 1, дом —
purchase-program.md); 1:1.
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
Month | int | Да | Month | Месяц платежа (1..TermMonths). |
Kind | MilestoneKind (enum строкой) | Да | Kind | percent / fixed / remainder. |
Value | decimal | Да | Value | Значение по Kind (percent: %; fixed: RUB; remainder: 0). |
Алгоритм
- Контекст и доступ. Читает менеджер застройщика (
User, permissioncrm.purchase-programs.manage, иначеCRM_PURCHASE_PROGRAMS_FORBIDDEN; администратор платформы — ADR-0053); tenant —TenantId = DeveloperCompanyId. Безопасный GET, идемпотентность не требуется. - Проверка входа.
TenantId != Guid.Empty— иначеVALIDATION_FAILED. Параметров маршрута/query нет; существование агрегата — на шаге 3; конфигурация читается всегда (статусной машины нет); доступ ограничен tenant, record-level не применяется. - Что читаем.
- живой агрегат программы покупки тенанта — единственный (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 вместе с пресетом.
- живой агрегат программы покупки тенанта — единственный (INV-PPR-1, unique partial
- Бизнес-правила и маппинг. Переходов состояний нет (read-only). Маппинг поле-в-поле
program → PurchaseProgramModel;presets → InstallmentPresetModel[];MilestonePayments.Items → MilestonePaymentModel[]. EnumKindсериализуется строкой. Расчётов нет — график платежей строит отдельный метод (../get-payment-schedule.md, будущий путь). - Результат.
PurchaseProgramModel→ HTTP200+ тело; ошибки →ProblemDetails(404/403/400). Актуальность: программа и пресеты согласованы на момент запроса. Изменения конфигурации отражают команды upsert (поднимают доменный фактPurchaseProgramChanged, purchase-program.md).
Диаграмма
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | TenantId пустой | «Некорректный запрос» | no retry |
UNAUTHORIZED | 401 | нет/просрочен JWT | «Требуется авторизация» | no retry |
CRM_PURCHASE_PROGRAMS_FORBIDDEN | 403 | нет permission crm.purchase-programs.manage | «Недостаточно прав» | no retry |
CRM_PURCHASE_PROGRAM_NOT_FOUND | 404 | нет живого агрегата у тенанта (застройщик не сидирован) | «Программа покупки не найдена» | no retry |
Совместимость и наблюдаемость
- Новые optional поля
PurchaseProgramModel/InstallmentPresetModel— additive (без breaking change); удаление/переименование/смена типа поля или значения enumMilestoneKind— 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; dashboardpin-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, Then403 CRM_PURCHASE_PROGRAMS_FORBIDDEN; без JWT —401. - Given пустой
TenantId(сбой identity-контекста), When GET, Then400 VALIDATION_FAILED.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (2): GET /api/public/crm/purchase-config — Конфигурация способов оплаты (витрина), POST /api/developer/crm/purchase-program/preview-schedule — Предпросмотр графика платежей
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: purchase-program.md (дом полей
PurchaseProgramEntity/InstallmentPresetEntity, jsonbMilestonePayments, enumMilestoneKind/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.