При разработке отчетов, печатных форм и обработок в системе «1С:Предприятие» программисты часто сталкиваются с классической задачей: необходимо вывести данные табличной части (ТЧ) документа так, чтобы реквизиты шапки (например, ссылка на документ, номер или дата) не дублировались для каждой строки номенклатуры. Стандартная выборка из таблицы ТЧ в запросе возвращает плоскую таблицу, где данные шапки повторяются на каждой строчке. Это делает визуальное восприятие информации неудобным для конечного пользователя.
В этой статье мы подробно разберем, как решить эту проблему различными методами в зависимости от ваших задач: с помощью Системы компоновки данных (СКД), механизмов языка запросов 1С, встроенного языка программирования и методов компоновки табличных документов.
---
Выясним причину такого поведения системы. Когда мы выбираем данные из табличной части документа, СУБД выполняет внутреннее соединение основной таблицы документа и подчиненной таблицы его табличной части. На уровне базы данных результатом всегда является плоская таблица. Если в документе «Приходная накладная №0001» содержится три строки номенклатуры (Бинт, Марля, Снотворное), то простая выборка вернет три записи:
Наша цель — превратить этот плоский список в структурированный вид, где ссылка на документ выводится один раз, а строки ТЧ отображаются компактно.
---
Если перед нами стоит задача разработать отчет, то наиболее простым, быстрым и технологически правильным решением будет использование встроенных функций СКД. Система компоновки данных умеет агрегировать строки на уровне вывода ресурсов.
Проанализируем работу функции СоединитьСтроки(). Этот метод позволяет объединить коллекцию значений из детальных записей в одну строковую ячейку с заданным разделителем. Выполним настройку по шагам:
Номенклатура в список ресурсов.
СоединитьСтроки(Номенклатура, ", ")
В результате работы отчета СКД автоматически сгруппирует все записи по документу-родителю, а в колонке номенклатуры выведет красивую строку: «Бинт, Марля, Снотворное».
Синтаксис функции СоединитьСтроки() выглядит следующим образом:
СоединитьСтроки(Выражение, [РазделительЭлементов], [РазделительКолонок])
В качестве выражения мы также можем передавать таблицы значений или массивы, собранные на уровне СКД, например, СоединитьСтроки(ТаблицаЗначений(Номенклатура.Наименование), "; ").
---
Рассмотрим классический способ группировки на уровне языка запросов 1С, который чаще всего применяется при программном выводе данных в макеты печатных форм. Для этого используется конструкция ИТОГИ ПО.
Посмотрим на пример построения такого запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПриходнаяТовары.Ссылка КАК Ссылка,
| ПриходнаяТовары.Номенклатура КАК Номенклатура
|ИЗ
| Документ.Приходная.Товары КАК ПриходнаяТовары
|
|УПОРЯДОЧИТЬ ПО
| Ссылка
|ИТОГИ ПО
| Ссылка";
РезультатЗапроса = Запрос.Выполнить();
Теперь разберем, как правильно обойти результат такого запроса во встроенном языке 1С. Ключевой момент здесь — обход по группировкам. Нам нужно использовать вложенные циклы:
// Получаем выборку верхнего уровня (по документам)
ВыборкаДокумент = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаДокумент.Следующая() Цикл
// Здесь мы выводим шапку документа ровно ОДИН раз
ЗаписатьВЛог("Документ: " + СокрЛП(ВыборкаДокумент.Ссылка));
// Получаем подчиненную выборку детальных записей (строки ТЧ для этого документа)
ВыборкаДетали = ВыборкаДокумент.Выбрать();
Пока ВыборкаДетали.Следующая() Цикл
// Здесь мы последовательно выводим строки табличной части
ЗаписатьВЛог(" -> Номенклатура: " + СокрЛП(ВыборкаДетали.Номенклатура));
КонецЦикла;
КонецЦикла;
Этот метод гарантирует, что ссылка на документ будет обработана ровно один раз на документ, а все подчиненные строки ТЧ будут выведены строго под ней.
---
Если по какой-то причине вы не хотите использовать итоги в запросе (например, если логика программы требует плоской выборки для других расчетов), мы можем применить алгоритмический метод с использованием временной переменной или флага первой строки.
Рассмотрим пример реализации такого алгоритма:
Выборка = РезультатЗапроса.Выбрать();
ТекущийДокумент = Неопределено;
Пока Выборка.Следующая() Цикл
Если Выборка.Ссылка <> ТекущийДокумент Тогда
// Ссылка изменилась, значит начался новый документ
// Выводим заполненную колонку с документом
ВывестиПолеДокумента(Выборка.Ссылка);
ТекущийДокумент = Выборка.Ссылка;
Иначе
// Документ тот же, выводим пустое пространство вместо ссылки
ВывестиПустоеПолеДокумента();
КонецЕсли;
// Номенклатуру выводим в любом случае
ВывестиПолеНоменклатуры(Выборка.Номенклатура);
КонецЦикла;
Этот простой подход позволяет визуально очистить дублирующиеся строки в первом столбце, оставляя заполненной только самую первую строку группы.
---
Бывают ситуации, когда нам вообще не требуется строить сложные запросы к табличным частям, а нужно просто вывести список номенклатуры документа в одну ячейку (например, в реестре документов). Начиная с версии платформы 8.3.6, в 1С доступна быстрая и удобная функция СтрСоединить(), которая позволяет избежать написания громоздких циклов.
Проанализируем ситуацию на примере получения списка номенклатуры из конкретного документа:
// Получаем массив значений из колонки "Номенклатура" табличной части "Товары"
МассивНоменклатуры = СсылкаНаДокумент.Товары.ВыгрузитьКолонку("Номенклатура");
// Преобразуем элементы массива ссылок в массив строк для корректного объединения
МассивСтрок = Новый Массив;
Для Каждого ТекущаяСсылка Из МассивНоменклатуры Цикл
МассивСтрок.Добавить(Строка(ТекущаяСсылка));
КонецЦикла;
// Объединяем элементы массива в одну строку с разделителем "запятая"
ИтоговаяСтрокаНоменклатуры = СтрСоединить(МассивСтрок, ", ");
Этот метод работает чрезвычайно быстро, так как выгрузка колонки табличной части в массив и его последующее склеивание выполняются на уровне платформенных C++ механизмов без лишних накладных расходов встроенного языка.
---
Если перед вами стоит задача разработать профессиональную печатную форму, где ячейка с названием документа должна физически объединять все строки номенклатуры по вертикали, вам поможет встроенный метод табличного документа Объединить().
Разберем по шагам, как программно реализовать вертикальное объединение ячеек при выводе строк:
ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("МакетПечати");
ОбластьСтроки = Макет.ПолучитьОбласть("СтрокаТЧ");
ВыборкаДокумент = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаДокумент.Следующая() Цикл
// Фиксируем строку начала вывода для текущего документа
СтартоваяСтрока = ТабДок.ВысотаТаблицы + 1;
ВыборкаДетали = ВыборкаДокумент.Выбрать();
ПервыйОбход = Истина;
Пока ВыборкаДетали.Следующая() Цикл
ОбластьСтроки.Параметры.Номенклатура = ВыборкаДетали.Номенклатура;
// Заполняем ссылку только для первой строки группы
Если ПервыйОбход Тогда
ОбластьСтроки.Параметры.Ссылка = ВыборкаДетали.Ссылка;
ПервыйОбход = Ложь;
Иначе
ОбластьСтроки.Параметры.Ссылка = "";
КонецЕсли;
ТабДок.Вывести(ОбластьСтроки);
КонецЦикла;
// Фиксируем строку окончания вывода документа
КонечнаяСтрока = ТабДок.ВысотаТаблицы;
// Если у нас вывелось более одной строки, объединяем ячейки по вертикали
Если КонечнаяСтрока > СтартоваяСтрока Тогда
// Предположим, что колонка "Ссылка" идет первой в таблице
НомерКолонки = 1;
ОбластьДляОбъединения = ТабДок.Область(СтартоваяСтрока, НомерКолонки, КонечнаяСтрока, НомерКолонки);
ОбластьДляОбъединения.Объединить();
КонецЕсли;
КонецЦикла;
Этот алгоритм создаст профессиональную и аккуратную печатную форму, в которой левая ячейка с наименованием документа будет растянута на всю высоту строк ТЧ, расположенных справа.
---
Мы рассмотрели различные подходы к выводу строк табличной части в одно поле или к их визуальной группировке. Выбор метода зависит от конкретного контекста:
СоединитьСтроки() в СКД, если делаете классический отчет.ИТОГИ ПО в запросах, если пишете программный вывод данных в печатные формы.СтрСоединить() для быстрого анализа объектов во встроенном коде.