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