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