При разработке в среде 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда синтаксический контроль выдает ошибку при попытке обратиться из одного общего модуля к другому. Особенно классической является ситуация, когда из общего модуля с включенными флагами «Клиент (управляемое приложение)» и «Сервер» (например, УчетСреднегоЗарабокаКлиентСервер) нужно вызвать метод общего модуля, который компилируется исключительно на «Сервере» (например, КадровыйУчет).
На первый взгляд это кажется странным: если модуль выполняется и на сервере, почему серверный вызов не проходит проверку? В этой статье мы подробно разберем физический смысл этой проблемы, проанализируем, почему простые решения «в лоб» могут навредить производительности системы, и научимся решать эту задачу красиво и в соответствии со стандартами разработки фирмы «1С».
Для того чтобы понять природу ошибки, рассмотрим детально, что происходит под капотом платформы 1С:Предприятия в момент компиляции общего модуля ОМ1, для которого установлены флаги «Клиент (управляемое приложение)» и «Сервер».
Когда конфигурация запускается в режиме тонкого или веб-клиента, платформа физически разделяет этот модуль и создает два независимых экземпляра кода:
При прохождении синтаксического контроля или при компиляции на стороне клиента препроцессор 1С анализирует весь текст модуля ОМ1 сверху вниз. Когда он натыкается на строчку вызова серверного модуля ОМ2 (у которого установлен только флаг «Сервер»), возникает конфликт:
С точки зрения клиентского приложения, общего модуля ОМ2 не существует в природе. На клиенте нет метаданных и исполняемого кода этого модуля. Так как у ОМ2 отсутствует свойство «Вызов сервера», клиентское приложение не может выполнить удаленный вызов процедуры. В результате компилятор выдает логичную ошибку: Переменная не определена (ИмяСерверногоМодуля).
Часто разработчики решают эту проблему «в лоб»: создают новый общий модуль, устанавливают для него флаг «Вызов сервера» и переносят вызов туда. Рассмотрим, почему этот подход противоречит стандартам БСП (Библиотеки стандартных подсистем) и рекомендациям 1С:ИТС:
ДокументОбъект, СправочникОбъект, а также таблицами значений. Эти типы данных являются мутабельными (изменяемыми). Их физически невозможно передать через границу «клиент-сервер». При попытке вызвать метод с такими параметрами через модуль «Вызова сервера» программа упадет с ошибкой непосредственно во время работы пользователя (в рантайме).Поскольку исходный модуль ОМ1 является клиент-серверным, предполагается, что его функции могут выполняться в обоих контекстах. Но если логика внутри функции требует обращения к базе данных или специфическим серверным механизмам (как в случае с получением кадровых данных сотрудников), этот участок кода должен отрабатывать только тогда, когда сам модуль ОМ1 выполняется на сервере.
Для того чтобы клиентский компилятор проигнорировал вызовы серверных процедур и успешно прошел синтаксический контроль, мы должны использовать инструкции препроцессора. Разберем по шагам, как правильно написать такой код.
Рассмотрим пример реализации:
// Функция располагается в модуле УчетСреднегоЗарабокаКлиентСервер
Функция ПолучитьСведенияОСотруднике(Сотрудник, ДатаСведений) Экспорт
Результат = Новый Структура("Подразделение, Должность");
#Если Сервер Или ВнешнееСоединение Тогда
// Этот блок кода будет скомпилирован исключительно в серверном контексте.
// На клиенте этот фрагмент кода физически не будет существовать при выполнении.
КадровыеДанные = КадровыйУчет.КадровыеДанныеСотрудников(Истина, Сотрудник, "Подразделение, Должность", ДатаСведений);
Если КадровыеДанные.Количество() > 0 Тогда
ЗаполнитьЗначенияСвойств(Результат, КадровыеДанные[0]);
КонецЕсли;
#Иначе
// При необходимости здесь описывается альтернативное поведение на клиенте,
// либо блок оставляется пустым, если на клиенте логика не требуется.
#КонецЕсли
Возврат Результат;
КонецФункции
Проанализируем ситуацию при таком написании кода:
#Если Сервер и #КонецЕсли. Ошибки «Переменная не определена» не возникает.КадровыйУчет происходит напрямую внутри оперативной памяти сервера приложений 1С.Чтобы в дальнейшем избегать подобных коллизий, давайте закрепим общие правила разделения ответственности между общими модулями, принятые в Библиотеке стандартных подсистем (БСП) и стандартах разработки фирмы «1С»:
Клиент (например, УчетСреднегоЗаработкаКлиент): Предназначены только для интерактивных действий. Здесь пишется логика открытия форм, предупреждений пользователя, работы с файловой системой клиента. Такие модули никогда не должны содержать запросов к базе данных.Сервер (например, КадровыйУчет): Чисто серверная бизнес-логика. Они работают с транзакциями, запросами, выполняют запись в базу данных. Флаг «Вызов сервера» для них должен быть отключен.КлиентСервер (например, УчетСреднегоЗарабокаКлиентСервер): Легковесные трансляторы и вычислители. Они должны содержать алгоритмы, работающие одинаково быстро везде: математические расчеты, преобразования строк, несложные алгоритмы валидации данных (проверка ИНН, КПП и т.д.). Любые обращения к серверным методам БД внутри них обязательно должны экранироваться инструкциями препроцессора.ВызовСервера (например, КадровыйУчетВызовСервера): Мосты между клиентом и сервером. Они создаются исключительно для того, чтобы клиентская форма могла быстро забрать необходимые данные с сервера за один серверный вызов. Код в них должен быть максимально лаконичным и оптимизированным.Придерживаясь этих простых правил проектирования архитектуры, вы обеспечите высокую скорость работы системы, простоту обновления конфигурации и отсутствие неочевидных ошибок при синтаксическом контроле метаданных.