Модуль доставки
Перевозчик в PrestaShop — запись в панели в разделе «Доставка → Перевозчики» с названием, сроком и таблицей цен. Обычно его создают вручную, и модуль не нужен. Модуль доставки пишут, когда стоимость надо считать кодом: запросить тариф у службы доставки или применить свою формулу. Такой модуль наследует CarrierModule.
Пример — отдельный модуль acmeship в папке modules/acmeship.
Главный класс
<?php
// modules/acmeship/acmeship.php
if (!defined('_PS_VERSION_')) {
exit;
}
class AcmeShip extends CarrierModule
{
public $id_carrier;
public function __construct()
{
$this->name = 'acmeship';
$this->tab = 'shipping_logistics';
$this->version = '1.0.0';
$this->author = 'Acme';
parent::__construct();
$this->displayName = $this->trans('Курьер Acme', [], 'Modules.Acmeship.Admin');
}
public function getOrderShippingCost($cart, $shippingCost)
{
return 5.0;
}
public function getOrderShippingCostExternal($cart)
{
return $this->getOrderShippingCost($cart, 0);
}
}Два метода обязательны. Движок вызывает их, когда считает доставку для корзины, и берёт возвращённое число как стоимость без налога. Здесь доставка всегда стоит 5 единиц валюты магазина. В свойство id_carrier движок перед вызовом записывает номер перевозчика.
| Метод | Когда вызывается |
|---|---|
getOrderShippingCost($cart, $shippingCost) | У перевозчика заданы диапазоны цен; во втором аргументе — цена из таблицы |
getOrderShippingCostExternal($cart) | Диапазоны не используются |
Создание перевозчика
Сам по себе модуль в списке доставки не появится: при установке он должен создать перевозчика, привязанного к модулю.
// modules/acmeship/acmeship.php
public function install(): bool
{
if (!parent::install() || !$this->registerHook('actionCarrierUpdate')) {
return false;
}
$carrier = new Carrier();
$carrier->name = 'Acme Courier';
$carrier->active = true;
$carrier->is_module = true;
$carrier->external_module_name = $this->name;
$carrier->shipping_external = true;
$carrier->need_range = true;
foreach (Language::getLanguages(false) as $language) {
$carrier->delay[(int) $language['id_lang']] = '2 дня';
}
if (!$carrier->add()) {
return false;
}
return Configuration::updateValue('ACMESHIP_CARRIER_ID', (int) $carrier->id);
}Carrier — модель ядра. Свойства is_module, external_module_name и shipping_external связывают перевозчика с модулем: стоимость будет спрашиваться у него. delay — текст о сроке доставки на каждом языке. Номер созданного перевозчика модуль запоминает в настройках.
Что ещё нужно перевозчику
Чтобы вариант показался покупателю, перевозчику назначают зоны, группы покупателей и диапазоны. Проще всего сделать это после установки в панели: «Доставка → Перевозчики», кнопка изменения у «Acme Courier».
| Что | Зачем |
|---|---|
| Зоны | В какие регионы возит |
| Группы | Каким группам покупателей доступен |
| Диапазоны веса или цены | Обязательны при need_range, даже если цену считает код |
Модули для распространения делают это в install() кодом — методами и моделями ядра addZone(), setGroups(), RangeWeight, RangePrice.
Как увидеть результат
php bin/console prestashop:module install acmeshipНастройте зоны и диапазоны, положите товар в корзину и дойдите до шага доставки: в списке появится «Acme Courier» с ценой 5 и сроком «2 дня».
Расчёт по корзине
// modules/acmeship/acmeship.php
public function getOrderShippingCost($cart, $shippingCost)
{
$weight = $cart->getTotalWeight();
if ($weight > 30) {
return false;
}
return 5.0 + $weight * 0.5;
}Стоимость теперь зависит от веса корзины. Значение false означает, что перевозчик для этой корзины недоступен, — вариант не покажется.
Смена номера перевозчика
Когда владелец магазина редактирует перевозчика, PrestaShop не меняет запись, а создаёт копию с новым номером — чтобы старые заказы ссылались на прежние условия. Модуль должен обновить сохранённый номер:
// modules/acmeship/acmeship.php
public function hookActionCarrierUpdate(array $params): void
{
if ((int) $params['id_carrier'] === (int) Configuration::get('ACMESHIP_CARRIER_ID')) {
Configuration::updateValue('ACMESHIP_CARRIER_ID', (int) $params['carrier']->id);
}
}В id_carrier приходит старый номер, в carrier — новый объект.
Частая ошибка
В getOrderShippingCost() обращаются к внешней службе доставки без кеша и без ограничения времени. Метод вызывается на каждой странице с корзиной, и медленный ответ службы тормозит весь магазин. Сохраняйте полученный тариф для пары «корзина — адрес» и ставьте короткий тайм-аут запроса.