В повседневной работе бухгалтера или кадровика часто возникает задача: распечатать документы (например, уведомления об отпуске или дополнительные соглашения) не на всех сотрудников из документа 1С, а только на нескольких выбранных. Стандартный механизм печати в Библиотеке стандартных подсистем (БСП) обычно предполагает передачу ссылки на весь документ целиком, и печатная форма формируется по всем строкам табличной части. Сегодня мы разберем, как обойти это ограничение и создать внешнюю печатную форму (ВПФ), которая будет «видеть», какие именно строки выделил пользователь.
Разберем ситуацию: когда пользователь нажимает кнопку «Печать», система вызывает экспортную процедуру Печать в модуле объекта обработки. В эту процедуру передается массив ссылок на документы. На этом этапе информация о том, на какой именно строке стоял курсор или какие строки были подсвечены синим цветом, уже потеряна. Объект ДокументСсылка содержит все данные документа, и штатный запрос выберет все записи из табличной части. Чтобы решить нашу задачу, нам нужно «перехватить» контекст формы документа до того, как управление уйдет на сервер для формирования печати.
Самым элегантным и правильным с точки зрения архитектуры решением является создание внешней печатной формы, которая открывает вспомогательную форму-посредник. Рассмотрим этот процесс пошагово.
Для начала нам необходимо правильно зарегистрировать нашу обработку в системе. В функции СведенияОВнешнейОбработке мы должны указать специальный тип команды — ОткрытиеФормы. Проанализируем пример заполнения регистрации:
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
НоваяКоманда.Представление = "Печать по выбранным строкам (Word)";
НоваяКоманда.Идентификатор = "ПечатьПоВыделеннымСтрокам";
НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыОткрытиеФормы();
НоваяКоманда.ПоказыватьОповещение = Истина;
Возврат ПараметрыРегистрации;
КонецФункции
Использование типа ТипКомандыОткрытиеФормы() заставляет 1С при нажатии кнопки печати не запускать формирование макета сразу, а открыть основную форму вашей внешней обработки.
Теперь перейдем к самому ответственному моменту — написанию кода в модуле формы ВПФ. Когда форма нашей обработки открывается из формы документа, у нее автоматически заполняется свойство ВладелецФормы. Это и есть ключ к решению. Мы можем обратиться к элементам формы документа «График отпусков» напрямую.
Рассмотрим, как собрать идентификаторы выделенных строк в процедуре ПриОткрытии или в обработчике команды на форме:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Проверяем, что форма открыта из другого объекта
Если ВладелецФормы = Неопределено Тогда
Возврат;
КонецЕсли;
// Допустим, в документе ГрафикОтпусков табличная часть называется "ОчередностьОтпусков"
// Нам нужно обратиться к элементу формы (таблице), чтобы узнать выделенные строки
Попытка
ТаблицаДокумента = ВладелецФормы.Элементы.ОчередностьОтпусков;
Исключение
Сообщить("Не удалось найти таблицу с сотрудниками на форме владельца.");
Отказ = Истина;
Возврат;
КонецПопытки;
МассивИдентификаторов = ТаблицаДокумента.ВыделенныеСтроки;
Если МассивИдентификаторов.Количество() = 0 Тогда
Сообщить("Пожалуйста, выделите хотя бы одну строку в списке!");
Отказ = Истина;
Возврат;
КонецЕсли;
// Подготавливаем данные для передачи на сервер
ДанныеДляПечати = ПолучитьДанныеВыделенныхСтрок(МассивИдентификаторов);
// Далее вызываем процедуру формирования Word-файла
СформироватьДокументWord(ДанныеДляПечати);
// После выполнения печати закрываем форму-посредник, чтобы пользователь ее не видел
Отказ = Истина;
КонецПроцедуры
Проанализируем коллекцию ВыделенныеСтроки. Важно понимать, что она содержит не индексы строк (0, 1, 2...) и не сами данные, а внутренние идентификаторы строк элементов формы. Чтобы получить реальные данные из табличной части, нам нужно использовать метод НайтиПоИдентификатору(). Рассмотрим пример функции, работающей на сервере:
&НаСервере
Функция ПолучитьДанныеВыделенныхСтрок(МассивID)
// Получаем объект документа через ВладельцаФормы (в управляемом приложении)
// ВладелецФормы.Объект - это ДанныеФормыСтруктура
ДокументОбъект = ВладелецФормы.Объект;
ТаблицаСотрудников = ДокументОбъект.ОчередностьОтпусков;
ВыбранныеДанные = Новый Массив;
Для Каждого ТекущийID Из МассивID Цикл
// Находим строку по ID
СтрокаТЧ = ТаблицаСотрудников.НайтиПоИдентификатору(ТекущийID);
Если СтрокаТЧ <> Неопределено Тогда
// Собираем структуру с нужными реквизитами (Сотрудник, Дата начала и т.д.)
Сведения = Новый Структура;
Сведения.Вставить("Сотрудник", СтрокаТЧ.Сотрудник);
Сведения.Вставить("ДатаНачала", СтрокаТЧ.ДатаНачала);
Сведения.Вставить("ДатаОкончания", СтрокаТЧ.ДатаОкончания);
ВыбранныеДанные.Добавить(Сведения);
КонецЕсли;
КонецЦикла;
Возврат ВыбранныеДанные;
КонецФункции
Поскольку автор вопроса планирует печатать шаблон Word, выясним, как правильно организовать вывод нескольких сотрудников. Если мы будем открывать новый экземпляр Word для каждой строки, это будет работать крайне медленно. Правильнее будет использовать один документ и добавлять данные последовательно, разделяя их разрывами страниц.
Посмотрим на пример логики формирования через COM-объект:
&НаКлиенте
Процедура СформироватьДокументWord(ДанныеДляПечати)
Попытка
MSWord = Новый COMОбъект("Word.Application");
Исключение
Сообщить("Word не установлен на данном компьютере!");
Возврат;
КонецПопытки;
// Путь к шаблону, который хранится в макете ВПФ
// Предварительно его нужно сохранить во временный файл
ПутьКШаблону = ПолучитьИмяВременногоФайла(".docx");
ДанныеМакета = ПолучитьМакетНаСервере(); // Функция получает макет из обработки
ДанныеМакета.Записать(ПутьКШаблону);
Документ = MSWord.Documents.Add(ПутьКШаблону);
MSWord.Visible = Истина;
Для Каждого СтрДанных Из ДанныеДляПечати Цикл
// Используем механизм поиска и замены или закладки
// Пример с поиском и заменой:
Замена = Документ.Content.Find;
Замена.Execute("[ФИО_Сотрудника]", Ложь, Истина, Ложь, , , Истина, 1, , Строка(СтрДанных.Сотрудник), 2);
// ВАЖНО: Если сотрудников несколько, нужно копировать содержимое шаблона
// или использовать InsertBreak для перехода на новую страницу
Если ДанныеДляПечати.Индекс(СтрДанных) < ДанныеДляПечати.Количество() - 1 Тогда
MSWord.Selection.EndKey(6); // Перейти в конец документа
MSWord.Selection.InsertBreak(7); // Вставить разрыв страницы (wdSectionBreakNextPage = 7)
// Далее логика вставки нового текста из шаблона...
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Рассмотрим другие варианты, если способ с ВладелецФормы кажется вам сложным или нестабильным (например, если фокус с таблицы часто слетает):
Булево. Пользователь просто отмечает нужные строки. В этом случае обычная ВПФ без всяких форм-посредников просто делает запрос к табличной части с условием ГДЕ Печатать = ИСТИНА. Это самый надежный способ, но он требует внесения изменений в структуру данных через расширение.ПараметрыПечати. БСП умеет прокидывать эти параметры в модуль объекта ВПФ.Для реализации печати по выделенным строкам в управляемых формах 1С (особенно в ЗУП 3.1) наиболее эффективным является путь создания ВПФ с собственной формой. Мы выяснили, что:
ТипКомандыОткрытиеФормы() в сведениях о регистрации.ВладелецФормы.Элементы.ИмяТаблицы.ВыделенныеСтроки дает доступ к выбранным данным.НайтиПоИдентификатору().InsertBreak для пакетного формирования документов.Используя этот подход, вы сможете значительно повысить удобство работы пользователей, избавив их от необходимости распечатывать лишние страницы или создавать отдельные документы на каждого сотрудника.