Создаем модуль «Новая почта» для Magento (часть 1)

в 9:14, , рубрики: ecommerce, Magento, php, Веб-разработка, новая почта, электронная коммерция, метки: , , , ,

Меня уже не один человек просил написать модуль для самого популярного грузового перевозчика Украины “Новая почта”. Дело это не на один час, поэтому руки никак не доходили. Но недавно я подумал, что если идея востребована, то почему бы не сделать что-то полезное для сообщества, а именно:
1. бесплатный модуль “Новая почта” с открытым кодом для Magento;
2. статью в нескольких частях с подробным описанием процесса.

Статья ориентирована на новичков в Magento, но, возможно, будет интересна и опытным разработчикам. Все исходники можно найти на GitHub: github.com/alexkuk/Ak_NovaPoshta/, они дополняются по ходу разработки.

Итак, начнем с постановки задачи. Модуль должен выполнять следующие функции:
1. добавить новый метод доставки в Magento;
2. настройки метода должны позволять задавать различную стоимость доставки для различного суммарного веса посылки (как в методе доставки Table Rates);
3. хранить и синхронизировать с Новой Почтой базу складов;
4. выводить склады Новой Почты в удобном для выбора виде на шаге Shipping Method оформления заказа, по умолчанию выводить только склады в городе пользователя;
5. добавить возможность отслеживания посылки в панель пользователя.

В последнее время API Новой почты также позволяет создавать и распечатывать ТТН, но, с вашего позволения, эту функциональность я оставлю на потом. Кроме того, касательно пункта 2, API также предоставляет средства для расчета стоимости доставки. Пока я предпочту более простой и стабильный вариант, который позволит владельцу магазина самостоятельно определять стоимость доставки в зависимости от суммарного веса заказа. Это связано с тем, что продавцу не всегда удается точно определить вес каждого товара, и стоимость доставки, выставленная через API может играть не на пользу продавца. Оставим расчет стоимости через API на потом.

Добавим новый метод доставки

Итак, создадим новый модуль и добавим новый метод доставки. Я буду работать с Magento CE 1.7.0.2. Модуль назовем Ak_NovaPoshta. Про структуру модулей в Magento написано уже немало статей, так что этот момент я опущу.

Стоит отметить, что Magento оперирует двумя сущностями, когда мы говорим о способе доставки, — это shipping carrier (перевозчик) и shipping methods (методы доставки, которые предоставляются перевозчиком). В нашем случае перевозчик — Новая Почта, в качестве методов мы будем использовать склады Новой Почты.

Чтобы добавить carrier, необходимо сделать три вещи:
1. добавить поля настроек нашего перевозчика в system.xml модуля;

2. добавить значения настроек по-умолчанию, а также ссылку на класс модели перевозчика, в config.xml модуля:

<config>
…
<default>
       <carriers>
           <novaposhta>
               <!-- отключен по-умолчанию, чтобы не предлагать недоконфигурированный метод доставки пользователю -->
               <active>0</active>
               <!-- флаг, ограничивать ли метод доставки по стране, которая указана в shipping address при оформлении заказа -->
               <sallowspecific>1</sallowspecific>
               <!-- ограничим по Украине -->
               <specificcountry>UA</specificcountry>
               <!-- ссылка на класс модели перевозчика -->
               <model>novaposhta/carrier_novaPoshta</model>
               <!-- название перевозчика -->
               <title>Новая Почта</title>
               <!-- сообщение об ошибке -->
               <specificerrmsg>Этот способ доставки на текущий момент не доступен. Если вы желаете, чтобы мы доставили заказ Новой Почтой, обратитесь к менеджеру интернет-магазина.</specificerrmsg>
           </novaposhta>
       </carriers>
    </default>
…
</config>

3. добавить класс модели для нашего перевозчика.
Класс модели перевозчика наследуется от Mage_Shipping_Model_Carrier_Abstract и реализует Mage_Shipping_Model_Carrier_Interface. В Mage_Shipping_Model_Carrier_Abstract уже определены некоторые полезные методы, как, например, метод getConfigData($field) для извлечения конфигурационных значений. В своем классе определяем основной метод collectRates(Mage_Shipping_Model_Rate_Request $request), который будет возвращать доступные методы доставки:

    public function collectRates(Mage_Shipping_Model_Rate_Request $request)
    {
       if (!$this->getConfigFlag('active')) {
           return false;
       }

       /** @var $result Mage_Shipping_Model_Rate_Result */
       $result = Mage::getModel('shipping/rate_result');

       $shippingPrice = 1.00; // dummy price
       $warehouseId = 1; // dummy warehouse ID
       $warehouseName = 'Склад №1'; // dummy warehouse name

       /** @var $method Mage_Shipping_Model_Rate_Result_Method */
       $method = Mage::getModel('shipping/rate_result_method');
       $method->setCarrier($this->_code)
           ->setCarrierTitle($this->getConfigData('name'))
           ->setMethod('warehouse_' . $warehouseId)
           ->setMethodTitle($warehouseName)
           ->setPrice($shippingPrice)
           ->setCost($shippingPrice);

       $result->append($method);

       return $result;
    }

Пока мы не реализовали синхронизацию складов, будем использовать один метод доставки для примера — Склад №1. Внутри метода collectRates() создаем экземпляр Mage_Shipping_Model_Rate_Result, и с помощью метода Mage_Shipping_Model_Rate_Result::append() добавляем в него экземпляры Mage_Shipping_Model_Rate_Result_Method.

Напоследок перепишем родительский метод isTrackingAvailable:

    public function isTrackingAvailable()
    {
       return true;
    }

На этом этапе наш метод доставки уже может использоваться, но, как видите, стоимость доставки всегда будет равна 1.00.

Добавим конфигурирование стоимости доставки

Следующий шаг — добавить конфигурационную опцию для связи суммарного веса заказа и стоимости доставки. В итоге хочется получить такую форму:

Создаем модуль «Новая почта» для Magento (часть 1)

Для этого добавим поле weight_price в system.xml нашего модуля:

…
                       <weight_price translate="label">
                           <label>Shipping price</label>
                           <frontend_model>novaposhta/config_field_weightPrice</frontend_model>
                           <backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
                           <sort_order>110</sort_order>
                           <show_in_default>1</show_in_default>
                           <show_in_website>1</show_in_website>
                           <show_in_store>1</show_in_store>
                       </weight_price>
…

Backend model — это модель, которая преобразует значение поля перед сохранением в базу. В данном случае мы будем использовать готовую модель Mage_Adminhtml_Model_System_Config_Backend_Serialized_Array. Frontend model — это вовсе не модель, а блок, отвечающий за представление поля. Мы добавим свой блок Ak_NovaPoshta_Block_Config_Field_WeightPrice:

class Ak_NovaPoshta_Block_Config_Field_WeightPrice extends Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract
{
    public function __construct()
    {
       $this->addColumn('weight', array(
           'label' => Mage::helper('novaposhta')->__('Weight upper limit'),
           'style' => 'width:120px',
       ));
       $this->addColumn('price', array(
           'label' => Mage::helper('novaposhta')->__('Price'),
           'style' => 'width:120px',
       ));
       $this->_addAfter = false;
       $this->_addButtonLabel = Mage::helper('novaposhta')->__('Add rate');
       parent::__construct();
    }
}

Как видите, абстрактный Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract делает за нас всю работу, нам остается настроить колонки и кнопки.

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

    /**
    * @return array
    */
    protected function _getWeightPriceMap()
    {
       $weightPriceMap = $this->getConfigData('weight_price');
       if (empty($weightPriceMap)) {
           return array();
       }

       return unserialize($weightPriceMap);
    }

    /**
    * @param $packageWeight
    *
    * @return float
    */
    protected function _getDeliveryPriceByWeight($packageWeight)
    {
       $weightPriceMap = $this->_getWeightPriceMap();
       $resultingPrice = 0.00;
       if (empty($weightPriceMap)) {
           return $resultingPrice;
       }

       $minimumWeight = 1000000000;
       foreach ($weightPriceMap as $weightPrice) {
           if ($packageWeight <= $weightPrice['weight'] && $weightPrice['weight'] <= $minimumWeight) {
               $minimumWeight = $weightPrice['weight'];
               $resultingPrice = $weightPrice['price'];
           }
       }

       return $resultingPrice;
    }

и в методе collectRates заменим

$shippingPrice = 1.00

на более реальное

$shippingPrice = $this->_getDeliveryPriceByWeight($request->getPackageWeight());

Готово

Создаем модуль «Новая почта» для Magento (часть 1)

В следующей части займусь синхронизацией базы складов с API Новой Почты. Спасибо за внимание!

Автор: AlexKuk

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js