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