С переходом на новую платформу интернет-магазина Битрикс D7 в службах доставки появились ограничения, которые позволяют ограничить действие доставки местоположениями, стоимостью или весом заказа и т.д.:
Изначально доступны несколько типов ограничений:
Но вы можете дополнить стандартный набор ограничений своими собственными. Для этого используется событие onSaleDeliveryRestrictionsClassNamesBuildList:
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler(
'sale',
'onSaleDeliveryRestrictionsClassNamesBuildList',
'myDeliveryRestrictions'
);
function myDeliveryRestrictions()
{
return new \Bitrix\Main\EventResult(
\Bitrix\Main\EventResult::SUCCESS,
array(
'\MyDeliveryRestriction' => '/path/to/your/class/restriction.php',
)
);
}
Минимальный класс ограничения службы доставки
Класс ограничения службы доставки должен быть унаследован от базового класса Bitrix\Sale\Delivery\Restrictions\Base. Но в папке /bitrix/modules/sale/lib/delivery/restrictions/ можно найти классы стандартных ограничений и унаследоваться от них при необходимости.
class MyRestriction extends Bitrix\Sale\Delivery\Restrictions\Base
{
public static function getClassTitle()
{
return 'Моё собственное ограничение';
}
public static function getClassDescription()
{
return 'Моё собственное ограничение для службы доставки';
}
protected static function extractParams(Bitrix\Sale\Shipment $shipment)
{
return null;
}
public static function getParamsStructure($deliveryId = 0)
{
return array();
}
public static function check($shipmentParams, array $restrictionParams, $deliveryId = 0)
{
return true;
}
}
Методы getClassTitle и getClassDescription возвращают название и описание ограничения.
Метод extractParams принимает объект отгрузки Bitrix\Sale\Shipment и должен подготовить необходимые данные для проверки ограничения и вернуть их. Далее эти данные передаются в метод check. В самом простом случае можно вернуть null. $shipment - объект отгрузки, для которой идёт подбор подходящих служб доставки. Примеры того, что можно получить из этого объекта:
protected static function extractParams(Bitrix\Sale\Shipment $shipment)
{
$someShipmentParams = array();
// Получаем товары в корзине:
foreach ($shipment->getShipmentItemCollection() as $shipmentItem) {
/** @var \Bitrix\Sale\BasketItem $basketItem - запись в корзине*/
$basketItem = $shipmentItem->getBasketItem();
// ...
}
// Получаем информацию о заказе:
/** @var \Bitrix\Sale\ShipmentCollection $collection - коллекция всех отгрузок в заказе */
$collection = $shipment->getCollection();
/** @var \Bitrix\Sale\Order $order - объект заказа*/
$order = $collection->getOrder();
// Получаем выбранные оплаты:
/** @var \Bitrix\Sale\Payment $payment - объект оплаты */
foreach($order->getPaymentCollection() as $payment) {
/** @var int $paySystemId - ID способа оплаты*/
$paySystemId = $payment->getPaymentSystemId();
// ...
$someShipmentParams["paySystem"] = $paySystemId;
}
//...
return $someShipmentParams;
}
Как работать с объектами корзины и заказа описано в соответствующих статьях: Объект корзины D7, Объект заказа D7.
Метод getParamsStructure должен возвращать массив параметров ограничения. Если ограничения не требует настройки, то возвращается пустой массив. Для примера допустим у ограничения есть три параметра:
public static function getParamsStructure($deliveryId = 0)
{
return array(
"MY_PARAM_CHECKBOX" => array(
'TYPE' => 'Y/N',
'VALUE' => 'Y',
'LABEL' => 'Галочка',
),
"MY_PARAM_ENUM" => array(
"TYPE" => "ENUM",
'MULTIPLE' => 'Y',
"OPTIONS" => array(1 => "Первый вариант", 2 => "Второй вариант"),
"LABEL" => 'Список',
),
"MY_PARAM_NUMBER" => array(
'TYPE' => 'NUMBER',
'DEFAULT' => "0",
'MIN' => 0,
'LABEL' => 'Число',
),
);
}
Метод check выполняет основную работу и должен возвращать true, если данная служба доставки доступна при данных параметрах. Первый аргумент - $shipmentParams - параметры отгрузки, те самые, которые вернул метод extractParams. Второй аргумент - $restrictionParams - параметры ограничения. Третий аргумент - $deliveryId - ID службы доставки. Например, extractParams вернул массив с ИД платежной системы, а в MY_PARAM_NUMBER пользователь ограничивает доставку необходимым ИД:
public static function check($shipmentParams, $restrictionParams, $deliveryId = 0)
{
if (intval($deliveryId) <= 0) {
return true;
}
return $restrictionParams["MY_PARAM_NUMBER"] == $shipmentParams["paySystem"];
}
Дополнительные возможности
В классе ограничения мы можем определить индекс сортировки, по умолчанию - 100. Чем выше индекс, тем позже это ограничение будет проверяться. Поэтому рекомендуется использовать индекс 100 для ограничений с простыми проверками (сравнение параметра с полем из заказа), индекс 200 для ограничений с проверками, содержащими запросы в БД и индекс 300 для ограничений с проверками, включающими тяжелые запросы и вычисления.
public static $easeSort = 100;
Если вы планируете значения параметров в своей таблице (сомнительная возможность, но может пригодиться), то можно переопределить следующие методы:
// Получение значений параметров
public static function prepareParamsValues(array $paramsValues, $deliveryId = 0)
{
// Достаём откуда-то значения
$paramsValues = array("MY_PARAM" => $value);
return $paramsValues;
}
// Сохранение ограничения
public static function save(array $fields, $restrictionId = 0)
{
$params = $fields["PARAMS"];
$fields["PARAMS"] = array();
$result = parent::save($fields, $restrictionId);
// Сохраняем куда надо
self::saveToYourPlace($fields["SERVICE_ID"], $params); // Здесь может быть ваш метод или код
return $result;
}
// Удаление ограничения
public static function delete($restrictionId, $deliveryId = 0)
{
self::deleteFromYourPlace($fields["SERVICE_ID"]); // Здесь может быть ваш метод или код
return parent::delete($restrictionId);
}
Эти методы переопределены, например, в ограничениях по местоположению. Как известно, ограничение по местоположению раньше было отдельной опцией и доставки и хранилось в отдельной таблице b_sale_delivery2location. В новых ограничениях с целью совместимости данная таблица еще существует и новое ограничение реализовано переопределением prepareParamsValues, save и delete.
Бонус
Кстати, об ограничении по местоположению. Если у вас много местоположений, и требуется ограничение "Все местоположения, кроме" можете воспользоваться моим вариантом. Это немного изменённое стандартное ограничение по местоположению:
// /bitrix/php_interface/init.php:
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler(
'sale',
'onSaleDeliveryRestrictionsClassNamesBuildList',
'myDeliveryRestrictions'
);
function myDeliveryRestrictions()
{
return new \Bitrix\Main\EventResult(
\Bitrix\Main\EventResult::SUCCESS,
array(
'\ExclLocationsDeliveryRestriction' => '/bitrix/php_interface/include/ExclLocationsDeliveryRestriction.php',
)
);
}
// /bitrix/php_interface/include/ExclLocationsDeliveryRestriction.php:
use Bitrix\Sale\Delivery\DeliveryLocationTable,
Bitrix\Sale\Internals\CollectableEntity,
Bitrix\Sale\Shipment;
class ExclLocationsDeliveryRestriction extends \Bitrix\Sale\Delivery\Restrictions\Base
{
public static $easeSort = 200;
public static function getClassTitle()
{
return 'По местоположению (Все, кроме)';
}
public static function getClassDescription()
{
return 'По местоположению (Все, кроме)';
}
public static function check($locationCode, array $restrictionParams, $deliveryId = 0)
{
if (intval($deliveryId) <= 0) {
return true;
}
if (strlen($locationCode) <= 0) {
return false;
}
try {
return !DeliveryLocationTable::checkConnectionExists(
intval($deliveryId),
$locationCode,
array(
'LOCATION_LINK_TYPE' => 'AUTO'
)
);
}
catch(\Bitrix\Sale\Location\Tree\NodeNotFoundException $e) {
return false;
}
}
protected static function extractParams(CollectableEntity $shipment)
{
$order = $shipment->getCollection()->getOrder();
if (!$props = $order->getPropertyCollection()) {
return '';
}
if (!$locationProp = $props->getDeliveryLocation()) {
return '';
}
if (!$locationCode = $locationProp->getValue()) {
return '';
}
return $locationCode;
}
protected static function prepareParamsForSaving(array $params = array(), $deliveryId = 0)
{
if ($deliveryId > 0) {
$arLocation = array();
if (!!\CSaleLocation::isLocationProEnabled()) {
if (strlen($params["LOCATION"]['L'])) {
$LOCATION1 = explode(':', $params["LOCATION"]['L']);
}
if (strlen($params["LOCATION"]['G'])) {
$LOCATION2 = explode(':', $params["LOCATION"]['G']);
}
}
if (isset($LOCATION1) && is_array($LOCATION1) && count($LOCATION1) > 0) {
$arLocation["L"] = array();
$locationCount = count($LOCATION1);
for ($i = 0; $i < $locationCount; $i++) {
if (strlen($LOCATION1[$i])) {
$arLocation["L"][] = $LOCATION1[$i];
}
}
}
if (isset($LOCATION2) && is_array($LOCATION2) && count($LOCATION2) > 0) {
$arLocation["G"] = array();
$locationCount = count($LOCATION2);
for ($i = 0; $i < $locationCount; $i++) {
if (strlen($LOCATION2[$i])) {
$arLocation["G"][] = $LOCATION2[$i];
}
}
}
DeliveryLocationTable::resetMultipleForOwner($deliveryId, $arLocation);
}
return array();
}
public static function getParamsStructure($deliveryId = 0)
{
$result = array(
"LOCATION" => array(
"TYPE" => "LOCATION_MULTI"
),
);
if ($deliveryId > 0) {
$result["LOCATION"]["DELIVERY_ID"] = $deliveryId;
}
return $result;
}
public static function save(array $fields, $restrictionId = 0)
{
$fields["PARAMS"] = self::prepareParamsForSaving($fields["PARAMS"], $fields["SERVICE_ID"]);
return parent::save($fields, $restrictionId);
}
public static function delete($restrictionId, $deliveryId = 0)
{
DeliveryLocationTable::resetMultipleForOwner($deliveryId);
return parent::delete($restrictionId);
}
}