При работе с системой 1С и ее интеграцией с внешними ресурсами, такими как Битрикс, мы часто сталкиваемся с нюансами обмена данными. Один из таких нюансов возникает при изменении штрихкодов у номенклатуры или ее характеристик. Мы можем заметить, что после добавления или удаления штрихкода соответствующая позиция не выгружается на сайт, хотя логически она изменилась. Давайте разберем эту проблему и найдем эффективные решения.
Суть проблемы заключается в том, что изменение штрихкода (который часто хранится в отдельном регистре сведений) не приводит к автоматическому изменению поля ВерсияДанных у самой характеристики номенклатуры или номенклатуры. Модуль обмена с Битрикс, особенно при использовании частичной выгрузки или определенных настроек версионности, ориентируется именно на это поле для определения факта изменения объекта и необходимости его повторной обработки на стороне сайта.
Прежде всего, давайте рассмотрим, что представляет собой поле ВерсияДанных в системе 1С. Это внутреннее служебное свойство любого объекта конфигурации, который хранится в информационной базе (справочника, документа и т.д.). Его основная функция — служить маркером изменения объекта. Поле ВерсияДанных автоматически обновляется при каждой записи объекта в базу данных.
Мы используем это поле для реализации оптимистической блокировки, а также для быстрого определения, был ли объект изменен после последнего прочтения. Это своего рода "отпечаток" состояния объекта на момент его записи. Важно понимать, что само по себе ВерсияДанных не предоставляет информации о том, какая именно часть объекта была изменена (какой реквизит), а лишь указывает на факт любого изменения и перезаписи.
В контексте баз данных, например, в MySQL, аналогичное поведение может быть реализовано через поле типа timestamp, которое также обновляется при каждой модификации записи. Таким образом, ВерсияДанных в 1С — это не хэш в криптографическом смысле, а скорее уникальный идентификатор версии записи, который меняется при каждом сохранении.
Далее, проанализируем, как осуществляется обмен данными между 1С и 1С-Битрикс и какую роль в этом процессе играет ВерсияДанных. Обмен обычно происходит по протоколу CommerceML, при котором 1С формирует XML-файлы (например, import.xml, offers.xml) и передает их на сайт. Инициатором обмена всегда является 1С.
На стороне Битрикс модуль обмена принимает эти XML-файлы и обрабатывает их. Для оптимизации процесса и предотвращения многократной обработки неизмененных данных, Битрикс использует механизм сравнения версий. В XML-файлах, выгружаемых из 1С, присутствует элемент IBLOCK_XML2_VERSION. Этот элемент может содержать значение поля ВерсияДанных из 1С или вычисленную контрольную сумму (CRC) для объекта, в зависимости от настроек и типа данных.
Битрикс сравнивает значение IBLOCK_XML2_VERSION из входящего XML-файла с версией, которая была сохранена на сайте при предыдущей выгрузке для того же элемента. Рассмотрим фрагмент логики, как это происходит на стороне Битрикса:
if(isset($arXMLElement[$this->mess["IBLOCK_XML2_VERSION"]]))
$arElement["TMP_ID"] = $arXMLElement[$this->mess["IBLOCK_XML2_VERSION"]];
else
$arElement["TMP_ID"] = $this->GetElementCRC($arXMLElement);
// ... далее в коде происходит сравнение
if($arDBElement = $rsElement->Fetch()) {
$bMatch = ($arElement["TMP_ID"] == $arDBElement["TMP_ID"]);
}
Если переменная $bMatch (соответствие версий) оказывается истинной, то Битрикс предполагает, что элемент не изменился. В этом случае он может просто активировать товар на сайте (если тот был деактивирован) и пропустить полную обработку всех его свойств. Если же версии не совпадают, то элемент считается измененным, и запускается полная процедура обновления всех его данных на сайте.
Именно здесь кроется корень нашей проблемы со штрихкодами. Штрихкоды в 1С часто хранятся не как прямые реквизиты объекта "ХарактеристикаНоменклатуры" или "Номенклатура", а в отдельном регистре сведений, например, Штрихкоды. При добавлении или удалении записи в этом регистре поле ВерсияДанных у соответствующей характеристики или номенклатуры не меняется, так как сам объект "ХарактеристикаНоменклатуры" не записывается. В результате, для Битрикса версия остается прежней, и обновленные штрихкоды не попадают на сайт, поскольку элемент не подвергается полной обработке.
Мы также должны проанализировать, как на эту ситуацию влияет настройка "Использовать версионность" в модуле обмена на стороне 1С или Битрикс. Если речь идет о настройке в модуле обмена 1С (часто это опция в настройках обмена с сайтом), которая влияет на формирование XML-файла, или о логике на стороне Битрикс, она может существенно изменить поведение.
При включении такой опции, модуль обмена может модифицировать значение, передаваемое в IBLOCK_XML2_VERSION, например, добавляя к нему некие числовые значения или генерируя уникальный идентификатор при каждой выгрузке. Это приводит к тому, что "ВерсияДанных" файла импорта всегда будет отличаться от версии, сохраненной на сайте, даже если объект по сути не менялся.
Последствия такого подхода очевидны: поскольку версии всегда будут разными, Битрикс каждый раз будет считать, что все выгруженные из 1С позиции изменились. Это означает, что при каждой полной выгрузке (а автор темы упоминает, что использует именно полную выгрузку) будет выполняться полная обработка всех позиций, удовлетворяющих условиям отбора, на стороне сайта.
Для небольшого количества товаров это может быть приемлемо, но при нескольких тысячах и более позиций, а также частой выгрузке, такая полная обработка может привести к значительной нагрузке на сервер сайта и замедлению работы. Автор темы справедливо отмечает, что не хочет обрабатывать на сайте каждый раз весь каталог только из-за изменения штрихкода у одной или нескольких позиций.
Исходя из всего вышесказанного, мы приходим к главному вопросу: как принудительно инициировать изменение поля ВерсияДанных у объекта "ХарактеристикаНоменклатуры" (или "Номенклатура") в 1С, когда изменяются только его штрихкоды?
Как мы уже выяснили, поле ВерсияДанных изменяется только при записи объекта. Это ключевой принцип. Следовательно, для того чтобы изменить это поле, нам необходимо выполнить запись соответствующего объекта в информационную базу. Если мы говорим о программном воздействии, это означает вызов метода Объект.Записать() для экземпляра объекта.
Итак, наша задача сводится к тому, чтобы при добавлении или удалении штрихкода программно найти связанную с ним характеристику (или номенклатуру) и вызвать для неё метод Записать(). Это заставит 1С обновить поле ВерсияДанных, и при следующей выгрузке на Битрикс эта позиция будет корректно распознана как измененная и полностью обработана.
Давайте разберем по шагам, как мы можем реализовать это решение в конфигурации 1С. Наша цель — при изменении информации о штрихкодах инициировать запись связанного объекта, чтобы обновить его ВерсияДанных.
Для автоматизации процесса мы должны отслеживать моменты, когда изменяются штрихкоды. Наиболее подходящим механизмом для этого является подписка на событие 1С.
Штрихкоды в типовых конфигурациях 1С (например, 1С:Управление торговлей) чаще всего хранятся в регистре сведений Штрихкоды. Поэтому нам потребуется создать подписку на событие для этого регистра. Мы можем выбрать событие ПриЗаписи или ПослеЗаписи регистра сведений Штрихкоды. Событие ПослеЗаписи обычно предпочтительнее, так как оно срабатывает уже после того, как изменения были успешно сохранены в базе данных.
Для создания подписки, перейдем в конфигурацию, найдем раздел "Общие" - "Подписки на события". Создадим новую подписку.
Теперь нам нужно создать процедуру-обработчик в указанном общем модуле. Эта процедура будет получать в качестве параметра объект регистра сведений, который был записан. Внутри этой процедуры мы должны определить, к какой характеристике или номенклатуре относится измененный штрихкод, получить соответствующий объект и вызвать для него метод Записать().
Рассмотрим пример кода для такого обработчика:
// В ОбщемМодуле "ОбщийМодульДляОбменаСайтом"
Процедура ПослеЗаписиШтрихкода(Источник, Отказ) Экспорт
// Источник - это объект типа РегистрСведенийЗапись.Штрихкоды
// Проверяем, что Источник является записью регистра сведений "Штрихкоды"
Если ТипЗнч(Источник) = Тип("РегистрСведенийЗапись.Штрихкоды") Тогда
// Определяем, к какому объекту относится штрихкод: к номенклатуре или характеристике
// В регистре Штрихкоды обычно есть поля Номенклатура и Характеристика
// Если штрихкод привязан к характеристике номенклатуры
Если НЕ Источник.Характеристика.Пустая() Тогда
// Получаем объект характеристики
// Предполагается, что Источник.Характеристика - это ссылка на справочник "ХарактеристикиНоменклатуры"
ОбъектХарактеристики = Источник.Характеристика.ПолучитьОбъект();
Попытка
// Вызываем метод Записать() для объекта характеристики
ОбъектХарактеристики.Записать();
// Сообщить("ВерсияДанных характеристики " + Источник.Характеристика + " успешно обновлена.");
Исключение
// Обрабатываем возможные ошибки при записи (например, блокировки)
Сообщить("Ошибка при записи характеристики '" + Источник.Характеристика + "': " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
// Если штрихкод относится к самой номенклатуре (без характеристики)
ИначеЕсли НЕ Источник.Номенклатура.Пустая() Тогда
// Получаем объект номенклатуры
// Предполагается, что Источник.Номенклатура - это ссылка на справочник "Номенклатура"
ОбъектНоменклатура = Источник.Номенклатура.ПолучитьОбъект();
Попытка
// Вызываем метод Записать() для объекта номенклатуры
ОбъектНоменклатура.Записать();
// Сообщить("ВерсияДанных номенклатуры " + Источник.Номенклатура + " успешно обновлена.");
Исключение
// Обрабатываем возможные ошибки
Сообщить("Ошибка при записи номенклатуры '" + Источник.Номенклатура + "': " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Важные моменты:
Источник.Характеристика.ПолучитьОбъект() и Источник.Номенклатура.ПолучитьОбъект() для получения динамического объекта, который затем можно записать.Попытка ... Исключение крайне важен для обработки возможных ошибок при записи объекта, таких как конфликты блокировок или нарушения целостности данных. Это позволит избежать аварийного завершения работы программы.ВерсияДанных и последующей выгрузкой на сайт.После реализации этой подписки на событие, любое изменение штрихкода (добавление, удаление, изменение существующего) для характеристики или номенклатуры приведет к записи соответствующего объекта. Это, в свою очередь, обновит его ВерсияДанных и гарантирует, что при следующей полной выгрузке на Битрикс эта позиция будет обработана как измененная, а обновленные штрихкоды попадут на сайт.
Хотя метод с принудительной записью объекта является наиболее прямым для изменения ВерсияДанных, существуют и другие способы сообщить системе обмена, что объект нуждается в выгрузке, даже если его ВерсияДанных не изменилась. Эти методы могут быть полезны в специфических сценариях или как альтернатива, если принудительная запись объекта нежелательна по каким-либо причинам (например, из-за сложных бизнес-процессов или блокировок).
В системе 1С существует стандартная обработка "Регистрация изменений для обмена данными". Мы можем использовать ее для ручной регистрации изменений для выбранных объектов по определенному узлу обмена. Этот метод подходит для разовых операций, тестирования или исправления небольшого числа некорректно выгруженных позиций.
Для использования этой обработки необходимо:
Это не автоматизированное решение, но оно дает представление о том, что система обмена может быть явно "проинформирована" о необходимости выгрузки объекта.
Для автоматизации можно использовать метод ЗарегистрироватьИзменения() менеджера планов обмена. Этот метод позволяет программно зарегистрировать объект для выгрузки по определенному узлу обмена, независимо от значения поля ВерсияДанных. Это более изящное решение, если сама запись объекта нежелательна.
Мы можем встроить вызов этого метода в наш обработчик подписки на событие ПослеЗаписи регистра Штрихкоды, вместо вызова Объект.Записать(). Приведем пример кода:
// В ОбщемМодуле "ОбщийМодульДляОбменаСайтом"
Процедура ПослеЗаписиШтрихкодаПрограммноРегистрировать(Источник, Отказ) Экспорт
Если ТипЗнч(Источник) = Тип("РегистрСведенийЗапись.Штрихкоды") Тогда
// Получаем ссылку на объект, который нужно зарегистрировать
СсылкаНаОбъектДляРегистрации = Неопределено;
Если НЕ Источник.Характеристика.Пустая() Тогда
СсылкаНаОбъектДляРегистрации = Источник.Характеристика;
ИначеЕсли НЕ Источник.Номенклатура.Пустая() Тогда
СсылкаНаОбъектДляРегистрации = Источник.Номенклатура;
КонецЕсли;
Если СсылкаНаОбъектДляРегистрации <> Неопределено Тогда
Попытка
// Получаем менеджер плана обмена (замените "ОбменСБитрикс" на имя вашего плана обмена)
ПланОбменаМенеджер = ПланыОбмена.ОбменСБитрикс;
// Находим узел обмена, куда хотим зарегистрировать изменения
// В реальной системе может быть несколько узлов, нужно определить целевой
// Например, по наименованию или коду
УзелОбмена = ПланОбменаМенеджер.НайтиПоКоду("001"); // Пример кода узла обмена, замените на актуальный
Если УзелОбмена <> Неопределено Тогда
// Регистрируем изменение конкретного объекта
ПланОбменаМенеджер.ЗарегистрироватьИзменения(УзелОбмена, СсылкаНаОбъектДляРегистрации);
// Сообщить("Изменение объекта " + СсылкаНаОбъектДляРегистрации + " зарегистрировано для обмена.");
Иначе
Сообщить("Ошибка: Не найден узел обмена для регистрации изменений.", СтатусСообщения.Важное);
КонецЕсли;
Исключение
Сообщить("Ошибка при программной регистрации изменений для '" + СсылкаНаОбъектДляРегистрации + "': " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Важные моменты:
ПланыОбмена.ОбменСБитрикс) и код или наименование узла обмена, с которым вы работаете.ПланОбменаМенеджер.ЗарегистрироватьИзменения(УзелОбмена, Метаданные.Справочники.ХарактеристикиНоменклатуры);), хотя последнее может привести к избыточной выгрузке.ВерсияДанных, а лишь помечает его для выгрузки в таблице регистрации изменений плана обмена.В заключение, мы выяснили, что проблема с невыгрузкой характеристик номенклатуры с измененными штрихкодами на Битрикс кроется в механизме обновления поля ВерсияДанных в 1С и его использовании модулем обмена. Мы рассмотрели два основных подхода к решению этой проблемы: принудительное изменение ВерсияДанных через вызов метода Записать() для объекта (с использованием подписки на событие) и программную регистрацию изменений для обмена. Оба метода могут быть эффективными, и выбор конкретного подхода зависит от ваших требований к производительности, желаемой степени автоматизации и общей архитектуры вашей системы обмена.
Хотя метод с Объект.Записать() может показаться не самым элегантным, он прост в реализации и эффективно решает поставленную задачу, обеспечивая своевременную выгрузку обновленных штрихкодов на сайт.