iac/apps/pm/openapi.yaml

1769 lines
86 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

openapi: 3.0.3
info:
title: PM Backend API
version: "1.0.0"
description: |
REST API сервиса **pm-backend** (`planning/pm-backend`) — управление
проектами и папками, задачами (иерархия, связи, ресурсы), базовыми планами
(состояниями), атрибутами, календарями, настройками КСГ, детализацией задач,
системным журналом и экспортом.
Сервис написан на Python (**Django 5.1 + Django REST Framework**) и запускается
как ASGI-приложение (`config.asgi_root:application`) через gunicorn с воркерами
`uvicorn.workers.UvicornWorker` (порт `8000`). Роутинг задан в `config/urls.py`
и состоит из трёх групп:
- публичный API — префикс `/api/pm/msp/` (`sarex.pm.urls`);
- внешний API v2 — префикс `/api/pm/external/v2/` (`sarex.pm.urls_external`),
только чтение;
- внутренний API — префикс `/internal/pm/` (`sarex.pm.urls_internal`),
предназначен для вызовов внутри кластера, аутентификация не требуется.
### Аутентификация
Публичные (`/api/pm/msp/*`) и внешние (`/api/pm/external/v2/*`) эндпоинты
требуют аутентификации (DRF `IsAuthenticated`). Проверка выполняется по цепочке
(`REST_FRAMEWORK.DEFAULT_AUTHENTICATION_CLASSES`):
1. **Zitadel** (`ZitadelJWTAuthentication`) — требуются одновременно заголовки
`Authorization: Bearer <jwt>` и `Identity: Identity <jwt>`. При отсутствии
любого из них — переход к следующему механизму/ошибка.
2. **sarex-backend** (`JWTAuthentication`) — подпись токена `Authorization:
Bearer <jwt>` проверяется публичным RSA-ключом (`AUTH_PUBLIC_KEY`,
алгоритм `RS512`). При `SERVER_DEBUG=true` аутентификация обходится
(возвращается анонимный пользователь).
Внутренние эндпоинты (`/internal/pm/*`) имеют `authentication_classes=[]` и
`AllowAny` — доступ ограничивается сетевым слоем. Отдельный публичный эндпоинт
`/api/pm/msp/external-task-info/` также открыт (`AllowAny`).
### Пагинация
По умолчанию используется DRF `LimitOffsetPagination` (`PAGE_SIZE=1000`).
Списочные ответы оборачиваются в объект `{ count, next, previous, results }`;
размер страницы задаётся query-параметром `limit`, смещение — `offset`. Часть
«тяжёлых» списков (задачи, привязки ресурсов, связи задач) использует
`ProjectTaskPagination` (`default_limit=30000`).
### Обработка ошибок
Ошибки возвращаются в стандартном формате DRF: ошибки валидации — `400`
(`{ "<field>": ["<msg>"] }` либо `{ "detail": "..." }`), нет прав — `403`
(`{ "detail": "..." }`), не аутентифицирован — `401`, не найдено — `404`.
Объектные права проверяются `PermissionMixin` (raw-SQL), суперпользователь
их обходит.
### Замечания (расхождения кода)
- Внешние вьюсеты v2 объявляют `http_method_names = ['get']`, поэтому доступны
только `list`/`retrieve` (и GET-`@action`), даже если в коде есть методы
записи.
- Ряд bulk-эндпоинтов принимает JSON-массив (list-сериализатор).
- Datetime-поля (`DateTimeWithoutTZFiled`) наивные — таймзона отбрасывается.
- Внутренние эндпоинты принимают/возвращают «сырые» словари, без модельных
сериализаторов.
contact:
name: pm-backend
url: https://gitlab/planning/pm-backend
servers:
- url: https://api.sarex.io
description: Production (ingress)
- url: https://stage-api.sarex.io
description: Stage (ingress)
- url: http://pm-backend-service.planning.svc.cluster.local:8000
description: Внутрикластерный адрес (ClusterIP, порт 8000) — единственный способ достучаться до /internal/pm
- url: http://localhost:8000
description: Локальный запуск (gunicorn/uvicorn, порт 8000)
tags:
- name: projects
description: Проекты и папки — CRUD, копирование, экспорт, права, шаблоны
- name: tasks
description: Задачи проекта — CRUD, массовые операции, права, описания
- name: resources
description: Ресурсы и их привязки к задачам и элементам
- name: states
description: Состояния проекта (базовые планы)
- name: relations
description: Связи задач и проектов, атрибутивные связи
- name: attributes
description: Атрибуты проекта и значения атрибутов задач
- name: calendars
description: Календари
- name: settings
description: Настройки/состояния КСГ
- name: detailing
description: Детализация задач, визуальные профили
- name: comments
description: Комментарии к задачам
- name: system-log
description: Системный журнал и откаты
- name: external
description: Внешний API v2 (только чтение)
- name: internal
description: Внутренние эндпоинты (только внутри кластера, без аутентификации)
security:
- bearerAuth: []
paths:
# ==========================================================================
# Projects (/api/pm/msp/projects)
# ==========================================================================
/api/pm/msp/projects/:
get:
tags: [projects]
summary: Список проектов и папок
operationId: listProjects
parameters:
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Offset'
- { name: search, in: query, schema: { type: string } }
- { name: ids, in: query, description: CSV идентификаторов, schema: { type: string } }
- { name: resource_id, in: query, schema: { type: string } }
- { name: company_id, in: query, schema: { type: integer } }
- { name: parent_id, in: query, schema: { type: integer } }
- { name: only_children, in: query, schema: { type: boolean } }
- { name: templates, in: query, schema: { type: boolean } }
- { name: is_folder, in: query, schema: { type: boolean } }
- { name: strict, in: query, schema: { type: boolean } }
- { name: extend, in: query, schema: { type: boolean } }
- { name: schema, in: query, description: "напр. tiny/detail", schema: { type: string } }
responses:
'200':
description: Страница проектов
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/PaginatedResponse'
- type: object
properties:
results:
type: array
items: { $ref: '#/components/schemas/Project' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
post:
tags: [projects]
summary: Создать проект/папку
operationId: createProject
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/ProjectCreate' }
responses:
'201':
description: Проект создан
content:
application/json:
schema: { $ref: '#/components/schemas/Project' }
'400': { $ref: '#/components/responses/BadRequest' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
/api/pm/msp/projects/{id}/:
parameters:
- $ref: '#/components/parameters/PathId'
get:
tags: [projects]
summary: Проект по id
operationId: retrieveProject
parameters:
- { name: extend, in: query, schema: { type: boolean } }
- { name: schema, in: query, schema: { type: string } }
responses:
'200':
description: Проект
content:
application/json:
schema: { $ref: '#/components/schemas/Project' }
'404': { $ref: '#/components/responses/NotFound' }
put:
tags: [projects]
summary: Полное обновление проекта
operationId: updateProject
requestBody:
required: true
content: { application/json: { schema: { $ref: '#/components/schemas/ProjectCreate' } } }
responses:
'200': { description: Обновлено, content: { application/json: { schema: { $ref: '#/components/schemas/Project' } } } }
'400': { $ref: '#/components/responses/BadRequest' }
'404': { $ref: '#/components/responses/NotFound' }
patch:
tags: [projects]
summary: Частичное обновление проекта
operationId: partialUpdateProject
requestBody:
required: true
content: { application/json: { schema: { $ref: '#/components/schemas/ProjectCreate' } } }
responses:
'200': { description: Обновлено, content: { application/json: { schema: { $ref: '#/components/schemas/Project' } } } }
'400': { $ref: '#/components/responses/BadRequest' }
'404': { $ref: '#/components/responses/NotFound' }
delete:
tags: [projects]
summary: Удалить проект
operationId: deleteProject
responses:
'204': { description: Удалено }
'404': { $ref: '#/components/responses/NotFound' }
/api/pm/msp/projects/root_folder/:
post:
tags: [projects]
summary: Создать корневую папку
operationId: createRootFolder
requestBody:
required: true
content: { application/json: { schema: { $ref: '#/components/schemas/FolderCreate' } } }
responses:
'201': { description: Папка создана, content: { application/json: { schema: { $ref: '#/components/schemas/Project' } } } }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/projects/rename/:
post:
tags: [projects]
summary: Переименовать проект
operationId: renameProject
responses:
'200': { description: OK }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/projects/import/:
post:
tags: [projects]
summary: Импорт задач в проект
operationId: importProject
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
pk: { type: integer }
from_type: { type: string, enum: [sarex, ms_project, primavera, template] }
update: { type: boolean }
template_id: { type: integer, nullable: true }
required: [pk, from_type]
responses:
'201': { description: Импорт запущен, content: { application/json: { schema: { type: object, properties: { uuid: { type: string, format: uuid } } } } } }
'404': { $ref: '#/components/responses/NotFound' }
/api/pm/msp/projects/import-from-gasprom/:
post:
tags: [projects]
summary: Импорт проекта из Газпром ЦПС
operationId: importProjectFromGaspromCps
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
project_name: { type: string }
company_id: { type: integer }
file_url: { type: string }
required: [project_name, company_id, file_url]
responses:
'201': { description: Импорт запущен, content: { application/json: { schema: { type: object, properties: { uuid: { type: string, format: uuid } } } } } }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/projects/{id}/states/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [projects]
summary: Состояния (базовые планы) проекта
operationId: listProjectStates
responses:
'200': { description: Список состояний, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ProjectState' } } } } }
/api/pm/msp/projects/{id}/permissions/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [projects]
summary: Права проекта
operationId: getProjectPermissions
responses:
'200': { description: Права, content: { application/json: { schema: { $ref: '#/components/schemas/PermissionsResponse' } } } }
post:
tags: [projects]
summary: Сохранить права проекта
operationId: saveProjectPermissions
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses:
'200': { description: Права, content: { application/json: { schema: { $ref: '#/components/schemas/PermissionsResponse' } } } }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/projects/{id}/all_permissions/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [projects]
summary: Все права проекта
operationId: getProjectAllPermissions
responses:
'200': { description: Права, content: { application/json: { schema: { type: object } } } }
/api/pm/msp/projects/{id}/copy_project/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post:
tags: [projects]
summary: Копировать проект
operationId: copyProject
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
parent_id: { type: integer, nullable: true }
project_name: { type: string }
assets_mapping: { type: object }
with_resources: { type: boolean }
responses:
'201': { description: Проект скопирован, content: { application/json: { schema: { $ref: '#/components/schemas/Project' } } } }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/projects/{id}/create_template/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post:
tags: [projects]
summary: Создать шаблон из проекта
operationId: createTemplateFromProject
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
project_name: { type: string }
description: { type: string, nullable: true }
required: [project_name]
responses:
'201': { description: Шаблон создан, content: { application/json: { schema: { $ref: '#/components/schemas/Project' } } } }
/api/pm/msp/projects/{id}/export_project_to_pdf/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post:
tags: [projects]
summary: Экспорт проекта в PDF
operationId: exportProjectToPdf
requestBody:
required: true
content: { application/json: { schema: { $ref: '#/components/schemas/ExportProject' } } }
responses:
'200': { description: Задача экспорта запущена }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/projects/{id}/project_export/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post:
tags: [projects]
summary: Экспорт проекта (xlsx/xml)
operationId: projectExport
responses:
'200': { description: Экспорт запущен }
/api/pm/msp/projects/{id}/key_milestones/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [projects]
summary: Ключевые вехи проекта
operationId: getKeyMilestones
responses:
'200': { description: Вехи, content: { application/json: { schema: { type: array, items: { type: object } } } } }
/api/pm/msp/projects/{id}/profiles/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [projects]
summary: Визуальные профили проекта
operationId: getProjectProfiles
responses:
'200': { description: Профили, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/VisualProfile' } } } } }
/api/pm/msp/projects/{id}/time_markers_data/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [projects]
summary: Данные временных маркеров
operationId: getTimeMarkersData
parameters:
- { name: attributes, in: query, description: CSV идентификаторов атрибутов, schema: { type: string } }
- { name: with_hierarchy, in: query, schema: { type: boolean } }
responses:
'200': { description: Данные, content: { application/json: { schema: { type: object } } } }
/api/pm/msp/projects/{id}/relations/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [projects]
summary: Связи/интеграции проекта
operationId: getProjectRelations
responses:
'200': { description: Связи, content: { application/json: { schema: { type: array, items: { type: object } } } } }
/api/pm/msp/projects/{id}/bulk_integration/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post:
tags: [projects]
summary: Массовое создание интеграций
operationId: bulkIntegration
responses:
'200': { description: OK }
/api/pm/msp/projects/{id}/issues_data/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [projects]
summary: Данные проблем проекта
operationId: getIssuesData
responses:
'200': { description: Данные, content: { application/json: { schema: { type: object } } } }
/api/pm/msp/projects/{id}/formula/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post:
tags: [projects]
summary: Пересчёт по формуле
operationId: applyFormula
responses:
'200': { description: OK }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/projects/{id}/rule/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [detailing]
summary: Правило детализации проекта
operationId: getProjectRule
responses:
'200': { description: Правило, content: { application/json: { schema: { type: object } } } }
post:
tags: [detailing]
summary: Создать правило детализации
operationId: createProjectRule
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
attributes: { type: array, items: { type: integer } }
summary_fields: { type: array, items: { type: string } }
mode: { type: string }
responses:
'200': { description: OK }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/projects/{id}/tasks/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [projects]
summary: Задачи проекта
operationId: getProjectTasks
responses:
'200': { description: Задачи, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ProjectTask' } } } } }
/api/pm/msp/projects/{id}/create_tasks/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post:
tags: [tasks]
summary: Массовое создание задач
operationId: bulkCreateTasks
requestBody:
required: true
content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ProjectTaskCreate' } } } }
responses:
'201': { description: Создано, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ProjectTask' } } } } }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/projects/{id}/update_tasks/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
patch:
tags: [tasks]
summary: Массовое обновление задач
operationId: bulkUpdateTasks
requestBody:
required: true
content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ProjectTaskUpdate' } } } }
responses:
'200': { description: Обновлено }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/projects/{id}/delete_tasks/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
delete:
tags: [tasks]
summary: Массовое удаление задач
operationId: bulkDeleteTasks
requestBody:
required: true
content: { application/json: { schema: { type: object, properties: { ids: { type: array, items: { type: integer } } } } } }
responses:
'204': { description: Удалено }
/api/pm/msp/projects/{id}/system-logs/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [system-log]
summary: Системные логи проекта
operationId: getSystemLogs
parameters:
- { name: task_id, in: query, schema: { type: integer } }
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Offset'
responses:
'200':
description: Страница логов
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/PaginatedResponse'
- type: object
properties: { results: { type: array, items: { $ref: '#/components/schemas/SystemLog' } } }
/api/pm/msp/projects/{id}/system-log-detail/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [system-log]
summary: Деталь записи журнала
operationId: getSystemLogDetail
parameters:
- { name: log_id, in: query, required: true, schema: { type: integer } }
responses:
'200': { description: Деталь, content: { application/json: { schema: { $ref: '#/components/schemas/SystemLog' } } } }
/api/pm/msp/projects/{id}/rollback-to-record/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post:
tags: [system-log]
summary: Откат к записи журнала
operationId: rollbackToRecord
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
log_id: { type: integer }
read_only: { type: boolean }
required: [log_id]
responses:
'200': { description: OK }
'400': { $ref: '#/components/responses/BadRequest' }
# ==========================================================================
# Tasks (/api/pm/msp/tasks)
# ==========================================================================
/api/pm/msp/tasks/:
get:
tags: [tasks]
summary: Список задач
operationId: listTasks
parameters:
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Offset'
- { name: project, in: query, schema: { type: integer } }
- { name: ids, in: query, schema: { type: string } }
- { name: name__icontains, in: query, schema: { type: string } }
- { name: time_start, in: query, schema: { type: string, format: date-time } }
- { name: time_end, in: query, schema: { type: string, format: date-time } }
- { name: status, in: query, description: CSV статусов, schema: { type: string } }
- { name: responsible, in: query, schema: { type: string } }
- { name: executors, in: query, schema: { type: string } }
- { name: with_hierarchy, in: query, schema: { type: boolean, default: true } }
responses:
'200':
description: Страница задач
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/PaginatedResponse'
- type: object
properties: { results: { type: array, items: { $ref: '#/components/schemas/ProjectTask' } } }
post:
tags: [tasks]
summary: Создать задачу
operationId: createTask
requestBody:
required: true
content: { application/json: { schema: { $ref: '#/components/schemas/ProjectTaskCreate' } } }
responses:
'201': { description: Создано, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectTask' } } } }
'400': { $ref: '#/components/responses/BadRequest' }
/api/pm/msp/tasks/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [tasks]
summary: Задача по id
operationId: retrieveTask
responses:
'200': { description: Задача, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectTask' } } } }
'404': { $ref: '#/components/responses/NotFound' }
put:
tags: [tasks]
summary: Полное обновление задачи
operationId: updateTask
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectTaskCreate' } } } }
responses:
'200': { description: Обновлено, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectTask' } } } }
'400': { $ref: '#/components/responses/BadRequest' }
patch:
tags: [tasks]
summary: Частичное обновление задачи
operationId: partialUpdateTask
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectTaskCreate' } } } }
responses:
'200': { description: Обновлено, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectTask' } } } }
delete:
tags: [tasks]
summary: Удалить задачу
operationId: deleteTask
responses:
'204': { description: Удалено }
/api/pm/msp/tasks/task_index/:
patch:
tags: [tasks]
summary: Переиндексация задач
operationId: updateTaskIndex
responses:
'200': { description: OK }
/api/pm/msp/tasks/copy/:
post:
tags: [tasks]
summary: Копировать задачи
operationId: copyTasks
requestBody:
required: true
content: { application/json: { schema: { type: object, properties: { ids: { type: array, items: { type: integer } } } } } }
responses:
'200': { description: OK }
/api/pm/msp/tasks/{id}/permissions/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [tasks]
summary: Права задачи
operationId: getTaskPermissions
responses:
'200': { description: Права, content: { application/json: { schema: { $ref: '#/components/schemas/PermissionsResponse' } } } }
post:
tags: [tasks]
summary: Сохранить права задачи
operationId: saveTaskPermissions
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses:
'200': { description: Права, content: { application/json: { schema: { $ref: '#/components/schemas/PermissionsResponse' } } } }
/api/pm/msp/tasks/{id}/descriptions/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [tasks]
summary: Описание задачи
operationId: getTaskDescription
responses:
'200': { description: Описание, content: { application/json: { schema: { $ref: '#/components/schemas/Description' } } } }
patch:
tags: [tasks]
summary: Обновить описание задачи
operationId: updateTaskDescription
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/Description' } } } }
responses:
'200': { description: Обновлено, content: { application/json: { schema: { $ref: '#/components/schemas/Description' } } } }
# ==========================================================================
# Detailed tasks / descriptions
# ==========================================================================
/api/pm/msp/detailed-tasks/:
get:
tags: [detailing]
summary: Список детализированных задач
operationId: listDetailedTasks
parameters:
- { name: task, in: query, schema: { type: integer } }
- { name: project, in: query, schema: { type: integer } }
responses:
'200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/DetailedTask' } } } } }
post:
tags: [detailing]
summary: Создать детализированную задачу
operationId: createDetailedTask
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DetailedTask' } } } }
responses:
'201': { description: Создано, content: { application/json: { schema: { $ref: '#/components/schemas/DetailedTask' } } } }
/api/pm/msp/detailed-tasks/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
patch:
tags: [detailing]
summary: Обновить детализированную задачу
operationId: updateDetailedTask
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DetailedTask' } } } }
responses:
'200': { description: Обновлено, content: { application/json: { schema: { $ref: '#/components/schemas/DetailedTask' } } } }
delete:
tags: [detailing]
summary: Удалить детализированную задачу
operationId: deleteDetailedTask
responses:
'204': { description: Удалено }
/api/pm/msp/descriptions/upload_file/:
post:
tags: [tasks]
summary: Загрузить файл описания
operationId: uploadDescriptionFile
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file: { type: string, format: binary }
responses:
'200': { description: OK }
# ==========================================================================
# Values (actual values)
# ==========================================================================
/api/pm/msp/values/:
get:
tags: [tasks]
summary: Фактические значения задач
operationId: listActualValues
parameters:
- { name: task, in: query, schema: { type: integer } }
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Offset'
responses:
'200': { description: Значения, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ActualValue' } } } } }
post:
tags: [tasks]
summary: Создать фактическое значение
operationId: createActualValue
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ActualValueCreate' } } } }
responses:
'201': { description: Создано, content: { application/json: { schema: { $ref: '#/components/schemas/ActualValue' } } } }
/api/pm/msp/values/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [tasks]
summary: Значение по id
operationId: retrieveActualValue
responses: { '200': { description: Значение, content: { application/json: { schema: { $ref: '#/components/schemas/ActualValue' } } } }, '404': { $ref: '#/components/responses/NotFound' } }
put:
tags: [tasks]
summary: Обновить значение
operationId: updateActualValue
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ActualValueCreate' } } } }
responses: { '200': { description: Обновлено, content: { application/json: { schema: { $ref: '#/components/schemas/ActualValue' } } } } }
delete:
tags: [tasks]
summary: Удалить значение
operationId: deleteActualValue
responses: { '204': { description: Удалено } }
# ==========================================================================
# Project states (base plans)
# ==========================================================================
/api/pm/msp/project-states/:
get:
tags: [states]
summary: Список состояний
operationId: listStates
responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ProjectState' } } } } } }
post:
tags: [states]
summary: Создать состояние (базовый план)
operationId: createState
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectStateCreate' } } } }
responses: { '201': { description: Создано, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectState' } } } } }
/api/pm/msp/project-states/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [states]
summary: Состояние по id
operationId: retrieveState
responses: { '200': { description: Состояние, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectState' } } } }, '404': { $ref: '#/components/responses/NotFound' } }
put:
tags: [states]
summary: Обновить состояние
operationId: updateState
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectStateCreate' } } } }
responses: { '200': { description: Обновлено, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectState' } } } } }
delete:
tags: [states]
summary: Удалить состояние
operationId: deleteState
responses: { '204': { description: Удалено } }
/api/pm/msp/project-states/{id}/data/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get:
tags: [states]
summary: Данные состояния (базового плана)
operationId: getStateData
responses: { '200': { description: Данные, content: { application/json: { schema: { type: object } } } } }
post:
tags: [states]
summary: Добавить задачи в базовый план
operationId: addTasksToState
responses: { '200': { description: OK } }
/api/pm/msp/project-states/{id}/edit_state/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
patch:
tags: [states]
summary: Редактировать состояние
operationId: editState
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
id: { type: integer }
time_start: { type: string, format: date-time, nullable: true }
time_end: { type: string, format: date-time, nullable: true }
progress: { type: number, nullable: true }
planned_value: { type: number, nullable: true }
unit: { type: string, nullable: true }
required: [id]
responses: { '200': { description: OK } }
# ==========================================================================
# Resources
# ==========================================================================
/api/pm/msp/resources/:
get:
tags: [resources]
summary: Список ресурсов
operationId: listResources
parameters:
- { name: ids, in: query, schema: { type: string } }
- { name: projects, in: query, schema: { type: string } }
- { name: type, in: query, schema: { type: string } }
- { name: parent, in: query, schema: { type: integer } }
- { name: company, in: query, schema: { type: integer } }
- { name: full, in: query, schema: { type: boolean } }
- { name: extend, in: query, schema: { type: boolean } }
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Offset'
responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Resource' } } } } } }
post:
tags: [resources]
summary: Создать ресурс
operationId: createResource
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ResourceCreate' } } } }
responses: { '201': { description: Создано, content: { application/json: { schema: { $ref: '#/components/schemas/Resource' } } } } }
/api/pm/msp/resources/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get: { tags: [resources], summary: Ресурс по id, operationId: retrieveResource, responses: { '200': { description: Ресурс, content: { application/json: { schema: { $ref: '#/components/schemas/Resource' } } } }, '404': { $ref: '#/components/responses/NotFound' } } }
put: { tags: [resources], summary: Обновить ресурс, operationId: updateResource, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ResourceCreate' } } } }, responses: { '200': { description: Обновлено, content: { application/json: { schema: { $ref: '#/components/schemas/Resource' } } } } } }
patch: { tags: [resources], summary: Частичное обновление ресурса, operationId: partialUpdateResource, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ResourceCreate' } } } }, responses: { '200': { description: Обновлено, content: { application/json: { schema: { $ref: '#/components/schemas/Resource' } } } } } }
delete: { tags: [resources], summary: Удалить ресурс, operationId: deleteResource, responses: { '204': { description: Удалено } } }
/api/pm/msp/resources/bulk_create/:
post:
tags: [resources]
summary: Массовое создание ресурсов
operationId: bulkCreateResources
requestBody: { required: true, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ResourceCreate' } } } } }
responses: { '201': { description: Создано } }
/api/pm/msp/resources/resource_planning/:
get:
tags: [resources]
summary: Ресурсное планирование
operationId: resourcePlanning
parameters:
- { name: projects, in: query, schema: { type: string } }
- { name: resource_type, in: query, schema: { type: string, default: human } }
- { name: start, in: query, required: true, schema: { type: string, format: date-time } }
- { name: end, in: query, required: true, schema: { type: string, format: date-time } }
- { name: scale, in: query, schema: { type: string, default: D } }
- { name: group, in: query, schema: { type: string } }
responses: { '200': { description: Данные, content: { application/json: { schema: { type: object } } } } }
/api/pm/msp/resources/{id}/bind_task/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post: { tags: [resources], summary: Привязать задачу к ресурсу, operationId: bindTaskToResource, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ResourceTaskRelation' } } } }, responses: { '201': { description: Создано } } }
/api/pm/msp/resources/{id}/bind_elements/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post: { tags: [resources], summary: Привязать элементы к ресурсу, operationId: bindElementsToResource, responses: { '201': { description: Создано } } }
# ---- Resource relations ----
/api/pm/msp/resources-tasks/:
get:
tags: [resources]
summary: Список привязок ресурс-задача
operationId: listResourceTaskRelations
parameters:
- { name: projects, in: query, schema: { type: string } }
- { name: tasks, in: query, schema: { type: string } }
- { name: resources, in: query, schema: { type: string } }
responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ResourceTaskRelation' } } } } } }
post: { tags: [resources], summary: Создать привязку ресурс-задача, operationId: createResourceTaskRelation, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ResourceTaskRelation' } } } }, responses: { '201': { description: Создано } } }
/api/pm/msp/resources-tasks/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get: { tags: [resources], summary: Привязка по id, operationId: retrieveResourceTaskRelation, responses: { '200': { description: Привязка, content: { application/json: { schema: { $ref: '#/components/schemas/ResourceTaskRelation' } } } } } }
patch: { tags: [resources], summary: Обновить привязку, operationId: updateResourceTaskRelation, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ResourceTaskRelation' } } } }, responses: { '200': { description: Обновлено } } }
delete: { tags: [resources], summary: Удалить привязку, operationId: deleteResourceTaskRelation, responses: { '204': { description: Удалено } } }
/api/pm/msp/resources-tasks/assign/:
patch: { tags: [resources], summary: Назначение ресурса на задачи, operationId: assignResourceToTasks, responses: { '200': { description: OK } } }
/api/pm/msp/resources-tasks/bulk_update/:
patch:
tags: [resources]
summary: Массовое обновление привязок ресурс-задача
operationId: bulkUpdateResourceTaskRelations
requestBody: { required: true, content: { application/json: { schema: { type: object, properties: { ids: { type: array, items: { type: integer } }, profile: { type: integer } } } } } }
responses: { '200': { description: OK } }
/api/pm/msp/resources-tasks/bulk_delete/:
delete:
tags: [resources]
summary: Массовое удаление привязок ресурс-задача
operationId: bulkDeleteResourceTaskRelations
requestBody: { required: true, content: { application/json: { schema: { type: object, properties: { ids: { type: array, items: { type: integer } } } } } } }
responses: { '204': { description: Удалено } }
/api/pm/msp/resources-elements/:
get:
tags: [resources]
summary: Список привязок ресурс-элемент
operationId: listResourceElementRelations
parameters:
- { name: resources, in: query, schema: { type: string } }
- { name: instances, in: query, schema: { type: string } }
responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ResourceElementRelation' } } } } } }
post: { tags: [resources], summary: Создать привязку ресурс-элемент, operationId: createResourceElementRelation, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ResourceElementRelation' } } } }, responses: { '201': { description: Создано } } }
# ==========================================================================
# Relations (task/project)
# ==========================================================================
/api/pm/msp/task-relations/:
get:
tags: [relations]
summary: Список связей задач
operationId: listTaskRelations
parameters: [ { name: project, in: query, schema: { type: integer } } ]
responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Task2TaskRelation' } } } } } }
post: { tags: [relations], summary: Создать связь задач, operationId: createTaskRelation, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/Task2TaskRelation' } } } }, responses: { '201': { description: Создано } } }
/api/pm/msp/task-relations/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
put: { tags: [relations], summary: Обновить связь задач, operationId: updateTaskRelation, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/Task2TaskRelation' } } } }, responses: { '200': { description: Обновлено } } }
delete: { tags: [relations], summary: Удалить связь задач, operationId: deleteTaskRelation, responses: { '204': { description: Удалено } } }
/api/pm/msp/task-relations/bulk_delete/:
delete:
tags: [relations]
summary: Массовое удаление связей задач
operationId: bulkDeleteTaskRelations
requestBody: { required: true, content: { application/json: { schema: { type: object, properties: { ids: { type: array, items: { type: integer } } } } } } }
responses: { '204': { description: Удалено } }
/api/pm/msp/project-relations/:
get: { tags: [relations], summary: Список связей проектов, operationId: listProjectRelations, parameters: [ { name: project, in: query, schema: { type: integer } } ], responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Project2ProjectRelation' } } } } } } }
post:
tags: [relations]
summary: Создать связь(и) проектов
operationId: createProjectRelation
requestBody: { required: true, content: { application/json: { schema: { oneOf: [ { $ref: '#/components/schemas/Project2ProjectRelation' }, { type: array, items: { $ref: '#/components/schemas/Project2ProjectRelation' } } ] } } } }
responses: { '201': { description: Создано } }
/api/pm/msp/project-relations/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
patch: { tags: [relations], summary: Обновить связь проектов, operationId: updateProjectRelation, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/Project2ProjectRelation' } } } }, responses: { '200': { description: Обновлено } } }
delete: { tags: [relations], summary: Удалить связь проектов, operationId: deleteProjectRelation, responses: { '204': { description: Удалено } } }
/api/pm/msp/entity-relations/:
get: { tags: [relations], summary: Список сущностных связей, operationId: listEntityRelations, parameters: [ { name: project_id, in: query, schema: { type: integer } } ], responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/EntityRelation' } } } } } } }
post: { tags: [relations], summary: Создать сущностную связь, operationId: createEntityRelation, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/EntityRelation' } } } }, responses: { '201': { description: Создано } } }
/api/pm/msp/entity-relations/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
patch: { tags: [relations], summary: Обновить сущностную связь, operationId: updateEntityRelation, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/EntityRelation' } } } }, responses: { '200': { description: Обновлено } } }
delete: { tags: [relations], summary: Удалить сущностную связь, operationId: deleteEntityRelation, responses: { '204': { description: Удалено } } }
# ==========================================================================
# Attributes
# ==========================================================================
/api/pm/msp/project-attribute/:
get:
tags: [attributes]
summary: Атрибуты проекта
operationId: listProjectAttributes
parameters: [ { name: project, in: query, schema: { type: integer } } ]
responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ProjectAttributeRelation' } } } } } }
post:
tags: [attributes]
summary: Привязать атрибут(ы) к проекту
operationId: createProjectAttribute
requestBody: { required: true, content: { application/json: { schema: { oneOf: [ { $ref: '#/components/schemas/ProjectAttributeRelation' }, { type: array, items: { $ref: '#/components/schemas/ProjectAttributeRelation' } } ] } } } }
responses: { '201': { description: Создано } }
/api/pm/msp/project-attribute/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
put: { tags: [attributes], summary: Обновить атрибут проекта, operationId: updateProjectAttribute, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectAttributeRelation' } } } }, responses: { '200': { description: Обновлено } } }
delete: { tags: [attributes], summary: Удалить атрибут проекта, operationId: deleteProjectAttribute, responses: { '204': { description: Удалено } } }
/api/pm/msp/task-value/:
get:
tags: [attributes]
summary: Значения атрибутов задач
operationId: listTaskValues
parameters:
- { name: tasks, in: query, description: CSV, schema: { type: string } }
- { name: attributes, in: query, description: CSV, schema: { type: string } }
responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/TaskAttributeValue' } } } } } }
post:
tags: [attributes]
summary: Создать значение(я) атрибутов задач
operationId: createTaskValue
requestBody: { required: true, content: { application/json: { schema: { oneOf: [ { $ref: '#/components/schemas/TaskAttributeValue' }, { type: array, items: { $ref: '#/components/schemas/TaskAttributeValue' } } ] } } } }
responses: { '201': { description: Создано } }
# ==========================================================================
# Calendars
# ==========================================================================
/api/pm/msp/calendars/:
get: { tags: [calendars], summary: Список календарей, operationId: listCalendars, responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Calendar' } } } } } } }
post: { tags: [calendars], summary: Создать календарь, operationId: createCalendar, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/Calendar' } } } }, responses: { '201': { description: Создано, content: { application/json: { schema: { $ref: '#/components/schemas/Calendar' } } } } } }
/api/pm/msp/calendars/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get: { tags: [calendars], summary: Календарь по id, operationId: retrieveCalendar, responses: { '200': { description: Календарь, content: { application/json: { schema: { $ref: '#/components/schemas/Calendar' } } } } } }
patch: { tags: [calendars], summary: Изменить календарь, operationId: updateCalendar, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/Calendar' } } } }, responses: { '200': { description: Обновлено } } }
delete: { tags: [calendars], summary: Удалить календарь, operationId: deleteCalendar, responses: { '204': { description: Удалено } } }
# ==========================================================================
# Project settings (KSG)
# ==========================================================================
/api/pm/msp/project-settings/:
get: { tags: [settings], summary: Список настроек КСГ, operationId: listProjectSettings, responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ProjectSettings' } } } } } } }
post: { tags: [settings], summary: Создать настройку КСГ, operationId: createProjectSettings, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectSettings' } } } }, responses: { '201': { description: Создано } } }
/api/pm/msp/project-settings/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get: { tags: [settings], summary: Настройка по id, operationId: retrieveProjectSettings, responses: { '200': { description: Настройка, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectSettings' } } } } } }
patch: { tags: [settings], summary: Изменить настройку, operationId: updateProjectSettings, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectSettings' } } } }, responses: { '200': { description: Обновлено } } }
delete: { tags: [settings], summary: Удалить настройку, operationId: deleteProjectSettings, responses: { '204': { description: Удалено } } }
/api/pm/msp/project-settings/{id}/apply/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
post:
tags: [settings]
summary: Применить глобальную настройку
operationId: applyProjectSettings
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
project_id: { type: integer }
connect_missing_attributes: { type: boolean }
required: [project_id]
responses: { '200': { description: OK } }
# ==========================================================================
# Visual profiles
# ==========================================================================
/api/pm/msp/visual-profiles/:
get: { tags: [detailing], summary: Список визуальных профилей, operationId: listVisualProfiles, responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/VisualProfile' } } } } } } }
post: { tags: [detailing], summary: Создать визуальный профиль, operationId: createVisualProfile, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/VisualProfile' } } } }, responses: { '201': { description: Создано } } }
/api/pm/msp/visual-profiles/{id}:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
patch: { tags: [detailing], summary: Изменить визуальный профиль, operationId: updateVisualProfile, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/VisualProfile' } } } }, responses: { '200': { description: Обновлено } } }
delete: { tags: [detailing], summary: Удалить визуальный профиль, operationId: deleteVisualProfile, responses: { '204': { description: Удалено } } }
# ==========================================================================
# Comments
# ==========================================================================
/api/pm/msp/comments/:
get: { tags: [comments], summary: Список комментариев, operationId: listComments, responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Comment' } } } } } } }
post: { tags: [comments], summary: Создать комментарий, operationId: createComment, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CommentCreate' } } } }, responses: { '201': { description: Создано } } }
# ==========================================================================
# Descriptions resource
# ==========================================================================
/api/pm/msp/descriptions/:
get: { tags: [tasks], summary: Список описаний, operationId: listDescriptions, parameters: [ { name: task, in: query, schema: { type: integer } } ], responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Description' } } } } } } }
post: { tags: [tasks], summary: Создать описание, operationId: createDescription, requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/Description' } } } }, responses: { '201': { description: Создано } } }
# ==========================================================================
# Task info (public/service)
# ==========================================================================
/api/pm/msp/task-info/:
get:
tags: [tasks]
summary: Задачи по профилям (not_started/in_progress/completed)
operationId: getTaskInfo
parameters:
- { name: projects, in: query, required: true, description: CSV идентификаторов, schema: { type: string } }
- { name: date, in: query, required: true, schema: { type: string, format: date-time } }
- { name: document, in: query, required: true, schema: { type: string } }
- { name: base_plane, in: query, schema: { type: string } }
responses: { '200': { description: Данные, content: { application/json: { schema: { type: object } } } } }
/api/pm/msp/external-task-info/:
get:
tags: [tasks]
summary: Инфо о фоновой задаче через брокер
operationId: getExternalTaskInfo
security: []
parameters: [ { name: task_id, in: query, schema: { type: string, format: uuid } } ]
responses: { '200': { description: Данные, content: { application/json: { schema: { type: object } } } } }
# ==========================================================================
# External API v2 (read-only)
# ==========================================================================
/api/pm/external/v2/projects/:
get:
tags: [external]
summary: Список проектов (внешний)
operationId: externalListProjects
parameters:
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Offset'
- { name: search, in: query, schema: { type: string } }
- { name: ids, in: query, schema: { type: string } }
- { name: parent_id, in: query, schema: { type: integer } }
responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/Project' } } } } } }
/api/pm/external/v2/projects/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get: { tags: [external], summary: Проект (внешний), operationId: externalRetrieveProject, responses: { '200': { description: Проект, content: { application/json: { schema: { $ref: '#/components/schemas/Project' } } } }, '404': { $ref: '#/components/responses/NotFound' } } }
/api/pm/external/v2/projects/{id}/project_data/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get: { tags: [external], summary: Данные проекта (внешний), operationId: externalProjectData, responses: { '200': { description: Данные, content: { application/json: { schema: { type: object } } } } } }
/api/pm/external/v2/tasks/:
get:
tags: [external]
summary: Список задач (внешний)
operationId: externalListTasks
parameters:
- { name: extend, in: query, schema: { type: boolean } }
- { name: ids, in: query, schema: { type: string } }
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Offset'
responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ProjectTask' } } } } } }
/api/pm/external/v2/tasks/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get: { tags: [external], summary: Задача (внешний), operationId: externalRetrieveTask, responses: { '200': { description: Задача, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectTask' } } } } } }
/api/pm/external/v2/values/:
get: { tags: [external], summary: Фактические значения (внешний), operationId: externalListValues, responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ActualValue' } } } } } } }
/api/pm/external/v2/values/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get: { tags: [external], summary: Значение (внешний), operationId: externalRetrieveValue, responses: { '200': { description: Значение, content: { application/json: { schema: { $ref: '#/components/schemas/ActualValue' } } } } } }
/api/pm/external/v2/project-states/:
get:
tags: [external]
summary: Состояния проекта (внешний)
operationId: externalListStates
parameters: [ { name: project_id, in: query, schema: { type: integer } } ]
responses: { '200': { description: Список, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/ProjectState' } } } } } }
/api/pm/external/v2/project-states/{id}/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get: { tags: [external], summary: Состояние (внешний), operationId: externalRetrieveState, responses: { '200': { description: Состояние, content: { application/json: { schema: { $ref: '#/components/schemas/ProjectState' } } } } } }
/api/pm/external/v2/project-states/{id}/data/:
parameters: [ { $ref: '#/components/parameters/PathId' } ]
get: { tags: [external], summary: Данные состояния (внешний), operationId: externalStateData, responses: { '200': { description: Данные, content: { application/json: { schema: { type: object } } } } } }
# ==========================================================================
# Internal API (no auth, cluster-only)
# ==========================================================================
/internal/pm/auto_scheduling/:
post:
tags: [internal]
summary: Интегрированное автопланирование
operationId: internalAutoScheduling
security: []
requestBody: { required: true, content: { application/json: { schema: { type: object, properties: { project_id: { type: integer } }, required: [project_id] } } } }
responses: { '200': { description: OK } }
/internal/pm/integrated_base_plan/:
post:
tags: [internal]
summary: Создать/обновить интегрированные состояния
operationId: internalIntegratedBasePlan
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
project_id: { type: integer }
user_id: { type: integer }
linked_project: { type: integer }
name: { type: string }
tasks: { type: array, items: { type: object } }
task_ids: { type: array, items: { type: integer } }
state_id: { type: integer }
responses: { '200': { description: OK } }
/internal/pm/detailed_tasks/:
post:
tags: [internal]
summary: Обновить значения атрибутов задачи
operationId: internalDetailedTasks
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
task_id: { type: integer }
result_fields: { type: object }
company_id: { type: integer }
required: [task_id, company_id]
responses: { '200': { description: OK } }
/internal/pm/sync_tasks/:
post:
tags: [internal]
summary: Синхронизация задач + автопланирование
operationId: internalSyncTasks
security: []
requestBody: { required: true, content: { application/json: { schema: { type: object, properties: { project_id: { type: integer } }, required: [project_id] } } } }
responses: { '200': { description: OK } }
/internal/pm/base_plans/:
get:
tags: [internal]
summary: Получить базовый план
operationId: internalGetBasePlan
security: []
parameters: [ { name: base_plan_id, in: query, required: true, schema: { type: integer } } ]
responses:
'200': { description: Данные, content: { application/json: { schema: { type: object } } } }
'400': { $ref: '#/components/responses/BadRequest' }
/internal/pm/tasks_dates/:
get:
tags: [internal]
summary: Даты и прогресс задач проекта
operationId: internalTasksDates
security: []
parameters: [ { name: project_id, in: query, required: true, schema: { type: integer } } ]
responses:
'200': { description: Данные, content: { application/json: { schema: { type: object } } } }
'400': { $ref: '#/components/responses/BadRequest' }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
JWT в заголовке `Authorization: Bearer <token>`. В режиме sarex-backend
подпись проверяется публичным ключом (`RS512`); в режиме Zitadel
дополнительно требуется заголовок `identity`.
identityToken:
type: apiKey
in: header
name: identity
description: |
Заголовок `identity` (`Identity <jwt>`) для режима Zitadel. При его
наличии полезная нагрузка берётся из этого токена.
parameters:
PathId:
name: id
in: path
required: true
schema: { type: integer }
Limit:
name: limit
in: query
required: false
schema: { type: integer, minimum: 0, default: 1000 }
Offset:
name: offset
in: query
required: false
schema: { type: integer, minimum: 0, default: 0 }
responses:
BadRequest:
description: Некорректный запрос / ошибка валидации
content:
application/json:
schema: { $ref: '#/components/schemas/ValidationError' }
Unauthorized:
description: Токен не предоставлен или невалиден
content:
application/json:
schema: { $ref: '#/components/schemas/Detail' }
Forbidden:
description: Недостаточно прав
content:
application/json:
schema: { $ref: '#/components/schemas/Detail' }
NotFound:
description: Ресурс не найден
content:
application/json:
schema: { $ref: '#/components/schemas/Detail' }
schemas:
# --- Общие ------------------------------------------------------------
Detail:
type: object
properties:
detail: { type: string }
required: [detail]
ValidationError:
type: object
description: |
Ошибка валидации DRF — либо словарь `{ "<field>": ["<msg>", ...] }`,
либо `{ "detail": "..." }`.
additionalProperties:
type: array
items: { type: string }
PaginatedResponse:
type: object
description: Обёртка DRF LimitOffsetPagination
properties:
count: { type: integer }
next: { type: string, format: uri, nullable: true }
previous: { type: string, format: uri, nullable: true }
results: { type: array, items: { type: object } }
required: [count, results]
# --- Проекты ----------------------------------------------------------
Project:
type: object
properties:
id: { type: integer, readOnly: true }
name: { type: string }
company: { type: integer }
parent: { type: integer, nullable: true }
is_folder: { type: boolean }
resource_id: { type: string, nullable: true }
document_id: { type: string, nullable: true }
bundle_id: { type: string, nullable: true }
description: { type: string, nullable: true }
settings: { type: object }
target_date: { type: string, format: date-time, nullable: true }
project_type: { type: string, nullable: true }
calendar: { type: integer, nullable: true }
status: { type: string, readOnly: true }
creator: { type: integer, readOnly: true }
permissions: { type: object, readOnly: true }
restrictions: { type: object, readOnly: true }
time_start: { type: string, format: date-time, readOnly: true, nullable: true }
time_end: { type: string, format: date-time, readOnly: true, nullable: true }
total_cost: { type: number, readOnly: true, nullable: true }
current_cost: { type: number, readOnly: true, nullable: true }
progress: { type: number, readOnly: true, nullable: true }
relations: { type: array, items: { type: object }, readOnly: true }
rule: { type: object, nullable: true }
created_at: { type: string, format: date-time, readOnly: true }
updated_at: { type: string, format: date-time, readOnly: true }
required: [name, company]
ProjectCreate:
type: object
properties:
name: { type: string }
company_id: { type: integer }
parent: { type: integer, nullable: true }
is_folder: { type: boolean }
resource_id: { type: string, nullable: true }
ms_file: { type: string, nullable: true }
description: { type: string, nullable: true }
document_id: { type: string, nullable: true }
bundle_id: { type: string, nullable: true }
settings: { type: object, default: {} }
target_date: { type: string, format: date-time, nullable: true }
project_type: { type: string, nullable: true }
calendar: { type: integer, nullable: true }
required: [name, company_id]
FolderCreate:
type: object
properties:
company: { type: integer }
company_name: { type: string }
creator: { type: integer }
service_account: { type: string, format: uuid }
folder_name: { type: string, nullable: true }
required: [company, company_name, creator, service_account]
PermissionsResponse:
type: object
properties:
direct_permissions: { type: array, items: { type: object } }
inherited_permissions: { type: array, items: { type: object } }
# --- Задачи -----------------------------------------------------------
ProjectTask:
type: object
properties:
id: { type: integer, readOnly: true }
project: { type: integer }
parent: { type: integer, nullable: true }
index_prefix: { type: string, nullable: true }
task_id: { type: string, nullable: true }
name: { type: string }
time_start: { type: string, format: date-time, nullable: true }
time_end: { type: string, format: date-time, nullable: true }
deadline: { type: string, format: date-time, nullable: true }
responsible: { type: array, items: { type: integer } }
executors: { type: array, items: { type: integer } }
task_type: { type: string }
unit: { type: string, nullable: true }
local_index: { type: integer, nullable: true }
constraint_date: { type: string, format: date-time, nullable: true }
constraint_type: { type: string, nullable: true }
cost: { type: number, nullable: true }
progress: { type: number, nullable: true }
status: { type: string, nullable: true }
duration: { type: number, nullable: true }
planned_duration: { type: number, nullable: true }
cipher: { type: string, nullable: true }
is_active: { type: boolean }
current_cost: { type: number, nullable: true }
planned_value: { type: number, nullable: true }
calendar: { type: integer, nullable: true }
distribution: { type: object, nullable: true }
actual_value: { type: number, readOnly: true, nullable: true }
total_cost: { type: number, readOnly: true, nullable: true }
attributes: { type: array, items: { type: object } }
permissions: { type: object }
ProjectTaskCreate:
type: object
properties:
project: { type: integer }
parent: { type: integer, nullable: true }
name: { type: string }
time_start: { type: string, format: date-time }
time_end: { type: string, format: date-time }
deadline: { type: string, format: date-time, nullable: true }
responsible: { type: array, items: { type: integer } }
executors: { type: array, items: { type: integer } }
task_type: { type: string, default: fix_value }
unit: { type: string, nullable: true }
cost: { type: number, nullable: true }
progress: { type: number, nullable: true }
status: { type: string, nullable: true }
is_active: { type: boolean, default: true }
calendar: { type: integer, nullable: true }
required: [project, name, time_start, time_end]
ProjectTaskUpdate:
type: object
description: Плоский сериализатор для массового обновления
properties:
id: { type: integer }
name: { type: string }
time_start: { type: string, format: date-time }
time_end: { type: string, format: date-time }
responsible: { type: array, items: { type: integer } }
executors: { type: array, items: { type: integer } }
cost: { type: number }
progress: { type: number }
status: { type: string }
required: [id]
ActualValue:
type: object
properties:
id: { type: integer, readOnly: true }
value: { type: number }
description: { type: string, nullable: true }
task: { type: integer }
time_start: { type: string, format: date-time }
time_end: { type: string, format: date-time }
creator: { type: integer, readOnly: true }
created_at: { type: string, format: date-time, readOnly: true }
updated_at: { type: string, format: date-time, readOnly: true }
ActualValueCreate:
type: object
properties:
value: { type: number }
description: { type: string, nullable: true }
task: { type: integer }
time_start: { type: string, format: date-time }
time_end: { type: string, format: date-time }
required: [value, task, time_start, time_end]
DetailedTask:
type: object
properties:
id: { type: integer, readOnly: true }
task: { type: integer }
weight: { type: number, nullable: true }
data: { type: object }
Description:
type: object
properties:
id: { type: integer, readOnly: true }
task: { type: integer }
text: { type: string }
required: [task, text]
# --- Состояния --------------------------------------------------------
ProjectState:
type: object
properties:
id: { type: integer, readOnly: true }
name: { type: string }
project: { type: integer }
creator: { type: integer, readOnly: true }
is_locked: { type: boolean, readOnly: true }
ProjectStateCreate:
type: object
properties:
name: { type: string }
project: { type: integer }
required: [name, project]
# --- Ресурсы ----------------------------------------------------------
Resource:
type: object
properties:
id: { type: integer, readOnly: true }
name: { type: string }
company: { type: integer }
calendar: { type: integer, nullable: true }
project: { type: integer, nullable: true }
resource_type: { type: string }
parent: { type: integer, nullable: true }
cost: { type: number, nullable: true }
path: { type: string, readOnly: true }
is_leaf: { type: boolean, readOnly: true }
elements: { type: array, items: { type: integer } }
projects: { type: array, items: { type: object } }
ResourceCreate:
type: object
properties:
name: { type: string }
company: { type: integer }
calendar: { type: integer, nullable: true }
project: { type: integer, nullable: true }
resource_type: { type: string }
parent: { type: integer, nullable: true }
cost: { type: number, nullable: true }
required: [name, company]
ResourceTaskRelation:
type: object
properties:
id: { type: integer, readOnly: true }
resource: { type: integer }
task: { type: integer }
profile: { type: integer, nullable: true }
ResourceElementRelation:
type: object
properties:
id: { type: integer, readOnly: true }
resource: { type: integer }
instance: { type: string }
model_name: { type: string }
attributes: { type: object }
path: { type: string, nullable: true }
# --- Связи ------------------------------------------------------------
Task2TaskRelation:
type: object
properties:
id: { type: integer, readOnly: true }
target: { type: integer }
source: { type: integer }
type: { type: string }
lag: { type: number, nullable: true }
Project2ProjectRelation:
type: object
properties:
id: { type: integer, readOnly: true }
successor: { type: integer }
predecessor: { type: integer }
mode: { type: string, nullable: true }
EntityRelation:
type: object
properties:
id: { type: integer, readOnly: true }
project: { type: integer }
entity_name: { type: string }
attribute_ids: { type: array, items: { type: integer } }
settings: { type: object }
entity_type: { type: string }
# --- Атрибуты ---------------------------------------------------------
ProjectAttributeRelation:
type: object
properties:
id: { type: integer, readOnly: true }
project: { type: integer }
asset_id: { type: string, nullable: true }
attribute_id: { type: integer }
is_system: { type: boolean }
default_value: { nullable: true }
mode: { type: string, nullable: true }
TaskAttributeValue:
type: object
properties:
id: { type: integer, readOnly: true }
task: { type: integer }
attribute_id: { type: integer }
type: { type: string, description: "Тип значения (вход): integer/float/string/datetime/date" }
values: { description: Значение(я) атрибута }
integer_value: { type: integer, nullable: true }
float_value: { type: number, nullable: true }
string_value: { type: string, nullable: true }
datetime_value: { type: string, format: date-time, nullable: true }
date_value: { type: string, format: date, nullable: true }
assets: { type: array, items: { type: string } }
# --- Календари --------------------------------------------------------
Calendar:
type: object
properties:
id: { type: integer, readOnly: true }
name: { type: string }
company: { type: integer }
mask: { type: array, items: { type: integer } }
work_time_start: { type: string }
work_time_end: { type: string }
work_day_duration: { type: number, readOnly: true }
is_system: { type: boolean }
holidays: { type: array, items: { type: string, format: date } }
exception_holidays: { type: array, items: { type: string, format: date } }
creator: { type: integer, readOnly: true }
projects_count: { type: integer, readOnly: true }
created_at: { type: string, format: date-time, readOnly: true }
updated_at: { type: string, format: date-time, readOnly: true }
required: [name, mask, work_time_start, work_time_end]
# --- Настройки --------------------------------------------------------
ProjectSettings:
type: object
properties:
id: { type: integer, readOnly: true }
name: { type: string }
project: { type: integer, nullable: true }
state: { type: integer, nullable: true }
image: { type: string, nullable: true }
is_default: { type: boolean }
visibility: { type: string, enum: [global, private, public] }
company_id: { type: integer, nullable: true }
service_accounts: { type: array, items: { type: string } }
creator: { type: integer, readOnly: true }
created_at: { type: string, format: date-time, readOnly: true }
updated_at: { type: string, format: date-time, readOnly: true }
VisualProfile:
type: object
properties:
id: { type: integer, readOnly: true }
name: { type: string }
color: { type: string, description: "HEX-цвет, напр. #RRGGBB" }
project: { type: integer }
behavior: { type: object }
Comment:
type: object
properties:
id: { type: integer, readOnly: true }
text: { type: string }
task: { type: integer }
creator: { type: integer, readOnly: true }
created_at: { type: string, format: date-time, readOnly: true }
updated_at: { type: string, format: date-time, readOnly: true }
CommentCreate:
type: object
properties:
text: { type: string }
task: { type: integer }
required: [text, task]
# --- Системный журнал -------------------------------------------------
SystemLog:
type: object
properties:
id: { type: integer, readOnly: true }
project_id: { type: integer }
user_id: { type: integer, nullable: true }
created_at: { type: string, format: date-time, readOnly: true }
message_data: { type: object }
action: { type: string }
entity: { type: string }
changes: { type: object, description: "Только в detail-варианте" }
# --- Экспорт ----------------------------------------------------------
ExportProject:
type: object
properties:
export_fields: { type: array, items: { type: string } }
export_attrs: { type: array, items: { type: integer } }
fields_order: { type: array, items: { type: string } }
data: { type: array, items: { type: object } }
page_format: { type: string }
page_size: { type: string }
left_table_percent_width: { type: number, minimum: 0, maximum: 100 }
base_plan_ids: { type: array, items: { type: integer } }
margins: { type: array, items: { type: number } }
expanded_tasks: { type: array, items: { type: integer } }
header_text: { type: string }
footer_text: { type: string }
display_relations: { type: boolean }
date_from: { type: string, format: date }
date_to: { type: string, format: date }
fields_width: { type: object }
project_name: { type: string }
show_counter: { type: boolean }
font_size: { type: integer }
conditional_formatting: { type: array, items: { type: object } }
required: [export_fields, data]