Общие модули 1С: синтаксис вызова, флаги и ошибки
Содержание
Чтобы вызвать процедуру общего модуля, пишут ИмяМодуля.ИмяПроцедуры(Параметры), а саму процедуру в модуле помечают ключевым словом Экспорт. Если модуль объявлен глобальным, имя модуля не указывают — обращаются прямо по имени процедуры. Это весь базовый синтаксис; всё остальное — нюансы контекста (клиент или сервер) и настройки флагов самого модуля.
Общий модуль — это общий склад переиспользуемого кода: процедуры и функции, которые нужны в разных местах конфигурации. Положили расчёт один раз, дёргаете из документов, обработок и форм. Разница между «работает» и «метод не обнаружен» почти всегда в двух вещах: забыли Экспорт или вызвали серверный код оттуда, где он не компилируется.
Ниже — синтаксис обращения, разбор флагов модуля, передача параметров и возврат значений, а также частые ошибки с конкретной причиной.
Где лежит общий модуль и как его создать
Общие модули собраны в дереве конфигурации в ветке Общие → Общие модули. Чтобы добавить новый, встаньте на эту ветку и нажмите «Добавить» (зелёный плюс в командной панели дерева). В свойствах нового модуля задаёте имя и отмечаете флаги контекста.
Имя модуля — это идентификатор, по которому к нему обращаются из кода, поэтому оно должно быть говорящим. Сложился стандарт именования по контексту:
- 📦 суффикс
Сервер— для серверных модулей (РаботаСФайламиСервер); - 📦 суффикс
Клиент— для клиентских (РаботаСФайламиКлиент); - 📦 суффикс
КлиентСервер— для модулей с кодом, который работает и там, и там; - 📦 суффикс
ВызовСервера— для серверных модулей, которые дёргают с клиента.
Суффиксы — не требование платформы, а соглашение, но оно сильно упрощает жизнь: по имени модуля сразу видно, откуда его можно вызывать. Код методов пишут в модуле объекта — открывается двойным щелчком по модулю или из контекстного меню.
Не сваливайте всё в один гигантский модуль. Разбивайте по смыслу: отдельно работа с файлами, отдельно расчёты, отдельно обмен. Так проще искать и меньше конфликтов при командной разработке.
Синтаксис обращения к процедуре и функции
Обращение строится через точку: имя общего модуля, затем имя метода.
// Вызов процедуры серверного общего модуля "РаботаСДокументами"
РаботаСДокументами.ПровестиДокумент(СсылкаНаДокумент);
// Вызов функции с получением результата
Итог = РасчётыОбщие.ВычислитьСумму(ТаблицаСтрок);
Имя модуля — это имя объекта метаданных, как оно задано в дереве конфигурации (ветка Общие → Общие модули), а не синоним и не заголовок. Регистр не важен, но лучше писать как в метаданных.
Процедура (Процедура … КонецПроцедуры) значения не возвращает — её вызывают отдельным оператором. Функция (Функция … КонецФункции) возвращает результат через Возврат, поэтому её вызов ставят в правой части присваивания или внутри выражения.
Если нужно обратиться к методу модуля из его же тела, имя модуля писать не надо — локальные методы видны напрямую.
Чтобы метод был виден снаружи модуля, он обязан быть экспортным:
Процедура ПровестиДокумент(Ссылка) Экспорт
// тело процедуры
КонецПроцедуры
Без ключевого слова Экспорт процедура остаётся внутренней: снаружи вызвать её нельзя, компилятор выдаст ошибку о необнаруженном методе.
Флаги общего модуля: где выполняется код
Самое важное отличие общего модуля от модуля формы: контекст исполнения задают не директивы на процедурах, а флаги самого модуля. Открываете свойства общего модуля (F4 на объекте) и отмечаете галочки.
| Флаг | Что включает |
|---|---|
| Клиент (управляемое приложение) | Код компилируется и исполняется на клиенте |
| Сервер | Код исполняется на сервере |
| Внешнее соединение | Доступен при работе через внешнее соединение (COM) |
| Вызов сервера | Разрешает обращаться к серверному модулю из клиентского кода |
| Привилегированный | Код работает без проверки прав доступа |
| Глобальный | Методы доступны без указания имени модуля |
Можно отметить и клиент, и сервер одновременно — тогда один и тот же код скомпилируется в обоих контекстах. Но выполняться он будет там, откуда его вызвали.
Практическое правило: серверную логику (запросы, проведение, работу с базой) кладут в модуль с флагом «Сервер», а мелкие клиентские помощники (предупреждения, вопросы пользователю) — в модуль «Клиент (управляемое приложение)».
Вызов сервера: клиент-серверное взаимодействие
В клиент-серверной базе клиент и сервер — это два разных процесса. Клиентский код не может напрямую трогать базу данных; для этого он обращается к серверу, и данные едут туда и обратно по сети.
Чтобы вызвать серверный общий модуль прямо из клиентского кода (например, из обработчика команды формы), у этого модуля должен стоять флаг «Вызов сервера». Тогда обращение из клиента к такому методу выполняется как серверный вызов: платформа сама сериализует параметры, выполняет метод на сервере и возвращает результат.
&НаКлиенте
Процедура ОбновитьКурсы(Команда)
// ВалютыСервер — серверный модуль с флагом "Вызов сервера"
ВалютыСервер.ЗагрузитьКурсыНаСегодня();
КонецПроцедуры
Есть ограничение, о которое спотыкаются чаще всего: через серверный вызов нельзя передавать и возвращать мутабельные прикладные объекты, которые не поддерживают передачу между клиентом и сервером. Примитивы (число, строка, дата, булево), ссылки, структуры и таблицы значений ходят свободно; а, например, объект документа (ДокументОбъект) в клиентский контекст так не вернуть.
Каждый серверный вызов — это обращение по сети. Десять вызовов в цикле — это десять раундтрипов. Собирайте данные в одну структуру и передавайте одним вызовом.
Сравните два подхода. Плохой — дёргать сервер в цикле:
// Антипример: N серверных вызовов
Для Каждого Строка Из Список Цикл
ОстаткиСервер.ОбновитьОстаток(Строка.Товар); // вызов за итерацию
КонецЦикла;
Хороший — собрать параметры и сходить на сервер один раз:
// Один серверный вызов на весь список
Товары = Новый Массив;
Для Каждого Строка Из Список Цикл
Товары.Добавить(Строка.Товар);
КонецЦикла;
ОстаткиСервер.ОбновитьОстатки(Товары);
На коротком списке разница незаметна, на сотнях строк — это секунды против долей секунды.
Ещё одна частая путаница — директивы компиляции. Разработчик пробует поставить &НаСервере или &НаКлиенте над процедурой общего модуля, как в модуле формы. В общих модулях директивы компиляции не используются — контекст полностью определяют флаги модуля.
Директивы (&НаКлиенте, &НаСервере, &НаСервереБезКонтекста) ставят в модулях форм, команд и в модуле управляемого приложения, где в одном файле уживается и клиентский, и серверный код. В общем модуле весь код работает в том контексте, который задан галочками, — отдельная пометка процедурам не нужна.
Отсюда простое следствие: если нужен метод и на клиенте, и на сервере, делают модуль с обоими флагами (Клиент + Сервер). Платформа скомпилирует две копии, и каждая сторона вызовет свою.
Передача параметров и возврат значений
Параметры в 1С по умолчанию передаются по ссылке: если процедура меняет переданную переменную, изменение видно у вызывающего кода. Чтобы запретить это и передать значение копией, перед параметром ставят ключевое слово Знач.
// Таблица передаётся копией — исходная не изменится
Функция ВычислитьСумму(Знач Таблица) Экспорт
Итог = 0;
Для Каждого Строка Из Таблица Цикл
Итог = Итог + Строка.Сумма;
КонецЦикла;
Возврат Итог;
КонецФункции
Функция обязана вернуть значение через Возврат. Если выполнение дойдёт до КонецФункции без Возврат, функция вернёт Неопределено — это законно, но часто это и есть скрытая причина ошибки «значение не определено» дальше по коду.
Вернуть можно что угодно: число, строку, структуру, массив, таблицу значений. Когда результатов несколько, удобно собрать их в структуру:
Функция РассчитатьИтоги(Знач ТаблицаСтрок) Экспорт
Результат = Новый Структура("Сумма, Количество", 0, 0);
Для Каждого Строка Из ТаблицаСтрок Цикл
Результат.Сумма = Результат.Сумма + Строка.Сумма;
Результат.Количество = Результат.Количество + 1;
КонецЦикла;
Возврат Результат;
КонецФункции
Выходные параметры тоже работают: передаёте переменную без Знач, процедура её заполняет, вызывающий код читает результат после вызова.
// Процедура возвращает данные через выходной параметр "Ошибки"
Процедура ПроверитьЗаполнение(Знач Объект, Ошибки) Экспорт
Ошибки = Новый Массив;
Если Не ЗначениеЗаполнено(Объект.Контрагент) Тогда
Ошибки.Добавить("Не заполнен контрагент");
КонецЕсли;
КонецПроцедуры
// Вызов
СписокОшибок = Неопределено;
ПроверкиСервер.ПроверитьЗаполнение(Документ, СписокОшибок);
Такой приём уместен, когда результатов несколько и они разнородны. Но для одного значения чаще читается возврат функции: он нагляднее и не заставляет заранее объявлять переменную.
Привилегированный режим и повторное использование
Два флага модуля прямо влияют на производительность и безопасность.
Привилегированный модуль выполняет код без проверки прав доступа и без наложения ограничений доступа к данным (RLS). Это ускоряет операции и позволяет выполнить то, на что у пользователя прав нет. Платить за это приходится безопасностью: внутри привилегированного кода легко случайно дать доступ в обход настроенных ограничений, поэтому такие модули держат короткими и не пускают туда пользовательский ввод без проверки.
Повторное использование возвращаемых значений — кэш результатов функций. У свойства три значения:
- 🔁
Не использовать— функция считается заново при каждом вызове; - 🔁
На время вызова— результат кэшируется в пределах одного серверного вызова; - 🔁
На время сеанса— результат живёт весь сеанс пользователя.
Кэш хранит результат для конкретного набора параметров: позвали ПолучитьНастройку("Валюта") — платформа запомнила и при повторном вызове с тем же аргументом вернёт из кэша, не выполняя тело.
Повторное использование работает только для функций, а параметры должны быть примитивными или ссылочными. Если функция читает данные, которые меняются в течение сеанса, режим «На время сеанса» вернёт устаревшее значение — используйте его только для редко меняющихся настроек.
Глобальный общий модуль: вызов без имени
Если у модуля стоит флаг «Глобальный», его экспортные методы доступны во всём прикладном решении напрямую — имя модуля писать не нужно.
// В глобальном модуле объявлена процедура ЗаписатьСобытие()
ЗаписатьСобытие("Начало обработки");
Удобно для сквозных помощников, которые дёргают из сотни мест. Но у глобальных модулей есть цена: их содержимое компилируется при старте, попадает в глобальный контекст, и имена методов начинают конкурировать со всеми прочими глобальными именами.
Поэтому глобальными делают немногие, действительно универсальные модули. Для остального предпочтительнее обычный вызов через имя модуля — он нагляднее: сразу видно, откуда метод.
Типичные ошибки вызова и как их устранить
Почти все ошибки обращения к общему модулю сводятся к нескольким причинам. Сопоставьте текст ошибки с таблицей.
Экспорт либо имя написано с опечаткой. Проверьте ключевое слово Экспорт в объявлении и точное имя метода.Возврат) или уберите присваивание.Если вы только проектируете модуль и не уверены, какие флаги ставить, оттолкнитесь от того, откуда будете вызывать метод.
Частые вопросы
Чем вызов процедуры отличается от вызова функции?
Возврат.Нужно ли писать имя модуля при вызове?
ИмяМодуля.Метод(). Для глобального модуля имя не пишут, метод вызывают напрямую. Внутри самого модуля к своим методам тоже обращаются без имени.Почему метод виден в конфигураторе, но не вызывается из другого модуля?
Экспорт. Без него метод доступен только внутри своего модуля.