Как сгруппировать контрагентов и вывести список актов через запятую в одной строке печатной формы

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

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

Способ 1. Иерархический обход результата запроса (По группировкам)

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

Для начала нам нужно изменить текст запроса, добавив в него секцию ИТОГИ. Это позволит платформе построить древовидную структуру данных.


ВЫБРАТЬ
    Акт.Контрагент КАК Контрагент,
    Акт.Контрагент.Представление КАК ПредставлениеКонтрагента,
    Акт.Ссылка КАК АктСсылка,
    Акт.Номер КАК НомерАкта
ИЗ
    Документ.АктВыполненныхРабот КАК Акт
ГДЕ
    Акт.Дата МЕЖДУ &НачалоПериода И &КонецПериода
ИТОГИ ПО
    Контрагент

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

  1. Выполняем запрос и получаем результат.
  2. Запускаем цикл по верхнему уровню (Контрагенты).
  3. Внутри цикла по контрагенту запускаем вложенный цикл по детальным записям (Акты).
  4. Накапливаем номера актов в строковую переменную.
  5. Выводим итоговую строку в макет.

Результат = Запрос.Выполнить();
ВыборкаКонтрагент = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаКонтрагент.Следующий() Цикл
    
    СписокАктов = "";
    ВыборкаДетали = ВыборкаКонтрагент.Выбрать(); // Обход детальных записей
    
    Пока ВыборкаДетали.Следующий() Цикл
        ТекстАкта = "Акт №" + ВыборкаДетали.НомерАкта;
        СписокАктов = СписокАктов + ?(СписокАктов = "", "", ", ") + ТекстАкта;
    КонецЦикла;
    
    // Теперь у нас есть заполненный СписокАктов для конкретного контрагента
    ОбластьМакета = Макет.ПолучитьОбласть("Строка");
    ОбластьМакета.Параметры.Контрагент = ВыборкаКонтрагент.ПредставлениеКонтрагента;
    ОбластьМакета.Параметры.Акты = СписокАктов;
    ТабДок.Вывести(ОбластьМакета);
    
КонецЦикла;

Способ 2. Использование функции СКД СоединитьСтроки()

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

Рассмотрим пошагово настройку ресурсов в схеме компоновки:

  1. Перейдите на закладку «Ресурсы».
  2. Выберите поле, которое нужно перечислить (например, АктСсылка).
  3. В колонке «Выражение» напишите: СоединитьСтроки(Массив(НомерАкта), ", ").
  4. В колонке «Рассчитывать по...» укажите поле группировки — Контрагент.

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

Способ 3. Программная свертка Таблицы Значений

Иногда удобнее работать не с выборкой запроса, а с объектом ТаблицаЗначений. Проанализируем ситуацию, когда данные уже получены и их нужно подготовить к выводу.

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


// Допустим, ТЗ_Данные - результат запроса с колонками "Контрагент", "Адрес", "Акт"
ТЗ_Свернутая = ТЗ_Данные.Скопировать();
ТЗ_Свернутая.Свернуть("Контрагент, Адрес", "Акт"); // Акт тут обнулится, если он не число

Для Каждого СтрИзСвернутой Из ТЗ_Свернутая Цикл
    
    ПараметрыОтбора = Новый Структура("Контрагент, Адрес", СтрИзСвернутой.Контрагент, СтрИзСвернутой.Адрес);
    НайденныеСтроки = ТЗ_Данные.НайтиСтроки(ПараметрыОтбора);
    
    МассивНомеров = Новый Массив;
    Для Каждого СтрОригинал Из НайденныеСтроки Цикл
        МассивНомеров.Добавить(СтрОригинал.Акт.Номер);
    КонецЦикла;
    
    СтрИзСвернутой.Акт = СтрСоединить(МассивНомеров, ", ");
    
КонецЦикла;

Здесь мы применили функцию СтрСоединить(), которая доступна в платформе 8.3. Она работает быстрее, чем ручная конкатенация строк в цикле, и автоматически обрабатывает разделители.

Способ 4. Объединение ячеек в Табличном документе (Визуальный метод)

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

Суть метода заключается в том, что мы запоминаем номер строки начала вывода контрагента и номер строки окончания. После того как все акты одного контрагента выведены, мы применяем объединение к области.


НомерСтрокиНачала = ТабДок.ВысотаТаблицы + 1;

Для Каждого Акт Из СписокАктовДляКонтрагента Цикл
    Область = Макет.ПолучитьОбласть("Строка");
    Область.Параметры.Контрагент = Контрагент;
    Область.Параметры.Акт = Акт;
    ТабДок.Вывести(Область);
КонецЦикла;

НомерСтрокиКонца = ТабДок.ВысотаТаблицы;

// Объединяем ячейки в первой колонке (Контрагент)
ОбластьДляОбъединения = ТабДок.Область("R" + НомерСтрокиНачала + "C1:R" + НомерСтрокиКонца + "C1");
ОбластьДляОбъединения.Объединить();
ОбластьДляОбъединения.ВертикальноеПоложение = ВертикальноеПоложение.Центр;

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

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

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

Выбор конкретного метода зависит от ваших задач. Если нужна гибкость и быстрота разработки — используйте СКД. Если важна жесткая структура макета и сложная логика вывода — используйте иерархический обход результата запроса с ИТОГИ ПО.

← На главную