Сервисы доставки СДЭК, ПР и EMS для MS2 ч.2


Это вторая часть статьи, где будет разобран только php код и работа с poscalc, вся база js кода у нас готова и работает в первой части статьи. Для тех, кто не хочет читать, добро пожаловать сразу на GitHub


И так, у нас уже есть полностью готовая база из js кода и парочку php скриптов которые пишут статус в сессию и достают его оттуда при необходимости, нам осталось лишь реализовать работу с сервисом poscalc.

Сразу обращу внимание что библиотека poscalc была мною немного переписана, и стандартная вам не подойдет, в идеале ее надо было бы вообще запихнуть в класс, но писал ее криворукий баран и вникать в тонкости ее работы мне было откровенно лень. Отдельно библиотека тут

Начнем пожалуй.
В нашей папке components/customDelivery/model/ создадим две отдельные папки, назовем их к примеру postcalc и postcalcEMS, в каждой папке создадим папку libs и зальем туда нашу библиотеку, которую вы можете скачать по ссылке выше. Мы могли бы залить ее конечно один раз и инициализировать из одной папки, но мне удобнее так, а вы можете делать как хотите :)

В папке postcalc создадим файл postcalc.php он будет отвечать за расчет доставки почты россии

<?php
//начало у нас аналогичное прошлой статье, инициализируем библиотеки и родительский класс, расширяем родительский класс и переписываем метод getCost
if(!class_exists('msDeliveryInterface')) {
    require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/minishop2/model/minishop2/msdeliveryhandler.class.php';
}
require_once __DIR__.'/libs/postcalc_light_lib.php';
class postCalcDelivery extends msDeliveryHandler implements msDeliveryInterface {
    public $modx;
    public $ms2;
    public function getCost(msOrderInterface $order, msDelivery $delivery, $cost = 0) {
        //проверяем есть ли у нас в сессии имя города, если нет, то возвращаем цену без каких либо модификаций, мы будем работать именно с именем города, но учтите то, что poscalc может высчитывать доставку и по индексу
	$cityName = $_SESSION['cityName'];
	if (empty($cityName)) {
		return $cost;
	}
        //считаем добавочную стоимость доставки, указанную вручную в методе доставки
        $add_price = $delivery->get('price');
        if (preg_match('/%$/', $add_price)) {
            $add_price = str_replace('%', '', $add_price);
            $add_price = $cost / 100 * $add_price;
        }
        //получаем массив текущей корзины и вытаскиваем оттуда вес
        $cart = $order->ms2->cart->status();
        $weight = $cart['total_weight'];
        //вес в корзине у нас в граммах, а poscalc работает с киллограммами, преобразовываем граммы в киллограммы
        $weight = floatval($weight) * 1000;
        //теперь указываем переменные нужные для poscalc from - индекс откуда будет отправление, можете также указать и название города
	$postcalc_from = 117216;
        //название или индекс куда будет отправление, мы вытаскиваем из сессии это значение выше
	$postcalc_to = $cityName;
        //вес
	$postcalc_weight = $weight;
        //ценность посылки, которую вы укажите при отправки, мы стабильно указываем 100р, но вы можете указать свое значение или же скажем высчитывать динамически, например половина от стоимости корзины
	$postcalc_valuation = 100;
        //страна, куда будет отправление, мы отправляем только по России, по этому это значение статичное, но вы можете доставать его из сессии, СДЭК умеет определять страну города, правда нужно чуть переписать скрипты, если нужно, объясню в комментариях
	$postcalc_country = 'RU';
        //выполняем функцию расчета, которая у нас лежит в либе, в ответ у нас приходит строка в случае успеха или массив в случае неудачи
	$arrResponse = postcalc_request($postcalc_from, $postcalc_to, $postcalc_weight, $postcalc_valuation, $postcalc_country);
          //обрабатываем ошибку
	  if ( !is_array($arrResponse) ) {
                   //пишем в лог массив ошибки
		   $this->modx->log(1, 'Ошибка postcalc '.$arrResponse);
		    if ( !is_array(error_get_last()) ){
		        $arrError = error_get_last();
		        $this->modx->log(1, 'Массив ошибки postcalc '.print_r($arrResponse, 1));
                    };
            //пишем в статус ошибку
            $textDeliveryCdek = 'Ошибка автоматического расчета Почты России, стоимость доставки посчитает для вас менеджер вручную.';
            session_start();
            $_SESSION['statusDelivery'] = $textDeliveryCdek;
            $deliveryCost = $add_price;
	  } else {
                        //обрабатываем успешный результат
                        //если нам нужно узнать все методы отправления, то нужно раскомментировать строчку ниже, в логи придут все методы 
			//$this->modx->log(1, 'Расчет postcalc '.print_r($arrResponse['Отправления'], 1));
                        //считаем стоимость доставки для ценной посылки
			$deliveryCost = $arrResponse['Отправления']['ЦеннаяПосылка']['Доставка'];
                        //прибавляем к стоимости доставки добавочную цену
			$deliveryCost = $deliveryCost + $add_price;
                        //пишем статус
            		$textDeliveryCdek = 'Стоимость доставки Почтой России в '.$cityName.' составит '.$deliveryCost.'р срок доставки составит '.$arrResponse['Отправления']['ЦеннаяПосылка']['СрокДоставки'].' дня';
            		session_start();
            		$_SESSION['statusDelivery'] = $textDeliveryCdek;
	   }
        //возвращаем результат наших расчетов и плюсуем к нему цену нашей корзины
    	return $deliveryCost + $cost;
    }
}
?>
Теперь нам нужно добавить наш класс обработчик к службам minishop2, для этого выполним в консоле код один раз:

if ($miniShop2 = $modx->getService('miniShop2')) {
    $miniShop2->addService('delivery', 'PostcalcPRdelivery',
        '{core_path}components/customDelivery/model/postcalc/postcalc.php'
    );
}
Cоздаем новый метод доставки с классом обработчиком PostcalcPRdelivery

Вот в принципе и все, служба ems сделана аналогично и объяснять ее я не вижу смысла, нужно просто доставать стоимость доставки не для ценной посылки, а для ems, код вы можете посмотреть опять же на GitHub

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

P.s. в моем случае администрация modstore поступила более чем достойно и вернула деньги за оба дополнения даже спустя пол года использования их, хотя на возврат даже не рассчитывал

Через неделю возможно запилю статью о том, как прикрутить ко всему этому добру отслеживание посылок и рассылку статуса посылки через сервис «ГдеПосылка»
05 октября 2017, 15:03    Pavel Zarubin   G+  
3    412 +12

Комментарии (18)

  1. Сергей Кепкин 05 октября 2017, 15:24 # -1
    Вы пишите, что клиент доволен? Ну рад за вас.
    Единственное, что резануло глаз — если я правильно вникнул в Ваш код — запрос на СДЭК происходит на каждом хите??? ООооо. У них там есть лимиты, это однозначно и проверено, но информацию об этом СДЭК не афиширует, а тупо блочит запросы и предлагает переходить на запросы через авторизацию, возможно на платной основе)))
    У postcalc вообще прямо написано про 10000 запросов в сутки с одного ip — это легко перекрывается, если 100 клиентов смотрят по 100 товаров))) А роботы?
    Я себе делал запрос не по готовности страницы, как у вас, а по скроллингу до блока с ценами доставки — это для корзины))) В карте товара сделал вообще кнопочкой.
    1. Pavel Zarubin 05 октября 2017, 15:33 # +2
      Вы немного плоховастенько вникли в код. Запрос на определение города уходит через js, а значит уходит он с ip пользователя, а не сайта.
      По поводу postcalc это нужно чтобы 10000 пользователей ЗАШЛИ В КОРЗИНУ и выбрали метод доставки Почта России или EMS, если у вас 10к пользователей В ДЕНЬ заходят в корзину, то я думаю тут нет сложности перейти на платное api, а мутить костыли в виде запроса по прокрутке — ад
      1. Сергей Кепкин 05 октября 2017, 15:53 # 0
        Я же писал — стоимость доставки просчитывается И на странице товара.
        И в чем костыль? Этот т.н. костыль есть в любом магазине уровня мвидео-эльдорадо-юлмарт)))
        1. Pavel Zarubin 05 октября 2017, 15:56 # 0
          В таком случае СДЭК к примеру можно посчитать стоимость доставки только через JS на странице товара, их api это позволяет и не попасть под репрессию СДЭК'а
          1. Сергей Кепкин 05 октября 2017, 15:57 # 0
            ок, годнота!
            между прочим, плюсанул за ваши темы!
            1. Pavel Zarubin 05 октября 2017, 17:25 # 0
              Спасибо :)
              1. Андрей 05 октября 2017, 20:38 # 0
                Все-таки было бы отлично посмотреть на Вашем сайте как это работает. Не стесняйтесь — явите миру)))
                1. Pavel Zarubin 05 октября 2017, 20:59 # +1
    2. Stan Ezersky 06 октября 2017, 09:02 # 0
      Спасибо за статьи, надеюсь на продолжение!
      1. Pavel Zarubin 06 октября 2017, 23:38 # 0
        Спасибо за приятные слова :)
        1. Сергей Кепкин 07 октября 2017, 22:32 # 0
          В настройках Минишоп2 методы поменяли названия?
          1. Pavel Zarubin 08 октября 2017, 02:07 # 0
            О чем вы?
          2. Pavel Zarubin 09 октября 2017, 12:32 # 0
            Добавил сниппет для вывода стоимости доставки на странице товара
            1. Алексей Бгатов 16 октября 2017, 14:07 # 0
              спасибо за статьи, ждем трекер посылок)
              1. Pavel Zarubin 16 октября 2017, 14:44 # +1
                Они неделю только думали дать доступ к API или нет, так что я только начал писать сам модуль :)
                1. Сергей Кепкин 16 октября 2017, 18:05 # 0
                  да вообще не ведают что творят!)) подскажи что значит эта конструкция в tpl.msOrder, как это будет на голом php?
                  {var $checked = !$order.delivery && $index == 0 || $delivery.id == $order.delivery}
                  1. Сергей Кепкин 16 октября 2017, 18:11 # 0
                    я понял так: $checked будет true, если не существует $order.delivery и это первая доставка в цикле ИЛИ при совпадении id текущей в цикле доставки с выбранной доставкой $order.delivery.
              Вы должны авторизоваться, чтобы оставлять комментарии.