Перейти к содержимому

Заказ из кода - чтение, поиск по номеру, изменение состава

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

Решение

Загружаем заказ и читаем его состав:

use Bitrix\Main\Loader;
use Bitrix\Sale\Order;
Loader::includeModule('sale');
$order = Order::load($orderId); // null, если заказа с таким номером нет
// загрузка собирает всё дерево заказа: корзину, оплаты, отгрузки, свойства
printf("номер=%s сумма=%.2f оплачен=%s\n",
$order->getField('ACCOUNT_NUMBER'), $order->getPrice(), $order->isPaid() ? 'да' : 'нет');
foreach ($order->getBasket() as $item) {
printf(" %-30s %s x %.2f\n", $item->getField('NAME'),
$item->getQuantity(), $item->getPrice());
}

Заказ загружается целиком, вместе с корзиной, оплатами и отгрузками. Отдельные выборки по таблицам дают те же данные, но мимо бизнес-логики: пересчёт и события при этом не работают.

Ищем заказ по номеру, а не по идентификатору:

use Bitrix\Sale\Internals\OrderTable;
$row = OrderTable::getRow([
'select' => ['ID'],
'filter' => ['=ACCOUNT_NUMBER' => '2024-000145'], // номер, видимый покупателю
]);
$order = $row ? Order::load($row['ID']) : null;

Номер заказа и его идентификатор - разные поля. Покупатель называет номер, код чаще работает с идентификатором, и путаница между ними даёт «заказ не найден» при существующем заказе.

Меняем состав заказа:

$basket = $order->getBasket();
$item = $basket->getItemById($basketItemId);
$item->setField('QUANTITY', 3); // изменение количества позиции
// $item->delete(); // или удаление позиции целиком
$result = $order->save();
if (!$result->isSuccess()) {
print_r($result->getErrorMessages());
}

Сохранение пересчитывает скидки, доставку и итоговую сумму. Прямая правка количества в таблице этого не делает, и заказ остаётся с несогласованными суммами - расхождение всплывает при выгрузке в учётную систему.

Читаем свойства заказа:

foreach ($order->getPropertyCollection() as $prop) {
printf("%-20s %s\n", $prop->getField('CODE'), $prop->getValue());
}
// фильтровать заказы по значению свойства можно через таблицу значений свойств
// свои дописки рядом с сохранением заказа оборачивают транзакцией сами

Свойства заказа лежат отдельной коллекцией, а не полями самого заказа. Поэтому выборка заказов по значению свойства строится соединением с таблицей значений, а не простым условием.

Отчёты и массовое чтение удобнее делать выборкой по таблицам заказов. Она не поднимает бизнес-логику и на десяти тысячах заказов работает в разы быстрее загрузки объектов. Правило простое: читать можно как угодно, а менять - только через объект заказа.

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

Проверять результат сохранения нужно всегда, без исключений. Метод возвращает объект результата вместо исключения, и молчаливый провал по правам или по проверке данных выглядит снаружи как «код отработал, а ничего не изменилось».

Типичные проблемы

Заказ по номеру не находится.

Поиск идёт по идентификатору вместо номера. Это разные поля, и совпадают они только на первых заказах.

Суммы заказа разошлись после правки.

Количество менялось прямой записью в таблицу. Пересчёт скидок и доставки выполняется только при сохранении объекта заказа.

Сохранение молча ничего не меняет.

Не проверяется результат: ошибка прав или проверки данных возвращается объектом, а не исключением.

Выборка по свойству заказа не работает.

Свойства лежат в отдельной таблице значений. Условие по ним строится соединением, а не полем заказа.

Обработчик события зациклился.

Он меняет и сохраняет заказ внутри обработки сохранения. Такие правки делают до сохранения, а не после.

Частые вопросы

Можно ли создать заказ полностью из кода?

Да, объект заказа собирают из корзины, свойств и отгрузок и сохраняют. Так делают импорт заказов и оформление в один клик.

Как получить стоимость доставки заказа?

Из коллекции отгрузок: там лежит и служба, и цена. В полях самого заказа хранится итог, а не его составляющие.

Почему номера заказов идут не подряд?

Номер присваивается по своему правилу и может включать префиксы и год. Разрывы возникают из-за незавершённых оформлений.

Что быстрее для отчётов - объект или таблицы?

Для чтения больших выборок - таблицы: они не поднимают бизнес-логику. Для любых изменений - только объект заказа.

Смежное

Первоисточник