Как сгруппировать данные и объединить ячейки по вертикали в печатной форме 1С?

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

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

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

Основной подход: Программное объединение ячеек в ТабличномДокументе

Для достижения желаемого результата — единого номера грузового места, выровненного по центру по вертикали, для нескольких номенклатурных позиций — мы будем использовать программное объединение ячеек в ТабличномДокументе. Прямая группировка средствами макета 1С не всегда позволяет получить такое визуальное представление, так как она чаще всего подразумевает вывод либо "шапки" группы, либо подведения итогов, но не объединение ячеек детальных записей в одном столбце.

Давайте разберем по шагам, как мы можем реализовать эту задачу:

  1. Подготовка данных: Прежде чем выводить данные, нам необходимо убедиться, что они отсортированы по полю, по которому будет производиться группировка (в нашем случае это "НомерГМ"). Это критически важно для корректного определения начала и конца группы. Мы можем сделать это с помощью запроса к временной таблице или методом Сортировать() для таблицы значений.
  2. Определение макета и областей: В макете печатной формы мы должны определить как минимум две области: одну для вывода номера грузового места (которая в дальнейшем будет объединена) и другую для вывода детальных строк номенклатурных позиций.
  3. Алгоритм вывода и объединения:
    • Инициализация: Нам понадобятся переменные для отслеживания номера предыдущего грузового места и номера строки, с которой начинается текущая группа для объединения.
    • Цикл по данным: Мы будем последовательно обходить все строки подготовленной таблицы значений.
    • Обнаружение начала новой группы: В каждой итерации цикла мы будем сравнивать номер текущего грузового места с номером предыдущего. Если они не совпадают (или это первая строка данных), это означает, что началась новая группа.
    • Вывод детальных строк: Для каждой номенклатурной позиции мы будем выводить отдельную строку в табличный документ.
    • Объединение ячеек: Когда мы обнаружим конец группы (либо при изменении номера ГМ, либо по окончании всех данных), мы будем программно объединять ячейки в столбце "НомерГМ", используя номера первой и последней строки текущей группы.
    • Установка выравнивания: После объединения ячеек мы установим вертикальное выравнивание текста по центру.

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

Предположим, у нас есть макет с двумя областями: ОбластьГМ (для вывода номера грузового места) и ОбластьДеталь (для вывода номенклатурных позиций). Колонка для номера грузового места в ОбластьДеталь должна быть пустой или содержать шаблон для вывода, который не будет использоваться, поскольку данные будут вставляться в ОбластьГМ, а затем объединяться. Или же можно вообще не выводить номер ГМ в области ОбластьДеталь, а только в области, которую мы планируем объединять.

Давайте рассмотрим пример программного кода, который реализует описанный алгоритм:

Шаг 1: Подготовка и сортировка данных.

Если у нас есть таблица значений ТаблицаГрузовыхМест, полученная, например, из Excel, первым делом мы должны ее отсортировать по полю НомерГМ. Это обеспечит последовательный вывод всех номенклатур, относящихся к одному грузовому месту.


// Предположим, у нас есть ТаблицаЗначений, полученная из Excel
// ТаблицаГрузовыхМест = ПолучитьДанныеИзExcel();

// Убедимся, что данные отсортированы по номеру грузового места
ТаблицаГрузовыхМест.Сортировать("НомерГМ ASC");

Шаг 2: Инициализация ТабличногоДокумента и макета.

Создаем новый ТабличныйДокумент и получаем доступ к макету печатной формы.


// Создаем новый ТабличныйДокумент
Перем ТабличныйДокумент;
ТабличныйДокумент = Новый ТабличныйДокумент;
ТабличныйДокумент.Очистить();

// Получаем макет печатной формы (например, из обработки или отчета)
Макет = ПолучитьМакет("МакетПечатнойФормыГМ");

// Получаем области макета
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ОбластьГМ = Макет.ПолучитьОбласть("ГМ"); // Область для вывода номера ГМ, которую будем объединять
ОбластьДеталь = Макет.ПолучитьОбласть("Деталь"); // Область для вывода детальных позиций

Шаг 3: Основной цикл вывода и объединения.

Теперь мы переходим к самому интересному — циклу обработки данных и динамическому объединению ячеек. Мы будем использовать переменные для отслеживания текущего состояния группировки.


ПредыдущийНомерГМ = "";
НачальнаяСтрокаГруппы = 0; // Будем хранить номер строки, с которой начинается текущая группа
ИндексСтроки = 0; // Используем для отслеживания текущей строки в ТабличномДокументе

// Выводим шапку таблицы
ТабличныйДокумент.Вывести(ОбластьШапка);
ИндексСтроки = ТабличныйДокумент.ВысотаТаблицы; // Начальная строка после шапки

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

        // Обновляем информацию о новой группе
        ПредыдущийНомерГМ = СтрокаТЗ.НомерГМ;
        НачальнаяСтрокаГруппы = ИндексСтроки; // Запоминаем текущую строку как начало новой группы
        
        // Выводим номер грузового места в соответствующую область
        // Здесь мы могли бы вывести ОбластьГМ, если она содержит только номер ГМ
        // и не является частью детальной строки.
        // Но для нашего примера мы выводим его как часть детальной строки, 
        // а затем объединяем. Важно: ОбластьГМ должна иметь параметр для НомерГМ.
        ОбластьГМ.Параметры.НомерГМ = СтрокаТЗ.НомерГМ;
        ТабличныйДокумент.Вывести(ОбластьГМ); // Выводим область с номером ГМ
        ИндексСтроки = ТабличныйДокумент.ВысотаТаблицы; // Обновляем текущую строку
        // Если мы хотим вывести НомерГМ только один раз, а потом объединять,
        // то мы должны записать его в первую строку группы, а остальные строки
        // в этом столбце оставить пустыми для последующего объединения.
        // ИЛИ же мы можем вывести его в область ГМ (как сейчас), а потом детальные строки.
        // Но более правильный подход для объединения: 
        // 1. Вывести первую детальную строку с номером ГМ.
        // 2. Все последующие детальные строки без номера ГМ.
        // 3. После группы объединить ячейки в первой колонке.
        // Для простоты, здесь мы выведем ОбластьГМ отдельно, что может быть не всегда оптимально.
        // Давайте лучше адаптируем подход для объединения ячеек одной и той же колонки детальных строк.
    КонецЕсли;
    
    // Выводим детальную строку (номенклатурную позицию)
    // Важно: в ОбластьДеталь не должно быть поля НомерГМ, 
    // если мы хотим объединять его в отдельной колонке!
    // Или же в макете ОбластьДеталь есть поле НомерГМ,
    // но в последующих строках мы его не заполняем, чтобы оно было пустым.
    
    // Для нашего примера, предположим, что в макете есть ячейка "НомерГМ" в области "Деталь".
    // Мы будем заполнять ее только для первой строки группы, а потом "стирать" ее для последующих,
    // чтобы потом объединить.
    ОбластьДеталь.Параметры.Номенклатура = СтрокаТЗ.Номенклатура;
    ОбластьДеталь.Параметры.Количество = СтрокаТЗ.Количество;
    // ... другие параметры номенклатуры
    
    // Если это первая строка в группе, выводим номер ГМ. Иначе оставляем пустым, 
    // чтобы потом объединить.
    Если НачальнаяСтрокаГруппы = ИндексСтроки - ОбластьГМ.ВысотаТаблицы Тогда // Проверка, что это первая детальная строка после вывода ОбластьГМ
        // Мы уже вывели НомерГМ через ОбластьГМ. Теперь в детальной строке не дублируем
    Иначе
        // Если мы не используем отдельную ОбластьГМ, а хотим объединять в детальных строках
        // то нужно в параметрах ОбластьДеталь.Параметры.НомерГМ задать "" для следующих строк
        // или просто не выводить в макете ОбластьДеталь эту колонку.
        // Для данной реализации, если мы вывели ОбластьГМ отдельно, то детальные строки уже не содержат ГМ.
        // Если же ОбластьГМ не выводилась, а номер ГМ является частью первой детальной строки,
        // то нужно для первой строки вывести НомерГМ, а для последующих - пустую строку.
        // Чтобы сделать это правильно, нам нужно запомнить строку и ее параметры, 
        // а потом вывести НомерГМ в первой строке группы, а остальные пустыми.
        // Этот подход является более гибким.
    КонецЕсли;

    // Выводим детальную строку
    ТабличныйДокумент.Вывести(ОбластьДеталь);
    ИндексСтроки = ТабличныйДокумент.ВысотаТаблицы; // Обновляем текущую строку
КонецЦикла;

// ВАЖНО: После цикла необходимо выполнить объединение для последней группы!
Если НачальнаяСтрокаГруппы > 0 Тогда
    НомерНачальнойКолонкиГМ = 1; 
    НомерКонечнойКолонкиГМ = 2;   
    
    ОбластьДляОбъединения = ТабличныйДокумент.Область(НачальнаяСтрокаГруппы, НомерНачальнойКолонкиГМ, ИндексСтроки - 1, НомерКонечнойКолонкиГМ);
    
    Если ОбластьДляОбъединения.Высота > 1 Тогда
        ОбластьДляОбъединения.Объединить();
    КонецЕсли;
    
    ОбластьДляОбъединения.ВертикальноеПоложение = ВертикальноеПоложение.Центр;
    ОбластьДляОбъединения.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
КонецЕсли;

// Показываем сформированный документ
ТабличныйДокумент.Показать();

Уточненный алгоритм для корректного объединения ячеек в одной колонке, входящей в детальную область:

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


Перем ТабличныйДокумент;
ТабличныйДокумент = Новый ТабличныйДокумент;
ТабличныйДокумент.Очистить();

Макет = ПолучитьМакет("МакетПечатнойФормыГМ");
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ОбластьСтрокаДеталей = Макет.ПолучитьОбласть("СтрокаДеталей"); // Одна область для всех детальных строк

ТабличныйДокумент.Вывести(ОбластьШапка);

ПредыдущийНомерГМ = "";
НачальнаяСтрокаДляОбъединенияГМ = ТабличныйДокумент.ВысотаТаблицы + 1; // Запоминаем номер строки в ТабличномДокументе

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

        // Начинаем новую группу
        ПредыдущийНомерГМ = СтрокаТЗ.НомерГМ;
        НачальнаяСтрокаДляОбъединенияГМ = ТабличныйДокумент.ВысотаТаблицы + 1; // Новое начало для объединения
        
        // Выводим текущий НомерГМ в первой строке новой группы
        ОбластьСтрокаДеталей.Параметры.НомерГМ = СтрокаТЗ.НомерГМ;
    Иначе
        // Если это не первая строка в группе, НомерГМ не выводим, оставляем пустым
        ОбластьСтрокаДеталей.Параметры.НомерГМ = "";
    КонецЕсли;
    
    // Заполняем остальные параметры детальной строки
    ОбластьСтрокаДеталей.Параметры.Номенклатура = СтрокаТЗ.Номенклатура;
    ОбластьСтрокаДеталей.Параметры.Количество = СтрокаТЗ.Количество;
    // ... другие параметры
    
    ТабличныйДокумент.Вывести(ОбластьСтрокаДеталей);
КонецЦикла;

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

ТабличныйДокумент.Показать();

Особенности работы с методом "Объединить()"

Метод Объединить() в 1С применяется к прямоугольной области табличного документа. Вы совершенно правы, что он работает именно с диапазонами ячеек. Его синтаксис обычно выглядит следующим образом:


ТабДок.Область(НачальнаяСтрока, НачальнаяКолонка, КонечнаяСтрока, КонечнаяКолонка).Объединить();

Где:

Для определения этих номеров колонок мы можем использовать свойства именованной области макета, такие как Лево и Право. Например, если в макете у вас есть именованная область ячейки НомерГМ в составе детальной области СтрокаДеталей, то можно получить ее границы так:


НомерНачальнойКолонкиГМ = ОбластьСтрокаДеталей.Области.НомерГМ.Лево;
НомерКонечнойКолонкиГМ = ОбластьСтрокаДеталей.Области.НомерГМ.Право;

После объединения ячеек крайне важно установить правильное выравнивание. Для этого мы обращаемся к свойствам полученной области:


ОбластьДляОбъединения.ВертикальноеПоложение = ВертикальноеПоложение.Центр;
ОбластьДляОбъединения.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр; // По желанию

Работа с макетами и областями

Макеты в 1С — это шаблоны для построения отчетов и печатных форм. В макете мы определяем структуру выводимого документа. Для нашей задачи мы используем именованные области, которые позволяют нам гибко управлять выводом различных частей отчета:

  1. Область "Шапка": Для заголовков таблицы или общей информации, которая выводится один раз в начале.
  2. Область "СтрокаДеталей": Для вывода каждой отдельной номенклатурной позиции. Именно в этой области мы определяем колонку для номера грузового места. Изначально она может содержать параметр [НомерГМ]. В ходе формирования отчета мы будем заполнять этот параметр только для первой строки группы, а для остальных строк группы оставлять его пустым.

Важно понимать, что объединение ячеек происходит уже в сформированном ТабличномДокументе, а не в самом макете. Макет лишь определяет шаблон и параметры, которые мы заполняем в коде, а программный код выполняет динамические изменения, такие как объединение.

Группировка в запросе как вспомогательный инструмент

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

Важные нюансы при формировании печатных форм

При работе с печатными формами в 1С есть несколько важных моментов, которые стоит учитывать:

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

Реализация такого функционала требует внимательного подхода к логике обхода данных и точному определению координат ячеек для объединения. Но, следуя описанным шагам и используя методы ТабличногоДокумента, мы сможете добиться желаемого результата по группировке данных и красивому оформлению печатных форм.

← На главную