Заказ в 1С-Битрикс — это не одна строка в базе, а дерево связанных объектов: сам заказ, его корзина, оплаты, отгрузки и свойства. Разобравшись в этой структуре, вы перестанете путаться в API и научитесь читать заказ так, как задумано в ядре модуля sale.
Общая модель: из чего состоит заказ
Начиная с модуля sale версии 15 и ядра D7, заказ описывается объектной моделью в пространстве имён Bitrix\Sale. Корневой объект — Order (Bitrix\Sale\Order). Он агрегирует внутри себя несколько коллекций-потомков, каждая из которых отвечает за свою часть логики.
- Basket — состав заказа, список товарных позиций;
- PaymentCollection — оплаты (одна или несколько);
- ShipmentCollection — отгрузки (доставки);
- PropertyValueCollection — значения свойств заказа (ФИО, телефон, адрес);
- Tax и Discount — налоги и скидки.
Ключевая идея: заказ — это композит. Вы не работаете с плоской записью, а получаете граф объектов, где Order управляет своими коллекциями, следит за консистентностью и пересчётом сумм. Изменение одной позиции корзины автоматически инициирует пересчёт итога заказа.
CSaleOrder, CSaleBasket) всё ещё работает как обёртка над D7, но для новой логики используйте объектную модель — она гарантирует корректный пересчёт и вызов обработчиков.Order — корень заказа
Order хранит шапку заказа: покупателя (USER_ID), валюту (CURRENCY), лицо (PERSON_TYPE_ID — физлицо или юрлицо), итоговую сумму (PRICE), флаги оплаты и отгрузки, статус (STATUS_ID). Физически шапка лежит в таблице b_sale_order.
Загрузить существующий заказ по идентификатору:
use Bitrix\Sale\Order;
$order = Order::load($orderId);
$sum = $order->getPrice();
$paid = $order->isPaid(); // оплачен ли
$ship = $order->isShipped(); // отгружен лиСоздание нового заказа выглядит иначе — через Order::create($siteId, $userId). Обратите внимание: у Order есть флаги PAYED и DEDUCTED — это агрегированные признаки. Они выставляются в Y автоматически, когда все вложенные оплаты оплачены, а все отгрузки — отгружены. Вручную их менять не нужно.
Все изменения фиксируются в БД только после явного вызова $order->save(). До этого вы работаете с объектом в памяти, и ядро накапливает изменения, чтобы записать их одной транзакцией.
Basket — состав заказа
Состав заказа — это коллекция Basket (Bitrix\Sale\Basket), состоящая из позиций BasketItem. Данные лежат в таблице b_sale_basket. Каждая позиция знает свой товар (PRODUCT_ID), количество (QUANTITY), цену (PRICE), базовую цену и применённые скидки.
Получить корзину из уже загруженного заказа:
$basket = $order->getBasket();
foreach ($basket as $item) {
echo $item->getField('NAME');
echo $item->getQuantity();
echo $item->getFinalPrice(); // цена * кол-во с учётом скидок
}Важная деталь: у позиции есть свойства товара в заказе — BasketPropertyItem, которые хранятся отдельно в b_sale_basket_props. Это «слепок» характеристик на момент заказа (цвет, размер, артикул торгового предложения), чтобы позднее изменение каталога не искажало историю. Читаются через $item->getPropertyCollection().
b_sale_basket_props) — это про товар. Свойства заказа (b_sale_order_props_value) — про покупателя и доставку. Это разные сущности с разным API.Payment и Shipment — деньги и логистика
В старой модели у заказа была одна оплата и одна доставка «внутри» шапки. В D7 их вынесли в отдельные коллекции, что позволило делать частичные оплаты и дробную отгрузку.
PaymentCollection содержит объекты Payment (таблица b_sale_order_payment). Каждая оплата привязана к платёжной системе (PAY_SYSTEM_ID), знает сумму и флаг PAID. Заказ можно оплатить двумя способами — например, часть бонусами, часть картой, — и это будут две записи Payment.
$paymentCollection = $order->getPaymentCollection();
foreach ($paymentCollection as $payment) {
echo $payment->getPaymentSystemId();
echo $payment->getSum();
echo $payment->isPaid() ? 'оплачено' : 'ждём';
}ShipmentCollection содержит Shipment (таблица b_sale_order_shipment) — отгрузки, привязанные к службе доставки (DELIVERY_ID). У отгрузки есть флаг DEDUCTED (списано со склада) и связь с товарами через ShipmentItem: именно здесь фиксируется, какие позиции корзины и в каком количестве уходят в конкретную отгрузку. Одна из отгрузок помечена как «системная» — она хранит ещё не распределённые по доставкам товары.
Связи и где что хранится
Все дочерние сущности связаны с заказом через ORDER_ID. Ниже — карта основных таблиц и объектов, полезная при отладке и написании SQL-запросов напрямую.
| Сущность | Класс D7 | Таблица БД |
|---|---|---|
| Заказ (шапка) | Bitrix\Sale\Order | b_sale_order |
| Позиция корзины | Bitrix\Sale\BasketItem | b_sale_basket |
| Свойство позиции | BasketPropertyItem | b_sale_basket_props |
| Оплата | Bitrix\Sale\Payment | b_sale_order_payment |
| Отгрузка | Bitrix\Sale\Shipment | b_sale_order_shipment |
| Товар в отгрузке | Bitrix\Sale\ShipmentItem | b_sale_order_shipment_item |
| Значение свойства заказа | PropertyValue | b_sale_order_props_value |
Логика связей: Order → множество Payment (1:N), Order → множество Shipment (1:N), Shipment → множество ShipmentItem, которые ссылаются на BasketItem. То есть один товар из корзины может быть распределён по нескольким отгрузкам частями.
PropertyValue — свойства заказа
PropertyValue хранит анкетные данные: ФИО, телефон, e-mail, адрес доставки, комментарий. Каждое значение привязано к описанию свойства (b_sale_order_props), которое настраивается в админке: Магазин → Настройки → Свойства заказа. Значения конкретного заказа лежат в b_sale_order_props_value.
Ключевые свойства помечены системными кодами (IS_EMAIL, IS_PHONE, IS_ADDRESS, IS_PROFILE_NAME) — по ним ядро находит e-mail для уведомлений и телефон для СМС. Читать удобнее через коллекцию:
$propertyCollection = $order->getPropertyCollection();
$phone = $propertyCollection->getPhone();
$email = $propertyCollection->getUserEmail();
foreach ($propertyCollection as $prop) {
echo $prop->getField('NAME') . ': ' . $prop->getValue();
}Свойства привязаны к типу плательщика (PERSON_TYPE_ID): у физлица набор полей один, у юрлица — другой (ИНН, КПП, юрадрес). Поэтому один и тот же заказ показывает разные анкеты в зависимости от того, кто оформлял.
Как читать заказ через API правильно
Есть два пути чтения. Быстрый — через ORM-таблицы (OrderTable, BasketTable, PaymentTable), когда нужен только список полей без бизнес-логики. Полноценный — через объект Order::load(), когда важны пересчёт, скидки и корректная работа обработчиков.
- Нужен отчёт или выборка списка — берите
Bitrix\Sale\Internals\OrderTable::getList(), это дешевле по ресурсам; - Нужно изменить заказ, добавить товар, провести оплату — только через объектную модель и
save(); - Не читайте поля
PRICE,PAYEDнапрямую из БД для принятия решений — используйте методы объекта, они учитывают незасохранённые изменения.
Типичная ошибка — обновить строку в b_sale_order напрямую SQL-запросом. Так вы обойдёте события OnSaleOrderSaved, не пересчитаете скидки и рассинхронизируете флаги оплаты. Ядро sale построено на событиях, и прямая правка БД ломает эту механику.
$order->toArray() — метод разворачивает весь граф в массив со всеми коллекциями.Итог
Заказ в 1С-Битрикс — это иерархия объектов: Order как корень, а внутри — Basket (состав), PaymentCollection (оплаты), ShipmentCollection (отгрузки) и PropertyValueCollection (свойства). Все они связаны через ORDER_ID, лежат в отдельных таблицах b_sale_* и читаются через объектную модель D7. Понимание этой структуры — база для любой доработки: от вывода свойств в списке до интеграции с 1С и платёжными системами.
Мы в B2Bsite проектируем и дорабатываем магазины на 1С-Битрикс с учётом этой архитектуры: настраиваем дробные отгрузки и частичные оплаты, выстраиваем корректный обмен заказами с 1С и пишем интеграции, которые не ломают пересчёт и события ядра. Если структура заказа под ваши процессы требует нестандартной логики — поможем спроектировать её надёжно.
Частые вопросы
Чем Order в D7 отличается от старого CSaleOrder?
Order — это объектная модель с коллекциями оплат, отгрузок и свойств, где ядро само следит за пересчётом и событиями. CSaleOrder — процедурная обёртка над той же логикой, оставленная для совместимости.
Может ли у одного заказа быть несколько оплат?
Да. PaymentCollection допускает несколько объектов Payment — например, часть суммы картой, часть бонусами. Заказ считается оплаченным, когда оплачены все Payment.
Где хранятся свойства товара в заказе, а где свойства покупателя?
Свойства позиции корзины (цвет, размер) лежат в b_sale_basket_props. Свойства заказа (ФИО, адрес, телефон) — в b_sale_order_props_value. Это разные сущности с разным API.
Как получить телефон и e-mail из заказа?
Через коллекцию свойств: $order->getPropertyCollection()->getPhone() и ->getUserEmail(). Ядро находит их по системным признакам IS_PHONE и IS_EMAIL.
Можно ли отгружать заказ частями?
Да. ShipmentCollection поддерживает несколько отгрузок, а ShipmentItem распределяет позиции корзины по ним. Один товар может уходить в разных отгрузках частями.
Почему нельзя менять статус заказа прямым SQL-запросом?
Прямая правка БД обходит события ядра sale, не пересчитывает скидки и рассинхронизирует флаги оплаты и отгрузки. Меняйте заказ только через объект и метод save().
Какие таблицы БД отвечают за заказ?
Шапка — b_sale_order, корзина — b_sale_basket, оплаты — b_sale_order_payment, отгрузки — b_sale_order_shipment, свойства — b_sale_order_props_value. Все связаны через ORDER_ID.
Как быстро выбрать список заказов без загрузки объектов?
Используйте ORM-таблицу Bitrix\Sale\Internals\OrderTable::getList(). Это дешевле, чем Order::load(), но не даёт бизнес-логики пересчёта — подходит для отчётов и выборок.
Поможем с настройкой и поддержкой 1С-Битрикс: Управление сайтом
Поможем с настройкой, доработкой и поддержкой 1С-Битрикс: Управление сайтом.