GET /api/developer/crm/pipeline-steps — Этапы воронки застройщика
Одна канбан-доска застройщика: системные группы этапов (SEED PipelineStepGroup) + настраиваемые этапы
его компании со счётчиком сделок в колонке (US-DEV-01). Read-only проекция
pipeline-step.md (+ SEED-справочник crm.pipeline_step_groups). Доска одна:
возвращаются все живые этапы (deleted_at IS NULL), отсортированные по Group.Order, затем Order.
Разграничение — TenantId = DeveloperCompanyId (ADR-0052). Мутаций и событий не производит.
Назначение
Отдать менеджеру застройщика полную доску воронки для экрана настройки этапов и как справочник колонок при
рендере канбан-доски сделок (list-deals.md): системные группы (колонки-секции) и внутри
каждой — настраиваемые этапы застройщика (Id, группа, имя, цвет, порядок, флаг SEED-дефолта, число сделок).
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | developer |
| Feature group | pipeline |
| Статус | draft |
| Документ | docs/06-services/crm/api/developer/list-steps.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Менеджер застройщика); Менеджер ПИН — platform-scope |
| Auth policy / Permissions | crm.pipeline.view (JWT identity) |
| Scope (RBAC) | организация (застройщик) — TenantId = DeveloperCompanyId |
| Record-level | не применяется: доска общая для всех участников CRM своего застройщика |
| Tenant isolation | TenantId в каждом запросе (= DeveloperCompanyId) |
| Audit | read (не sensitive): PII отсутствует (pipeline-step.md) |
Record-level не применяется: доска канбана общая для всех участников CRM своего застройщика (не
привязана к конкретному менеджеру). Разграничение — только по tenant (TenantId = DeveloperCompanyId,
ADR-0052).
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/developer/crm/pipeline-steps |
| Success status | 200 |
| Idempotency header | не применяется (read-only, безопасный метод) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
Иных параметров, кроме TenantId, нет: возвращается вся доска застройщика целиком (не список с
фильтрами/пагинацией). Фильтр колонки по сделкам — задача list-deals.md
(StepId/StepGroup), а не этого метода.
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims | Guid | Да | Required, != Guid.Empty (DeveloperCompanyId) | Tenant isolation |
Модель ответа PipelineBoardModel
Единый container-model доски: доска одна и ограниченная — десятки строк на застройщика (pipeline-step.md, «Партиционирование/retention»), пагинация не нужна. Две именованные коллекции — системные группы (колонки) и настраиваемые этапы.
Ответ всегда 200 с полной доской, а не 404: Groups содержит все 7 SEED-групп даже без единого живого
этапа застройщика, Steps в этом случае — [].
PipelineBoardModel (container)
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
Groups | IReadOnlyList<PipelineGroupModel> | Да | SEED crm.pipeline_step_groups | Все 7 системных групп, отсортированы по Order. Пустой список невозможен (SEED). |
Steps | IReadOnlyList<PipelineStepModel> | Да | crm.pipeline_steps (живые) | Настраиваемые этапы, глобальный порядок Group.Order → Order; [] возможен только теоретически (SEED-дефолты создаются при онбординге). |
PipelineGroupModel (SEED-группа, «один дом» — pipeline-step.md, SEED-таблица)
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Code | PipelineStepGroup | Да | pipeline_step_groups.Code (enum строкой) | — | Application/Booking/Contract/Payment/Registration/Completed/Cancelled. |
Name | string | Да | pipeline_step_groups.Name | — | Русское имя колонки-секции («Заявка», «Бронирование», …). |
Order | int | Да | pipeline_step_groups.Order | — | Порядок групп на доске (10..70). |
IsTerminal | bool | Да | pipeline_step_groups.IsTerminal | — | Completed/Cancelled = true (guard переходов сделок). |
PipelineStepModel (этап, «один дом» — pipeline-step.md; здесь только дельта)
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | PipelineStepEntity.Id | — | Идентификатор этапа (ключ для Deal.StepId, move/archive). |
Group | PipelineStepGroup | Да | PipelineStepEntity.Group (enum строкой) | — | Системная группа этапа (иммутабельна, INV-STEP-1). |
Name | string | Да | PipelineStepEntity.Name | — | Название этапа. |
Color | string | Да | PipelineStepEntity.Color (#RRGGBB) | — | Цвет колонки/ярлыка. |
Order | int | Да | PipelineStepEntity.Order | — | Позиция внутри группы (вторичный ключ сортировки). |
IsSeedDefault | bool | Да | PipelineStepEntity.IsSeedDefault | — | Создан дефолтным SEED-набором застройщика (UI-подсказка). |
DealsCount | int | Да | computed: count(crm.deals) где step_id = Id AND deleted_at IS NULL | — | Число живых сделок в колонке (счётчик доски). 0 — пустой этап. |
Все enum — строкой; дом значений групп и полей этапа — pipeline-step.md. Поля SEED-группы не переописываются здесь как контракт — это проекция read-only справочника.
Алгоритм
- Контекст и доступ. Читает менеджер застройщика (
User; менеджер ПИН — platform-scope) с permissioncrm.pipeline.view(иначеCRM_PIPELINE_FORBIDDEN); tenant —TenantId = DeveloperCompanyId. БезопасныйGET, идемпотентность не требуется. Параметров, кроме tenant, нет. - Проверка входа.
TenantId != Guid.Empty— иначеVALIDATION_FAILED(в норме подставляется identity; пустой — инфраструктурный сбой). Id-фильтров нет; доступ ограничен tenant, record-level не применяется. - Что читаем.
- системные группы — read-only SEED-справочник (7 строк, tenant-независим,
Order asc); может кэшироваться в памяти (набор меняется только миграцией); - все живые этапы застройщика (
deleted_at IS NULL,Order asc) по индексу(tenant_id, group);DealsCount— коррелированный счётчик живых сделок этапа (step_id = этап AND deleted_at IS NULL) внутри той же выборки, без второго запроса; - оба чтения — в одном снапшоте (консистентность этапов и счётчиков сделок).
- системные группы — read-only SEED-справочник (7 строк, tenant-независим,
- Бизнес-правила и маппинг. Переходов состояний нет. Глобальная сортировка доски: этапы группируются по
системным колонкам по возрастанию
Group.Order, затем по локальномуOrder(tiebreakId). Группы без живых этапов всё равно присутствуют вGroups(доска показывает все системные колонки). Маппинг поле-в-поле — таблицы «Модель ответа»; enum сериализуются строкой. - Результат.
PipelineBoardModel { Groups, Steps }→ HTTP200. Всегда200с полной доской (не 404): даже при отсутствии настраиваемых этаповSteps = [],Groups— 7 SEED-групп. Этапы — внутренний справочник, интеграционных событий не публикуют; внешние контексты видят группу этапа только в deal-stage-changed.md.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Integration events | none (read-only; этапы — внутренний справочник) |
| External calls | none (данные — своя схема crm) |
| Cache / projections / search | SEED-группы могут кэшироваться в памяти (immutable вне миграций); этапы — прямой запрос PostgreSQL |
| Notifications | none |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | TenantId = Guid.Empty (сбой identity-контекста) | «Проверьте параметры запроса» | no retry |
UNAUTHORIZED | 401 | нет/просрочен JWT | «Требуется авторизация» | no retry |
CRM_PIPELINE_FORBIDDEN | 403 | нет permission crm.pipeline.view | «Недостаточно прав» | no retry |
Совместимость и наблюдаемость
- Новые optional поля ответа — additive (без breaking); удаление/переименование/смена типа поля или значения
enum
PipelineStepGroup— breaking (migration + rollout, набор групп меняется только SEED-миграцией, INV-STEP-5). Unknown enum на выходе невозможен (значения фиксированы SEED). - Logs:
developer pipeline board listed(TenantId, число этапов); metrics:crm_pipeline_board_list_total{result}, latency p95 (< 1 c, SLO списков); traces — сквозной trace черезtraceparent; dashboardpin-crm; runbook —docs/06-services/crm/runbooks/(будущий путь).
Acceptance criteria
- Given застройщик, When менеджер запрашивает доску, Then
200сPipelineBoardModel: все 7 системных групп (Groups, порядокOrder) и живые настраиваемые этапы (Steps, глобальный порядокGroup.Order → Order → Id) сDealsCountпо колонкам. - Given у застройщика нет ни одного настраиваемого этапа, When GET, Then
200,Steps = [],Groups— все 7 SEED-групп (доска показывает системные колонки всегда). - Given в колонке нет живых сделок, When GET, Then её
DealsCount = 0; группы без живых этапов всё равно присутствуют вGroups. - Given
TenantIdпустой (сбой identity-контекста), When GET, Then400 VALIDATION_FAILED. - Given актор без
crm.pipeline.view, When GET, Then403 CRM_PIPELINE_FORBIDDEN; без JWT —401.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (1): GET /api/developer/crm/statistics — Статистика ЛК застройщика
- Use Cases (1): UC-CRM-003. Движение сделки по канбан-доске
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: pipeline-step.md (дом полей этапа и SEED-групп,
PipelineStepGroup, инварианты, индексы), deal.md (StepId/StepGroup— источникDealsCount). - Смежные операции: list-deals.md (сделки колонок по
StepId/StepGroup); событие жизненного цикла: deal-stage-changed.md. - Контекст: README; tenancy — ADR-0052.