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

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

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

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

Почему стандартный последовательный вывод не работает?

Давайте проанализируем ситуацию, с которой столкнулся автор темы. В его коде использовались два последовательных цикла:


// Ошибка: Последовательный вывод двух таблиц
Для Каждого СтрЗайм Из ПолучениеЗайма Цикл
    // Заполнение параметров левой области
    ТабДокумент.Вывести(ОбластьСтрокаПолучение);
КонецЦикла;

Для Каждого СтрПогашение Из ПогашениеЗайма Цикл
    // Заполнение параметров правой области
    ТабДокумент.Присоединить(ОбластьСтрокаПолучение2);
КонецЦикла;

Чтобы понять, почему этот код превращает правую таблицу в горизонтальную «колбасу» внизу листа, разберем поведение платформы 1С. Метод Вывести() добавляет область в конец документа, сдвигая курсор строки вниз. После окончания первого цикла курсор находится на самой последней строке первой таблицы.

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

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

Подготовка макета табличного документа

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

Допустим, у нас есть строка макета с именем Строка. Внутри нее мы определяем две вертикальные области:

  1. ЛеваяКолонка — для данных первой таблицы.
  2. ПраваяКолонка — для данных второй таблицы.

В коде мы будем получать их как пересечение областей:

ОбластьЛевая = Макет.ПолучитьОбласть("Строка|ЛеваяКолонка");
ОбластьПравая = Макет.ПолучитьОбласть("Строка|ПраваяКолонка"); ---

Способ 1. Параллельный обход коллекций в одном цикле

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

Разберем по шагам реализацию этого алгоритма:


// Определяем максимальное количество строк среди двух коллекций
МаксСтрок = Макс(ПолучениеЗайма.Количество(), ПогашениеЗайма.Количество());

Для Индекс = 0 По МаксСтрок - 1 Цикл
    
    // Шаг 1. Выводим левую колонку (создаем строку табличного документа)
    Если Индекс < ПолучениеЗайма.Количество() Тогда
        СтрЗайм = ПолучениеЗайма[Индекс];
        ОбластьЛевая = Макет.ПолучитьОбласть("Строка|ЛеваяКолонка");
        ОбластьЛевая.Параметры.СчетПолучение = СтрЗайм.Счет;
        ОбластьЛевая.Параметры.ДатаПолучение = Формат(СтрЗайм.ДатаСчета, "ДФ=dd.MM.yyyy");
        ОбластьЛевая.Параметры.СуммаПолучение = СтрЗайм.Сумма;
        ТабДокумент.Вывести(ОбластьЛевая);
    Иначе
        // Если данные в левой таблице закончились, выводим пустую область,
        // чтобы сдвинуть курсор вниз и подготовить место для правой колонки
        ОбластьЛеваяПустая = Макет.ПолучитьОбласть("Строка|ЛеваяКолонка");
        ОбластьЛеваяПустая.Параметры.Очистить();
        ТабДокумент.Вывести(ОбластьЛеваяПустая);
    КонецЕсли;
    
    // Шаг 2. Присоединяем правую колонку к текущей строке
    Если Индекс < ПогашениеЗайма.Количество() Тогда
        СтрПогашение = ПогашениеЗайма[Индекс];
        ОбластьПравая = Макет.ПолучитьОбласть("Строка|ПраваяКолонка");
        ОбластьПравая.Параметры.СчетПогашение = СтрПогашение.Счет;
        ОбластьПравая.Параметры.ДатаПогашение = Формат(СтрПогашение.ДатаСчета, "ДФ=dd.MM.yyyy");
        ОбластьПравая.Параметры.СуммаПогашение = СтрПогашение.Сумма;
        ТабДокумент.Присоединить(ОбластьПравая);
    КонецЕсли;
    
КонецЦикла;

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

---

Способ 2. Использование временных табличных документов (Наиболее простой и красивый метод)

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

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

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


// Создаем временный документ для левой стороны
ТабДокЛевый = Новый ТабличныйДокумент;
Для Каждого СтрЗайм Из ПолучениеЗайма Цикл
    ОбластьЛевая = Макет.ПолучитьОбласть("Строка|ЛеваяКолонка");
    ОбластьЛевая.Параметры.СчетПолучение = СтрЗайм.Счет;
    ОбластьЛевая.Параметры.ДатаПолучение = Формат(СтрЗайм.ДатаСчета, "ДФ=dd.MM.yyyy");
    ОбластьЛевая.Параметры.СуммаПолучение = СтрЗайм.Сумма;
    ТабДокЛевый.Вывести(ОбластьЛевая);
КонецЦикла;

// Создаем временный документ для правой стороны
ТабДокПравый = Новый ТабличныйДокумент;
Для Каждого СтрПогашение Из ПогашениеЗайма Цикл
    ОбластьПравая = Макет.ПолучитьОбласть("Строка|ПраваяКолонка");
    ОбластьПравая.Параметры.СчетПогашение = СтрПогашение.Счет;
    ОбластьПравая.Параметры.ДатаПогашение = Формат(СтрПогашение.ДатаСчета, "ДФ=dd.MM.yyyy");
    ОбластьПравая.Параметры.СуммаПогашение = СтрПогашение.Сумма;
    ТабДокПравый.Вывести(ОбластьПравая);
КонецЦикла;

// Соединяем временные документы в итоговом отчете
ТабДокумент.Вывести(ТабДокЛевый);      // Выводим левую таблицу целиком сверху вниз
ТабДокумент.Присоединить(ТабДокПравый); // Присоединяем правую таблицу целиком по всей высоте!

Важное преимущество метода: Платформа 1С автоматически сопоставит строки по высоте и красиво расположит таблицы «стык в стык» по горизонтали.

---

Важный нюанс: Метод СоздатьФорматСтрок() для сохранения ширины колонок

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

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

Пример использования метода:


// Пример фиксации разметки после формирования строки
ТабДокумент.Вывести(ОбластьЛевая);
ТабДокумент.Присоединить(ОбластьПравая);
ТабДокумент.Область(ТабДокумент.ВысотаТаблицы,, ТабДокумент.ВысотаТаблицы).СоздатьФорматСтрок();

---

Способ 3. Сведение данных на уровне запроса или СКД

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

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

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

← На главную