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