/sites/default/files/2026-03/Mrgav.png

Эта статья о том, как в реальном проекте на Drupal собрать устойчивый рабочий процесс: данные по товарам и заказам синхронизируются с Business.ru, отправки оформляются через Catapulto, а статусы и треки доходят до нужных систем без ручной рутины.

1. С чего начинали

Когда в проекте одновременно живут сайт, учетная система и внешний логистический сервис, самая частая проблема — не «нет интеграции», а «интеграция есть, но она неудобная и хрупкая». Часть действий приходится делать вручную, статусы запаздывают, а треки дописываются отдельными задачами.

Поэтому цель была очень практичной: выстроить нормальную последовательность этапов, в которой каждому участнику процесса понятно, что происходит дальше. Пользователь оформляет заказ, менеджер видит прозрачный путь отправки, а система сама поддерживает актуальные данные между Drupal, Business.ru и Catapulto.

Ключевая идея: не «один большой скрипт», а несколько понятных шагов, где каждый закрывает свою задачу и аккуратно передает данные следующему этапу.

2. Общая картина решения

Архитектуру разделили на три слоя. Сначала сделали стабильный API-слой Business.ru, затем разложили обменные процессы по отдельным сценариям, и только после этого встроили Catapulto как продолжение маршрута после оформления заказа.

 

1-integration-scheme
Схема 1. Как связаны Drupal, Business.ru, Catapulto и сервисы обогащения данных

 

Такой порядок оказался важным: сначала «фундамент», потом прикладные процессы, и только потом тонкая операционная логика с доставкой, треками и фоновыми проверками.

3. Этап 1: единый API-клиент для Business.ru

Первым шагом мы оперлись на единый класс Business_ru_api_lib (core_api_connector.php), чтобы все запросы работали по единым правилам: подпись параметров, проверка подписи ответа, единая обработка ошибок, обновление токена через repair().

public function request($action, $model, $params = array()) {
  $params['app_id'] = $this->app_id;
  ksort($params);
  $params_string = http_build_query($params);

  $params = array();
  $params['app_psw'] = md5($this->token . $this->secret . $params_string);
  $params_string .= '&' . http_build_query($params);

  $url = $this->address . "/api/rest/" . $model . ".json";
  // ... cURL вызов ...

  if ($status_code == 200) {
    $result = json_decode($result, true);
    $app_psw = $result['app_psw'];
    unset($result['app_psw']);

    if (md5($this->token . $this->secret . json_encode($result)) == $app_psw) {
      $this->token = $result['token'];
      return $result;
    }
  }
  return array("status" => "error");
}

Этот шаг может казаться «техническим», но именно он дал предсказуемость дальше: все сценарии обмена используют одну и ту же точку входа и одинаково себя ведут при сбоях.

4. Этап 2: обмен с Business.ru — разложили на понятные сценарии

Когда базовый слой стабилен, следующий логичный шаг — разделить обмен по задачам. В проекте это сделали через отдельные скрипты: клиенты/заказы, каталог/остатки и статусы/треки.

 

2-exchange-scenarios
Схема 2. Отдельные сценарии обмена с Business.ru

 

4.1 Сайт -> Business.ru: клиенты и заказы

Скрипт portal_out_sync.php передает новых клиентов и заказы. На практике самым полезным решением стала защита от дублей контрагентов: сначала ищем по ID сайта, а если его нет — по email, телефону и ФИО.

$search_fields = [
  'email' => strtolower($mail),
  'phone' => $phone,
  'name' => $name,
];

foreach ($search_fields as $search_field => $value) {
  $response = $api->request("get", "partners", [
    'extended' => 1,
    'with_additional_fields' => 1,
    $search_field => $value,
  ]);

  if (!empty($response['result'][0])) {
    $cid = $response['result'][0]['id'];
    break;
  }
}

Дополнительно смаппили локальные коды доставок и оплат в справочники Business.ru, чтобы в учетной системе и на сайте этапы заказа читались одинаково.

4.2 Business.ru -> сайт: каталог, цены, остатки

Скрипт portal_in_sync.php постранично грузит товары, цены и остатки. Остатки считаются по складам с учетом резерва, а цена выбирается по нужному типу прайса.

$response = $api->request("get", "goods", [
  'with_additional_fields' => 1,
  'with_barcodes' => 1,
  'with_remains' => 1,
  'with_prices' => 1,
  'limit' => 250,
  'page' => $page,
]);

foreach ($product['prices'] as $item) {
  if ($item['price_type']['id'] == $selling_retail_price_type_id) {
    $price = $item['price'];
    break;
  }
}

После того как обмен клиентами, заказами, каталогом и остатками стал стабильным, стало возможно перейти к следующему шагу: автоматизировать саму отправку, а не только хранение ее результата.

5. Этап 3: Catapulto как продолжение процесса после оформления заказа

 

3-shipping-way-to-send
Схема 3. Путь от выбора доставки до создания отправления

 

5.1 Что видит пользователь

В форме оформления заказа подключается виджет Catapulto. Пользователь выбирает вариант доставки, а параметры выбора сохраняются в поле field_catapulto в JSON.

$out .= '<span id="catapulto-widget-open" class="btn-green btn-small">Выбрать способ доставки</span>';
$out .= '<div id="for_catapultowidget" weight="' . $weight . '"></div>';
$out .= '<script type="module" crossorigin src="/sites/all/libraries/CatapultoWidget/release/catapulto-widget.js"></script>';
$out .= '<script type="text/javascript" src="/sites/all/modules/checkout_custom/js/shipping_widget.js"></script>';

В JS добавили практичную логику расчета грузомест по весовым диапазонам, чтобы оценка доставки была ближе к реальному отправлению.

if (weight < 5) {
  cargo = [{ height: 200, length: 200, width: 300, quantity: 1, weight: weight * 1000 }];
} else if (weight >= 25 && weight < 36) {
  cargo = [
    { height: 600, length: 400, width: 400, quantity: 1, weight: (weight * 1000)/2 },
    { height: 600, length: 400, width: 400, quantity: 1, weight: (weight * 1000)/2 }
  ];
}

5.2 Что делает менеджер

На странице /custom_catapulto менеджер подтверждает параметры отправки и, при необходимости, корректирует их перед созданием shipment. Для этого используется отдельное поле field_catapulto_custom.

Важный момент: мы не перезаписываем пользовательский выбор, а сохраняем менеджерскую версию отдельно. Благодаря этому всегда понятно, что было выбрано изначально и что ушло в отправку.

6. Этап 4: создание отправления и фиксация результата

После подтверждения отправка создается в Catapulto API: сначала контакт, затем shipment. Возвращенные идентификаторы записываются в заказ Drupal и передаются в Business.ru по статусу.

$response = _custom_catapulto_curl_post($apiUrl . "/contact", $receiver_contact, $token);

if ($response['code'] == 201) {
  $shipment['receiver_contact_id'] = $response['data']['id'];
  $response = _custom_catapulto_curl_post($apiUrl . "/shipment", $shipment, $token);
}

if ($response['code'] === 201) {
  $order->field_catapulto_track['und'][0]['value'] = $response['data']['key'];
  $order->field_catapulto_shipment_id['und'][0]['value'] = $response['data']['number'];
  node_save($order);
}

На этом шаге процесс не заканчивается: отправление создано, но его нужно сопровождать до завершения. Поэтому следующий этап — фоновые проверки и синхронизация.

 

4-cron-support-streams-statuses
Схема 4. Как cron поддерживает синхронизацию треков и статусов

 

7. Этап 5: фоновые задачи и «дотягивание» данных

В hook_cron() заложены две задачи: обновление треков/статусов по активным отправкам и дозапись треков в Business.ru, если где-то они остались пустыми.

function custom_catapulto_cron() {
  _custom_catapulto_update_status_task();
  _custom_catapulto_sync_tracking_to_business_ru_task();
}

Именно этот блок делает интеграцию «живой»: даже если в момент отправки произошла частичная ошибка, система позже корректно выравнивает состояние и возвращает процесс в норму.

8. Дополнительный шаг: качество адресов через DaData

Чтобы снизить адресные неточности, добавлен запрос к DaData по fias_id. Это улучшает отображение адреса и уменьшает спорные случаи на этапе отправки.

$response = drupal_http_request(
  'https://suggestions.dadata.ru/suggestions/api/4_1/rs/findById/address',
  array(
    'method' => 'POST',
    'data' => json_encode(array('query' => $fias_id)),
    'headers' => array(
      'Content-Type' => 'application/json',
      'Authorization' => 'Token ' . $api_key,
    ),
  )
);

9. Что в итоге изменилось в работе

БылоСтало
Разрозненные действия между системамиЕдиная последовательность этапов от оформления до финального статуса
Ручные корректировки статусов и трековАвтоматическая синхронизация + фоновые довороты данных
Риск дублей контрагентовПоиск и связывание по ID + email + телефон + ФИО
Непрозрачная логика отправкиОтдельный интерфейс подтверждения менеджером

Главный практический результат: процесс стал предсказуемым и «человечным» не только для клиента, но и для команды. У каждого этапа есть понятный вход, понятный выход и понятная точка ответственности.

10. Что улучшать дальше

  1. Добавить централизованный retry/backoff для внешних API: так мы уменьшаем количество «потерянных» операций при кратковременных сбоях сети или ограничениях провайдера.
  2. Сделать отдельный журнал интеграционных событий с correlation id: это ускорит разбор инцидентов, потому что путь одного заказа можно будет проследить сквозь все системы по одному идентификатору.
  3. Добавить smoke-тесты для критичных сценариев: это позволит ловить регрессии до релиза и снижать риск незаметной поломки обмена после изменений в модуле.

11. Как можно усилить процесс с помощью искусственного интеллекта

После стабилизации базовой интеграции следующий логичный шаг — добавить ИИ не «ради моды», а в те точки, где он снижает ручную нагрузку и сокращает количество ошибок.

 

5-ai-enhanced-integration
Схема 5. Где ИИ усиливает процесс интеграций

 

  1. Умная проверка адресов перед отправкой: модель может анализировать адрес, индекс, город и выбранный способ доставки, находить противоречия (например, несоответствие индекса городу) и предлагать корректный вариант до создания shipment.
  2. Автоматическая классификация интеграционных ошибок: ИИ-слой над логами может группировать сбои по типам (лимиты API, адресные ошибки, несогласованные статусы), показывать приоритет и сразу предлагать рекомендуемое действие для менеджера или разработчика.
  3. Прогноз проблемных заказов: на основе истории отправок можно предсказывать риск задержки, возврата или ручной доработки и заранее поднимать такие заказы в очередь оператора.
  4. Интеллектуальный контроль статусов: если заказ «завис» между этапами дольше нормы, ИИ может автоматически формировать задачу, комментарий или уведомление с контекстом по заказу и списком вероятных причин.
  5. Помощник менеджера в интерфейсе: встроенный AI-ассистент может кратко объяснять, почему система предлагает тот или иной тариф/маршрут, и подсказывать оптимальный следующий шаг без перехода между несколькими системами.

Важно внедрять эти улучшения поэтапно: сначала как рекомендательный слой (подсказки), затем как полуавтоматические действия с подтверждением менеджера, и только после накопления точности — как полностью автоматизированные правила для узких сценариев.

11.1 Примеры LLM-моделей под задачи процесса

Ниже примеры того, как можно подобрать модели по роли, чтобы получить баланс качества, скорости и стоимости:

  • Для AI-помощника менеджера (объяснение тарифа, формулировка комментария по заказу): GPT-4.1 или Claude 3.5 Sonnet.
  • Для массовой классификации логов и инцидентов в фоне: GPT-4o mini или Llama 3.1 70B Instruct.
  • Для on-prem/self-hosted контура с чувствительными данными: Llama 3.1 70B Instruct или Qwen2.5 72B Instruct.

Практичный путь внедрения: начать с одной модели в режиме «подсказок» для операторов, замерить качество и экономию времени, затем расширить на автоматическую приоритизацию ошибок и SLA-контроль.

Добавить комментарий