В разработке на платформе 1С:Предприятие 8.2 и 8.3 при использовании обычных форм программисты часто сталкиваются с ограничением стандартного механизма отборов. Когда источником данных ТабличноеПоле является ДокументТабличнаяЧасть, платформа позволяет устанавливать фильтры только по тем реквизитам, которые физически существуют в объекте метаданных. Однако на практике нам часто требуется выводить расчетные данные (например, остатки, цены со скидками или статусы), которые вычисляются "на лету" в событии ПриВыводеСтроки. Рассмотрим подробнее, как обойти это ограничение и предоставить пользователю полноценный инструмент фильтрации.
Проанализируем ситуацию: механизм Отбор у объекта ДокументТабличнаяЧасть работает на уровне обращения к базе данных или к коллекции строк документа. Поскольку расчетные поля существуют только в визуальном представлении (в ячейках ОформлениеСтроки), система просто "не знает" об их содержимом в момент применения фильтра. Попытка изменить тип ТабличноеПоле на ТаблицаЗначений часто приводит к тому, что кнопка "Установить отбор" становится неактивной. Выясним причину: для активации штатного диалога отбора по ТаблицаЗначений требуется сложная программная обвязка или использование вспомогательных объектов.
Для реализации полноценного отбора мы применим стратегию подмены источника данных. Вместо прямой связи с табличной частью документа, мы будем использовать временную ТаблицаЗначений, в которую добавим физические колонки для всех наших расчетных данных. Разберем этот процесс по шагам.
Шаг 1. Подготовка структуры данных
Нам необходимо создать на форме переменную или реквизит формы с типом ТаблицаЗначений. В эту таблицу нужно добавить все колонки, которые есть в табличной части, плюс дополнительные колонки для расчетных данных. Рассмотрим пример инициализации в коде:
// В модуле формы создадим переменную для хранения данных
Перем ТЗ_Данные;
Процедура ПриОткрытии()
// Создаем таблицу значений на основании ТЧ документа
ТЗ_Данные = Товары.Выгрузить();
// Добавляем колонку для расчетного поля, по которому нужен отбор
ТЗ_Данные.Колонки.Добавить("ОстатокНаСкладе", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
ТЗ_Данные.Колонки.Добавить("ЦенаСоСкидкой", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
// Заполняем расчетные данные один раз при открытии
Для Каждого СтрокаТЗ Из ТЗ_Данные Цикл
СтрокаТЗ.ОстатокНаСкладе = ПолучитьОстатокПоТовару(СтрокаТЗ.Номенклатура);
СтрокаТЗ.ЦенаСоСкидкой = СтрокаТЗ.Цена * 0.9; // Пример расчета
КонецЦикла;
// Связываем ТабличноеПоле на форме с нашей ТЗ
ЭлементыФормы.ТабличноеПолеТовары.Значение = ТЗ_Данные;
ЭлементыФормы.ТабличноеПолеТовары.СоздатьКолонки();
КонецПроцедуры
Шаг 2. Использование объекта ПостроительОтчета для фильтрации
Чтобы предоставить пользователю удобный интерфейс (такой же, как в отчетах), мы воспользуемся объектом ПостроительОтчета. Это мощный инструмент, который умеет фильтровать и сортировать коллекции ТаблицаЗначений. Посмотрим на пример настройки:
Процедура НастроитьПостроитель()
Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТЗ_Данные);
// Теперь в Построитель.Отбор доступны все колонки нашей ТЗ
// Можно вывести этот отбор на форму в виде другого ТабличногоПоля
ЭлементыФормы.ТабличноеПолеОтбор.Значение = Построитель.Отбор;
КонецПроцедуры
Процедура ПрименитьФильтр()
// Выполняем отбор и получаем результат
Построитель.Выполнить();
ТЗ_Результат = Построитель.Результат.Выгрузить();
// Обновляем данные на форме
ЭлементыФормы.ТабличноеПолеТовары.Значение = ТЗ_Результат;
КонецПроцедуры
Важный нюанс при работе с копией данных в ТаблицаЗначений — необходимость синхронизации изменений обратно в документ. Если пользователь отредактирует количество или цену в табличном поле, эти данные должны попасть в ЭтотОбъект.Товары. Рассмотрим, как это сделать правильно, чтобы не потерять данные и сохранить свойство Модифицированность формы.
Проанализируем алгоритм синхронизации:
ТабличноеПоле (событие ПриИзменении или ПослеУдаления), мы должны обновить соответствующую строку в основной табличной части документа.
Процедура ТабличноеПолеТоварыПриИзменении(Элемент)
// Помечаем форму как измененную
ЭтаФорма.Модифицированность = Истина;
// Если нужно, пересчитываем расчетные колонки для текущей строки
ТекущаяСтрока = ЭлементыФормы.ТабличноеПолеТовары.ТекущиеДанные;
Если ТекущаяСтрока <> Неопределено Тогда
ТекущаяСтрока.ЦенаСоСкидкой = ТекущаяСтрока.Цена * 0.9;
КонецЕсли;
КонецПроцедуры
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Синхронизируем ТЗ обратно в ТЧ документа перед сохранением
Товары.Загрузить(ТЗ_Данные);
КонецПроцедуры
В исходном сообщении упоминалось использование ПриВыводеСтроки. Если данных много, постоянные расчеты в этом событии могут замедлить прокрутку списка. Разберем более современный подход — использование события ПриПолученииДанных. Это событие вызывается один раз для целой "порции" строк, отображаемых на экране. Это позволяет выполнить один пакетный запрос к базе данных (например, для получения остатков по всем товарам в списке сразу), вместо десятков мелких запросов.
Посмотрим на пример логики:
ОформленияСтрок.Подведем итог нашего анализа. Для реализации качественного отбора по расчетным полям в обычных формах 1С 8.2/8.3 лучше всего использовать комбинацию ТаблицаЗначений + ПостроительОтчета. Это дает пользователю гибкость (виды сравнения "В списке", "Между", "Содержит"), а разработчику — полный контроль над данными.
Основные преимущества такого подхода:
Рассмотренный метод является "золотым стандартом" для сложных интерфейсов в конфигурациях типа УПП или КА 1.1, где переход на управляемые формы невозможен или нецелесообразен.