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

· 9 мин

Содержание

Чтобы создать процедуру в 1С 8.3, откройте нужный модуль в Конфигураторе и напишите блок Процедура Имя(Параметры) … КонецПроцедуры. Если процедуру будут вызывать из других модулей, добавьте после скобок слово Экспорт. В модуле формы перед объявлением поставьте директиву компиляции, например &НаСервере.

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

Синтаксис объявления процедуры в 1С

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

Процедура ЗаполнитьКомментарий()
    Комментарий = "Заполнено автоматически";
КонецПроцедуры

Полный формат объявления:

&НаСервере
Процедура ПересчитатьСуммы(Знач СтавкаНДС, ТаблицаТоваров, ОкруглятьДоРубля = Ложь) Экспорт
    // тело процедуры
КонецПроцедуры

Из чего состоит строка объявления:

  • 🏷️ &НаСервере — директива компиляции. Нужна в модулях форм и в модулях команд.
  • 🔤 ПересчитатьСуммы — имя. Буквы, цифры и подчёркивание, начинается не с цифры.
  • 📥 (Знач СтавкаНДС, …) — список параметров через запятую. Скобки обязательны, даже если параметров нет.
  • 📤 Экспорт — делает процедуру доступной из других модулей.
  • 🔚 КонецПроцедуры — закрывает блок. Точка с запятой после него не нужна.

Правила именования

Стандарты разработки 1С рекомендуют имена в стиле «ГлаголСуществительное» без пробелов, каждое слово с заглавной: ЗаполнитьТабличнуюЧасть, ПроверитьЗаполнениеРеквизитов. Имя должно отвечать на вопрос «что делает процедура». Имена Процедура1 или Обработка через месяц не поймёт никто, включая автора.

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

// Пересчитывает суммы в табличной части с учётом ставки НДС.
//
// Параметры:
//  СтавкаНДС       - Число - ставка в процентах, например 20.
//  ТаблицаТоваров  - ТаблицаЗначений - строки с колонками Цена, Количество, Сумма.
//  ОкруглятьДоРубля - Булево - округлять ли итог до целых рублей.
//
Процедура ПересчитатьСуммы(Знач СтавкаНДС, ТаблицаТоваров, ОкруглятьДоРубля = Ложь) Экспорт

Порядок в модуле

Модуль 1С состоит из трёх частей, и порядок жёсткий:

  1. Раздел объявления переменных (Перем).
  2. Раздел процедур и функций.
  3. Раздел основной программы — операторы вне процедур.

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

Где разместить процедуру: модули и директивы компиляции

Самый частый вопрос новичка — «в какой модуль писать». От этого зависит, кто сможет вызвать процедуру и на какой стороне она выполнится.

МодульЧто туда кластьДирективы
Модуль формыОбработчики событий формы и элементов, логика интерфейсаОбязательны: &НаКлиенте, &НаСервере и др.
Модуль объектаЛогика конкретного документа или элемента: ПередЗаписью, ОбработкаПроведенияНе нужны, код серверный
Модуль менеджераДействия с объектами этого вида в целом: печать, выборки, заполнение по умолчаниюНе нужны
Общий модульКод, который нужен в разных местах конфигурацииЗадаются флажками в свойствах модуля
Модуль командыОбработчик ОбработкаКоманды&НаКлиенте

Директивы в модуле формы

Форма живёт сразу на двух сторонах: клиентской и серверной. Директива говорит платформе, где компилировать процедуру.

  • 🖥️ &НаКлиенте — работа с элементами формы, диалоги, вопросы пользователю.
  • 🗄️ &НаСервере — запросы к базе, доступ к реквизитам формы и объекту на сервере.
  • ⚡ &НаСервереБезКонтекста — серверный код без передачи данных формы. Быстрее обычного серверного вызова.
  • 🔁 &НаКлиентеНаСервереБезКонтекста — код, который компилируется на обеих сторонах. Подходит для расчётов без обращения к базе.

Если директиву не указать, процедура в модуле формы считается &НаКлиенте.

Флажки общего модуля

Для общих модулей директив нет. Сторону выполнения задают свойства модуля: Клиент (управляемое приложение), Сервер, Вызов сервера, Внешнее соединение, Глобальный, Привилегированный, Повторное использование возвращаемых значений.

Без флажка «Вызов сервера» экспортную процедуру серверного общего модуля нельзя вызвать с клиента. Это одна из самых частых причин ошибки «Процедура или функция с указанным именем не определена».

Как создать процедуру-обработчик через свойства

Обработчики событий вручную лучше не писать: легко ошибиться в имени или параметрах.

  1. Откройте форму в Конфигураторе.
  2. Выделите элемент, например кнопку или поле.
  3. В палитре свойств найдите раздел событий и нужное событие.
  4. Нажмите кнопку с лупой справа от поля события.
  5. В модуле формы появится процедура с правильным именем, директивой и параметрами.

Для событий серверной стороны Конфигуратор предложит выбрать, как создать обработчик: только клиентскую процедуру или пару «клиентская процедура + серверная процедура».

Как передать параметры в процедуру: Знач и по ссылке

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

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

А = 5;
Удвоить(А);
// А теперь равно 10

Ключевое слово Знач включает передачу по значению. Процедура получает копию, и внешняя переменная не меняется.

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

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

Подвох с объектами и коллекциями

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

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

Знач при серверных вызовах

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

Для параметров, которые процедура только читает, ставьте Знач. На больших структурах и массивах это заметно сокращает трафик и время вызова.

Необязательные параметры

Параметру можно задать значение по умолчанию. Оно должно быть литералом: числом, строкой, датой, Истина, Ложь или Неопределено.

Процедура ЗаписатьВЛог(Текст, Уровень = "Инфо", ПоказатьПользователю = Ложь)
    // ...
КонецПроцедуры

ЗаписатьВЛог("Старт обмена");
ЗаписатьВЛог("Ошибка загрузки", "Ошибка", Истина);
ЗаписатьВЛог("Пропуск", , Истина); // второй параметр пропущен, будет "Инфо"

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

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

Разница одна по сути: функция возвращает значение, процедура — нет.

ПризнакПроцедураФункция
Ключевые словаПроцедура … КонецПроцедурыФункция … КонецФункции
Возвращает значениеНетДа, через Возврат Значение;
Можно использовать в выраженииНетДа: Сумма = РассчитатьСумму(Цена, Кол);
Оператор ВозвратБез значения, для досрочного выходаСо значением
Функция СуммаСНДС(Сумма, Ставка)
    Возврат Сумма + Сумма * Ставка / 100;
КонецФункции

Процедура ПроверитьОстаток(Количество)
    Если Количество <= 0 Тогда
        Возврат; // досрочный выход из процедуры
    КонецЕсли;
    // ...
КонецПроцедуры

Пример: СуммаСНДС(1000, 20) вернёт 1200.

Когда что выбирать

  • ✅ Процедура — когда нужно что-то сделать: заполнить табличную часть, записать в журнал, показать сообщение.
  • ✅ Функция — когда нужно получить результат: сумму, ссылку, признак «можно ли провести».
  • ⚠️ Не делайте функцию, которая и меняет данные, и возвращает результат, без явной причины. Такой код трудно читать.

Функцию можно вызвать как процедуру, отбросив результат. Обратное не работает : попытка присвоить «результат» процедуры даст ошибку при выполнении.

Обработка ошибок внутри процедуры: Попытка и Исключение

Если в процедуре может произойти сбой — запись объекта, обращение к файлу, загрузка из внешней системы — оберните опасный участок в Попытка … Исключение … КонецПопытки.

Процедура ЗагрузитьКурс(Валюта, Дата)
    Попытка
        ВыполнитьЗагрузку(Валюта, Дата);
    Исключение
        ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
        ЗаписьЖурналаРегистрации("ЗагрузкаКурсов",
            УровеньЖурналаРегистрации.Ошибка, , , ТекстОшибки);
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

Что здесь происходит:

  1. ИнформацияОбОшибке() возвращает объект с данными о сбое.
  2. ПодробноеПредставлениеОшибки превращает его в текст со стеком вызовов. В актуальных версиях платформы ту же задачу решает ОбработкаОшибок.ПодробноеПредставлениеОшибки.
  3. Текст пишется в журнал регистрации.
  4. ВызватьИсключение; без параметров пробрасывает исходную ошибку дальше.

Ошибка с собственным текстом

Если нужно прервать выполнение по бизнес-причине, вызовите исключение с текстом:

Если НЕ ЗначениеЗаполнено(Контрагент) Тогда
    ВызватьИсключение "Не указан контрагент. Запись невозможна.";
КонецЕсли;

Попытка и транзакции

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

НачатьТранзакцию();
Попытка
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

Не «глушите» ошибки пустым блоком Исключение. Пользователь не увидит сбоя, данные запишутся не полностью, а причину потом придётся искать неделями.

Как ускорить процедуры в 1С

Медленный код в 1С чаще всего тормозит не из-за алгоритма, а из-за лишних обращений к серверу и к базе.

Меньше серверных вызовов

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

// Плохо: вызов сервера на каждую строку
Для Каждого Строка Из Объект.Товары Цикл
    Строка.Цена = ПолучитьЦенуНаСервере(Строка.Номенклатура);
КонецЦикла;

// Хорошо: один вызов на всю таблицу
ЗаполнитьЦеныНаСервере();

&НаСервереБезКонтекста вместо &НаСервере

При вызове &НаСервере платформа передаёт на сервер и обратно данные формы. При &НаСервереБезКонтекста — только параметры. Если процедуре не нужны реквизиты формы, используйте вариант без контекста.

Запросы вне цикла

  • 🐢 Запрос в цикле по строкам — тысячи обращений к базе.
  • 🚀 Один запрос с отбором по списку (В (&Список)) или соединением с временной таблицей.
  • 🚫 Обращение через точку к реквизитам ссылки в цикле (Строка.Номенклатура.Артикул) — это скрытый запрос на каждую строку. Получайте реквизиты пакетно, одним запросом.

Кэширование через общий модуль

Для функций, которые возвращают редко меняющиеся данные (настройки, константы), заведите общий модуль с флажком Повторное использование возвращаемых значений = «На время сеанса». Повторный вызов с теми же параметрами вернёт результат из кэша без обращения к базе.

Замер производительности

Перед оптимизацией измерьте. В Конфигураторе в режиме отладки включите замер производительности через меню Отладка. Он покажет, сколько времени заняла каждая строка процедуры. Чаще всего 80% времени уходит на две-три строки.

Проверка процедуры перед сдачей
Выполнено: 0 / 7

Процедуры в расширении конфигурации

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

  • ⬅️ &Перед("ИмяПроцедуры") — ваш код выполнится до типового.
  • ➡️ &После("ИмяПроцедуры") — после типового.
  • 🔄 &Вместо("ИмяПроцедуры") — вместо типового; вызвать исходный код можно через ПродолжитьВызов().
  • ✏️ &ИзменениеИКонтроль("ИмяПроцедуры") — правка текста типовой процедуры с контролем изменений при обновлении.
&После("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    Если НЕ ЗначениеЗаполнено(Комментарий) Тогда
        Комментарий = "Создан через расширение";
    КонецЕсли;
КонецПроцедуры

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

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

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