Оператор «пр» в 1С: что это и как объявлять процедуры

· 10 мин

Содержание

«Пр» в коде 1С — это сокращение от слова процедура. Отдельного оператора или ключевого слова с именем пр во встроенном языке 1С:Предприятие 8.3 нет: процедуру объявляют полным ключевым словом Процедура. «Пр» разработчики используют в разговоре, а ещё как приставку в именах переменных и методов — например прРасчетИтогов или ПрЗаполнить.

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

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

«Пр» — это процедура: как её объявить

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

Процедура ПоказатьПриветствие()
    Сообщить("Здравствуйте!");
КонецПроцедуры

Правила именования простые:

  • 🔤 имя начинается с буквы или подчёркивания, пробелов внутри быть не может;
  • 🔁 в пределах одного модуля имя должно быть уникальным — двух процедур с одинаковым именем не бывает;
  • 📏 регистр значения не имеет: Процедура и процедура для компилятора одно и то же.

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

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

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

Процедура ЗаписатьВЖурнал(Текст) Экспорт
    ЗаписьЖурналаРегистрации("Событие", , , , Текст);
КонецПроцедуры

Без Экспорт процедура видна только внутри своего модуля. Это удобно для внутренних вспомогательных методов, которые не нужно показывать «наружу».

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

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

ПризнакПроцедураФункция
Ключевое словоПроцедураФункция
ЗакрываетсяКонецПроцедурыКонецФункции
Возвращает значениенетда, через Возврат
Как вызываюткак отдельную командувнутри выражения
Типичная задачавыполнить действиевычислить и отдать результат

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

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

// вызов внутри выражения
Итог = СуммаСНДС(1000);   // Итог = 1200

Процедуру в выражении использовать нельзя. Строка вида Итог = ПоказатьПриветствие(); вызовет ошибку компиляции — платформа сообщит, что процедуру нельзя применять как функцию.

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

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

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

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

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

Значение = 5;
УвеличитьНаЕдиницу(Значение);
Сообщить(Значение);   // выведет 6

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

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

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

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

Процедура Залогировать(Текст, Уровень = "Информация")
    ЗаписьЖурналаРегистрации(Уровень, , , , Текст);
КонецПроцедуры

Залогировать("Старт обработки");            // Уровень = "Информация"
Залогировать("Сбой", "Ошибка");             // Уровень = "Ошибка"

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

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

Где «живут» переменные: область видимости и директивы

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

Чтобы переменная была доступна всем процедурам модуля, её объявляют словом Перем в самом верху модуля, до первой процедуры:

Перем КэшКурсов;   // переменная модуля, видна всем методам

Добавив Экспорт, такую переменную модуля делают видимой и за его пределами:

Перем ТекущийПользователь Экспорт;

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

  • 🖥 &НаКлиенте — на компьютере пользователя, есть доступ к элементам формы;
  • 🗄 &НаСервере — на сервере, с доступом к реквизитам формы и базе;
  • ⚙️ &НаСервереБезКонтекста — на сервере, но без данных формы (быстрее, рекомендуется для вычислений);
  • 🔁 &НаКлиентеНаСервереБезКонтекста — доступна и там, и там.
&НаСервереБезКонтекста
Процедура ПересчитатьОстатки(Номенклатура)
    // запрос к базе, без доступа к форме
КонецПроцедуры

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

Для общих модулей место выполнения задаётся не директивой, а флажками самого модуля — «Клиент», «Сервер», «Внешнее соединение» — в его свойствах.

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

В каких модулях пишут процедуры

Процедуру нельзя написать «просто в проекте» — она всегда лежит внутри какого-то модуля. От типа модуля зависит, откуда её можно вызвать и где она выполняется:

  • 🧰 Общий модуль — процедуры, общие для всей конфигурации. Сюда выносят повторяющуюся логику и помечают её Экспорт, чтобы вызывать из любого места по имени модуля.
  • 📄 Модуль объекта (справочника, документа) — методы конкретного объекта и обработчики его событий (ПередЗаписью, ОбработкаПроведения).
  • 🖼 Модуль формы — поведение формы и её элементов.
  • 🔘 Модуль команды — процедура, которая срабатывает при нажатии команды.
  • 🗂 Модуль менеджера — методы уровня всего справочника или документа в целом, а не отдельного экземпляра.

Открыть нужный модуль можно из дерева конфигурации: правой кнопкой по объекту → пункт вида Открыть модуль объекта или Открыть модуль менеджера; у формы модуль открывается на отдельной вкладке редактора формы.

Логику, которую вызывают из нескольких мест, держите в общем модуле с Экспорт. Так не придётся копировать один и тот же код по разным объектам и потом править его в десяти местах.

Примеры: как написать и вызвать процедуру

Соберём разобранное в рабочие примеры.

1. Процедура без параметров. Выводит служебное сообщение и очищает предыдущие:

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

ОчиститьИПоказать("Обработка завершена");

2. Процедура с несколькими параметрами и умолчанием. Заполняет реквизит документа:

Процедура ЗаполнитьКомментарий(Документ, Текст, Перезаписывать = Ложь)
    Если Перезаписывать ИЛИ НЕ ЗначениеЗаполнено(Документ.Комментарий) Тогда
        Документ.Комментарий = Текст;
    КонецЕсли;
КонецПроцедуры

ЗаполнитьКомментарий(ДокументОбъект, "Проверено бухгалтерией");

3. Экспортная процедура из общего модуля. В общем модуле РаботаСДокументами объявлена процедура с Экспорт — её вызывают по имени модуля:

// в общем модуле:
Процедура ПровестиПакет(МассивДокументов) Экспорт
    Для Каждого Док Из МассивДокументов Цикл
        Док.Записать(РежимЗаписиДокумента.Проведение);
    КонецЦикла;
КонецПроцедуры

// вызов из любого места конфигурации:
РаботаСДокументами.ПровестиПакет(СписокНаПроведение);

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

Процедуры-обработчики: 1С вызывает их сама

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

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

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    Если НЕ ЗначениеЗаполнено(Дата) Тогда
        Отказ = Истина;   // отменяем запись
    КонецЕсли;
КонецПроцедуры

Параметр Отказ здесь передаётся по ссылке: присвоив ему Истина, вы отменяете операцию. Это общий приём для обработчиков ПередЗаписью, ОбработкаПроверкиЗаполнения, ПередУдалением.

Где искать такие процедуры:

  • 📄 модуль объекта — события справочника или документа: ПередЗаписью, ПриЗаписи, ОбработкаПроведения;
  • 🖼 модуль формы — события формы и её элементов: ПриОткрытии, ПриИзменении, ОбработкаВыбора;
  • 🔘 модуль команды — процедура ОбработкаКоманды, которая срабатывает при нажатии.

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

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

Проверьте себя
Чем закрывается объявление процедуры?
Что вернёт процедура, если в ней нет оператора Возврат?
Как передать параметр так, чтобы процедура не меняла исходную переменную?
Где объявляют переменную, видимую всем процедурам модуля?

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

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

  • 🚫 Процедуру используют как функцию. Рез = МояПроцедура(); — платформа не даст такой код скомпилировать. Если нужен результат — сделайте функцию или верните данные через параметр, переданный по ссылке.
  • 🔢 Не совпадает число параметров. «Слишком мало фактических параметров» означает, что при вызове не передали обязательный аргумент. Либо задайте ему значение по умолчанию, либо передавайте всегда.
  • 🖥 Вызов серверного метода с клиента напрямую. Клиентская процедура не видит метод, помеченный &НаСервере в другом контексте. Обращение к базе выносите на сервер.
  • 🔁 Дубль имени в модуле. Две процедуры с одинаковым именем в одном модуле — ошибка. Переименуйте одну.
  • 🧩 Неожиданное изменение данных по ссылке. Процедура меняет переданную структуру или таблицу значений, а вызывающий код этого не ожидал. Ставьте Знач, если менять исходник не нужно.
  • 📛 Забыли Экспорт. Процедуру из общего модуля не видно снаружи — проверьте, что в заголовке есть Экспорт, а у модуля стоят нужные флажки «Клиент/Сервер».

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

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

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