Как прочитать константу из клиентского кода управляемой формы
Содержание
В тонком и веб-клиенте 1С 8.3 получить значение константы напрямую нельзя: менеджер Константы на клиенте не существует. Значение читают на сервере через Константы.ИмяКонстанты.Получить() и передают на клиент — серверной функцией с директивой &НаСервереБезКонтекста, через реквизит формы, заполненный в ПриСозданииНаСервере, или через общий модуль с повторным использованием возвращаемых значений.
Какой способ выбрать, зависит от того, как часто клиентский код обращается к константе и может ли её значение поменяться за время сеанса. Ниже код для каждого варианта, разбор ошибок и сравнение по числу серверных вызовов.
Почему константа недоступна в клиентском коде
Управляемое приложение разделяет код на клиентский и серверный. Клиент не имеет прямого доступа к базе данных: всё, что лежит в таблицах, читается только на сервере. Константа хранится в собственной таблице базы, поэтому её менеджер существует только в серверном контексте.
Где доступен менеджер Константы:
- ✅ сервер (модуль объекта, модуль менеджера, серверные процедуры формы, серверные общие модули);
- ✅ толстый клиент в режиме обычного приложения;
- ✅ внешнее соединение;
- ❌ тонкий клиент;
- ❌ веб-клиент;
- ❌ толстый клиент в режиме управляемого приложения (клиентский код формы).
Если написать обращение к константе в процедуре с директивой &НаКлиенте, синтаксический контроль в Конфигураторе остановит вас ошибкой Переменная не определена (Константы). Это не баг, а следствие архитектуры: каждое чтение данных — это вызов сервера.
Каждый вызов сервера — сетевой обмен. В локальной сети это миллисекунды, через интернет и веб-клиент — ориентировочно 50–300 мс на вызов. Поэтому задача не просто «получить константу», а получить её с минимумом вызовов.
Чтение константы на сервере: Получить(), набор и запрос
Любой способ доставки на клиент начинается с серверного чтения. Вариантов три.
Метод Получить() менеджера значения
Самый короткий путь — одна строка:
ВалютаУчета = Константы.ВалютаРегламентированногоУчета.Получить();
Константы.ИмяКонстанты возвращает менеджер значения константы, а Получить() читает текущее значение из базы. Записывают его зеркальным методом Установить(Значение).
Набор констант для нескольких значений
Если нужно сразу несколько констант, создайте набор и прочитайте его одним обращением:
Набор = Константы.СоздатьНабор("ВалютаРегламентированногоУчета,ИспользоватьНесколькоОрганизаций");
Набор.Прочитать();
ВалютаУчета = Набор.ВалютаРегламентированногоУчета;
НесколькоОрганизаций = Набор.ИспользоватьНесколькоОрганизаций;
Имена в СоздатьНабор() перечисляются через запятую. Если вызвать метод без параметров, в набор попадут все константы конфигурации — для чтения двух значений это лишнее.
Запрос к таблице константы
Константы доступны и в языке запросов: у каждой есть таблица Константа.Имя с полем Значение. Это удобно, когда к значению константы нужно сразу получить реквизит ссылки:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Константа.Значение КАК Валюта,
| Константа.Значение.Наименование КАК НаименованиеВалюты
|ИЗ
| Константа.ВалютаРегламентированногоУчета КАК Константа";
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Валюта = Выборка.Валюта;
НаименованиеВалюты = Выборка.НаименованиеВалюты;
КонецЕсли;
Запрос выручает, если константа ссылочного типа, а на клиенте нужен не только сам элемент справочника, но и его наименование, код или другой реквизит.
Значение константы в реквизите формы
Самый экономный вариант для формы — прочитать значение при её создании и хранить в реквизите. Форма создаётся на сервере, поэтому чтение в ПриСозданииНаСервере не добавляет ни одного вызова: данные уйдут на клиент вместе с формой.
Порядок действий:
- Откройте форму в Конфигураторе и на вкладке
Реквизитыдобавьте реквизит, напримерВалютаУчета, с тем же типом, что у константы. - Если реквизит нужен только коду, а не пользователю, не выводите его на форму.
- В модуле формы заполните реквизит:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ВалютаУчета = Константы.ВалютаРегламентированногоУчета.Получить();
КонецПроцедуры
&НаКлиенте
Процедура СуммаПриИзменении(Элемент)
Если Объект.Валюта <> ВалютаУчета Тогда
// пересчёт по курсу без обращения к серверу
КонецЕсли;
КонецПроцедуры
После этого клиентский код читает ВалютаУчета как обычную переменную — без задержки и без вызовов.
Форма, связанная с константами напрямую
Для форм настроек платформа даёт ещё один путь: основной реквизит формы с типом КонстантыНабор. Такую форму удобно собрать для раздела настроек: элементы привязываются к константам, а при записи значения сохраняются автоматически. Для одной константы можно создать форму самой константы — её основной реквизит имеет тип менеджера значения этой константы.
Значение в реквизите — снимок на момент открытия формы. Если другой пользователь изменит константу, пока форма открыта, клиент об этом не узнает. Для редких настроек это нормально; для значений, которые меняются в течение дня, перечитывайте их перед критичной операцией.
Серверная функция для получения константы с клиента
Если значение нужно в момент действия пользователя, а не при открытии формы, вызовите серверную функцию. Ключевое — директива.
Внеконтекстный вызов из модуля формы
&НаКлиенте
Процедура Проверить(Команда)
Лимит = ЛимитСкидкиНаСервере();
Если Объект.ПроцентСкидки > Лимит Тогда
Сообщить(СтрШаблон("Скидка выше лимита: %1%%", Лимит));
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЛимитСкидкиНаСервере()
Возврат Константы.МаксимальнаяСкидка.Получить();
КонецФункции
Директива &НаСервереБезКонтекста не передаёт на сервер данные формы. С директивой &НаСервере на сервер уехали бы все реквизиты формы, а обратно — изменённые. Для чтения одной константы это бессмысленная нагрузка, особенно если в форме есть табличная часть на сотни строк.
Общий модуль с вызовом сервера
Когда константа нужна в нескольких формах, вынесите функцию в общий модуль. Настройки модуля в палитре свойств:
| Свойство | Значение |
|---|---|
| Сервер | Да |
| Вызов сервера | Да |
| Клиент (управляемое приложение) | Нет |
| Привилегированный | по ситуации (см. раздел об ошибках) |
// Общий модуль НастройкиУчетаВызовСервера
Функция ЗначениеКонстанты(ИмяКонстанты) Экспорт
Возврат Константы[ИмяКонстанты].Получить();
КонецФункции
Вызов с клиента: НастройкиУчетаВызовСервера.ЗначениеКонстанты("МаксимальнаяСкидка").
Универсальная функция с именем в параметре открывает клиенту чтение любой константы. Если среди них есть пароли, ключи API или служебные настройки, не делайте такую функцию экспортной в модуле с вызовом сервера. Лучше отдельная функция под каждое нужное значение или проверка имени по списку разрешённых.
Несколько констант за один вызов
Не вызывайте сервер трижды ради трёх констант. Верните структуру:
Функция НастройкиДляФормыЗаказа() Экспорт
Набор = Константы.СоздатьНабор("МаксимальнаяСкидка,ВалютаРегламентированногоУчета");
Набор.Прочитать();
Результат = Новый Структура;
Результат.Вставить("МаксимальнаяСкидка", Набор.МаксимальнаяСкидка);
Результат.Вставить("ВалютаУчета", Набор.ВалютаРегламентированногоУчета);
Возврат Новый ФиксированнаяСтруктура(Результат);
КонецФункции
Кэширование значения константы на клиенте
Если константа почти не меняется, а читают её часто (в обработчике ПриИзменении колонки, в цикле по строкам), даже один вызов на каждое событие заметен. Здесь помогает кэш.
Общий модуль с повторным использованием
Создайте отдельный общий модуль с флагами Сервер и Вызов сервера и установите свойство Повторное использование возвращаемых значений в значение На время сеанса. Имя по стандартам разработки — с суффиксом ПовтИсп, например НастройкиУчетаВызовСервераПовтИсп.
// Общий модуль НастройкиУчетаВызовСервераПовтИсп
Функция МаксимальнаяСкидка() Экспорт
Возврат Константы.МаксимальнаяСкидка.Получить();
КонецФункции
Первый вызов с клиента идёт на сервер. Повторные вызовы с теми же параметрами платформа отдаёт из клиентского кэша без обращения к серверу.
Что учитывать:
- ⏱ Значение живёт в кэше ограниченное время: оно удаляется через 20 минут после вычисления или через 6 минут после последнего использования, а также при нехватке памяти.
- 🔄 Принудительно сбросить кэш
можно методом
ОбновитьПовторноИспользуемыеЗначения()— например, после записи новой настройки. - 🔒 Возвращайте неизменяемые значения (
ФиксированнаяСтруктура,ФиксированныйМассив) или примитивы. Изменяемая структура из кэша может быть испорчена вызывающим кодом. - 📝 Параметры такой функции служат ключом кэша, поэтому передавайте только примитивные значения.
Глобальная переменная модуля приложения
Старый, но рабочий приём: объявите в модуле управляемого приложения экспортную переменную и заполните её при запуске.
// Модуль управляемого приложения
Перем глВалютаУчета Экспорт;
Процедура ПриНачалеРаботыСистемы()
глВалютаУчета = НастройкиУчетаВызовСервера.ЗначениеКонстанты("ВалютаРегламентированногоУчета");
КонецПроцедуры
Переменная видна из любого клиентского кода. Минус — значение не обновится до перезапуска сеанса, а сама практика глобальных переменных в современных конфигурациях не приветствуется.
Параметры работы клиента в БСП
В типовых конфигурациях на Библиотеке стандартных подсистем для этого есть штатный механизм — параметры работы клиента. Свои значения добавляют в переопределяемой процедуре общего модуля ОбщегоНазначенияПереопределяемый (процедуры с именами вида ПриДобавленииПараметровРаботыКлиента), а на клиенте читают через СтандартныеПодсистемыКлиент.ПараметрыРаботыКлиента(). Изменения в типовых модулях делайте через расширение, чтобы не снимать конфигурацию с поддержки.
Функциональные опции для булевых констант
Если константа логическая и включает часть функциональности, привяжите её к функциональной опции. Метод ПолучитьФункциональнуюОпцию("ИмяОпции") доступен на клиенте, а значения опций платформа кэширует. Заодно опция сама скроет элементы интерфейса, связанные с выключенной функциональностью.
Ошибки при получении константы и что с ними делать
Права доступа и привилегированный режим
Если константа хранит служебное значение, а давать пользователям право на неё не хочется, читайте её в общем модуле с флагом Привилегированный. Код такого модуля выполняется без проверки прав. Альтернатива — установить УстановитьПривилегированныйРежим(Истина) внутри серверной функции.
Проверить право заранее можно так:
Если ПравоДоступа("Чтение", Метаданные.Константы.МаксимальнаяСкидка) Тогда
Лимит = Константы.МаксимальнаяСкидка.Получить();
КонецЕсли;
Привилегированный режим снимает проверку прав целиком. Возвращайте на клиент только то значение, ради которого он включён, и никогда — служебные пароли и токены.
Пустое значение и тип по умолчанию
Если константу ни разу не записывали, Получить() вернёт пустое значение её типа: 0 для числа, пустую строку, Ложь, пустую ссылку. Клиентский код должен к этому быть готов, иначе проверка «скидка больше лимита» сработает на каждой строке. Проверяйте значение через ЗначениеЗаполнено() и задавайте поведение по умолчанию явно.
Какой способ быстрее: сравнение
Скорость здесь определяется не чтением из базы (константа читается мгновенно), а числом клиент-серверных вызовов и объёмом передаваемых данных.
| Способ | Вызовов сервера | Свежесть значения | Когда применять |
|---|---|---|---|
Реквизит формы из ПриСозданииНаСервере | 0 дополнительных | на момент открытия формы | значение нужно в одной форме |
&НаСервереБезКонтекста | 1 на каждое обращение | всегда актуальное | разовые проверки, меняющиеся настройки |
&НаСервере | 1 + передача данных формы | всегда актуальное | только если на сервере нужны и данные формы |
Общий модуль ПовтИсп | 1 на сеанс (до сброса кэша) | до 20 минут задержки | частое чтение редких настроек |
| Глобальная переменная | 1 при запуске | до перезапуска сеанса | старые конфигурации без БСП |
| Функциональная опция | кэшируется платформой | после обновления кэша | логические флаги функциональности |
Посчитать вызовы в своей конфигурации можно без догадок. Запустите клиент с параметром /DisplayPerformance — в строке состояния появится счётчик серверных вызовов и объём переданных данных. Выполните операцию до и после правки и сравните цифры. Для детального разбора по строкам кода используйте замер производительности в отладчике Конфигуратора.
Пример из практики: обработчик ПриИзменении колонки «Количество» проверяет лимит из константы. В документе на 200 строк при загрузке из файла это 200 вызовов сервера. Через веб-клиент при 100 мс на вызов — 20 секунд ожидания. Перенос значения в реквизит формы убирает все 200 вызовов.
Частые вопросы
Можно ли записать значение константы с клиента?
Установить(), с клиента её вызывают через серверную функцию или общий модуль с флагом «Вызов сервера». Пользователю нужно право «Изменение» на эту константу.Нужен ли серверный вызов в обычном приложении?
Константы доступен в клиентском коде, и Константы.Имя.Получить() работает напрямую.Как получить на клиенте константу из внешней обработки?
&НаСервереБезКонтекста. Права пользователя при этом действуют обычные.Что вернёт Получить() для константы типа ХранилищеЗначения?
ХранилищеЗначения. Распакуйте его на сервере методом Получить() и передайте на клиент уже само значение, если его тип можно передать на клиент.