При работе с системой 1С:Предприятие 8.3 программисты часто сталкиваются с задачей заполнения табличной части документа данными, которые были предварительно получены из запроса и выгружены в таблицу значений. Эта операция является одной из базовых и ключевых для автоматизации бизнес-процессов.
Рассмотрим типовую ситуацию, с которой столкнулся автор вопроса. У нас есть запрос, который формирует таблицу значений (далее ТЗ) с необходимыми данными, например, о маршрутах, водителях, автомобилях, пробеге и других показателях. Задача состоит в том, чтобы перенести эти данные в табличную часть некоторого документа. Автор в своем коде попытался сделать это путем перебора строк табличной части документа и присваивания им значений из колонок всей ТЗ, что является фундаментальной ошибкой, так как переменная Т в его примере представляет всю таблицу значений, а не отдельную ее строку. Например, запись Стр.Маршрут = Т.Маршрут; некорректна, поскольку Т.Маршрут — это колонка таблицы значений, а не значение для текущей строки.
// Некорректный код автора
Для Каждого Стр Из Маршруты Цикл
Стр.Маршрут = Т.Маршрут; // Ошибка: Т.Маршрут - это колонка, а не значение текущей строки ТЗ
Стр.Машины = Т.Автомобиль;
// ... и так далее
КонецЦикла;
Чтобы правильно решить эту задачу, мы можем использовать несколько подходов, каждый из которых имеет свои преимущества и области применения. Мы подробно разберем каждый из них.
Одним из наиболее простых, производительных и распространенных способов заполнения табличной части документа является использование метода Загрузить(). Этот метод позволяет загрузить все строки или часть строк из другой таблицы значений или массива структур напрямую в табличную часть документа. Он значительно сокращает объем кода и повышает производительность, особенно при большом количестве строк.
Для успешного использования метода Загрузить() необходимо, чтобы имена колонок в вашей исходной таблице значений (в нашем случае это Т, результат выполнения запроса) совпадали с именами реквизитов табличной части документа. Если имена совпадают, задача решается одной строкой кода.
Очистить().Загрузить() для табличной части, передавая ему нашу таблицу значений.Давайте посмотрим на пример:
// Исходный запрос автора
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ТаблицаВодителиВодители.Ссылка,
| ТаблицаВодителиВодители.Ссылка.Дата,
| ТаблицаВодителиВодители.Маршрут КАК Маршрут,
| ТаблицаВодителиВодители.Автомобиль КАК Автомобиль,
| ТаблицаВодителиВодители.Водитель КАК Водитель,
| ЕСТЬNULL(ТаблицаВодителиВодители.ПробегЗаДень, 0) КАК КМ,
| ТаблицаВодителиВодители.ТемператураВХолодильнике КАК Температура,
| ЕСТЬNULL(ТаблицаВодителиВодители.ТопливоГрн, 0) КАК Топливо,
| ЕСТЬNULL(ТаблицаВодителиВодители.СкоростьГрн, 0) КАК Скорость,
| ЕСТЬNULL(ТаблицаВодителиВодители.Штраф, """") КАК Штраф
|ИЗ
| Документ.ТаблицаВодители.Водители КАК ТаблицаВодителиВодители
|ГДЕ
| ТаблицаВодителиВодители.Ссылка = &СсылкаТВ";
Запрос.УстановитьПараметр("СсылкаТВ", СсылкаТВ);
ТаблицаДанныхЗапроса = Запрос.Выполнить().Выгрузить(); // Получаем ТаблицуЗначений
// Предположим, что "Объект" - это наш документ, а "Маршруты" - его табличная часть
// 1. Очищаем табличную часть перед заполнением
Объект.Маршруты.Очистить();
// 2. Загружаем данные из ТаблицыДанныхЗапроса в табличную часть
Объект.Маршруты.Загрузить(ТаблицаДанныхЗапроса);
// Если вам нужно выполнить дополнительные расчеты, как в исходном коде автора,
// это можно сделать после загрузки данных, или еще лучше - в самом запросе.
// Например, пройдемся по уже заполненной табличной части для расчета стоимости
Для Каждого СтрТЧ Из Объект.Маршруты Цикл
// Предполагаем, что ЗарплатаВодителю и ЗарплатаГрузчику уже как-то определены
// или являются реквизитами табличной части
СтрТЧ.Стоимость = СтрТЧ.ЗарплатаВодителю + СтрТЧ.ЗарплатаГрузчику + СтрТЧ.Скорость + СтрТЧ.Топливо;
КонецЦикла;
Особенности использования метода Загрузить() с соответствием колонок:
Если имена колонок в вашей таблице значений отличаются от имен реквизитов табличной части документа, вы можете использовать перегруженный вариант метода Загрузить(), который принимает параметр СоответствиеКолонок. Этот параметр представляет собой структуру, где ключ структуры — это имя колонки в исходной таблице значений, а значение структуры — это имя реквизита в табличной части документа.
Предположим, в запросе колонка называется МаршрутЗаДень, а в табличной части реквизит — МаршрутДоставки. Тогда соответствие будет выглядеть так:
// Создаем структуру соответствия колонок
Соответствие = Новый Структура();
Соответствие.Вставить("МаршрутЗаДень", "МаршрутДоставки");
Соответствие.Вставить("Автомобиль", "Машины"); // Если в запросе "Автомобиль", а в ТЧ "Машины"
// Добавляем все необходимые соответствия
Объект.Маршруты.Очистить();
Объект.Маршруты.Загрузить(ТаблицаДанныхЗапроса, Соответствие);
Когда использовать: Этот метод идеален для ситуаций, когда необходимо перенести большой объем данных без сложной дополнительной логики или преобразований для каждой строки. Это самый производительный способ массового заполнения.
Иногда метод Загрузить() не подходит из-за более сложной логики заполнения: например, нам нужно выполнять расчеты, проверять условия, добавлять не все строки из исходной таблицы, или если имена реквизитов не полностью совпадают и удобнее обрабатывать каждую строку индивидуально. В таких случаях мы используем построчный перебор исходной таблицы значений.
Давайте разберем шаги:
ТаблицаДанныхЗапроса).ТаблицаДанныхЗапроса мы добавляем новую пустую строку в табличную часть документа, используя метод Добавить(). Этот метод возвращает ссылку на вновь созданную строку табличной части.ЗаполнитьЗначенияСвойств(): Это очень удобный метод, который копирует значения одноименных свойств из одного объекта в другой. Если имена колонок текущей строки таблицы значений совпадают с именами реквизитов новой строки табличной части, мы можем использовать его.Посмотрим на пример, используя подход с Добавить() и ЗаполнитьЗначенияСвойств():
// Исходный запрос остается тем же
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ТаблицаВодителиВодители.Ссылка,
| ТаблицаВодителиВодители.Ссылка.Дата,
| ТаблицаВодителиВодители.Маршрут КАК Маршрут,
| ТаблицаВодителиВодители.Автомобиль КАК Автомобиль,
| ТаблицаВодителиВодители.Водитель КАК Водитель,
| ЕСТЬNULL(ТаблицаВодителиВодители.ПробегЗаДень, 0) КАК КМ,
| ТаблицаВодителиВодители.ТемператураВХолодильнике КАК Температура,
| ЕСТЬNULL(ТаблицаВодителиВодители.ТопливоГрн, 0) КАК Топливо,
| ЕСТЬNULL(ТаблицаВодителиВодители.СкоростьГрн, 0) КАК Скорость,
| ЕСТЬNULL(ТаблицаВодителиВодители.Штраф, """") КАК Штраф
|ИЗ
| Документ.ТаблицаВодители.Водители КАК ТаблицаВодителиВодители
|ГДЕ
| ТаблицаВодителиВодители.Ссылка = &СсылкаТВ";
Запрос.УстановитьПараметр("СсылкаТВ", СсылкаТВ);
ТаблицаДанныхЗапроса = Запрос.Выполнить().Выгрузить();
// 1. Очищаем табличную часть документа
Объект.Маршруты.Очистить();
// 2. Перебираем строки таблицы значений
Для Каждого СтрокаТЗ Из ТаблицаДанныхЗапроса Цикл
// 3. Добавляем новую строку в табличную часть документа
НоваяСтрокаТЧ = Объект.Маршруты.Добавить();
// 4. Заполняем свойства новой строки с помощью ЗаполнитьЗначенияСвойств()
// Этот метод скопирует все свойства из СтрокаТЗ в НоваяСтрокаТЧ,
// имена которых совпадают.
ЗаполнитьЗначенияСвойств(НоваяСтрокаТЧ, СтрокаТЗ);
// 5. Если есть дополнительные расчеты или реквизиты с отличающимися именами,
// заполняем их вручную.
// Например, в примере автора было: Стр.Машины = Т.Автомобиль;
// Если в ТЗ колонка "Автомобиль", а в ТЧ реквизит "Машины", то делаем так:
// НоваяСтрокаТЧ.Машины = СтрокаТЗ.Автомобиль;
// Расчет поля "Стоимость", как в примере автора
// Предполагаем, что ЗарплатаВодителю и ЗарплатаГрузчику либо есть в ТЗ,
// либо их значения берутся откуда-то еще, или являются реквизитами самой ТЧ.
// Если их нет в ТЗ, то нам нужно их получить для расчета.
// Для примера предположим, что они есть как реквизиты ТЧ и их нужно заполнить
// или они уже заполнены/рассчитаны.
// Если их нет нигде, а нужно рассчитать, то это уже более сложная логика.
// Для простоты примера:
НоваяСтрокаТЧ.Стоимость = 0; // Инициализируем
Если СтрокаТЗ.Свойство("ЗарплатаВодителю") Тогда
НоваяСтрокаТЧ.ЗарплатаВодителю = СтрокаТЗ.ЗарплатаВодителю;
КонецЕсли;
Если СтрокаТЗ.Свойство("ЗарплатаГрузчику") Тогда
НоваяСтрокаТЧ.ЗарплатаГрузчику = СтрокаТЗ.ЗарплатаГрузчику;
КонецЕсли;
// После заполнения всех полей из ТЗ и дополнительных расчетов
НоваяСтрокаТЧ.Стоимость = НоваяСтрокаТЧ.ЗарплатаВодителю + НоваяСтрокаТЧ.ЗарплатаГрузчику
+ НоваяСтрокаТЧ.Скорость + НоваяСтрокаТЧ.Топливо;
КонецЦикла;
Когда использовать: Этот метод дает большую гибкость. Используйте его, если вам нужен детальный контроль над процессом добавления каждой строки, если требуется выполнять вычисления, применять сложную логику фильтрации, или если существует необходимость в адаптации данных перед их записью в табличную часть. Он менее производителен, чем Загрузить() при больших объемах данных, но более гибок.
В некоторых случаях задача может быть не в полном заполнении табличной части с нуля, а в обновлении уже существующих строк или добавлении новых только при их отсутствии. В таком сценарии нам понадобится найти соответствующую строку в исходной таблице значений или, наоборот, в табличной части документа. Для этого удобно использовать метод НайтиСтроки().
Метод НайтиСтроки() доступен для таблиц значений и табличных частей и позволяет найти все строки, которые соответствуют заданным условиям отбора. Условия отбора задаются в виде структуры, где ключ структуры — это имя колонки/реквизита, а значение — искомое значение.
Давайте рассмотрим сценарий: мы хотим обновить уже существующие маршруты в табличной части документа, а если маршрута нет — добавить его. Это более сложная логика, но НайтиСтроки() здесь может помочь.
// Предположим, что ТаблицаДанныхЗапроса уже получена.
// Мы хотим пройтись по данным из запроса и либо обновить существующую строку в ТЧ "Маршруты",
// либо добавить новую.
Для Каждому СтрокаИзЗапроса Из ТаблицаДанныхЗапроса Цикл
// Ищем строку в табличной части документа по ключевому полю, например, "Маршрут" и "Автомобиль"
Отбор = Новый Структура();
Отбор.Вставить("Маршрут", СтрокаИзЗапроса.Маршрут);
Отбор.Вставить("Машины", СтрокаИзЗапроса.Автомобиль); // Предполагаем, что в ТЧ реквизит называется "Машины"
НайденныеСтрокиТЧ = Объект.Маршруты.НайтиСтроки(Отбор);
Если НайденныеСтрокиТЧ.Количество() > 0 Тогда
// Если нашли существующую строку, обновляем ее
СтрокаТЧДляОбновления = НайденныеСтрокиТЧ[0]; // Берем первую найденную строку
ЗаполнитьЗначенияСвойств(СтрокаТЧДляОбновления, СтрокаИзЗапроса);
// Дополнительные расчеты или присвоения
СтрокаТЧДляОбновления.Стоимость = СтрокаТЧДляОбновления.ЗарплатаВодителю + СтрокаТЧДляОбновления.ЗарплатаГрузчику
+ СтрокаТЧДляОбновления.Скорость + СтрокаТЧДляОбновления.Топливо;
Иначе
// Если строка не найдена, добавляем новую
НоваяСтрокаТЧ = Объект.Маршруты.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрокаТЧ, СтрокаИзЗапроса);
// Дополнительные расчеты или присвоения для новой строки
НоваяСтрокаТЧ.Стоимость = НоваяСтрокаТЧ.ЗарплатаВодителю + НоваяСтрокаТЧ.ЗарплатаГрузчику
+ НоваяСтрокаТЧ.Скорость + НоваяСтрокаТЧ.Топливо;
КонецЕсли;
КонецЦикла;
Когда использовать: Этот подход полезен для синхронизации данных, когда табличная часть документа не должна быть полностью перезаписана, а требуется лишь обновить или добавить отдельные элементы на основе данных из внешнего источника.
Для создания надежного и эффективного кода при заполнении табличных частей, мы рекомендуем учитывать следующие моменты:
Стоимость в примере автора) непосредственно в запросе. Это значительно снижает нагрузку на клиентский или серверный код, повышает производительность и упрощает отладку. Если бы поле Стоимость было рассчитано в запросе, то при использовании метода Загрузить() оно заполнилось бы автоматически.
// Пример расчета стоимости в запросе
Запрос.Текст = "ВЫБРАТЬ
| // ... другие поля ...
| ЕСТЬNULL(ТаблицаВодителиВодители.СкоростьГрн, 0) КАК Скорость,
| ЕСТЬNULL(ТаблицаВодителиВодители.ТопливоГрн, 0) КАК Топливо,
| ЕСТЬNULL(ТаблицаВодителиВодители.ЗарплатаВодителю, 0) +
| ЕСТЬNULL(ТаблицаВодителиВодители.ЗарплатаГрузчику, 0) +
| ЕСТЬNULL(ТаблицаВодителиВодители.СкоростьГрн, 0) +
| ЕСТЬNULL(ТаблицаВодителиВодители.ТопливоГрн, 0) КАК Стоимость
|ИЗ
| Документ.ТаблицаВодители.Водители КАК ТаблицаВодителиВодители
|// ... остальная часть запроса ...";
Очистить() удаляет все существующие строки. Если вы не очистите ее, новые данные будут добавлены к уже существующим.Загрузить() без параметра СоответствиеКолонок или ЗаполнитьЗначенияСвойств().ЕСТЬNULL(). Это правильный подход для обработки возможных пустых значений, чтобы избежать ошибок при работе с данными, особенно при выполнении расчетов. Продолжайте использовать ее для всех полей, которые могут быть NULL и участвуют в вычислениях или должны иметь значение по умолчанию.Загрузить(). Построчный перебор и добавление каждой строки значительно медленнее из-за накладных расходов на создание объектов.Применяя эти методы и рекомендации, вы сможете эффективно и корректно заполнять табличные части документов в 1С:Предприятие 8.3, делая ваш код более надежным, производительным и легким для поддержки.