Эта статья о том, как в реальном проекте на Drupal собрать устойчивый рабочий процесс: данные по товарам и заказам синхронизируются с Business.ru, отправки оформляются через Catapulto, а статусы и треки доходят до нужных систем без ручной рутины.
1. С чего начинали
Когда в проекте одновременно живут сайт, учетная система и внешний логистический сервис, самая частая проблема — не «нет интеграции», а «интеграция есть, но она неудобная и хрупкая». Часть действий приходится делать вручную, статусы запаздывают, а треки дописываются отдельными задачами.
Поэтому цель была очень практичной: выстроить нормальную последовательность этапов, в которой каждому участнику процесса понятно, что происходит дальше. Пользователь оформляет заказ, менеджер видит прозрачный путь отправки, а система сама поддерживает актуальные данные между Drupal, Business.ru и Catapulto.
Ключевая идея: не «один большой скрипт», а несколько понятных шагов, где каждый закрывает свою задачу и аккуратно передает данные следующему этапу.
2. Общая картина решения
Архитектуру разделили на три слоя. Сначала сделали стабильный API-слой 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 — разложили на понятные сценарии
Когда базовый слой стабилен, следующий логичный шаг — разделить обмен по задачам. В проекте это сделали через отдельные скрипты: клиенты/заказы, каталог/остатки и статусы/треки.
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 как продолжение процесса после оформления заказа
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);
}На этом шаге процесс не заканчивается: отправление создано, но его нужно сопровождать до завершения. Поэтому следующий этап — фоновые проверки и синхронизация.
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. Что улучшать дальше
- Добавить централизованный retry/backoff для внешних API: так мы уменьшаем количество «потерянных» операций при кратковременных сбоях сети или ограничениях провайдера.
- Сделать отдельный журнал интеграционных событий с correlation id: это ускорит разбор инцидентов, потому что путь одного заказа можно будет проследить сквозь все системы по одному идентификатору.
- Добавить smoke-тесты для критичных сценариев: это позволит ловить регрессии до релиза и снижать риск незаметной поломки обмена после изменений в модуле.
11. Как можно усилить процесс с помощью искусственного интеллекта
После стабилизации базовой интеграции следующий логичный шаг — добавить ИИ не «ради моды», а в те точки, где он снижает ручную нагрузку и сокращает количество ошибок.
- Умная проверка адресов перед отправкой: модель может анализировать адрес, индекс, город и выбранный способ доставки, находить противоречия (например, несоответствие индекса городу) и предлагать корректный вариант до создания shipment.
- Автоматическая классификация интеграционных ошибок: ИИ-слой над логами может группировать сбои по типам (лимиты API, адресные ошибки, несогласованные статусы), показывать приоритет и сразу предлагать рекомендуемое действие для менеджера или разработчика.
- Прогноз проблемных заказов: на основе истории отправок можно предсказывать риск задержки, возврата или ручной доработки и заранее поднимать такие заказы в очередь оператора.
- Интеллектуальный контроль статусов: если заказ «завис» между этапами дольше нормы, ИИ может автоматически формировать задачу, комментарий или уведомление с контекстом по заказу и списком вероятных причин.
- Помощник менеджера в интерфейсе: встроенный 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-контроль.
Добавить комментарий