Коннекторы служб доставки добавляют логистические сервисы с возможностью отправки посылок, отслеживания доставки, распечатки этикеток и расширения формы обработки заказа в CRM.
Создание службы доставки
Работа с коннкеторами для служб доставки ведётся в разделе "Службы доставки" по кнопке "Файлы". Сами файлы коннекторов располагаются в папке delivery
вашего хака. Название файла может состоять из маленьких латинских букв и цифр. Оно обязательно должно начинаться с буквы, а не цифры. Нельзя использовать заглавные буквы и спецсимволы, в том числе дефис или знак подчёркивания.
В момент создания коннектора, в файле автоматически создаётся соответствующий класс. Название класса состоит из слова delivery
, названия хака и названия коннектора, разделённых знаком подчёркивания, например: delivery_dhl
. Коннектор должен наследоваться от класса deliverybase
.
class delivery_dhl extends deliverybase { … }
Класс службы может реализовывать следующие поля и функции:
track()
- трекинг посылки, основа работы служб доставки.calc()
- подсчёт стоимости и сроков доставки.send()
- отправка посылки в службу доставки.$cansend
- поддерживается отправка посылок.$canauto
- поддерживается автоматическая отправка посылок.bulkprint()
- массовая распечатка этикеток.$canbulk
- поддерживается массовая распечатка этикеток.options()
- доступные кнопки службы доставки.actions()
- реализация действий для кнопок службы доставки.block()
- контент-блок и дополнительная форма для службы доставки.ajax()
- реализация действий и загрузки данных для контент-блока._courier( $id )
- возвращает внутреннее имя курьера по ID из поляcourier
для аналитики._tariff( $id )
- возвращает внутреннее имя тарифа по ID из поляtariff
для аналитики.
Обязательной реализации не существует, но рекомендуется как минимум реализация трекинга.
Подключение к системе
Коннекторы не появятся в списке служб доставки автоматически, вам нужно указать их через файл запуска. Для этого в массиве инициализации укажите ключ delivery
, а в качестве значения - список доступных в вашем хаке коннекторов. Например:
return [ /* другие команды инициализации */ 'delivery' => [ 'cdek', 'dhl', 'ponyexpress' ], ];
Трекинг
Основной функционал любой службы доставки - это отслеживание посылок. Функция track
должна принимать на вход два параметра: трек-код и массив с заказом. В качестве результата должна возвращать ассоциативный массив с историей статусов, текущим статусом посылки и временем следующей проверки.
return [ 'next' => time() + 43000, // Время следующей проверки, UNIX timestamp 'status' => $status, // Текущий статус посылки 'time' => $tm, // Время изменения статуса, UNIX timesramp 'stage' => $stage // Массив статусов ];
Поле status
может принимать одно из следующих числовых значений:
wait
— посылка ожидает отправки.transfer
— посылка находится в пути, переводится в статус "Доставка".problem
— при доставке посылки возникли проблемы, требуется реакция поставщика.delivered
— посылка прибыла в пункт назначения, переводится в статус "Доставлено".paid
— посылка успешно выкуплена, переводится в статус "Оплачено".return
— посылка возвращена или утилизирована, переводится в статус "Возврат".comment
— произвольный комментарий, не меняющий состояние посылки.
Массив stage должен состоять из записей движения заказа. Записи рекомендуется отсортировать от самой старой к самой свежей. Каждая запись может содержать следующие поля:
status
- числовой идентификатор статуса.time
- время возникновения статуса.country
- двух-символьный ISO-код страны, в которой возник статус.zip
- индекс или ZIP-код отделения связи, на котором возник статус.city
- город, в котором возник статус.comment
- произвольный текстовый комментарий к статусу.md5
- уникальный хеш статуса, по которому проводится проверка дубликатов.
Подготовка массива статусов и определения конечного статуса и времени может выглядеть вот так:
$stage = []; $tm = $status = 0; foreach ( $info as $i ) { // Make the stage $s = [ 'status' => $this->s2i[$i['status']], 'time' => $core->text->number( $i['time'] ), 'country' => $core->text->link( $i['country'] ), 'zip' => $core->text->anum( $i['zip'] ), 'city' => $core->text->line( $i['city'] ), 'comment' => $core->text->line( $i['comment'] ), ]; // Add stage to the list $tm = $s['time']; $status = $s['status']; $s['md5'] = md5( $s['status'].$s['time'].$s['country'].$s['zip'].$s['city'].$s['comment'] ); $stage[] = $s; }
Рекомендуется ознакомиться с примерами реализации механизмов проверки статусов посылок, которые доступны в системе. Код модулей интеграции со службами доставки открыт. Файлы располагаются в папке core/delivery
вашей платформы.
Калькулятор стоимости и сроков
Калькулятор стоимости и сроков позволяет подставить цену и себестоимость доставки в посылку. Функция calc
получает на вход массив с данными заказа. В качестве результата должна вызывать функцию calcs
с четырьмя параметрами: цена доставки, данные заказа, минимальный срок, максимальный срок. Обязательным параметром является только цена доставки.
Пример простой реализации функции:
public function calc( $o ) { $price = $this->somemagic( $o ); return $this->calcs( $price, $o ); }
Пример реализации функции со сроками доставки:
public function calc( $o ) { $price = $this->_price( $o ); $term = $this->_term( $o ); return $this->calcs( $price, $o, $term['min'], $term['max'] ); }
Отправка посылки
Автоматическая отправка посылки выполняется через функцию send
, которая принимает на вход массив данных заказа и должна возвращать true
или false
в зависимости от успеха.
В процессе работы, функция сама должна отредактировать заказ и внести в него трек-код и иные необходимые данные. Это реализуется функцией $core->lead->edit()
с указанием трек-кода в параметре track
и активацией трекинга в параметре trackon
.
$core->lead->edit( $o['order_id'], [ 'track' => $code, 'trackon' => 1 ] );
Пример реализации функции отправки:
public function send( $o ) { $code = $this->somemagic( $o ); if ( $code ) { $this->core->lead->edit( $o['order_id'], [ 'track' => $code, 'trackon' => 1 ] ); return true; } else return false; }
Чтобы активировать автоматическую отправку посылок, укажите в начале объявления класса:
public $canauto = true; public $cansend = true;
Печать бланков
Массовая распечатка бланков реализуется через функцию bulkprint
, которая принимает на вход массив трек-кодов посылок. При этом ключ массива - ID заказа в системе. Функция должна возвращать массив ссылок или путей к файлам, в которых хранятся готовые бланки. Автоматика сама скачает нужные бланки, сформирует из них архив и выдаст пользователю.
Пример реализации функции массовой распечатки:
public function bulkprint( $ids ) { $files = []; foreach ( $ids as $i ) $files[] = $this->_print( $i ); return $files; }
Чтобы активировать массовую распечатку бланков, укажите в начале объявления класса:
public $canprint = true;
Также, хорошим тоном будет завести функцию print
, которая принимает на вход массив данных заказа и возвращает ссылку на файл с бланком. Пример реализации такой функции:
public function print( $o ) { if ( $o['order_status'] > 9 ) return false; if ( $o['order_status'] < 6 ) return false; if ( ! $o['track_code'] ) return false; return $this->_print( $o['track_code'] ); }
Кнопки действий
Кнопки действий добавляются в правую часть формы заказа под блоком с техническими данными (метки, гео, источники). С помощью этих кнопок вы можете вызывать внутренние действия вашей службы доставки, такие как отправка посылки, распечатка документов нужного типа или отмена отправки посылки.
За список действий отвечает функция options
, которая принимает на вход один параметр - массив заказа. Она должна возвращать массив, в котором содержится описание пунктов меню. Каждый пункт меню - это массив, содержащий поля:
name
- заголовок пункта меню, например "Отправить посылку".short
- краткий заголовок, который отображается в списке заказов, например "Отправить".icon
- название иконки Font Awesome, которая прикрепляется к пункту меню, напримерfa-paper-plane
.confirm
- текст всплывающего окна с подтверждением действия, например "Вы уверены, что хотите отправить эту посылку?"url
- ссылка на само действие.
Ссылку на действие нужно формировать следующей функцией:
$core->u([ 'order', $o['order_id'] ], 'action=dlaction&oa=send' )
Где вместо send
будет указываться ваше название действия. Пример функции может выглядеть так:
public function options( $o ) { if ( $o['order_status'] > 9 ) return false; if ( $o['order_status'] < 6 ) return false; $core = $this->core; if ( $o['track_code'] ) { return [[ 'icon' => 'fa-address-card', 'url' => $core->u([ 'order', $o['order_id'] ], 'action=dlaction&oa=print' ), 'name' => $core->lang['delivery_print'], 'short' => $core->lang['delivery_print_s'], ]]; } elseif ( $this->config['api'] ) { return [[ 'icon' => 'fa-paper-plane', 'url' => $core->u([ 'order', $o['order_id'] ], 'action=dlaction&oa=send' ), 'name' => $core->lang['delivery_send'], 'short' => $core->lang['delivery_send_s'], 'confirm' => $core->lang['delivery_send_c'], ]]; } }
За обработку действий отвечает функция action
, которая принимает два параметра - название действия и массив заказа. Функция должна обрабатывать действие и перенаправлять пользователя на страницу заказа с выполненным действием или ошибкой.
Пример функции, которая реализует предыдущий массив действий:
public function action( $action, $o ) { switch ( $action ) { case 'send': if ( $this->send( $o ) ) { $this->core->msgo( 'ok', $this->core->u([ 'order', $o['order_id'] ]) ); } else $this->core->msgo( 'error', $this->core->u([ 'order', $o['order_id'] ]) ); case 'print': if ( $url = $this->print( $o ) ) { $this->core->go( $url ); } else $this->core->msgo( 'error', $this->core->u([ 'order', $o['order_id'] ]) ); default: $this->core->msgo( 'error', $this->core->u([ 'order', $o['order_id'] ]) ); } }
Для обработки ошибки или успеха, используйте умное перенаправление вот такого вида:
$this->core->msgo( 'error', $this->core->u([ 'order', $o['order_id'] ]) );
Вместо 'error'
может быть указано 'ok'
в случае успешного выполнения.
Формы
Форма добавляется на страницу редактирования заказа в CRM. Она располагается под списком товаров, над основными данными заказа. В этой форме можно реализовать выбор курьеров, методов доставки, пунктов выдачи, даты и времени получения и любых других внутренних параметров, характерных для доставки.
За вывод формы отвечает функция block
, которая принимает на вход один параметр - массив данных заказа. Функция должна возвращать сгенерированный код для вставки в страницу.
Код формы рекомендуется генерировать с помощью встроенного шаблонизатора. Для загрузки шаблона используйте функцию $core->hack->dhl->tpl( 'innerform', 'shpt-dhl' )
, где вместо dhl
указывается название вашего хака, а shpt-dhl
- это название вашего шаблона. Для генерации шаблона используйте $core->tpl->make( 'innerform' )
.
Пример реализации функции:
public function block( $o ) { $core = $this->core; $tm = xxdec( $o['track_meta'] ); $courier = isset( $tm['courier'] ) ? $tm['courier'] : -1; $core->hack->dhl->tpl( 'innerform', 'shpt-dhl' ); $core->tpl->vars( 'innerform', [ 'u_check' => $core->u( [ 'order', $o['order_id'] ], 'action=dlajax&oa=check' ), 'couriers' => json_encode( $this->courier, JSON_PRETTY_PRINT ), ]); foreach ( $this->courier as $i => $n ) $core->tpl->block( 'innerform', 'courier', [ 'i' => $i, 'n' => $n, 's' => $i == $courier ]); return $core->tpl->make( 'innerform' ); }
AJAX-действия
Действия через AJAX могут использоваться для получения данных и выполнения задач встроенными формами. За обработку действий отвечает функция ajax
, которая принимает на вход код действия и массив заказа. Функция должна возвращать массив данных, который будет передан в ответе как JSON-объект.
Пример реализации функции:
public function ajax( $action, $o ) { switch ( $action ) { case 'cities': $area = $this->core->post['area']; return $this->cities( $area ); } }
Ссылка на саму функцию должна формироваться с помощью кода:
$core->u( [ 'order', $o['order_id'] ], 'action=dlajax&oa=cities' )
Где вместо cities
будет указываться код действия, которое необходимо выполнить.
Вы можете сохранять внутри заказа дополнительные данные, которые используете в формах и передаёте через AJAX-действия. В этих данных можно хранить идентификаторы тарифов, курьеров, пунктов выдачи и прочих внутренних сущностей доставки.
Данные доставки принято хранить в поле track_meta
массива данных заказа. Данные извлекаются вот так:
$tm = xxdec( $o['track_meta'] );
Для сохранения мета-данных доставки, используйте следующий код:
$core->lead->edit( $o['order_id'], [ 'tmeta' => $tm ] );
Обратите внимание, что в $tm
должны присутствовать все поля сразу. Если вы отправите только часть полей, другие ранее существовавшие поля будут удалены.
Статистика по тарифам и курьерам
В массиве track_meta
используется два "волшебных" параметра, которые позволяют строить аналитику доставки в разрезе внутренних тарифов и курьеров. Чтобы реализовать такую статистику, добавьте два поля:
tariff
- идентификатор тарифа службы доставки.courier
- идентификатор курьера службы доставки.
Рекомендуется использовать числовые идентификаторы. Для красоты показа статистики, вы можете добавить функции _tariff
и _courier
, которые принимают на вход идентификатор тарифа или курьера и возвращают его красивое название.
Функция настройки
Чтобы добавить дополнительные поля к настройкам, необходимо реализовать две функции:
form
- возвращает массив с дополнительными полями формы.save
- принимает сырые данные и возвращает массив с полями конфигурации.
Функция form
не имеет параметров. Доступ к полям конфигурации должен выполняться через массив $this->config
. Результатом работы функции должен быть массив полей формы. Каждое поле - это массив, который может содержать следующие поля:
type
- тип поля:text
,email
,textarea
,number
,select
,mselect
,radio
,checkbox
.name
- название поля, рекомендуется добавлять префикс с названием модуля.head
- заголовок поля, используйте языковой файл для хранения.descr
- описание поля, используйте языковой файл для хранения.value
- текущее значение поля, используйте$this->config
.options
- набор опций для полей типаselect
,mselect
,radio
.checked
- пометка для поля типаcheckbox
.
Пример реализации формы:
public function form() { return [ [ 'type' => 'head', 'value' => $this->core->lang['settings'] ], [ 'type' => 'text', 'name' => 'generic_url', 'head' => $this->core->lang['delivery_url_track'], 'value' => $this->config['url'] ], [ 'type' => 'text', 'name' => 'generic_doc', 'head' => $this->core->lang['delivery_url_doc'], 'value' => $this->config['doc'] ], ]; }
Функция save
получает на вход параметр $data
и должна вернуть обработанный массив параметров в виде ключ-значение. Пример реализации:
public function save( $data ) { return [ 'url' => str_replace( '&', '&', $this->esc( $data['generic_url'] ) ), 'doc' => str_replace( '&', '&', $this->esc( $data['generic_doc'] ) ), ]; }