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

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

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

Разберем подробнее, почему платформа 1С накладывает такое ограничение и как правильно спроектировать архитектуру проверки и подтверждения действий пользователя.

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

Выясним причину, по которой интерактивные методы недоступны в серверном контексте и в модуле объекта:

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

Все интерактивные действия с пользователем должны выполняться исключительно в клиентском контексте &НаКлиенте и строго до начала транзакции записи.

Архитектурный шаблон решения: отмена, вопрос, повторная запись

Проанализируем стандартный паттерн платформы 1С для реализации подтверждения записи. Он строится на перехвате события формы на клиенте, серверной проверке без открытия транзакции и последующем асинхронном вопросе.

Разберем реализацию по шагам:

  1. В клиентском событии формы ПередЗаписью проверяем наличие управляющего флага подтверждения в структуре ПараметрыЗаписи.
  2. Если флаг отсутствует, вызываем серверную функцию для проверки условия (например, поиска существующего документа с аналогичным значением реквизита).
  3. Если дубль найден, прерываем текущую запись установкой Отказ = Истина и вызываем асинхронный диалог ВопросАсинх (или ПоказатьВопрос).
  4. При согласии пользователя добавляем признак согласия в структуру параметров записи и программно вызываем метод формы Записать(). При повторном заходе алгоритм видит признак и пропускает документ на запись.
  5. При необходимости передаем признак подтверждения в модуль объекта через обработчик формы ПередЗаписьюНаСервере и коллекцию ДополнительныеСвойства.

Пример реализации в управляемой форме

Посмотрим на практический пример кода в модуле формы документа. Реализуем асинхронную схему проверки наличия дубликата реквизита:


&НаКлиенте
Асинх Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)

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

	// Проверяем наличие дубля на сервере без транзакции
	НайденДубль = ПроверитьНаличиеДубляНаСервере();

	Если НайденДубль Тогда
		// Отменяем текущую попытку стандартной записи
		Отказ = Истина;

		// Задаем интерактивный вопрос асинхронно
		ТекстВопроса = "Документ с таким реквизитом уже существует в базе данных. Все равно записать?";
		Ответ = Ждать ВопросАсинх(ТекстВопроса, РежимДиалогаВопрос.ДаНет);

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

			// Повторно инициируем запись формы с установленным флагом
			ЭтотОбъект.Записать(ПараметрыПовторнойЗаписи);
		КонецЕсли;
	КонецЕсли;

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

&НаСервере
Функция ПроверитьНаличиеДубляНаСервере()
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	Док.Ссылка КАК Ссылка
		|ИЗ
		|	Документ.ПриходнаяНакладная КАК Док
		|ГДЕ
		|	Док.НомерВходящегоДокумента = &НомерВходящегоДокумента
		|	И Док.Ссылка <> &ТекущаяСсылка";
		
	Запрос.УстановитьПараметр("НомерВходящегоДокумента", Объект.НомерВходящегоДокумента);
	Запрос.УстановитьПараметр("ТекущаяСсылка", Объект.Ссылка);
	
	Возврат НЕ Запрос.Выполнить().Пустой();
	
КонецФункции

Передача подтверждения в модуль объекта

Рассмотрим случай, когда проверка бизнес-логики должна дублироваться непосредственно в модуле объекта для предотвращения некорректной записи из внешних источников. В этом случае флаг согласия пользователя транслируется через форму в свойства самого объекта.

В модуле формы передаем параметр через коллекцию ДополнительныеСвойства:


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

В модуле объекта в событии ПередЗаписью анализируем переданное свойство:


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

	Подтверждено = Ложь;
	Если ДополнительныеСвойства.Свойство("ПодтвержденоПользователем") Тогда
		Подтверждено = ДополнительныеСвойства.ПодтвержденоПользователем;
	КонецЕсли;

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

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

Альтернативный вариант: проверка при изменении реквизита

Рассмотрим еще один удобный подход с точки зрения пользовательского интерфейса. Вместо ожидания момента нажатия кнопки «Записать», проверку на уникальность можно выполнять непосредственно в событии ПриИзменении поля ввода на форме.

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

← На главную