При настройке обмена данными между конфигурациями 1С с помощью инструмента 1С:Конвертация данных 2.1 (КД2) разработчики часто сталкиваются с классической задачей: как обновить объект в базе-приемнике, но при этом гарантировать, что важные заполненные реквизиты (например, телефон контрагента, ИНН, внутренний код) не будут затерты пустыми значениями, если в базе-источнике эти поля по какой-то причине оказались не заполнены. Для регулярного выполнения синхронизации по готовым правилам можно автоматизировать обмен данными между базами 1С по расписанию.
Поведение КД2 по умолчанию абсолютно прямолинейно — оно перезаписывает все сопоставленные свойства объекта. Если из источника пришло пустое значение, приемник послушно очистит соответствующий реквизит у себя. В этой статье мы подробно проанализируем различные подходы к решению этой проблемы, начиная от простых программных обработчиков в приемнике и заканчивая максимально производительными методами оптимизации обмена на стороне источника.
Давайте сначала разберем интерактивные возможности настройки правил конвертации свойств (ПКС). В КД2 действительно существует флаг «Не замещать значение свойства у существующих объектов в приёмнике». Но решает ли он нашу задачу?
Выясним причину, почему эта галка нам не подходит. Данный флаг работает безусловно: если объект в приемнике уже существует, то КД2 вообще никогда не будет обновлять этот реквизит, даже если из источника пришло новое, актуальное и заполненное значение. Наша же задача гораздо тоньше: мы хотим обновлять реквизит, если в источнике появилось новое значение, но не хотим очищать его, если в источнике реквизит пустой.
Поэтому стандартная галка в ПКС здесь бессильна, и нам придется прибегнуть к написанию несложного программного кода. Для облегчения процесса написания алгоритмов и быстрого доступа к параметрам событий можно использовать модифицированную конфигурацию «Конвертация данных» с подсказками по обработчикам.
Рассмотрим наиболее производительный и правильный с точки зрения архитектуры способ. Вместо того чтобы обрабатывать пустые значения в базе-приемнике, мы можем вообще не выгружать пустое свойство из базы-источника.
Для реализации этого подхода откроем настройку нужного правила конвертации свойств (ПКС) и перейдем в обработчик события Перед выгрузкой. Напишем там следующий код:
Если НЕ ЗначениеЗаполнено(Источник.ИмяРеквизита) Тогда
Отказ = Истина;
КонецЕсли;
Проанализируем ситуацию и разберем по шагам, почему этот простой код работает так эффективно:
Отказ = Истина.Преимущества этого метода колоссальны. Во-первых, мы уменьшаем размер XML-файла выгрузки. Во-вторых, мы полностью избавляем базу-приемник от необходимости выполнять дополнительные ресурсоемкие обращения к СУБД для проверки старого состояния объекта. Для контроля состава выгруженных данных и проверки структуры XML-файла перед его загрузкой можно использовать обработку для визуального анализа файлов обмена.
Иногда логику обмена требуется реализовать исключительно на стороне приемника (например, когда правила выгрузки из источника закрыты для редактирования или схема обмена жестко зафиксирована). В этом случае мы можем восстановить старое значение непосредственно в процессе загрузки объекта. Если объекты в разных базах имеют несовпадающие внутренние идентификаторы, для сопоставления можно использовать перенос данных между похожими конфигурациями 1С.
Посмотрим на пример реализации. В правиле конвертации объектов (ПКО) для нашего объекта перейдем в обработчик события После загрузки. Напишем код восстановления значения:
Если ОбъектНайден Тогда
Если ЗначениеЗаполнено(Ссылка.НашРеквизит) И НЕ ЗначениеЗаполнено(Объект.НашРеквизит) Тогда
Объект.НашРеквизит = Ссылка.НашРеквизит;
КонецЕсли;
КонецЕсли;
Разберем, как работает эта конструкция:
ОбъектНайден — это булевый флаг КД2, который сообщает, существовал ли загружаемый объект в базе-приемнике до начала текущего сеанса обмена. Если объект создается впервые, восстанавливать нам нечего, и код внутри условия не выполняется.Объект содержит воссозданный в оперативной памяти объект (еще не записанный в базу данных), свойства которого заполнены значениями из XML-файла.Ссылка представляет собой ссылку на оригинальный элемент, хранящийся непосредственно в базе данных приемника.ЗначениеЗаполнено(Ссылка.НашРеквизит)), а в загруженном объекте оно пустое (НЕ ЗначениеЗаполнено(Объект.НашРеквизит)), то мы принудительно возвращаем старому объекту его исходное значение.Важное предупреждение о производительности! При использовании обращения через точку к свойствам ссылки (Ссылка.НашРеквизит) платформа 1С выполняет неявный SQL-запрос к базе данных для получения этого значения. Если вы переносите десятки тысяч объектов за один сеанс обмена, подобные обращения могут критически замедлить загрузку (проблема «N+1 запросов»).
Если вам критически важно использовать именно этот способ на больших объемах данных, рекомендуется предварительно кэшировать старые значения реквизитов с помощью пакетного запроса в обработчике При загрузке или оптимизировать чтение через функцию ОбщегоНазначения.ЗначениеРеквизитаОбъекта(), если она доступна в вашей конфигурации.
Описанные выше способы отлично подходят для реквизитов шапки документов или справочников. Но что делать, если речь идет о реквизитах внутри строк табличных частей (ТЧ)?
Выясним техническую особенность работы КД2 с табличными частями. При загрузке объекта табличные части по умолчанию очищаются полностью и заполняются заново на основании пришедших данных. Способ с Отказ = Истина в ПКС строки ТЧ здесь не сработает должным образом, так как строка будет перезаписана целиком, и пропущенная колонка просто окажется пустой.
Чтобы обойти это ограничение, нам придется временно сохранить («запомнить») старое состояние табличной части базы-приемника. Рассмотрим пошаговый алгоритм решения:
Шаг 1. В обработчике ПКО При загрузке прочитаем старую табличную часть из базы данных и сохраним ее во временную структуру параметров объекта:
Если ОбъектНайден Тогда
// Сохраняем исходную ТЧ в структуру параметров загружаемого объекта
Параметры.Вставить("СтараяТабличнаяЧасть", Ссылка.Товары.Выгрузить());
КонецЕсли;
Шаг 2. В обработчике ПКО После загрузки сопоставим новые строки табличной части со старыми сохраненными строками (например, по ключевому полю Номенклатура) и восстановим пустые колонки:
Если ОбъектНайден Тогда
ТаблицаСтарыхДанных = Неопределено;
Если Параметры.Свойство("СтараяТабличнаяЧасть", ТаблицаСтарыхДанных) Тогда
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
// Ищем строку по ключевому полю в старых данных
ПараметрыОтбора = Новый Структура("Номенклатура", СтрокаТЧ.Номенклатура);
НайденныеСтроки = ТаблицаСтарыхДанных.НайтиСтроки(ПараметрыОтбора);
Если НайденныеСтроки.Количество() > 0 Тогда
СтараяСтрока = НайденныеСтроки[0];
// Если новое значение реквизита в строке пустое, возвращаем старое
Если НЕ ЗначениеЗаполнено(СтрокаТЧ.Спецификация) И ЗначениеЗаполнено(СтараяСтрока.Спецификация) Тогда
СтрокаТЧ.Спецификация = СтараяСтрока.Спецификация;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
Этот изящный алгоритм позволяет точечно управлять заполнением реквизитов даже внутри сложных табличных частей, сохраняя целостность исторических данных в базе-приемнике. Для контроля результата и поиска расхождений после настройки правил обмена полезно автоматически сравнить бухгалтерские и налоговые проводки между базами 1С.
В попытках быстро решить проблему затирания данных некоторые разработчики прибегают к использованию флага НеЗамещатьОбъект = Истина в обработчике При загрузке.
Проанализируем риски такого решения. Этот флаг полностью отменяет запись любых изменений в найденный объект. Если объект уже существует в базе, обмен просто проигнорирует его обновление. Помимо того, что новые полезные изменения также не попадут в базу, у этого флага есть неприятная техническая особенность в алгоритмах КД2.
Если в XML-файле один и тот же объект сначала выгружается со всеми реквизитами, а затем в других узлах на него просто передается ссылка по идентификатору (например, в качестве реквизита другого документа), то взведенный флаг НеЗамещатьОбъект у первого объекта может привести к неожиданной очистке ссылок на него в последующих загружаемых документах. Происходит это из-за особенностей работы механизма сопоставления объектов КД2 «по ссылкам». Поэтому всегда безопаснее и надежнее использовать точечное управление свойствами, которое мы подробно разобрали выше.
Подведем итоги нашего исследования: