Объект Sale\Order в Битрикс D7: поля, оплата, свойства и пример создания
Связь заказа с ядром D7
Начиная с редакции интернет-магазина на современном стеке, сущность заказа живёт в объектном слое модуля sale как часть D7. Поведение близко к тому же, что было в поздней 15.5, но рабочую точку входа лучше держать в пространстве имён Bitrix\Sale и подключать модуль перед любыми манипуляциями.
Важное правило: экземпляр Order не обязан соответствовать уже записанной строке БД до первого успешного save(), и любые правки свойств без сохранения в базу не попадут. Коллекции у заказа типичные: корзина, отгрузки, платежи и набор свойств оформления.
Загрузка и основные поля
Существующую сущность получают по первичному ключу или человекочитаемому номеру счёта. Для общего списка имён столбцов есть getAvailableFields(); универсальный доступ — через getField()/setField() с последующим сохранением.
use Bitrix\Sale;
$storedOrderPk = 4821;
$issuedOrderEntity = Sale\Order::load($storedOrderPk);
$publicOrderReference = 'K-9184';
$issuedOrderEntity = Sale\Order::loadByAccountNumber($publicOrderReference);
$issuedOrderEntity->getId();
$issuedOrderEntity->getSiteId();
$issuedOrderEntity->getDateInsert();
$issuedOrderEntity->getPersonTypeId();
$issuedOrderEntity->getUserId();
$issuedOrderEntity->getPrice();
$issuedOrderEntity->getDiscountPrice();
$issuedOrderEntity->getDeliveryPrice();
$issuedOrderEntity->getSumPaid();
$issuedOrderEntity->getCurrency();
$issuedOrderEntity->isPaid();
$issuedOrderEntity->isAllowDelivery();
$issuedOrderEntity->isShipped();
$issuedOrderEntity->isCanceled();
$grossKg = $issuedOrderEntity->getField('ORDER_WEIGHT');
$issuedOrderEntity->setField('USER_DESCRIPTION', 'Позвонить перед выдачей');
$issuedOrderEntity->save();Идентификаторы выбранных служб можно быстро собрать вызовами getPaymentSystemId() и getDeliverySystemId(). Результаты действия правил ценообразования смотрят через объект скидок заказа: $issuedOrderEntity->getDiscount()->getApplyResult() вернёт структуру, где отдельно лежат скидочные строки и купоны; у каждой записи стоит проверять признак применения, потому что менеджер мог отключить часть акций из административной панели.
Корзина и свойства
Коллекция строк привязки к заказу — это объект Sale\Basket: её подставляют методом setBasket() или читают через getBasket(). Детальнее про строки позиций и ORM имеет смысл смотреть в заметках про работу именно с Basket; здесь достаточно помнить, что без согласованной корзины пересчёты сумм будут неполными.
Свойства оформления собраны в PropertyValueCollection. Массовый снимок: getArray() с вложенными properties и groups, группы отдельно — getGroups(), фильтр по группе — getGroupProperties($groupInnerId). Для стандартных ролей (телефон, ФИО, местоположение и т.д.) есть shortcuts вроде getUserEmail(), getPayerName(), getDeliveryLocation(), getPhone(), getAddress(). Конкретное значение по ID свойства за заказ читают getItemByOrderPropertyId(); у найденного Sale\PropertyValue доступны getValue(), человекочитаемый вывод — getViewHtml(), метаданные поля — getProperty(), признак обязательности — isRequired().
Изменить текст свойства можно setValue(); надёжный путь — сохранить сам заказ: $issuedOrderEntity->save(). Узкая запись только элемента коллекции тоже возможна, но пересборка сумм может не случиться автоматически.
Оплаты и инициирование платёжной формы
Через getPaymentCollection() приходят агрегаты по всем платежам: isPaid(), hasPaidPayment(), getPaidSum(), признак оплаты с внутреннего счёта и перебор отдельных Sale\Payment. Для каждого платежа читают сумму, статусы оплаты/возврата, связанную платежную службу (getPaySystem(), имя и ID). Ручное переключение статусов — setPaid('Y'|'N'), setReturn('Y') с обязательным финальным $issuedOrderEntity->save(). Чтобы показать готовый сценарий оплаты (кнопка, виджет, redirect), получают сервис платёжной системы и дергают initiatePay(), передавая текущий HTTP-контекст.
$paymentAggregate = $issuedOrderEntity->getPaymentCollection();
if ($paymentAggregate->hasPaidPayment()) {
// есть хотя бы одна успешная оплата
}
foreach ($paymentAggregate as $moneyChunk) {
$moneyChunk->getSum();
$moneyChunk->isPaid();
$moneyChunk->isReturn();
$moneyChunk->getPaymentSystemId();
$moneyChunk->getPaymentSystemName();
$moneyChunk->isInner();
}
$primaryMoneyChunk = $paymentAggregate->current();
$primaryMoneyChunk->setPaid('N'); // отметить как не оплачен
$issuedOrderEntity->save();
$payService = Sale\PaySystem\Manager::getObjectById($primaryMoneyChunk->getPaymentSystemId());
$runtimeContext = \Bitrix\Main\Application::getInstance()->getContext();
$payService->initiatePay($primaryMoneyChunk, $runtimeContext->getRequest());Минимальный сценарий создания заказа
Ниже упрощённый скрипт покупательского сценария: одна SKU из каталога, фиктивная «без доставки», одна платёжная система с известным ID, заполнение телефона и ФИО из запроса. В бою понадобятся проверки остатков, валидация формы и выбор живых профилей плательщика; ориентир по полям можно подсмотреть в стандартном sale.order.ajax.
use Bitrix\Main\Context;
use Bitrix\Currency\CurrencyManager;
use Bitrix\Sale\Order;
use Bitrix\Sale\Basket;
use Bitrix\Sale\Delivery;
use Bitrix\Sale\PaySystem;
global $USER;
\Bitrix\Main\Loader::includeModule('sale');
\Bitrix\Main\Loader::includeModule('catalog');
$incoming = Context::getCurrent()->getRequest();
$catalogElementId = (int) $incoming['PRODUCT_ID'];
$buyerPhone = trim((string) $incoming['PHONE']);
$buyerFullName = trim((string) $incoming['NAME']);
$checkoutNote = trim((string) $incoming['COMMENT']);
$portalSiteCode = Context::getCurrent()->getSite();
$moneyIso = CurrencyManager::getBaseCurrency();
$anonProfileUserId = 539; // служебный гость; замените на свою модель авторизации
$buyerPortalId = $USER->IsAuthorized() ? (int) $USER->GetID() : $anonProfileUserId;
$newCommerceOrder = Order::create($portalSiteCode, $buyerPortalId);
$newCommerceOrder->setPersonTypeId(1);
$newCommerceOrder->setField('CURRENCY', $moneyIso);
if ($checkoutNote !== '') {
$newCommerceOrder->setField('USER_DESCRIPTION', $checkoutNote);
}
$draftBasket = Basket::create($portalSiteCode);
$basketSkuLine = $draftBasket->createItem('catalog', $catalogElementId);
$basketSkuLine->setFields([
'QUANTITY' => 1,
'CURRENCY' => $moneyIso,
'LID' => $portalSiteCode,
'PRODUCT_PROVIDER_CLASS' => 'CCatalogProductProvider',
]);
$newCommerceOrder->setBasket($draftBasket);
$shipmentBag = $newCommerceOrder->getShipmentCollection();
$movement = $shipmentBag->createItem();
$silentCourier = Delivery\Services\Manager::getById(
Delivery\Services\EmptyDeliveryService::getEmptyDeliveryServiceId()
);
$movement->setFields([
'DELIVERY_ID' => $silentCourier['ID'],
'DELIVERY_NAME' => $silentCourier['NAME'],
]);
$movementLines = $movement->getShipmentItemCollection();
$movementRow = $movementLines->createItem($basketSkuLine);
$movementRow->setQuantity($basketSkuLine->getQuantity());
$payments = $newCommerceOrder->getPaymentCollection();
$plannedPayment = $payments->createItem();
$psEntity = PaySystem\Manager::getObjectById(1);
$plannedPayment->setFields([
'PAY_SYSTEM_ID' => $psEntity->getField('PAY_SYSTEM_ID'),
'PAY_SYSTEM_NAME' => $psEntity->getField('NAME'),
]);
$propsBag = $newCommerceOrder->getPropertyCollection();
$propsBag->getPhone()->setValue($buyerPhone);
$propsBag->getPayerName()->setValue($buyerFullName);
$newCommerceOrder->doFinalAction(true);
$persistOutcome = $newCommerceOrder->save();
$freshPrimaryKey = $newCommerceOrder->getId();Что держать в голове
- Всегда завершайте бизнес-сценарий вызовом
save()у заказа, если менялись суммы, доставка или оплаты. - Разделяйте чтение агрегатов и ручные правки статусов: последние почти всегда требуют явного сохранения и могут дергать обработчики платёжных систем.
- Для отладки скидок опирайтесь на структуру из
getDiscount()->getApplyResult(), а не на устаревшие глобальные функции модулей.
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии