Заказ из кода не сохраняется - разбор причин
Скрипт создаёт заказ, отрабатывает до конца, а заказа в списке нет. Разбираем причины по убыванию частоты, начиная с результата сохранения.
С чего начать
Читаем результат сохранения, а не факт выполнения:
$result = $order->save();if (!$result->isSuccess()) { foreach ($result->getErrorMessages() as $message) { echo $message, "\n"; // здесь лежит настоящая причина отказа }}// сохранение возвращает объект результата и исключений не бросает// $result->getId() отдаёт номер записи только при успешном сохраненииСохранение заказа не бросает исключение при отказе, а возвращает объект с ошибками. Код без этой проверки выглядит успешным при любом исходе, и разбор всегда начинают именно отсюда.
Проверяем обязательный набор перед сохранением:
printf("тип плательщика=%s позиций=%d сайт=%s\n", $order->getPersonTypeId(), $order->getBasket() ? $order->getBasket()->count() : 0, $order->getSiteId());// заказ без типа плательщика и без позиций корзины не сохранится// сайт заказа определяет, в каком списке он потом появитсяТип плательщика, сайт и непустая корзина - минимум для сохранения. Заказ, собранный из кода без одного из этих трёх, отклоняется на самом сохранении, а причина остаётся в объекте результата.
Смотрим, не отменил ли заказ чужой обработчик:
$handlers = \Bitrix\Main\EventManager::getInstance() ->findEventHandlers('sale', 'OnSaleOrderBeforeSaved');printf("обработчиков события: %d\n", count($handlers));// свой или сторонний обработчик умеет отменить сохранение заказа целикомСобытие перед сохранением умеет отменять операцию. Решение из маркетплейса и свой код проверки заказа отменяют сохранение одинаково тихо, а сообщение попадает в тот же объект результата.
Собираем минимальный заказ целиком:
$order = \Bitrix\Sale\Order::create(SITE_ID, $userId);$order->setPersonTypeId($personTypeId); // без него заказ не сохранится$order->setBasket($basket); // корзина с позициями$shipment = $order->getShipmentCollection()->createItem();$shipment->setFields(['DELIVERY_ID' => $deliveryId]);$payment = $order->getPaymentCollection()->createItem();$payment->setFields(['PAY_SYSTEM_ID' => $paySystemId, 'SUM' => $order->getPrice()]);Заказ собирают целиком и сохраняют один раз. Сохранение записывает заказ, корзину, отгрузки и платежи каскадом, поэтому промежуточные сохранения только добавляют события и лишние запросы.
Причины
-
Результат сохранения не проверяется примерно 30% случаев
ПризнакСкрипт отрабатывает без ошибок, заказа в списке нет, в журнале пусто.
ПроверкаПечатаем сообщения об ошибках из объекта результата сразу после сохранения.
Что делатьПроверяем результат всегда: сохранение сообщает об отказе объектом, а не исключением.
-
Не задан тип плательщика примерно 25% случаев
ПризнакВ ошибках упоминается тип плательщика или свойства заказа, которых нет.
ПроверкаСмотрим, задан ли у заказа тип плательщика и существует ли он в настройках магазина.
Что делатьЗадаём тип плательщика до сохранения: от него зависят свойства заказа и доступные службы.
-
Корзина заказа пуста или не привязана примерно 20% случаев
ПризнакЗаказ создан объектом, но позиций в нём ноль, сумма нулевая.
ПроверкаСчитаем позиции корзины перед сохранением и проверяем, привязана ли корзина к заказу.
Что делатьНаполняем корзину и привязываем её к заказу: заказ без позиций магазину не нужен.
-
Не заданы отгрузка и оплата примерно 15% случаев
ПризнакЗаказ сохраняется, но выглядит незавершённым: нет доставки, нет платежа, суммы не сходятся.
ПроверкаСмотрим коллекции отгрузок и платежей заказа и наличие несистемных записей в них.
Что делатьДобавляем отгрузку со службой доставки и платёж с платёжной системой до сохранения.
-
Сохранение отменил обработчик события примерно 10% случаев
ПризнакИз административной части заказ создаётся, из своего кода - нет.
ПроверкаСмотрим список обработчиков события перед сохранением заказа и временно отключаем свои.
Что делатьПравим условие проверки в обработчике: оно не должно срабатывать на корректном заказе.
Частые вопросы
Почему сохранение не бросает исключение?
Ядро продаж возвращает объект результата: так ошибку можно показать покупателю, а не уронить страницу. Проверка результата в этом подходе обязательна.
Нужно ли сохранять заказ несколько раз?
Нет, одно сохранение записывает заказ, корзину, отгрузки и платежи каскадом. Несколько сохранений подряд только добавляют лишние события и запросы.
Почему в коллекции отгрузок есть лишняя запись?
Это системная отгрузка: она хранит нераспределённые товары и существует всегда. При переборе её пропускают, иначе действия применяются не к той записи.
Как менять уже сохранённый заказ?
Специализированными методами: смена статуса, разрешение доставки, запись свойств. Общая запись полей мимо них не поднимает нужные события и ломает связанные данные.
Заказ создан, но покупатель его не видит.
Заказ привязан к другому пользователю или к другому сайту. Оба значения задаются при создании и потом молча определяют видимость заказа в личном кабинете.
Смежное
-
Заказы - оглавление подтемы
-
Заказ из кода: чтение, поиск по номеру, изменение состава - как создавать заказ правильно
-
Оплаты и отгрузки заказа: объекты, признаки, частичные операции - устройство отгрузок и платежей
-
Свойства заказа: чтение, запись и фильтрация по значению - свойства и тип плательщика
-
События оформления заказа: свои проверки, запрет и допданные - кто отменяет сохранение
-
Не работает оформление заказа: корзина есть, а на оформлении пусто - тот же сбой в публичной части
-
Интернет-магазин на 1С-Битрикс - устройство магазина целиком
-
Заказ изнутри: коллекции, пересчёт, сохранение, события - что возвращает сохранение и почему