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

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

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

Почему нельзя вызвать вопрос в ОбработкеПроведения?

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

Решение 1: Стандартный паттерн прерывания записи в модуле формы

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

Для начала создадим в форме документа переменную, которая будет хранить состояние нашего вопроса. Назовем ее ОтветНаВопросПолучен.


&НаКлиенте
Перем ОтветНаВопросПолучен;

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

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

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

Теперь создадим процедуру обработки ответа. Важно понимать, что в управляемых формах ПоказатьВопрос работает асинхронно.


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

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

Иногда недостаточно просто разрешить или запретить проведение. Нужно, чтобы сама процедура ОбработкаПроведения в модуле объекта знала, что именно ответил пользователь. Для этого удобно использовать коллекцию ДополнительныеСвойства.

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


&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    // Предположим, вопрос уже задан и ответ получен
    ПараметрыЗаписи.Вставить("ПерезаписыватьВнешнююБазу", Истина);
КонецПроцедуры

Затем в модуле формы используем обработчик ПередЗаписьюНаСервере, чтобы "пробросить" этот параметр в сам объект документа:


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

Теперь в модуле объекта, в процедуре ОбработкаПроведения, мы можем прочитать это значение и выполнить нужную логику:


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

Архитектурные предостережения: Веб-сервисы и транзакции

Проанализируем ситуацию, описанную в теме: вызов веб-сервиса другой базы внутри проведения. Это крайне опасная практика. Если внешняя база будет отвечать долго (например, из-за медленного интернета или большой нагрузки), транзакция в вашей текущей базе будет "висеть".

Рекомендуемый подход:

  1. В ОбработкеПроведения только фиксируйте необходимость обмена (например, записывайте ссылку на документ в специальный регистр "ОчередьОбмена").
  2. Используйте регламентное задание, которое будет раз в минуту проверять этот регистр и отправлять данные через веб-сервис в фоновом режиме.
  3. Если пользователю критично знать результат здесь и сейчас, используйте форму документа для индикации статуса обмена ("Отправлено", "Ошибка" и т.д.), но не блокируйте проведение самого документа ожиданием ответа от другой системы.

Важный момент: всегда помните о групповом проведении документов. Когда бухгалтер запускает перепроведение за месяц, форма документа не открывается, и ваш вопрос в ПередЗаписью формы просто не сработает. Код в модуле объекта должен быть готов к тому, что дополнительные свойства могут быть не заполнены, и в этом случае система должна вести себя предсказуемо (например, пропускать интерактивные действия).

← На главную