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

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

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

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

В чем сложность: формат строк табличного документа

Прежде чем перейти к написанию кода, давайте выясним первопричину проблем, с которыми сталкиваются программисты при попытке склеить два документа. Главный «подводный камень» заключается в понятии формата строк табличного документа.

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

Для того чтобы обойти эти ограничения, мы можем применить несколько программных подходов. Рассмотрим их подробнее.

Решение 1. Раздельное копирование областей с помощью метода ВставитьОбласть()

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

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


// Определяем исходные параметры документов
Док1Ширина = ТабДокПродажиПоСотруднику.ШиринаТаблицы;
Док2Ширина = ТабДокПоЧасам.ШиринаТаблицы;
Док2Высота = ТабДокПоЧасам.ВысотаТаблицы;

// Шаг 1. Переносим область заголовка (строка 2)
// Копируем заголовок из второго документа и вставляем его справа от заголовка первого документа
ОбластьИсточникЗаголовок = ТабДокПоЧасам.Область(2, 1, 2, 2);
ОбластьПриемникЗаголовок = ТабДокПродажиПоСотруднику.Область(2, Док1Ширина + 1);
ТабДокПродажиПоСотруднику.ВставитьОбласть(ОбластьИсточникЗаголовок, ОбластьПриемникЗаголовок);

// Шаг 2. Переносим область данных таблицы (строки с 4 по конец документа)
// Вставляем данные второго документа справа с небольшим отступом (+2 колонки)
ОбластьИсточникДанные = ТабДокПоЧасам.Область(4, 1, Док2Высота, Док2Ширина);
ОбластьПриемникДанные = ТабДокПродажиПоСотруднику.Область(4, Док1Ширина + 2, Док2Высота, Док1Ширина + Док2Ширина + 1);
ТабДокПродажиПоСотруднику.ВставитьОбласть(ОбластьИсточникДанные, ОбластьПриемникДанные);

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

Важное примечание: Координаты строк в данном примере захардкожены (строки 2 и 4). На практике рекомендуется программно вычислять границы областей, например, используя именованные области макета или выполняя поиск ключевых ячеек, чтобы избежать ошибок при изменении структуры отчета пользователем.

Решение 2. Построчный вывод в новый документ методами Вывести() и Присоединить()

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

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

Разберем по шагам, как реализовать этот алгоритм:

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

Посмотрим на программную реализацию этого алгоритма:


ТабДокРезультирующий = Новый ТабличныйДокумент;

Высота1 = ТабДок1.ВысотаТаблицы;
Высота2 = ТабДок2.ВысотаТаблицы;
МаксВысота = Макс(Высота1, Высота2);

Ширина1 = ТабДок1.ШиринаТаблицы;
Ширина2 = ТабДок2.ШиринаТаблицы;

Для НомерСтроки = 1 По МаксВысота Цикл
    
    // Получаем и выводим строку из первого документа
    Если НомерСтроки <= Высота1 Тогда
        Область1 = ТабДок1.Область(НомерСтроки, 1, НомерСтроки, Ширина1);
        ТабДокРезультирующий.Вывести(Область1);
    Иначе
        // Если первый документ закончился, выводим пустую область соответствующей ширины
        ОбластьПустая = Новый ТабличныйДокумент;
        ОбластьПустая.Область(1, 1, 1, Ширина1);
        ТабДокРезультирующий.Вывести(ОбластьПустая);
    КонецЕсли;
    
    // Присоединяем строку из второго документа справа
    Если НомерСтроки <= Высота2 Тогда
        Область2 = ТабДок2.Область(НомерСтроки, 1, НомерСтроки, Ширина2);
        ТабДокРезультирующий.Присоединить(Область2);
    КонецЕсли;
    
КонецЦикла;

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

Решение 3. Использование метода СоздатьФорматСтрок()

Что делать, если после объединения колонок таблицы все равно выглядят искаженными, а текст не помещается в ячейки? В платформе 1С для таких задач предусмотрен специализированный метод СоздатьФорматСтрок().

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


// Допустим, мы вставили область из второго документа в результирующий табличный документ
ОбластьВставки = ТабДокРезультирующий.Область(НачалоСтроки, НачалоКолонки, КонецСтроки, КонецКолонки);

// Принудительно создаем отдельный формат строк для этой области
ОбластьВставки.СоздатьФорматСтрок();

// Теперь мы можем программно задать ширину колонок для вставленной области, 
// не боясь, что это повлияет на ширину колонок первой (левой) таблицы
Для Кол = 1 По ШиринаВторойТаблицы Цикл
    ШиринаИсходнойКолонки = ТабДок2.Область(, Кол).ШиринаКолонки;
    ТабДокРезультирующий.Область(НачалоСтроки, НачалоКолонки + Кол - 1).ШиринаКолонки = ШиринаИсходнойКолонки;
КонецЦикла;

Использование СоздатьФорматСтрок() — это мощный инструмент для тонкой настройки внешнего вида сложных комбинированных отчетов. Он полностью решает проблему «разъехавшихся» колонок при горизонтальном объединении разнородных таблиц.

Альтернативное решение: объединение таблиц на уровне СКД

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

Способ А. Виртуальное связывание наборов данных

Если ваши таблицы формируются по разным запросам, мы можем объединить их в одной схеме компоновки данных:

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

Способ Б. Переопределение процедуры ПриКомпоновкеРезультата

Если отчеты должны оставаться архитектурно независимыми, мы можем объединить их программный вывод в модуле объекта отчета:


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

Подведем итоги

Мы подробно разобрали особенности работы с табличными документами в 1С и выяснили, как обходить платформенные ограничения при горизонтальном объединении таблиц.

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

← На главную