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