В практике разработки на платформе 1С:Предприятие 8.3 (особенно при работе с обычными формами) часто возникает задача расширить стандартный список документов дополнительной информацией. Например, вывести статус из регистра сведений или данные из связанного бизнес-процесса. Если мы добавляем такую колонку программно и заполняем её "на лету", стандартный механизм отбора перестает работать. В этой статье мы подробно разберем, почему так происходит и какими способами можно решить эту задачу.
Для начала проанализируем ситуацию. Когда мы используем объект ДокументСписок в обычных формах, система работает напрямую с базой данных через SQL-запросы. Программное добавление колонки в ЭлементыФормы.Список.Колонки создает лишь визуальный элемент в интерфейсе. Рассмотрим типичный код добавления колонки, который часто используют разработчики:
Колонка = ЭлементыФормы.Список.Колонки.Найти(ПослеКолонкиИмя);
НомерКолонки = ЭлементыФормы.Список.Колонки.Индекс(Колонка);
НоваяКолонка = ЭлементыФормы.Список.Колонки.Вставить(НомерКолонки + 1, "Статус");
НоваяКолонка.Имя = "Статус";
НоваяКолонка.ТекстШапки = "Статус документа";
Далее разработчик обычно заполняет эту колонку в событии ПриВыводеСтроки или ПриПолученииДанных:
Процедура СписокПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
Запись = РегистрыСведений.СтатусыДокументов.Получить(Новый Структура("Документ", ДанныеСтроки.Ссылка));
ОформлениеСтроки.Ячейки.Статус.УстановитьТекст(Запись.Статус);
КонецПроцедуры
Выясним причину проблемы: стандартный отбор 1С пытается найти поле "Статус" в таблице базы данных (SQL), но его там не существует. Оно существует только в памяти формы для тех 20-30 строк, которые видны на экране. Именно поэтому попытка включить НастройкаОтбора.Статус.Доступность = Истина либо вызывает ошибку "Поле объекта не обнаружено", либо приводит к пустому результату списка.
Это самый популярный и относительно простой способ обойти ограничение. Суть метода заключается в том, чтобы сначала самостоятельно найти все документы, подходящие под условие, а затем наложить стандартный отбор по полю Ссылка с видом сравнения ВСписке.
Рассмотрим пошаговый алгоритм реализации этого метода:
Проанализируем пример кода для реализации такого отбора:
Процедура ПолеОтбораСтатусПриИзменении(Элемент)
// 1. Формируем запрос для поиска документов с нужным статусом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СтатусыДокументов.Документ КАК Ссылка
|ИЗ
| РегистрСведений.СтатусыДокументов КАК СтатусыДокументов
|ГДЕ
| СтатусыДокументов.Статус = &ВыбранныйСтатус";
Запрос.УстановитьПараметр("ВыбранныйСтатус", Элемент.Значение);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
МассивСсылок = РезультатЗапроса.ВыгрузитьКолонку("Ссылка");
// 2. Устанавливаем отбор по полю Ссылка
Если МассивСсылок.Количество() > 0 Тогда
Список.Отбор.Ссылка.ВидСравнения = ВидСравнения.ВСписке;
Список.Отбор.Ссылка.Значение = МассивСсылок;
Список.Отбор.Ссылка.Использовать = Истина;
Иначе
// Если ничего не найдено, можно либо очистить список, либо вывести пустое условие
Список.Отбор.Ссылка.ВидСравнения = ВидСравнения.Равно;
Список.Отбор.Ссылка.Значение = Справочники.Документы.ПустаяСсылка();
Список.Отбор.Ссылка.Использовать = Истина;
КонецЕсли;
КонецПроцедуры
Важный нюанс: этот метод отлично работает, если количество документов, попадающих в фильтр, не превышает нескольких тысяч. Если же список ссылок будет слишком огромным, SQL-запрос может стать неоптимальным из-за слишком длинного условия IN (...).
Если вам нужен полноценный, гибкий отбор, который ведет себя так же, как стандартный, нам придется отказаться от использования ДокументСписок как основного источника данных и перейти на использование ПостроителяЗапроса.
Разберем, как это работает. ПостроительЗапроса позволяет динамически управлять текстом запроса, включая условия ГДЕ и соединения таблиц. Мы можем связать основную таблицу документов с регистром сведений прямо в тексте запроса. В этом случае колонка "Статус" станет для системы "родной", и по ней можно будет строить любые индексы и фильтры.
Посмотрим на пример настройки такого подхода:
// В процедуре ПриОткрытии или инициализации формы
Построитель = Новый ПостроительЗапроса;
Построитель.Текст =
"ВЫБРАТЬ
| Док.Ссылка,
| Док.Дата,
| Док.Номер,
| ЕСТЬNULL(Статусы.Статус, ""Не установлен"") КАК Статус
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Док
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СтатусыДокументов КАК Статусы
| ПО Док.Ссылка = Статусы.Документ";
Построитель.ЗаполнитьНастройки();
// Теперь поле "Статус" доступно в Построитель.Отбор
Преимущество этого метода в том, что вы можете выводить настройки отбора построителя на форму, и пользователь сможет сам выбирать условия (равно, не равно, в списке и т.д.). Однако стоит помнить, что использование построителя запроса вместо динамического списка требует ручного управления обновлением данных и пагинацией (если список очень большой).
Проанализируем ситуацию с точки зрения производительности. Если отбор по полю "Статус" требуется пользователям постоянно, а объем данных в базе исчисляется сотнями тысяч записей, любые "костыли" с программным добавлением колонок будут тормозить интерфейс.
Рекомендуется следующий подход:
В этом случае задача решается автоматически: колонка сама появится в настройках списка, отбор будет работать на уровне индексов СУБД, а код формы станет чище и проще. Мы избавляемся от необходимости использовать тяжелые события ПриВыводеСтроки, что значительно ускоряет прокрутку списка пользователем.
Подводя итог, можно сказать: программно добавить колонку для отображения данных в обычных формах несложно, но сделать по ней стандартный отбор невозможно из-за архитектуры ДокументСписок.
Мы рассмотрели три пути решения:
Выбирайте метод, исходя из требований к производительности и гибкости вашей системы.