Pull to refresh

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

Reading time6 min
Views23K

Оглавление


  1. Создаем модуль «Новая почта» для Magento (часть 1), где мы добавляем новый метод доставки в Magento
  2. Создаем модуль «Новая почта» для Magento (часть 2), где мы учим Magento хранить и синхронизировать с Новой Почтой базу складов


Меня уже не один человек просил написать модуль для самого популярного грузового перевозчика Украины “Новая почта”. Дело это не на один час, поэтому руки никак не доходили. Но недавно я подумал, что если идея востребована, то почему бы не сделать что-то полезное для сообщества, а именно:
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.

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


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



Для этого добавим поле 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());


Готово



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

Articles