Как программно ограничить список выбора статусов в документе 1С через расширение

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

При разработке расширений для типовых конфигураций, таких как 1С:ERP или 1С:Комплексная автоматизация, часто возникает задача ограничить пользователю выбор определенных статусов документа в зависимости от его прав или свойств самого объекта. Например, в документе ЗаявкаНаРасходованиеДенежныхСредств необходимо скрыть статус «Согласована» для определенной категории сотрудников.

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

Анализ проблемы: почему значения дублируются или не выбираются

Рассмотрим ситуацию, с которой столкнулся автор темы. При попытке переопределить список выбора в процедуре &После("НачалоВыбора") значения либо добавляются к стандартным, либо отображаются в списке, но не фиксируются в поле при нажатии. Проанализируем причины:

  1. Особенности аннотации &После: Когда мы используем &После, основной код типовой конфигурации уже отработал. Если в типовом решении уже заложена логика формирования списка, ваши программные манипуляции могут вступить в конфликт с уже сформированным объектом ДанныеВыбора.
  2. Разрыв между отображением и данными: В 1С существует разница между временным списком, подсовываемым в момент нажатия (ДанныеВыбора), и статичным списком, хранящимся в элементе формы (СписокВыбора). Если СписокВыбора заполнен, платформа часто отдает приоритет ему.
  3. Типизация значений: Если при формировании списка программно используются строковые представления вместо ссылок на перечисления (или наоборот), платформа не сможет сопоставить выбор пользователя с типом данных реквизита Объект.Статус.

Способ 1. Использование события НачалоВыбора

Этот метод подходит, если список должен формироваться динамически в момент обращения пользователя к полю. Разберем правильную реализацию в расширении. Чтобы полностью контролировать процесс, лучше использовать аннотацию &Вместо, однако если нужно сохранить часть типовой логики, можно использовать и &Перед, установив СтандартнаяОбработка = Ложь.

Посмотрим на пример кода, который решает задачу подмены списка:


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

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

Важный нюанс: Чтобы значения из этого списка корректно выбирались, убедитесь, что свойство элемента формы РежимВыбораИзСписка установлено в значение Ложь (так как мы подменяем именно данные поиска/выбора), либо, если оно Истина, нужно работать напрямую со свойством СписокВыбора.

Способ 2. Очистка и заполнение СписокВыбора (наиболее стабильный)

Выясним причину, по которой значения могут двоиться. Если у элемента формы заполнено свойство СписокВыбора, платформа будет пытаться объединить его с тем, что вы передаете в ДанныеВыбора. Чтобы этого избежать, в процедуре НачалоВыбора (или даже лучше — при создании формы) нужно принудительно очистить коллекцию элемента.

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


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

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

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

Способ 3. Контроль через ОбработкаВыбора

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

Пример кода для события ОбработкаВыбора:


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

Этот метод является «последним рубежом» обороны. Он менее эстетичен с точки зрения пользователя (статус виден, но его нельзя нажать), однако он максимально надежен, так как срабатывает непосредственно перед изменением данных в объекте.

Рекомендации по реализации в расширениях

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

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


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

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

← На главную