Расчёт и применение маршрута выездов
Все операции. Авторизация. Запросы и повторы.
Все пути в этом файле дополняют /api/v1 на домене организации.
Описание и ограничения
Назначение
Проверяемый расчёт оптимизации/распределения выездов и отдельное применение предложения к расписанию.
Поля и назначение
| Публичное поле | Формат и смысл | Доступ |
|---|---|---|
id |
UUID | только чтение |
date, workshop_id, user_ids, visit_ids |
один день, мастерская и ограниченные наборы | входное поле |
mode, work_start, work_end, return_to_workshop |
optimize / distribute, HH:mm, логическое значение | входное поле |
status, progress, preview |
состояние и предложение | только чтение |
version |
меняется на advance/apply/cancel, непрозрачная строка | только чтение |
source_version |
отдельная непрозрачная строка; сохраняется на старте | только чтение |
operation_id |
ID расчётной операции | только чтение |
budget |
max_requests и max_matrix_pairs, положительные целые |
входное поле / только чтение |
usage |
requests и matrix_pairs, целые числа |
только чтение |
error |
null либо объект code/message; без сырых ошибок провайдера | только чтение |
Обязательность и null указаны в схемах каждой операции ниже.
Доступ и выборка
visit_route_plans:read/write и полномочия диспетчеризации; доступ к каждой мастерской/единице состава и отдельный разрешённый бюджет геосервиса.
Запросы и состояния
Для создания требуются date, workshop_id, mode, work_start, work_end, user_ids, visit_ids и budget. Work_start/work_end — HH:mm в context.timezone, внутри одного дня, начало раньше конца. user_ids — 1–100 уникальных сотрудников одной мастерской; для optimize ровно один. visit_ids — 1–100 уникальных доступных выездов. Это лимит пакета внешнего API, не ограничение числа выездов в панели. return_to_workshop=false по умолчанию. Переданные source/matrix/preview/status запрещены.
budget содержит ровно max_requests (1–1000 внешних HTTP-запросов route/matrix суммарно) и max_matrix_pairs (1–10000 пар матрицы). Это ограничение количества платных обращений, не обещание фиксированной цены в RUB. Оно дополнительно ограничивается доступной квотой подключения; денежный тариф геосервиса не меняется. Расход по лимиту резервируется до внешнего запроса и учитывает неизвестный результат. Повтор запроса шага не расходует бюджет снова; иной Idempotency-Key не даёт выполнить уже закреплённый шаг повторно.
Для платных шагов нужна отдельно настроенная дневная квота подключения. День считается по UTC. Без положительной квоты такие шаги запрещены. Расход относится к дню резервирования; поздний ответ, восстановление или отмена не переносят его на другой день. Неизвестный результат внешнего запроса не освобождает зарезервированный расход. Каждый новый шаг ограничен оставшимся бюджетом плана и дневной квотой.
Создание возвращает 201 с карточкой status=pending и сохранённым source_version, без оплачиваемого расчёта. status: pending/processing/ready/applied/cancelled/failed/needs_reconciliation. progress: { "completed_steps": 0, "total_steps": 4 }; total_steps может уточниться после разбивки дальних пар. preview=null до готовности, затем { "moves": [{"visit_id": 12, "crew_user_ids": [3], "starts_at": "…", "ends_at": "…"}], "unassigned_visit_ids": [] }; это только предложение, без сырых provider matrix/source. operation_id указывает на последний принятый шаг.
Advance/apply/cancel принимают {} и If-Resource-Version версии расчёта. Advance закрепляет один следующий шаг, отвечает 202 при фоновой обработке или 200 при уже готовом расчёте без нового эффекта; одновременно допускается один исполнитель. Исчерпание бюджета — failed с ROUTE_BUDGET_EXHAUSTED; неизвестный внешний исход — needs_reconciliation, без слепого повтора. Apply разрешён только для ready, перепроверяет source_version, текущие права, весь состав, окна и пересечения под блокировками, затем атомарно сохраняет предложение; устаревшее расписание — 409 ROUTE_SOURCE_CHANGED, новое действие требует нового расчёта. Устаревший If-Resource-Version самого плана — 412.
Если бюджета недостаточно до закрепления внешнего шага, advance возвращает 200 с карточкой status=failed и error.code=ROUTE_BUDGET_EXHAUSTED; расход не увеличивается, ложный ответ 202 не создаётся. error=null означает отсутствие сохранённой ошибки. Остальные значения ошибки преобразуются через фиксированный список разрешённых значений кодов и сообщений сервера.
Ответ 202 содержит карточку со status=processing, а также транспортный state=processing и ID принятой операции. Эта запись об операции неизменна: повтор запроса возвращает исходный 202 и после завершения шага. Текущее состояние читается через GET плана или журнал операции; новый advance готового плана без эффекта возвращает 200.
Apply возвращает 200 с карточкой applied и affected_resources выездов; GET карточки не делает геозапросов. Cancel прекращает только новые шаги, уже отправленный запрос доводится до учёта результата/неизвестного результата; 200 с cancelled, расход уже отправленного шага не обещается вернуть. Отмена applied не откатывает расписание и возвращает 409 ROUTE_PLAN_ALREADY_APPLIED.
Готовое предложение без изменений расписания не применяется: apply возвращает 409 ROUTE_PLAN_HAS_NO_CHANGES, сохраняя карточку ready. Неготовое или уже завершённое состояние возвращает 409 ROUTE_PLAN_NOT_READY.
Правила и связи
- Клиент получает ID и не может прислать отредактированную matrix/source/preview для apply.
- GET не запускает геозапросы. Каждый POST advance имеет собственный ключ шага; повтор шага не запрашивает платную матрицу повторно.
- Применение сравнивает актуальный состав/расписание и атомарно меняет назначения.
- Расчёт и сохранение расписания разделены; cancel расчёта не отменяет уже применённые выезды.
- Следуют существующим ограничениям бригад, окон и одной мастерской/дня.
Операции
| Метод | Путь | Назначение |
|---|---|---|
| POST | /visit-route-plans |
Создание серверного расчёта с явным бюджетом |
| GET | /visit-route-plans/{id} |
Текущее состояние и предложение |
| POST | /visit-route-plans/{id}/cancel |
Остановка незавершённого расчёта |
| POST | /visit-route-plans/{id}/advance |
Продолжение ограниченного шага расчёта |
| POST | /visit-route-plans/{id}/apply |
Применение актуального предложения |
POST /visit-route-plans
Создание серверного расчёта с явным бюджетом.
Доступ. Разрешения подключения: visit_route_plans:write. Права сотрудника: «Акты приёма оборудования → Изменение», «Акты приёма оборудования → Просмотр». Дополнительно действуют доступ к записям, мастерским, модулям и условия полей из описания ресурса выше.
Параметры
| Имя | Где | Тип | Обязательно | null | Ограничения |
|---|---|---|---|---|---|
Authorization |
заголовок | строка | да | нет | Bearer <SERVICEAPP_TOKEN> |
Idempotency-Key |
заголовок | строка | да | нет | не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._:-]+$ |
Тело запроса
Content-Type: application/json. Тело обязательно.
| Поле | Тип | Обязательно | null | Ограничения и смысл |
|---|---|---|---|---|
date |
строка (дата) | да | нет | — |
workshop_id |
целое число | да | нет | ≥ 1 |
mode |
строка | да | нет | Допустимо: "optimize", "distribute" |
work_start |
строка | да | нет | Шаблон: ^(?:[01]\d|2[0-3]):[0-5]\d$ |
work_end |
строка | да | нет | Шаблон: ^(?:[01]\d|2[0-3]):[0-5]\d$; Условие: after:work_start |
user_ids |
массив | да | нет | не менее 1 элементов; не более 100 элементов; Без повторов |
user_ids[] |
целое число | да | нет | ≥ 1 |
visit_ids |
массив | да | нет | не менее 1 элементов; не более 100 элементов; Без повторов |
visit_ids[] |
целое число | да | нет | ≥ 1 |
return_to_workshop |
логическое значение | нет | нет | — |
budget |
объект | да | нет | Только перечисленные ключи |
budget.max_requests |
целое число | да | нет | ≥ 1; ≤ 1000 |
budget.max_matrix_pairs |
целое число | да | нет | ≥ 1; ≤ 10000 |
Ответы
| HTTP | Назначение и схема |
|---|---|
| 201 | Создано. Полная схема и пример |
| 401 | Требуется действующий Bearer. Коды и формат ошибки |
| 403 | Недостаточно допуска. Коды и формат ошибки |
| 404 | Не найдено или недоступно. Коды и формат ошибки |
| 409 | Конфликт состояния или повтора. Коды и формат ошибки |
| 422 | Ошибка входных данных. Коды и формат ошибки |
| 429 | Лимит запросов, Retry-After. Коды и формат ошибки |
| 503 | Временно недоступно. Коды и формат ошибки |
| HTTP | Заголовок ответа | Тип | Описание |
|---|---|---|---|
| 201 | Location |
строка |
Каждый ответ содержит новый X-Request-Id в заголовке. Общие транспортные отказы и правила повтора действуют также при отказе до контроллера; см. ошибки.
У принятого POST Location и meta.operation_id указывают на журнал операции; ETag отсутствует. При повторе запроса сохраняются исходные data/meta, добавляется Idempotency-Replayed: true. Версии передавайте по назначению, указанному в описании ресурса.
Пример запроса
Запускается из каталога комплекта после настройки переменных в быстром старте. Замените демонстрационные идентификаторы 101 и UUID идентификаторами доступных записей. $RESOURCE_VERSION берётся из GET владельца версии, описанного выше. Для нового действия сохраните новый ключ; для повтора используйте прежний.
curl --silent --show-error --globoff --request POST \
"$API/visit-route-plans" \
--header "Authorization: Bearer $SERVICEAPP_TOKEN" \
--header "Accept: application/json" \
--header "Idempotency-Key: example:v1-visit-route-plans-create:001" \
--header "Content-Type: application/json" \
--data-binary '@examples/requests/v1.visit-route-plans.create.json'
GET /visit-route-plans/{id}
Текущее состояние и предложение.
Доступ. Разрешения подключения: visit_route_plans:read. Права сотрудника: «Акты приёма оборудования → Просмотр». Дополнительно действуют доступ к записям, мастерским, модулям и условия полей из описания ресурса выше.
Параметры
| Имя | Где | Тип | Обязательно | null | Ограничения |
|---|---|---|---|---|---|
Authorization |
заголовок | строка | да | нет | Bearer <SERVICEAPP_TOKEN> |
id |
путь | строка | да | нет | Шаблон: ^(?:[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12})$ |
If-None-Match |
заголовок | строка | нет | нет | — |
Ответы
| HTTP | Назначение и схема |
|---|---|
| 200 | Успех. Полная схема и пример |
| 304 | Представление не изменилось, без тела. Условный GET |
| 401 | Требуется действующий Bearer. Коды и формат ошибки |
| 403 | Недостаточно допуска. Коды и формат ошибки |
| 404 | Не найдено или недоступно. Коды и формат ошибки |
| 422 | Ошибка входных данных. Коды и формат ошибки |
| 429 | Лимит запросов, Retry-After. Коды и формат ошибки |
| 503 | Временно недоступно. Коды и формат ошибки |
| HTTP | Заголовок ответа | Тип | Описание |
|---|---|---|---|
| 200 | ETag |
строка | |
| 304 | ETag |
строка |
Каждый ответ содержит новый X-Request-Id в заголовке. Общие транспортные отказы и правила повтора действуют также при отказе до контроллера; см. ошибки.
Пример запроса
Запускается из каталога комплекта после настройки переменных в быстром старте. Замените демонстрационные идентификаторы 101 и UUID идентификаторами доступных записей. $RESOURCE_VERSION берётся из GET владельца версии, описанного выше. Для нового действия сохраните новый ключ; для повтора используйте прежний.
curl --silent --show-error --globoff \
"$API/visit-route-plans/ef62763b-87f9-4c90-83bb-e919f3c8e111" \
--header "Authorization: Bearer $SERVICEAPP_TOKEN" \
--header "Accept: application/json"
POST /visit-route-plans/{id}/cancel
Остановка незавершённого расчёта.
Доступ. Разрешения подключения: visit_route_plans:write. Права сотрудника: «Акты приёма оборудования → Изменение», «Акты приёма оборудования → Просмотр». Дополнительно действуют доступ к записям, мастерским, модулям и условия полей из описания ресурса выше.
Параметры
| Имя | Где | Тип | Обязательно | null | Ограничения |
|---|---|---|---|---|---|
Authorization |
заголовок | строка | да | нет | Bearer <SERVICEAPP_TOKEN> |
id |
путь | строка | да | нет | Шаблон: ^(?:[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12})$ |
Idempotency-Key |
заголовок | строка | да | нет | не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._:-]+$ |
If-Resource-Version |
заголовок | строка | да | нет | не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._~-]+$ |
Тело запроса
Content-Type: application/json. Тело обязательно.
Тело — пустой JSON-объект {}. Другие ключи не принимаются.
Ответы
| HTTP | Назначение и схема |
|---|---|
| 200 | Успех. Полная схема и пример |
| 401 | Требуется действующий Bearer. Коды и формат ошибки |
| 403 | Недостаточно допуска. Коды и формат ошибки |
| 404 | Не найдено или недоступно. Коды и формат ошибки |
| 409 | Конфликт состояния или повтора. Коды и формат ошибки |
| 412 | Версия устарела. Коды и формат ошибки |
| 422 | Ошибка входных данных. Коды и формат ошибки |
| 428 | Нужно предусловие версии. Коды и формат ошибки |
| 429 | Лимит запросов, Retry-After. Коды и формат ошибки |
| 503 | Временно недоступно. Коды и формат ошибки |
| HTTP | Заголовок ответа | Тип | Описание |
|---|---|---|---|
| 200 | Location |
строка |
Каждый ответ содержит новый X-Request-Id в заголовке. Общие транспортные отказы и правила повтора действуют также при отказе до контроллера; см. ошибки.
У принятого POST Location и meta.operation_id указывают на журнал операции; ETag отсутствует. При повторе запроса сохраняются исходные data/meta, добавляется Idempotency-Replayed: true. Версии передавайте по назначению, указанному в описании ресурса.
Пример запроса
Запускается из каталога комплекта после настройки переменных в быстром старте. Замените демонстрационные идентификаторы 101 и UUID идентификаторами доступных записей. $RESOURCE_VERSION берётся из GET владельца версии, описанного выше. Для нового действия сохраните новый ключ; для повтора используйте прежний.
curl --silent --show-error --globoff --request POST \
"$API/visit-route-plans/ef62763b-87f9-4c90-83bb-e919f3c8e111/cancel" \
--header "Authorization: Bearer $SERVICEAPP_TOKEN" \
--header "Accept: application/json" \
--header "Idempotency-Key: example:v1-visit-route-plans-cancel:001" \
--header "If-Resource-Version: $RESOURCE_VERSION" \
--header "Content-Type: application/json" \
--data-binary '@examples/requests/v1.visit-route-plans.cancel.json'
POST /visit-route-plans/{id}/advance
Продолжение ограниченного шага расчёта.
Доступ. Разрешения подключения: visit_route_plans:write. Права сотрудника: «Акты приёма оборудования → Изменение», «Акты приёма оборудования → Просмотр». Дополнительно действуют доступ к записям, мастерским, модулям и условия полей из описания ресурса выше.
Параметры
| Имя | Где | Тип | Обязательно | null | Ограничения |
|---|---|---|---|---|---|
Authorization |
заголовок | строка | да | нет | Bearer <SERVICEAPP_TOKEN> |
id |
путь | строка | да | нет | Шаблон: ^(?:[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12})$ |
Idempotency-Key |
заголовок | строка | да | нет | не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._:-]+$ |
If-Resource-Version |
заголовок | строка | да | нет | не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._~-]+$ |
Тело запроса
Content-Type: application/json. Тело обязательно.
Тело — пустой JSON-объект {}. Другие ключи не принимаются.
Ответы
| HTTP | Назначение и схема |
|---|---|
| 200 | Успех. Полная схема и пример |
| 202 | Принято, итог в журнале. Полная схема и пример |
| 401 | Требуется действующий Bearer. Коды и формат ошибки |
| 403 | Недостаточно допуска. Коды и формат ошибки |
| 404 | Не найдено или недоступно. Коды и формат ошибки |
| 409 | Конфликт состояния или повтора. Коды и формат ошибки |
| 412 | Версия устарела. Коды и формат ошибки |
| 422 | Ошибка входных данных. Коды и формат ошибки |
| 428 | Нужно предусловие версии. Коды и формат ошибки |
| 429 | Лимит запросов, Retry-After. Коды и формат ошибки |
| 503 | Временно недоступно. Коды и формат ошибки |
| HTTP | Заголовок ответа | Тип | Описание |
|---|---|---|---|
| 200 | Location |
строка | |
| 202 | Location |
строка | |
| 202 | Retry-After |
строка | |
| 401 | Location |
строка | |
| 403 | Location |
строка | |
| 404 | Location |
строка | |
| 409 | Location |
строка | |
| 412 | Location |
строка | |
| 422 | Location |
строка | |
| 428 | Location |
строка | |
| 429 | Location |
строка | |
| 503 | Location |
строка |
Каждый ответ содержит новый X-Request-Id в заголовке. Общие транспортные отказы и правила повтора действуют также при отказе до контроллера; см. ошибки.
У принятого POST Location и meta.operation_id указывают на журнал операции; ETag отсутствует. При повторе запроса сохраняются исходные data/meta, добавляется Idempotency-Replayed: true. Версии передавайте по назначению, указанному в описании ресурса.
Пример запроса
Запускается из каталога комплекта после настройки переменных в быстром старте. Замените демонстрационные идентификаторы 101 и UUID идентификаторами доступных записей. $RESOURCE_VERSION берётся из GET владельца версии, описанного выше. Для нового действия сохраните новый ключ; для повтора используйте прежний.
curl --silent --show-error --globoff --request POST \
"$API/visit-route-plans/ef62763b-87f9-4c90-83bb-e919f3c8e111/advance" \
--header "Authorization: Bearer $SERVICEAPP_TOKEN" \
--header "Accept: application/json" \
--header "Idempotency-Key: example:v1-visit-route-plans-advance:001" \
--header "If-Resource-Version: $RESOURCE_VERSION" \
--header "Content-Type: application/json" \
--data-binary '@examples/requests/v1.visit-route-plans.advance.json'
POST /visit-route-plans/{id}/apply
Применение актуального предложения.
Доступ. Разрешения подключения: visit_route_plans:write. Права сотрудника: «Акты приёма оборудования → Изменение», «Акты приёма оборудования → Просмотр». Дополнительно действуют доступ к записям, мастерским, модулям и условия полей из описания ресурса выше.
Параметры
| Имя | Где | Тип | Обязательно | null | Ограничения |
|---|---|---|---|---|---|
Authorization |
заголовок | строка | да | нет | Bearer <SERVICEAPP_TOKEN> |
id |
путь | строка | да | нет | Шаблон: ^(?:[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12})$ |
Idempotency-Key |
заголовок | строка | да | нет | не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._:-]+$ |
If-Resource-Version |
заголовок | строка | да | нет | не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._~-]+$ |
Тело запроса
Content-Type: application/json. Тело обязательно.
Тело — пустой JSON-объект {}. Другие ключи не принимаются.
Ответы
| HTTP | Назначение и схема |
|---|---|
| 200 | Успех. Полная схема и пример |
| 401 | Требуется действующий Bearer. Коды и формат ошибки |
| 403 | Недостаточно допуска. Коды и формат ошибки |
| 404 | Не найдено или недоступно. Коды и формат ошибки |
| 409 | Конфликт состояния или повтора. Коды и формат ошибки |
| 412 | Версия устарела. Коды и формат ошибки |
| 422 | Ошибка входных данных. Коды и формат ошибки |
| 428 | Нужно предусловие версии. Коды и формат ошибки |
| 429 | Лимит запросов, Retry-After. Коды и формат ошибки |
| 503 | Временно недоступно. Коды и формат ошибки |
| HTTP | Заголовок ответа | Тип | Описание |
|---|---|---|---|
| 200 | Location |
строка |
Каждый ответ содержит новый X-Request-Id в заголовке. Общие транспортные отказы и правила повтора действуют также при отказе до контроллера; см. ошибки.
У принятого POST Location и meta.operation_id указывают на журнал операции; ETag отсутствует. При повторе запроса сохраняются исходные data/meta, добавляется Idempotency-Replayed: true. Версии передавайте по назначению, указанному в описании ресурса.
Пример запроса
Запускается из каталога комплекта после настройки переменных в быстром старте. Замените демонстрационные идентификаторы 101 и UUID идентификаторами доступных записей. $RESOURCE_VERSION берётся из GET владельца версии, описанного выше. Для нового действия сохраните новый ключ; для повтора используйте прежний.
curl --silent --show-error --globoff --request POST \
"$API/visit-route-plans/ef62763b-87f9-4c90-83bb-e919f3c8e111/apply" \
--header "Authorization: Bearer $SERVICEAPP_TOKEN" \
--header "Accept: application/json" \
--header "Idempotency-Key: example:v1-visit-route-plans-apply:001" \
--header "If-Resource-Version: $RESOURCE_VERSION" \
--header "Content-Type: application/json" \
--data-binary '@examples/requests/v1.visit-route-plans.apply.json'