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

—

от автора

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

ссылка на оригинал статьи http://habrahabr.ru/post/157647/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *