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

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

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

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

Причина поведения платформы при загрузке

Выясним причину, почему нельзя просто отключить перенос остальных колонок в правилах конвертации. Механизм десериализации XML или формата EnterpriseData обрабатывает коллекции целиком:

  1. При получении данных объекта табличная часть очищается.
  2. Создаются новые строки на основании переданных в сообщении обмена данных.
  3. Все поля, которые отсутствовали в пакете загрузки, принимают значения по умолчанию (0 для чисел, пустые строки, пустые ссылки).

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

Решение 1. Восстановление данных через обработчик «После загрузки»

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

Посмотрим на пример программного кода:


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

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

Решение 2. Оптимизированный перенос с предварительным сохранением в «Перед загрузкой»

Проанализируем проблему производительности предыдущего способа: многократное обращение к Объект.Ссылка.Товары внутри цикла порождает множество одиночных запросов к базе данных, что существенно замедляет загрузку больших пакетов документов. Чтобы ускорить процесс, разберем по шагам более эффективный подход через структуру ПараметрыОбъекта.

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

Решение 3. Точное сопоставление по ключу строки вместо индекса

Ориентация только на номер или индекс строки (Индекс) несет высокий риск искажения данных. Если пользователи отсортировали строки в документе, удалили промежуточную строку или добавили новую, сопоставление по позиции приведет к тому, что реквизиты будут перепутаны между разными строками.

Разберем надежный подход, при котором строки связываются по уникальному ключу:

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

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

Альтернативные архитектурные решения

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

  1. Вынос данных в независимый регистр сведений: Значение колонки хранится в непериодическом регистре сведений с измерениями Документ и ИдентификаторСтроки. На форме документа колонка отображается динамически, а при обмене передаются только записи регистра без перезаписи и блокировки самого документа.
  2. Отмена стандартной загрузки (Отказ = Истина): В обработчике загрузки предотвращается полная перезапись объекта правилами обмена, а точечное обновление нужного реквизита выполняется программно через вызов метода объекта с записью в режиме РежимЗаписиДокумента.Запись без повторного проведения.
  3. Использование HTTP-сервисов: Вместо громоздкого обмена объектами на стороне базы-приемника создается HTTP-сервис, принимающий минимальный JSON со связкой идентификаторов документа, строки и нового значения, точечно обновляя только требуемое поле.
← На главную