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