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