Как ограничить поля отбора в динамическом списке и ничего не сломать
С чем разработчик может столкнуться при попытке установки ограничений в динамическом списке.
Динамические списки - это один из базовых объектов для разработок на базе 1С.
Однако, долгое время разработчики не имели возможность запрещать пользователям делать отборы по конкретным полям и их реквизитам. Такое бывает необходимо, чтобы не дать возможность “положить” списки со сложными запросами или с большим количеством данных.
И вот с версии 8.3.10 в платформе появился метод УстановитьОграниченияИспользованияВОтборе()
В справке нас интересует этот пункт:
Запрещает использование указанных полей и всех их дочерних полей в настройках отбора. Поля, на которые установлено ограничение, не входят в коллекцию доступных полей. После вызова метода список полей с ограничениями замещается указанным. Вызов метода с пустым списком полей отменяет ранее установленные ограничения
Это означает, что метод можно вызывать повторно, настраивая доступность полей в зависимости от каких-то событий. Но нужно понимать нюансы работы.
Обычно метод применяют в одном событии формы - ПриСозданииНаСервере()
. Когда платформа ещё не приступила к непосредственному открытию формы и всячески к этому готовится. В такие моменты разработчик отбирает у пользователей возможность фильтрации по каким-то полям. А что если менять состав полей несколько раз?
Приведём такой пример. Есть динамический список с документами. Необходимо запретить пользователям делать фильтры по полям, если в шапке формы пустой параметр Организация
. Возьмём для нашего эксперимента форму журнала документов продажи из демо базы ERP 2.4:
Для этого добавим форму в расширение:
Подключимся к обработчику изменения организации:
И вставим такой простой код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
&НаСервере
&После("УстановитьОтборПоОрганизации")
Процедура ЖурналДокументовПродажи_УстановитьОтборПоОрганизации()
ЗапрещенныеВОтбореПоля = Новый Массив;
Если НЕ ЗначениеЗаполнено(Организация) Тогда
ЗапрещенныеВОтбореПоля.Добавить("Подразделение");
КонецЕсли;
СписокДокументыПродажи.УстановитьОграниченияИспользованияВОтборе(ЗапрещенныеВОтбореПоля);
ОтключитьНедоступныеОтборы(СписокДокументыПродажи.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы);
КонецПроцедуры
Обновим базу и откроем форму в режиме предприятия. Сразу видим, что в контекстном меню по полю “Подразделение” нет возможности поиска:
Расширенный поиск не позволяет выбирать подразделение:
Так же отбор недоступен и в форме настройки списка:
А теперь заполним организацию:
И появилась возможность фильтровать в расширенном поиске:
Ну и настройка динамического списка заработала:
А значит теперь мы можем наложить отбор на подразделение. Давайте так и сделаем.
Сейчас в списке доступны три строки с двумя подразделениями:
Установим отбор по подразделению “Дирекция” при помощи Ctrl+Alt+F
Всё хорошо. Но что будет, если мы сейчас очистим организацию в шапке формы, тем самым запретив отбирать по подразделению?
Странно, да? Отбор отключился. Но надпись осталась. Да, её не трудно закрыть крестиком вручную, но пользователю будет непонятно, почему показан отбор, который не работает.
Такое странное поведение платформы можно наблюдать вплоть до 8.3.16. К сожалению, никаким образом на более ранних версиях платформы обойти этот баг исправить не удаётся. Приходится просто отключать отображение состояния просмотра. Если вдруг найдёте другой способ - напишите в комментариях.
Ну хорошо, допустим мы свыклись с тем, что надпись не обновляется и отключили её. Но что с отбором через “Настроить список” ?
Для начала снова выберем организацию в шапке формы и установим отбор по подразделению:
Результат такой:
Хорошо, а теперь очистим организацию в шапке:
Мы установили ограничение на отбор, но существующий при этом не сбросился. А в настройке списка наш отбор помечен красным и более недоступен:
Как же так? Понять это поможет другая статья, в которой рассказывается принцип того, как СКД (на которой и базируются динамические списки) работает с ограничениями полей: Ограничения полей, или как обмануть СКД
Прочитав её, станет понятно, что ограничения полей в отборах полноценно влияют только на пользовательскую доступность полей, но не на логику выборки данных. То есть, пользовательские настройки обходят ограничения полей.
Интересно, что решение, которое было предложено в статье, в данной ситуации не работает. Если использовать КомпоновщикНастроек.Восстановить()
, то пользовательские отборы всё равно остаются на месте.
Попробуем же программно отключить использование отборов, если их полей нет в коллекции ДоступныеПоляОтбора.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
&НаСервере
&После("УстановитьОтборПоОрганизации")
Процедура ЖурналДокументовПродажи_УстановитьОтборПоОрганизации()
ЗапрещенныеВОтбореПоля = Новый Массив;
Если НЕ ЗначениеЗаполнено(Организация) Тогда
ЗапрещенныеВОтбореПоля.Добавить("Подразделение");
КонецЕсли;
СписокДокументыПродажи.УстановитьОграниченияИспользованияВОтборе(ЗапрещенныеВОтбореПоля);
ОтключитьНедоступныеОтборы(СписокДокументыПродажи.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ОтключитьНедоступныеОтборы(Коллекция, ДоступныеПоляОтбора = Неопределено)
Для Каждого ТекущиеДанные Из Коллекция Цикл
Если ТипЗнч(ТекущиеДанные) = Тип("ОтборКомпоновкиДанных") Тогда
ОтключитьНедоступныеОтборы(ТекущиеДанные.Элементы, ТекущиеДанные.ДоступныеПоляОтбора);
ИначеЕсли ТипЗнч(ТекущиеДанные) = Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
ОтключитьНедоступныеОтборы(ТекущиеДанные.Элементы, ДоступныеПоляОтбора);
ИначеЕсли ТипЗнч(ТекущиеДанные) = Тип("ЭлементОтбораКомпоновкиДанных") Тогда
Если НЕ ТекущиеДанные.Использование Тогда
Продолжить;
КонецЕсли;
Для Каждого ИмяСвойства Из СтрРазделить("ЛевоеЗначение,ПравоеЗначение",",") Цикл
ТекущееПоле = ТекущиеДанные[ИмяСвойства];
Если ТипЗнч(ТекущееПоле) = Тип("ПолеКомпоновкиДанных")
И ДоступныеПоляОтбора.НайтиПоле(ТекущееПоле) = Неопределено Тогда
ТекущиеДанные.Использование = Ложь;
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Теперь когда пользователь очищает поле “Организация”, то и наш “неправильный” отбор отключается.
Можно заметить, что при этом поле остаётся. И самое интересное, что если пользователь вручную нажмёт его использование, то и, несмотря на ограничения отборов, фильтрация применится:
Но что же делать? Придётся не отключать использование отбора, а просто удалять его.
Подправим код в расширении:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
&НаСервере
&После("УстановитьОтборПоОрганизации")
Процедура ЖурналДокументовПродажи_УстановитьОтборПоОрганизации()
ЗапрещенныеВОтбореПоля = Новый Массив;
Если НЕ ЗначениеЗаполнено(Организация) Тогда
ЗапрещенныеВОтбореПоля.Добавить("Подразделение");
КонецЕсли;
СписокДокументыПродажи.УстановитьОграниченияИспользованияВОтборе(ЗапрещенныеВОтбореПоля);
ОтключитьНедоступныеОтборы(СписокДокументыПродажи.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ОтключитьНедоступныеОтборы(Коллекция, ДоступныеПоляОтбора = Неопределено)
УдаляемыеЭлементы = Новый Массив;
Для Каждого ТекущиеДанные Из Коллекция Цикл
Если ТипЗнч(ТекущиеДанные) = Тип("ОтборКомпоновкиДанных") Тогда
ОтключитьНедоступныеОтборы(ТекущиеДанные.Элементы, ТекущиеДанные.ДоступныеПоляОтбора);
ИначеЕсли ТипЗнч(ТекущиеДанные) = Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
ОтключитьНедоступныеОтборы(ТекущиеДанные.Элементы, ДоступныеПоляОтбора);
ИначеЕсли ТипЗнч(ТекущиеДанные) = Тип("ЭлементОтбораКомпоновкиДанных") Тогда
Если НЕ ТекущиеДанные.Использование Тогда
Продолжить;
КонецЕсли;
Для Каждого ИмяСвойства Из СтрРазделить("ЛевоеЗначение,ПравоеЗначение",",") Цикл
ТекущееПоле = ТекущиеДанные[ИмяСвойства];
Если ТипЗнч(ТекущееПоле) = Тип("ПолеКомпоновкиДанных")
И ДоступныеПоляОтбора.НайтиПоле(ТекущееПоле) = Неопределено Тогда
УдаляемыеЭлементы.Добавить(ТекущиеДанные);
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
Для Каждого УдаляемыйЭлемент Из УдаляемыеЭлементы Цикл
Коллекция.Удалить(УдаляемыйЭлемент);
КонецЦикла;
КонецПроцедуры
Теперь при очистке организации, так же удаляются и “запрещенные” отборы:
Пользователь теперь не может наложить отбор, так как его нет в доступных полях для отбора. А старые отборы почистились автоматически.
Хорошо, мы разобрались с ограничениями отборов в динамическом списке. Но как обстоят дела с другими ограничениями? Добавим запреты на группировку и сортировку:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
&НаСервере
&После("УстановитьОтборПоОрганизации")
Процедура ЖурналДокументовПродажи_УстановитьОтборПоОрганизации()
ЗапрещенныеПоля = Новый Массив;
Если НЕ ЗначениеЗаполнено(Организация) Тогда
ЗапрещенныеПоля.Добавить("Подразделение");
КонецЕсли;
СписокДокументыПродажи.УстановитьОграниченияИспользованияВОтборе(ЗапрещенныеПоля);
СписокДокументыПродажи.УстановитьОграниченияИспользованияВГруппировке(ЗапрещенныеПоля);
СписокДокументыПродажи.УстановитьОграниченияИспользованияВПорядке(ЗапрещенныеПоля);
ОтключитьНедоступныеОтборы(СписокДокументыПродажи.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы);
КонецПроцедуры
Открываем форму с пустой организацией и видим, что подразделения нет в доступных к группировке полей. Всё хорошо.
Теперь выберем организацию и добавим группировку:
Всё хорошо применилось:
А теперь очистим организацию. После очистки, наш код запрещает использование поля Подразделение в группировках.
И вот мы видим такую ошибку:
На группировку 1С поругалась. И форма теперь показывает пустой список:
То же самое и с ограничением по сортировке.
Выходит, что платформа позволяет блокировать такие действия. Но с отборами это не работает. Хорошо, что мы написали свой метод по обработке этого?
На первый взгляд, все эти проблемы возникнут только в тех случаях, если разработчик в рамках одного списка производит включение\отключение ограничений. Однако, это не совсем так.
Если изначально форма списка не содержала ограничения по отборам, то при внесении изменений, у пользователей могут остаться сохранённые настройки. Как и те, что подгружаются автоматически при открытии формы, так и те, что пользователь может подгрузить вручную по кнопке “Выбрать настройки”.
И это так же нужно обрабатывать кодом. Иначе нет никакой уверенности, что ваши ограничения действительно заработали и пользователи не накладывают “запрещенные” отборы.
А напоследок, давайте вспомним один из хитрых методов обхода ограничений по отборам из статьи Ограничения полей, или как обмануть СКД.
Он позволяет наложить нехитрый отбор для примитивных типов полей, поэтому немного изменим наш код и ограничим поле “Сумма”. И оставим пока только ограничение отборов. (В нашей обработке два поля “Сумма”, поэтому, чтобы избежать путаницы, ограничим оба)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&НаСервере
&После("УстановитьОтборПоОрганизации")
Процедура ЖурналДокументовПродажи_УстановитьОтборПоОрганизации()
ЗапрещенныеПоля = Новый Массив;
Если НЕ ЗначениеЗаполнено(Организация) Тогда
ЗапрещенныеПоля.Добавить("Сумма");
ЗапрещенныеПоля.Добавить("СуммаДокумента");
КонецЕсли;
СписокДокументыПродажи.УстановитьОграниченияИспользованияВОтборе(ЗапрещенныеПоля);
ОтключитьНедоступныеОтборы(СписокДокументыПродажи.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы);
КонецПроцедуры
Для этого в форме с пустой организацией зайдём в “Настроить список”. Как видим, поля суммы в отборах нет:
А теперь нажмём “Ещё” -> “Изменить форму”
И перенесем доступные к сортировке поля в группу с отборами:
Вуаля!
Организация у нас пустая, но поле Сумма теперь доступно. И мы можем сделать своё грязное дело - перенести поле в отбор:
Фильтр применился. Несмотря на ограничения.
Да, этот способ обхода замудрён, но работает. И к сожалению, события по изменению отборов в 1С нет…
Есть простой способ предотвратить это. Если вы отключаете использование в отборах, то и запрещайте использовать в группировках и сортировках. Тогда неоткуда будет подтянуть поле с отбором.
Выводы:
Ограничения отборов в динамическом списке автоматически обрабатываются недостаточно, поэтому:
- Дополнительно программно обходите настройки (при установки ограничений и при загрузке пользовательских настроек)
- Если запрещаете отборы, то запрещайте и группировку\сортировку.
- Если планируете включать\отключать ограничения и у вас платформа меньше 8.3.16, то отключите отображения поля состояния просмотра, чтобы оно не вводило пользователя в заблуждение.