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