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

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

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

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

Анализ проблемы: почему ломается стандартная верстка?

Рассмотрим классическую ситуацию, с которой столкнулся автор темы. Нам нужно вывести три области в табличный документ:

  1. Область 1 — широкая шапка документа на всю ширину.
  2. Область 2 — левый блок данных, который имеет высоту в несколько строк.
  3. Область 3 — правый блок данных, который должен располагаться строго справа от Области 2, причем их верхние границы должны находиться на одной линии.

Если мы применим «тупой» последовательный вывод:


ТабДок.Вывести(Область1);
ТабДок.Вывести(Область2);
ТабДок.Присоединить(Область3);

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

Выясним причину такого поведения. Метод Присоединить() всегда начинает вывод с первой свободной колонки последней выведенной строки. Он не умеет автоматически возвращаться «назад во времени» к началу строки, если область-источник занимает по вертикали больше одной ячейки. Нам нужен инструмент для точечного позиционирования. Разберем, как его реализовать.

Критика идеи парсинга координат и правильная альтернатива

В ходе обсуждения автор темы пришел к идее получать адрес выведенной области в виде строки формата "R24C1:R26C5" через свойство Имя результирующей области и парсить эту строку регулярными выражениями или функциями работы со строками, чтобы вычислить координаты для следующего вывода:


ТочкаВыводаОбласти2 = ДокументРезультат.Вывести(Область2);
ТочкаВыводаОбласти2Строка = ТочкаВыводаОбласти2.Имя; // Возвращает "R24C1:R26C5"
// Далее предполагается сложный разбор строки для поиска номеров строк и колонок...

Внимание: это плохая практика! Парсинг строки адреса — избыточный, медленный и потенциально опасный путь. Координатная сетка может измениться, формат локализации строки адреса в разных версиях платформы или региональных настройках системы также может отличаться (например, использование латинских букв R и C вместо русских Ш и К).

Платформа 1С предоставляет встроенные числовые свойства для любого объекта типа ОбластьЯчеекТабличногоДокумента, которые работают мгновенно и надежно:

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

Способ №1: Использование метода ВставитьОбласть()

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

Этот метод позволяет скопировать фрагмент ячеек из макета и «наложить» его на произвольный диапазон ячеек результирующего документа. Давайте разберем этот процесс по шагам на практическом примере:


// 1. Выводим левую Область 2 стандартным методом и получаем объект выведенных ячеек
РезультирующаяОбласть2 = ТабДок.Вывести(Область2);

// 2. Рассчитываем координаты для правой Области 4 на основе координат выведенной Области 2
// Нам нужно, чтобы Область 4 начиналась на той же строке, где начинается Область 2
СтрокаНачала = РезультирующаяОбласть2.Верх; 

// Область 4 должна располагаться сразу справа от Области 2
КолонкаНачала = РезультирующаяОбласть2.Право + 1;

// Определяем высоту и ширину Области 4, обратившись к исходному макету
ВысотаОбласти4 = Область4.ВысотаТаблицы;
ШиринаОбласти4 = Область4.ШиринаТаблицы;

// Вычисляем конечные координаты для области-приемника
СтрокаКонца = СтрокаНачала + ВысотаОбласти4 - 1;
КолонкаКонца = КолонкаНачала + ШиринаОбласти4 - 1;

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

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

Важная деталь: Использование параметра ТипСмещенияТабличногоДокумента.БезСмещения критически важно. В этом режиме ячейки макета просто копируются поверх сетки табличного документа. Если выбрать смещение по горизонтали или вертикали, платформа сдвинет уже существующие строки и колонки, что приведет к непредсказуемому искажению внешнего вида отчета.

Способ №2: Пересечение областей (Золотой стандарт 1С)

Программирование точных числовых координат в коде — рабочий метод, но он делает программный код жестко зависимым от структуры макета. Если в будущем вам потребуется добавить одну колонку в Область 2, программные расчеты в коде придется переписывать.

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

В макете создаются горизонтальные именованные строки (например, СтрокаШапки, СтрокаДанных1, СтрокаДанных2) и вертикальные колонки (например, КолонкаЛевая, КолонкаПравая). При выводе мы обращаемся к пересечению этих областей с помощью оператора вертикальной черты |:


// Выводим первую строку (Шапка) на всю ширину
ТабДок.Вывести(Макет.ПолучитьОбласть("СтрокаШапки|КолонкаЛевая"));
ТабДок.Присоединить(Макет.ПолучитьОбласть("СтрокаШапки|КолонкаПравая"));

// Выводим левую часть второй строки (Область 2)
ТабДок.Вывести(Макет.ПолучитьОбласть("СтрокаДанных1|КолонкаЛевая"));
// Справа от нее мы ничего не присоединяем — там остается чистое незаполненное пространство

// Выводим третью строку (Область 3 слева и Область 4 справа)
ТабДок.Вывести(Макет.ПолучитьОбласть("СтрокаДанных2|КолонкаЛевая"));
ТабДок.Присоединить(Макет.ПолучитьОбласть("СтрокаДанных2|КолонкаПравая"));

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

Способ №3: Использование временных табличных документов (Метод сборщика)

Бывают сценарии, когда левая и правая части таблицы генерируются динамически на основе разных источников данных с неизвестным заранее количеством строк. Например, слева у нас выводится список сотрудников отдела (может быть 5 человек, а может 50), а справа — график дежурств (совершенно другая таблица).

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

  1. Создаем два независимых временных табличных документа: ТабДокЛевый и ТабДокПравый.
  2. Наполняем каждый документ своими данными независимо друг от друга, используя стандартные методы Вывести() и Присоединить().
  3. Затем объединяем их в один результирующий документ с помощью расчета координат.

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


// Создаем временные буферы
ТабДокЛевый = Новый ТабличныйДокумент;
ТабДокПравый = Новый ТабличныйДокумент;

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

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

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

// Определяем область-приемник для правой части
СтрокаНачала = 2; // Например, сразу под шапкой
КолонкаНачала = ТабДокЛевый.ШиринаТаблицы + 1;
СтрокаКонца = СтрокаНачала + ТабДокПравый.ВысотаТаблицы - 1;
КолонкаКонца = КолонкаНачала + ТабДокПравый.ШиринаТаблицы - 1;

ОбластьПриемник = ТабДок.Область(СтрокаНачала, КолонкаНачала, СтрокаКонца, КолонкаКонца);

// Вставляем правую часть в результат
ТабДок.ВставитьОбласть(ТабДокПравый.Область(), ОбластьПриемник, ТипСмещенияТабличногоДокумента.БезСмещения);

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

Способ №4: Использование рисунков табличного документа

Иногда область, которую нужно вывести сбоку, носит чисто декоративный, пояснительный характер или представляет собой штамп/карточку подписи. Если границы этой области не должны влиять на ширину колонок основной таблицы, выводить ее в сетку ячеек не имеет практического смысла — это лишь усложнит верстку.

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

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


// Получаем область из макета, содержащую рисунок штампа
ОбластьРисунка = Макет.ПолучитьОбласть("ОбластьСоШтампом");
РисунокМакет = ОбластьРисунка.Рисунки.ШтампПечати;

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

// Устанавливаем точные геометрические координаты размещения рисунка на листе
НовыйРисунок.Верх = 120; // Координата Y в миллиметрах
НовыйРисунок.Лево = 150; // Координата X в миллиметрах
НовыйРисунок.Ширина = 50; 
НовыйРисунок.Высота = 30;

Этот подход гарантирует стабильность верстки основного документа и исключает проблемы с нестыковкой ширины колонок в параллельных блоках данных.

Резюме и рекомендации

Мы подробно проанализировали проблему нелинейного вывода областей в табличный документ 1С и изучили лучшие практики разработки. Подведем итоги:

← На главную