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

· 9 мин

Содержание

Функция во встроенном языке 1С:Предприятие 8.3 объявляется ключевым словом Функция, закрывается строкой КонецФункции и обязательно возвращает значение через оператор Возврат. Простейший пример — три строки:

Функция СуммаСНДС(Сумма, Ставка = 20) Экспорт
    Возврат Сумма + Сумма * Ставка / 100;
КонецФункции

Главное отличие функции от процедуры — именно возвращаемое значение. Процедура выполняет действия и ничего не отдаёт наружу, функцию же вызывают там, где нужен результат: в присваивании, в условии, в выражении. Тип этого результата заранее не объявляют — язык 1С не требует указывать, что вернётся: число, строка, булево или ссылка.

Ниже — синтаксис объявления, как передавать параметры, чем Возврат в функции отличается от Возврат в процедуре, где физически размещать код и какие ошибки повторяются у начинающих разработчиков.

Чем функция отличается от процедуры

Процедура и функция — два вида методов. Пишутся почти одинаково, разница в одном: функция возвращает значение, процедура — нет.

ПризнакПроцедураФункция
Ключевые словаПроцедура … КонецПроцедурыФункция … КонецФункции
Возврат значениянетда, через Возврат Значение;
Оператор Возвратбез значения, досрочный выходсо значением, обязателен
Где вызываютотдельной строкойв выражении, присваивании, условии

Процедуру вызывают командой — отдельной инструкцией:

ПровестиДокумент(Ссылка);

Функцию вызывают там, где ждут результат:

Итого = СуммаСНДС(1000);       // 1200
Если СуммаСНДС(1000) > Лимит Тогда
    // ...
КонецЕсли;

Практическое правило: если метод отвечает на вопрос «сколько?», «какой?», «истина или ложь?» — это функция. Если он что-то делает («провести», «записать», «заполнить») — процедура.

Формально вызвать функцию можно и как процедуру — отдельной строкой, проигнорировав результат. Платформа это допускает, но смысла в таком вызове обычно нет: вы посчитали значение и выбросили его.

Синтаксис объявления и имя метода

Полная форма объявления выглядит так:

&НаСервереБезКонтекста
Функция РассчитатьМаржу(Выручка, Знач Себестоимость, Валюта = Неопределено) Экспорт
    // тело функции
    Возврат Результат;
КонецФункции

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

  • 🏷 Директива компиляции (&НаСервере, &НаКлиенте и др.) — ставится строкой выше объявления. Нужна в модулях управляемых форм и общих модулях; в модулях объектов её не пишут.
  • 🔑 Ключевое слово Функция.
  • 📛 Имя — любое допустимое имя: буквы, цифры, подчёркивание, не начинается с цифры. Русские и латинские имена работают одинаково, но в пределах проекта лучше держать один язык. Имя должно говорить, что метод возвращает: ПолучитьКурсВалюты, а не Обработка1.
  • ( ) Список параметров — может быть пустым.
  • 🟡 Экспорт — необязательное слово. С ним метод виден из других модулей, без него — только внутри своего.

Имя функции не может совпадать с именем другого метода или переменной в том же модуле. Если объявить две функции с одинаковым именем, компилятор выдаст ошибку «Метод с указанным именем уже определён» ещё до запуска.

Параметры: по значению и по ссылке, значения по умолчанию

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

Передача по ссылке — поведение по умолчанию. Если внутри метода изменить значение параметра, изменится и переменная, которую передали при вызове:

Процедура Удвоить(Число)
    Число = Число * 2;
КонецПроцедуры

Х = 5;
Удвоить(Х);   // теперь Х = 10

Передача по значению — через ключевое слово Знач. Внутри метода работает копия, исходная переменная не меняется:

Процедура Удвоить(Знач Число)
    Число = Число * 2;   // меняется только копия
КонецПроцедуры

Х = 5;
Удвоить(Х);   // Х по-прежнему 5

Передача по ссылке касается и структур, таблиц значений, массивов. Даже если такой параметр объявлен со Знач, копируется только сама ссылка на коллекцию, а не её содержимое — очистив массив внутри метода, вы очистите его и снаружи. Знач защищает от переприсваивания, но не от изменения внутренностей коллекции.

Значения по умолчанию задают через = после имени параметра. Такой параметр при вызове можно не указывать:

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

Округлить(3.14159);      // 3.14 — взято умолчание
Округлить(3.14159, 0);   // 3

Параметры со значением по умолчанию обязаны идти последними — после обязательных. Чтобы пропустить средний необязательный параметр, оставляют пустое место между запятыми: Метод(А, , В).

Оператор Возврат и типы возвращаемого значения

В функции Возврат обязателен и несёт значение. Как только выполнение дошло до Возврат, функция завершается и отдаёт указанное значение в точку вызова.

Функция ЗнакЧисла(Число)
    Если Число > 0 Тогда
        Возврат 1;
    ИначеЕсли Число < 0 Тогда
        Возврат -1;
    КонецЕсли;
    Возврат 0;
КонецФункции

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

Тип значения жёстко не задан. Одна и та же функция теоретически может вернуть и число, и строку в разных ветках — платформа это разрешит. На практике так делать не стоит: вызывающий код рассчитывает на один тип. Если функция логически возвращает «значение или ничего», возвращайте Неопределено как признак отсутствия результата и проверяйте его на стороне вызова.

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

Функция РазложитьСумму(Сумма, Ставка = 20)
    Результат = Новый Структура;
    Результат.Вставить("НДС", Сумма * Ставка / (100 + Ставка));
    Результат.Вставить("БезНДС", Сумма - Сумма * Ставка / (100 + Ставка));
    Возврат Результат;
КонецФункции

Вызывающий код получает структуру и обращается к полям по имени: Разбор = РазложитьСумму(1200); НДС = Разбор.НДС;. Для суммы 1200 и ставки 20 это 1200 × 20 / 120 = 200.

В процедуре Возврат тоже допустим, но без значения — он просто досрочно прерывает выполнение:

Процедура Обработать(Элемент)
    Если Не ЗначениеЗаполнено(Элемент) Тогда
        Возврат;   // выходим, дальше не идём
    КонецЕсли;
    // основная логика
КонецПроцедуры

Где размещать код: модули и область видимости

Функция живёт внутри модуля. От того, в каком модуле она объявлена и стоит ли у неё Экспорт, зависит, откуда её можно вызвать.

Основные типы модулей и что в них держать:

  • 🧩 Общие модули (ветка Общие → Общие модули в дереве конфигурации) — для переиспользуемого кода, который нужен в разных местах. Именно сюда выносят функции-утилиты: расчёты, преобразования, обращения к данным.
  • 📄 Модуль формы — обработчики событий формы и её элементов, вспомогательные методы конкретной формы.
  • 📦 Модуль объекта (документа, справочника) — логика самого объекта: заполнение, проверка, события записи и проведения.
  • 🗂 Модуль менеджера — методы, относящиеся не к конкретному элементу, а к объекту в целом.

Область видимости определяется словом Экспорт:

  • Без Экспорт функция видна только внутри своего модуля — это локальный метод.
  • С Экспорт в общем модуле — функция доступна отовсюду через имя модуля: ОбщегоНазначения.РассчитатьМаржу(...).
  • С Экспорт в модуле объекта — доступна через ссылку на объект.

У общих модулей есть флажки контекста компиляции, которые задают в палитре свойств модуля: «Сервер», «Клиент (управляемое приложение)», «Вызов сервера». Они определяют, откуда разрешено вызывать экспортные методы модуля. Для функции, которая обращается к базе данных, нужен серверный контекст — иначе получите ошибку компиляции о недоступности метода на клиенте.

Директивы компиляции над самим объявлением уточняют это для конкретного метода. Самые ходовые:

  • &НаСервере — метод формы, выполняется на сервере, имеет доступ к данным формы.
  • &НаКлиенте — выполняется на клиенте, работает с интерфейсом и реагирует на действия пользователя.
  • &НаСервереБезКонтекста — серверный метод без доступа к данным формы. Самый лёгкий по ресурсам: контекст формы на сервер не передаётся. Для чистых расчётов выбирайте именно его.

Если директиву в модуле управляемой формы не указать, метод компилируется по умолчанию как серверный. В общих модулях директиву над методом не ставят — контекст задают флажками всего модуля.

Если один и тот же расчёт нужен в нескольких документах — не копируйте функцию в каждый модуль объекта. Вынесите её в общий модуль с Экспорт и вызывайте оттуда. Меньше дублирования — меньше мест для правки.

Практический пример: функция расчёта показателя

Соберём всё вместе. Задача — посчитать рентабельность по выручке и себестоимости, с защитой от деления на ноль. Функцию разместим в общем модуле ОбщегоНазначения с директивой &НаСервереБезКонтекста — она не трогает ни форму, ни контекст объекта, только считает.

&НаСервереБезКонтекста
Функция РентабельностьПродаж(Знач Выручка, Знач Себестоимость) Экспорт

    Если Выручка = 0 Тогда
        Возврат 0;
    КонецЕсли;

    Прибыль = Выручка - Себестоимость;
    Рентабельность = Прибыль / Выручка * 100;

    Возврат Окр(Рентабельность, 2);

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

Вызов из модуля формы или другого метода:

Показатель = ОбщегоНазначения.РентабельностьПродаж(1000, 600);
// Прибыль = 400, 400 / 1000 * 100 = 40, результат 40

Разберём, что здесь сделано правильно:

  1. Защита граничного случая — проверка Выручка = 0 стоит в начале, до деления. Иначе при нулевой выручке функция упадёт с ошибкой деления на ноль.
  2. Параметры со Знач — функция только читает их, менять исходные переменные ей незачем.
  3. Единственный тип результата — всегда число. Нет веток, которые вернули бы строку или Неопределено.
  4. Говорящее имя — по вызову понятно, что получим рентабельность в процентах.
Проверьте себя
Что вернёт функция, если выполнение дойдёт до `КонецФункции`, не встретив ни одного оператора `Возврат`?
Каким ключевым словом параметр передают по значению, защищая исходную переменную от изменения?
Где разместить функцию-расчёт, чтобы вызывать её из нескольких документов?

Частые ошибки при создании функций

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

  • 🔴 Функция не возвращает значение по всем веткам. Условие покрыло не все случаи, и в одной из веток нет Возврат. Результат — Неопределено там, где ждали число. Лечится финальным Возврат после всех условий.
  • 🔴 Вызов серверной функции с клиента напрямую. В управляемом приложении код клиента и сервера разделён директивами. Клиентский метод не может напрямую дёрнуть серверную функцию, работающую с базой. Нужен серверный вызов — через общий модуль с флагом «Вызов сервера» или серверный метод формы.
  • 🔴 Изменение параметра без Знач по невнимательности. Передали переменную в метод, внутри её переприсвоили — и снаружи она неожиданно изменилась. Если менять исходные данные не нужно, ставьте Знач.
  • 🔴 Необязательный параметр не в конце. Параметр со значением по умолчанию стоит перед обязательным — компилятор не пропустит.
  • 🔴 Тяжёлые вычисления и запросы внутри цикла по строкам. Функция дёргает базу на каждой итерации. Для таблицы в тысячи строк это десятки секунд. Запрос выносят за цикл, а в функцию передают уже готовые данные.

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

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

Частые вопросы

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