Функции во встроенном языке 1С 8.3: объявление, вызов и место в модулях

· 11 мин

Содержание

Функцию в 1С задают блоком Функция Имя(Параметры) … КонецФункции, а результат отдают оператором Возврат Значение;. Чтобы функцию можно было вызвать из другого модуля, после списка параметров пишут слово Экспорт. Всё остальное — где разместить код, как передать параметры и на какой стороне он выполнится — решает, будет ли функция работать так, как вы ждёте.

Типичная боль начинающего программиста 1С: функция написана, синтаксис-контроль молчит, а при вызове из формы — «метод не обнаружен» или параметр внезапно изменился у вызывающего кода. Ниже — синтаксис, правила размещения в модулях, тонкости параметров и рабочий пример для платформы 8.3 в управляемом интерфейсе.

Функция и процедура в 1С: в чём разница

Во встроенном языке 1С есть два вида методов: процедура и функция. Разница одна, но принципиальная.

  • 🔹 Процедура выполняет действия и ничего не возвращает. Её вызывают отдельной строкой.
  • 🔹 Функция выполняет действия и возвращает значение. Её можно поставить в выражение: присвоить переменной, передать в другой метод, использовать в условии.
ПризнакПроцедураФункция
Ключевые словаПроцедура … КонецПроцедурыФункция … КонецФункции
Возврат значениянет, Возврат; только выходит из методаВозврат Значение;
Использование в выражениинельзяможно
Вызов отдельной строкойдада, результат просто отбрасывается

Функцию допустимо вызвать как процедуру — платформа выполнит её и отбросит результат. Обратное не работает : процедура в выражении даст ошибку уже при синтаксическом контроле модуля.

Если метод что-то вычисляет или проверяет — делайте функцию. Если меняет данные и вызывающему коду нечего сообщить — процедуру. Функция с побочными эффектами (пишет в базу и при этом что-то возвращает) допустима, но имя должно это показывать: ЗаписатьИПолучитьНомер(), а не ПолучитьНомер().

Как объявить функцию в 1С: синтаксис и вызов

Полная форма объявления:

&НаСервере
Функция ИмяФункции(Параметр1, Знач Параметр2, Параметр3 = 0) Экспорт

	// тело функции
	Результат = Параметр1 + Параметр2 * Параметр3;
	Возврат Результат;

КонецФункции

Разберём по частям:

  1. &НаСервере — директива компиляции. Нужна в модулях форм и в общих модулях с несколькими контекстами. В модуле объекта и менеджера её не пишут.
  2. Функция — ключевое слово. Регистр не важен, но принято писать с заглавной.
  3. ИмяФункции — идентификатор: буквы, цифры, подчёркивание, первым символом не цифра. Пробелы недопустимы.
  4. Список параметров в круглых скобках через запятую. Скобки обязательны даже без параметров: Функция ТекущийПользовательИмя().
  5. Экспорт — делает функцию доступной снаружи модуля. Без него она видна только внутри своего модуля.
  6. Возврат — завершает функцию и отдаёт значение. Операторов Возврат может быть несколько, в разных ветках.
  7. КонецФункции — закрывает блок. Точку с запятой после него не ставят.

Как вызвать функцию

Внутри того же модуля — просто по имени:

Сумма = ИмяФункции(100, 20, 3); // 100 + 20 * 3 = 160

Из другого модуля — через имя общего модуля или менеджера объекта:

Курс = РаботаСВалютамиСервер.КурсНаДату(Валюта, ТекущаяДатаСеанса());
Остаток = Справочники.Номенклатура.ОстатокПоСкладу(Номенклатура, Склад);

Второй вариант работает, если функция КурсНаДату объявлена с Экспорт в общем модуле, а ОстатокПоСкладу — с Экспорт в модуле менеджера справочника. Имена модулей в примерах условные — подставьте свои.

Что вернёт функция без Возврат

Если выполнение дошло до КонецФункции, не встретив Возврат, функция возвращает Неопределено. Ошибки нет, и это коварно: вызывающий код получит пустое значение и упадёт дальше, в совсем другом месте.

Проверяйте, что каждая ветка Если … ИначеЕсли … Иначе заканчивается Возврат. Надёжный приём — завести переменную Результат в начале, заполнять её по веткам и вернуть одной строкой в конце.

Куда поместить функцию: модуль формы, модуль объекта или общий модуль

Место хранения определяет, кто сможет вызвать функцию и на какой стороне — клиенте или сервере — она выполнится.

Где лежит функцияКто вызываетКогда выбирать
Модуль формытолько эта формаработа с реквизитами и элементами формы
Модуль объектаметоды этого объекта: ДокументОбъект.Метод()логика конкретного объекта: заполнение, проверки перед записью
Модуль менеджераДокументы.ИмяДокумента.Метод()действия без открытого объекта: печать, выборки по виду объектов
Общий модульлюбой код конфигурацииповторно используемые функции: строки, даты, JSON, обращения к сервисам

Как создать общий модуль

  1. В Конфигураторе откройте дерево конфигурации, ветку Общие → Общие модули.
  2. Правой кнопкой — Добавить. Задайте имя, например ОбщегоНазначенияСервер.
  3. В палитре свойств отметьте, где модуль будет компилироваться: Сервер, Клиент (управляемое приложение), Внешнее соединение.
  4. Если серверные функции модуля должны вызываться с клиента, включите флажок Вызов сервера.
  5. Откройте модуль, напишите функцию со словом Экспорт.

Удобно называть модули по суффиксу контекста: …Сервер, …Клиент, …ВызовСервера, …КлиентСервер. Тогда по вызову сразу видно, где выполнится код.

Директивы компиляции в модуле формы

В модуле управляемой формы каждая функция помечается директивой:

  • 🖥️ &НаКлиенте — выполняется у пользователя, видит элементы формы, но не может обращаться к базе напрямую.
  • 🗄️ &НаСервере — выполняется на сервере, получает данные формы, может делать запросы. Вызов передаёт данные формы на сервер и обратно.
  • ⚡ &НаСервереБезКонтекста — выполняется на сервере без передачи формы. Самый лёгкий серверный вызов: передаются только параметры.
  • 🔁 &НаКлиентеНаСервереБезКонтекста — компилируется в обоих контекстах; подходит для чистых вычислений без обращения к базе.

Если серверной функции не нужны реквизиты формы, ставьте &НаСервереБезКонтекста. На тонком клиенте и вебе разница в скорости заметна сразу.

Повторное использование возвращаемых значений

У общего модуля есть свойство Повторное использование возвращаемых значений с вариантами На время вызова и На время сеанса. Платформа кэширует результат экспортной функции по набору параметров и при повторном вызове не выполняет код заново.

Это подходит для редко меняющихся данных: настроек, констант, списков ролей. Возвращайте из таких функций фиксированные коллекции — ФиксированнаяСтруктура, ФиксированныйМассив, чтобы вызывающий код случайно не испортил закэшированное значение.

Параметры функции в 1С: по ссылке, Знач и значения по умолчанию

По ссылке — поведение по умолчанию

Без ключевого слова параметр передаётся по ссылке. Если функция присвоит параметру новое значение, оно изменится и у вызывающего кода:

Функция Удвоить(Число)
	Число = Число * 2;
	Возврат Число;
КонецФункции

А = 5;
Б = Удвоить(А); // Б = 10, и А тоже стало 10

Знач — передача копии

Слово Знач перед параметром передаёт копию значения. Присваивание внутри функции наружу не уходит:

Функция Удвоить(Знач Число)
	Число = Число * 2;
	Возврат Число;
КонецФункции

А = 5;
Б = Удвоить(А); // Б = 10, А осталось 5

С коллекциями Знач не защищает содержимое. Для массива, структуры или таблицы значений копируется ссылка на объект, а не сам объект. Метод Массив.Добавить() внутри функции изменит исходный массив. Защищает Знач только от присваивания параметру нового объекта.

Нужна независимая копия коллекции — делайте её явно: новый массив с переносом элементов, ТаблицаЗначений.Скопировать(), для структур — новый объект с переносом пар ключ–значение.

Знач при серверных вызовах

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

Параметры по умолчанию

Необязательному параметру задают значение после знака =:

Функция ФорматСуммы(Сумма, Валюта = "руб.", ЗнаковПослеЗапятой = 2) Экспорт
	ФорматнаяСтрока = "ЧДЦ=" + Формат(ЗнаковПослеЗапятой, "ЧГ=0");
	Возврат Формат(Сумма, ФорматнаяСтрока) + " " + Валюта;
КонецФункции

Строка1 = ФорматСуммы(1500);          // 1 500,00 руб.
Строка2 = ФорматСуммы(1500, "USD", 0);  // 1 500 USD
Строка3 = ФорматСуммы(1500, , 0);     // 1 500 руб.

Правила:

  • 📌 Значение по умолчанию — только литерал: число, строка, дата, Истина, Ложь, Неопределено. Выражение или вызов функции подставить нельзя.
  • 📌 Пропустить параметр из середины можно, оставив пустое место между запятыми, как в Строка3.
  • 📌 Переменного числа параметров язык не поддерживает. Если аргументов много или их набор меняется — передавайте одну структуру с настройками.

Пример функции для работы с JSON в 1С

Задача, которая встречается в каждой интеграции: превратить структуру в строку JSON и обратно. С платформы 8.3.6 для этого есть объекты ЗаписьJSON, ЧтениеJSON и методы глобального контекста ЗаписатьJSON(), ПрочитатьJSON().

Создайте серверный общий модуль РаботаСJSON и поместите в него две функции:

// Преобразует структуру, массив или соответствие в строку JSON.
Функция ЗначениеВJSON(Знач Значение) Экспорт

	Запись = Новый ЗаписьJSON;
	Запись.УстановитьСтроку();
	ЗаписатьJSON(Запись, Значение);
	Возврат Запись.Закрыть();

КонецФункции

// Разбирает строку JSON. При ошибке возвращает Неопределено
// и пишет описание в параметр ТекстОшибки.
Функция JSONВЗначение(Знач ТекстJSON, ТекстОшибки = "", Знач ВСоответствие = Ложь) Экспорт

	ТекстОшибки = "";
	Если ПустаяСтрока(ТекстJSON) Тогда
		ТекстОшибки = "Передана пустая строка";
		Возврат Неопределено;
	КонецЕсли;

	Чтение = Новый ЧтениеJSON;
	Чтение.УстановитьСтроку(ТекстJSON);
	Попытка
		Результат = ПрочитатьJSON(Чтение, ВСоответствие);
	Исключение
		ТекстОшибки = ОписаниеОшибки();
		Результат = Неопределено;
	КонецПопытки;
	Чтение.Закрыть();

	Возврат Результат;

КонецФункции

Вызов:

Данные = Новый Структура("Номер, Сумма, Оплачен", "А-125", 4800, Истина);
Текст = РаботаСJSON.ЗначениеВJSON(Данные);
// Текст — объект JSON с полями Номер, Сумма, Оплачен

ТекстОшибки = "";
Ответ = РаботаСJSON.JSONВЗначение(Текст, ТекстОшибки);
Если Ответ = Неопределено Тогда
	ВызватьИсключение ТекстОшибки;
КонецЕсли;
Сообщить(Ответ.Сумма); // 4800

Что в примере сделано намеренно:

  • ✅ ТекстОшибки передан без Знач — это выходной параметр, через него функция сообщает причину сбоя. Так функция возвращает и результат, и диагностику.
  • ✅ Параметр ВСоответствие по умолчанию Ложь, и ответ читается в Структура. Если ключи в JSON не подходят под имена 1С — с дефисом, пробелом или начинаются с цифры, например order-id, — передайте Истина: результат будет Соответствие, где допустимы любые строковые ключи.
  • ✅ Попытка … Исключение стоит только вокруг разбора, где ошибка ожидаема: невалидный JSON от внешнего сервиса — обычная ситуация.

ЗаписатьJSON() принимает не любые значения. Ссылки на справочники и документы, таблицы значений и соответствия с нестроковыми ключами без настроек сериализации вызовут исключение. Перед записью переложите данные в структуры и массивы из примитивов: строк, чисел, дат, булевых.

Как сделать функцию быстрой и понятной

Функцию читают чаще, чем пишут. Производительность в 1С чаще всего теряется не в вычислениях, а в лишних обращениях к базе и серверу.

Производительность

  • 🚀 Нет запросам в цикле. Сто вызовов функции, каждый с запросом по одной ссылке, — это сто запросов. Передайте в функцию массив ссылок и сделайте один запрос с условием В (&Ссылки).
  • 🚀 Нет серверным вызовам в цикле на клиенте. Каждый вызов — сетевой обмен. Соберите данные и вызовите сервер один раз.
  • 🚀 Не открывайте объект ради одного реквизита. Ссылка.ПолучитьОбъект() читает объект целиком с табличными частями. Для одного-двух полей хватит запроса или функции БСП ОбщегоНазначения.ЗначениеРеквизитаОбъекта(), если библиотека есть в конфигурации.
  • 🚀 Ранний выход. Проверьте входные параметры в начале и сразу верните результат, если работать не с чем. Меньше вложенных Если, меньше лишней работы.
  • 🚀 Кэш для неизменных данных — через модуль с повторным использованием значений, описанный выше.

Читаемость

  • 📖 Имя начинается с глагола или отвечает на вопрос: ПолучитьКурс, ЭтоОрганизацияПлательщикНДС, СуммаПрописью.
  • 📖 Одна функция — одна задача. Если в имени просится «И», функцию стоит разделить.
  • 📖 Перед экспортной функцией — комментарий с описанием параметров и возвращаемого значения. Конфигуратор и EDT показывают его во всплывающей подсказке при наборе.
  • 📖 Экспортные функции общих модулей группируют в области #Область ПрограммныйИнтерфейс … #КонецОбласти, служебные — в СлужебныеПроцедурыИФункции.

Шаблон комментария, который понимает контекстная подсказка:

// Возвращает сумму документа с учётом скидки.
//
// Параметры:
//  Сумма   - Число - сумма до скидки.
//  Процент - Число - процент скидки, от 0 до 100.
//
// Возвращаемое значение:
//  Число - сумма после скидки, округлённая до копеек.
//
Функция СуммаСоСкидкой(Знач Сумма, Знач Процент = 0) Экспорт
	Возврат Окр(Сумма * (100 - Процент) / 100, 2);
КонецФункции

Проверка: СуммаСоСкидкой(1250, 12) даёт 1250 × 88 / 100 = 1100.

Ошибки при написании функций в 1С и как их найти

Диагностика: выберите симптом

Ещё одна ловушка, которую не ловит синтаксический контроль, — рекурсия без надёжного условия выхода. Рекурсия в 1С разрешена, но функция обхода иерархии, которая наткнётся на зацикленные данные (элемент ссылается сам на себя через цепочку родителей), будет вызывать себя до исчерпания стека. Добавьте ограничение глубины или храните уже пройденные ссылки в соответствии и пропускайте их.

Для отладки поставьте точку останова (F9) на первую строку функции и войдите в неё по F11. В окне Локальные видны значения всех параметров, а окно вычисления выражения (Shift+F9) покажет значение любого выражения прямо в контексте функции.

Частые вопросы о функциях в 1С

Как вернуть из функции несколько значений?
Верните структуру: Возврат Новый Структура("Сумма, НДС", Сумма, НДС);. Другой путь — выходные параметры без Знач, но структура читается проще.
Можно ли объявить функцию внутри другой функции?
Нет. Во встроенном языке 1С вложенных функций и лямбда-выражений нет. Вспомогательную функцию объявляют рядом, в том же модуле, без Экспорт.
Как вызвать функцию по имени, записанному в строке?
Через Вычислить("ИмяМодуля.ИмяФункции(Параметр)"). Имя должно приходить только из вашего кода, а не от пользователя: иначе через Вычислить можно выполнить произвольный код.
В каком порядке объявлять функции в модуле?
Порядок не важен: функцию можно вызвать выше по тексту, чем она объявлена. Исключение — переменные модуля: их объявляют в самом начале, до процедур и функций.
Можно ли сделать функцию с переменным числом параметров?
Нет. Используйте необязательные параметры со значениями по умолчанию или передайте одну структуру с нужными полями.