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

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

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

ПроверкаЗначение
Actor typeUser (Менеджер застройщика); Менеджер ПИН — platform-scope
Auth policy / Permissionscrm.pipeline.view (JWT identity)
Scope (RBAC)организация (застройщик) — TenantId = DeveloperCompanyId
Record-levelне применяется: доска общая для всех участников CRM своего застройщика
Tenant isolationTenantId в каждом запросе (= DeveloperCompanyId)
Auditread (не sensitive): PII отсутствует (pipeline-step.md)
Доступ

Record-level не применяется: доска канбана общая для всех участников CRM своего застройщика (не привязана к конкретному менеджеру). Разграничение — только по tenant (TenantId = DeveloperCompanyId, ADR-0052).

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/developer/crm/pipeline-steps
Success status200
Idempotency headerне применяется (read-only, безопасный метод)
Correlationсквозной trace через заголовок traceparent

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

Важно

Иных параметров, кроме TenantId, нет: возвращается вся доска застройщика целиком (не список с фильтрами/пагинацией). Фильтр колонки по сделкам — задача list-deals.md (StepId/StepGroup), а не этого метода.

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

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

Единый container-model доски: доска одна и ограниченная — десятки строк на застройщика (pipeline-step.md, «Партиционирование/retention»), пагинация не нужна. Две именованные коллекции — системные группы (колонки) и настраиваемые этапы.

Важно

Ответ всегда 200 с полной доской, а не 404: Groups содержит все 7 SEED-групп даже без единого живого этапа застройщика, Steps в этом случае — [].

PipelineBoardModel (container)

ПолеТипОбязательностьИсточникОписание
GroupsIReadOnlyList<PipelineGroupModel>ДаSEED crm.pipeline_step_groupsВсе 7 системных групп, отсортированы по Order. Пустой список невозможен (SEED).
StepsIReadOnlyList<PipelineStepModel>Даcrm.pipeline_steps (живые)Настраиваемые этапы, глобальный порядок Group.Order → Order; [] возможен только теоретически (SEED-дефолты создаются при онбординге).

PipelineGroupModel (SEED-группа, «один дом» — pipeline-step.md, SEED-таблица)

ПолеТипОбязательностьИсточникMaskingОписание
CodePipelineStepGroupДаpipeline_step_groups.Code (enum строкой)Application/Booking/Contract/Payment/Registration/Completed/Cancelled.
NamestringДаpipeline_step_groups.NameРусское имя колонки-секции («Заявка», «Бронирование», …).
OrderintДаpipeline_step_groups.OrderПорядок групп на доске (10..70).
IsTerminalboolДаpipeline_step_groups.IsTerminalCompleted/Cancelled = true (guard переходов сделок).

PipelineStepModel (этап, «один дом» — pipeline-step.md; здесь только дельта)

ПолеТипОбязательностьИсточникMaskingОписание
IdGuidДаPipelineStepEntity.IdИдентификатор этапа (ключ для Deal.StepId, move/archive).
GroupPipelineStepGroupДаPipelineStepEntity.Group (enum строкой)Системная группа этапа (иммутабельна, INV-STEP-1).
NamestringДаPipelineStepEntity.NameНазвание этапа.
ColorstringДаPipelineStepEntity.Color (#RRGGBB)Цвет колонки/ярлыка.
OrderintДаPipelineStepEntity.OrderПозиция внутри группы (вторичный ключ сортировки).
IsSeedDefaultboolДаPipelineStepEntity.IsSeedDefaultСоздан дефолтным SEED-набором застройщика (UI-подсказка).
DealsCountintДаcomputed: count(crm.deals) где step_id = Id AND deleted_at IS NULLЧисло живых сделок в колонке (счётчик доски). 0 — пустой этап.

Все enum — строкой; дом значений групп и полей этапа — pipeline-step.md. Поля SEED-группы не переописываются здесь как контракт — это проекция read-only справочника.

Алгоритм

  1. Контекст и доступ. Читает менеджер застройщика (User; менеджер ПИН — platform-scope) с permission crm.pipeline.view (иначе CRM_PIPELINE_FORBIDDEN); tenant — TenantId = DeveloperCompanyId. Безопасный GET, идемпотентность не требуется. Параметров, кроме tenant, нет.
  2. Проверка входа. TenantId != Guid.Empty — иначе VALIDATION_FAILED (в норме подставляется identity; пустой — инфраструктурный сбой). Id-фильтров нет; доступ ограничен tenant, record-level не применяется.
  3. Что читаем.
    • системные группы — read-only SEED-справочник (7 строк, tenant-независим, Order asc); может кэшироваться в памяти (набор меняется только миграцией);
    • все живые этапы застройщика (deleted_at IS NULL, Order asc) по индексу (tenant_id, group); DealsCount — коррелированный счётчик живых сделок этапа (step_id = этап AND deleted_at IS NULL) внутри той же выборки, без второго запроса;
    • оба чтения — в одном снапшоте (консистентность этапов и счётчиков сделок).
  4. Бизнес-правила и маппинг. Переходов состояний нет. Глобальная сортировка доски: этапы группируются по системным колонкам по возрастанию Group.Order, затем по локальному Order (tiebreak Id). Группы без живых этапов всё равно присутствуют в Groups (доска показывает все системные колонки). Маппинг поле-в-поле — таблицы «Модель ответа»; enum сериализуются строкой.
  5. Результат. PipelineBoardModel { Groups, Steps } → HTTP 200. Всегда 200 с полной доской (не 404): даже при отсутствии настраиваемых этапов Steps = [], Groups — 7 SEED-групп. Этапы — внутренний справочник, интеграционных событий не публикуют; внешние контексты видят группу этапа только в deal-stage-changed.md.

Диаграмма

Побочные эффекты

ТипДетали
Integration eventsnone (read-only; этапы — внутренний справочник)
External callsnone (данные — своя схема crm)
Cache / projections / searchSEED-группы могут кэшироваться в памяти (immutable вне миграций); этапы — прямой запрос PostgreSQL
Notificationsnone

Ошибки и failure modes

Ошибки
КодHTTP statusУсловиеСообщениеRetry
VALIDATION_FAILED400TenantId = Guid.Empty (сбой identity-контекста)«Проверьте параметры запроса»no retry
UNAUTHORIZED401нет/просрочен JWT«Требуется авторизация»no retry
CRM_PIPELINE_FORBIDDEN403нет 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; dashboard pin-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, Then 400 VALIDATION_FAILED.
  • Given актор без crm.pipeline.view, When GET, Then 403 CRM_PIPELINE_FORBIDDEN; без JWT — 401.

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

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

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

  • Сущности: pipeline-step.md (дом полей этапа и SEED-групп, PipelineStepGroup, инварианты, индексы), deal.md (StepId/StepGroup — источник DealsCount).
  • Смежные операции: list-deals.md (сделки колонок по StepId/StepGroup); событие жизненного цикла: deal-stage-changed.md.
  • Контекст: README; tenancy — ADR-0052.