Перейти к содержимому
СервисАпп API
На этой странице

Сквозные примеры

Примеры продолжают быстрый старт: переменные 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 через команду изменения. Подробности: отметки выезда, заявки ТО, планы.