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