Интернет-магазин на 1С-Битрикс - каталог, корзина, заказы
Магазин на 1С-Битрикс собирается из трёх модулей: контент товара живёт в инфоблоках, торговые данные - в каталоге, а корзина и заказы - в продажах. Разберём эту связку, программное создание заказа и типичные ошибки, из-за которых товар «есть, но не покупается».
Как это работает
Три модуля и разделение ответственности. Модуль iblock хранит карточку
товара как элемент инфоблока. Модуль catalog отвечает за торговые данные: цену,
остаток, НДС, доступность. Модуль sale - за корзину, заказы, оплаты, доставки и
скидки уровня корзины.
Товар - это элемент плюс запись каталога. Пока не создана товарная запись с тем же идентификатором, элемент остаётся просто карточкой контента: его не увидят ни корзина, ни заказы, ни скидки. Это причина номер один ситуации «создал товар кодом, а он не работает».
Торговые предложения. Товар с вариантами - это родительская карточка в одном инфоблоке и набор предложений в другом. Связь задаёт специальное свойство предложения, которое создаётся автоматически при включении опции торговых предложений. Отбор варианта покупателем работает только по немножественным свойствам типов «список», «справочник» и «привязка к элементам».
Типы товара определяют поведение. Простой товар, комплект, родитель с предложениями, само предложение, услуга. Родительские карточки в корзину не добавляются - туда идут простые товары, комплекты, предложения и услуги.
Торговые данные - только в объектах каталога. Цена, остаток, единица измерения, НДС и доступность хранятся в таблицах модуля каталога. Если «рабочую» цену положить в свойство инфоблока, её не увидят ни корзина, ни заказ, ни скидки, ни склад.
Цены и типы цен. Тип цены - это ценовая колонка для группы покупателей. Базовый тип всегда один и неудаляем, дополнительные создаются рядом. Права на тип цены задаются по группам пользователей отдельно от доступности товара: товар может быть в наличии, но без права на тип цены покупатель его не увидит и не купит. Итоговую цену со скидками, купонами и диапазонами считает специальный метод - вручную её не собирают.
Скидки двух видов. Скидки каталога уменьшают цену товара до попадания в корзину. Правила корзины действуют на состав и сумму заказа. Логика описывается деревьями условий и действий, а не плоскими полями.
Складской учёт. Остатки бывают общие и по складам. При включённом складском учёте их меняют только складскими документами - документ хранит причину движения. Прямая запись остатка допустима только при начальной загрузке. Свободный остаток считают в коде как разницу между складским количеством и резервом.
Примеры
1. Создание товара: два обязательных шага
use Bitrix\Main\Loader;use Bitrix\Catalog\Model\Product;use Bitrix\Catalog\Model\Price;
Loader::requireModule('iblock');Loader::requireModule('catalog');
// шаг 1: элемент инфоблока$el = new CIBlockElement();$elementId = $el->Add([ 'IBLOCK_ID' => $iblockId, 'NAME' => 'Кресло Осло', 'ACTIVE' => 'Y',]);
// шаг 2: товарная запись - без неё это не товар$result = Product::add([ 'ID' => $elementId, 'TYPE' => Product::TYPE_PRODUCT, 'QUANTITY' => 10, 'AVAILABLE' => 'Y',]);
// ценаPrice::add([ 'PRODUCT_ID' => $elementId, 'CATALOG_GROUP_ID' => 1, // тип цены 'PRICE' => 15900, 'CURRENCY' => 'RUB',]);Перед добавлением цены имеет смысл проверить, нет ли уже записи на пару «товар плюс тип цены» - иначе появится дубль. И помните: у товара с предложениями цена принадлежит предложению, а не родительской карточке.
2. Программное создание заказа
use Bitrix\Sale;
Loader::requireModule('sale');
$basket = Sale\Basket::create(SITE_ID);
foreach ($products as $product) { $item = $basket->createItem('catalog', $product['PRODUCT_ID']); $item->setFields([ 'QUANTITY' => $product['QUANTITY'], 'CURRENCY' => 'RUB', 'LID' => SITE_ID, 'PRODUCT_PROVIDER_CLASS' => \CCatalogProductProvider::class, ]);}
$order = Sale\Order::create(SITE_ID, $userId);$order->setPersonTypeId(1);$order->setBasket($basket);
// отгрузка$shipmentCollection = $order->getShipmentCollection();$shipment = $shipmentCollection->createItem( Sale\Delivery\Services\Manager::getObjectById($deliveryId));$shipmentItemCollection = $shipment->getShipmentItemCollection();foreach ($basket as $item) { $shipmentItem = $shipmentItemCollection->createItem($item); $shipmentItem->setQuantity($item->getQuantity());}
// оплата$paymentCollection = $order->getPaymentCollection();$payment = $paymentCollection->createItem( Sale\PaySystem\Manager::getObjectById($paySystemId));$payment->setField('SUM', $order->getPrice());
$result = $order->save();
if (!$result->isSuccess()) { foreach ($result->getErrors() as $error) { // обработка ошибок }}Один вызов сохранения каскадно записывает заказ, корзину, отгрузки и платежи и возвращает объект результата. Для нераспределённых товаров система сама создаёт служебную отгрузку.
3. Смена статуса и оплата
Специализированные методы вместо общего обновления полей - принципиальный момент: только они гарантируют вызов всей сопутствующей логики, включая обработчики оплаты, списание остатков и почтовые уведомления.
Статусы бывают двух типов: статус заказа в целом и статус отгрузки. Есть неудаляемые служебные: начальный и финальный для заказа, ожидание обработки и отгрузка для отгрузки. При включённом уведомлении смена статуса порождает почтовое событие.
4. Своя служба доставки
class MyDeliveryHandler extends \Bitrix\Sale\Delivery\Services\Base{ protected function calculateConcrete(\Bitrix\Sale\Shipment $shipment): \Bitrix\Sale\Delivery\CalculationResult { $result = new \Bitrix\Sale\Delivery\CalculationResult(); $result->setDeliveryPrice($this->calculatePrice($shipment));
return $result; }}Служба состоит из профилей с тарифами, имеет ограничения по весу, габаритам, местоположению и типу плательщика. Для расчёта по адресу нужно свойство заказа типа местоположения с соответствующим флагом - по одному на тип плательщика.
Платёжные системы устроены похоже: обработчик наследуется от базового класса, а все ответы платёжных сервисов приходят на единый служебный скрипт.
Справочник API
| API | Назначение | Особенности |
|---|---|---|
Catalog\Model\Product | товарная запись | без неё элемент не является товаром |
Catalog\Model\Price | цены товара | проверяйте существующую запись перед добавлением |
Catalog\ProductTable и другие таблицы | чтение товарных данных | запись - через модели |
CCatalogSKU::getInfoByIBlock() | тип каталога и связь с предложениями | |
CCatalogProduct::GetOptimalPrice() | итоговая цена со скидками | вручную цену не считают |
CCatalogDiscount | скидки каталога | D7-таблица только для чтения |
Sale\Basket::create() | корзина | позиции через createItem |
Sale\Order::create() | заказ | сохранение каскадное |
getShipmentCollection() | отгрузки заказа | системная отгрузка создаётся автоматически |
getPaymentCollection() | платежи заказа | оплату можно разделить на части |
Delivery\Services\Base | своя служба доставки | метод расчёта возвращает результат |
PaySystem\ServiceHandler | обработчик платёжной системы | ответы приходят на служебный скрипт |
CCatalogDocs | складские документы | остатки меняют только ими |
Catalog\Config\State::isUsedInventoryManagement() | включён ли складской учёт |
Частые ошибки
Элемент создан, но товаром не стал. Не вызвана регистрация товарной записи.
Цена не участвует в расчёте. Она добавлена родительской карточке, а не предложению. Для товаров с вариантами цена принадлежит предложению.
Дубли цен. Запись создана без проверки существующей пары «товар плюс тип цены».
Товар доступен, но не покупается. Кроме доступности проверяются активность элемента, даты публикации и права группы на тип цены.
Цена в каталоге и корзине различается. В корзине показывается наименьшая доступная пользователю цена, даже если этот тип цены скрыт в каталоге.
Запись скидок через D7-таблицу. Методы изменения там - заглушки, которые бросают исключение. Скидки пишут классическим API.
Прямые SQL-запросы в таблицы каталога. Обходят проверки, события и пересчёт доступности - данные разъезжаются.
Остатки меняют напрямую при включённом складском учёте. Движения должны идти через складские документы.
Частые вопросы
Почему созданный кодом товар не появляется в каталоге?
Потому что создан только элемент инфоблока. Товар - это элемент плюс товарная запись с тем же идентификатором: тип, остаток, доступность. Без второго шага корзина, заказы и скидки просто не видят эту позицию. Сразу после создания элемента добавляйте товарные параметры, а затем цену.
Где хранить цену и остаток - в свойствах инфоблока или в каталоге?
Только в объектах каталога. Свойство инфоблока подходит для справочной информации, но рабочая цена и остаток должны лежать в таблицах модуля каталога: иначе их не увидят корзина, заказ, скидки и складской учёт. Итоговую цену со скидками и правами считает штатный метод, а не ваш код.
Как правильно менять статус заказа из кода?
Специализированными методами, а не общим обновлением полей. Только они запускают сопутствующую логику: обработчики оплаты, списание и резервирование остатков, почтовые уведомления, автоматизацию процессов. Обновление полей напрямую формально сработает, но бизнес-логика вокруг него не выполнится.
Товар в наличии, а купить нельзя. Что проверить?
По порядку: активен ли элемент и попадает ли он в даты публикации; есть ли у группы пользователя право видеть и покупать по этому типу цены; есть ли вообще цена для нужного типа; для товаров с предложениями - есть ли доступное предложение. Доступность в товарной записи - лишь одно из условий, а не гарантия покупки.
Связанные темы
- Каталог: продвинутое - комплекты, скидки, склады, подписка
- Валюты - мультивалютность и форматирование
- Личный кабинет и кассы - покупатель и 54-ФЗ
- Инфоблоки - карточка товара
- Обмен с 1С и HTTP - откуда в магазин приезжают товары, цены и остатки
- Обмен: выгрузка товаров - откуда каталог приезжает на сайт
- Не выгружаются товары из 1С - пустая витрина после обмена
- Ошибка импорта метаданных - лимит складов и типов цен редакции
- Торговые предложения - размеры, цвета и другие варианты товара
- Торговые предложения: настройка и связь с товаром - как включить и почему теряется привязка
- Каталог товаров - витрина, карточка и вывод свойств
- Карточка товара - шаблон, свои свойства и пустая детальная
- Обмен заказами с 1С - куда уезжает заказ после оформления
- Цены, скидки и купоны - типы цен, правила корзины, купоны
- Скидка на отдельную позицию в корзине - правила, купоны и ручная цена
- Заказы - статусы, свойства и события заказа
- Статусы заказов - смена из кода и сопоставление с 1С
- Корзина - добавление товара, количество, пересчёт
- Работа с корзиной из кода - добавить товар и посчитать итог
- Витрина каталога: список раздела, сортировка, фильтр - вывод товаров раздела и порядок
- Склады и остатки: выгрузка, вывод наличия, фильтр по складу - учёт наличия на практике
- Оформление заказа: настройка компонента и правка шаблона - форма заказа на практике
- Оформление заказа - решения по форме заказа
- Служба доставки и платёжная система: настройка и свой обработчик - доставка и оплата на практике
- Доставка и оплата - решения по службам и платёжным системам
- Правила работы с корзиной: условия, порядок, отладка - скидки на практике
- Раздел Интернет-магазин
- Онлайн-касса и чеки - фискальная часть продаж