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