При переходе на платформу 1С:Предприятие 8.3 многие разработчики сталкиваются с серьезной проблемой: привычные модальные окна, которые останавливали выполнение кода, ушли в прошлое. Теперь использование функции ПоказатьВопрос не прерывает выполнение процедуры. Рассмотрим ситуацию: нам нужно задать вопрос пользователю в момент записи или проведения документа, и в зависимости от ответа либо продолжить операцию, либо прервать ее. Если просто вызвать вопрос в процедуре ПередЗаписью, код продолжит выполняться дальше, документ запишется, и только потом пользователь увидит диалоговое окно.
Разберем подробно, как правильно обойти это ограничение, сохранив логику работы программы и не нарушая правила работы с транзакциями.
Проанализируем критически важное замечание, которое часто встречается на форумах: «За вопрос внутри транзакции — расстрел». Это не просто шутка. Когда 1С начинает запись документа на сервере, открывается транзакция базы данных. Если в этот момент на клиенте «зависнет» вопрос, транзакция останется открытой, удерживая блокировки на таблицы базы данных. Другие пользователи не смогут работать с этими данными, пока текущий пользователь не нажмет кнопку в окне вопроса. Именно поэтому все интерактивные действия должны выполняться ДО начала серверных процедур записи.
Это самый распространенный способ реализации проверки с вопросом. Мы будем использовать переменную (реквизит формы), которая будет указывать, был ли уже задан вопрос. Разберем этот алгоритм по шагам:
ПередЗаписью на клиенте проверяем наше условие и состояние флага.Отказ = Истина и вызываем ПоказатьВопрос.Истина и программно вызываем метод Записать().ПередЗаписью срабатывает второй раз, видит установленный флаг и пропускает блок с вопросом, позволяя документу записаться.Посмотрим на пример кода:
&НаКлиенте
Перем ВопросУжеЗадан;
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
// Если вопрос уже был задан и получен положительный ответ, выходим
Если ВопросУжеЗадан = Истина Тогда
ВопросУжеЗадан = Ложь; // Сбрасываем для следующего раза
Возврат;
КонецЕсли;
// Проверяем условие, нужно ли задавать вопрос
Если ПараметрыЗаписи.РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
Отказ = Истина; // Прерываем текущую попытку записи
Оповещение = Новый ОписаниеОповещения("ПослеОтветаНаВопрос", ЭтаФорма, ПараметрыЗаписи);
ПоказатьВопрос(Оповещение, "Вы уверены, что хотите провести документ без заполнения доп. данных?", РежимДиалогаВопрос.ДаНет);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеОтветаНаВопрос(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
ВопросУжеЗадан = Истина;
// Повторно инициируем запись с теми же параметрами
ЭтаФорма.Записать(ДополнительныеПараметры);
КонецЕсли;
КонецПроцедуры
Вместо использования глобальной переменной на форме, мы можем использовать стандартный механизм платформы — структуру ПараметрыЗаписи. Это более чистое решение с точки зрения архитектуры. Мы вставляем в эту структуру свой флаг, который передается между вызовами.
Рассмотрим, как это работает. Метод Записать() принимает структуру параметров. Мы можем добавить туда любое поле, например, ПодтвержденоПользователем. Проанализируем пример:
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
// Проверяем, есть ли в параметрах наш специальный ключ
Если ПараметрыЗаписи.Свойство("ДополнительноеПодтверждениеПолучено") Тогда
Возврат; // Пользователь уже ответил "Да", не мешаем записи
КонецЕсли;
Если ПараметрыЗаписи.РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
Отказ = Истина;
Оповещение = Новый ОписаниеОповещения("ЗавершитьЗаписьПослеВопроса", ЭтаФорма, ПараметрыЗаписи);
ПоказатьВопрос(Оповещение, "Провести документ?", РежимДиалогаВопрос.ДаНет);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ЗавершитьЗаписьПослеВопроса(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
// Добавляем признак подтверждения в структуру параметров
ДополнительныеПараметры.Вставить("ДополнительноеПодтверждениеПолучено", Истина);
ЭтаФорма.Записать(ДополнительныеПараметры);
КонецЕсли;
КонецПроцедуры
Выясним причину, по которой иногда лучше вообще не трогать ПередЗаписью. В сложных конфигурациях, построенных на БСП (Библиотека стандартных подсистем), логика записи сильно завязана на системные механизмы. В таких случаях программисты часто создают свою кнопку «Провести», скрывая стандартную. На нажатие этой кнопки вешается обработчик, который сначала задает все необходимые вопросы через ПоказатьВопрос, собирает ответы, и только потом вызывает ЭтотОбъект.Записать().
Это позволяет избежать сложной логики в обработчиках событий и гарантирует, что никакие транзакции еще не открыты в момент диалога с пользователем.
Если ваша задача — заставить пользователя дозаполнить данные перед проведением, рассмотрите стандартное событие ОбработкаПроверкиЗаполнения. Оно специально предназначено для контроля данных. Хотя оно не позволяет вызывать интерактивные диалоги (оно выполняется на сервере), оно позволяет вывести список ошибок в удобном для пользователя виде. Если же нужен именно «мягкий» вопрос (хотите ли вы продолжить?), то методы 1 и 2 остаются приоритетными.
Важные моменты:
РежимЗаписи. Вопрос должен возникать только при проведении, если это логически оправдано, чтобы не раздражать пользователя при обычном сохранении (Ctrl+S).Вопрос() там просто не будут работать.Таким образом, мы выяснили, что ключ к успеху — в прерывании записи через Отказ = Истина и повторном программном вызове метода Записать() после получения ответа от пользователя.