Артикулы товаров - хранение, уникальность и поиск
Артикул - учётный номер позиции, по которому её ищут менеджер, кладовщик и учётная система. Разбираем, где платформа его держит, как удержать уникальность и как найти товар по артикулу на витрине и в заказе.
Решение
Где хранится артикул
Заводим свойство под артикул:
$prop = new \CIBlockProperty();$propId = $prop->Add([ 'IBLOCK_ID' => 5, 'NAME' => 'Артикул', 'CODE' => 'ARTNUMBER', // код обязателен: без него свойство не видно ORM 'PROPERTY_TYPE' => 'S', // строка: ведущие нули и буквы сохраняются 'IS_REQUIRED' => 'Y',]);Отдельного поля под артикул в торговом каталоге нет. Демо-каталог держит его
свойством ARTNUMBER, обмен с 1С заводит своё свойство CML2_ARTICLE, и на
одном сайте эти два свойства нередко уживаются вместе.
Артикул легко спутать с внешним кодом XML_ID. Внешний код - поле элемента, и
его ставит учётная система, чтобы сопоставлять позиции при обмене. Артикул
назначает человек, и для платформы это обычная строка. Штрихкод - третья
сущность: он лежит в своей таблице каталога и участвует в приёмке.
Артикул у товаров с вариантами
Смотрим, в какой инфоблок класть свойство:
$info = \CCatalogSKU::getInfoByIBlock($iblockId);// 'P' - инфоблок товаров, предложения лежат в отдельном инфоблоке// 'O' - инфоблок предложений: артикул заводят здесьprintf("%s -> %s\n", $info['CATALOG_TYPE'], $info['IBLOCK_ID'] ?? '-');Продаётся у такого товара предложение, а не родительская карточка. Артикул поэтому принадлежит предложению: у родителя он остаётся названием серии и в заказ не попадает.
Поиск товара по артикулу
Ищем позицию точным совпадением:
$rs = \CIBlockElement::GetList([], [ 'IBLOCK_ID' => $offersIblockId, '=PROPERTY_ARTNUMBER' => trim($query), // знак равенства - точное совпадение], false, ['nTopCount' => 2], ['ID', 'NAME', 'PROPERTY_CML2_LINK']);$found = $rs->Fetch();Это выборка по инфоблоку, а не полнотекстовый поиск. Одна найденная позиция - повод сразу открыть карточку товара, две и больше - показать обычный список.
Поиск по сайту устроен иначе: он ходит только в свой индекс, и значение свойства попадает туда по отдельному флагу. Включение индексации свойства и переиндексацию разбирает страница поиска по товарам.
Проверка уникальности
Проверяем артикул до записи элемента:
AddEventHandler('iblock', 'OnBeforeIBlockElementUpdate', 'checkArticle');function checkArticle(&$arFields) { // на добавление вешается отдельной строкой global $APPLICATION; $article = trim($arFields['PROPERTY_VALUES']['ARTNUMBER'] ?? ''); if ($article === '') { return; } $rs = \CIBlockElement::GetList([], ['IBLOCK_ID' => $arFields['IBLOCK_ID'], '=PROPERTY_ARTNUMBER' => $article, '!ID' => $arFields['ID']], false, ['nTopCount' => 1], ['ID']); if ($rs->Fetch()) { $APPLICATION->ThrowException('Артикул уже занят другим товаром'); return false; // отказ виден и в админке, и в своём скрипте импорта }}Уникальность значений свойства платформа не сторожит: два товара с одним артикулом сохраняются молча. Обработчик закрывает и правку руками в админке, и запись из своего кода.
Уже накопленные дубли обработчик не покажет: он смотрит только на текущую запись. Их ищут разовой проверкой каталога одним запросом к таблице свойств инфоблока.
Вывод артикула в карточке, корзине и заказе
Добираем артикулы к позициям заказа:
$items = [];foreach ($order->getBasket() as $basketItem) { $items[$basketItem->getProductId()] = ['PROPERTIES' => []]; // ключ обязателен}\CIBlockElement::GetPropertyValuesArray($items, $offersIblockId, ['ID' => array_keys($items)], ['CODE' => 'ARTNUMBER']);Позиция корзины хранит название, цену и количество, но свойств товара не знает. Одна выборка на весь заказ обходится дешевле, чем чтение свойств в цикле по позициям.
В карточке артикул печатает компонент каталога, и свойство должно стоять в его
списке свойств. Тот же список решает, попадёт ли артикул снимком в позицию
корзины: в демо-каталоге туда штатно кладут ARTNUMBER.
Обмен и импорт из файла
Записываем артикулы пачкой:
foreach ($rows as $id => $article) { \CIBlockElement::SetPropertyValuesEx($id, $iblockId, ['ARTNUMBER' => $article]);}\CIBlock::clearIblockTagCache($iblockId); // метод пишет свойство, но кеш не чиститМетод трогает только переданное свойство и не обнуляет остальные. Коды свойств
регистрозависимы: с ключом artnumber вместо ARTNUMBER значение не запишется
и ошибки не будет.
Штатный обмен сопоставляет позиции по внешнему коду, а не по артикулу. Артикул
приходит свойством и переписывается каждой выгрузкой, поэтому правка на сайте
живёт до следующего обмена. В импорте из файла артикул задают колонкой
IP_PROP с номером своего свойства.
Типичные проблемы
Обмен затирает артикул, поправленный в админке.
Артикул приходит из учётной системы свойством и переписывается каждой выгрузкой. Менять значение нужно в источнике, а на сайте оно только отображается.
Артикул виден в карточке, а в заказе его нет.
Позиция корзины хранит название, цену и количество, а свойства товара в неё не переносятся. Их добирают выборкой по идентификатору товара или кладут снимком при добавлении.
Поиск находит артикул МГ-14, но не находит МГ14.
Полнотекстовый индекс режет строку на слова, и написание без разделителя в него не попадает. Точный поиск выборкой по свойству от написания не зависит.
У товаров с вариантами артикул после обмена пустой.
Свойство заведено в инфоблоке товаров, а предложения лежат в отдельном инфоблоке. Свойство создают там, где живут сами предложения.
Ведущие нули артикула пропали после загрузки таблицы.
Свойство заведено числом либо файл открывали в табличном редакторе и он обрезал нули. Артикул хранят строкой и приводят к строке прямо при чтении файла.
Частые вопросы
Чем артикул отличается от внешнего кода товара?
Внешний код XML_ID - поле элемента и ключ сопоставления при обмене и импорте. Артикул - свойство инфоблока, его читают люди и печатают в документах. Совпадать они не обязаны, и по артикулу штатный обмен позиции не связывает.
Как сделать подбор товара по артикулу в интернет-магазине?
Двумя способами. Точная выборка по свойству находит позицию по полному артикулу и открывает её карточку. Поиск по сайту находит артикул среди прочего текста, но только после того, как свойство отмечено индексируемым и индекс перестроен.
Где заводить артикул у товара с торговыми предложениями?
В инфоблоке предложений. Учётная позиция здесь - предложение: у него свой остаток, своя цена и свой артикул. У родительской карточки артикул полезен как код серии, но в корзину и заказ он не уходит.
Как вывести список артикулов в письме о заказе?
Собрать их выборкой по идентификаторам товаров заказа и подставить в шаблон письма. Читать свойства по одному товару в цикле не стоит: на заказе из двадцати позиций это двадцать лишних запросов.
Есть ли штатная галочка «артикул уникален»?
Нет, уникальность значений свойства платформа не проверяет. Её делают обработчиком события до записи элемента, а накопленные дубли ищут отдельной проверкой каталога.
Смежное
- Каталог и карточка товара - оглавление подтемы
- Каталог и продажи - устройство каталога целиком
- Штрихкоды товаров - код для приёмки, отдельная таблица каталога
- Аудит каталога - разовая проверка и поиск накопленных дублей артикулов
- Поиск по товарам - индексация свойства и переиндексация после обмена
- Импорт каталога из файла - ключ связи и колонки свойств
- Дубли товаров после обмена - что делать с повторами по внешнему коду
- Торговые предложения - варианты товара и их учётные данные
- Карточка товара - вывод свойства в шаблоне
- Массовая правка товаров - запись свойств порциями