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

· 9 мин

Содержание

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

Разберём по шагам: как записать процедуру, как передать в неё данные, где она видна из других модулей и что происходит, когда процедура вызывает сама себя. Примеры даны на синтаксисе платформы 8.3 — он одинаков и в Бухгалтерии 3.0, и в ЗУП 3.1, и в любой другой конфигурации на управляемых формах.

Процедура и функция: в чём разница

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

// Функция — считает и отдаёт результат
Функция СуммаСНДС(Сумма, Ставка = 20)
    Возврат Сумма + Сумма * Ставка / 100;
КонецФункции

// Процедура — делает действие, ничего не отдаёт
Процедура ВывестиИтог(Сумма)
    Сообщить("Итого к оплате: " + Формат(Сумма, "ЧДЦ=2"));
КонецПроцедуры

Отсюда правило вызова:

  • 🔹 Функцию можно поставить в выражение: Итог = СуммаСНДС(1000);
  • 🔹 Процедуру в выражение поставить нельзя — она не имеет значения, её вызывают отдельной строкой-оператором.

Обратное допустимо: функцию разрешается вызвать как процедуру, просто проигнорировав результат (СуммаСНДС(1000);). Платформа не ругается, но смысла в этом мало.

Как выбрать при проектировании: если спрашиваете у метода «сколько / какой / есть ли» — делайте функцию. Если командуете «запиши / проведи / покажи / обнови» — процедуру.

У процедуры тоже есть оператор Возврат, но без значения — Возврат;. Он нужен для досрочного выхода, когда дальше выполнять нечего:

Процедура ЗаполнитьРеквизиты(Объект)
    Если НЕ ЗначениеЗаполнено(Объект) Тогда
        Возврат; // объект пустой — выходим, не выполняя остальное
    КонецЕсли;
    Объект.ДатаЗаполнения = ТекущаяДата();
КонецПроцедуры

Как объявить процедуру и как её вызвать

Минимальное объявление — имя и пустое тело между ключевыми словами:

Процедура ПоказатьСообщение(Текст)
    Сообщить(Текст);
КонецПроцедуры

Имя процедуры пишут без пробелов, обычно в стиле «КаждоеСловоСБольшойБуквы». После имени в круглых скобках перечисляют параметры; если параметров нет — скобки всё равно ставят пустыми.

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

ПоказатьСообщение("Документ проведён");

Количество переданных аргументов должно совпадать с числом параметров. Если передать меньше — платформа выдаст Недостаточно фактических параметров, если больше — Слишком много фактических параметров. Исключение — параметры со значением по умолчанию, их можно опускать (об этом ниже).

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

Передача параметров: по ссылке, по значению и Знач

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

Процедура УвеличитьНаЕдиницу(Число)
    Число = Число + 1;
КонецПроцедуры

Счётчик = 5;
УвеличитьНаЕдиницу(Счётчик);
Сообщить(Счётчик); // 6 — значение изменилось

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

Процедура РазложитьФИО(Знач ФИО, Фамилия, Имя, Отчество)
    Части = СтрРазделить(ФИО, " ", Ложь);
    Фамилия = ?(Части.Количество() > 0, Части[0], "");
    Имя     = ?(Части.Количество() > 1, Части[1], "");
    Отчество = ?(Части.Количество() > 2, Части[2], "");
КонецПроцедуры

РазложитьФИО("Иванов Пётр Сергеевич", Ф, И, О);
// после вызова: Ф = "Иванов", И = "Пётр", О = "Сергеевич"

Здесь Фамилия, Имя, Отчество — выходные параметры: процедура в них записывает результат. Отдельного ключевого слова для «выходного» параметра во встроенном языке 1С нет — выходным параметр делает сама передача по ссылке.

Если изменять переданную переменную не нужно, параметр защищают ключевым словом Знач — тогда он передаётся по значению, внутри процедуры создаётся копия:

Процедура ОбработатьКопию(Знач Таблица)
    Таблица.Очистить(); // чистим копию, оригинал не пострадает
КонецПроцедуры

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

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

Процедура Записать(Объект, ПроводитьДокумент = Ложь)
    // ...
КонецПроцедуры

Записать(Док);        // ПроводитьДокумент = Ложь
Записать(Док, Истина); // ПроводитьДокумент = Истина

Параметры со значением по умолчанию ставят в конце списка — после обязательных.

Где процедура видна: модули, экспорт и директивы

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

Процедура ОбновитьКэшНастроек() Экспорт
    // ...
КонецПроцедуры

Экспортные процедуры общих модулей — это, по сути, общедоступные методы конфигурации. К ним обращаются по имени модуля:

ОбщегоНазначения.ОбновитьКэшНастроек();

В управляемом приложении у каждой процедуры есть контекст выполнения — клиент или сервер. Его задают директивой компиляции перед объявлением:

ДирективаГде выполняетсяДля чего
&НаКлиентена компьютере пользователяобработка команд формы, диалоги, вопросы
&НаСерверена сервере, с данными формычтение и запись в базу, запросы
&НаСервереБезКонтекстана сервере, без данных формыбыстрые серверные операции без передачи формы
&НаКлиенте
Процедура ОбновитьНажатие(Команда)
    ЗагрузитьДанныеНаСервере();
КонецПроцедуры

&НаСервере
Процедура ЗагрузитьДанныеНаСервере()
    // здесь доступны запросы и объекты базы
КонецПроцедуры

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

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

Рекурсия: когда процедура вызывает сама себя

Рекурсивная процедура — та, что внутри себя вызывает саму себя. В 1С это удобно для обхода иерархических данных: дерева значений, групп справочника, строк с подчинёнными строками.

Процедура ОбойтиСтроки(КоллекцияСтрок)
    Для Каждого Строка Из КоллекцияСтрок Цикл
        Сообщить(Строка.Наименование);
        // спускаемся на уровень ниже
        ОбойтиСтроки(Строка.Строки);
    КонецЦикла;
КонецПроцедуры

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

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

Если данных много и глубина большая, рекурсию иногда заменяют обходом через стек (массив узлов, которые ещё предстоит обработать). Но для типовых справочников и деревьев конфигурации рекурсивная процедура проще и читается лучше.

Примеры процедур из типовых задач

Несколько шаблонов, которые встречаются в прикладной разработке чаще всего.

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

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    Если НЕ ЗначениеЗаполнено(Объект.Дата) Тогда
        Объект.Дата = ТекущаяДата();
    КонецЕсли;
КонецПроцедуры

Обработчик команды. Реакция на нажатие кнопки — всегда клиентская процедура с параметром Команда:

&НаКлиенте
Процедура ЗаполнитьТабличнуюЧасть(Команда)
    ЗаполнитьНаСервере();
КонецПроцедуры

Процедура-действие с проверкой. Сначала проверяем входные данные, при проблеме выходим через Возврат;, иначе выполняем действие:

Процедура ПровестиЕслиМожно(ДокументОбъект)
    Если ДокументОбъект.Сумма <= 0 Тогда
        Сообщить("Сумма документа не заполнена");
        Возврат;
    КонецЕсли;
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецПроцедуры

Обратите внимание: имена процедур-обработчиков (ПриСозданииНаСервере, ПриОткрытии, ПередЗаписью) задаёт сама платформа — их нельзя переименовать произвольно, иначе событие не сработает. А вот ваши собственные процедуры (ПровестиЕслиМожно, ЗаполнитьНаСервере) называйте как удобно.

Где повторно используемый код: общие модули

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

// Общий модуль "РаботаСДатами", свойство Сервер = Истина
Функция НачалоКвартала(Дата) Экспорт
    Месяц = (Месяц(Дата) - 1) \ 3 * 3 + 1;
    Возврат Дата(Год(Дата), Месяц, 1);
КонецФункции

Вызов из любого модуля — через имя общего модуля:

Начало = РаботаСДатами.НачалоКвартала(ТекущаяДата());

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

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

Ошибки здесь однотипные, и текст сообщения обычно прямо указывает на причину.

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

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

Проверьте себя
Что обязательно есть у функции, но не обязательно у процедуры?
Как по умолчанию передаётся параметр во встроенном языке 1С?
Что делает ключевое слово Знач перед параметром?

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

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