Перейти к содержимому

Дубли товаров после обмена - поиск и разведение

Находим дубли товаров, появившиеся после обмена, и разводим их так, чтобы не потерять историю заказов.

Решение

Ищем повторяющиеся внешние коды:

$res = CIBlockElement::GetList([], ['IBLOCK_ID' => 5], ['XML_ID']);
while ($row = $res->Fetch()) {
if ($row['CNT'] > 1) {
printf("код %-24s повторяется %d раз\n", $row['XML_ID'], $row['CNT']);
}
}

Повторяющийся внешний код означает, что обмен создал позицию заново вместо обновления существующей. Это главный признак дубля: одинаковые названия при разных кодах - совсем другая история.

Ищем дубли по названию при разных кодах:

$res = CIBlockElement::GetList([], ['IBLOCK_ID' => 5], ['NAME']);
while ($row = $res->Fetch()) {
if ($row['CNT'] > 1) {
printf("название %-40s встречается %d раз\n",
mb_substr($row['NAME'], 0, 40), $row['CNT']);
}
}

Одинаковые названия при разных кодах - обычно не дубли, а разные позиции номенклатуры. Прежде чем чистить такие пары, стоит убедиться, что перед вами действительно один товар, а не два похожих между собой.

Смотрим, у какой позиции есть заказы:

use Bitrix\Sale\Internals\BasketTable;
$rows = BasketTable::getList([
'select' => ['ORDER_ID'],
'filter' => ['=PRODUCT_ID' => $elementId, '!=ORDER_ID' => null],
'limit' => 5,
])->fetchAll();
printf("заказов с этой позицией: %d\n", count($rows));

Позицию, на которую ссылаются заказы, удалять нельзя ни при каких условиях: история продаж потеряет связь с товаром. Из пары дублей оставляют именно эту позицию, а вторую деактивируют.

Разводим найденные дубли:

$el = new CIBlockElement();
// лишнюю позицию не удаляем, а снимаем с публикации и метим кодом
$el->Update($duplicateId, [
'ACTIVE' => 'N',
'XML_ID' => 'DUP-' . $duplicateId, // освобождаем код для основной позиции
]);

Смена внешнего кода у лишней позиции освобождает его для основной записи. После этого обмен снова находит нужный товар и перестаёт плодить всё новые копии.

Дубли разделов ведут себя точно так же и появляются даже чаще товарных. Смена структуры групп в учётной системе создаёт новые разделы вместо переименования старых, и каталог получает две ветки с одинаковыми названиями.

Чистка накопленных дублей всегда идёт по одному правилу: сначала выясняют, какая из позиций участвует в заказах, и только потом решают судьбу второй. Обратный порядок приводит к потере истории продаж, а восстановить её потом уже нечем.

Предотвращение дублей обходится заметно дешевле их чистки. Внешние коды не меняют после первой выгрузки, один инфоблок связывают с одним источником, а переносы базы проверяют на копии сайта - это закрывает почти все сценарии появления дублей.

Типичные проблемы

После переноса базы каталог удвоился.

Внешние коды товаров не совпали с прежними значениями. Обмен не нашёл существующие позиции и создал их все заново.

Дубли появляются после каждой выгрузки.

Учётная система присылает новые внешние коды при каждом обмене. Это настройка именно на её стороне, а вовсе не на стороне самого сайта.

Удаление дубля сломало старые заказы.

Позиции старых заказов ссылались именно на удалённый элемент. Из пары дублей оставляют ту позицию, что участвует в заказах.

В каталоге две одинаковые ветки разделов.

Структура групп в учётной системе менялась, и разделы приехали как новые. Прежние разделы при этом остаются на своём месте.

Одинаковые названия оказались не дублями.

Это разные позиции номенклатуры с разными внешними кодами. Совпадение одних только названий дублем не является.

Частые вопросы

Можно ли объединить дубли автоматически?

Штатного объединения нет: платформа не знает, какая позиция главная. Пары разводят скриптом по своим правилам с ручной проверкой списка.

Что делать с картинками дубля?

Оставить у основной позиции, если у неё их нет. Файлы дубля после деактивации остаются на диске и чистятся отдельно.

Почему нельзя просто удалить лишние позиции?

На них могут ссылаться заказы, подписки и связанные товары. Деактивация обратима, удаление - нет.

Как убедиться, что дубли перестали появляться?

Повторить поиск повторяющихся кодов после следующего обмена. Пустой результат означает, что причина устранена.

Смежное

Первоисточник