Как настроить поиск ссылки в приемнике без создания новых объектов в Конвертации данных 2.0 (2.1)?

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия IT и автоматизация бизнеса
← На главную

Разберем классическую и очень востребованную задачу, с которой сталкиваются разработчики при настройке интеграции между конфигурациями 1С с помощью программного продукта «1С:Конвертация данных 2.1». Представим ситуацию: мы настраиваем обмен между базами (например, между «1С:Бухгалтерией» и «БИТ.ФИНАНС»). Нам необходимо перегрузить справочник БанковскиеСчета, который содержит реквизит, ссылающийся на справочник Договоры.

Однако бизнес-логика требует, чтобы договоры определенных типов вообще не выгружались из источника как новые объекты и не изменялись в базе-приемнике. Вместо этого программа должна лишь попытаться найти существующий договор в приемнике по уникальному идентификатору (GUID) или коду и подставить его в поле банковского счета. Если же договор не найден, создавать его не нужно. В процессе реализации этой логики разработчики часто наступают на типичные «грабли»: получают ошибки компиляции, путают обработчики загрузки или сталкиваются с появлением «битых» ссылок. Выясним причины этих проблем и рассмотрим проверенные архитектурные варианты решения задачи.

Способ 1. Динамическое переопределение ПКО на лету

Этот подход является наиболее изящным и правильным с точки зрения архитектуры «Конвертации данных». Мы разделим логику обработки договоров на два независимых Правила Конвертации Объектов (ПКО) и будем динамически переключаться между ними в зависимости от свойств выгружаемого объекта.

Разберем по шагам, как это настроить:

  1. Создадим первое ПКО для справочника Договоры с наименованием Договоры_ПолныйПеренос. В нем настроим стандартный перенос всех необходимых реквизитов. Это правило будет использоваться по умолчанию для тех типов договоров, которые должны переноситься полностью.
  2. Создадим второе ПКО для справочника Договоры с наименованием Договоры_ТолькоПоиск. В настройках этого правила обязательно установим флаги: «Не замещать существующие объекты в приемнике» и «Не создавать новый объект в приемнике, если он НЕ найден». В ПКС (Правилах конвертации свойств) для этого ПКО мы настроим только ключевые свойства поиска (например, УникальныйИдентификатор или Код).
  3. Перейдем в ПКО справочника БанковскиеСчета. Найдем ПКС для свойства ДоговорКонтрагента (или аналогичного реквизита, хранящего ссылку на договор).
  4. В обработчике события «Перед выгрузкой» этого ПКС напишем алгоритм, определяющий тип договора и динамически подставляющий нужное правило конвертации:

Если Источник.Договор.ТипДоговора = Перечисления.ТипыДоговоров.СПокупателем Тогда
    ИмяПКО = "Договоры_ПолныйПеренос";
Иначе
    ИмяПКО = "Договоры_ТолькоПоиск";
КонецЕсли;

Проанализируем этот код. Переменная ИмяПКО является предопределенной в данном обработчике. Указывая в ней имя конкретного правила, мы заставляем платформу выгружать связанный объект по индивидуальной схеме. Если договор относится к типу, который не нужно перезаписывать или создавать, сработает правило Договоры_ТолькоПоиск. В XML-файл запишется только базовая информация для поиска, и при загрузке объект либо найдется и подставится, либо проигнорируется.

Способ 2. Передача параметров через ПКС и обработка «После загрузки»

Иногда разработчики пытаются решить задачу «в лоб»: передать реквизиты объекта во временные параметры и обработать их при загрузке. Но здесь их часто подстерегает ошибка компиляции вида Получение элемента по индексу для значения не определено или Поле объекта не обнаружено при обращении к параметрам в обработчике ПриЗагрузкеОбъекта.

Выясним причину этой ошибки:

Чтобы решить эту проблему правильно, выполним следующие действия:

  1. В ПКО для договоров добавим новое Правило конвертации свойства (ПКС). В качестве источника укажем реквизит типа договора (например, бит_ТипДоговора), а поле приемника оставим пустым.
  2. В настройках этого ПКС установим флаг «Передавать данные в параметр». В поле «Имя параметра» укажем, например, ТипДоговораДляПоиска.
  3. Перенесем весь код обработки параметров из события При загрузке в событие «После загрузки» ПКО. На этом этапе XML-данные уже полностью прочитаны, и структура ПараметрыОбъекта доступна для полноценной работы.
  4. В обработчике «После загрузки» ПКО напишем код, который предотвратит перезапись реквизитов, если тип договора совпадает с невыгружаемым:

ТипДоговора = "";
Если ПараметрыОбъекта <> Неопределено Тогда
    ПараметрыОбъекта.Свойство("ТипДоговораДляПоиска", -ТипДоговора);
КонецЕсли;

Если ТипДоговора = "НеВыгружаемыйТип" Тогда
    // Если объект уже существует в базе, откатим изменения реквизитов к исходным значениям из базы
    Если Не Объект.ЭтоНовый() Тогда
        // Читаем старое состояние объекта напрямую из базы данных
        СтарыйОбъект = Объект.Ссылка.ПолучитьОбъект();
        Если СтарыйОбъект <> Неопределено Тогда
            ЗаполнитьЗначенияСвойств(Объект, СтарыйОбъект);
        КонецЕсли;
    КонецЕсли;
КонецЕсли;

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

Способ 3. Предотвращение появления «битых» ссылок

Рассмотрим ситуацию: мы использовали Способ 1 с флагом «Не создавать новый объект в приемнике, если он не найден». Что произойдет, если в базе-приемнике действительно нет такого договора?

Платформа успешно считает GUID договора из файла обмена, попытается найти объект по этому GUID, потерпит неудачу и откажется создавать новый элемент справочника Договоры. Однако при записи родительского объекта (банковского счета) в базу запишется пустая «битая» ссылка с этим GUID. В интерфейсе пользователя это отобразится в виде некрасивой надписи <Объект не найден>....

Чтобы избежать захламления базы недействительными ссылками, выполним программную очистку в ПКО родительского объекта. В обработчике события «После загрузки» ПКО БанковскиеСчета напишем следующий алгоритм проверки:


Если ЗначениеЗаполнено(Объект.ДоговорКонтрагента) Тогда
    // Попытка получить реальный объект из базы данных по ссылке. 
    // Если ссылка "битая", метод ПолучитьОбъект() вернет Неопределено.
    Если Объект.ДоговорКонтрагента.ПолучитьОбъект() = Неопределено Тогда
        // Очищаем битую ссылку, записывая пустую ссылку справочника
        Объект.ДоговорКонтрагента = Справочники.ДоговорыКонтрагентов.ПустаяСсылка();
    КонецЕсли;
КонецЕсли;

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

Способ 4. Оптимизация выгрузки с помощью свойства ТолькоПолучитьУзелСсылки

Если мы точно знаем, что для определенных типов договоров нам не нужно передавать в XML-файл никаких их реквизитов (даже полей поиска), а достаточно выгрузить исключительно GUID для сопоставления, мы можем существенно ускорить процесс выгрузки и сократить размер файла обмена.

Для этого воспользуемся встроенным флагом оптимизации ТолькоПолучитьУзелСсылки. В ПКО БанковскиеСчета найдем ПКС для свойства ДоговорКонтрагента и в обработчике «Перед выгрузкой» пропишем следующий код:


Если Источник.ДоговорКонтрагента.ТипДоговора = Перечисления.ТипыДоговоров.НеВыгружаемый Тогда
    ТолькоПолучитьУзелСсылки = Истина;
КонецЕсли;

При установке флага ТолькоПолучитьУзелСсылки = Истина конвертация пропустит детальный обход связанных реквизитов договора и запишет в файл обмена только базовую ссылку (GUID). Это минимизирует нагрузку на оперативную память сервера при выполнении обмена.

Способ 5. Альтернативный вариант через обработчик «Поля поиска» в приемнике

Выясним, как решить задачу без создания дополнительных ПКО. Мы можем перенести всю логику фильтрации и принятия решений о создании объектов непосредственно на сторону базы-приемника. Для этого используется обработчик «Поля поиска» в ПКО Договоры.

В этом обработчике нам доступны полезные переменные управления: СвойстваПоиска (структура свойств поиска), ПрекратитьПоиск и Импортировать. Предположим, мы передали тип договора в параметрах объекта (как описано в Способе 2). В обработчике «Поля поиска» ПКО Договоры мы пишем следующий код:


ТипДоговора = "";
Если ПараметрыОбъекта <> Неопределено Тогда
    ПараметрыОбъекта.Свойство("ТипДоговораДляПоиска", ТипДоговора);
КонецЕсли;

Если ТипДоговора = "НеСоздавать" Тогда
    // Пытаемся вручную найти объект по коду (или другому ключевому полю)
    КодДоговора = СвойстваПоиска["Код"];
    СсылкаНаОбъект = Справочники.ДоговорыКонтрагентов.НайтиПоКоду(КодДоговора);
    
    Если Не ЗначениеЗаполнено(СсылкаНаОбъект) Тогда
        // Если объект не найден в приемнике, отменяем стандартный механизм поиска
        ПрекратитьПоиск = Истина;
        // Запрещаем создавать новый объект и импортировать его из файла
        Импортировать = Ложь;
    КонецЕсли;
КонецЕсли;

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

← На главную