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