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