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