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