Как разделить печать каждой номенклатуры в документе на отдельные страницы в 1С?

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

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

Анализ проблемы и текущей реализации

Проанализируем ситуацию, описанную автором. Основная ошибка заключается в архитектуре построения цикла вывода. В исходном коде используется вспомогательная функция ТекстНоменклатура, которая обходит всю таблицу товаров, «склеивает» их наименования в одну длинную строку через запятую и возвращает этот результат в один параметр макета. В итоге, сколько бы позиций ни было в документе, они все попадают в одну область ОбластьЛицевая.Параметры.Пункт3_1.

Вторая проблема — итерация происходит по документам (РТУ или Рейс), а не по строкам товаров. Для того чтобы добиться разделения по страницам, нам необходимо спуститься на уровень ниже — к строкам табличной части. Также автор упомянул условие: если для номенклатуры не указан связанный документ, ее печатать не нужно. Это требует дополнительной фильтрации в запросе или в теле цикла.

Шаг 1: Изменение логики запроса

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

Разберем, как добавить условие фильтрации. Чтобы исключить номенклатуру без привязанного документа, в тексте запроса следует использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ или добавить условие в секцию ГДЕ:


|ГДЕ
|    Рейс.Ссылка = &СсылкаР
|    И НЕ Рейс.Заказы.Документ ЕСТЬ NULL
|    И Рейс.Заказы.Документ <> ЗНАЧЕНИЕ(Документ.РеализацияТоваровУслуг.ПустаяСсылка)

Шаг 2: Рефакторинг цикла вывода областей

Рассмотрим основной алгоритм формирования ТабличныйДокумент. Сейчас код выглядит так: Получить область -> Заполнить параметры всей номенклатурой -> Вывести. Нам нужно изменить его на: Начать цикл по строкам -> Получить область -> Заполнить параметры ОДНОЙ строкой -> Вывести -> Добавить разделитель.

Выясним причину, по которой ТабДок.Очистить() не должен находиться внутри цикла по номенклатуре. Если вызывать метод Очистить() на каждой итерации, то в итоговом печатном документе останется только последний товар, так как все предыдущие листы будут стерты из памяти объекта. Метод Очистить() вызывается один раз перед началом формирования всех страниц.

Шаг 3: Работа с табличным документом и разделителями страниц

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


// Предположим, у нас есть ТЗНоменклатура с данными для печати
КоличествоСтрок = ТЗНоменклатура.Количество();

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

Шаг 4: Особенности двусторонней печати

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

Если бизнес-логика требует, чтобы каждый товар был на отдельном физическом листке (лицевая и оборотная сторона относятся к одному товару), то необходимо следить, чтобы количество страниц на одну номенклатуру всегда было четным. В случае вывода «Лицевая + Оборотная» у вас получается 2 страницы, что корректно для двусторонней печати. Если же выводится только одна сторона, а следующая должна быть на новом листе, придется программно добавлять пустую страницу или отключать двустороннюю печать.

Итоговый пример исправленной функции

Проанализируем, как должен выглядеть итоговый код формирования печатной формы (упрощенно):


Функция Сформировать_ПФ_РаздельнаяПечать(Рейс) Экспорт
    ТабДок = Новый ТабличныйДокумент;
    Макет = ПолучитьМакет("ПФ_MXL_ТранспортнаяНакладная");
    
    // 1. Получаем данные запросом (сразу с фильтрацией пустых документов)
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ ... (все поля) ... 
                   |ИЗ Документ.Рейс.Заказы КАК Заказы 
                   |ГДЕ Заказы.Ссылка = &СсылкаР И Заказы.Документ <> ЗНАЧЕНИЕ(Документ.РеализацияТоваровУслуг.ПустаяСсылка)";
    Запрос.УстановитьПараметр("СсылкаР", Рейс);
    
    Результат = Запрос.Выполнить();
    Выборка = Результат.Выбрать();
    
    ВсегоСтрок = Выборка.Количество();
    ТекущийИндекс = 0;
    
    Пока Выборка.Следующий() Цикл
        ТекущийИндекс = ТекущийИндекс + 1;
        
        // Лицевая сторона
        ОбластьЛицевая = Макет.ПолучитьОбласть("ГоризонтальнаяЛицеваяСторона");
        // Заполняем параметры из Выборка (ИНН, Адреса, Номенклатура конкретной строки)
        ЗаполнитьПараметрыОбласти(ОбластьЛицевая, Выборка); 
        ТабДок.Вывести(ОбластьЛицевая);
        
        // Оборотная сторона
        ОбластьОборотная = Макет.ПолучитьОбласть("ГоризонтальнаяОборотнаяСторона");
        ЗаполнитьПараметрыОбласти(ОбластьОборотная, Выборка);
        ТабДок.Вывести(ОбластьОборотная);
        
        // Разделитель страниц (чтобы следующая номенклатура пошла с нового листа)
        Если ТекущийИндекс < ВсегоСтрок Тогда
            ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
        КонецЕсли;
    КонецЦикла;
    
    Возврат ТабДок;
КонецФункции

Важные рекомендации

  1. Оптимизация параметров: Не создавайте Новый Запрос внутри цикла. Все данные для всех строк номенклатуры должны быть получены одним запросом заранее. Внутри цикла мы только перемещаемся по результатам выборки.
  2. Проверка заполнения: Если требуется не печатать номенклатуру без документа, лучше всего отсечь такие строки еще на уровне запроса (как показано выше). Это избавит от лишних итераций цикла и пустых страниц.
  3. Использование областей: Если в макете есть общие области (например, заголовок с номером рейса), которые одинаковы для всех товаров, вы можете выводить их в начале каждой страницы.
  4. Масштабирование: При большом количестве номенклатуры (сотни позиций) один ТабличныйДокумент может стать очень тяжелым. Однако для транспортных накладных, где позиций обычно немного, этот подход является стандартным.

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

← На главную