Как написать процедуру или функцию на встроенном языке 1С
Содержание
Процедура в 1С выполняет действия и ничего не возвращает, функция — возвращает значение через Возврат. Процедуру объявляют парой Процедура … КонецПроцедуры, функцию — Функция … КонецФункции. Чтобы метод был виден из других модулей, после списка параметров ставят ключевое слово Экспорт.
Всё остальное — передача параметров, директивы &НаСервере и &НаКлиенте, значения по умолчанию, оформление — влияет на то, будет ли код работать быстро и предсказуемо. Синтаксис и поведение одинаковы для всех конфигураций на платформе 8.3: БП, ЗУП, УТ или своей разработки.
Чем процедура отличается от функции в 1С
Разница одна, но принципиальная: функция даёт результат, который можно подставить в выражение. Процедура — нет.
| Признак | Процедура | Функция |
|---|---|---|
| Объявление | Процедура Имя() … КонецПроцедуры | Функция Имя() … КонецФункции |
| Возвращает значение | Нет | Да, через Возврат Значение |
Вызов в выражении (А = Имя()) | Ошибка компиляции | Можно |
Вызов отдельной строкой (Имя();) | Можно | Можно, результат отбрасывается |
Без Возврат | Выполняется до КонецПроцедуры | Вернёт Неопределено |
Когда что выбирать:
- 🔧 Процедура — если метод что-то меняет: заполняет табличную часть, записывает объект, выводит сообщение, устанавливает видимость элементов формы.
- 📤 Функция — если метод что-то вычисляет или получает: сумму, ссылку на элемент справочника, результат запроса, признак «можно/нельзя».
- ⚖️ Спорный случай — метод и меняет данные, и сообщает об успехе. Тогда удобна функция, которая возвращает
Истина/Ложь. Но лучше не смешивать: если метод «Записать» вдруг возвращает таблицу, код читать тяжело.
Хорошая проверка: если имя метода — глагол действия («Заполнить», «Пересчитать», «Записать»), это процедура. Если имя описывает результат («СуммаДокумента», «ОстатокТовара», «ЕстьПраваНаИзменение»), это функция.
Синтаксис объявления процедур и функций
Общий шаблон
&НаСервере
Процедура ЗаполнитьТовары(Контрагент, Знач ДатаОстатков = Неопределено) Экспорт
Перем ТекущаяСтрока;
Если ДатаОстатков = Неопределено Тогда
ДатаОстатков = ТекущаяДатаСеанса();
КонецЕсли;
// тело процедуры
КонецПроцедуры
Функция СуммаСНДС(Сумма, СтавкаПроцентов) Экспорт
Возврат Сумма + Сумма * СтавкаПроцентов / 100;
КонецФункции
Из чего состоит объявление:
- Директива компиляции (
&НаСервере,&НаКлиентеи др.) — только в модулях форм и команд. В других модулях её нет. - Ключевое слово
ПроцедураилиФункция. - Имя — без пробелов, начинается с буквы или подчёркивания. Регистр не важен:
суммаСНДСиСуммаСНДС— одно и то же. - Параметры в скобках через запятую. Скобки обязательны, даже если параметров нет.
Экспорт— по необходимости.- Тело и закрывающее
КонецПроцедурыилиКонецФункции. Точка с запятой после них не нужна.
Экспорт: кто увидит метод
Без Экспорт метод доступен только внутри своего модуля. С ним — из других мест:
- 📦 Общий модуль — вызов через имя модуля:
РаботаСТоварами.ЗаполнитьТовары(...). - 🗂️ Модуль менеджера — через менеджер объекта:
Справочники.Номенклатура.НайтиПоАртикулу(...). - 📄 Модуль объекта — через сам объект:
ДокументОбъект.Пересчитать(). - 🖥️ Модуль формы — экспортные методы формы вызывают через переменную формы, например из другой формы, открытой из неё.
Экспортируйте только то, что действительно вызывают снаружи. Каждый лишний Экспорт — это обязательство: при изменении метода придётся проверять все места вызова.
Директивы компиляции в модуле формы
В модуле формы код делится на клиентский и серверный. Директива пишется строкой над объявлением:
| Директива | Где выполняется | Доступ к данным формы |
|---|---|---|
&НаКлиенте | На клиенте | Есть |
&НаСервере | На сервере | Есть, форма передаётся на сервер |
&НаСервереБезКонтекста | На сервере | Нет, только переданные параметры |
&НаКлиентеНаСервереБезКонтекста | И там, и там | Нет |
Если директиву не указать, метод формы считается серверным (&НаСервере). Ошибиться легко: обработчик кнопки без директивы не сработает как клиентский.
&НаСервереБезКонтекста быстрее, чем &НаСервере: на сервер не уходят данные формы. Если серверному методу нужны только два-три значения, передайте их параметрами и используйте вызов без контекста.
В общих модулях директив нет. Там место выполнения задают флажки в свойствах модуля: «Клиент (управляемое приложение)», «Сервер», «Вызов сервера», «Привилегированный» и другие.
Переменные и область видимости
Переменные внутри метода бывают двух видов:
- ✍️ Объявленные через
Перем— строкиПеремидут в начале тела, до остального кода. - ⚡ Созданные присваиванием —
Итог = 0;. Так пишут чаще всего.
Обе разновидности локальны: живут только во время вызова и не видны другим методам. Параметры метода ведут себя так же — это локальные переменные, получившие значения при вызове.
Переменные модуля объявляют через Перем в самом начале модуля, до первой процедуры. Они видны всем методам этого модуля, а с Экспорт — и снаружи (в модулях объекта и формы).
Переменные модуля объекта не сохраняются в базе. После записи и повторного получения объекта их значения пропадают. Для передачи данных между событиями записи используйте ДополнительныеСвойства объекта.
Порядок методов в модуле не важен: процедуру можно вызвать выше по тексту, чем она объявлена. Платформа компилирует модуль целиком.
Параметры процедур: передача по значению и по ссылке
По умолчанию — по ссылке
Если перед параметром нет слова Знач, он передаётся по ссылке. Присвоение параметру нового значения внутри метода меняет переменную в вызывающем коде:
Процедура Удвоить(Число)
Число = Число * 2;
КонецПроцедуры
А = 5;
Удвоить(А);
// А = 10
Знач — передача по значению
С Знач метод получает копию значения. Что бы ни происходило с параметром внутри, исходная переменная не меняется:
Процедура Удвоить(Знач Число)
Число = Число * 2;
КонецПроцедуры
А = 5;
Удвоить(А);
// А = 5
Ловушка с массивами, структурами и таблицами
Для объектных типов Знач копирует ссылку на объект, а не сам объект. Поэтому:
| Действие внутри метода | Без Знач | С Знач |
|---|---|---|
Массив.Добавить(1) | Исходный массив изменится | Исходный массив изменится |
Массив = Новый Массив | Переменная снаружи укажет на новый массив | Снаружи останется старый массив |
Если метод не должен портить переданную таблицу значений или массив, работайте с копией: у таблицы значений есть метод Скопировать(), для массива — создайте новый и перенесите элементы в цикле.
Знач при вызове сервера
При вызове серверного метода с клиента параметры сериализуются и уходят на сервер. Параметры без Знач после выполнения ещё и возвращаются обратно на клиент — лишний трафик. Поэтому параметры серверных методов, которые вызываются с клиента, обычно объявляют с Знач.
Значения по умолчанию
Функция ЦенаТовара(Номенклатура, ВидЦены = Неопределено, Дата = Неопределено) Экспорт
Правила:
- Параметры со значением по умолчанию ставят в конец списка.
- Значением может быть только литерал: число, строка, дата,
Истина,Ложь,Неопределено. Выражение вродеТекущаяДата()не подойдёт. - При вызове такие параметры можно не указывать:
ЦенаТовара(Товар). - Пропустить параметр в середине можно запятой:
ЦенаТовара(Товар, , ДатаЦены).
Если параметров набирается много, передайте одну структуру с настройками. Стандарты разработки 1С советуют не раздувать список: ориентир — не больше семи параметров.
Параметры = Новый Структура;
Параметры.Вставить("Склад", Склад);
Параметры.Вставить("УчитыватьРезервы", Истина);
Остаток = ОстатокТовара(Товар, Параметры);
Возврат значений и выход из метода
Как функция возвращает результат
Возврат Выражение; сразу завершает функцию и отдаёт значение. Код после него не выполняется. Таких операторов в функции может быть несколько:
Функция СтатусОстатка(Количество) Экспорт
Если Количество < 0 Тогда
Возврат "Отрицательный";
ИначеЕсли Количество = 0 Тогда
Возврат "Нет в наличии";
КонецЕсли;
Возврат "В наличии";
КонецФункции
Если функция дошла до КонецФункции, не встретив Возврат, она вернёт Неопределено. Это частый источник скрытых ошибок: на одной из веток забыли вернуть результат, а вызывающий код спокойно работает с Неопределено.
Ранний выход из процедуры
В процедуре Возврат пишут без значения. Он прерывает выполнение — удобно для проверок в начале метода:
Процедура ПересчитатьСуммы(Объект)
Если Объект.Товары.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Для Каждого Строка Из Объект.Товары Цикл
Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;
КонецПроцедуры
Такой приём — «охранное условие» — избавляет от лишнего уровня вложенности Если.
Как вернуть несколько значений
Функция возвращает одно значение. Если нужно несколько, есть два пути:
- 📋 Вернуть структуру —
Новый Структура("Сумма, НДС", 1000, 200). Вызывающий код берётРезультат.Сумма,Результат.НДС. - 🔁 Дописать выходные параметры — передать переменные без
Значи заполнить их внутри. Работает, но читается хуже.
Ошибки внутри метода
Если метод не может выполнить задачу, не возвращайте «магическое» значение вроде -1. Для действительно ошибочной ситуации есть ВызватьИсключение "Не указан склад";. Вызывающий код перехватит её в блоке Попытка … Исключение … КонецПопытки или покажет пользователю.
Асинхронные процедуры и функции
Начиная с платформы 8.3.18 в языке появились асинхронные методы. Их объявляют с ключевым словом Асинх, а внутри ждут результата через Ждать:
&НаКлиенте
Асинх Процедура УдалитьСтроки(Команда)
Ответ = Ждать ВопросАсинх("Удалить выделенные строки?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
УдалитьСтрокиНаСервере();
КонецЕсли;
КонецПроцедуры
Раньше тот же диалог требовал отдельной процедуры-обработчика и объекта ОписаниеОповещения. Асинхронная запись короче и читается сверху вниз.
Асинхронная функция возвращает не само значение, а объект Обещание. Получить результат можно через Ждать в другом асинхронном методе. Если в вашей базе режим совместимости ниже 8.3.18, Асинх недоступен — остаётся вариант с ОписаниеОповещения.
Как оформлять процедуры и функции по стандартам
Комментарий-описание
Над экспортными методами пишут комментарий в принятом формате. Конфигуратор и EDT по нему показывают подсказку при вводе:
// Рассчитывает сумму с НДС.
//
// Параметры:
// Сумма - Число - сумма без НДС.
// СтавкаПроцентов - Число - ставка, например 20.
//
// Возвращаемое значение:
// Число - сумма с НДС.
//
Функция СуммаСНДС(Сумма, СтавкаПроцентов) Экспорт
Имена
- ✅ Полные слова без сокращений:
КоличествоСтрок, а неКолСтр. - ✅ Каждое слово с заглавной буквы, без подчёркиваний.
- ✅ Процедуры называют действием, функции — результатом (см. совет в первом разделе).
- ❌ Не называйте метод так же, как метод платформы или глобального контекста:
Сообщить,Формат,Найти.
Области модуля
Методы группируют инструкциями #Область … #КонецОбласти. В общих модулях типовых конфигураций принят порядок: ПрограммныйИнтерфейс (экспортные методы для всех), СлужебныйПрограммныйИнтерфейс (для своей подсистемы), СлужебныеПроцедурыИФункции (внутренние). Если делаете доработку, повторите эту схему — так код проще сопровождать и обновлять.
Размер и ответственность метода
Один метод — одна задача. Если процедура занимает несколько экранов, у неё почти наверняка несколько обязанностей. Вынесите части в отдельные методы с говорящими именами: основной метод превратится в читаемый «план действий».
Запросы к базе в цикле — главный враг скорости. Если метод вызывается для каждой строки табличной части и внутри делает запрос, перепишите его: один запрос по всем строкам до цикла, затем поиск по результату.
Ошибки при написании процедур и функций
Итог = Пересчитать();. Либо перепишите метод на функцию с Возврат, либо вызывайте его отдельной строкой.Экспорт. Или имя написано с опечаткой.Если не заканчивается оператором Возврат. Добавьте Возврат по умолчанию перед КонецФункции.Знач или работайте с копией внутри метода.Ещё несколько промахов, которые компилятор не поймает:
- 🐢 Серверный вызов в цикле с клиента. Каждый вызов
&НаСервере— отдельный обмен с сервером. Сто строк — сто обращений. Соберите данные и сделайте один вызов. - 🧩 Склейка числа со строкой.
"Строк: " + Количестводаст число с пробелами-разделителями тысяч. ИспользуйтеФормат(Количество, "ЧГ=0")илиСтрШаблон("Строк: %1", Количество). - 🔄 Удаление элементов в прямом цикле. При удалении строк коллекции идите с конца:
Для Сч = 1 По Коллекция.Количество() Цикл Элемент = Коллекция[Коллекция.Количество() - Сч]; …. - 🗃️ Изменение типового метода напрямую. Правка процедуры типовой конфигурации усложняет обновления. Доработку лучше вынести в расширение и использовать аннотации
&Перед,&Послеили&Вместо.
Частые вопросы
Можно ли вызвать функцию как процедуру?
ИмяФункции(); отдельной строкой, функция выполнится, а результат просто отбросится. Обратное — вызвать процедуру в выражении — нельзя.Сколько операторов Возврат может быть в функции?
Поддерживает ли язык 1С рекурсию?
Можно ли объявить две процедуры с одинаковым именем и разными параметрами?
Где разместить процедуру, которую вызывают из нескольких документов?
Экспорт. Если метод работает только с одним видом объекта, уместнее модуль менеджера этого объекта.