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