Как правильно реализовать вопрос пользователю при записи или проведении документа в 1С 8.3

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

При переходе на платформу 1С:Предприятие 8.3 многие разработчики сталкиваются с серьезной проблемой: привычные модальные окна, которые останавливали выполнение кода, ушли в прошлое. Теперь использование функции ПоказатьВопрос не прерывает выполнение процедуры. Рассмотрим ситуацию: нам нужно задать вопрос пользователю в момент записи или проведения документа, и в зависимости от ответа либо продолжить операцию, либо прервать ее. Если просто вызвать вопрос в процедуре ПередЗаписью, код продолжит выполняться дальше, документ запишется, и только потом пользователь увидит диалоговое окно.

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

Почему нельзя вызывать диалоги внутри транзакции

Проанализируем критически важное замечание, которое часто встречается на форумах: «За вопрос внутри транзакции — расстрел». Это не просто шутка. Когда 1С начинает запись документа на сервере, открывается транзакция базы данных. Если в этот момент на клиенте «зависнет» вопрос, транзакция останется открытой, удерживая блокировки на таблицы базы данных. Другие пользователи не смогут работать с этими данными, пока текущий пользователь не нажмет кнопку в окне вопроса. Именно поэтому все интерактивные действия должны выполняться ДО начала серверных процедур записи.

Решение 1: Использование флага на форме (Паттерн «Повторный вызов»)

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

  1. В процедуре ПередЗаписью на клиенте проверяем наше условие и состояние флага.
  2. Если условие выполняется, а вопрос еще не был задан, мы устанавливаем Отказ = Истина и вызываем ПоказатьВопрос.
  3. В обработчике оповещения (коллбэке), если пользователь ответил «Да», мы устанавливаем флаг в значение Истина и программно вызываем метод Записать().
  4. Процедура ПередЗаписью срабатывает второй раз, видит установленный флаг и пропускает блок с вопросом, позволяя документу записаться.

Посмотрим на пример кода:


&НаКлиенте
Перем ВопросУжеЗадан;

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

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

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

Решение 2: Использование структуры ПараметрыЗаписи

Вместо использования глобальной переменной на форме, мы можем использовать стандартный механизм платформы — структуру ПараметрыЗаписи. Это более чистое решение с точки зрения архитектуры. Мы вставляем в эту структуру свой флаг, который передается между вызовами.

Рассмотрим, как это работает. Метод Записать() принимает структуру параметров. Мы можем добавить туда любое поле, например, ПодтвержденоПользователем. Проанализируем пример:


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

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

Решение 3: Переопределение команд формы (подход БСП)

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

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

Решение 4: ОбработкаПроверкиЗаполнения

Если ваша задача — заставить пользователя дозаполнить данные перед проведением, рассмотрите стандартное событие ОбработкаПроверкиЗаполнения. Оно специально предназначено для контроля данных. Хотя оно не позволяет вызывать интерактивные диалоги (оно выполняется на сервере), оно позволяет вывести список ошибок в удобном для пользователя виде. Если же нужен именно «мягкий» вопрос (хотите ли вы продолжить?), то методы 1 и 2 остаются приоритетными.

Важные моменты:

Таким образом, мы выяснили, что ключ к успеху — в прерывании записи через Отказ = Истина и повторном программном вызове метода Записать() после получения ответа от пользователя.

← На главную