Работа с заказом в Битрикс D7

Работа с объектом заказа SaleOrder в новом ядре Битрикс Д7, получение списка доставок, оплат, свойств заказа. Аналог CSaleOrder на D7

Заказ (SaleOrder)

Заказ представляет собой объект класса BitrixSaleOrder. Нужно запомнить, что пока не вызван метод save() этот объект необязательно связан с сохранённым заказом. Также пока вы не вызовете save(), изменения в заказе не будут сохранены в базе данных.

Существующий заказ можно получить следующим образом:

/** int $orderId ID заказа */$order = SaleOrder::load($orderId);/** mixed $orderNumber номер заказа */$order = SaleOrder::loadByAccountNumber($orderNumber);

Поля заказа можно получить короткими вызовами:

$order->getId(); // ID заказа$order->getSiteId(); // ID сайта$order->getDateInsert(); // объект BitrixMainTypeDateTime$order->getPersonTypeId(); // ID типа покупателя$order->getUserId(); // ID пользователя$order->getPrice(); // Сумма заказа$order->getDiscountPrice(); // Размер скидки$order->getDeliveryPrice(); // Стоимость доставки$order->getSumPaid(); // Оплаченная сумма$order->getCurrency(); // Валюта заказа$order->isPaid(); // true, если оплачен$order->isAllowDelivery(); // true, если разрешена доставка$order->isShipped(); // true, если отправлен$order->isCanceled(); // true, если отменен

Также любое поле по имени можно получить так:

$order->getField(“ORDER_WEIGHT”); // Вес заказа$order->getField(‘PRICE’); // Сумма заказа// Изменение поля:$order->setField(‘USER_DESCRIPTION’, ‘Комментарий к заказу’);$order->save();

Список доступных полей можно получить, вызвав $order->getAvailableFields().

Заказ имеет связь один-ко-многим с несколькими объектами в виде коллекций – коллекция товаров в корзине (SaleBasket), коллекция отгрузок (SaleShipmentCollection), коллекция оплат (SalePaymentCollection) и коллекция свойств заказа (SalePropertyValueCollection).

Самый простой способ получить список способов доставки и оплаты – короткие вызовы:

$paymentIds = $order->getPaymentSystemId(); // массив id способов оплат$deliveryIds = $order->getDeliverySystemId(); // массив id способов доставки

Чтобы получить список примененных к заказу скидок, нужно вызвать:

$discountData = $order->getDiscount()->getApplyResult();

В массиве $discountData[‘DISCOUNT_LIST’] содержится список скидок, в $discountData[‘COUPON_LIST’] содержится список купонов. Т.к. скидки можно отключать в админке, следует проверять поле APPLY: если Y – скидка/купон применёны, если N – были отключены менеджером.

Перехватываем событие формирования письма.

Но. мы можем просто перехватить событие, формирования письма, подмешать в него свои данные и отправить дальше. Для этого используем событие OnOrderNewSendEmail

Создаем событие в файле /local/php_iterface/init.php. Подсказки по коду прямо в нем

//СОБЫТИЕ
AddEventHandler(“sale”, “OnOrderNewSendEmail”, “ModifyOrderSaleMails”); function ModifyOrderSaleMails($orderID, &$eventName, &$arFields) {    if(CModule::IncludeModule(“sale”) && CModule::IncludeModule(“iblock”))    {  //СОСТАВ ЗАКАЗА РАЗБИРАЕМ SALE_ORDER НА ЗАПЧАСТИ       $strOrderList = “”;       $dbBasketItems = CSaleBasket::GetList(                  array(“NAME” => “ASC”),                  array(“ORDER_ID” => $orderID),                  false,                  false,                  array(“PRODUCT_ID”, “ID”, “NAME”, “QUANTITY”, “PRICE”, “CURRENCY”)                ); while ($arProps = $dbBasketItems->Fetch())  {  //ПЕРЕМНОЖАЕМ КОЛИЧЕСТВО НА ЦЕНУ      $summ = $arProps[‘QUANTITY’] * $arProps[‘PRICE’];  //СОБИРАЕМ В СТРОКУ ТАБЛИЦЫ       $strCustomOrderList .= “<tr><td>”.$arProps[‘NAME’].”</td><td>”.$arProps[‘QUANTITY’].”</td><td>”.$arProps[‘PRICE’].”</td><td>”.$arProps[‘CURRENCY’].”</td><td>”.$summ.”</td><tr>”;  }  //ОБЪЯВЛЯЕМ ПЕРЕМЕННУЮ ДЛЯ ПИСЬМА  $arFields[“ORDER_TABLE_ITEMS”] = $strCustomOrderList; } }

Вот и все. Мы создали новую переменную #ORDER_TABLE_ITEMS# для письма, в которую можем подмешивать любые данные, в моем случае просто строка таблицы. Которую можно красиво стилизовать.

snimok-ekrana-2017_02_18-v-1.48.39.png

Само собой, вы можете создать несколько переменных с отдельными данным, если нужно сильно разнести состав заказа по письму. Вот так:

while ($arProps = $dbBasketItems->Fetch()) {  //ДРОБИМ НА ЗАПЧАСТИ  $name = $arProps[‘NAME’];  $quantity = $arProps[‘QUANTITY’];  $price = $arProps[‘PRICE’];  $currency = $arProps[‘CURRENCY’];  $summ = $arProps[‘QUANTITY’] * $arProps[‘PRICE’];  }  //ОБЪЯВЛЯЕМ НОВЫЕ ПЕРМЕННЫЕ ДЛЯ ПИСЬМА  $arFields[“NAME_ITEM”] = $name;  $arFields[“QUANTITY_ITEM”] = $quantity;  $arFields[“PRICE_ITEM”] = $price;  $arFields[“CURRENCY_ITEM”] = $currency;  $arFields[“SUMM_ITEM”] = $sum;

И в принципе, перехватывая данное письмо, можно добавлять любые данные в письмо. От данных плательщика, местоположения, телефона итд. Тех переменных которых нет в типовом шаблоне письма

Таким образом и сделали что хотели, и не “поломали” компонент оформления заказа

Вебинар:

Трансформации начались с момента выпуска совершенно нового движка магазина. Чтобы обновиться, необходимо было провести конвертацию заказов.

Корзина заказа (SaleBasket)

Про работу с корзиной была отдельная статья, привязать и получить корзину заказа можно так:

/** SaleBasket $basket */$order->setBasket($basket);$basket = $order->getBasket();

Проблемы и запросы клиентов:

  • «Длинные» заказы у крупного бизнеса == громоздкие запросы в большом количестве;
  • Нужны частичные отгрузки, нужны частичные оплаты;
  • Архив заказов;
  • Перевод публичных и админских механизмов на одинаковый механизм и логику.

Одна из характеристик нового подхода: обрабатывать во время записи только те данные, которые реально обновились.

Свойства заказа (SalePropertyValueCollection)

Свойства заказа – объекты BitrixSalePropertyValue – собраны в коллекции propertyCollection

$propertyCollection = $order->getPropertyCollection();

Получить значения всех свойств и группы свойств можно так:

$ar = $propertyCollection->getArray(); // массив [‘properties’ => [..], ‘groups’ => [..] ];$ar = $propertyCollection->getGroups(); // массив групп$ar = $propertyCollection->getGroupProperties($groupId); // массив свойств, относящихся к группе

У многих свойств заказа есть определенное встроенное назначение (атрибуты IS_EMAIL, IS_PAYER, IS_LOCATION, IS_LOCATION4TAX, IS_PROFILE_NAME, IS_ZIP, IS_PHONE, IS_ADDRESS). Такие свойства можно получить следующими методами:

$emailPropValue = $propertyCollection->getUserEmail();$namePropValue = $propertyCollection->getPayerName();$locPropValue = $propertyCollection->getDeliveryLocation();$taxLocPropValue = $propertyCollection->getTaxLocation();$profNamePropVal = $propertyCollection->getProfileName();$zipPropValue = $propertyCollection->getDeliveryLocationZip();$phonePropValue = $propertyCollection->getPhone();$addrPropValue = $propertyCollection->getAddress();

Получить значение свойства по ID:

$somePropValue = $propertyCollection->getItemByOrderPropertyId($orderPropertyId);

В любом случае получаем значение свойства – экземпляр класса BitrixSalePropertyValue. Из него мы можем получить значение свойства:

$somePropValue->getValue(); // значение свойства$somePropValue->getViewHtml(); // представление значения в читаемом виде (напр. для местоположения – путь страна, регион, город)

И информацию о самом свойстве:

$arProp = $somePropValue->getProperty(); // массив данных о самом свойстве$propId = $somePropValue->getPropertyId(); // ID свойства$propName = $somePropValue->getName(); // Название$isRequired = $somePropValue->isRequired(); // true, если свойство обязательное$propPerson = $somePropValue->getPersonTypeId(); // Тип плательщика$propGroup = $somePropValue->getGroupId(); // ID группы

Чтобы изменить значение свойства следует вызвать метод setValue и сохранить сущность

$somePropValue->setValue(“value”);$order->save(); // можно $somePropValue->save(), но пересчета заказа не произойдёт

Два основных класса

BitrixSaleOrder и BitrixSaleBasket

Специфика: работать с объектом как с единым целым и сохранять данные только у головной сущности. Механика нового подхода сама решит, что внутри изменилось, что требует сохранения в базу данных и пересчёт.

¶Примеры кода и ошибок:

Исходная ситуация: получаем текущий заказ и добавляем товар

BitrixMainLoader::includeModule(‘sale’);$orderId = 752;$order = BitrixSaleOrder::load($orderId);$productId = 412;$basket = $order->getBasket();$basketItem = $basket->createItem(‘catalog’, $productId);// Вариант 1:// Устанавливаем количество товаров 5$basketItem->setField(‘QUANTITY’, 5);// Ставим цену и маркируем её как кастомную$basketItem->setPrice(175, true);// Вариант 2:$basketItem->setFields([ ‘QUANTITY’ => 5, ‘PRICE’ => 175, ‘CUSTOM_PRICE’ => ‘Y’]);// Сохраняем головную сущность$order->save();

Ошибка:

// Головной объект (заказ)// фактически не обновлён// и значит не произведены// необходимые пересчёты$basket->save();

Излишне (двойное сохранение):

$basket->save();$order->save();

Правильно:

$order->save();

Оплаты заказа (SalePaymentCollection)

$paymentCollection = $order->getPaymentCollection();

Из коллекции оплат также можно получить информацию об оплате, что и из объекта заказа. Оплата с внутреннего счета также считается одной из оплат:

$paymentCollection->isPaid(); // true, если все оплаты оплачены$paymentCollection->hasPaidPayment(); // true, если хотя бы одна оплата оплачена$paymentCollection->getPaidSum(); // оплаченная сумма$paymentCollection->isExistsInnerPayment(); // true, если осуществлена оплата с внутреннего счета

Коллекция содержит объекты оплаты SalePayment с информацией об оплатах:

foreach ($paymentCollection as $payment) { $sum = $payment->getSum(); // сумма к оплате $isPaid = $payment->isPaid(); // true, если оплачена $isReturned = $payment->isReturn(); // true, если возвращена $ps = $payment->getPaySystem(); // платежная система (объект SalePaySystemService) $psID = $payment->getPaymentSystemId(); // ID платежной системы $psName = $payment->getPaymentSystemName(); // название платежной системы $isInnerPs = $payment->isInner(); // true, если это оплата с внутреннего счета}

Оплатить или вернуть оплату можно методами setPaid(), setReturn():

$onePayment = $paymentCollection[0];$onePayment->setPaid(“N”); // отмена оплаты$onePayment->setPaid(“Y”); // оплата$onePayment->setReturn(“Y”); // возврат (деньги возвращаются на внутренний счет или в платежную систему, если обработчик реализует интерфейс SalePaySystemIRefund)// после любых действий нужно сохранить сущность:$order->save();

Инициировать оплату (вывести шаблон оплаты: форму, кнопку и т.п.) можно следующим образом:

$service = SalePaySystemManager::getObjectById($onePayment->getPaymentSystemId());$context = BitrixMainApplication::getInstance()->getContext();$service->initiatePay($onePayment, $context->getRequest());

¶Современный подход

После выпуска топ-редакции (1С-Битрикс24: Интернет-магазин+CRM), чтобы код работы с главными сущностями интернет-магазина был универсальным, следует работать с реестром классов:

use BitrixSaleBasketBase;use BitrixSaleFuser;use BitrixSaleOrderBase;use BitrixSaleRegistry;// Получаем объект, заведующий реестрами$registry = Registry::getInstance(Registry::REGISTRY_TYPE_ORDER);/** * @var $orderClass OrderBase * @var $basketClass BasketBase */// Получаем класс работы с заказами$orderClass = $registry->getOrderClassName();// Получаем класс работы с корзинами$basketClass = $registry->getBasketClassName();// Читаем заказ$order = $orderClass::load($orderId);// Читаем корзину из объекта заказа$basket = $order->getBasket();// Читаем корзину через класс по объекту заказа$basket = $basketClass::loadItemsForOrder($order);// Читаем корзину для текущего покупателя$currentFUser = Fuser::getId();$basket = $basketClass::loadItemsForFUser($currentFUser, SITE_ID);/** * @var BitrixSaleBasketItem $bi */// Добываем данные по полям товаров в корзинеforeach ($basket->getBasketItems() as $bi) { $basketProductFields = $bi->getFieldValues();}

Использование библиотеки регистров открывает возможности по кастомизации локальных движков методом регистрации своих классов-обработчиков

¶Список стандартных классов в реестре заказов:

Registry::ENTITY_ORDER =>’BitrixSaleOrder’,Registry::ENTITY_PAYMENT =>’BitrixSalePayment’,Registry::ENTITY_PAYMENT_COLLECTION =>’BitrixSalePaymentCollection’,Registry::ENTITY_SHIPMENT =>’BitrixSaleShipment’,Registry::ENTITY_SHIPMENT_COLLECTION =>’BitrixSaleShipmentCollection’,Registry::ENTITY_SHIPMENT_ITEM =>’BitrixSaleShipmentItem’,Registry::ENTITY_SHIPMENT_ITEM_COLLECTION =>’BitrixSaleShipmentItemCollection’,Registry::ENTITY_SHIPMENT_ITEM_STORE =>’BitrixSaleShipmentItemStore’,Registry::ENTITY_SHIPMENT_ITEM_STORE_COLLECTION =>’BitrixSaleShipmentItemStoreCollection’,Registry::ENTITY_PROPERTY_VALUE_COLLECTION =>’BitrixSalePropertyValueCollection’,Registry::ENTITY_PROPERTY_VALUE =>’BitrixSalePropertyValue’,Registry::ENTITY_PROPERTY =>’BitrixSaleProperty’,Registry::ENTITY_TAX =>’BitrixSaleTax’,Registry::ENTITY_BASKET_PROPERTY_ITEM =>’BitrixSaleBasketPropertyItem’,Registry::ENTITY_BUNDLE_COLLECTION =>’BitrixSaleBundleCollection’,Registry::ENTITY_BASKET =>’BitrixSaleBasket’,Registry::ENTITY_BASKET_ITEM =>’BitrixSaleBasketItem’,Registry::ENTITY_BASKET_PROPERTIES_COLLECTION =>’BitrixSaleBasketPropertiesCollection’,Registry::ENTITY_DISCOUNT =>’BitrixSaleDiscount’,Registry::ENTITY_DISCOUNT_COUPON =>’BitrixSaleDiscountCouponsManager’,Registry::ENTITY_ORDER_DISCOUNT =>’BitrixSaleOrderDiscount’,Registry::ENTITY_OPTIONS =>’BitrixMainConfigOption’,Registry::ENTITY_PERSON_TYPE =>’BitrixSalePersonType’,Registry::ENTITY_ORDER_STATUS =>’BitrixSaleOrderStatus’,Registry::ENTITY_DELIVERY_STATUS =>’BitrixSaleDeliveryStatus’,Registry::ENTITY_ENTITY_MARKER =>’BitrixSaleEntityMarker’,Registry::ENTITY_ORDER_HISTORY =>’BitrixSaleOrderHistory’,Registry::ENTITY_NOTIFY =>’BitrixSaleNotify’,Registry::ENTITY_TRADE_BINDING_COLLECTION =>’BitrixSaleTradeBindingCollection’,Registry::ENTITY_TRADE_BINDING_ENTITY =>’BitrixSaleTradeBindingEntity’,

Получение класса из реестра:

Битрикс. Магазин, заказы, корзина. Получение класса из реестра

¶Переопределение класса-обработчика:

Событие для проброса своих классов-обработчиков:

use BitrixMainEventManager;(EventManager::getInstance()) ->addEventHandler( ‘main’, ‘OnInitRegistryList’, [ ‘AlexeyGfiBasketHelper’, ‘registerCustomInstances’ ] );

В методе BitrixSaleRegistry::initRegistry результат обработчика будет с-array_merge поверх стандартного списка:

Битрикс. Магазин, заказы, корзина. Переопределение класса в реестре обработчиков

Пример оформления заказа в D7

Для примера приведу простейший код оформления заказа с комментариями

use BitrixMainContext, BitrixCurrencyCurrencyManager, BitrixSaleOrder, BitrixSaleBasket, BitrixSaleDelivery, BitrixSalePaySystem;global $USER;BitrixMainLoader::includeModule(“sale”);BitrixMainLoader::includeModule(“catalog”);// Допустим некоторые поля приходит в запросе$request = Context::getCurrent()->getRequest();$productId = $request[“PRODUCT_ID”];$phone = $request[“PHONE”];$name = $request[“NAME”];$comment = $request[“COMMENT”];$siteId = Context::getCurrent()->getSite();$currencyCode = CurrencyManager::getBaseCurrency();// Создаёт новый заказ$order = Order::create($siteId, $USER->isAuthorized() ? $USER->GetID() : 539);$order->setPersonTypeId(1);$order->setField(‘CURRENCY’, $currencyCode);if ($comment) { $order->setField(‘USER_DESCRIPTION’, $comment); // Устанавливаем поля комментария покупателя}// Создаём корзину с одним товаром$basket = Basket::create($siteId);$item = $basket->createItem(‘catalog’, $productId);$item->setFields(array( ‘QUANTITY’ => 1, ‘CURRENCY’ => $currencyCode, ‘LID’ => $siteId, ‘PRODUCT_PROVIDER_CLASS’ => ‘CCatalogProductProvider’,));$order->setBasket($basket);// Создаём одну отгрузку и устанавливаем способ доставки – “Без доставки” (он служебный)$shipmentCollection = $order->getShipmentCollection();$shipment = $shipmentCollection->createItem();$service = DeliveryServicesManager::getById(DeliveryServicesEmptyDeliveryService::getEmptyDeliveryServiceId());$shipment->setFields(array( ‘DELIVERY_ID’ => $service[‘ID’], ‘DELIVERY_NAME’ => $service[‘NAME’],));$shipmentItemCollection = $shipment->getShipmentItemCollection();$shipmentItem = $shipmentItemCollection->createItem($item);$shipmentItem->setQuantity($item->getQuantity());// Создаём оплату со способом #1$paymentCollection = $order->getPaymentCollection();$payment = $paymentCollection->createItem();$paySystemService = PaySystemManager::getObjectById(1);$payment->setFields(array( ‘PAY_SYSTEM_ID’ => $paySystemService->getField(“PAY_SYSTEM_ID”), ‘PAY_SYSTEM_NAME’ => $paySystemService->getField(“NAME”),));// Устанавливаем свойства$propertyCollection = $order->getPropertyCollection();$phoneProp = $propertyCollection->getPhone();$phoneProp->setValue($phone);$nameProp = $propertyCollection->getPayerName();$nameProp->setValue($name);// Сохраняем$order->doFinalAction(true);$result = $order->save();$orderId = $order->getId();

Подробнее можно прочитать по этой ссылке или в компоненте sale.order.ajax.

Читайте также:

  • Работа с корзиной в D7
Рейтинг
( 1 оценка, среднее 5 из 5 )
Загрузка ...