Как программно скопировать и размножить строки в табличной части 1С

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

В процессе разработки часто возникает задача, когда необходимо пересмотреть состав табличной части документа или обработки на основании данных из другого источника. Типичный пример: у нас есть основная таблица, и если одной строке в ней соответствует несколько записей в другой таблице, нам нужно «размножить» исходную строку столько раз, сколько соответствий найдено, и заполнить специфические поля. Разберем подробно, как это реализовать правильно, избегая типичных ошибок производительности и логики. Если такое сопоставление и заполнение табличных частей необходимо выполнить в рамках миграции между различными базами со схожей структурой, применяется перенос данных между похожими конфигурациями 1С с возможностью поиска объектов по произвольным реквизитам.

Анализ основной проблемы пользователя

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

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

Способ 1. Использование циклов и метода ЗаполнитьЗначенияСвойств

Этот метод наиболее понятен и подходит для работы с небольшими объемами данных (до нескольких сотен строк). Разберем по шагам алгоритм действий:

  1. Выгружаем вспомогательную таблицу (источник соответствий) в ТаблицаЗначений.
  2. Для повышения скорости поиска рекомендуется проиндексировать вспомогательную таблицу по полю связи (например, Номенклатура).
  3. Обходим основную табличную часть. Для каждой строки ищем соответствия во вспомогательной таблице.
  4. Если соответствий больше одного, добавляем новые строки в основную ТЧ.

Рассмотрим пример кода, который исправляет ошибку автора:


// Предположим, ТЧ_Копия - это наша эталонная таблица
ТЗ_Копия = Объект.Товары_копия.Выгрузить();
ТЗ_Копия.Индексы.Добавить("Номенклатура");

// Чтобы не зациклиться при добавлении строк в ту же ТЧ, 
// сначала сформируем массив строк или выгрузим ТЧ
ТЗ_Основная = Объект.Товары.Выгрузить();
Объект.Товары.Очистить();

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

Способ 2. Использование механизмов Запроса (Рекомендуемый)

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

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

Разберем алгоритм реализации через временные таблицы:


МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Передаем ТЧ в запрос
Запрос.Текст = 
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Основная ИЗ &ТЗ_Основная КАК ТЗ_Основная;
|ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Копия ИЗ &ТЗ_Копия КАК ТЗ_Копия;
|
|ВЫБРАТЬ
|   ВТ_Основная.Номенклатура,
|   ВТ_Основная.Цена,
|   ВТ_Копия.ВидТовара КАК ВидТовара
|ИЗ
|   ВТ_Основная КАК ВТ_Основная
|       ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Копия КАК ВТ_Копия
|       ПО ВТ_Основная.Номенклатура = ВТ_Копия.Номенклатура";

Запрос.УстановитьПараметр("ТЗ_Основная", Объект.Товары.Выгрузить());
Запрос.УстановитьПараметр("ТЗ_Копия", Объект.Товары_копия.Выгрузить());

Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
    Объект.Товары.Загрузить(Результат.Выгрузить());
КонецЕсли;

Преимущества этого метода:

Способ 3. Копирование строк через Таблицу Значений

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

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


// Выгружаем ТЧ во временную таблицу для манипуляций
ТЗ_Временная = Объект.Товары.Выгрузить();

// Допустим, нам нужно продублировать только товары с определенной ценой
Отбор = Новый Структура("Цена", 100);
ТЗ_ТолькоНужные = ТЗ_Временная.Скопировать(Отбор);

// Теперь добавим эти строки к основной ТЧ
Для Каждого Стр Из ТЗ_ТолькоНужные Цикл
    НоваяСтрока = Объект.Товары.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, Стр);
КонецЦикла;

Особенности работы на Управляемых Формах

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

При работе с управляемыми формами придерживайтесь следующих правил:

  1. Все операции по массовому изменению строк переносите в процедуру &НаСервере.
  2. Если вы используете метод Загрузить(), помните, что он полностью замещает содержимое коллекции. Если нужно именно «добавить в хвост», используйте цикл или предварительное объединение таблиц значений на сервере.
  3. Для визуального комфорта пользователя, если строк очень много, используйте ЭтаФорма.ОбновитьОтображениеДанных() после завершения цикла, чтобы интерфейс не «подвисал» в процессе отрисовки каждой новой строки.

Рекомендации по оптимизации

Выясним, на что обратить внимание, чтобы код работал стабильно: 1. Проверка на пустые значения. Перед сопоставлением полей (например, по номенклатуре) убедитесь, что ключевые поля заполнены, иначе ЛЕВОЕ СОЕДИНЕНИЕ или НайтиСтроки могут выдать непредсказуемый результат.

2. Типы данных. Метод ЗаполнитьЗначенияСвойств чувствителен к именам колонок. Если в исходной ТЧ колонка называется Товар, а в новой — Номенклатура, автоматического копирования не произойдет. В таких случаях используйте третий параметр метода — список сопоставления или заполняйте поле вручную.

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

Подводя итог, можно сказать, что выбор метода зависит от сложности задачи. Для простого дублирования нескольких строк достаточно цикла с ЗаполнитьЗначенияСвойств, но для сложной трансформации данных и «размножения» строк по условиям — использование запроса является золотым стандартом разработки в среде 1С.

← На главную