Как программно установить отбор в журнале документов 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке прикладных решений на платформе «1С:Предприятие 8.3» часто возникает необходимость ограничить список отображаемых документов в журнале. Например, требуется сделать так, чтобы текущий пользователь при открытии формы видел только «свои» документы (где он указан в качестве ответственного), либо документы по конкретной организации, кассе или контрагенту. В зависимости от типа используемого интерфейса (обычные формы или управляемые формы на базе динамических списков) подход к реализации этой задачи существенно различается.

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

Часть 1. Установка отбора в управляемых формах (1С v8.3)

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

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

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


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Получаем префикс базы по текущему пользователю
    Преф = ОбщиеДействия.ПолучитьПрефиксБазыПоПользователю();
    
    Если Преф <> "Общий" Тогда
        
        // Получаем коллекцию элементов отбора динамического списка
        ТекОтбор = Список.Отбор.Элементы;
        
        // Очищаем существующие элементы, чтобы избежать дублирования
        ТекОтбор.Очистить();
        
        // Добавляем новый элемент отбора компоновки данных
        УсловиеОтбора = ТекОтбор.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        
        // Указываем поле, по которому будет производиться фильтрация
        УсловиеОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Номер");
        
        // Задаем вид сравнения
        УсловиеОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Содержит;
        
        // Указываем значение, с которым будет сравниваться поле
        УсловиеОтбора.ПравоеЗначение = Преф;
        
        // Включаем использование отбора
        УсловиеОтбора.Использование = Истина;
        
        // Запрещаем пользователю интерактивно изменять или отключать этот отбор
        УсловиеОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
        
    КонецЕсли;
    
КонецПроцедуры

Обратим внимание на свойство РежимОтображения. Установив значение Недоступный, мы гарантируем, что пользователь не сможет случайно или намеренно снять галочку отбора и получить доступ к нежелательным данным. Если же необходимо установить отбор по умолчанию, но оставить пользователю возможность его отключить, следует использовать значение РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ или просто опустить эту настройку.

Часть 2. Сложные отборы с использованием логического условия «ИЛИ»

Стандартный отбор в динамическом списке соединяет все добавленные элементы по логическому закону «И» (все условия должны выполняться одновременно). Однако на практике часто требуется реализовать условие «ИЛИ». Например: показать документы, где текущий пользователь является ответственным, ИЛИ документы, по которым касса пустая (доступная для всех).

Выясним, как решить эту задачу в управляемых формах. Для этого мы объединим элементы отбора в группу с типом связи ГруппаИЛИ. Рассмотрим пример кода:


&НаСервере
Процедура УстановитьСложныйОтборИЛИ()
    
    ТекОтбор = Список.Отбор.Элементы;
    ТекОтбор.Очистить();
    
    // Создаем группу отбора с типом связи ИЛИ
    ГруппаОтбора = ТекОтбор.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
    ГруппаОтбора.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИЛИ;
    ГруппаОтбора.Использование = Истина;
    
    // Первое условие: Касса равна пустой ссылке
    ЭлементКасса = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементКасса.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Касса");
    ЭлементКасса.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
    ЭлементКасса.ПравоеЗначение = Справочники.Кассы.ПустаяСсылка();
    ЭлементКасса.Использование = Истина;
    
    // Второе условие: Ответственный равен текущему пользователю
    ЭлементОтветственный = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементОтветственный.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ответственный");
    ЭлементОтветственный.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
    ЭлементОтветственный.ПравоеЗначение = ПараметрыСеанса.ТекущийПользователь;
    ЭлементОтветственный.Использование = Истина;
    
КонецПроцедуры

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

Часть 3. Программная настройка отборов в обычных формах

Если вы продолжаете поддерживать или развивать конфигурации на обычных формах (например, УПП 1.3 или КА 1.1), то работа с отборами в журналах будет строиться иначе. Здесь мы манипулируем свойствами коллекции Отбор табличного поля, связанного с журналом или списком документов.

Проанализируем пример настройки отбора в обработчике события ПередОткрытием формы списка:


Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
    
    ТекПользователь = ПараметрыСеанса.ТекущийПользователь;
    
    // Получаем основную кассу пользователя по умолчанию из настроек
    КассаПоУмолчанию = УправлениеПользователями.ПолучитьЗначениеПоУмолчанию(ТекПользователь, "ОсновнаяКасса");
    
    Если ЗначениеЗаполнено(КассаПоУмолчанию) Тогда
        
        // Устанавливаем параметры отбора
        ЭтаФорма.ДокументСписок.Отбор.Касса.ВидСравнения = ВидСравнения.Равно;
        ЭтаФорма.ДокументСписок.Отбор.Касса.Значение = КассаПоУмолчанию;
        ЭтаФорма.ДокументСписок.Отбор.Касса.Использование = Истина;
        
        // Запрещаем пользователю интерактивно изменять этот отбор на панели
        ЭлементыФормы.ДокументСписок.НастройкаОтбора.Касса.Доступность = Ложь;
        
    КонецЕсли;
    
КонецПроцедуры

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

Часть 4. Работа с составными типами данных в отборах

Часто реквизиты документов, по которым необходимо установить отбор, имеют составной тип данных. Типичный пример — поле «Контрагент», которое может принимать тип СправочникСсылка.Контрагенты, СправочникСсылка.ФизическиеЛица или обычную строку.

При заполнении значения отбора для составного типа программисты нередко сталкиваются с ошибкой «Несоответствие типов». Выясним причину этой проблемы. Ошибка возникает тогда, когда в значение отбора передается объект целиком (например, строка таблицы значений или объектная ссылка без явного указания типа данных).

Посмотрим на пример правильного заполнения отбора для поля составного типа в обычном приложении:


// Безопасное обращение к значению
ТекСотрудник = ТекСтрока.Сотрудник; // Должна быть ссылка, а не объект данных!

Если ЗначениеЗаполнено(ТекСотрудник) Тогда
    
    // Включаем использование отбора и задаем вид сравнения перед установкой значения
    ЭтаФорма.ДокументСписок.Отбор.Контрагент.Использование = Истина;
    ЭтаФорма.ДокументСписок.Отбор.Контрагент.ВидСравнения = ВидСравнения.Равно;
    
    // Передаем строго ссылку. Приведение типов выполнится платформой автоматически
    ЭтаФорма.ДокументСписок.Отбор.Contragent.Значение = ТекСотрудник.Ссылка;
    
КонецЕсли;

Если вы передаете в отбор список, то все элементы этого списка должны соответствовать типам, которые допустимы для данного реквизита.

Часть 5. Передача списка значений для отбора (Вид сравнения «В списке»)

Когда требуется отобрать документы, удовлетворяющие сразу нескольким значениям реквизита (например, по нескольким определенным кассам организации), необходимо использовать вид сравнения ВСписке.

Рассмотрим, как правильно подготовить массив или список значений и передать его в отбор:


// 1. Создаем и заполняем Список Значений
СписокКасс = Новый СписокЗначений;
СписокКасс.Добавить(Справочники.Кассы.НайтиПоКоду("000000001"));
СписокКасс.Добавить(Справочники.Кассы.НайтиПоКоду("000000005"));

// 2. Устанавливаем отбор в списке
ДокументСписок.Отбор.БанковскийСчетКасса.ВидСравнения = ВидСравнения.ВСписке;
ДокументСписок.Отбор.БанковскийСчетКасса.Значение = СписокКасс;
ДокументСписок.Отбор.БанковскийСчетКасса.Использование = Истина;

В управляемом приложении аналогичная логика реализуется через СКД. Проанализируем пример кода на сервере:


&НаСервере
Процедура УстановитьОтборСпискомНаСервере()
    
    СписокПодразделений = Новый Массив;
    СписокПодразделений.Добавить(Справочники.Подразделения.НайтиПоНаименованию("Отдел продаж"));
    СписокПодразделений.Добавить(Справочники.Подразделения.НайтиПоНаименованию("Отдел логистики"));
    
    ТекОтбор = Список.Отбор.Элементы;
    
    ЭлементОтбора = ТекОтбор.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Подразделение");
    ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
    ЭлементОтбора.ПравоеЗначение = СписокПодразделений;
    ЭлементОтбора.Использование = Истина;
    
КонецПроцедуры

Часть 6. Очистка неверных или «сломавшихся» настроек пользователя

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

Если такое произошло, пользователь оказывается заблокирован и не может зайти в форму, чтобы сбросить проблемный отбор интерактивно. Разберем варианты решения этой проблемы. Чтобы исключить такие сбои при обслуживании системы, применяется автоматическое архивирование, обновление и тестирование баз 1С по расписанию.

Вариант 1. Кардинальный (полный сброс всех настроек пользователя)

Если нам не жалко индивидуальных настроек интерфейса пользователя (размеров колонок, окон, избранного) и нужно оперативно восстановить работоспособность сотрудника, можно применить программный метод очистки всех настроек:


&НаКлиенте
Процедура ОчиститьВсеНастройкиПользователя(ИмяПользователя)
    
    // Полное удаление настроек пользователя информационной базы
    ОчиститьНастройкиПользователя(ПользователиИнформационнойБазы.НайтиПоИмени(ИмяПользователя));
    
КонецПроцедуры

Вариант 2. Точечный сброс конкретной «упавшей» формы

Для сохранения других персональных настроек пользователя лучше сбросить настройки только одной проблемной формы журнала документов. В типовых конфигурациях на базе БСП (Библиотека Стандартных Подсистем) для этого предусмотрено специальное рабочее место: Администрирование -> Настройки пользователей и прав -> Настройки пользователей. Там можно выбрать нужного человека, найти конкретную форму списка в дереве настроек и удалить исключительно ее записи. Данная рутинная операция в интерфейсе автоматизируется с использованием роботизации действий пользователя в интерфейсе OneRPA без изменения программного кода.

Программно точечно очистить сохраненные настройки формы можно с помощью Хранилища Системных Настроек:


&НаСервере
Процедура СброситьНастройкиПроблемнойФормы(ИмяПользователя)
    
    КлючОбъекта = "ЖурналДокументов.Продажи.Форма.ФормаСписка/НастройкиФормы";
    ХранилищеСистемных Настроек.Удалить(КлючОбъекта, Неопределено, ИмяПользователя);
    
КонецПроцедуры

Часть 7. Оптимизация производительности: Отборы против Параметров

В заключение разберем важный архитектурный аспект разработки высоконагруженных систем. Если ваш динамический список построен по сложному произвольному запросу, наложение динамических отборов через свойство Список.Отбор может приводить к генерации СУБД неоптимальных планов запросов. Это происходит потому, что СУБД пытается наложить условия «на лету» поверх уже выполняющегося запроса.

Для ускорения работы критически важных журналов рекомендуется использовать передачу отборов через параметры запроса самого динамического списка. Опишем этот процесс:

  1. В схеме компоновки динамического списка (в тексте запроса) жестко прописываем условие: ГДЕ Документ.Ответственный = &ТекущийПользователь.
  2. В модуле формы при создании на сервере устанавливаем значение этого параметра:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Передаем параметр напрямую в запрос динамического списка
    Список.Параметры.УстановитьЗначениеПараметра("ТекущийПользователь", ПараметрыСеанса.ТекущийПользователь);
    
КонецПроцедуры

Использование параметризованных запросов существенно снижает нагрузку на SQL-сервер, так как планы таких запросов кэшируются СУБД, обеспечивая мгновенный отклик даже при работе с миллионами записей в журналах документов.

← На главную