В процессе разработки в среде 1С:Предприятие начинающие специалисты часто сталкиваются с фундаментальной архитектурной проблемой: попыткой получить значение какого-либо поля формы, имея на руках только ссылку на элемент справочника (СправочникСсылка). При этом нужное значение визуально отображается на форме элемента, но физически отсутствует в реквизитах самого справочника.
Давайте подробно разберем, почему возникает эта сложность, проанализируем внутреннее устройство платформы 1С и рассмотрим три практических способа решения этой задачи в зависимости от контекста и сложности вашей конфигурации.
Для того чтобы понять суть проблемы, нам необходимо четко разграничить два понятия: данные базы данных и данные интерфейса (формы). Это классический пример разделения ответственности в архитектуре программного обеспечения (паттерн MVC — Model-View-Controller).
Когда вы оперируете переменной типа СправочникСсылка (например, ссылкой на номенклатуру или контрагента), вы работаете с уникальным идентификатором (GUID) записи в таблице СУБД. Через эту ссылку (или получаемый из нее объект СправочникОбъект) можно прочитать только те данные, которые жестко закреплены за этой таблицей в метаданных конфигурации:
В то же время реквизиты формы (в управляемых формах) или элементы управления (в обычных формах) — это временные программные сущности. Давайте проанализируем их жизненный цикл:
Именно поэтому попытка написать код вида СсылкаНаСправочник.ИмяРеквизитаФормы всегда будет приводить к ошибке: у объекта базы данных просто нет такого свойства.
Ниже представлены проверенные методы, которые помогут нам обойти это ограничение и получить заветное значение.
Давайте подумаем логически: если при открытии формы элемента поле ввода заполняется каким-то значением, значит, в коде модуля формы (в процедурах ПриСозданииНаСервере или ПриОткрытии) прописан алгоритм его получения. Данные не могут появиться из ниоткуда.
Чаще всего источником таких данных выступает регистр сведений. Например, актуальная цена товара, основная спецификация номенклатуры, текущий лимит кредита контрагента или ответственный менеджер. Вместо того чтобы пытаться «вытащить» значение из формы, мы должны обратиться напрямую к этому регистру.
Рассмотрим по шагам, как это сделать программно с помощью запроса или менеджера регистра сведений:
// Пример получения значения из периодического регистра сведений "ЦеныНоменклатуры"
// Если на форме отображалась актуальная цена товара по ссылке СсылкаНаНоменклатуру
Функция ПолучитьЦенуИзПервоисточника(СсылкаНаНоменклатуру, АктуальнаяДата) Экспорт
// Подготовим структуру отбора для получения последней записи регистра
Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", СсылкаНаНоменклатуру);
// Обратимся к менеджеру регистра сведений для получения последнего среза
РесурсЗначение = РегистрыСведений.ЦеныНоменклатуры.ПолучитьПоследнее(АктуальнаяДата, Отбор);
Возврат РесурсЗначение.Цена;
КонецФункции
Если алгоритм получения сложнее, мы можем использовать полноценный запрос к базе данных. Это наиболее производительный метод, который работает мгновенно и не нагружает систему вызовом интерфейсных механизмов.
// Альтернативный вариант получения значения через запрос к базе данных
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЦеныНоменклатурыСрезПоследних.Цена КАК АктуальнаяЦена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура = &Номенклатура) КАК ЦеныНоменклатурыСрезПоследних";
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Номенклатура", СсылкаНаОбъект);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
НужноеЗначение = Выборка.АктуальнаяЦена;
КонецЕсли;
Иногда алгоритм расчета значения в модуле формы настолько запутан, огромен и завязан на внутренние процедуры интерфейса, что переписать его в виде отдельного запроса крайне трудно. В этом случае мы можем пойти на хитрость: программно создать форму в памяти компьютера, прочитать значение нужного реквизита и сразу же закрыть ее, даже не показывая пользователю.
Давайте рассмотрим, как реализовать этот метод для управляемых форм (актуально для платформ 1С 8.2 и 1С 8.3):
// Код выполняется на КЛИЕНТЕ
&НаКлиенте
Процедура ПолучитьЗначениеИзФормыУправляемой(СсылкаНаОбъект)
// Передаем ссылку в параметры для инициализации формы нужным объектом
ПараметрыФормы = Новый Структура("Ключ", СсылкаНаОбъект);
// Создаем форму программно. Она создается в памяти, но не открывается на экране
ФормаОбъекта = ПолучитьФорму("Справочник.Номенклатура.Форма.ФормаЭлемента", ПараметрыФормы);
// Способ А: Если нужный реквизит формы объявлен в модуле формы с ключевым словом "Экспорт"
// Или если это стандартный реквизит формы, доступный напрямую:
НужноеЗначение = ФормаОбъекта.ИмяРеквизитаФормы;
// Способ Б: Если в модуле формы создана экспортная функция для получения этого значения
НужноеЗначение = ФормаОбъекта.ПолучитьРассчитанноеЗначение();
КонецПроцедуры
Для обычных форм (в режиме толстого клиента, который активно использовался в 1С 8.2) программное получение значения выглядит несколько иначе. Мы обращаемся непосредственно к элементам управления формы:
// Код для обычного приложения (обычные формы)
Процедура ПолучитьЗначениеИзОбычнойФормы(СсылкаНаОбъект)
// Получаем объект из ссылки
ОбъектСправочника = СсылкаНаОбъект.ПолучитьОбъект();
// Инициализируем форму элемента
ФормаОбъекта = ОбъектСправочника.ПолучитьФорму("ФормаЭлемента");
// Напрямую читаем значение из элемента управления (например, ПолеВвода)
НужноеЗначение = ФормаОбъекта.ЭлементыФормы.ПолеВводаЗначения.Значение;
КонецПроцедуры
Важное предупреждение: Использование Способа 2 является крайне нежелательным («костыльным») решением. Создание формы — это очень тяжелая операция для процессора и оперативной памяти, так как серверу приходится генерировать структуру всего интерфейса. Кроме того, этот метод категорически не будет работать на сервере (например, в регламентных или фоновых заданиях), где контекст отображения интерфейса полностью отсутствует.
Для того чтобы в будущем не сталкиваться с необходимостью «выковыривать» данные из интерфейса, мы рекомендуем придерживаться золотого правила разработки 1С: «Модуль формы отвечает только за визуализацию, а вся бизнес-логика должна находиться на сервере в общих модулях или модулях менеджера».
Давайте разберем правильную схему реализации этой логики по шагам:
Контрагенты).
// Размещаем в Модуле менеджера справочника "Контрагенты"
Функция ПолучитьДополнительныйПараметрКонтрагента(СсылкаНаОбъект) Экспорт
// Сюда помещаем всю логику: запросы к регистрам, вычисления, проверки
РезультатРасчета = "";
Запрос = Новый Запрос;
// ... здесь пишем текст запроса ...
Возврат РезультатРасчета;
КонецФункции
// В модуле управляемой формы элемента справочника
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Заполняем реквизит формы через вызов функции из модуля менеджера
ЭтотОбъект.ИмяРеквизитаФормы = Справочники.Контрагенты.ПолучитьДополнительныйПараметрКонтрагента(Объект.Ссылка);
КонецПроцедуры
// Вызов из внешней обработки или отчета
НужноеЗначение = Справочники.Контрагенты.ПолучитьДополнительныйПараметрКонтрагента(ВыбраннаяСсылка);
Таким образом, мы полностью разделили интерфейс и данные. Наш код стал чистым, масштабируемым и соответствующим официальным стандартам разработки фирмы «1С».