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