Как реализовать отбор по реквизиту табличной части в списке документов в 1С:УПП 1.3

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

При работе в конфигурации 1С:Управление производственным предприятием 1.3 (на платформе 8.3 с использованием обычных форм) программисты часто сталкиваются с ограничением: стандартный механизм отборов в форме списка не позволяет фильтровать данные по реквизитам табличных частей. Это связано с архитектурой хранения данных в СУБД, где документы и их строки находятся в разных физических таблицах. Попытка вывести данные ТЧ через событие ПриВыводеСтроки решает задачу визуализации, но кнопка отбора остается неактивной.

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

Причина ограничения: почему отбор не работает «из коробки»

Разберем ситуацию с технической точки зрения. Когда мы открываем форму списка, система выполняет запрос к таблице документов. Если мы добавим колонку, которая вычисляется динамически (например, берет первую строку из табличной части в момент отрисовки), платформа не может применить к ней индекс или выполнить эффективную фильтрацию на уровне SQL-запроса без использования соединений (JOIN). Именно поэтому стандартный отбор «по значению в текущей колонке» в таких случаях неактивен.

Способ №1: Дублирование реквизита в шапку документа (самый популярный)

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

Шаг 1. Изменение структуры метаданных

Для начала добавим в документ (например, ПлатежноеПоручениеВходящее) новый реквизит СтатьяДДСДляОтбора. Тип должен соответствовать типу реквизита в табличной части (СправочникСсылка.СтатьиДвиженияДенежныхСредств).

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

Шаг 2. Автоматизация заполнения реквизита

Теперь нам нужно гарантировать, что при каждой записи документа новый реквизит будет актуализироваться. Рассмотрим, почему лучше использовать модуль объекта и событие ПередЗаписью.

Проанализируем пример кода, который необходимо разместить в модуле документа:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Проверяем, что запись происходит не в режиме загрузки данных (например, через обмен)
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    // Проверяем наличие строк в табличной части
    Если РасшифровкаПлатежа.Количество() > 0 Тогда
        // Заполняем реквизит шапки значением из первой строки табличной части
        СтатьяДДСДляОтбора = РасшифровкаПлатежа[0].СтатьяДвиженияДенежныхСредств;
    Иначе
        // Если строк нет, очищаем реквизит
        СтатьяДДСДляОтбора = Справочники.СтатьиДвиженияДенежныхСредств.ПустаяСсылка();
    КонецЕсли;

КонецПроцедуры

Использование обработчика ПередЗаписью в модуле объекта критически важно. Это гарантирует заполнение данных при любом способе записи: будь то ручной ввод пользователем, программное создание или использование групповой обработки справочников и документов. Если поместить этот код в форму, он не сработает при записи без открытия формы.

Шаг 3. Обработка исторических данных

После внесения изменений в конфигурацию новый реквизит у старых документов останется пустым. Нам потребуется написать разовую обработку для заполнения этих данных. Разберем пример логики такой обработки:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ПлатежноеПоручениеВходящее.Ссылка КАК Ссылка
    |ИЗ
    |   Документ.ПлатежноеПоручениеВходящее КАК ПлатежноеПоручениеВходящее
    |ГДЕ
    |   ПлатежноеПоручениеВходящее.СтатьяДДСДляОтбора = ЗНАЧЕНИЕ(Справочник.СтатьиДвиженияДенежныхСредств.ПустаяСсылка)";

Результат = Запрос.Выполнить().Выбрать();
Пока Результат.Следующий() Цикл
    ОбъектДок = Результат.Ссылка.ПолучитьОбъект();
    // При вызове Записать() сработает наш код в ПередЗаписью()
    ОбъектДок.Записать(РежимЗаписиДокумента.Запись); 
КонецЦикла;

Способ №2: Использование «Критериев отбора»

Если «корежить» конфигурацию добавлением новых реквизитов в каждый документ не хочется, можно воспользоваться механизмом Критерии отбора. Это специальный объект метаданных, предназначенный как раз для таких задач.

Рассмотрим порядок действий:

  1. В дереве конфигурации найдем ветку Общие -> Критерии отбора.
  2. Создадим новый критерий, например, ПоСтатьеДДС.
  3. На вкладке «Данные» укажем тип — нужный нам справочник статей.
  4. На вкладке «Состав» отметим галочками табличные части всех документов, по которым должен выполняться поиск.

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

Способ №3: Программный отбор через СписокЗначений (для небольших баз)

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

Проанализируем ситуацию: этот метод хорош только в том случае, если выборка документов невелика. Если документов в базе десятки тысяч, отбор Ссылка В Списке будет работать крайне медленно.

Пример программной реализации в обработчике нажатия кнопки:


Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ВыбраннаяСтатья", ПоискСтатьяДДС);
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |   РасшифровкаПлатежа.Ссылка
    |ИЗ
    |   Документ.ПлатежноеПоручениеВходящее.РасшифровкаПлатежа КАК РасшифровкаПлатежа
    |ГДЕ
    |   РасшифровкаПлатежа.СтатьяДвиженияДенежныхСредств = &ВыбраннаяСтатья";

СписокСсылок = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");

// Устанавливаем отбор в элементе формы "Список"
ДокументСписок.Отбор.Ссылка.Использование = Истина;
ДокументСписок.Отбор.Ссылка.ВидСравнения = ВидСравнения.ВСписке;
ДокументСписок.Отбор.Ссылка.Значение.ЗагрузитьЗначения(СписокСсылок);

Сравнение и выводы

Подведем итоги нашего исследования. Мы рассмотрели три основных подхода:

  1. Дублирование в шапку: Самый быстрый способ для пользователя. Отлично работает в УПП на больших объемах данных. Требует аккуратности при групповом проведении (используйте ПередЗаписью). Недостаток — если в документе две разные статьи в ТЧ, отбор найдет документ только по первой.
  2. Критерии отбора: «Чистый» способ с точки зрения архитектуры 1С. Позволяет найти документ по любой строке табличной части. Минус — менее привычный интерфейс для рядового бухгалтера, привыкшего жать правую кнопку мыши прямо в списке.
  3. Программный подзапрос: Хорош для разовых задач, не требует изменения конфигуратора. Главный риск — падение производительности при росте базы.

Выясним причину, почему автор вопроса остановился именно на первом методе: в УПП 1.3 специфика бизнес-процессов часто подразумевает, что одно платежное поручение относится к одной статье ДДС. В этом случае дублирование в шапку с индексацией является «золотым стандартом» разработки, обеспечивающим комфортную работу пользователей без ожидания формирования сложных запросов.

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

← На главную