Как прочитать константу из клиентского кода управляемой формы

· 10 мин

Содержание

В тонком и веб-клиенте 1С 8.3 получить значение константы напрямую нельзя: менеджер Константы на клиенте не существует. Значение читают на сервере через Константы.ИмяКонстанты.Получить() и передают на клиент — серверной функцией с директивой &НаСервереБезКонтекста, через реквизит формы, заполненный в ПриСозданииНаСервере, или через общий модуль с повторным использованием возвращаемых значений.

Какой способ выбрать, зависит от того, как часто клиентский код обращается к константе и может ли её значение поменяться за время сеанса. Ниже код для каждого варианта, разбор ошибок и сравнение по числу серверных вызовов.

Почему константа недоступна в клиентском коде

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

Где доступен менеджер Константы:

  • ✅ сервер (модуль объекта, модуль менеджера, серверные процедуры формы, серверные общие модули);
  • ✅ толстый клиент в режиме обычного приложения;
  • ✅ внешнее соединение;
  • ❌ тонкий клиент;
  • ❌ веб-клиент;
  • ❌ толстый клиент в режиме управляемого приложения (клиентский код формы).

Если написать обращение к константе в процедуре с директивой &НаКлиенте, синтаксический контроль в Конфигураторе остановит вас ошибкой Переменная не определена (Константы). Это не баг, а следствие архитектуры: каждое чтение данных — это вызов сервера.

Каждый вызов сервера — сетевой обмен. В локальной сети это миллисекунды, через интернет и веб-клиент — ориентировочно 50–300 мс на вызов. Поэтому задача не просто «получить константу», а получить её с минимумом вызовов.

Чтение константы на сервере: Получить(), набор и запрос

Любой способ доставки на клиент начинается с серверного чтения. Вариантов три.

Метод Получить() менеджера значения

Самый короткий путь — одна строка:

ВалютаУчета = Константы.ВалютаРегламентированногоУчета.Получить();

Константы.ИмяКонстанты возвращает менеджер значения константы, а Получить() читает текущее значение из базы. Записывают его зеркальным методом Установить(Значение).

Набор констант для нескольких значений

Если нужно сразу несколько констант, создайте набор и прочитайте его одним обращением:

Набор = Константы.СоздатьНабор("ВалютаРегламентированногоУчета,ИспользоватьНесколькоОрганизаций");
Набор.Прочитать();

ВалютаУчета = Набор.ВалютаРегламентированногоУчета;
НесколькоОрганизаций = Набор.ИспользоватьНесколькоОрганизаций;

Имена в СоздатьНабор() перечисляются через запятую. Если вызвать метод без параметров, в набор попадут все константы конфигурации — для чтения двух значений это лишнее.

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

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

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Константа.Значение КАК Валюта,
|	Константа.Значение.Наименование КАК НаименованиеВалюты
|ИЗ
|	Константа.ВалютаРегламентированногоУчета КАК Константа";
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
	Валюта = Выборка.Валюта;
	НаименованиеВалюты = Выборка.НаименованиеВалюты;
КонецЕсли;

Запрос выручает, если константа ссылочного типа, а на клиенте нужен не только сам элемент справочника, но и его наименование, код или другой реквизит.

Значение константы в реквизите формы

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

Порядок действий:

  1. Откройте форму в Конфигураторе и на вкладке Реквизиты добавьте реквизит, например ВалютаУчета, с тем же типом, что у константы.
  2. Если реквизит нужен только коду, а не пользователю, не выводите его на форму.
  3. В модуле формы заполните реквизит:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	ВалютаУчета = Константы.ВалютаРегламентированногоУчета.Получить();
КонецПроцедуры

&НаКлиенте
Процедура СуммаПриИзменении(Элемент)
	Если Объект.Валюта <> ВалютаУчета Тогда
		// пересчёт по курсу без обращения к серверу
	КонецЕсли;
КонецПроцедуры

После этого клиентский код читает ВалютаУчета как обычную переменную — без задержки и без вызовов.

Форма, связанная с константами напрямую

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

Значение в реквизите — снимок на момент открытия формы. Если другой пользователь изменит константу, пока форма открыта, клиент об этом не узнает. Для редких настроек это нормально; для значений, которые меняются в течение дня, перечитывайте их перед критичной операцией.

Серверная функция для получения константы с клиента

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

Внеконтекстный вызов из модуля формы

&НаКлиенте
Процедура Проверить(Команда)
	Лимит = ЛимитСкидкиНаСервере();
	Если Объект.ПроцентСкидки > Лимит Тогда
		Сообщить(СтрШаблон("Скидка выше лимита: %1%%", Лимит));
	КонецЕсли;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ЛимитСкидкиНаСервере()
	Возврат Константы.МаксимальнаяСкидка.Получить();
КонецФункции

Директива &НаСервереБезКонтекста не передаёт на сервер данные формы. С директивой &НаСервере на сервер уехали бы все реквизиты формы, а обратно — изменённые. Для чтения одной константы это бессмысленная нагрузка, особенно если в форме есть табличная часть на сотни строк.

Общий модуль с вызовом сервера

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

СвойствоЗначение
СерверДа
Вызов сервераДа
Клиент (управляемое приложение)Нет
Привилегированныйпо ситуации (см. раздел об ошибках)
// Общий модуль НастройкиУчетаВызовСервера
Функция ЗначениеКонстанты(ИмяКонстанты) Экспорт
	Возврат Константы[ИмяКонстанты].Получить();
КонецФункции

Вызов с клиента: НастройкиУчетаВызовСервера.ЗначениеКонстанты("МаксимальнаяСкидка").

Универсальная функция с именем в параметре открывает клиенту чтение любой константы. Если среди них есть пароли, ключи API или служебные настройки, не делайте такую функцию экспортной в модуле с вызовом сервера. Лучше отдельная функция под каждое нужное значение или проверка имени по списку разрешённых.

Несколько констант за один вызов

Не вызывайте сервер трижды ради трёх констант. Верните структуру:

Функция НастройкиДляФормыЗаказа() Экспорт
	Набор = Константы.СоздатьНабор("МаксимальнаяСкидка,ВалютаРегламентированногоУчета");
	Набор.Прочитать();
	Результат = Новый Структура;
	Результат.Вставить("МаксимальнаяСкидка", Набор.МаксимальнаяСкидка);
	Результат.Вставить("ВалютаУчета", Набор.ВалютаРегламентированногоУчета);
	Возврат Новый ФиксированнаяСтруктура(Результат);
КонецФункции

Кэширование значения константы на клиенте

Если константа почти не меняется, а читают её часто (в обработчике ПриИзменении колонки, в цикле по строкам), даже один вызов на каждое событие заметен. Здесь помогает кэш.

Общий модуль с повторным использованием

Создайте отдельный общий модуль с флагами Сервер и Вызов сервера и установите свойство Повторное использование возвращаемых значений в значение На время сеанса. Имя по стандартам разработки — с суффиксом ПовтИсп, например НастройкиУчетаВызовСервераПовтИсп.

// Общий модуль НастройкиУчетаВызовСервераПовтИсп
Функция МаксимальнаяСкидка() Экспорт
	Возврат Константы.МаксимальнаяСкидка.Получить();
КонецФункции

Первый вызов с клиента идёт на сервер. Повторные вызовы с теми же параметрами платформа отдаёт из клиентского кэша без обращения к серверу.

Что учитывать:

  • ⏱ Значение живёт в кэше ограниченное время: оно удаляется через 20 минут после вычисления или через 6 минут после последнего использования, а также при нехватке памяти.
  • 🔄 Принудительно сбросить кэш можно методом ОбновитьПовторноИспользуемыеЗначения() — например, после записи новой настройки.
  • 🔒 Возвращайте неизменяемые значения (ФиксированнаяСтруктура, ФиксированныйМассив) или примитивы. Изменяемая структура из кэша может быть испорчена вызывающим кодом.
  • 📝 Параметры такой функции служат ключом кэша, поэтому передавайте только примитивные значения.

Глобальная переменная модуля приложения

Старый, но рабочий приём: объявите в модуле управляемого приложения экспортную переменную и заполните её при запуске.

// Модуль управляемого приложения
Перем глВалютаУчета Экспорт;

Процедура ПриНачалеРаботыСистемы()
	глВалютаУчета = НастройкиУчетаВызовСервера.ЗначениеКонстанты("ВалютаРегламентированногоУчета");
КонецПроцедуры

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

Параметры работы клиента в БСП

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

Функциональные опции для булевых констант

Если константа логическая и включает часть функциональности, привяжите её к функциональной опции. Метод ПолучитьФункциональнуюОпцию("ИмяОпции") доступен на клиенте, а значения опций платформа кэширует. Заодно опция сама скроет элементы интерфейса, связанные с выключенной функциональностью.

Как часто клиентский код читает константу?

Ошибки при получении константы и что с ними делать

Диагностика: выберите симптом

Права доступа и привилегированный режим

Если константа хранит служебное значение, а давать пользователям право на неё не хочется, читайте её в общем модуле с флагом Привилегированный. Код такого модуля выполняется без проверки прав. Альтернатива — установить УстановитьПривилегированныйРежим(Истина) внутри серверной функции.

Проверить право заранее можно так:

Если ПравоДоступа("Чтение", Метаданные.Константы.МаксимальнаяСкидка) Тогда
	Лимит = Константы.МаксимальнаяСкидка.Получить();
КонецЕсли;

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

Пустое значение и тип по умолчанию

Если константу ни разу не записывали, Получить() вернёт пустое значение её типа: 0 для числа, пустую строку, Ложь, пустую ссылку. Клиентский код должен к этому быть готов, иначе проверка «скидка больше лимита» сработает на каждой строке. Проверяйте значение через ЗначениеЗаполнено() и задавайте поведение по умолчанию явно.

Какой способ быстрее: сравнение

Скорость здесь определяется не чтением из базы (константа читается мгновенно), а числом клиент-серверных вызовов и объёмом передаваемых данных.

СпособВызовов сервераСвежесть значенияКогда применять
Реквизит формы из ПриСозданииНаСервере0 дополнительныхна момент открытия формызначение нужно в одной форме
&НаСервереБезКонтекста1 на каждое обращениевсегда актуальноеразовые проверки, меняющиеся настройки
&НаСервере1 + передача данных формывсегда актуальноетолько если на сервере нужны и данные формы
Общий модуль ПовтИсп1 на сеанс (до сброса кэша)до 20 минут задержкичастое чтение редких настроек
Глобальная переменная1 при запускедо перезапуска сеансастарые конфигурации без БСП
Функциональная опциякэшируется платформойпосле обновления кэшалогические флаги функциональности

Посчитать вызовы в своей конфигурации можно без догадок. Запустите клиент с параметром /DisplayPerformance — в строке состояния появится счётчик серверных вызовов и объём переданных данных. Выполните операцию до и после правки и сравните цифры. Для детального разбора по строкам кода используйте замер производительности в отладчике Конфигуратора.

Пример из практики: обработчик ПриИзменении колонки «Количество» проверяет лимит из константы. В документе на 200 строк при загрузке из файла это 200 вызовов сервера. Через веб-клиент при 100 мс на вызов — 20 секунд ожидания. Перенос значения в реквизит формы убирает все 200 вызовов.

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

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