1. Ключевые понятия
FUSER
FUSER (сокращение от "Fuser ID") — уникальный идентификатор покупателя, не зависящий от авторизации пользователя. Он обеспечивает работу корзины для анонимных пользователей и связывает корзину с сессией или пользовательским аккаунтом после входа.
Ключевые моменты о FUSER
-
Назначение
-
Отслеживает корзину в сессии анонимного пользователя.
-
После авторизации автоматически связывается с ID пользователя (
USER_ID), обеспечивая перенос корзины. -
Хранится в cookies (
SALE_UID) или сессии (SALE_USER_ID).
-
-
Как получить FUSER Основные методы класса
Bitrix\Sale\Fuser:use Bitrix\Sale\Fuser; // Текущий покупатель (создаёт ID, если не существует) $fuserId = Fuser::getId(); // Для конкретного пользователя (например, после авторизации) $userId = 123; $fuserId = Fuser::getIdByUserId($userId);Важно! Всегда используйте в сочетании с ID сайта:
$siteId = \Bitrix\Main\Context::getCurrent()->getSite(); // Для публичной части $basket = \Bitrix\Sale\Basket::loadItemsForFUser($fuserId, $siteId);Связь FUSER и пользователя
-
Анонимный режим: FUSER генерируется при первом добавлении товара и сохраняется в cookies.
-
Авторизация: Вызов
Fuser::getIdByUserId($userId)связывает корзину с аккаунтом, предотвращая дублирование 39.
-
Примечание: getSite работает только в публичной части.
Методы класса Fuser
-
getId()- Возвращает ID текущего покупателя (создаёт при необходимости)$fuserId = Fuser::getId(); -
getIdByUserId()- Возвращает FUSER для указанного пользователя$fuserId = Fuser::getIdByUserId(123); -
getUserIdById()- Возвращает USER_ID по FUSER (для поиска привязанных аккаунтов)$userId = Fuser::getUserIdById($fuserId); -
refreshSessionCurrentId()- Обновляет FUSER в сессии (например, после авторизации)Fuser::refreshSessionCurrentId();
Практические примеры
Создание корзины с привязкой к FUSER:
if (\Bitrix\Main\Loader::includeModule('sale')) {
$siteId = \Bitrix\Main\Context::getCurrent()->getSite();
$fuserId = \Bitrix\Sale\Fuser::getId();
$basket = \Bitrix\Sale\Basket::loadItemsForFUser($fuserId, $siteId);
}
``` :cite[1]:cite[8].
**Перенос корзины при авторизации:**
```php
// После входа пользователя
$userId = $USER->GetID();
$newFuserId = \Bitrix\Sale\Fuser::getIdByUserId($userId);
$basket->setFUserId($newFuserId);
$basket->save();
``` :cite[3]:cite[6].
---
#### **Ошибки и решения**
- **Проблема:** Корзина не сохраняется между сессиями.
**Решение:** Убедитесь, что FUSER записан в cookies:
```php
// Проверка настроек модуля sale
if (!\Bitrix\Main\Config\Option::get("sale", "encode_fuser_id")) {
// Включить шифрование FUSER в настройках
}
``` :cite[2]:cite[4].
- **Проблема:** Дублирование товаров после авторизации.
**Решение:** Всегда вызывайте `Fuser::getIdByUserId()` после входа и обновляйте корзину :cite[3].
---
#### **Итог**
**FUSER** — системный компонент для управления корзиной, критичный для работы интернет-магазина. Ключевые правила:
1. Используйте `Fuser::getId()` для анонимных операций.
2. Привязывайте корзину к аккаунту через `Fuser::getIdByUserId()`.
3. Всегда передавайте `SITE_ID` при загрузке корзины :cite[1]:cite[8].
```php
// Минимальный рабочий пример
$basket = \Bitrix\Sale\Basket::loadItemsForFUser(
\Bitrix\Sale\Fuser::getId(),
\Bitrix\Main\Context::getCurrent()->getSite()
);
Корзина пользователя vs Корзина заказа в Bitrix D7
В корзину по дефолту попадает товар с наименьшей возможной, для пользователя, ценой.
В системе Bitrix существуют два типа корзин:
-
Пользовательская корзина (активная)
-
Не привязана к заказу
-
Хранится в сессии/FUSER
-
Изменяется до оформления заказа
-
-
Корзина заказа (историческая)
-
Привязана к конкретному заказу (ORDER_ID)
-
Фиксируется при оформлении
-
Доступна только для чтения
-
Важно!
Сохраняем корзину:
\$order->save(); // корзина ПРИВЯЗАНА к заказу \$basket->save(); // корзина НЕ привязана к заказу
Сохранять корзину нужно после всех изменений.
1. Работа с пользовательской корзиной (не привязанной к заказу)
Получение:
use Bitrix\Sale\Basket;
use Bitrix\Sale\Fuser;
$siteId = \Bitrix\Main\Context::getCurrent()->getSite();
$fuserId = Fuser::getId();
// Загрузка активной корзины текущего покупателя
$basket = Basket::loadItemsForFUser($fuserId, $siteId);
Основные операции
Добавление товара:
$item = $basket->createItem('catalog', 123); // ID товара
$item->setFields([
'QUANTITY' => 2,
'CURRENCY' => Bitrix\Currency\CurrencyManager::getBaseCurrency(),
'LID' => $siteId
]);
$basket->save();
Обновление количества:
if ($item = $basket->getItemById(456)) { // ID элемента корзины
$item->setField('QUANTITY', 3);
$basket->save();
}
Удаление товара:
if ($item = $basket->getItemById(456)) {
$item->delete();
$basket->save();
}
Очистка корзины:
$basket->clearCollection();
$basket->save();
Получение содержимого:
foreach ($basket as $item) {
echo "Товар: " . $item->getField('NAME')
. ", Количество: " . $item->getQuantity();
}
Общая стоимость:
$totalPrice = $basket->getPrice();
Применение купона:
use Bitrix\Sale\DiscountCouponsManager;
DiscountCouponsManager::add("DISCOUNT10");
Сохранение изменений:
// Пример: добавление товара
$item = $basket->createItem('catalog', 123);
$item->setFields([
'QUANTITY' => 2,
'CURRENCY' => Bitrix\Currency\CurrencyManager::getBaseCurrency(),
'LID' => $siteId
]);
// Сохранение в базу
$result = $basket->save();
if (!$result->isSuccess()) {
$errors = $result->getErrorMessages();
}
Особенности:
-
Хранится в таблице
b_sale_basketсORDER_ID = NULL -
Автоматически очищается после оформления заказа (настраивается в модуле "Интернет-магазин")
2. Работа с корзиной заказа (привязанной)
Получение через заказ:
use Bitrix\Sale;
$orderId = 789; // ID существующего заказа
$order = Sale\Order::load($orderId);
// Получаем привязанную корзину
$orderBasket = $order->getBasket();
Особенности:
-
Доступна только для чтения (
save()недоступен) -
Данные хранятся в таблицах:
-
b_sale_order- заказ -
b_sale_basket- элементы корзины (сORDER_ID = $orderId) -
b_sale_basket_props- свойства элементов
-
Экспорт данных:
$items = [];
foreach ($orderBasket as $item) {
$items[] = [
'NAME' => $item->getField('NAME'),
'PRICE' => $item->getPrice(),
'QUANTITY' => $item->getQuantity(),
'PROPS' => $item->getPropertyCollection()->getArray()
];
}
print_r($items);
3. Конвертация пользовательской корзины в корзину заказа
Происходит автоматически при оформлении. Ручное копирование:
// Создаем новый заказ
$order = Sale\Order::create($siteId, $userId);
$order->setPersonTypeId(1); // Тип плательщика
// Клонируем корзину
$clonedBasket = Basket::create($siteId);
foreach ($basket as $item) {
$newItem = $clonedBasket->createItem($item->getField('MODULE'), $item->getProductId());
$newItem->setFields($item->getFields());
}
// Привязываем к заказу
$order->setBasket($clonedBasket);
$order->save(); // Теперь это корзина заказа!
4. Сравнение типов корзин
| Характеристика | Пользовательская корзина | Корзина заказа |
| Статус | Активная, изменяемая | Зафиксированная |
| Хранение |
b_sale_basket (ORDER_ID=NULL)
|
Привязана к заказу |
| Изменение товаров | Разрешено | Запрещено |
| Доступ через |
Fuser::getId()
|
Order::load()
|
| Автоочистка | Да (после оформления) | Нет |
| Сохранение |
$basket->save();
|
$order->save();
|
2. Сравнение Legacy и D7 API
Добавление товара
Legacy:
Add2BasketByProductID(123, 2);
D7:
$basket->createItem('catalog', 123)->setFields(['QUANTITY' => 2]);
$basket->save();
Обновление количества
Legacy:
CSaleBasket::Update(456, ["QUANTITY" => 3]);
D7:
$basket->getItemById(456)->setField('QUANTITY', 3);
$basket->save();
3. Практические примеры
Информация о корзине
$price = $basket->getPrice(); // Цена с учетом скидок
$fullPrice = $basket->getBasePrice(); // Цена без учета скидок
$weight = $basket->getWeight(); // Общий вес корзины
$basket->getVatSum(); // Сумма НДС
$basket->getVatRate(); // Ставка НДС
$basket->getFUserId(); // Id покупателя
$basket->getOrder(); // Заказ, к которому привязана
// Товары
$basketItems = $basket->getBasketItems(); // Все товары
$basketItemsOrderable = $basket->getOrderableItems(); // Только товары, доступные для заказа
$arBasketInfo = $basket->getListOfFormatText(); // Массив товаров в читаемом виде
$arQuantityList = $basket->getQuantityList();// Массив вида basketId - количество
Получить количество товаров в корзине
array_sum($basket->getQuantityList()); // количество товаров в корзине
count($basket->getQuantityList()); // количество позиций в корзине
Копирование корзины
//Если привязана к заказу, то скопирует вместе с заказом
$basket->createClone();
//скопирует, только если корзина не привязана к заказу
$basket->copy();
Создать заказ из пользовательской корзины
$order = Sale\Order::create($siteId, $userId);
$order->setBasket($basket); // Автоматическая конвертация
// Установка других параметров заказа
$order->setField('CURRENCY', 'RUB');
$order->setField('USER_DESCRIPTION', 'Комментарий');
// Финализация
if ($order->save()->isSuccess()) {
$basket->clearCollection(); // Очистка пользовательской корзины
$basket->save();
}
Получить все корзины пользователя (включая исторические)
$dbRes = Sale\Basket::getList([
'filter' => [
'FUSER_ID' => Fuser::getId(),
'ORDER_ID' => null // Только активные корзины
// Для исторических: '!ORDER_ID' => null
],
'select' => ['ID', 'PRODUCT_ID', 'QUANTITY']
]);
while ($item = $dbRes->fetch()) {
// Обработка элементов
}
Добавить товар в корзину. Обновить запись и проверить наличие
/** int $productId ID товара */
/** int $quantity количество */
if ($item = $basket->getExistsItem('catalog', $productId)) {
$item->setField('QUANTITY', $item->getQuantity() + $quantity);
}
else {
$item = $basket->createItem('catalog', $productId);
$item->setFields(array(
'QUANTITY' => $quantity,
'CURRENCY' => Bitrix\Currency\CurrencyManager::getBaseCurrency(),
'LID' => Bitrix\Main\Context::getCurrent()->getSite(),
'PRODUCT_PROVIDER_CLASS' => 'CCatalogProductProvider',
));
}
$basket->save();
Добавить товар с произвольной (кастомной ценой)
$item->setFields(array(
'QUANTITY' => $quantity,
'CURRENCY' => Bitrix\Currency\CurrencyManager::getBaseCurrency(),
'LID' => Bitrix\Main\Context::getCurrent()->getSite(),
'PRICE' => $customPrice,
'CUSTOM_PRICE' => 'Y', // см. сюда!!!
));
В последних версиях модуля catalog
Bitrix\Main\Loader::includeModule("catalog");
$fields = [
'PRODUCT_ID' => 98, // ID товара, обязательно
'QUANTITY' => 2, // количество, обязательно
'PROPS' => [
['NAME' => 'Test prop', 'CODE' => 'TEST_PROP', 'VALUE' => 'test value'],
],
];
$r = Bitrix\Catalog\Product\Basket::addProduct($fields);
if (!$r->isSuccess()) {
var_dump($r->getErrorMessages());
}
Данный метод проверяет доступность товара к покупке (при отсутствии будет возвращен результат с ошибкой "Товар отсутствует"), сам проверяет наличие товара в корзине и при наличии увеличивает количество товара в корзине.
Также к товару добавляются свойства корзины, необходимые для обмена с 1С: PRODUCT.XML_ID и CATALOG.XML_ID.
Но при этом нет возможности передать в корзину кастомную цену.
Товары корзины формируем в массив
$arItems = array();
foreach ($basket as $item) {
$arItems[] = array(
"ID" => $item->getProductId(),
"COUNT" => $item->getQuantity(),
"SUM" => $item->getFinalPrice(),
"PRICE" => $item->getPrice()
);
}
Получить запись по ID и удалить запись из корзины (аналог CSaleBasket::Delete**)**
/** int $id ID записи */
$basket->getItemById($id)->delete();
$basket->save();
Получить товары в корзине, доступные для покупки (CAN_BUY=Y)
$orderBasket = $basket->getOrderableItems();
Методы форматирования корзины для писем и отчётов
$formattedItems = $basket->getListOfFormatText();
var_dump($formattedItems);
/* Результат:
array(2) {
[ID] => "Кольцо [Размер: 18] - 1 : 14 800 руб."
[ID] => "Браслет - 4 : 6 000 руб."
}
*/
$quantityData = $basket->getQuantityList(); // Массив количеств
var_dump($quantityData);
/* Результат:
array(2) {
[ID] => float(1) // 1 единица товара ID=*
[ID] => float(4) // 4 единицs товара ID=*
}
*/
4. Товары в корзине D7 (объект Bitrix\Sale\BasketItem)
1. Архитектура корзины и модель данных
В ядре D7 корзина представлена объектом Bitrix\Sale\Basket, содержащим коллекцию элементов типа Bitrix\Sale\BasketItem. Каждый элемент инкапсулирует данные о товаре, его свойствах и состоянии в корзине. Ключевые особенности 17:
-
Коллекция реализует интерфейсы
\ArrayAccess,\Countable,\IteratorAggregate, что позволяет обрабатывать элементы как массив. -
Для работы требуется подключенный модуль
sale:if (!\Bitrix\Main\Loader::includeModule('sale')) throw new \Exception('Модуль sale не доступен'); -
Корзина загружается через статический метод:
$basket = \Bitrix\Sale\Basket::loadItemsForFUser( \Bitrix\Sale\Fuser::getId(), \Bitrix\Main\Context::getCurrent()->getSite() );
2. Получение элементов корзины
2.1. Доступ к коллекции:
// Получение как массива объектов BasketItem
$basketItems = $basket->getBasketItems();
// Итерация через foreach (благодаря IteratorAggregate)
foreach ($basket as $basketItem) {
$productName = $basketItem->getField('NAME');
$quantity = $basketItem->getQuantity();
}
2.2. Фильтрация элементов:
-
getOrderableItems()– возвращает товары сCAN_BUY='Y'(исключает отложенные и недоступные). -
getExistsItem($moduleId, $productId)– проверяет наличие товара в корзине.
3. Методы объекта BasketItem: данные элемента
$item = $basketItems[0];
$item->getId(); // ID записи в корзине
$item->getProductId(); // ID товара
$item->getQuantity(); // Количество
$item->getWeight(); // Вес
$item->getField('NAME');// Любое поле товара в корзине
$item->canBuy(); // true, если доступно для покупки
$item->isDelay(); // true, если отложено
// Все поля элемента корзины
$item->getFields(); // Вариант 1
$item->getFieldsCatalogProduct(); // Вариант 2
$item->getField('PRICE') // Получение значения любого поля
$item->getBasketCode() // Код корзины
$item->getFUserId() // id владельца корзины
$item->getCurrency() // Код валюты
$item->getFinalPrice(); // Сумма
$item->getPrice(); // Цена за единицу, цена с учетом скидок
$item->getBasePrice() // Цена без учета скидок
$item->getDefaultPrice() // Цена по умолчанию
$item->getDiscountPrice() // Величина скидки
$item->isCustomPrice() // Цена указана вручную (без использования провайдера)
$item->getVatRate() // Ставка НДС
$item->getPriceWithVat() // Цена с НДС
$item->getBasePriceWithVat() // Базовая цена с НДС
$item->getInitialPrice() // Исходная цена без НДС
$item->getVat() // НДС
$item->isVatInPrice() // Цена включает НДС
$item->getCallbackFunction() // Поле "CALLBACK_FUNC"
$item->getProviderEntity() // Объект провайдера
$item->getProvider() // Класс провайдера
$item->getAvailableFields() // Массив кодов всех полей
$item->getSettableFields() // Массив кодов изменяемых полей
$item->getCalculatedFields() // Массив кодов генерируемых полей
$item->isCalculatedField('FIELD_NAME') // Поле является генерируемым
$item->isBarcodeMulti()
$item->getPropertyCollection(); // Свойства товара в корзине, коллекция объектов Sale\BasketPropertyItem, см. ниже
$item->getCollection(); // Корзина, в которой лежит товар
Получить товар в корзине по id позиции в корзине (не путать с id товара)
$item = $basket->getItemById($id);
получить товар в корзине по id товара
$item = $basket->getExistsItem('catalog', $productId); // Вернет товар, если он без свойств в корзине
$item = $basket->getExistsItem('catalog', $productId, $properties); // Вернет товар, с такими же $properties
Вернуть товар в корзине по productId
если товаров несколько, только с разными свойствами, вернет первый
function GetExistsBasketItem($basket,$moduleId,$productId){
$result = false;
if(!empty($productId) && (intval($productId)>0) && (intval($productId)==$productId) && ($moduleId!='')){
foreach ($basket as $item) {
if($productId == $item->getProductId() && ($item->getField('MODULE') == $moduleId)){
$result = $item;
break;
}
}
}
return $result;
}
$item = GetExistsBasketItem($basket,'catalog',$productID); // Вернет false, если нет такого
Получить название единицы измерения товара
$arProductIds = array($productId1,$productId2); // ID товаров
$arMeasure = \Bitrix\Catalog\ProductTable::getCurrentRatioWithMeasure($arProductIds);
echo $arMeasure[$productId1]['MEASURE']['SYMBOL_RUS'];
echo $arMeasure[$productId2]['MEASURE']['SYMBOL_RUS'];
Действия над товаром
$item->setField('QUANTITY', $quantity); // Изменение поля
$item->setFields(array(
'QUANTITY' => $quantity,
'CUSTOM_PRICE' => $customPrice,
)); // Изменение полей
$item->delete(); // Удаление
$item->save(); // Сохранение изменения, можно использовать и $basket->save();
4. Свойства товаров в корзине ( объект Sale\BasketPropertiesCollection)
/** Sale\BasketItem $item объект товара в корзине */
$basketPropertyCollection = $item->getPropertyCollection();
$basketPropertyCollection->getPropertyValues(); // Возвращает массив свойств $basketPropertyCollection = $item->getPropertyCollection(); // \Bitrix\Sale\BasketPropertyItem
foreach ($basketPropertyCollection as $basketProperty){
$name = $basketProperty->getField('NAME')); // любое поле свойства 'NAME','CODE','VALUE','SORT','XML_ID'
}
$basketPropertyCollection->getPropertyValues(); // возвращает массив свойств
$prolong = $basketItem->getPropertyCollection()->getPropertyValues()["prolong"]["NAME"];// можно сразу к свойству обратиться
Добавить новое свойство или изменить существующие можно следующим образом
$basketPropertyCollection->setProperty(array(
array(
'NAME' => 'Цвет',
'CODE' => 'COLOR',
'VALUE' => 'Кофе с молоком',
'SORT' => 100,
),
));
$basketPropertyCollection->save();
Пример удаления свойства
foreach ($basketPropertyCollection as $propertyItem) {
if ($propertyItem->getField('CODE') == 'COLOR') {
$propertyItem->delete();
break;
}
}
$basketPropertyCollection->save();
5. ORM-классы
Обращаться напрямую к таблице корзины, без использования объектов можно с использованием ORM-класса Bitrix\Sale\Internals\BasketTable
Прямой доступ к данным корзины без использования объектной модели осуществляется через ORM-классы.
Этот подход обеспечивает максимальную производительность при массовых операциях, но требует ручного управления данными.
Перебрать товары в корзине текущего пользователя
$basketRes = Sale\Internals\BasketTable::getList(array(
'filter' => array(
'FUSER_ID' => Sale\Fuser::getId(),
'ORDER_ID' => null,
'LID' => SITE_ID,
'CAN_BUY' => 'Y',
)
));
while ($item = $basketRes->fetch()) {
var_dump($item);
}
Получить количество и сумму товаров в корзине текущего юзера
$result = Sale\Internals\BasketTable::getList(array(
'filter' => array(
'FUSER_ID' => Sale\Fuser::getId(),
'ORDER_ID' => null,
'LID' => SITE_ID,
'CAN_BUY' => 'Y',
),
'select' => array('BASKET_COUNT', 'BASKET_SUM'),
'runtime' => array(
new \Bitrix\Main\Entity\ExpressionField('BASKET_COUNT', 'COUNT(*)'),
new \Bitrix\Main\Entity\ExpressionField('BASKET_SUM', 'SUM(PRICE*QUANTITY)'),
)
))->fetch();
Получить свойства товаров в корзине
Используем класс Bitrix\Sale\Internals\BasketPropertyTable
$basketPropRes = Sale\Internals\BasketPropertyTable::getList(array(
'filter' => array(
"BASKET_ID" => $basketItemId,
),
));
while ($property = $basketPropRes->fetch()) {
var_dump($property);
}
6. Скидки
Считать скидки нужно ПОСЛЕ привязки корзины к заказу
Если скидка произвольная (не правило корзины и не скидка на товар)
задаете 3 поля: CUSTOM_PRICE = 'Y' PRICE = цена со скидкой DISCOUNT_PRICE - величина скидки
Важное условие - сумма PRICE и DISCOUNT_PRICE должна давать значение из поля BASE_PRICE
В публичной части
Этот код даст цены без учета скидок торгового каталога (если они еще используются у вас на проекте).
$basket = \Bitrix\Sale\Basket::loadItemsForFUser(\Bitrix\Sale\Fuser::getId(), SITE_ID);
$discounts = \Bitrix\Sale\Discount::buildFromBasket($basket, new \Bitrix\Sale\Discount\Context\Fuser($basket->getFUserId(true)));
$discounts->calculate();
$result = $discounts->getApplyResult(true);
$prices = $result['PRICES']['BASKET'];
Если используем скидки торгового каталога (скидки на товар)
В этом случае необходимо еще выполнять refresh корзины после ее загрузки из базы:
$basket = \Bitrix\Sale\Basket::loadItemsForFUser(\Bitrix\Sale\Fuser::getId(), SITE_ID);
$basket->refreshData(array('PRICE', 'COUPONS'));
$discounts = \Bitrix\Sale\Discount::buildFromBasket($basket, new \Bitrix\Sale\Discount\Context\Fuser($basket->getFUserId(true)));
$discounts->calculate();
$result = $discounts->getApplyResult(true);
$prices = $result['PRICES']['BASKET'];
Чтобы применить скидку
$order->doFinalAction(true);
Если на странице присутствуют другие компоненты, работающие с корзиной, например малая корзина, то в начало кода следует добавить
\Bitrix\Sale\Compatible\DiscountCompatibility::stopUsageCompatible();
Пример создания заказа с применением скидок
/**
* Создаем виртуальный заказ для расчета стоимости доставок, скидок и купонов.
*/
public static function createVirtualOrder(array $preparedData): object | array
{
if (empty($preparedData)) {
return self::createErrorResponse('Передана пустая data');
}
try {
\Bitrix\Main\Loader::includeModule("sale");
\Bitrix\Main\Loader::includeModule("catalog");
//dd($preparedData);
global $USER;
$propertyValues = $preparedData['properties'];
$siteId = Context::getCurrent()->getSite();
$currencyCode = CurrencyManager::getBaseCurrency();
// СОЗДАЕМ НОВЫЙ ЗАКАЗ.
//$userId = $USER->isAuthorized() ? $USER->GetID() : \CSaleUser::GetAnonymousUserID();
//$userId = $USER->isAuthorized() ? $USER->GetID() : \Bitrix\Sale\Fuser::getId();
$userId = \Bitrix\Sale\Fuser::getId();
$basketUser = \Bitrix\Sale\Basket::loadItemsForFUser($userId, $siteId);
$basket = $basketUser->copy();
$order = Order::create($siteId, $userId);
$order->setPersonTypeId($preparedData['person_type_id']);
$order->setField('CURRENCY', $currencyCode);
$order->setBasket($basket);
$shipmentCollection = $order->getShipmentCollection();
$shipment = $shipmentCollection->createItem();
$service = Delivery\Services\Manager::getById($preparedData['delivery_id']);
$shipment->setFields([
'DELIVERY_ID' => $service['ID'],
'DELIVERY_NAME' => $service['NAME'],
]);
$shipmentItemCollection = $shipment->getShipmentItemCollection();
$shipment->setField('CURRENCY', $currencyCode);
foreach ($order->getBasket()->getOrderableItems() as $basketItem) {
$shipmentItem = $shipmentItemCollection->createItem($basketItem);
$shipmentItem->setQuantity($basketItem->getQuantity());
}
if ($preparedData['delivery_cost'] > 0) {
$shipment->setField('BASE_PRICE_DELIVERY', $preparedData['delivery_cost']);
$shipment->setField('PRICE_DELIVERY', $preparedData['delivery_cost']);
$shipment->setField('CUSTOM_PRICE_DELIVERY', 'Y');
}
$paymentCollection = $order->getPaymentCollection();
$payment = $paymentCollection->createItem();
$paySystemService = PaySystem\Manager::getObjectById($preparedData['pay_system_id']);
$payment->setFields([
'PAY_SYSTEM_ID' => $paySystemService->getField("PAY_SYSTEM_ID"),
'PAY_SYSTEM_NAME' => $paySystemService->getField("NAME"),
]);
$discounts = $order->getDiscount();
$discounts->setApplyResult(true);
$discounts->calculate();
$order->refreshData();
$order->doFinalAction(true);
foreach ($order->getPropertyCollection() as $prop) {
/**
* @var PropertyValue $prop
*/
if ($prop->isUtil()) {
continue;
}
$value = $propertyValues[$prop->getField('CODE')] ?? null;
if (empty($value)) {
$value = $prop->getProperty()['DEFAULT_VALUE'];
}
if (!empty($value)) {
$prop->setValue($value);
}
}
return $order;
} catch (\Exception $e) {
return self::createErrorResponse($e->getMessage());
}
}
Критические замечания
-
Не путать контексты:
-
Пользовательская корзина =
FUSER_ID + ORDER_ID=NULL -
Корзина заказа =
ORDER_ID != NULL
-
-
Безопасность: При работе с корзиной заказа всегда проверяйте права:
if (!$order->getUserId() == $currentUserId) { throw new Exception("Доступ запрещен"); } -
Производительность: Для заказов используйте оптимизированные запросы:
$basket = Sale\Basket::loadItemsForOrder($orderId); -
Всегда проверяйте модуль:
if (!CModule::IncludeModule("sale")) die(); -
Обработка ошибок в D7:
try { $basket->save(); } catch (\Exception $e) { $error = $e->getMessage(); } -
Перенос корзины при авторизации:
// После входа пользователя $newFuserId = Fuser::getIdByUserId($USER->GetID()); $basket->setFUserId($newFuserId); $basket->save();
Скрипты, сниппеты
Получить заказ, позицию в корзине и добавить свойство
use Bitrix\Sale;
Bitrix\Main\Loader::includeModule("sale");
function UpdateOrderDealerToClient($orderId,$email,$basketItemId=""){
$result = "";
if (!empty($orderId) && !empty($email)){
$rsUser = CUser::GetByLogin($email);
if ($arUser = $rsUser->Fetch()) {
$order = Sale\Order::load($orderId);
if (!empty($order)){
$basket = $order->getBasket();
if (!empty($basketItemId)) {
$item = $basket->getItemById($basketItemId);
$basketPropertyCollection = $item->getPropertyCollection();
$arProps = []; //сюда соберем ВСЕ имеющиеся свойства
foreach ($basketPropertyCollection as $basketProperty) {
$arProps[] = [
'NAME' => $basketProperty->getField('NAME'),
'CODE' => $basketProperty->getField('CODE'),
'VALUE' => $basketProperty->getField('VALUE'),
'SORT' => $basketProperty->getField('SORT'),
];
}
//добавим новое
$arProps [] = array(
'NAME' => 'Customer: ' . $email,
'CODE' => 'client',
'VALUE' => $arUser["ID"],
'SORT' => 100,
);
//сохраним
$basketPropertyCollection->setProperty($arProps);
$basketPropertyCollection->save();
}
else{
$items = $basket->getBasketItems();
foreach ($items as $item){
$basketPropertyCollection = $item->getPropertyCollection();
$arProps = []; //сюда соберем ВСЕ имеющиеся свойства
foreach ($basketPropertyCollection as $basketProperty) {
$arProps[] = [
'NAME' => $basketProperty->getField('NAME'),
'CODE' => $basketProperty->getField('CODE'),
'VALUE' => $basketProperty->getField('VALUE'),
'SORT' => $basketProperty->getField('SORT'),
];
}
//добавим новое
$arProps [] = array(
'NAME' => 'Customer: ' . $email,
'CODE' => 'client',
'VALUE' => $arUser["ID"],
'SORT' => 100,
);
//сохраним
$basketPropertyCollection->setProperty($arProps);
$basketPropertyCollection->save();
}
}
$order->save();
$result = "ok";
} else {
$result = "order not found";
}
} else {
$result = "user not found";
}
} else {
$result = "empty orderId or email";
}
return $result;
}