Yandex Metrika
sanches.free 1 просмотр

Объект 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 дней гарантии