Как заменить интерактивный вопрос при проведении документа в управляемом приложении 1С?

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

Переход с обычных форм на управляемые часто ставит перед разработчиком сложную задачу: что делать с кодом, который вызывает диалоговые окна (например, Вопрос()) непосредственно в модуле объекта или в подписках на события? В управляемом приложении существует жесткое разделение на клиентскую и серверную части. Сервер — это "черный ящик", который не имеет графического интерфейса и не может взаимодействовать с пользователем. Рассмотрим, как правильно адаптировать логику интерактивных подтверждений под требования современной платформы 1С:Предприятие 8.3.

Почему старый подход не работает?

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

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

Решение 1: Перенос логики в модуль формы (событие ПередЗаписью)

Самый распространенный и правильный метод — переместить вызов вопроса в модуль формы документа. Разберем этот процесс по шагам.

1. В модуле формы находим или создаем предопределенную процедуру ПередЗаписью (на клиенте). Нам нужно перехватить момент записи, если она инициирована пользователем для проведения.

2. Используем асинхронный метод ПоказатьВопрос вместо устаревшего синхронного Вопрос. Это необходимо для работы в веб-клиенте и мобильном приложении.

Рассмотрим пример кода в модуле формы:


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

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

Решение 2: Передача ответа на сервер через ДополнительныеСвойства

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

Для этого используется механизм ДополнительныеСвойства. В модуле формы в процедуре ПриЗаписиНаСервере (или в ПередЗаписьюНаСервере) мы извлекаем данные из ПараметрыЗаписи и помещаем их в объект.

Проанализируем пример кода в модуле формы:


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
	
	// Передаем ответ из параметров формы в свойства объекта документа
	Если ПараметрыЗаписи.Свойство("ОтветПользователяДа") Тогда
		ТекущийОбъект.ДополнительныеСвойства.Вставить("ЗаменятьКоличество", ПараметрыЗаписи.ОтветПользователяДа);
	КонецЕсли;
	
КонецПроцедуры

Теперь в модуле объекта (или в подписке на событие) мы можем безопасно обратиться к этому свойству:


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

Решение 3: Использование Библиотеки Стандартных Подсистем (БСП)

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

Например, метод ОбщегоНазначенияКлиент.ПоказатьВопросПользователю позволяет гибко управлять блокировкой окна и внешним видом диалога. Кроме того, в БСП часто используется паттерн проверки перед записью, где все проверки собираются в одну структуру и выводятся пользователю списком.

Важные нюансы и ограничения

  1. Групповое проведение: Помните, что при проведении документов из формы списка или через обработку "Групповое проведение", события формы не вызываются. В этом случае вопрос задан не будет. Если логика критически важна, следует либо запретить проведение таких документов вне формы, либо реализовать в модуле объекта значение "по умолчанию".
  2. Блокировка интерфейса: При вызове асинхронного вопроса (ПоказатьВопрос) форма остается доступной для редактирования, если не указать режим блокировки окна. Всегда используйте РежимБлокировкиОкна.ВесьИнтерфейс или ОкноВладелец в параметрах описания оповещения, чтобы пользователь не нажал кнопку записи дважды, пока думает над ответом.
  3. Транзакции и сообщения: Если вам нужно просто проинформировать пользователя, используйте метод ОбщегоНазначения.СообщитьПользователю (на сервере). Эти сообщения накопятся и будут показаны клиенту сразу после завершения серверного вызова.

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

← На главную