В процессе разработки в среде 1С:Предприятие 8 часто возникает задача организации взаимодействия между различными модулями системы. Типичный сценарий: у нас есть встроенная или внешняя обработка, на форме которой объявлена некоторая полезная экспортная процедура, и мы хотим вызвать её из общего серверного модуля. Однако при попытке сделать это напрямую разработчики часто сталкиваются с ошибками работы с контекстом или сообщениями о том, что вызываемый метод не найден.
В этой статье мы подробно разберем, почему стандартный интуитивный подход «вызвать процедуру формы напрямую с сервера» не работает, выясним причину ограничений платформы и рассмотрим правильные архитектурные решения этой задачи в соответствии со стандартами разработки фирмы «1С».
Проанализируем классическую ошибку, с которой сталкиваются разработчики при попытке решить эту задачу "в лоб", и пошагово разберем корректные способы реализации.
Разберем типичную попытку реализации, которая приводит к ошибке:
// Попытка вызова в общем серверном модуле:
Ф = ПолучитьФорму("Обработка.ИзОдинСВActiveDirectory.Форма.Форма");
Ф.Команда1(Неопределено); // Вызывает ошибку: "Значение не является значением объектного типа (Команда1)"
Для понимания природы ошибки нам необходимо рассмотреть два ключевых ограничения платформы 1С:Предприятие 8:
ПолучитьФорму() предназначен для выполнения на стороне клиента. Если наш общий модуль является серверным (а большинство общих модулей в современных конфигурациях выполняются именно на сервере), вызов интерактивных методов в нем недоступен. Сервер «не знает» о существовании экранных форм, оконного интерфейса и конкретного сеанса отображения данных у пользователя. На сервере этот метод вернет либо ошибку «Интерактивные операции недоступны», либо значение Неопределено. Именно поэтому при последующем обращении к методам формы возникает ошибка «Значение не является значением объектного типа».&НаКлиенте физически выполняются на компьютере пользователя (в оперативной памяти клиентского приложения). Серверный общий модуль выполняется на сервере приложений 1С. Вызвать код «снизу вверх» (то есть инициировать выполнение клиентского кода напрямую из серверного потока) технически невозможно. Платформа 1С не поддерживает вызов клиентских процедур с сервера.Более того, согласно официальным стандартам разработки фирмы «1С», крайне не рекомендуется размещать прикладную (бизнес) логику в модулях форм. Модуль формы должен отвечать исключительно за визуальное представление и взаимодействие с пользователем. Вся расчетная часть должна быть вынесена в модули объектов или общие модули.
---
Если алгоритм, который мы хотим запустить из общего модуля, относится к логике работы самой обработки, его необходимо перенести из модуля формы в модуль объекта обработки и сделать процедуру или функцию экспортной.
Выполним это по шагам:
Шаг 1. Перенос кода. Открываем модуль объекта нашей обработки (не формы!) и пишем там нашу процедуру с ключевым словом Экспорт:
// В модуле объекта обработки "ИзОдинСВActiveDirectory"
Процедура ВыполнитьИнтеграциюСAD(ПараметрыПодключения) Экспорт
// Здесь размещается вся серверная логика работы с Active Directory
// ...
КонецПроцедуры
Шаг 2. Вызов из общего модуля. Теперь в нашем серверном общем модуле мы можем легко создать экземпляр этой обработки как прикладного объекта и вызвать её экспортный метод:
// В серверном общем модуле:
Процедура ЗапуститьСинхронизацию() Экспорт
// Создаем экземпляр обработки на сервере
ОбработкаОбъект = Обработки.ИзОдинСВActiveDirectory.Создать();
// Передаем необходимые параметры и вызываем экспортный метод модуля объекта
ПараметрыAD = Новый Структура("Домен, Логин", "corp.local", "admin");
ОбработкаОбъект.ВыполнитьИнтеграциюСAD(ПараметрыAD);
КонецПроцедуры
Этот подход полностью соответствует стандартам разработки 1С, так как разделяет интерфейсную часть (форму) и бизнес-логику (модуль объекта).
---
Рассмотрим ситуацию, когда наша обработка является не встроенной в конфигурацию, а внешней (хранится в виде отдельного файла .epf на диске или загружена в справочник «Дополнительные отчеты и обработки» в базах на БСП).
В этом случае напрямую обратиться через менеджер Обработки нельзя. Нам нужно сначала создать объект внешней обработки программно.
Вариант А: Загрузка внешней обработки из файла на сервере
// В общем модуле на сервере:
ПутьКФайлу = "C:\Distr\MyProcessing.epf";
// Ложь - параметр, отключающий безопасный режим, если обработке нужны повышенные привилегии
ОбработкаОбъект = ВнешниеОбработки.Создать(ПутьКФайлу, Ложь);
ОбработкаОбъект.ВыполнитьИнтеграциюСAD(ПараметрыAD);
Вариант Б: Загрузка внешней обработки с использованием механизмов БСП (Библиотеки стандартных подсистем)
Если ваша конфигурация построена на базе БСП, внешние обработки обычно хранятся в справочнике. Программный вызов их методов из общего модуля будет выглядеть следующим образом:
// Получаем ссылку на элемент справочника дополнительных обработок
СсылкаНаОбработку = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию("Интеграция с AD");
// Используем стандартный метод БСП для получения серверного объекта обработки
ОбработкаОбъект = ДополнительныеОтчетыИОбработки.ПолучитьОбъектВнешнейОбработки(СсылкаНаОбработку);
// Вызываем наш экспортный метод
ОбработкаОбъект.ВыполнитьИнтеграциюСAD(ПараметрыAD);
---
Иногда серверному общему модулю для выполнения вычислений требуются данные, которые пользователь в данный момент ввел на форме обработки в режиме реального времени. Передавать саму форму на сервер целиком — плохая практика, нагружающая канал связи между клиентом и сервером.
Рассмотрим правильный паттерн передачи контекста данных:
Шаг 1. На сервере формы мы конвертируем интерактивные данные формы (реквизит Объект) в реальный прикладной объект обработки с помощью платформенного метода РеквизитФормыВЗначение:
&НаСервере
Процедура ВыполнитьДействиеНаСервере()
// Преобразуем данные формы в полноценный объект обработки со всеми заполненными реквизитами
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
// Передаем этот объект в наш общий модуль для обработки
ИмяВашегоОбщегоМодуля.ОбработатьДанныеФормы(ОбработкаОбъект);
// Если общий модуль изменил какие-то реквизиты объекта, возвращаем их обратно на форму
ЗначениеВРеквизитФормы(ОбработкаОбъект, "Объект");
КонецПроцедуры
Шаг 2. В общем модуле мы описываем процедуру, которая принимает этот объект как параметр, работает с его реквизитами и выполняет необходимые действия на сервере без необходимости знать что-либо о существовании самой формы:
// В общем модуле ИмяВашегоОбщегоМодуля
Процедура ОбработатьДанныеФормы(ОбработкаОбъект) Экспорт
// Нам доступен весь контекст реквизитов обработки, заполненных пользователем
Если ЗначениеЗаполнено(ОбработкаОбъект.ИмяПользователяAD) Тогда
// Выполняем логику
ОбработкаОбъект.СтатусПодключения = "Успешно";
КонецЕсли;
КонецПроцедуры
---
Рассмотрим ситуацию, когда вызов процедуры из общего модуля требовался именно для того, чтобы обновить интерфейс формы (например, вывести сообщение пользователю, заблокировать элементы интерфейса или показать предупреждение после выполнения серверной операции).
Поскольку сервер не может напрямую командовать клиентом, архитектуру взаимодействия необходимо построить по принципу «запрос-ответ»:
ПоказатьПредупреждение() или делает элементы формы доступными).Посмотрим на пример реализации данного паттерна:
// Код в модуле формы (Клиент)
&НаКлиенте
АсинхроннаяПроцедура ЗапуститьОперациюНаКлиенте()
// Вызываем серверный метод формы для связи с общим модулем
РезультатВыполнения = ВыполнитьРасчетНаСервереФормы();
// На клиенте обрабатываем результат и управляем интерфейсом
Если РезультатВыполнения.Успех Тогда
ПоказатьПредупреждение(, "Операция успешно завершена!");
Иначе
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(РезультатВыполнения.ТекстОшибки);
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция ВыполнитьРасчетНаСервереФормы()
// Вызываем серверный общий модуль и возвращаем результат на клиент
Возврат ИмяВашегоОбщегоМодуля.ПровестиСложныеРасчеты();
КонецФункции
В общем модуле код будет выглядеть максимально просто и независимо от интерфейса:
// В серверном общем модуле
Функция ПровестиСложныеРасчеты() Экспорт
Результат = Новый Структура("Успех, ТекстОшибки", Истина, "");
Попытка
// Бизнес-логика вычислений
// ...
Исключение
Результат.Успех = Ложь;
Результат.ТекстОшибки = ОписаниеОшибки();
КонецПопытки;
Возврат Результат;
КонецФункции
Использование этого паттерна гарантирует стабильную работу вашей конфигурации в любых режимах работы (толстый клиент, тонкий клиент, веб-клиент) и полностью соответствует стандартам качественной разработки на платформе 1С:Предприятие 8.