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

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

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

Суть проблемы и "плохой" пример из практики

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


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

&НаСервере
Процедура ПроверитьНоменклатуруНаДублиСервер(КонтрольПоДублям)
    // Логика поиска дублей в базе данных
КонецПроцедуры

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

Техническая причина: Механизм завершения работы системы

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

В специфическом состоянии "завершения работы" выполнение серверных вызовов из клиентских обработчиков блокируется на уровне ядра платформы. Если в коде прописан вызов &НаСервере, произойдет следующее:

  1. Платформа попытается закрыться и сохранить данные.
  2. Сработает обработчик ПередЗаписью на клиенте.
  3. При попытке вызвать серверную процедуру платформа выдаст ошибку: "Вызов серверных методов во время завершения работы запрещен".
  4. В результате форма не запишется и, что самое неприятное, не закроется. Пользователь не сможет выйти из программы обычным способом, а администратор не сможет корректно завершить сеанс для обслуживания базы.

Проблема транзакционности и "грязного" чтения

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

Эффект "зацикливания" формы

Еще одна неприятная особенность заключается в автоматическом управлении свойством Модифицированность. Если серверный метод, вызванный из ПередЗаписью, случайно изменит какой-либо реквизит формы, платформа может снова установить признак того, что данные изменены. Рассмотрим цепочку событий:

  1. Пользователь нажимает "Записать".
  2. Срабатывает ПередЗаписью на клиенте -> вызов сервера.
  3. На сервере меняется значение реквизита.
  4. Объект записывается.
  5. После записи платформа видит, что на сервере были изменения, которые не были "отработаны" штатно, и форма снова помечается как измененная.
  6. При попытке закрыть форму пользователь снова получает вопрос: "Сохранить изменения?", хотя он их только что сохранил. Это создает бесконечный цикл, раздражающий пользователя.

Как реализовать задачу правильно: использование ПараметрыЗаписи

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

  1. В клиентском обработчике ПередЗаписью мы только подготавливаем флаги.
  2. Вся тяжелая логика и проверки переносятся в ПередЗаписьюНаСервере.

Посмотрим на пример реализации:


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

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

Интерактивные проверки

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

Подведем итог: Наличие нарушений в типовых конфигурациях (как в случае с УНФ) не является разрешением на использование подобных приемов в собственных разработках. Это "технический долг" или осознанный риск разработчиков вендора. Для создания стабильных и отказоустойчивых систем всегда придерживайтесь правила: клиентский обработчик ПередЗаписью не должен ходить на сервер напрямую. Используйте ПараметрыЗаписи и серверный обработчик ПередЗаписьюНаСервере для обеспечения чистоты архитектуры и надежности работы приложения при завершении сеансов.

← На главную