Сквозные примеры
Примеры продолжают быстрый старт: переменные API, SERVICEAPP_TOKEN и CUSTOMER_ID уже заданы. Все данные демонстрационные. Используйте доступные ID своей организации. Перед каждой командой сохраняйте тело, ключ и версию. При превышении времени ожидания повторяйте только исходную команду с сохранёнными значениями; повторный запуск всего сценария с заново прочитанными версиями не является тем же HTTP-запросом.
Для новых независимых демонстраций меняйте RUN_ID. Для повтора текущей демонстрации сохраните прежний. Ни один пример не выполняет оплату, внешнее списание бонусов или платный расчёт маршрута.
Общие функции curl
export RUN_ID='demo-20261104-001'
get_api() {
curl --fail-with-body --silent --show-error --globoff \
--header "Authorization: Bearer $SERVICEAPP_TOKEN" \
--header 'Accept: application/json' "$API$1"
}
# Аргументы: путь, постоянный суффикс ключа, JSON-файл, версия (если нужна).
post_api() {
local path="$1" key="$2" file="$3" version="${4-}"
local -a version_header=()
if [ -n "$version" ]; then
version_header=(--header "If-Resource-Version: $version")
fi
curl --fail-with-body --silent --show-error --request POST \
--header "Authorization: Bearer $SERVICEAPP_TOKEN" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: $RUN_ID:$key" \
"${version_header[@]}" --data-binary "@$file" "$API$path"
}
get_api /context > context.json
jq '.data | {workshop_ids, scopes, features, timezone, currency}' context.json
# Выберите мастерскую из context.workshop_ids и сотрудника этой мастерской.
get_api /workshops | jq .
export WORKSHOP_ID='<INTEGER_WORKSHOP_ID>'
get_api "/users?filter[workshop_id]=$WORKSHOP_ID" | jq .
export TECHNICIAN_ID='<INTEGER_USER_ID>'
Замените условные значения целыми идентификаторами перед jq --argjson. Мастерская должна быть доступна токену и роли. Для создания акта в ней должен быть настроен допустимый открытый статус нужного типа и склад, выбираемый штатными правилами. Если в организации есть обязательные дополнительные поля, добавьте их по определениям custom-fields; пустые значения в примерах их не обходят.
1. Клиент → ремонтный акт
Клиент создаётся запросом из быстрого старта. Дополнительно нужны acts:write и acts:read, права создания/просмотра актов и доступа к клиенту и мастерской.
jq -n --argjson customer "$CUSTOMER_ID" --argjson workshop "$WORKSHOP_ID" \
'{type:"repair", customer_id:$customer, workshop_id:$workshop,
model_name:"Демонстрационный насос", serial_number:"00001234",
customer_note:"Требуется осмотр"}' > repair-act-create.json
post_api /acts repair-act-create repair-act-create.json > repair-act-response.json
export ACT_ID="$(jq -r '.data.id' repair-act-response.json)"
get_api "/acts/$ACT_ID" > repair-act-current.json
jq '.data | {id, type, status_id, version, links}' repair-act-current.json
Ответ создания — 201 с актом type=repair. ID акта отличается от номера документа. Ссылка links.edit ведёт на правильную карточку панели. Для монтажа и ТО используется тот же ресурс /acts с type=installation или maintenance, но действуют соответствующие модули и ограничения ресурса.
Работы и материалы добавляются через отдельные ресурсы состава; при добавлении нужна версия акта, а не каталожной позиции. Например, после выбора доступной работы:
export OPERATION_CATALOG_ID='<INTEGER_OPERATION_ID>'
get_api "/acts/$ACT_ID" > repair-act-before-line.json
export ACT_VERSION="$(jq -r '.data.version' repair-act-before-line.json)"
jq -n --argjson operation "$OPERATION_CATALOG_ID" \
'{operation_id:$operation,quantity:"1"}' > repair-operation-create.json
post_api "/acts/$ACT_ID/operations" repair-operation-create \
repair-operation-create.json "$ACT_VERSION" > repair-operation-response.json
get_api "/acts/$ACT_ID/operations" | jq .
Для выбора работы нужны доступ к её каталогу и права из контракта строк работ. Ручную цену в примере не передаём: используется штатное ценообразование. Для ручной цены достаточно обычного права изменения акта. При изменении/delete строки используйте её собственную version. Вложения можно добавить по примеру multipart.
Для перехода акта получите разрешённый status_id из reference/statuses, перечитайте акт и передайте его версию в /acts/{id}/status. Завершающий статус запускает штатные связанные эффекты: это не редактирование простого текстового поля. При активном внешнем списании UDS/Самоселл соответствующая команда вернёт 409 EXTERNAL_EFFECT_RECONCILIATION_REQUIRED.
2. Оборудование → план ТО → заявка → выезд → выполнение
Для этого сценария нужны доступ к ТОиР, создание/чтение оборудования и планов, заявки и чек-листы, акты и выезды, отметки выезда, чтение мастерских, сотрудников, справочников и журнала. Выберите scopes:
customers:read customers:write
equipment:read equipment:write
equipment_checklists:read equipment_checklists:write
maintenance_plans:read maintenance_plans:write
maintenance_schedules:read maintenance_schedules:write
maintenance_checklists:read maintenance_checklists:write
acts:read acts:write
visits:read visits:write
visit_events:read visit_events:write
workshops:read users:read reference:read api_operations:read
Также требуются соответствующие права сотрудника. Переход завершения должен быть разрешён текущему статусу акта ТО. Для простого примера используйте новую единицу без действующего договора, складских материалов и внешней лояльности.
Создать оборудование и обязательный пункт осмотра
jq -n --argjson customer "$CUSTOMER_ID" \
'{name:"Демонстрационный насос ТО",customer_id:$customer,
serial_number:"00005678"}' > equipment-create.json
post_api /equipment equipment-create equipment-create.json > equipment-response.json
export EQUIPMENT_ID="$(jq -r '.data.id' equipment-response.json)"
export EQUIPMENT_VERSION="$(jq -r '.data.version' equipment-response.json)"
cat > equipment-checklist-create.json <<'JSON'
{"description":"Проверить герметичность корпуса","is_required":true}
JSON
post_api "/equipment/$EQUIPMENT_ID/checklist-items" equipment-template \
equipment-checklist-create.json "$EQUIPMENT_VERSION" > equipment-checklist-response.json
Пункт оборудования — шаблон для последующего ТО. Сохранённый результат конкретной заявки читается и меняется через другой ресурс checklist-results.
Создать календарный план и получить заявку
Выберите дату начала демонстрации в context.timezone. Пример использует 4 ноября 2026 года; при работе в другой день замените даты и последующие интервалы согласованно.
export MAINTENANCE_DAY='2026-11-04'
jq -n --argjson equipment "$EQUIPMENT_ID" --argjson workshop "$WORKSHOP_ID" \
--arg day "$MAINTENANCE_DAY" \
'{title:"Ежемесячный осмотр",equipment_id:$equipment,workshop_id:$workshop,
trigger_type:"calendar",schedule_mode:"fixed",starts_on:$day,anchor_date:$day,
calendar_interval_value:1,calendar_interval_unit:"month",lead_time_days:0}' \
> maintenance-plan-create.json
post_api /maintenance-plans maintenance-plan-create \
maintenance-plan-create.json > maintenance-plan-response.json
export PLAN_ID="$(jq -r '.data.id' maintenance-plan-response.json)"
export PLAN_VERSION="$(jq -r '.data.version' maintenance-plan-response.json)"
printf '%s\n' '{}' > empty-command.json
post_api "/maintenance-plans/$PLAN_ID/generate" maintenance-plan-generate \
empty-command.json "$PLAN_VERSION" > maintenance-generated.json
export SCHEDULE_ID="$(jq -r '.data.id' maintenance-generated.json)"
get_api "/maintenance-schedules/$SCHEDULE_ID" > maintenance-schedule-current.json
export SCHEDULE_VERSION="$(jq -r '.data.version' maintenance-schedule-current.json)"
В выбранном календарном цикле generate возвращает связанную заявку; повтор исходной команды не генерирует второй цикл. Управляемые регламентом планы имеют дополнительные ограничения на изменение. Сам GET ничего не генерирует и не инициализирует.
Сформировать общий акт ТО и запланированный выезд
Время задаётся с явным смещением. Укажите реальный свободный интервал и участника, который может работать в мастерской. В примере выбран billable без договора. Для covered требуется действующий договор, покрывающий весь состав и дату.
export VISIT_START='2026-11-04T14:00:00+03:00'
export VISIT_END='2026-11-04T15:00:00+03:00'
jq -n --argjson customer "$CUSTOMER_ID" --argjson workshop "$WORKSHOP_ID" \
--argjson schedule "$SCHEDULE_ID" --arg version "$SCHEDULE_VERSION" \
--argjson technician "$TECHNICIAN_ID" --arg day "$MAINTENANCE_DAY" \
--arg starts "$VISIT_START" --arg ends "$VISIT_END" \
'{customer_id:$customer,workshop_id:$workshop,planned_date:$day,
maintenance_schedule_ids:[$schedule],
expected_versions:[{type:"maintenance-schedules",id:$schedule,version:$version}],
billing_mode:"billable",
visit:{starts_at:$starts,ends_at:$ends,crew_user_ids:[$technician],
planning_duration_minutes:60,address:"Демонстрационный адрес обслуживания"}}' \
> maintenance-plan-visit.json
post_api /maintenance-schedules/plan-visit maintenance-plan-visit \
maintenance-plan-visit.json > maintenance-plan-visit-response.json
export MAINTENANCE_ACT_ID="$(jq -r '.data.act_id' maintenance-plan-visit-response.json)"
export VISIT_ID="$(jq -r '.data.visit_id' maintenance-plan-visit-response.json)"
get_api "/acts/$MAINTENANCE_ACT_ID" | jq .
get_api "/visits/$VISIT_ID" | jq .
Здесь используется режим существующих заявок. Нельзя одновременно передать equipment_ids и maintenance_schedule_ids. У каждой выбранной записи есть expected_versions. visit=null не создаёт выезд; {} создаёт черновой выезд. При заданном времени требуются обе границы и непустая бригада. Максимальный атомарный состав — 100 единиц. Для создания выезда нужно разрешение visits:write.
Выполнить обязательный пункт и сохранить результат
get_api "/maintenance-schedules/$SCHEDULE_ID/checklist-results" > maintenance-checklist.json
jq '.data.items' maintenance-checklist.json
# Для одного добавленного пункта. Если обязательных пунктов больше, выполните каждый.
export CHECKLIST_ITEM_ID="$(jq -r '.data.items[0].id' maintenance-checklist.json)"
export CHECKLIST_ITEM_VERSION="$(jq -r '.data.items[0].version' maintenance-checklist.json)"
jq -n --argjson version "$CHECKLIST_ITEM_VERSION" \
'{version:$version,is_completed:true,note:"Герметичность проверена"}' \
> maintenance-checklist-update.json
post_api "/maintenance-schedules/$SCHEDULE_ID/checklist-results/$CHECKLIST_ITEM_ID/update" \
maintenance-checklist-update maintenance-checklist-update.json > maintenance-checklist-updated.json
get_api "/maintenance-schedules/$SCHEDULE_ID" > maintenance-before-execution.json
export SCHEDULE_VERSION="$(jq -r '.data.version' maintenance-before-execution.json)"
cat > maintenance-execution.json <<'JSON'
{"result":"Осмотр выполнен, неисправности не выявлены"}
JSON
post_api "/maintenance-schedules/$SCHEDULE_ID/execution" maintenance-execution \
maintenance-execution.json "$SCHEDULE_VERSION" > maintenance-execution-response.json
У пункта версия — целое число JSON, его update не принимает If-Resource-Version. У заявки версия — строка для заголовка. Неизвестный, скрытый или принадлежащий другой заявке пункт не принимается. Все обязательные пункты и поля должны быть заполнены до завершения.
Зафиксировать путь, прибытие и завершение выезда
get_api "/visits/$VISIT_ID" > visit-before-en-route.json
export VISIT_VERSION="$(jq -r '.data.version' visit-before-en-route.json)"
post_api "/visits/$VISIT_ID/en-route" visit-en-route \
empty-command.json "$VISIT_VERSION" > visit-en-route-response.json
get_api "/visits/$VISIT_ID" > visit-before-arrive.json
export VISIT_VERSION="$(jq -r '.data.version' visit-before-arrive.json)"
post_api "/visits/$VISIT_ID/arrive" visit-arrive \
empty-command.json "$VISIT_VERSION" > visit-arrive-response.json
get_api '/reference/statuses?filter[act_type]=maintenance&filter[completes_act]=true' | jq .
# Выберите завершающий статус ТО, допустимый из текущего статуса акта.
export MAINTENANCE_CLOSED_STATUS_ID='<INTEGER_ALLOWED_STATUS_ID>'
jq -n --argjson status "$MAINTENANCE_CLOSED_STATUS_ID" \
'{status_id:$status}' > visit-complete.json
get_api "/visits/$VISIT_ID" > visit-before-complete.json
export VISIT_VERSION="$(jq -r '.data.version' visit-before-complete.json)"
post_api "/visits/$VISIT_ID/complete" visit-complete \
visit-complete.json "$VISIT_VERSION" > visit-complete-response.json
export COMPLETE_OPERATION_ID="$(jq -r '.meta.operation_id' visit-complete-response.json)"
get_api "/visits/$VISIT_ID" | jq '.data.state'
get_api "/maintenance-schedules/$SCHEDULE_ID" | jq '.data | {status,completed_date,result}'
get_api "/equipment/$EQUIPMENT_ID" | jq '.data | {last_maintenance_date,next_maintenance_date}'
get_api "/maintenance-plans/$PLAN_ID" | jq .
get_api "/api-operations/$COMPLETE_OPERATION_ID" | jq .
Отметки en-route/arrive без occurred_at используют время принятия запроса. Завершение выезда переводит связанный акт в указанный завершающий статус и выполняет штатный жизненный цикл ТО: завершает связанные заявки, обновляет оборудование и цикл плана. Смотрите affected_resources и актуальные GET, а не только локальную отметку «выезд завершён». Для одного выбранного оборудования результатом будет completed у заявки и выезда. Уведомление в подключённый канал может отправляться отдельно после фиксации команды.
При 412 перечитайте ресурс и проверьте действие. При потере ответа complete повторите именно последний POST с прежним VISIT_VERSION, файлом visit-complete.json и ключом $RUN_ID:visit-complete; не выполняйте завершение под новым ключом для проверки исхода. Журнал и запись об операции позволят узнать результат без повторного изменения.
Повторное открытие — отдельная POST-команда /visits/{id}/reopen с актуальной версией выезда и допустимым незавершающим status_id. Она затрагивает связанный акт и историю ТО; это не изменение state через команду изменения. Подробности: отметки выезда, заявки ТО, планы.