При настройке обмена данными в 1С через универсальный формат часто возникает задача, когда для корректной загрузки одного объекта (например, документа) необходимо, чтобы другой, связанный с ним объект (например, элемент справочника), уже существовал в базе-приемнике. Особенно актуальной эта проблема становится, когда поиск связанного объекта выполняется не по стандартному уникальному идентификатору (УИД), а по особым правилам, например, по номеру телефона или другому реквизиту.
Рассмотрим ситуацию: у нас есть документ, который ссылается на справочник. В базе-приемнике этот элемент справочника должен быть найден по номеру телефона. Если документ загружается раньше, чем сам элемент справочника, поиск не дает результатов, и в реквизит документа записывается пустая ссылка. Как гарантировать, что справочник будет загружен раньше документа, чтобы поиск всегда отрабатывал корректно?
Проанализируем эту задачу и разберем наиболее надежные способы ее решения.
В универсальном формате обмена, в отличие от "Конвертации данных 2.0", нет встроенного механизма для установки приоритетов загрузки для разных типов объектов в рамках одного XML-файла. Объекты обрабатываются последовательно, в том порядке, в котором они записаны в файле обмена.
При стандартном обмене по УИДам это не является проблемой. Если система встречает ссылку на еще не загруженный объект, она создает "битую ссылку". Когда нужный объект будет загружен позже в рамках этого же пакета, платформа автоматически заменит битую ссылку на корректную.
Проблема возникает, когда мы отходим от стандартной логики. Если в правилах конвертации прописано: "найти объект по полю X, и если не найден — установить пустую ссылку", то механизм разрешения битых ссылок не срабатывает. Система честно выполняет инструкцию, записывает Справочники.ИмяСправочника.ПустаяСсылка(), и эта пустая ссылка уже никогда не будет заменена автоматически, даже если нужный элемент справочника загрузится следующим шагом.
Первая мысль, которая приходит в голову — попытаться "положить" в XML-файл нужный справочник раньше документа. Это можно сделать, принудительно выгрузив элемент справочника в событии ПриОтправкеДанных самого документа.
Посмотрим на пример:
При выгрузке документа мы можем добавить код, который найдет связанный объект и явно добавит его в пакет данных.
// В модуле объекта, в процедуре ПриОтправкеДанных
// ПартнерОбъект - это ссылка на элемент справочника, который нужно выгрузить
ОбменДаннымиXDTOСервер.ВыгрузкаОбъектаВыборки(КомпонентыОбмена, ПартнерОбъект);
// Дополнительно можно передать параметры для поиска в приемнике
КомпонентыОбмена.AdditionalInfo.Вставить("ПараметрыПоискаПартнера", ЗначенияДляПоиска);
Почему это ненадежно?
Хотя на практике такой подход часто приводит к тому, что объект справочника оказывается в файле раньше документа, нет стопроцентной гарантии, что это будет происходить всегда. Механизм формирования XML-файла может иметь свою внутреннюю логику, и порядок объектов может измениться. Использовать этот метод можно только для простых случаев и с полным осознанием рисков.
Самым правильным и надежным решением является использование механизма отложенной обработки. Суть в том, чтобы не пытаться установить ссылку в момент загрузки документа, а отложить эту операцию до момента, когда все объекты из пакета будут гарантированно загружены.
Разберем этот процесс по шагам.
В обработчике события, где происходит поиск и установка ссылки (например, ПередЗаписью в правилах конвертации объекта), мы выполняем поиск. Если объект не найден, мы не устанавливаем пустую ссылку. Вместо этого мы сохраняем всю необходимую информацию для повторного поиска в будущем.
Идеальное место для временного хранения таких данных — параметр КомпонентыОбмена.ПараметрыКонвертации. Это структура, которая "живет" на протяжении всей транзакции загрузки пакета данных.
Пример кода в ПКО документа (обработчик ПередЗаписью):
// Пытаемся найти физлицо по номеру телефона
НайденноеФизЛицо = НайтиФизЛицоПоТелефону(ТелефонИзИсточника);
Если НЕ ЗначениеЗаполнено(НайденноеФизЛицо) Тогда
// Если не нашли, не ставим пустую ссылку, а сохраняем данные для отложенной обработки
// Проверяем, существует ли наш массив в параметрах
Если КомпонентыОбмена.ПараметрыКонвертации.Свойство("ОбъектыДляОтложеннойОбработки") Тогда
МассивОбработки = КомпонентыОбмена.ПараметрыКонвертации.ОбъектыДляОтложеннойОбработки;
Иначе
МассивОбработки = Новый Массив;
КомпонентыОбмена.ПараметрыКонвертации.Вставить("ОбъектыДляОтложеннойОбработки", МассивОбработки);
КонецЕсли;
// Добавляем в массив структуру с нужными данными
СтруктураЗадачи = Новый Структура;
СтруктураЗадачи.Вставить("ЗагруженныйДокумент", Объект.Ссылка);
СтруктураЗадачи.Вставить("ТелефонДляПоиска", ТелефонИзИсточника);
МассивОбработки.Добавить(СтруктураЗадачи);
// Важно! Сам документ можно записать, но, возможно, стоит отменить его проведение
Объект.ДополнительныеСвойства.Вставить("ОтменитьПроведение", Истина);
Иначе
// Если физлицо найдено сразу, просто устанавливаем его
Объект.ФизЛицо = НайденноеФизЛицо;
КонецЕсли;
Нам нужен механизм, который сработает после того, как все объекты из XML-файла будут загружены. Для этого можно использовать обработчик ПослеЗагрузкиДанных в модуле менеджера плана обмена.
В этом обработчике мы проверяем, есть ли у нас задачи для отложенной обработки, и выполняем их.
Пример кода в модуле менеджера плана обмена:
Процедура ПослеЗагрузкиДанных(КомпонентыОбмена) Экспорт
// Проверяем, есть ли у нас отложенные задачи
Если КомпонентыОбмена.ПараметрыКонвертации.Свойство("ОбъектыДляОтложеннойОбработки") Тогда
МассивОбработки = КомпонентыОбмена.ПараметрыКонвертации.ОбъектыДляОтложеннойОбработки;
Для Каждого Задача Из МассивОбработки Цикл
// Теперь все справочники уже загружены, поиск должен быть успешным
НайденноеФизЛицо = НайтиФизЛицоПоТелефону(Задача.ТелефонДляПоиска);
Если ЗначениеЗаполнено(НайденноеФизЛицо) Тогда
// Получаем объект документа и дозаполняем реквизит
ДокументОбъект = Задача.ЗагруженныйДокумент.ПолучитьОбъект();
ДокументОбъект.ФизЛицо = НайденноеФизЛицо;
// Пытаемся записать и провести
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
// Записать лог ошибки
КонецПопытки;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Этот подход гарантирует целостность данных, так как обработка зависимостей происходит в тот момент, когда вся необходимая информация уже точно загружена в базу-приемник.
Иногда лучшим решением является не усложнение механизма обмена, а упрощение логики поиска. Хотя это не всегда возможно из-за требований заказчика, этот вариант стоит рассмотреть.
Вместо поиска по сложному составному ключу (например, номеру телефона из табличной части) можно:
Код или специально созданный реквизит).В этом случае заработают типовые механизмы платформы. Даже если документ загрузится раньше справочника, система создаст битую ссылку, которую затем корректно обработает после загрузки справочника. Это избавляет от необходимости писать сложный код отложенной обработки.
В итоге, хотя в универсальном формате обмена нет прямого аналога "приоритетов загрузки", платформа 1С предоставляет гибкие и надежные механизмы, такие как отложенная обработка, для решения подобных нетривиальных задач. Этот подход является более масштабируемым и правильным по сравнению с попытками манипулировать порядком объектов в файле выгрузки.