Как добавить новые строки в табличную часть существующего документа в Конвертации Данных, не затирая ручные изменения?

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

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

В рамках этой статьи мы подробно разберем несколько эффективных способов решения этой задачи с использованием конфигурации «Конвертация данных 2.1» (КД 2.1) и механизмов платформы «1С:Предприятие». Мы проанализируем различные подходы — от простых и быстрых до архитектурно правильных и надежных.

Постановка технической задачи

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

  1. Существующие строки табличной части в приемнике не изменялись и не затирались данными из источника.
  2. Новые строки, появившиеся в источнике, успешно добавились в конец табличной части документа в приемнике.
  3. Не произошло дублирования строк при последующих итерациях обмена.

Рассмотрим подробнее доступные методы реализации данной логики.

Решение 1. Передача табличной части источника через параметры (Наиболее изящный способ)

Стандартный механизм работы Правил Конвертации Группы Свойств (ПКГС) для табличных частей по умолчанию очищает коллекцию в приемнике перед загрузкой новых данных. Чтобы обойти это поведение, мы можем полностью отказаться от стандартной выгрузки табличной части и передать данные источника как Параметр объекта.

Разберем по шагам этот алгоритм:

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

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

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

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

Решение 2. Восстановление ручных изменений из ссылки (Метод слияния «на лету»)

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

Проанализируем этот сценарий в обработчике события После загрузки:


// Обработчик "После загрузки" ПКО
// Ссылка указывает на версию объекта, которая в данный момент еще записана в БД приемника.
// Объект содержит новые данные, пришедшие из файла обмена.

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

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

Решение 3. Использование встроенных переменных КД «НеОчищать» и «НеЗамещать»

Малоизвестный, но крайне полезный функционал КД 2.1 кроется в предопределенных переменных, которые можно использовать в обработчиках событий ПКГС (Правил конвертации группы свойств).

Если мы зайдем в настройки правил конвертации свойств для нашей табличной части, в обработчике Перед загрузкой мы можем определить специальную переменную НеОчищать:


// Обработчик "Перед загрузкой" ПКГС табличной части
НеОчищать = Истина;

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

Чтобы избежать дублирования, этот метод необходимо сочетать со сверткой или программным удалением дублей в обработчике После загрузки ПКО документа:


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

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

Критически важный нюанс: Синхронизация строк по логическому бизнес-ключу

В Сообщении 9 участник форума предлагает сравнивать строки по их физическому номеру (индексу строки). Выясним причину, почему это крайне опасный и некорректный подход.

В процессе работы в базе-приемнике пользователь может совершить следующие действия:

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

Правильное решение — использование логического бизнес-ключа строки. В качестве такого ключа может выступать:

  1. Простая аналитика: Номенклатура (если в документе не может быть двух строк с одинаковым товаром).
  2. Составной ключ: Номенклатура + Характеристика + Серия.
  3. Специальный уникальный идентификатор строки (GUID): для этого в табличную часть документа в обеих базах добавляется строковый реквизит (например, КлючСтроки), который заполняется уникальным значением при генерации строки в источнике и переносится в приемник. Это самый надежный способ, гарантирующий 100% точность сопоставления.

Рассмотрим пример реализации сопоставления по составному бизнес-ключу в обработчике После загрузки:


// Формируем таблицу соответствия на основе составного ключа в приемнике
СоответствиеПриемника = Новый Соответствие;
Для Каждого СтрокаПриемника Из Ссылка.Товары Цикл
    
    // Формируем уникальный строковый ключ для каждой строки приемника
    КлючСтроки = Строка(СтрокаПриемника.Номенклатура.УникальныйИдентификатор());
    Если ЗначениеЗаполнено(СтрокаПриемника.Характеристика) Тогда
        КлючСтроки = КлючСтроки + "_" + Строка(СтрокаПриемника.Характеристика.УникальныйИдентификатор());
    КонецЕсли;
    
    СоответствиеПриемника.Вставить(КлючСтроки, СтрокаПриемника);
КонецЦикла;

Методологические рекомендации и архитектурный подход

Как справедливо заметили участники обсуждения в сообщениях 7, 11 и 12, сама необходимость такой настройки часто свидетельствует о проблемах в архитектуре бизнес-процессов компании. Постоянные ручные корректировки документов в базе-приемнике без обратной синхронизации рано или поздно приведут к расхождению взаиморасчетов и остатков между базами.

Для повышения надежности системы рекомендуется рассмотреть следующие улучшения:

Выбрав один из предложенных программных методов (передачу через параметры или сопоставление по логическому ключу в обработчике «После загрузки»), вы сможете гибко настроить обмен данными под специфические нужды вашего бизнеса, минимизировав риски потери пользовательских данных.

← На главную