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