Как отфильтровать табличную часть для печати в 1С?

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

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

Основная проблема, с которой сталкиваются разработчики, — это путаница между визуальным представлением данных на форме и фактической обработкой данных на сервере при формировании печатной формы. То, что пользователь видит на экране, и то, с чем работает процедура печати, — это разные вещи. Попытки "скрыть" строки на форме, чтобы они не попали в печать, обречены на провал.

Почему просто скрыть строки на форме недостаточно?

В начале пути разработчик может предположить, что у строки табличной части есть некое свойство, отвечающее за ее видимость, например, Строка.Видимость = Ложь. Это неверно. У объектов данных (строк табличной части документа) нет свойств, управляющих их отображением на форме. За отображение отвечают элементы формы, а не сами данные.

Для фильтрации данных непосредственно на форме существует специальный механизм — свойство ОтборСтрок у элемента формы "Таблица". Посмотрим на пример кода, который применяет визуальный фильтр:


// ФильтрТорговыйПредставитель - реквизит формы с выбранным сотрудником
Отбор = Новый Структура("Ответственный", ФильтрТорговыйПредставитель); 

// Применяем отбор к элементу формы "Заказы", который отображает табличную часть
Элементы.Заказы.ОтборСтрок = Новый ФиксированнаяСтруктура(Отбор);

Этот код отлично работает для интерфейса. Пользователь на форме увидит только те строки, где в колонке "Ответственный" указан выбранный сотрудник. Однако это чисто визуальный эффект. Сам объект документа в памяти по-прежнему содержит все строки своей табличной части. Когда мы вызовем команду печати, она будет работать с полным набором данных, и в печатную форму попадут все строки, а не только отфильтрованные на экране.

Попытка отбора через `НайтиСтроки` и ее последствия

Следующий логичный шаг — попробовать отобрать нужные строки программно. Для этого в 1С есть метод НайтиСтроки(). Он действительно находит строки, соответствующие условию, но возвращает их в виде массива.


Отбор = Новый Структура("Ответственный", ФильтрТорговыйПредставитель); 
	
// В переменной ОтфильтрованнаяТЧ будет массив строк табличной части, 
// а не новая отфильтрованная табличная часть
ОтфильтрованнаяТЧ = Объект.Заказы.НайтиСтроки(Отбор);

И здесь возникает главный вопрос, который поставил в тупик автора на форуме: что делать с этим массивом ОтфильтрованнаяТЧ? Как передать его в процедуру печати, которая, скорее всего, находится в модуле внешней печатной формы и ничего не знает об этой переменной? Можно придумать сложные схемы с сохранением этого массива, но это не является правильной архитектурой в 1С.

Передача целого массива данных с клиента на сервер для печати — неоптимальный подход. Это создает лишнюю нагрузку и усложняет код в модуле печати, где пришлось бы обходить этот массив циклом вместо удобной обработки результата запроса.

Правильный подход: передача параметров отбора в печатную форму

Самое правильное, производительное и гибкое решение — передавать в процедуру печати не сами отфильтрованные данные, а условия для их отбора.

Рассмотрим алгоритм по шагам:

  1. На форме документа размещаем реквизит для фильтра (например, поле для выбора ответственного сотрудника).
  2. При изменении этого реквизита применяем визуальный фильтр через Элементы.ИмяТаблицы.ОтборСтрок, чтобы пользователь сразу видел результат.
  3. В модуле команды печати мы не пытаемся получить отфильтрованные строки. Вместо этого мы считываем значение из нашего реквизита-фильтра.
  4. Это значение (условие отбора) мы передаем в процедуру формирования печатной формы в качестве параметра.
  5. В модуле печатной формы (например, во внешней обработке) мы получаем этот параметр и используем его в условии ГДЕ нашего запроса к базе данных.

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

Посмотрим на пример.

Допустим, на форме документа есть реквизит ФильтрОтветственный.

В модуле команды печати на форме документа (или в общем модуле) мы готовим параметры для передачи:


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

Теперь в модуле менеджера внешней печатной формы, в процедуре Печать(), мы можем получить эти параметры и использовать их в запросе.


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

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


Запрос.Текст =
"ВЫБРАТЬ
|    // ... поля ...
|ИЗ
|    // ... таблица ...
|ГДЕ
|    Документ1Заказы.Ссылка В (&МассивОбъектов)
|    И (&ИспользоватьОтборПоОтветственному = ЛОЖЬ
|            ИЛИ Документ1Заказы.Документ2.Ответственный = &Ответственный)";
	
Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
Запрос.УстановитьПараметр("Ответственный", ОтветственныйФильтр);
// Устанавливаем флаг, нужно ли вообще применять отбор
Запрос.УстановитьПараметр("ИспользоватьОтборПоОтветственному", ЗначениеЗаполнено(ОтветственныйФильтр));

Этот подход является стандартом в разработке на платформе 1С. Он обеспечивает высокую производительность, читаемость кода и гибкость, позволяя легко добавлять новые условия фильтрации в будущем.

← На главную