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