При разработке печатных форм в 1С часто возникает задача, когда необходимо вывести данные в сгруппированном виде, при этом сгруппированное поле должно отображаться единожды для всей группы, занимая несколько строк, и быть выровненным по центру по вертикали — для этого подойдёт редактор макетов и печатных форм для 1С без программирования. Рассмотрим на примере, когда у нас есть таблица с номенклатурными позициями, и несколько из этих позиций относятся к одному грузовому месту. Мы хотим, чтобы номер грузового места был выведен только один раз для всей группы номенклатур, а не повторялся для каждой строки.
Типичная ситуация, которую мы будем разбирать, следующая: данные о товарах и их принадлежности к грузовым местам поступают в виде таблицы значений. Некоторые грузовые места содержат несколько номенклатурных позиций. Нам требуется организовать вывод таким образом, чтобы номер грузового места отображался в одной ячейке, которая объединяет строки всех позиций, входящих в это грузовое место, и была отцентрирована по вертикали.
Для достижения желаемого результата — единого номера грузового места, выровненного по центру по вертикали, для нескольких номенклатурных позиций — мы будем использовать программное объединение ячеек в ТабличномДокументе. Прямая группировка средствами макета 1С не всегда позволяет получить такое визуальное представление, так как она чаще всего подразумевает вывод либо "шапки" группы, либо подведения итогов, но не объединение ячеек детальных записей в одном столбце.
Давайте разберем по шагам, как мы можем реализовать эту задачу:
Сортировать() для таблицы значений.Предположим, у нас есть макет с двумя областями: ОбластьГМ (для вывода номера грузового места) и ОбластьДеталь (для вывода номенклатурных позиций). Колонка для номера грузового места в ОбластьДеталь должна быть пустой или содержать шаблон для вывода, который не будет использоваться, поскольку данные будут вставляться в ОбластьГМ, а затем объединяться. Или же можно вообще не выводить номер ГМ в области ОбластьДеталь, а только в области, которую мы планируем объединять.
Давайте рассмотрим пример программного кода, который реализует описанный алгоритм:
Шаг 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С есть несколько важных моментов, которые стоит учитывать:
СоздатьФорматСтрок(). Это может потребоваться, чтобы избежать нежелательных смещений или изменений форматирования, когда вы программно манипулируете структурой ТабличногоДокумента. Однако для простой задачи объединения ячеек в одной колонке внутри уже существующей области, как правило, этого не требуется.Реализация такого функционала требует внимательного подхода к логике обхода данных и точному определению координат ячеек для объединения. Но, следуя описанным шагам и используя методы ТабличногоДокумента, мы сможете добиться желаемого результата по группировке данных и красивому оформлению печатных форм.