При разработке печатных форм документов, отчетов или обработок в 1С часто возникает задача оптимизации пространства на листе. Типичный пример: в отчете или печатной форме заложен календарный месяц (31 день), но данные есть только за несколько дней. Выводить все 31 колонку нецелесообразно, так как пустые столбцы делают таблицу громоздкой, нечитаемой и затрудняют вывод на печать. В этой статье мы подробно разберем, как решить эту проблему в обычных формах (без использования механизма компоновки данных — СКД) и скрыть пустые колонки в готовом табличном документе.
Проанализируем ситуацию: у нас есть макет печатной формы, в котором жестко задана структура колонок на 31 день. Нам необходимо определить, в каких из этих дней отсутствуют данные, и убрать эти колонки из итогового документа. Рассмотрим несколько вариантов реализации этой задачи — от простых и быстрых до наиболее архитектурно правильных.
Это самый быстрый и часто используемый разработчиками способ. Его суть заключается в том, что после формирования всего табличного документа мы находим области пустых колонок и программно устанавливаем их ширину равной нулю. При этом визуально колонка полностью исчезает с экрана и не выводится на печать.
Для реализации этого метода мы используем объект ТабличныйДокумент и его метод Область(). Посмотрим на пример кода, который скрывает конкретную колонку:
// НомерКолонки — это индекс столбца в сформированном Табличном Документе
НомерКолонки = 5; // Например, скрываем 5-й день
Область = ТабДок.Область(1, НомерКолонки, ТабДок.ВысотаТаблицы, НомерКолонки);
Область.Ширина = 0;
Преимущества метода: простота реализации. Не требуется менять логику построения самого макета.
Недостатки: при установке ширины в ноль в некоторых версиях платформы или при определенных настройках отображения могут оставаться видимыми границы ячеек (сетка), что визуально портит печатную форму. Кроме того, при экспорте в Excel такие колонки все равно физически будут присутствовать в файле, просто в скрытом виде.
В платформе 1С для объектов типа ОбластьЯчеекТабличногоДокумента существует штатное свойство Видимость. Это более надежный инструмент, чем простое зануление ширины, поскольку он корректно обрабатывает границы смежных ячеек и полностью исключает колонку из структуры отображения.
Разберем по шагам, как применить это свойство ко всей вертикальной колонке сверху донизу. Обратите внимание, что в методе Область() мы можем не указывать номера строк (оставить их пустыми), чтобы получить область, состоящую из всей колонки целиком:
// Получаем область всей колонки без привязки к конкретным строкам
ОбластьКолонки = ТабДок.Область(, НомерКолонки, , НомерКолонки);
ОбластьКолонки.Видимость = Ложь;
Этот вариант является более чистым с точки зрения платформенных стандартов 1С. Колонка перестает влиять на ширину печатного листа, а сетка таблицы перестраивается автоматически без деформаций.
Если вам требуется, чтобы пустые колонки не просто скрывались, а полностью удалялись из результирующего табличного документа (что особенно критично при последующем сохранении в XLS/PDF), можно применить метод УдалитьОбласть(). При этом оставшиеся колонки автоматически сдвинутся влево.
Выясним, как правильно написать такой код:
// Определяем область колонки, которую нужно удалить
ОбластьДляУдаления = ТабДок.Область(, НомерКолонки, , НомерКолонки);
// Удаляем область с горизонтальным смещением табличного документа
ТабДок.УдалитьОбласть(ОбластьДляУдаления, ТипСмещенияТабличногоДокумента.ПоГоризонтали);
Важное примечание: при физическом удалении колонок в цикле обязательно нужно производить обход колонок справа налево (то есть от большего номера к меньшему). В противном случае после удаления первой же колонки индексы всех последующих колонок сместятся, и вы удалите не те столбцы либо вызовете ошибку выхода за границы индекса.
Частая ошибка начинающих разработчиков — написание 31 конструкции Если...Тогда для проверки каждого дня отдельно. Это приводит к раздуванию кода и усложнению поддержки. Проанализируем, как обойти эту проблему элегантно с помощью предварительного анализа данных в ТаблицеЗначений.
Рассмотрим алгоритм динамического определения пустых колонок:
ТаблицуЗначений.ТабличномДокументе.Посмотрим на пример реализации такого алгоритма:
// Предположим, ТаблицаДанных - это ТЗ с результатами нашего запроса
// Шаг 1. Создаем массив имен колонок, которые нужно проверить
МассивПустыхКолонок = Новый Массив;
Для День = 1 По 31 Цикл
МассивПустыхКолонок.Добавить("День" + День);
КонецЦикла;
// Шаг 2. Исключаем из массива те колонки, где есть хотя бы одно значение
Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
Для Индекс = -МассивПустыхКолонок.ВГруппа() По 0 Цикл
// Обходим с конца, чтобы безопасно удалять элементы из массива во время итерации
ОбратныйИндекс = -Индекс;
ИмяКолонки = МассивПустыхКолонок[ОбратныйИндекс];
Если ЗначениеЗаполнено(СтрокаДанных[ИмяКолонки]) Тогда
МассивПустыхКолонок.Удалить(ОбратныйИндекс);
КонецЕсли;
КонецЦикла;
КонецЦикла;
// В МассивПустыхКолонок остались только те имена, по которым нет данных во всей ТЗ
// Теперь при выводе в ТабДок мы можем скрывать соответствующие области
Самым профессиональным решением является отказ от статического макета на 31 день. Вместо того чтобы выводить заведомо избыточные колонки, а затем мужественно скрывать их или удалять, правильнее изначально конструировать печатную форму только из тех колонок, по которым есть данные.
Для этого в конфигураторе наш макет разбивается не только на горизонтальные строки, но и на вертикальные секции (колонки). Мы создаем:
Аналогичным образом на горизонтальные и вертикальные секции разбиваются и строки данных (например: СтрокаЛево, СтрокаДень, СтрокаПраво).
Рассмотрим подробнее логику вывода документа в коде:
// 1. Получаем список только тех дней (или дат), по которым реально есть данные
СписокДнейСДанными = ПолучитьСписокДнейСДанными(ВыборкаЗапроса);
// 2. Выводим левую фиксированную часть шапки
ОбластьШапкаЛево = Макет.ПолучитьОбласть("ШапкаЛево");
ТабДок.Вывести(ОбластьШапкаЛево);
// 3. Динамически присоединяем только нужные колонки дней в шапку
Для Каждого ТекущийДень Из СписокДнейСДанными Цикл
ОбластьШапкаДень = Макет.ПолучитьОбласть("ШапкаДень");
ОбластьШапкаДень.Параметры.НомерДня = ТекущийДень;
ТабДок.Присоединить(ОбластьШапкаДень); // Метод Присоединить() дописывает область справа
КонецЦикла;
// 4. Присоединяем правую фиксированную часть шапки
ОбластьШапкаПраво = Макет.ПолучитьОбласть("ШапкаПраво");
ТабДок.Присоединить(ОбластьШапкаПраво);
// 5. Выводим строки с данными по аналогичной схеме
Пока ВыборкаСтрок.Следующий() Цикл
// Выводим левую часть строки данных
ОбластьСтрокаЛево = Макет.ПолучитьОбласть("СтрокаЛево");
ЗаполнитьЗначенияСвойств(ОбластьСтрокаЛево.Параметры, ВыборкаСтрок);
ТабДок.Вывести(ОбластьСтрокаЛево);
// Присоединяем только те дни, которые мы определили в начале
Для Каждого ТекущийДень Из СписокДнейСДанными Цикл
ОбластьСтрокаДень = Макет.ПолучитьОбласть("СтрокаДень");
// Получаем значение показателя за конкретный день из выборки
ОбластьСтрокаДень.Параметры.ЗначениеПоказателя = ПолучитьЗначениеЗаДень(ВыборкаСтрок, ТекущийДень);
ТабДок.Присоединить(ОбластьСтрокаДень);
КонецЦикла;
// Присоединяем правую часть строки с итогами
ОбластьСтрокаПраво = Макет.ПолучитьОбласть("СтрокаПраво");
ОбластьСтрокаПраво.Параметры.Итого = ВыборкаСтрок.Итого;
ТабДок.Присоединить(ОбластьСтрокаПраво);
КонецЦикла;
При таком подходе в итоговой таблице никогда не возникнет лишних пустых колонок. Код получается максимально декларативным, чистым и легко масштабируемым. Если в будущем вам потребуется выводить не 31 день, а, например, данные за целый год по неделям, структура алгоритма останется неизменной — поменяется только логика заполнения исходного списка выводимых колонок.