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

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

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

Основная сложность заключается в том, что стандартное заполнение идет слева направо, сверху вниз. Но что делать, если заказчик просит заполнять таблицу вертикально по колонкам? То есть сначала заполнить до конца первую колонку сверху вниз, затем перейти к верхней ячейке второй колонки, заполнить её вниз и так далее (такой способ вывода часто называют «змейкой»).

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

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

Колонка 1: Номера 1, 2, 3
Колонка 2: Номера 4, 5, 6
Колонка 3: Номера 7, 8, 9
Колонка 4: Номера 10, 11, 12
Колонка 5: Номера 13, 14

Итоговая печатная форма должна построчно вывести ячейки в таком порядке:

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

---

Метод 1. Буферизация данных и математический пересчет индексов (Рекомендуемый)

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

Проанализируем формулу расчета индекса элемента. Пусть:

Зная текущую строку (от 1 до КолСтрок) и текущую колонку (от 1 до КолКолонок) в цикле вывода, мы можем моментально вычислить индекс нужного элемента в исходном плоском массиве:

Индекс = (НомерКолонки - 1) * КолСтрок + (НомерСтроки - 1)

Разберем реализацию этого алгоритма по шагам в коде 1С:


// Шаг 1. Собираем данные из выборки в плоский массив
МассивДокументов = Новый Массив;
Пока ВыборкаДокументов.Следующий() Цикл
    СтруктураДанных = Новый Структура;
    Если ВыборкаДокументов.КолЭкз > 1 Тогда 
        СтруктураДанных.Вставить("РегНомер", ВыборкаДокументов.РегНомер + " " + Формат(ВыборкаДокументов.КолЭкз, "ЧГ=0") + " экз.");
    Иначе 
        СтруктураДанных.Вставить("РегНомер", ВыборкаДокументов.РегНомер);
    КонецЕсли;
    МассивДокументов.Добавить(СтруктураДанных);
КонецЦикла;

// Шаг 2. Расчет параметров сетки
ВсегоЭлементов = МассивДокументов.Количество();
КолКолонок = 5; // Требование заказчика

Если ВсегоЭлементов = 0 Тогда
    Возврат; // Нечего выводить
КонецЕсли;

// Расчет количества строк с округлением в большую сторону
КолСтрок = Цел(ВсегоЭлементов / КолКолонок);
Если (ВсегоЭлементов % КолКолонок) > 0 Тогда
    КолСтрок = КолСтрок + 1;
КонецЕсли;

// Шаг 3. Построчный вывод со сменой индексов
ОбластьЯчейки = ОбъектМакет.ПолучитьОбласть("ЗначенияТаблРегНом|КолонкаШаблон");

Для НомерСтроки = 1 По КолСтрок Цикл
    Для НомерКолонки = 1 По КолКолонок Цикл
        
        // Математический расчет индекса для вертикального заполнения
        Индекс = (НомерКолонки - 1) * КолСтрок + (НомерСтроки - 1);
        
        Если Индекс < ВсегоЭлементов Тогда
            // Элемент существует — заполняем параметр
            ОбластьЯчейки.Параметры.РегНомер = МассивДокументов[Индекс].РегНомер;
        Иначе
            // Элементов не хватило на полную сетку — выводим пустую ячейку, чтобы не ломать границы таблицы
            ОбластьЯчейки.Параметры.РегНомер = "";
        ИначеЕсли;
        
        // Выводим первую колонку новой строки через Вывести(), остальные — через Присоединить()
        Если НомерКолонки = 1 Тогда
            ТабДок.Вывести(ОбластьЯчейки);
        Иначе
            ТабДок.Присоединить(ОбластьЯчейки);
        КонецЕсли;
        
    КонецЦикла;
КонецЦикла;

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

---

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

Рассмотрим более продвинутый подход, который базируется на интересной особенности платформы 1С: метод Присоединить() умеет работать не только с одиночными областями макета, но и с целыми табличными документами.

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

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


// Шаг 1. Первичный сбор данных во временную коллекцию
МассивДанных = Новый Массив;
Пока ВыборкаДокументов.Следующий() Цикл
    МассивДанных.Добавить(ВыборкаДокументов.РегНомер);
КонецЦикла;

ВсегоЭлементов = МассивДанных.Количество();
КолКолонок = 5;

Если ВсегоЭлементов = 0 Тогда
    Возврат;
КонецЕсли;

// Рассчитываем высоту одной колонки
КолСтрок = Цел(ВсегоЭлементов / КолКолонок) + ?((ВсегоЭлементов % КолКолонок) > 0, 1, 0);

// Шаг 2. Создаем массив временных табличных документов
МассивТабДоков = Новый Массив;
Для НомКолонки = 1 По КолКолонок Цикл
    МассивТабДоков.Добавить(Новый ТабличныйДокумент);
КонецЦикла;

// Шаг 3. Распределяем данные по колонкам вертикально
ОбластьЯчейки = ОбъектМакет.ПолучитьОбласть("ЗначенияТаблРегНом|КолонкаШаблон");

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

// Корректируем высоту пустых ячеек в последней колонке, чтобы рамки таблицы не обрезались
ДлинаПоследнейКолонки = ВсегоЭлементов % КолСтрок;
Если ДлинаПоследнейКолонки > 0 Тогда
    НедостаетСтрок = КолСтрок - ДлинаПоследнейКолонки;
    ОбластьЯчейки.Параметры.РегНомер = "";
    Для Шаг = 1 По НедостаетСтрок Цикл
        МассивТабДоков[КолКолонок - 1].Вывести(ОбластьЯчейки);
    КонецЦикла;
КонецЕсли;

// Шаг 4. Объединяем все колонки в один результирующий ТабличныйДокумент
ТабДок.Вывести(МассивТабДоков[0]); // Выводим первую колонку-ленту целиком
Для НомКолонки = 1 По КолКолонок - 1 Цикл
    ТабДок.Присоединить(МассивТабДоков[НомКолонки]); // Присоединяем последующие колонки справа
КонецЦикла;

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

---

Метод 3. Адресация через метод Область() (Прямая запись по координатам)

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

Суть метода заключается в том, что мы сначала выводим в итоговый документ сетку из абсолютно пустых ячеек, формируя таким образом координатное пространство (строки и колонки). А затем, зная номера строк и колонок для каждого элемента, обращаемся к конкретной ячейке по её адресу через метод Область() и записываем туда значение.

Рассмотрим, как это выглядит на практике:


// Шаг 1. Предварительно выгружаем данные
МассивДанных = Новый Массив;
Пока ВыборкаДокументов.Следующий() Цикл
    МассивДанных.Добавить(ВыборкаДокументов.РегНомер);
КонецЦикла;

ВсегоЭлементов = МассивДанных.Количество();
КолКолонок = 5;
КолСтрок = Цел(ВсегоЭлементов / КолКолонок) + ?((ВсегоЭлементов % КолКолонок) > 0, 1, 0);

// Запоминаем текущую строку в документе перед началом вывода таблицы (смещение)
СмещениеСтрок = ТабДок.ВысотаТаблицы;

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

// Шаг 3. Заполняем ячейки напрямую по вычисленным координатам
Для Индекс = 0 По ВсегоЭлементов - 1 Цикл
    
    // Вычисляем координаты относительно начала нашей таблицы
    ОтносительнаяСтрока = (Индекс % КолСтрок) + 1;
    ОтносительнаяКолонка = Цел(Индекс / КолСтрок) + 1;
    
    // Абсолютные координаты ячейки в итоговом документе
    ИтоговаяСтрока = СмещениеСтрок + ОтносительнаяСтрока;
    ИтоговаяКолонка = ОтносительнаяКолонка; // Если таблица начинается с первой колонки слева
    
    // Получаем область конкретной ячейки
    ТекущаяЯчейка = ТабДок.Область(ИтоговаяСтрока, ИтоговаяКолонка);
    ТекущаяЯчейка.Текст = МассивДанных[Индекс];
    
    // При необходимости можно программно управлять оформлением
    ТекущаяЯчейка.АвтоВысотаСтроки = Истина;
    
КонецЦикла;

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

---

Сравнение методов: какой выбрать?

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

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

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

← На главную