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

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

При разработке расширений или доработок в конфигурациях 1С:Предприятие версии 8.3 на управляемых формах, мы часто сталкиваемся с необходимостью интерактивного взаимодействия с пользователем. Например, при выборе элемента справочника, нам может потребоваться задать вопрос пользователю перед тем, как окончательно принять его выбор. В частности, если вы работаете с конфигурацией "Бухгалтерия 3.0" и хотите, чтобы при выборе контрагента с задолженностью система спрашивала "Контрагент с задолженностью! Продолжить?", а при ответе "нет" просто отменяла выбор, эта статья поможет разобраться в тонкостях работы с асинхронными диалогами.

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

Основы работы с асинхронным вопросом в стандартных обработчиках

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

Разберем по шагам, как мы можем реализовать отмену стандартного действия при выборе "Нет" и продолжение при выборе "Да".

  1. Отменяем стандартную обработку в начале.

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


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

Обратите внимание, что мы передаем в ОписаниеОповещения дополнительные параметры через структуру. Это очень важно, так как в процедуре ПослеЗакрытияВопроса нам понадобится знать, какой элемент формы мы изменяем и какое значение было выбрано.

После того как пользователь нажмет "Да" или "Нет", будет вызвана процедура, указанная в ОписаниеОповещения. В ней мы и будем анализировать результат и принимать дальнейшие действия.


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

Как мы видим, если пользователь выбирает "Нет" (Результат = КодВозвратаДиалога.Нет), мы просто не пишем никакого кода в этой ветке. Благодаря тому, что мы изначально установили СтандартнаяОбработка = Ложь, платформа не продолжит стандартное действие, и выбор контрагента не будет завершен. Если же выбран "Да", мы программно устанавливаем значение элемента формы, тем самым имитируя стандартное поведение выбора.

Современный подход: Асинхронность с использованием Асинх и Ждать

Начиная с версии платформы 8.3.20, в 1С появился более удобный механизм работы с асинхронными операциями — ключевые слова Асинх и Ждать (аналоги Async и Await в других языках программирования). Этот подход позволяет писать асинхронный код в более простом, "синхронном" стиле, значительно упрощая логику и избегая необходимости передачи параметров через ОписаниеОповещения и создания отдельных процедур-обработчиков.

Давайте посмотрим, как можно переписать предыдущий пример, используя Асинх и Ждать:


&НаКлиенте
Асинх Процедура Обработка_контрагент_с_задолженностьюСписокВыборПосле(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
    // Отменяем стандартную обработку
    СтандартнаяОбработка = Ложь;

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

В этом примере мы видим, что процедура Обработка_контрагент_с_задолженностьюСписокВыборПосле помечена как Асинх. Перед вызовом ПоказатьВопрос мы используем ключевое слово Ждать. Это означает, что выполнение кода в данной процедуре будет приостановлено до тех пор, пока пользователь не ответит на вопрос. Результат ответа сразу же будет присвоен переменной РезультатВопроса, что делает код значительно чище и легче для понимания.

Этот современный механизм является предпочтительным для работы с асинхронными операциями в новых разработках на платформе 8.3.20 и выше.

Альтернативные и архитектурные подходы: Предварительный отбор

Хотя вышеописанные методы позволяют решить проблему на уровне интерактивного диалога, часто более производительным и правильным с точки зрения архитектуры является ограничение выбора еще на этапе открытия формы. Вместо того чтобы позволять пользователю выбирать "неправильного" контрагента и затем сообщать об ошибке или задавать вопрос, лучше изначально не показывать таких контрагентов в списке выбора.

Давайте рассмотрим несколько способов реализации такого подхода:

  1. Использование RLS (Record Level Security - Ограничение доступа на уровне записей).

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

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

Регистр сведений "ДопустимыеСвязиОрганизацияКонтрагент" со следующими измерениями:

При открытии формы выбора контрагента, мы можем установить отбор по этому регистру.

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

  1. В расширении добавить форму выбора справочника "Контрагенты".
  2. Изменить запрос динамического списка.

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


ВЫБРАТЬ
    СпрКонтрагенты.Ссылка,
    СпрКонтрагенты.Код,
    СпрКонтрагенты.Наименование
    // Другие поля контрагента
ИЗ
    Справочник.Контрагенты КАК СпрКонтрагенты
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДопустимыеСвязиОрганизацияКонтрагент КАК ДопСвязи
    ПО СпрКонтрагенты.Ссылка = ДопСвязи.Контрагент
    // И ДопСвязи.Организация = &ТекущаяОрганизация
    // И НЕ ДопСвязи.ЕстьЗадолженность // Пример условия
ГДЕ
    (ДопСвязи.Контрагент ЕСТЬ NULL ИЛИ ДопСвязи.Активен = ИСТИНА)
    // ИЛИ: СпрКонтрагенты.ЕстьЗадолженность = ЛОЖЬ
    // ИЛИ: СпрКонтрагенты.Ссылка НЕ В (ВЫБРАТЬ Контрагент ИЗ РегистрНакопления.ВзаиморасчетыГДЕ ... ЕстьЗадолженность)

В этом запросе мы используем условия ГДЕ, чтобы отфильтровать контрагентов, у которых есть задолженность или которые не разрешены для текущей организации. Параметр &ТекущаяОрганизация может быть установлен в форме выбора при ее открытии.

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

Использование предварительного отбора имеет несколько преимуществ:

Заключение

Мы рассмотрели несколько подходов к работе с асинхронными диалогами в 1С:Предприятие при выборе контрагента. От начального решения с явной отменой стандартной обработки и передачей параметров через ОписаниеОповещения, до современного и более удобного механизма Асинх/Ждать. Кроме того, мы выяснили, что в большинстве случаев, когда речь идет о валидации данных, предпочтительнее использовать архитектурные решения, такие как предварительный отбор данных через RLS или настройку динамических списков, чтобы изначально предлагать пользователю только корректные варианты выбора. Выбор конкретного метода зависит от версии платформы, сложности логики и требований к пользовательскому интерфейсу.

Надеемся, эти рекомендации помогут вам эффективно решать задачи по интерактивному взаимодействию с пользователем в ваших проектах 1С.

← На главную