Вызов функции внутри процедуры на встроенном языке 1С
Содержание
Чтобы вызвать функцию из процедуры, напишите её имя со скобками как обычное выражение и присвойте результат переменной: Итог = ВычислитьНДС(Сумма, 20);. Процедура сама ничего не возвращает, поэтому результат работы она берёт именно у функции — внутри своего тела, в нужный момент.
Разберём это по шагам: чем отличается функция от процедуры, как вызвать функцию в одном модуле и из другого, что происходит с параметрами, можно ли передать функцию «как аргумент» и как ловить ошибки при вызове. Все примеры — на встроенном языке 1С 8.3, конфигурация роли не играет: синтаксис один и тот же в Бухгалтерии, ЗУП и УТ.
Процедура и функция: одно отличие, которое всё решает
Разница всего одна, но она определяет, как вы будете их вызывать.
- 🔹 Функция возвращает значение. Внутри неё есть оператор
Возвратс результатом. Вызов функции — это выражение, у которого есть «ответ»: число, строка, ссылка, таблица значений. - 🔹 Процедура ничего не возвращает. Она выполняет действия — пишет в регистр, меняет реквизиты, выводит сообщение. Вызов процедуры — это отдельный оператор, не выражение.
Объявляются они почти одинаково, различаются ключевые слова:
// Процедура — делает, но не отдаёт результат
Процедура ЗаписатьВЖурнал(Текст)
ЗаписьЖурналаРегистрации("Расчёт", , , , Текст);
КонецПроцедуры
// Функция — вычисляет и отдаёт значение
Функция ВычислитьНДС(Сумма, Ставка)
Возврат Сумма * Ставка / 100;
КонецФункции
Отсюда правило вызова: функцию вы ставите туда, где нужен результат (Налог = ВычислитьНДС(...)), а процедуру вызываете как самостоятельное действие (ЗаписатьВЖурнал("Готово");).
Технически функцию можно вызвать и как процедуру — просто проигнорировать возвращённое значение: написать ВычислитьНДС(Сумма, 20); без присваивания. Платформа не ругается. Но так теряется смысл функции, поэтому делают это редко.
Базовый синтаксис: вызов функции в теле процедуры
Процедура и функция могут лежать в одном модуле — в модуле формы, объекта, общем модуле. Тогда вызов идёт просто по имени, без всяких префиксов.
Процедура РассчитатьДокумент()
Сумма = 100;
Ставка = 20;
// вызов функции — результат сразу в переменную
Налог = ВычислитьНДС(Сумма, Ставка);
// результат можно использовать в другом выражении
ИтогСНДС = Сумма + Налог;
Сообщить("НДС: " + Налог + ", итого: " + ИтогСНДС);
КонецПроцедуры
Функция ВычислитьНДС(Сумма, Ставка)
Возврат Сумма * Ставка / 100;
КонецФункции
В примере 100 * 20 / 100 даёт 20, итог — 120. Функцию можно вызывать прямо внутри другого выражения, не заводя лишнюю переменную:
Сообщить("Итого: " + (Сумма + ВычислитьНДС(Сумма, 20)));
Порядок объявления в модуле значения не имеет: процедура может стоять выше функции, которую вызывает, — платформа компилирует модуль целиком. Важно только, чтобы обе были в области видимости вызова.
В модулях управляемых форм действуют директивы компиляции. Если процедура помечена &НаКлиенте, а функция — &НаСервере, вызов сработает (это серверный вызов), но клиент не сможет вызвать &НаСервереБезКонтекста-функцию с передачей неприметивного значения формы. Держите вызывающую и вызываемую стороны в совместимом контексте.
Вызов функции из другого модуля
Если функция лежит в соседнем модуле, одного имени мало — нужен префикс и ключевое слово Экспорт у самой функции. Без Экспорт функция видна только внутри своего модуля.
Общий модуль — самый частый случай. Пусть есть общий модуль РаботаСНДС с экспортной функцией:
// В общем модуле "РаботаСНДС"
Функция ВычислитьНДС(Сумма, Ставка) Экспорт
Возврат Сумма * Ставка / 100;
КонецФункции
Вызов из любой процедуры — через имя модуля как префикс:
Налог = РаботаСНДС.ВычислитьНДС(Сумма, 20);
Чтобы вызов прошёл, у общего модуля должны быть выставлены флаги контекста (Клиент / Сервер / Внешнее соединение) под то место, откуда вы вызываете. Серверную функцию из клиентского кода вызвать можно, обратное — нет.
Модуль менеджера объекта — функции справочников, документов, регистров. Обращение идёт через менеджер:
Данные = Справочники.Номенклатура.ПолучитьОстаток(Товар);
Модуль объекта — функцию вызывают через конкретную ссылку или объект:
ОбъектДокумента = Документ.ПолучитьОбъект();
Результат = ОбъектДокумента.ПроверитьЗаполнение();
Коротко, куда класть экспортную функцию:
| Где лежит функция | Как вызвать | Когда использовать |
|---|---|---|
| Общий модуль | ИмяМодуля.Функция() | Логика, нужная в нескольких местах |
| Модуль менеджера | Справочники.Имя.Функция() | Операции над видом объектов |
| Модуль объекта | Объект.Функция() | Логика конкретного экземпляра |
Параметры: по значению, по ссылке и значения по умолчанию
По умолчанию параметры в 1С передаются по ссылке: если функция меняет параметр, изменение видно в вызвавшей процедуре. Чтобы передать копию и защитить исходную переменную, ставьте Знач перед именем параметра.
// Знач — функция работает с копией, оригинал не меняется
Функция Удвоить(Знач Число)
Число = Число * 2;
Возврат Число;
КонецФункции
Процедура Проверить()
Х = 5;
Y = Удвоить(Х);
Сообщить("" + Х + " / " + Y); // выведет 5 / 10
КонецПроцедуры
Без Знач переменная Х тоже стала бы равна 10 — функция изменила бы её напрямую. Для функций всегда указывайте Знач, если не собираетесь намеренно менять входной параметр: это убирает незаметные побочные эффекты.
Параметрам можно задать значение по умолчанию — тогда при вызове их разрешено не передавать:
Функция ОкруглитьСумму(Сумма, Разрядность = 2)
Возврат Окр(Сумма, Разрядность);
КонецФункции
// оба вызова корректны
А = ОкруглитьСумму(10.457); // 10.46
Б = ОкруглитьСумму(10.457, 0); // 10
Параметры со значением по умолчанию должны идти в конце списка. Пропускать средний параметр нельзя заполнением пустого места — это не оператор Новый, а список аргументов, где порядок жёсткий.
Как передать функцию «как параметр»
Полноценных указателей на функции во встроенном языке нет — функцию нельзя положить в переменную и дёрнуть позже. Но задачу «вызвать переданную извне логику» решают тремя способами.
1. Передать имя строкой и вызвать через Вычислить. Подходит, когда вызываемая функция видна в текущем модуле.
Процедура ПрименитьОперацию(ИмяФункции, А, Б)
Выражение = ИмяФункции + "(" + А + ", " + Б + ")";
Результат = Вычислить(Выражение);
Сообщить(Результат);
КонецПроцедуры
Вычислить и Выполнить исполняют строку как код в контексте текущего модуля. Никогда не передавайте в них данные, пришедшие от пользователя или из внешнего источника без проверки — это прямой путь к выполнению чужого кода.
2. Оповещение ОписаниеОповещения — штатный механизм обратного вызова (callback) в управляемых формах, в том числе для асинхронных операций.
Оповещение = Новый ОписаниеОповещения("ПослеВыбора", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Продолжить?", РежимДиалогаВопрос.ДаНет);
// отдельная процедура-обработчик вызовется, когда пользователь ответит
&НаКлиенте
Процедура ПослеВыбора(Ответ, ДопПараметры) Экспорт
Если Ответ = КодВозвратаДиалога.Да Тогда
// действия
КонецЕсли;
КонецПроцедуры
3. Передать ссылку на объект с нужным методом. Если «варианты поведения» — это методы разных объектов, передавайте в функцию сам объект, а она вызывает его метод. Так обходятся без динамических вызовов вообще.
Для большинства задач третий путь надёжнее Вычислить: его проверяет компилятор, он быстрее и безопаснее.
Обработка ошибок при вызове функции
Если функция может «упасть» — деление на ноль, разбор некорректной строки, обращение к несуществующему объекту — оберните вызов в Попытка … Исключение … КонецПопытки.
Попытка
Результат = РазобратьЧисло(ВходнаяСтрока);
Исключение
Результат = 0;
ЗаписьЖурналаРегистрации("Разбор", УровеньЖурналаРегистрации.Ошибка,
, , ОписаниеОшибки());
КонецПопытки;
ОписаниеОшибки() возвращает текст последней ошибки. В современных релизах полнее работать через ИнформацияОбОшибке() — она даёт структурированный объект, из которого можно достать и описание, и причину.
Если же ошибку должна «поднимать» сама функция — когда входные данные недопустимы, — используйте ВызватьИсключение:
Функция РазделитьСумму(Сумма, Делитель)
Если Делитель = 0 Тогда
ВызватьИсключение "Делитель не может быть нулём";
КонецЕсли;
Возврат Сумма / Делитель;
КонецФункции
Тогда вызывающая процедура сама решит, ловить это исключение или дать ему прерваться. Не глушите исключения пустым блоком Исключение без записи в журнал: ошибка исчезнет из виду, а данные тихо посчитаются неверно.
Когда выносить код в функцию, а когда не стоит
Вынос логики в отдельную функцию почти всегда оправдан, но не превращайте это в самоцель.
Выносите в функцию, когда:
- ✅ один и тот же расчёт нужен в двух и более местах — иначе копии разойдутся при правках;
- ✅ выражение стало длинным и нечитаемым — имя функции заменит комментарий;
- ✅ логику хочется покрыть отдельной проверкой или переиспользовать в обработке.
Оставьте «по месту», когда:
- 🔸 это одна простая строка, которая нигде больше не встретится;
- 🔸 вынос потребует тащить в функцию десяток параметров — связность выше, чем польза.
О производительности: сам вызов функции в 1С стоит дёшево, им почти никогда не стоит жертвовать читаемостью. Реальная цена появляется, если внутри цикла по тысячам строк функция каждый раз делает запрос к базе. Тогда проблема не в вызове, а в запросе внутри цикла — его и нужно вынести за цикл, а не отказываться от функции.
Частый приём: вынести из процедуры «чистую» функцию без побочных эффектов (только вход → результат, без записи в базу). Такую функцию легко проверить и переиспользовать, а процедура отвечает за запись результата.
Примеры вызова: от простого расчёта до рекурсии
Соберём разобранное в три примера нарастающей сложности — их можно вставить в модуль и запустить.
Простой: функция в выражении. Процедура считает строку табличной части, вызывая функцию на каждой итерации.
Процедура ЗаполнитьСуммы()
Для Каждого Строка Из Товары Цикл
Строка.СуммаНДС = ВычислитьНДС(Строка.Сумма, Строка.СтавкаНДС);
Строка.Всего = Строка.Сумма + Строка.СуммаНДС;
КонецЦикла;
КонецПроцедуры
Функция вызывается внутри цикла, но к базе не обращается — считает в памяти, поэтому цикл остаётся быстрым.
Средний: функция, возвращающая структуру. Когда результатов несколько, функция отдаёт не одно число, а структуру — вызывающая процедура разбирает её по полям.
Функция РазложитьСумму(Сумма, Ставка)
Результат = Новый Структура("БезНДС, НДС, Всего");
Результат.НДС = Сумма * Ставка / (100 + Ставка);
Результат.БезНДС = Сумма - Результат.НДС;
Результат.Всего = Сумма;
Возврат Результат;
КонецФункции
Процедура Показать()
Итог = РазложитьСумму(120, 20);
Сообщить("Без НДС: " + Итог.БезНДС + ", НДС: " + Итог.НДС);
КонецПроцедуры
Для суммы 120 со ставкой 20% получим НДС 120 * 20 / 120 = 20 и сумму без НДС 100.
Сложный: функция вызывает сама себя (рекурсия). Классический случай — обход иерархии или вычисление, сводящееся к задаче меньшего размера. Функция факториала вызывает себя с уменьшенным аргументом:
Функция Факториал(Знач Н)
Если Н <= 1 Тогда
Возврат 1; // условие выхода — без него рекурсия бесконечна
КонецЕсли;
Возврат Н * Факториал(Н - 1);
КонецФункции
Вызов Факториал(5) развернётся в 5 * 4 * 3 * 2 * 1 = 120. У любой рекурсивной функции обязательно должно быть условие выхода — ветка, где она возвращает результат без обращения к себе. Иначе стек вызовов переполнится и платформа аварийно завершит модуль.
Частые вопросы
Можно ли из процедуры вызвать функцию, объявленную ниже в том же модуле?
Почему функция из общего модуля «не видна» при вызове?
Экспорт; у общего модуля не выставлен нужный флаг контекста (Клиент/Сервер); вы обращаетесь без префикса — нужно ИмяМодуля.Функция().Что вернёт функция, если в ней не сработал `Возврат`?
КонецФункции, минуя все операторы Возврат, функция вернёт значение Неопределено. Чтобы не ловить такое, следите, чтобы каждая ветка Если заканчивалась возвратом.Чем `Вызвать` отличается от `Вычислить` при вызове функции?
Вызвать в языке нет. Для исполнения строки-кода есть Выполнить (для операторов, без результата) и Вычислить (для выражений, с результатом). Функцию, возвращающую значение, исполняют через Вычислить.Можно ли вызвать серверную функцию из клиентской процедуры?
&НаСервере-функцию своей формы — это обычный серверный вызов. Обратное запрещено: сервер не вызывает клиентский код напрямую.