При настройке правил обмена в «1С:Конвертация данных 2.1» (КД 2) разработчики часто сталкиваются с нестандартными задачами трансформации данных. Одна из таких сложных ситуаций — необходимость изменить уникальный идентификатор (GUID/УИД) объекта непосредственно в процессе переноса, особенно когда типы данных в источнике и приемнике различаются.
В этой статье мы подробно разберем сценарий, когда в базе-источнике есть документ, ссылающийся на простой СправочникА (содержащий в себе сложный СправочникБ), а в базе-приемнике аналогичный документ должен ссылаться напрямую на СправочникБ, но с идентификатором (GUID) от СправочникаА. Мы проанализируем ошибки проектирования таких правил, выясним, почему стандартные подходы могут приводить к появлению «битых» ссылок, и рассмотрим три практических способа решения этой задачи.
Типичная ошибка начинающих разработчиков КД 2 — попытка передать нужный GUID в параметре объекта и перезаписать его в базу-приемник на этапе загрузки (например, в обработчике После загрузки). Схема выглядит логично: объект создается, мы перехватываем его, генерируем новый УИД на основе переданного параметра и записываем.
Однако при такой схеме ссылки в загружаемых документах гарантированно окажутся «битыми» (будет отображаться объект со строкой <Объект не найден...>). Давайте разберем причину этого поведения, скрытую в архитектуре работы обработки УниверсальныйОбменДаннымиXML:
После загрузки, объект записывается в базу данных с новым идентификатором. Но внутренняя таблица КД об этом «не знает» — в ней по-прежнему зарегистрировано сопоставление со старым, промежуточным идентификатором.Чтобы избежать этой проблемы, подмену идентификатора необходимо делать на этапе поиска или сериализации в XML, чтобы КД сразу зафиксировала правильный GUID в своей карте соответствий. Рассмотрим рабочие методы реализации.
Это самый чистый, надежный и простой в поддержке способ. Он не требует ручного вмешательства в XML-структуру и избавляет от необходимости прописывать код переноса для десятков реквизитов вручную.
Идея заключается в том, чтобы передать GUID СправочникаА в качестве параметра поиска для СправочникаБ и принудительно инициализировать объект по этой ссылке еще до начала его заполнения.
Разберем реализацию по шагам:
Шаг 1. Передача GUID из документа-источника
В ПРАВИЛЕ Конвертации Свойств (ПКС) документа, в котором выгружается ссылка на справочник, в обработчике Перед выгрузкой мы перехватываем GUID владельца и передаем его во входящие данные:
// Инициализируем структуру входящих данных для ПКО СправочникаБ
ИсходящиеДанные = Новый Структура;
Если ЗначениеЗаполнено(Источник.РеквизитСправочникаА) Тогда
// Получаем строковое представление GUID справочника А
ИсходящиеДанные.Вставить("ГУИД_А", Строка(Источник.РеквизитСправочникаА.УникальныйИдентификатор()));
КонецЕсли;
Шаг 2. Настройка ПКО СправочникБ -> СправочникБ
Перейдем в ПРАВИЛО Конвертации Объектов (ПКО) СправочникБ:
Искать объект приемника по внутреннему идентификатору источника. Это заставит КД использовать алгоритмы поиска вместо автоматического сопоставления по «родному» GUID.Параметры. Назовите параметр, например, ГУИД_А.Передавать данные в параметр и Использовать при поиске.Перед выгрузкой этого ПКС напишите код для получения переданного значения:
Если ВходящиеДанные <> Неопределено Тогда
Значение = ВходящиеДанные.Получить("ГУИД_А");
КонецЕсли;
Шаг 3. Подмена ссылки в обработчике «Поля поиска»
Теперь откройте обработчик Поля поиска в ПКО СправочникБ и пропишите код инициализации объекта по новому GUID:
ГУИД = "";
Если ПараметрыОбъекта <> Неопределено Тогда
ГУИД = ПараметрыОбъекта.Получить("ГУИД_А");
КонецЕсли;
Если Не ПустаяСтрока(ГУИД) Тогда
НовыйУИД = Новый УникальныйИдентификатор(ГУИД);
// Генерируем или находим ссылку в приемнике с УИД-ом справочника А
СсылкаНаОбъект = Справочники.СправочникБ.ПолучитьСсылку(НовыйУИД);
// Прекращаем дальнейший стандартный поиск КД
ПрекратитьПоиск = Истина;
КонецЕсли;
Как это работает: Установив флаг ПрекратитьПоиск = Истина и заполнив переменную СсылкаНаОбъект, мы явно указываем КД использовать именно эту ссылку. Если элемента с таким GUID еще нет в базе-приемнике, платформа создаст его при записи свойств. Если он уже существовал, КД просто обновит его реквизиты. При этом таблица соответствий заполнится корректно, и документы запишутся без «битых» ссылок.
Второй способ базируется на стандартном механизме КД, когда ПКО настраивается между разными типами объектов. Если источником выступает СправочникА, а приемником — СправочникБ, КД по умолчанию автоматически запишет GUID источника (то есть СправочникаА) в создаваемый объект приемника (СправочникБ).
Проблема этого подхода в исходной задаче — данные для заполнения СправочникаБ находятся внутри его реквизитов в источнике, а не в самом СправочникеА. Нам придется перенаправлять свойства.
Рассмотрим по шагам реализацию этого метода:
СправочникА, а Приемник — СправочникБ.Перед выгрузкой каждого ПКС пишется код:
// Перенаправляем получение данных на внутренний реквизит Справочника Б
Значение = Источник.РеквизитСправочникаБ[ИмяРеквизита];
Если у СправочникаБ очень много реквизитов и табличных частей, прописывать такой код для каждого свойства вручную может быть трудоемко. Этот способ отлично подходит, если структура справочника-приемника простая или состоит всего из нескольких полей.
Этот способ можно назвать «низкоуровневым хаком». Мы принудительно заменяем выгружаемый узел идентификатора в XML-файле обмена на нужный нам GUID еще на стороне базы-источника.
Давайте проанализируем, как это сделать:
Перед выгрузкой перенаправляем выгрузку на внутренний СправочникБ:
Значение = Источник.РеквизитСправочникаА.РеквизитСправочникаБ;
Мы используем стандартное ПКО СправочникБ -> СправочникБ. Но в самом ПКО в обработчике Перед выгрузкой мы отключаем стандартные поля поиска и вручную конструируем XML-узел уникального идентификатора, подставляя туда GUID от СправочникаА:
// Код в обработчике "Перед выгрузкой" ПКО СправочникБ -> СправочникБ
// (Предварительно нужно передать ГУИД_А через ВходящиеДанные из документа)
УзелКоллекцииСвойств.ЗаписатьНачалоЭлемента("Свойство");
УстановитьАтрибут(УзелКоллекцииСвойств, "Имя", "{УникальныйИдентификатор}");
Если НЕ ВыполнитьОбменДаннымиВОптимизированномФормате Тогда
УстановитьАтрибут(УзелКоллекцииСвойств, "Тип", "Строка");
КонецЕсли;
одЗаписатьЭлемент(УзелКоллекцииСвойств, "Значение", ГУИД_А);
УзелКоллекцииСвойств.ЗаписатьКонецЭлемента();
Важное предупреждение: Хотя этот способ рабочий, применять его в промышленной разработке крайне не рекомендуется. Он чувствителен к версии платформы, настройкам оптимизированного формата обмена КД и существенно усложняет чтение и отладку правил другими разработчиками.
Мы рассмотрели три подхода к решению проблемы подмены GUID при конвертации данных. Проанализировав все плюсы и минусы, можно сделать вывод: Способ 1 (через обработчик «Поля поиска») является наиболее предпочтительным. Он сохраняет декларативную структуру правил КД 2.1, гарантирует отсутствие битых ссылок при загрузке документов и легко поддается отладке стандартными средствами платформы 1С.