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