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