Серверный код в 1С 8.3: от директивы &НаСервере до фонового задания
Содержание
Чтобы запустить процедуру на сервере 1С, поставьте над ней директиву &НаСервере (или &НаСервереБезКонтекста) в модуле формы и вызовите её из клиентской процедуры обычным вызовом по имени. Платформа сама передаст управление на сервер, выполнит код и вернёт результат на клиент. Для общего модуля нужны флажки Сервер и Вызов сервера и ключевое слово Экспорт у процедуры.
Способов выполнить код на сервере несколько, и выбирают их по задаче. Нужно что-то прочитать из базы при нажатии кнопки — хватит серверной процедуры формы. Логика нужна в десяти формах — её выносят в общий модуль. Операция идёт минуты — её запускают фоновым заданием, чтобы не вешать интерфейс. Всё это работает одинаково в клиент-серверном и файловом варианте: в файловом «сервер» просто живёт внутри процесса клиентского приложения.
Где выполняется код: клиент, сервер и директивы компиляции
В управляемом приложении код формы делится на две части. Клиентская часть работает на компьютере пользователя и не видит базу данных. Серверная работает в рабочем процессе кластера и имеет доступ к запросам, объектам и регистрам.
Где выполнится конкретная процедура модуля формы, решает директива компиляции перед её объявлением.
| Директива | Где выполняется | Данные формы | Когда применять |
|---|---|---|---|
&НаКлиенте | клиент | доступны | обработчики кнопок, диалоги, работа с интерфейсом |
&НаСервере | сервер | доступны (передаются на сервер) | нужно читать или менять реквизиты формы и обращаться к базе |
&НаСервереБезКонтекста | сервер | недоступны | расчёт по параметрам, запрос к базе без обращения к форме |
&НаКлиентеНаСервереБезКонтекста | там, откуда вызвали | недоступны | общие вычисления, нужные и клиенту, и серверу |
Процедура без директивы в модуле формы считается серверной. Полагаться на это не стоит: явная директива читается проще и не путает коллег.
В общих модулях директив нет. Контекст задают флажки в свойствах модуля:
- 🖥
Сервер— модуль компилируется на сервере; - 📞
Вызов сервера— его экспортные методы можно вызывать с клиента; - 💻
Клиент (управляемое приложение)— модуль работает на клиенте; - 🔗
Внешнее соединение— доступен при подключении через COM; - 🔐
Привилегированный— код выполняется без проверки прав.
Внутри модулей, которые компилируются в нескольких контекстах (модуль объекта, менеджера, общий модуль с флажками Клиент и Сервер), участки кода отделяют инструкциями препроцессора:
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
// код, которому нужна база данных
#КонецЕсли
Как вызвать процедуру на сервере из формы: параметры и возврат значения
Классическая связка — клиентский обработчик кнопки плюс серверная функция. Клиент собирает данные, сервер считает, клиент показывает итог.
&НаКлиенте
Процедура ПосчитатьОстаток(Команда)
Остаток = ОстатокТовараНаСервере(Объект.Номенклатура, Объект.Склад);
ПоказатьПредупреждение(, "Остаток: " + Формат(Остаток, "ЧДЦ=3; ЧН=0"));
КонецПроцедуры
&НаСервереБезКонтекста
Функция ОстатокТовараНаСервере(Знач Номенклатура, Знач Склад)
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ЕСТЬNULL(СУММА(Т.КоличествоОстаток), 0) КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура = &Номенклатура И Склад = &Склад) КАК Т";
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Запрос.УстановитьПараметр("Склад", Склад);
Выборка = Запрос.Выполнить().Выбрать();
Выборка.Следующий();
Возврат Выборка.Количество;
КонецФункции
Имя регистра и его измерения здесь условные: подставьте те, что есть в вашей конфигурации. Агрегатная функция СУММА гарантирует одну строку в результате, поэтому Выборка.Следующий() всегда вернёт запись.
Что можно передавать между клиентом и сервером
Параметры и результат сериализуются и едут по сети. Поэтому годится не любой тип.
- ✅ Можно: примитивы (Строка, Число, Дата, Булево), ссылки на объекты базы,
Структура,Массив,Соответствие, их фиксированные варианты,ХранилищеЗначения, данные формы. - ❌ Нельзя:
СправочникОбъект,ДокументОбъект,ТаблицаЗначений,Запрос,РезультатЗапроса— эти типы живут только на сервере.
Нужна таблица на клиенте — перегоните её в массив структур или в реквизит формы типа «Таблица значений». Нужен объект целиком — работайте с ним на сервере, а на клиент верните только то, что надо показать.
Знач и параметры по ссылке
При вызове сервера с клиента параметр без Знач передаётся «по ссылке»: изменения, сделанные на сервере, вернутся на клиент. Платформа для этого гонит значение туда и обратно. Если серверу параметр нужен только для чтения, пишите Знач — это минус лишняя пересылка, особенно для больших структур и массивов.
Чем &НаСервере дороже &НаСервереБезКонтекста
При вызове &НаСервере платформа передаёт на сервер данные формы, а потом возвращает изменения. Для формы документа с табличной частью в 500 строк это ощутимый трафик на каждый клик.
Правило практика: если серверной процедуре не нужны реквизиты формы, ставьте &НаСервереБезКонтекста и передавайте ровно те значения, что нужны. В медленных сетях и на тонком клиенте через интернет разница видна невооружённым глазом.
Ещё один приём — объединять вызовы. Три подряд серверные функции из одного клиентского обработчика — это три сетевых обращения. Одна функция, которая вернёт Структуру с тремя значениями, — одно.
Вызов серверного кода из общих модулей и модулей объектов
Когда одна и та же серверная логика нужна в нескольких местах, её выносят в общий модуль. Порядок такой:
- В Конфигураторе создайте общий модуль, например
РаботаСОстаткамиВызовСервера. - В свойствах отметьте
СервериВызов сервера. ФлажокКлиентне ставьте. - Объявите функцию с ключевым словом
Экспорт. - С клиента вызывайте её через имя модуля:
РаботаСОстаткамиВызовСервера.Остаток(Номенклатура, Склад).
Без флажка Вызов сервера клиент этот модуль не увидит. Без Экспорт метод не виден за пределами модуля. Удобная привычка — держать серверную логику в модуле только с флажком Сервер, а в модуле …ВызовСервера оставлять тонкие функции-обёртки. Так с клиента доступно лишь то, что вы осознанно открыли.
Модуль объекта и модуль менеджера
Модули объекта и менеджера справочника или документа выполняются на сервере, но вызвать их напрямую с клиента нельзя. Из формы элемента к модулю объекта обращаются так:
&НаСервере
Процедура ЗаполнитьПоОснованиюНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ЗаполнитьТовары(); // экспортная процедура модуля объекта
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Метод модуля менеджера вызывают в серверной процедуре через менеджер: Документы.РеализацияТоваровУслуг.ИмяМетода(...). Метод должен быть экспортным, а сам вызов — внутри серверного кода.
Выполнение процедуры на сервере в привилегированном режиме
Бывает, что пользователю нельзя давать права на регистр, но код должен в него записать. Например, служебный журнал операций. Для этого есть привилегированный режим: в нём не проверяются права доступа и ограничения на уровне записей.
Включить его можно двумя способами.
- 🔐 Флажок
Привилегированныйу общего модуля. Весь серверный код модуля выполняется без проверки прав. Модуль при этом может быть только серверным. - ⚙️ Метод
УстановитьПривилегированныйРежим(Истина)внутри серверной процедуры. Режим действует до вызоваУстановитьПривилегированныйРежим(Ложь)или до возврата на клиент.
&НаСервереБезКонтекста
Процедура ЗаписатьВЖурналОпераций(Знач Текст)
УстановитьПривилегированныйРежим(Истина);
Запись = РегистрыСведений.ЖурналОпераций.СоздатьМенеджерЗаписи();
Запись.Период = ТекущаяДатаСеанса();
Запись.Пользователь = Пользователи.ТекущийПользователь();
Запись.Текст = Текст;
Запись.Записать();
УстановитьПривилегированныйРежим(Ложь);
КонецПроцедуры
Регистр ЖурналОпераций условный, а Пользователи.ТекущийПользователь() есть в конфигурациях на Библиотеке стандартных подсистем. На клиенте привилегированный режим не включается: метод работает только на сервере. Проверить текущее состояние можно функцией ПривилегированныйРежим().
Никогда не принимайте в привилегированной процедуре «сырой» текст запроса или имя объекта от клиента. Иначе любой пользователь, вызвав такой метод, прочитает данные, на которые у него нет прав. Привилегированный код должен делать ровно одну заранее известную операцию.
Асинхронный запуск процедуры на сервере: фоновые задания
Серверный вызов из формы синхронный. Пока сервер считает, интерфейс у пользователя стоит. Для операций дольше нескольких секунд — пересчёт, загрузка файла, массовое перепроведение — процедуру запускают фоновым заданием.
Как запустить фоновое задание
Процедура для фона должна быть экспортной и лежать в серверном общем модуле. Результат она кладёт во временное хранилище.
// Общий модуль ДлительныеРасчеты (флажок «Сервер»)
Процедура ПересчитатьЦены(Параметры, АдресРезультата) Экспорт
Обработано = 0;
// ... долгий расчёт ...
ПоместитьВоВременноеХранилище(Обработано, АдресРезультата);
КонецПроцедуры
&НаСервере
Функция ЗапуститьПересчетНаСервере()
АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(Новый Структура("Склад", Объект.Склад));
ПараметрыЗадания.Добавить(АдресРезультата);
Задание = ФоновыеЗадания.Выполнить("ДлительныеРасчеты.ПересчитатьЦены",
ПараметрыЗадания, , "Пересчёт цен");
ИдентификаторЗадания = Задание.УникальныйИдентификатор;
Возврат АдресРезультата;
КонецФункции
Первый аргумент ФоновыеЗадания.Выполнить — строка «ИмяМодуля.ИмяМетода». Второй — массив параметров в порядке объявления. Третий — ключ: два задания с одним ключом одновременно не запустятся, это защищает от двойного нажатия кнопки. Четвёртый — наименование, которое видно в списке заданий.
Временное хранилище создаётся с идентификатором формы. Пока форма открыта, результат не пропадёт.
Как дождаться результата
На клиенте подключите обработчик ожидания и опрашивайте состояние задания:
&НаКлиенте
Процедура ПроверитьЗадание() Экспорт
Если ЗаданиеЗавершено(ИдентификаторЗадания) Тогда
Обработано = ПолучитьИзВременногоХранилища(АдресРезультата);
ПоказатьОповещениеПользователя("Готово", , "Обработано: " + Формат(Обработано, "ЧГ=0; ЧН=0"));
Иначе
ПодключитьОбработчикОжидания("ПроверитьЗадание", 2, Истина);
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЗаданиеЗавершено(Знач Идентификатор)
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Идентификатор);
Возврат Задание = Неопределено Или Задание.Состояние <> СостояниеФоновогоЗадания.Активно;
КонецФункции
ИдентификаторЗадания и АдресРезультата здесь — реквизиты формы. На сервере, если нужно, задание можно ждать синхронно: Задание.ОжидатьЗавершенияВыполнения(30) — ждать не дольше 30 секунд.
В типовых конфигурациях на БСП не пишите опрос вручную. Там есть готовые ДлительныеОперации.ВыполнитьВФоне на сервере и ДлительныеОперацииКлиент.ОжидатьЗавершение на клиенте: они сами показывают индикатор, ловят ошибки и чистят хранилище.
Асинх и Ждать — это не про сервер
С платформы 8.3.18 в клиентском коде появились Асинх и Ждать. Они упрощают работу с диалогами и файлами на клиенте. Серверный вызов от них асинхронным не становится: для долгого кода по-прежнему нужен фон.
Разовый запуск процедуры на сервере без правки конфигурации
Иногда процедуру надо выполнить один раз в рабочей базе: поправить данные, проверить гипотезу, вызвать метод общего модуля. Снимать конфигурацию с поддержки ради этого не нужно.
Внешняя обработка. Создайте в Конфигураторе внешнюю обработку
(Файл → Новый → Внешняя обработка), добавьте форму, кнопку и пару процедур: клиентский обработчик и серверную процедуру с вашим кодом. Откройте файл в пользовательском режиме через Файл → Открыть. Серверная часть выполнится в контексте базы с правами текущего пользователя. Запуск внешних обработок требует соответствующего права, а при включённой защите от опасных действий платформа может попросить подтверждение.
Регламентное задание. Если процедура уже оформлена как регламентное задание, его можно запустить вручную. В конфигурациях на БСП это делают в разделе Администрирование → Обслуживание → Регламентные и фоновые задания: выделите задание и выполните его сразу, не дожидаясь расписания. Там же видны активные и завершённые фоновые задания с текстом ошибок.
Код, который меняет данные, сначала прогоните на копии базы. Обработку, которая массово перезаписывает документы, в рабочей базе запускайте, когда пользователи не проводят документы: иначе возможны конфликты блокировок и ошибки у них.
Отладка и диагностика серверного кода
Отладка в файловом варианте
В файловой базе серверный код выполняется внутри клиентского приложения. Точки останова в процедурах &НаСервере срабатывают сразу: запустите отладку из Конфигуратора клавишей F5, ничего дополнительно настраивать не нужно.
Отладка на сервере 1С
В клиент-серверном варианте рабочий процесс должен разрешать отладку. Для этого агент сервера запускают с ключом -debug. На Windows его дописывают в командную строку службы сервера 1С:Предприятия, затем службу перезапускают. Для отладки по протоколу HTTP добавляют ещё ключ -http, и тот же режим выбирают в Конфигураторе в параметрах отладки.
Перезапуск службы сервера 1С обрывает все сеансы пользователей во всех базах кластера. Делайте это вне рабочего времени или на тестовом сервере. На рабочем кластере отладку постоянно не держат: она снижает производительность.
После перезапуска в Конфигураторе откройте Отладка → Подключение и включите автоматическое подключение серверных предметов отладки. Тогда точки останова в серверных процедурах будут срабатывать, как в файловой базе.
Диагностика без отладчика
Отладчик не всегда доступен: рабочий сервер, нет прав, ошибка плавающая. Тогда помогают другие инструменты.
- 📋 Журнал регистрации. В серверном коде пишите события методом
ЗаписьЖурналаРегистрации("Пересчёт цен", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки()). Смотрят его вАдминистрирование → Обслуживание → Журнал регистрацииили из Конфигуратора. - ⏱ Замер производительности. В Конфигураторе
Отладка → Замер производительностипоказывает, какие строки серверного кода съедают время. Здесь же видно лишние серверные вызовы. - 🧾 Технологический журнал. Настраивается файлом
logcfg.xmlна сервере и пишет события рабочих процессов: исключения, длительные запросы, блокировки. Это инструмент для случаев, когда ошибка воспроизводится только у пользователей. - 🧩 Попытка / Исключение. Оборачивайте фоновую процедуру целиком и пишите
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())в журнал: ошибка в фоне не всплывает к пользователю сама.