При разработке правил обмена в Конвертации данных 2.1 (КД 2.1) программисты часто сталкиваются с задачей: при загрузке справочника нужно сразу создать запись в связанном регистре сведений (например, для хранения внешних кодов синхронизации). Основная сложность заключается в том, что в обработчике После загрузки новый объект еще не записан в базу данных, и его стандартное свойство Объект.Ссылка остается пустым. Если попытаться передать такую пустую ссылку в регистр, система выдаст ошибку.
В этой статье мы подробно разберем, как правильно зарезервировать ссылку для нового объекта, почему не стоит использовать метод Записать() внутри обработчиков и какие архитектурные подходы считаются наиболее профессиональными.
Проанализируем механизм работы универсальной обработки обмена данными. Когда данные попадают в базу-приемник, система сначала создает объект в оперативной памяти, заполняет его реквизиты и только в самом конце транзакции фиксирует изменения. Обработчик После загрузки срабатывает именно в тот момент, когда объект в памяти уже полностью сформирован, но физически в таблицах базы данных его еще нет. Следовательно, уникальный идентификатор (GUID) еще не назначен, если мы не сделаем этого принудительно.
Рассмотрим ситуацию, описанную автором: при попытке вызвать Объект.УстановитьСсылкуНового() без параметров ничего не происходит, а попытка записи в регистр проваливается, так как поле Ссылка (ведущее измерение или реквизит регистра) не может быть пустым.
Это самый быстрый и правильный с точки зрения платформы 1С способ. Мы можем самостоятельно сгенерировать ссылку (GUID) и «сообщить» объекту, что при записи он должен использовать именно этот идентификатор. Разберем алгоритм действий в обработчике ПКО После загрузки:
1. Сначала проверим, является ли объект новым. Для этого в КД 2.1 есть встроенная переменная ОбъектНайден.
2. Если объект новый, попытаемся получить уже зарезервированную ссылку.
3. Если ссылка еще не была зарезервирована платформой, создадим её программно.
Посмотрим на пример «правильного кода»:
Если ОбъектНайден Тогда
// Объект уже есть в базе, используем его существующую ссылку
СсылкаДляРегистра = Ссылка;
Иначе
// Объект новый, нужно зарезервировать GUID
СсылкаДляРегистра = Объект.ПолучитьСсылкуНового();
Если СсылкаДляРегистра.Пустая() Тогда
// Если ссылка еще не назначена, создаем новую через менеджер справочника
// Замените "Номенклатура" на имя вашего справочника
СсылкаДляРегистра = Справочники.Номенклатура.ПолучитьСсылку();
Объект.УстановитьСсылкуНового(СсылкаДляРегистра);
КонецЕсли;
КонецЕсли;
// Теперь переменную СсылкаДляРегистра можно смело использовать для записи в регистр
// Например, через создание набора записей или вызов алгоритма
Этот метод позволяет нам работать с объектом так, будто он уже существует, сохраняя при этом целостность транзакции обмена.
В обсуждениях часто встречается мнение: «Просто напишите Объект.Записать() в начале обработчика, и ссылка появится». Проанализируем, почему этого делать категорически не рекомендуется:
Записать() провоцирует выполнение всех подписок на события «При записи», «Перед записью», выполнение движений и проверку прав. Сразу после завершения обработчика После загрузки, сама обработка конвертации все равно вызовет запись объекта еще раз. Таким образом, работа сервера удваивается.Методика «просто записать» считается «костылем», который может работать в простых обменах, но неизбежно ведет к проблемам в высоконагруженных системах или при сложных алгоритмах синхронизации.
Если нам нужно не просто записать одну строчку в регистр, а выполнить полноценную конвертацию связанных данных, лучше воспользоваться архитектурным подходом КД 2.1. Вместо написания кода записи в регистр вручную (через СоздатьНаборЗаписей), мы можем создать отдельное ПКО (Правило конвертации объекта) для регистра сведений.
В этом случае в обработчике После загрузки справочника мы вызываем программную выгрузку:
// Подготовим структуру с данными для регистра
ПараметрыРегистра = Новый Структура;
ПараметрыРегистра.Вставить("Номенклатура", СсылкаДляРегистра);
ПараметрыРегистра.Вставить("ВнешнийКод", Объект.ВнешнийКод);
// Вызываем выгрузку по правилу для регистра
// "СинхронизацияВнешнихКодов" - это имя вашего ПКО для регистра
ВыгрузитьПоПравилу(,, ПараметрыРегистра,, "СинхронизацияВнешнихКодов");
Этот способ позволяет системе управления обменом самой решить, когда и как записать данные в регистр, соблюдая все правила и иерархию объектов.
Выясним еще одну деталь: иногда лучшим решением будет использование уникального идентификатора непосредственно из файла-источника. В КД 2.1 идентификатор источника доступен в обработчиках. Если мы принудительно установим ссылку нового объекта равной идентификатору из источника, мы обеспечим полную идентичность данных в обеих базах. Это избавляет от необходимости хранить «карты соответствия» в регистрах, так как поиск всегда будет идти по UID.
Для реализации этого подхода в ПКО Перед загрузкой или После загрузки (если объект новый) можно выполнить:
Если Не ОбъектНайден Тогда
UID = Новый УникальныйИдентификатор(СокрЛП(ПараметрыОбъекта.Получить("ИдентификаторИсточника")));
НоваяСсылка = Справочники.Номенклатура.ПолучитьСсылку(UID);
Объект.УстановитьСсылкуНового(НоваяСсылка);
КонецЕсли;
Рассмотрев проблему получения ссылки нового объекта, мы пришли к следующим выводам:
Объект.Записать() внутри обработчиков загрузки, если этого можно избежать.ПолучитьСсылкуНового() и УстановитьСсылкуНового().ПолучитьСсылку() менеджера соответствующего справочника.ВыгрузитьПоПравилу(), передавая зарезервированную ссылку в качестве параметра.Соблюдение этих правил гарантирует стабильную работу обмена данными, отсутствие «битых» ссылок в регистрах и высокую производительность системы при загрузке больших массивов информации.