Как программно распечатать только выделенные строки табличной части документа?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки в среде 1С часто возникает задача: предоставить пользователю возможность распечатать не весь многостраничный документ, а только определенные, выделенные им вручную строки табличной части. На первый взгляд задача кажется простой, но при ее реализации на управляемых формах программисты регулярно сталкиваются с неочевидными архитектурными и техническими сложностями.

В этой статье мы подробно разберем, как правильно получить выделенные строки на клиенте, корректно передать их в модуль менеджера для печати и почему использование индексов вместо идентификаторов может привести к критическим ошибкам в данных.

Разница между идентификаторами и индексами строк (Разбор технической ошибки)

При работе с таблицами на управляемой форме начинающие разработчики часто путают два ключевых понятия: индекс строки и идентификатор строки. Давайте разберем, почему эта путаница опасна.

  1. Идентификатор строки — это уникальное числовое значение, которое платформа присваивает строке таблицы при ее выводе на форму. Он остается строго неизменным на протяжении всего сеанса работы с формой. Даже если пользователь отсортирует строки, переместит их стрелками вверх-вниз или удалит соседние элементы, идентификатор конкретной строки не изменится. Свойство Элементы.ИмяТаблицы.ВыделенныеСтроки возвращает массив именно таких уникальных идентификаторов.
  2. Индекс строки — это порядковый номер строки в коллекции данных (начиная с 0 до Количество - 1). Индексы динамически пересчитываются платформой при любой сортировке, добавлении или сдвиге строк.

В чем заключается ошибка? Если использовать метод Получить(ИдентификаторСтроки), платформа будет ожидать на входе именно индекс. На чистой форме без сортировок это может случайно сработать, так как изначально индексы и идентификаторы часто совпадают. Однако стоит пользователю отсортировать таблицу или переместить строки кнопками на форме, метод Получить() по старому идентификатору вернет совершенно другую строку или вызовет аварийное завершение работы программы с ошибкой "Индекс находится за пределами границы".

Правильное решение: Для поиска строки по ее идентификатору в управляемой форме необходимо использовать исключительно метод НайтиПоИдентификатору(). Рассмотрим корректный пример сбора данных на клиенте:


&НаКлиенте
Процедура ПолучитьВыделенныеСтроки()
    
    МассивИдентификаторов = Элементы.МедОсмотр.ВыделенныеСтроки;
    МассивСтрокДляПечати = Новый Массив;
    
    Для Каждого ИдентификаторСтроки Из МассивИдентификаторов Цикл
        // Находим строку табличной части по уникальному идентификатору формы
        СтрокаТЧ = Объект.МедОсмотр.НайтиПоИдентификатору(ИдентификаторСтроки);
        Если СтрокаТЧ <> Неопределено Тогда
            // Сохраняем физический номер строки (уникальный в рамках документа)
            МассивСтрокДляПечати.Добавить(СтрокаТЧ.НомерСтроки);
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Обратите внимание: мы сохраняем в массив не сами объекты строк (передавать ссылочные данные элементов формы на сервер напрямую не рекомендуется), а их физические номера НомерСтроки, которые хранятся непосредственно в базе данных.

Передача выделенных строк на сервер через параметры печати БСП

Стандартная архитектура печати в конфигурациях на базе Библиотеки стандартных подсистем (БСП) предполагает, что процедура формирования табличного документа выполняется на сервере в модуле менеджера объекта. Метод Печать() модуля менеджера принимает только ссылку на документ и ничего не знает о состоянии формы на клиенте и о том, какие строки сейчас выделены мышкой.

Чтобы подружить клиентское выделение строк и серверную печать, реализуем передачу параметров через структуру дополнительных параметров БСП. Рассмотрим этот процесс по шагам.

Шаг 1. Перехват команды печати на клиенте

В модуле формы документа переопределим или создадим команду печати. Нам нужно собрать номера выделенных строк и поместить их в структуру параметров вывода:


&НаКлиенте
Процедура КомандаПечатьВыделенного(Команда)
    
    // Сбор номеров выделенных строк
    МассивНомеровСтрок = Новый Массив;
    Для Каждого Идентификатор Из Элементы.МедОсмотр.ВыделенныеСтроки Цикл
        СтрокаДанных = Объект.МедОсмотр.НайтиПоИдентификатору(Идентификатор);
        Если СтрокаДанных <> Неопределено Тогда
            МассивНомеровСтрок.Добавить(СтрокаДанных.НомерСтроки);
        КонецЕсли;
    КонецЦикла;
    
    Если МассивНомеровСтрок.Количество() = 0 Тогда
        ПоказатьПредупреждение(, "Не выделено ни одной строки для печати!");
        Возврат;
    КонецЕсли;
    
    // Формируем параметры для БСП
    ПараметрыПечати = Новый Структура;
    ПараметрыПечати.Вставить("ВыделенныеНомераСтрок", МассивНомеровСтрок);
    
    // Вызываем стандартную печать БСП с передачей параметров
    УправлениеПечатьюКлиент.ВыполнитьКомандуПечати(
        "Документ.МедОсмотры", 
        "ПечатьВыделенныхСтрок", 
        Новый МассивСсылок(Объект.Ссылка), 
        ЭтотФорма, 
        ПараметрыПечати
    );
    
КонецПроцедуры

Шаг 2. Прием параметров и фильтрация в модуле менеджера

В модуле менеджера документа найдем (или создадим) процедуру печати. Нам нужно извлечь переданный массив строк и наложить условие фильтрации в запросе формирования печатной формы.


Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
    
    // Проверяем, переданы ли конкретные строки для печати
    МассивСтрок = Неопределено;
    Если ПараметрыПечати <> Неопределено Тогда
        ПараметрыПечати.Свойство("ВыделенныеНомераСтрок", МассивСтрок);
    КонецЕсли;
    
    ТабличныйДокумент = СформироватьПечатнуюФорму(МассивОбъектов, МассивСтрок);
    
    // Заполняем коллекцию печатных форм для вывода пользователю
    // ...
    
КонецПроцедуры

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

Этот способ является наиболее чистым с точки зрения архитектуры БСП, так как он разделяет интерфейсную логику (выбор строк пользователем на клиенте) и бизнес-логику формирования печатного листа на сервере без лишних вызовов контекста формы.

Альтернативное и более надежное решение: Использование флажков («чекбоксов»)

Несмотря на то, что программная работа с выделенными строками полностью решает задачу, у интерактивного выделения строк (через зажатый Shift или Ctrl) есть существенный эргономический недостаток: пользователь может случайно сбросить все выделение одним неловким кликом мыши. Если документ содержит сотни строк, и менеджер долго выбирал нужные позиции, случайный клик вызовет сильное раздражение.

Для повышения надежности и удобства работы пользователей рассмотрим альтернативное решение — добавление специальной колонки отметки (флажка):

  1. Добавим в табличную часть документа реквизит с именем Печать и типом данных Булево.
  2. Выведем этот реквизит на форму в виде флажка. Пользователь сможет спокойно отмечать галочками нужные строки.
  3. При нажатии на кнопку "Печать" нам больше не потребуется собирать сложные идентификаторы на клиенте и передавать их параметрами. Мы можем сразу обратиться к базе данных на сервере и отфильтровать строки.

Запрос в модуле менеджера в таком случае значительно упрощается и выглядит следующим образом:


ВЫБРАТЬ
    МедОсмотры.Ссылка КАК Ссылка,
    МедОсмотры.МедОсмотр.(
        НомерСтроки,
        Сотрудник,
        Результат
    ) КАК МедОсмотр
ИЗ
    Документ.МедОсмотры КАК МедОсмотры
ГДЕ
    МедОсмотры.Ссылка В (&МассивОбъектов)
    И МедОсмотры.МедОсмотр.Печать = ИСТИНА

Использование флажка исключает вероятность случайной потери выделения, делает интерфейс интуитивно понятным и существенно упрощает программный код, избавляя от необходимости связывать тонкого клиента и сервер сложными структурами параметров.

← На главную