[СДЕЛАЙ САМ] Интеграция по API калькулятора расчёта доставки от Logsis
Дальше идёт работающее решение для сайта blackbuffalo.ru/ (это чтобы посмотреть как работает), но оно мне не нравится изначально я хотел расширить класс доставки, как говорится «пацан к успеху шёл. не повезло. не фартануло». А теперь к делу. API сервиса простое, из нюансов могу отметить только то, что комиссия за наложенный платёж прибавляется только в личном кабинете Logsis, на сайт её не затащить, поэтому сумма на сайте будет отличаться от суммы в ЛК процентов на 8-10. Для более точных расчётов можно передавать параметр os (оценочная стоимость), но мой заказчик пожелал этого не делать. Переходим к коду.Начту на мой взгляд с самого узкого места это js. Во-первых проблему с автозаполнением полей в Chrome мне решить не удалось, там (в браузере) есть такая функция, которая заполняет поля пачками, например пользователь ранее заполнял уже где-то телефон и адрес, вот Chrome по телефону подтянет и адрес, при этом калькуляция не производится и если обновить страницу, то некоторые поля адреса пустеют. В общем, кто не понимает о чём речь, го на сайт по ссылке в самом верху заметки. А в целом я постарался максимально интегрироваться в родной js от minishop2 и правил исходники (знаю, нехорошо), но я правил так, чтобы при обновлении не затёрлось, благо Василий предусмотрел. Значит, делаем копию файла assets/components/minishop2/js/web/dafault.js и переименовываем, я назвал custom.js и в нём пишем примерно такую функцию
P.S. Техподдержка у сервиса Logsis г**но, я обращался я знаю)))
function deliveryCalc(){
//собираем адрес в одну строку
var region = $('input[name="region"]').val(),
city = $('input[name="city"]').val(),
street = $('input[name="street"]').val(),
building = $('input[name="building"]').val(),
address = region + ',' + city + ',' + street + ',' + building;
//отправляем на сервер
$.post(document.location.href, {'action':'calculate', 'address':address}, function(response){
response = JSON.parse(response);
$('.delivery_cost').text(miniShop2.Utils.formatPrice(response.delivery_cost)); //показываем стоимость доставки
$('#ms2_order_cost').text(miniShop2.Utils.formatPrice(response.total_cost)); //показываем общую стоимость
if(!response.success){
$('.delivery_error').text(response.message); //показываем ошибку если доставка не рассчиталась
}else{
$('.delivery_error').text(''); //удаляем текст ошибки если всё ок.
}
});
}
Я написал «примерно такую» потому что логика работы с полученными после расчёта данными может отличаться, но главное функция должна передавать на сервер данные для расчёт доставки, а именно адрес. Вызов функции располагаем внутри метода getcost примерно на 458 строке файла custom.jsgetcost: function () {
var callbacks = miniShop2.Order.callbacks;
callbacks.getcost.response.success = function (response) {
$(miniShop2.Order.orderCost, miniShop2.Order.order).text(miniShop2.Utils.formatPrice(response.data['cost']));
/* custom code */
deliveryCalc(); //вот тут
};
var data = {};
data[miniShop2.actionName] = 'order/getcost';
miniShop2.send(data, miniShop2.Order.callbacks.getcost, miniShop2.Callbacks.Order.getcost);
},
Чтобы на сервере данные обработать, я написал сниппет ajaxReceiver, идея взята из этой заметки, располагаю его в самом верху страницы сразу после <!doctype html>. <?php
//$modx->log(1, 'Request type ' . $_SERVER['REQUEST_METHOD']);
// Откликаться будет ТОЛЬКО на ajax запросы
if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {return;}
// Сниппет будет обрабатывать не один вид запросов, поэтому работать будем по запрашиваемому действию
// Если в массиве POST нет действия - выход
//$modx->log(xPDO::LOG_LEVEL_ERROR, 'Массив ПОСТ ' . print_r($_POST, 1));
if (empty($_POST['action'])) {return;}
// А если есть - работаем
$res = '';
$action = $_POST['action'];
//$modx->log(xPDO::LOG_LEVEL_ERROR, 'Экшн ' . $action);
switch ($action) {
case 'calculate':
$res = $modx->runSnippet('getDeliveryCost');
break;
// А вот сюда потом добавлять новые методы prodFastView
}
// $modx->log(xPDO::LOG_LEVEL_ERROR, 'Результат ' . $res);
// Если у нас есть, что отдать на запрос - отдаем и прерываем работу парсера MODX
if (!empty($res)) {
die($res);
}
Этот сниппет ловит нужный ajax и запускает другой сниппет, который я назвал getDeliveryCost.<?php
$ms2 = $modx->getService('miniShop2');
$ms2->initialize($modx->context->key);
$cart = $ms2->cart->get(); //нужна корзина чтобы получить общую стоимость
$data = $ms2->order->get();
//$modx->log(1, print_r($cart,1));
$cartTotal = $ms2->cart->status();
$cartTotalCost = $cartTotal['total_cost'];
$delivery_cost = 0;
// этот кусок кода нужен потому что в $data адрес приходит кусками, а иногда целиком, в общем я логику не уловил
// и подстраховался - передаю адрес сам, если его вдруг нет, проверяю в $data.
if(isset($_POST['address'])){
$address = $_POST['address'];
}else{
$region = $data['region'];
$city = $data['city'];
$street = $data['street'];
$building = $data['building'];
$address = $region .','. $city .','. $street .','. $building;
}
$res = array(
'success' => false,
'message' => 'Стоимость доставки не расcчитана. Проверьте корректность адреса',
'delivery_cost' => $delivery_cost,
'total_cost' => $cartTotalCost + $delivery_cost
);
//$modx->log(1, print_r($data,1));
//$modx->log(1, print_r($address,1));
if($address){
$dimension_side1 = 0;
$dimension_side2 = 0;
$dimension_side3 = 0;
$total_weight = $cartTotal['total_weight'];
$total_count = $cartTotal['total_count'];
foreach($cart as $item){
$prod = $modx->getObject('msProduct', $item['id']);
$size = $prod->get('size');
if($size){ //размеры нужно задавать в стандартном поле карточки товара в формате 10-20-30
$size = explode('-',$size[0]); //разделитель можно изменить
$dimension_side1 += $size[0]; //может быть = 0
$dimension_side2 += $size[1]; //может быть = 0
$dimension_side3 += $size[2]; //может быть = 0
}
}
// 'os' => $cartTotalCost можно передавать для более точного расчёта
$params = array(
'apikey' => $modx->getOption('logsis_api'),
'address' => $address,
'weight' => $total_weight,
'dimension_side1' => $dimension_side1,
'dimension_side2' => $dimension_side2,
'dimension_side3' => $dimension_side3
);
$response = $modx->runSnippet('integrateLogsis', array('url' => 'http://api.logsis.ru/api/v1/public/calculate', 'method' => 'post', 'params'=> $params));
//$modx->log(1,'CALC ' . print_r($params,1));
if($response['status'] == 200){ // коды ответов есть в документации http://api.logsis.ru/logsis-api/
$delivery_cost = ceil($response['response']['total']);
$res = array(
'success' => true,
'message' =>'Стоимость доставки расcчитана',
'delivery_cost' => $delivery_cost,
'total_cost' => $cartTotalCost + $delivery_cost
);
}
}
return json_encode($res); //кодируем в JSON и возвращаем ответ.
В предыдущем куске кода фигурирует сниппет integrateLogsis он собственно отправляет запрос и больше ничего<?php
if(!$url){
$modx->log(1, 'integrateLogsis: Не передан url для отправки запроса');
return false;
}
if(!$params){
$modx->log(1, 'integrateLogsis: Не переданы параметры запроса');
return false;
}
$ch = curl_init($url);
if($method == 'post'){
curl_setopt($ch, CURLOPT_POST, 1);
}
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params, '', '&'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$html = curl_exec($ch);
curl_close($ch);
$response = json_decode($html,1);
//$modx->log(1,'integrateLogsis ' . print_r($response,1));
if($response['Error']){
return $response['Error'];
}else{
return $response;
}
Собственно по калькуляции это всё. Дальше задача стояла создать заказ на доставку в ЛК Logsis и получить трек-номер. Для создания заказа нужно пройти два этапа собственно создание и подтверждение. Для этого я написал плагин на событие msOnBeforeCreateOrder вот его код</php
//ещё раз рассчитываем стоимость доставки, а то мало ли
$deliveryCalc = json_decode($modx->runSnippet('getDeliveryCost'),1);
$msOrder->set('cost', $deliveryCalc['total_cost']); //меняем стоимость заказа
$msOrder->set('delivery_cost', ceil($deliveryCalc['delivery_cost'])); //устанавливаем стоимость доставки
$inner_n = $msOrder->get('num'); //Внутренний номер отправителя
$address = $msOrder->getOne('Address');
$products = $msOrder->getMany('Products');
$proerties = $address->get('properties');
$key = $modx->getOption('logsis_api');
$date = new DateTime($_POST['order_date']) ?: date();
$delivery_date = $date->format('Y-m-d') ?: date('Y-m-d'); //Дата доставки (YYYY-mm-dd)
$delivery_time = $_POST['extfld_delivery_time']; //Интервалы: Москва и московская область и Санкт-Петербург
$target_name = $address->get('receiver');
$target_contacts = preg_replace('/[^0-9]/', '', $address->get('phone'));
$os = $msOrder->get('cart_cost'); //Оценочная стоимость заказа
$np = 1; //Услуга 'Прием наложенного платежа'
$price_client_delivery = $msOrder->get('delivery_cost'); //Стоимость доставки для конечного получателя
$price_client = $msOrder->get('cost'); //Величина наложенного платежа, руб
$order_weight = $msOrder->get('weight'); //Вес отправления, кг
$places_count = 1; //Количество мест в заказе.
$dimension_side1 = 0; //Габарит заказа 1, в см. Не передавать 0!
$dimension_side2 = 0; //Габарит заказа 2, в см. Не передавать 0!
$dimension_side3 = 0; //Габарит заказа 3, в см. Не передавать 0!
$post_code = $address->get('index'); //Почтовый индекс
$addr = $address->get('region') . ',' .$address->get('city') . ',' .$address->get('street').','.$address->get('building');
$sms = $modx->getOption('logsis_sms'); //Признак услуги sms-информирования.
$open_option = 3; //Признак возможности вскрытия заказа.
$call_option = $modx->getOption('logsis_call'); //Дополнительный (тарифицируемый) звонок клиенту.
$docs_option = 0; //Возврат накладных / документов, вложенных в заказ (опция тарифицируется).
$partial_option = 0; //Признак возможности частичного выкупа заказа клиентом
$dress_fitting_option = 0; //Признак возможности примерки товаров
$lifting_option = 0; //Занос / подъем КГТ заказов (более 25 кг) до квартиры
$goods = array();
$k = 0;
// тут я просто не уверен какой ключ поэтому ключ создал отдельно
// предполагаю что можно было сделать так foreach($products as $k => $product){}
foreach($products as $product){
$goods[$k]['articul'] = $product->get('article') ?: $product->get('name'); //Артикул товарной позиции / услуги
$goods[$k]['artname'] = $product->get('name'); //Название товарной позиции / услуги
$goods[$k]['count'] = $product->get('count'); //Количество, ед.
$goods[$k]['weight'] = $product->get('weight'); //Вес единицы товара, кг
$goods[$k]['price'] = $product->get('price'); //Цена единицы товара, руб
$goods[$k]['nds'] = 2; //Вид налога на товар/услугу
$prod = $modx->getObject('msProduct', $product->get('product_id'));
$size = $prod->get('size');
$size = explode('-',$size[0]);
$dimension_side1 += $size[0];
$dimension_side2 += $size[1];
$dimension_side3 += $size[2];
$k++;
}
$params = array(
'key' => $key,
'inner_n' => $inner_n,
'delivery_date' => $delivery_date,
'delivery_time' => $delivery_time,
'target_name' => $target_name,
'target_contacts' => $target_contacts,
'os' => $os,
'np' => $np,
'price_client' => $price_client,
'price_client_delivery' => $price_client_delivery,
'order_weight' => $order_weight,
'places_count' => $places_count,
'dimension_side1' => $dimension_side1,
'dimension_side2' => $dimension_side2,
'dimension_side3' => $dimension_side3,
'post_code' => $post_code,
'addr' => $addr,
'sms' => $sms,
'open_option' => $open_option,
'call_option' => $call_option,
'docs_option' => $docs_option,
'partial_option' => $partial_option,
'dress_fitting_option' => $dress_fitting_option,
'lifting_option' => $lifting_option,
'goods' => $goods
);
//$modx->log(1,'Get track number ' . print_r($params,1));
//создаем заказ на доставку
$response = $modx->runSnippet('integrateLogsis', array('url' => 'http://cab.logsis.ru/apiv2/createorder/', 'method' => 'post', 'params'=> $params));
if($response['status'] == 200){
$params = array(
'key' => $key,
'inner_n' => $inner_n,
);
//подтверждаем заказ на доставку
$response = $modx->runSnippet('integrateLogsis', array('url' => 'http://cab.logsis.ru/apiv2/confirmorder', 'method' => 'post', 'params'=> $params));
//записываем полученный трек номер в комментарии к заказу чтобы отправить пользователю в письме
$msOrder->set('comment', $response['response']['order_id']);
$msOrder->save();
}
В коде выше есть дополнительное поле extfld_delivery_time оно создано для сохранения кода интервала доставки для Москвы и Питера, по-хорошему его бы надо соотносить с реальными интервалами и показывать в админке в карточке заказа, но в моём случае требовалось просто передавать его в Logsis, по сути можно было и дополнительное поле не прикручивать, а получать его просто из массива $_POST, но у меня есть проверенный и лёгкий способ взятый здесь Чанк письма с трек-номером думаю приводить не надо, приведу просто код для вывода комментария<h3>Трек номер: {$comment}</h3>
Вот собственно и всё, надеюсь ничего не забыл. Буду рад если кому-то это пригодится, а также замечаниям по качеству кода и предложениям по его улучшению.P.S. Техподдержка у сервиса Logsis г**но, я обращался я знаю)))
Поблагодарить автора
Отправить деньги