Как устроены процедуры во встроенном языке 1С

· 9 мин

Содержание

Процедура в 1С — это именованный блок встроенного языка, который выполняет действие и ничего не возвращает в точку вызова. Объявляется парой операторов Процедура … КонецПроцедуры, вызывается по имени как отдельная строка кода. Параметры передаются в круглых скобках, по умолчанию — по ссылке.

Главное отличие от функции: процедура не отдаёт результат оператором Возврат Значение, поэтому её нельзя подставить в выражение или присвоить переменной. Если код должен что-то посчитать и вернуть — нужна функция. Если просто выполнить шаги (заполнить таблицу, открыть форму, записать объект) — процедура.

Ниже разберём синтаксис объявления, передачу параметров с модификатором Знач, видимость через Экспорт, директивы компиляции &НаКлиенте / &НаСервере и ошибки, на которых чаще всего спотыкаются.

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

Обе конструкции — подпрограммы встроенного языка. Разница в одном: функция обязана вернуть значение, процедура — нет.

ПризнакПроцедураФункция
Ключевые словаПроцедура … КонецПроцедурыФункция … КонецФункции
Возврат значениянетобязателен, оператор Возврат <значение>
Как вызываютотдельной строкой: Пересчитать();в выражении: Итог = Сумма(А, Б);
Типичное имяглагол: ЗаполнитьТаблицусуществительное или вопрос: СуммаДокумента, ЭтоРабочийДень

💡 Функцию разрешено вызвать и как процедуру — просто отдельной строкой, проигнорировав результат. А вот процедуру в выражение подставить нельзя: получите ошибку «Обнаружена попытка использования процедуры как функции».

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

Синтаксис объявления и вызова

Минимальная процедура выглядит так:

Процедура Поприветствовать()
    Сообщить("Готово");
КонецПроцедуры

Вызов — имя со скобками отдельной строкой:

Поприветствовать();

Имя подчиняется правилам идентификаторов: буквы, цифры и подчёркивание, начинается не с цифры. Регистр не важен — Поприветствовать и ПОПРИВЕТСТВОВАТЬ для платформы одно и то же, но в типовых решениях принят «ВерблюжийРегистр».

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

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

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

Как передавать параметры: по ссылке и по значению

Параметры перечисляются в скобках через запятую, порядок при вызове должен совпадать с порядком объявления. Жёсткого ограничения на их число нет, но если параметров набирается больше пяти-шести — это сигнал передавать структуру с именованными полями: читать такой вызов проще, а добавить новое поле можно, не трогая сигнатуру.

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

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

Х = 5;
Удвоить(Х);
Сообщить(Х); // выведет 10

Чтобы защититься от этого, ставят модификатор Знач — параметр принимается по значению, то есть копией:

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

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

Важная тонкость: Знач копирует ссылку, а не сам объект. Для коллекций — Массив, Структура, Соответствие, ТаблицаЗначений — даже с Знач вы работаете с тем же объектом в памяти. Добавление элемента в массив будет видно снаружи, потому что копируется ссылка на массив, а не его содержимое. Знач страхует только от переприсваивания самого параметра.

Процедура ДобавитьЭлемент(Знач Список)
    Список.Добавить("новый"); // изменит исходный массив!
КонецПроцедуры

Значения параметров по умолчанию

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

Процедура Записать(Объект, ВыводитьСообщение = Истина)
    Объект.Записать();
    Если ВыводитьСообщение Тогда
        Сообщить("Записано");
    КонецЕсли;
КонецПроцедуры

Записать(Договор);            // ВыводитьСообщение = Истина
Записать(Договор, Ложь);      // тихо, без сообщения

Ограничения, о которые часто спотыкаются:

  • 📌 значением по умолчанию может быть только литерал — число, строка, булево, дата, Неопределено. Выражение или вызов функции туда не поставить;
  • 📌 параметры со значением по умолчанию идут только в конце списка. Нельзя задать умолчание среднему параметру, а следующему — нет;
  • 📌 «пропустить» параметр в середине, оставив запятые, в 1С нельзя — только отбросить хвост.

Видимость: локальные и экспортные процедуры

По умолчанию процедура видна только внутри своего модуля. Чтобы вызвать её из другого модуля, добавьте Экспорт после скобок:

Процедура ПересчитатьИтоги() Экспорт
    // ...
КонецПроцедуры

Где лежит процедура — определяет, откуда её видно:

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

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

💡 Имя процедуры — часть её документации. Для общих модулей в типовых решениях принят префикс области ответственности, а само имя — глагол в неопределённой форме: ЗаполнитьРеквизиты, ПроверитьПрава, ОтправитьУведомление. По такому имени видно, что процедура делает, ещё до чтения тела.

Директивы компиляции: клиент и сервер

В управляемом приложении код исполняется на двух сторонах — на клиенте и на сервере. Поэтому перед процедурами в модулях форм и команд ставят директивы компиляции, которые говорят платформе, где выполнять код:

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

&НаСервере
Процедура ПересчитатьНаСервере()
    // здесь доступны данные формы и запросы
КонецПроцедуры

Клиент не может напрямую вызвать произвольную серверную процедуру. Разрешён вызов &НаСервере-процедур своей формы и экспортных процедур общих модулей с установленным флагом «Вызов сервера». Попытка иначе обратиться к серверу с клиента даёт ошибку компиляции.

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

💡 Правило экономии: тяжёлые вычисления и работу с базой выносите в &НаСервереБезКонтекста, а серверных вызовов делайте как можно меньше — каждый вызов это передача данных по сети и накладные расходы.

Процедуры-обработчики событий

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

Привязка обработчика зависит от того, где он живёт:

  • 🔌 Модуль объекта или формы — обработчик с предопределённым именем платформа находит сама. Например, процедуру ПередЗаписью в модуле объекта система вызовет перед каждой записью. Создавать такие обработчики удобно из палитры свойств объекта по значку с лупой напротив события.
  • 🔌 Элемент формы — обработчик назначается в свойствах элемента (поля, кнопки, таблицы). Платформа создаёт процедуру с директивой &НаКлиенте и прописывает её имя в свойстве события.
&НаКлиенте
Процедура СуммаПриИзменении(Элемент)
    РассчитатьИтогНаСервере();
КонецПроцедуры

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

Процедура может вызывать сама себя — рекурсия в языке 1С разрешена. Это удобно при обходе иерархии: процедура обрабатывает узел и для каждого дочернего вызывает себя же.

Процедура ОбойтиСтроки(Знач Строки)
    Для Каждого Строка Из Строки Цикл
        ОбработатьСтроку(Строка);
        Если Строка.Строки.Количество() > 0 Тогда
            ОбойтиСтроки(Строка.Строки); // спуск на уровень ниже
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Условие выхода здесь — отсутствие дочерних строк. Без такого условия рекурсия уйдёт в бесконечность и платформа аварийно завершит работу с переполнением стека.

Проверьте себя

Проверьте себя
Можно ли результат процедуры присвоить переменной?
Что делает модификатор Знач у параметра?
Где нельзя использовать метод Сообщить?

Частые ошибки при работе с процедурами

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

Хороший стиль: одна процедура решает одну задачу, имя начинается с глагола и отражает действие, а всё, что должно быть доступно снаружи, помечено Экспорт осознанно, а не «на всякий случай».

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

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