Как в 1С:Конвертация данных 2.1 не перезаписывать заполненное свойство пустым значением?

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

При настройке обмена данными между конфигурациями 1С с помощью инструмента 1С:Конвертация данных 2.1 (КД2) разработчики часто сталкиваются с классической задачей: как обновить объект в базе-приемнике, но при этом гарантировать, что важные заполненные реквизиты (например, телефон контрагента, ИНН, внутренний код) не будут затерты пустыми значениями, если в базе-источнике эти поля по какой-то причине оказались не заполнены.

Поведение КД2 по умолчанию абсолютно прямолинейно — оно перезаписывает все сопоставленные свойства объекта. Если из источника пришло пустое значение, приемник послушно очистит соответствующий реквизит у себя. В этой статье мы подробно проанализируем различные подходы к решению этой проблемы, начиная от простых программных обработчиков в приемнике и заканчивая максимально производительными методами оптимизации обмена на стороне источника.

Почему стандартная настройка КД2 не всегда подходит?

Давайте сначала разберем интерактивные возможности настройки правил конвертации свойств (ПКС). В КД2 действительно существует флаг «Не замещать значение свойства у существующих объектов в приёмнике». Но решает ли он нашу задачу?

Выясним причину, почему эта галка нам не подходит. Данный флаг работает безусловно: если объект в приемнике уже существует, то КД2 вообще никогда не будет обновлять этот реквизит, даже если из источника пришло новое, актуальное и заполненное значение. Наша же задача гораздо тоньше: мы хотим обновлять реквизит, если в источнике появилось новое значение, но не хотим очищать его, если в источнике реквизит пустой.

Поэтому стандартная галка в ПКС здесь бессильна, и нам придется прибегнуть к написанию несложного программного кода.

Способ №1. Самый эффективный: Отказ от выгрузки свойства на стороне источника

Рассмотрим наиболее производительный и правильный с точки зрения архитектуры способ. Вместо того чтобы обрабатывать пустые значения в базе-приемнике, мы можем вообще не выгружать пустое свойство из базы-источника.

Для реализации этого подхода откроем настройку нужного правила конвертации свойств (ПКС) и перейдем в обработчик события Перед выгрузкой. Напишем там следующий код:


Если НЕ ЗначениеЗаполнено(Источник.ИмяРеквизита) Тогда
    Отказ = Истина;
КонецЕсли;

Проанализируем ситуацию и разберем по шагам, почему этот простой код работает так эффективно:

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

Преимущества этого метода колоссальны. Во-первых, мы уменьшаем размер XML-файла выгрузки. Во-вторых, мы полностью избавляем базу-приемник от необходимости выполнять дополнительные ресурсоемкие обращения к СУБД для проверки старого состояния объекта.

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

Иногда логику обмена требуется реализовать исключительно на стороне приемника (например, когда правила выгрузки из источника закрыты для редактирования или схема обмена жестко зафиксирована). В этом случае мы можем восстановить старое значение непосредственно в процессе загрузки объекта.

Посмотрим на пример реализации. В правиле конвертации объектов (ПКО) для нашего объекта перейдем в обработчик события После загрузки. Напишем код восстановления значения:


Если ОбъектНайден Тогда
    Если ЗначениеЗаполнено(Ссылка.НашРеквизит) И НЕ ЗначениеЗаполнено(Объект.НашРеквизит) Тогда
        Объект.НашРеквизит = Ссылка.НашРеквизит;
    КонецЕсли;
КонецЕсли;

Разберем, как работает эта конструкция:

Важное предупреждение о производительности! При использовании обращения через точку к свойствам ссылки (Ссылка.НашРеквизит) платформа 1С выполняет неявный SQL-запрос к базе данных для получения этого значения. Если вы переносите десятки тысяч объектов за один сеанс обмена, подобные обращения могут критически замедлить загрузку (проблема «N+1 запросов»).

Если вам критически важно использовать именно этот способ на больших объемах данных, рекомендуется предварительно кэшировать старые значения реквизитов с помощью пакетного запроса в обработчике При загрузке или оптимизировать чтение через функцию ОбщегоНазначения.ЗначениеРеквизитаОбъекта(), если она доступна в вашей конфигурации.

Способ №3. Особенности работы с табличными частями

Описанные выше способы отлично подходят для реквизитов шапки документов или справочников. Но что делать, если речь идет о реквизитах внутри строк табличных частей (ТЧ)?

Выясним техническую особенность работы КД2 с табличными частями. При загрузке объекта табличные части по умолчанию очищаются полностью и заполняются заново на основании пришедших данных. Способ с Отказ = Истина в ПКС строки ТЧ здесь не сработает должным образом, так как строка будет перезаписана целиком, и пропущенная колонка просто окажется пустой.

Чтобы обойти это ограничение, нам придется временно сохранить («запомнить») старое состояние табличной части базы-приемника. Рассмотрим пошаговый алгоритм решения:

Шаг 1. В обработчике ПКО При загрузке прочитаем старую табличную часть из базы данных и сохраним ее во временную структуру параметров объекта:


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

Шаг 2. В обработчике ПКО После загрузки сопоставим новые строки табличной части со старыми сохраненными строками (например, по ключевому полю Номенклатура) и восстановим пустые колонки:


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

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

Почему не стоит использовать радикальный флаг «НеЗамещатьОбъект»?

В попытках быстро решить проблему затирания данных некоторые разработчики прибегают к использованию флага НеЗамещатьОбъект = Истина в обработчике При загрузке.

Проанализируем риски такого решения. Этот флаг полностью отменяет запись любых изменений в найденный объект. Если объект уже существует в базе, обмен просто проигнорирует его обновление. Помимо того, что новые полезные изменения также не попадут в базу, у этого флага есть неприятная техническая особенность в алгоритмах КД2.

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

Резюме и рекомендации

Подведем итоги нашего исследования:

  1. Если у вас есть доступ к редактированию правил на стороне источника, всегда выбирайте Способ №1 (программный отказ в ПКС Перед выгрузкой). Он экономит трафик, снижает нагрузку на СУБД приемника и является самым чистым с точки зрения кода.
  2. Если правила можно менять только в приемнике, используйте Способ №2 (программное восстановление в После загрузки), но помните о производительности и старайтесь избегать множественных неявных запросов к СУБД на больших базах.
  3. Для работы со сложными структурами и табличными частями применяйте кеширование через временную таблицу значений в событии При загрузке с последующим сопоставлением.
← На главную