Оператор «пр» в 1С: что это и как объявлять процедуры
Содержание
«Пр» в коде 1С — это сокращение от слова процедура. Отдельного оператора или ключевого слова с именем пр во встроенном языке 1С:Предприятие 8.3 нет: процедуру объявляют полным ключевым словом Процедура. «Пр» разработчики используют в разговоре, а ещё как приставку в именах переменных и методов — например прРасчетИтогов или ПрЗаполнить.
Процедура — это именованный блок кода, который выполняет действия, но не возвращает значение. Этим она отличается от функции. Процедуру вызывают как отдельную команду, а результат её работы — это изменённые данные, записанный объект, выведенное сообщение.
Процедуры и функции вместе называют методами модуля. На них держится вся логика конфигурации: заполнение документов, проверки перед записью, расчёты в отчётах, реакция на нажатие кнопки. Разобравшись с процедурой на простом примере, проще читать и остальной код 1С. Все примеры ниже — на встроенном языке 1С:Предприятие 8.3.
«Пр» — это процедура: как её объявить
Процедуру открывают словом Процедура, а закрывают — КонецПроцедуры. Между ними идёт тело — набор операторов, которые выполнятся при вызове.
Процедура ПоказатьПриветствие()
Сообщить("Здравствуйте!");
КонецПроцедуры
Правила именования простые:
- 🔤 имя начинается с буквы или подчёркивания, пробелов внутри быть не может;
- 🔁 в пределах одного модуля имя должно быть уникальным — двух процедур с одинаковым именем не бывает;
- 📏 регистр значения не имеет:
Процедураипроцедурадля компилятора одно и то же.
Вызывают процедуру по имени, со скобками. Даже если параметров нет, скобки обязательны:
ПоказатьПриветствие();
Чтобы процедуру можно было вызвать из другого модуля, в конце заголовка добавляют слово Экспорт:
Процедура ЗаписатьВЖурнал(Текст) Экспорт
ЗаписьЖурналаРегистрации("Событие", , , , Текст);
КонецПроцедуры
Без Экспорт процедура видна только внутри своего модуля. Это удобно для внутренних вспомогательных методов, которые не нужно показывать «наружу».
Процедура или функция — в чём разница
Главное отличие в одном: процедура ничего не возвращает, а функция возвращает результат через оператор Возврат. Функцию закрывают словом КонецФункции.
| Признак | Процедура | Функция |
|---|---|---|
| Ключевое слово | Процедура | Функция |
| Закрывается | КонецПроцедуры | КонецФункции |
| Возвращает значение | нет | да, через Возврат |
| Как вызывают | как отдельную команду | внутри выражения |
| Типичная задача | выполнить действие | вычислить и отдать результат |
Функцию ставят туда, где нужен результат:
Функция СуммаСНДС(Сумма, Ставка = 20)
Возврат Сумма + Сумма * Ставка / 100;
КонецФункции
// вызов внутри выражения
Итог = СуммаСНДС(1000); // Итог = 1200
Процедуру в выражении использовать нельзя. Строка вида Итог = ПоказатьПриветствие(); вызовет ошибку компиляции — платформа сообщит, что процедуру нельзя применять как функцию.
Простой ориентир: если из кода нужно получить значение — пишите функцию; если нужно просто выполнить действие — процедуру. Функцию без Возврат платформа тоже позволит объявить, но она вернёт Неопределено, и смысла в ней немного.
На практике процедуры берут на себя «действия»: записать объект, провести документ , заполнить табличную часть, вывести сообщение. Функции отвечают за «вопросы»: посчитать сумму, проверить условие, найти элемент справочника. Если метод и считает, и что-то меняет в базе — это повод разделить его на функцию-расчёт и процедуру-запись, чтобы код было проще тестировать.
Параметры: по ссылке, по значению и значения по умолчанию
Параметры перечисляют в скобках после имени. По умолчанию в 1С параметры передаются по ссылке — то есть процедура работает с тем же объектом, что и вызывающий код, и может его изменить.
Процедура УвеличитьНаЕдиницу(Число)
Число = Число + 1;
КонецПроцедуры
Значение = 5;
УвеличитьНаЕдиницу(Значение);
Сообщить(Значение); // выведет 6
Чтобы процедура не трогала исходную переменную, параметр объявляют с ключевым словом Знач — тогда внутрь передаётся копия значения:
Процедура УвеличитьНаЕдиницу(Знач Число)
Число = Число + 1; // меняется только локальная копия
КонецПроцедуры
Передача по ссылке — частый источник плавающих ошибок. Процедура «неожиданно» меняет переданную таблицу значений или структуру, а вызывающий код об этом не знает. Если менять исходные данные не нужно — ставьте Знач.
Параметру можно задать значение по умолчанию через знак =. Тогда при вызове его разрешается не указывать:
Процедура Залогировать(Текст, Уровень = "Информация")
ЗаписьЖурналаРегистрации(Уровень, , , , Текст);
КонецПроцедуры
Залогировать("Старт обработки"); // Уровень = "Информация"
Залогировать("Сбой", "Ошибка"); // Уровень = "Ошибка"
Значения по умолчанию задают только константами (число, строка, булево, Неопределено), выражение или объект туда подставить нельзя. Параметры с умолчанием ставят в конце списка — после них не может идти обязательный параметр.
Со сложными объектами — таблицей значений, структурой, списком — есть нюанс. Даже с Знач процедура получает ссылку на тот же объект, а не его копию. То есть переприсвоить переменную внутри она не сможет, а вот изменить содержимое объекта (добавить строку в таблицу, поменять значение в структуре) — вполне. Если нужна полностью независимая копия, её делают явно — например методом Скопировать() у таблицы значений (в типовых конфигурациях на БСП для структур и соответствий есть метод ОбщегоНазначенияКлиентСервер.СкопироватьРекурсивно).
Где «живут» переменные: область видимости и директивы
Переменные внутри процедуры — локальные. Они создаются при входе в процедуру и исчезают при выходе. Соседние методы их не видят.
Чтобы переменная была доступна всем процедурам модуля, её объявляют словом Перем в самом верху модуля, до первой процедуры:
Перем КэшКурсов; // переменная модуля, видна всем методам
Добавив Экспорт, такую переменную модуля делают видимой и за его пределами:
Перем ТекущийПользователь Экспорт;
Для процедур в модулях управляемых форм и команд важны директивы компиляции — они говорят, где выполняется код:
- 🖥
&НаКлиенте— на компьютере пользователя, есть доступ к элементам формы; - 🗄
&НаСервере— на сервере, с доступом к реквизитам формы и базе; - ⚙️
&НаСервереБезКонтекста— на сервере, но без данных формы (быстрее, рекомендуется для вычислений); - 🔁
&НаКлиентеНаСервереБезКонтекста— доступна и там, и там.
&НаСервереБезКонтекста
Процедура ПересчитатьОстатки(Номенклатура)
// запрос к базе, без доступа к форме
КонецПроцедуры
Клиентская процедура не может напрямую вызвать серверную без контекста из другого места, кроме как через серверный вызов. Поэтому обращение к базе всегда выносят на сервер, а с формой работают на клиенте.
Для общих модулей место выполнения задаётся не директивой, а флажками самого модуля — «Клиент», «Сервер», «Внешнее соединение» — в его свойствах.
Переменными модуля, особенно экспортными, лучше не злоупотреблять. Данные, которые нужны процедуре, обычно удобнее передавать через параметры: так метод не зависит от «скрытого» состояния модуля, его проще читать и тестировать. Переменную модуля оставляют для того, что действительно общее на весь модуль, — например кэша, который считается один раз и переиспользуется несколькими методами.
В каких модулях пишут процедуры
Процедуру нельзя написать «просто в проекте» — она всегда лежит внутри какого-то модуля. От типа модуля зависит, откуда её можно вызвать и где она выполняется:
- 🧰 Общий модуль — процедуры, общие для всей конфигурации. Сюда выносят повторяющуюся логику и помечают её
Экспорт, чтобы вызывать из любого места по имени модуля. - 📄 Модуль объекта (справочника, документа) — методы конкретного объекта и обработчики его событий (
ПередЗаписью,ОбработкаПроведения). - 🖼 Модуль формы — поведение формы и её элементов.
- 🔘 Модуль команды — процедура, которая срабатывает при нажатии команды.
- 🗂 Модуль менеджера — методы уровня всего справочника или документа в целом, а не отдельного экземпляра.
Открыть нужный модуль можно из дерева конфигурации: правой кнопкой по объекту → пункт вида Открыть модуль объекта или Открыть модуль менеджера; у формы модуль открывается на отдельной вкладке редактора формы.
Логику, которую вызывают из нескольких мест, держите в общем модуле с Экспорт. Так не придётся копировать один и тот же код по разным объектам и потом править его в десяти местах.
Примеры: как написать и вызвать процедуру
Соберём разобранное в рабочие примеры.
1. Процедура без параметров. Выводит служебное сообщение и очищает предыдущие:
Процедура ОчиститьИПоказать(Текст)
ОчиститьСообщения();
Сообщить(Текст);
КонецПроцедуры
ОчиститьИПоказать("Обработка завершена");
2. Процедура с несколькими параметрами и умолчанием. Заполняет реквизит документа:
Процедура ЗаполнитьКомментарий(Документ, Текст, Перезаписывать = Ложь)
Если Перезаписывать ИЛИ НЕ ЗначениеЗаполнено(Документ.Комментарий) Тогда
Документ.Комментарий = Текст;
КонецЕсли;
КонецПроцедуры
ЗаполнитьКомментарий(ДокументОбъект, "Проверено бухгалтерией");
3. Экспортная процедура из общего модуля. В общем модуле РаботаСДокументами объявлена процедура с Экспорт — её вызывают по имени модуля:
// в общем модуле:
Процедура ПровестиПакет(МассивДокументов) Экспорт
Для Каждого Док Из МассивДокументов Цикл
Док.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;
КонецПроцедуры
// вызов из любого места конфигурации:
РаботаСДокументами.ПровестиПакет(СписокНаПроведение);
Обратите внимание на порядок: сначала процедуру объявляют, а вызвать её можно откуда угодно в пределах видимости. Внутри одного модуля порядок объявления на вызов не влияет — платформа «видит» все методы модуля сразу.
Процедуры-обработчики: 1С вызывает их сама
Отдельный случай — процедуры, которые вы не вызываете из своего кода. Их вызывает сама платформа, когда наступает событие: открытие формы, запись объекта, проведение документа, нажатие кнопки. Такую процедуру называют обработчиком события.
У обработчика фиксированное имя и фиксированный набор параметров — их задаёт платформа, менять нельзя. Например, в модуле объекта документа обработчик перед записью всегда выглядит так:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если НЕ ЗначениеЗаполнено(Дата) Тогда
Отказ = Истина; // отменяем запись
КонецЕсли;
КонецПроцедуры
Параметр Отказ здесь передаётся по ссылке: присвоив ему Истина, вы отменяете операцию. Это общий приём для обработчиков ПередЗаписью, ОбработкаПроверкиЗаполнения, ПередУдалением.
Где искать такие процедуры:
- 📄 модуль объекта — события справочника или документа:
ПередЗаписью,ПриЗаписи,ОбработкаПроведения; - 🖼 модуль формы — события формы и её элементов:
ПриОткрытии,ПриИзменении,ОбработкаВыбора; - 🔘 модуль команды — процедура
ОбработкаКоманды, которая срабатывает при нажатии.
Обработчик добавляют не вручную, а через палитру свойств объекта или элемента формы — рядом со списком событий есть кнопка с лупой. Платформа сама создаст заготовку процедуры с правильным именем и параметрами.
Главное отличие обработчика от обычной процедуры: его нельзя вызвать произвольно из своего кода «просто так». Он привязан к событию. Если нужно выполнить ту же логику в другом месте, её выносят в отдельную процедуру и вызывают из обработчика.
Типичные ошибки при работе с процедурами
Ошибки с процедурами почти всегда всплывают на этапе компиляции или первого вызова. Самые частые:
- 🚫 Процедуру используют как функцию.
Рез = МояПроцедура();— платформа не даст такой код скомпилировать. Если нужен результат — сделайте функцию или верните данные через параметр, переданный по ссылке. - 🔢 Не совпадает число параметров. «Слишком мало фактических параметров» означает, что при вызове не передали обязательный аргумент. Либо задайте ему значение по умолчанию, либо передавайте всегда.
- 🖥 Вызов серверного метода с клиента напрямую. Клиентская процедура не видит метод, помеченный
&НаСерверев другом контексте. Обращение к базе выносите на сервер. - 🔁 Дубль имени в модуле. Две процедуры с одинаковым именем в одном модуле — ошибка. Переименуйте одну.
- 🧩 Неожиданное изменение данных по ссылке. Процедура меняет переданную структуру или таблицу значений, а вызывающий код этого не ожидал. Ставьте
Знач, если менять исходник не нужно. - 📛 Забыли
Экспорт. Процедуру из общего модуля не видно снаружи — проверьте, что в заголовке естьЭкспорт, а у модуля стоят нужные флажки «Клиент/Сервер».
Прежде чем менять код в рабочей базе, снимите копию конфигурации или работайте в копии базы. Ошибка в экспортной процедуре, которую вызывают многие объекты, затронет сразу все места её использования.
Частые вопросы
Можно ли из процедуры вернуть значение?
Знач) и записать в него результат внутри процедуры — вызывающий код увидит изменённое значение.Чем отличается экспортная процедура от обычной?
Экспорт в заголовке делает процедуру видимой за пределами её модуля. Без него метод доступен только внутри своего модуля и служит внутренним помощником.Где писать процедуры, общие для всей конфигурации?
Экспорт, а флажки модуля («Клиент», «Сервер») задают, где код будет выполняться. Вызывают через имя модуля: ИмяМодуля.ИмяПроцедуры().Нужны ли скобки, если у процедуры нет параметров?
Процедура Старт() и Старт();.В чём разница между `Перем` внутри процедуры и `Перем` в начале модуля?
Перем внутри процедуры, локальная — живёт только во время её работы. Перем в начале модуля создаёт переменную модуля, видимую всем его процедурам, а с Экспорт — и за пределами модуля.Можно ли вызвать процедуру раньше строки, где она объявлена?
Экспорт для вызова из других модулей).