Как реализовать вычисляемые поля в Дереве Значений в 1С

Программист 1С v8.3 (Управляемые формы) Управленческий учет
← На главную

Работа с иерархическими структурами данных, такими как ДеревоЗначений, часто ставит перед разработчиком задачу не просто вывести список элементов, но и произвести сложные математические расчеты на лету. В этой статье мы подробно разберем, как правильно создавать вычисляемые поля, избегать типичных ошибок в запросах и эффективно обрабатывать итоги по иерархии. Мы пройдем путь от исправления ошибок в языке запросов до продвинутых методов использования Системы компоновки данных (СКД).

Разбор ошибки группировки в запросе

Рассмотрим ситуацию, когда при заполнении дерева значений через запрос возникает ошибка: «Поле не входит в группу». Проанализируем причину. В языке запросов 1С существует строгое правило: если в блоке ВЫБРАТЬ используется хотя бы одна агрегатная функция (например, СУММА, МАКСИМУМ, КОЛИЧЕСТВО), то все остальные поля, не обернутые в агрегаты, обязаны присутствовать в секции СГРУППИРОВАТЬ ПО.

Посмотрим на пример ошибочного кода, который часто встречается в практике:


ВЫБРАТЬ
    РасходТовары.Товар КАК Товар,
    СУММА(РасходТовары.Количество) КАК ОбщееКоличество,
    РасходТовары.Количество / РАЗНОСТЬДАТ(&Начало, &Конец, ДЕНЬ) КАК ДеньПродажа
ИЗ
    Документ.Расход.Товары КАК РасходТовары
СГРУППИРОВАТЬ ПО
    РасходТовары.Товар

Выясним причину ошибки: поле РасходТовары.Количество в выражении для ДеньПродажа используется без агрегатной функции, и при этом само выражение отсутствует в блоке группировки. Чтобы исправить это, мы должны либо включить расчет в агрегат, либо добавить все используемые поля в группировку. Правильный вариант будет выглядеть так:


ВЫБРАТЬ
    РасходТовары.Товар КАК Товар,
    СУММА(РасходТовары.Количество) КАК ОбщееКоличество,
    СУММА(РасходТовары.Количество) / РАЗНОСТЬДАТ(&Начало, &Конец, ДЕНЬ) КАК ДеньПродажа
ИЗ
    Документ.Расход.Товары КАК РасходТовары
СГРУППИРОВАТЬ ПО
    РасходТовары.Товар

Безопасное деление и обработка нулевых значений

Проанализируем выражение Продажи / РАЗНОСТЬДАТ(&Начало, &Конец, ДЕНЬ). Программисту важно помнить, что если параметры &Начало и &Конец совпадут, функция РАЗНОСТЬДАТ вернет ноль. В 1С деление на ноль в запросе приводит к фатальной ошибке выполнения.

Для реализации надежного вычисляемого поля используем конструкцию ВЫБОР:


ВЫБРАТЬ
    Товар,
    ВЫБОР 
        КОГДА РАЗНОСТЬДАТ(&Начало, &Конец, ДЕНЬ) = 0 ТОГДА 0 
        ИНАЧЕ СУММА(Количество) / РАЗНОСТЬДАТ(&Начало, &Конец, ДЕНЬ) 
    КОНЕЦ КАК СреднедневныеПродажи
ИЗ
    РегистрНакопления.Продажи
СГРУППИРОВАТЬ ПО
    Товар

Использование СКД для формирования дерева значений

Наиболее мощным инструментом для создания вычисляемых полей в иерархии является Система компоновки данных (СКД). Рассмотрим преимущества этого метода:

  1. Вкладка «Вычисляемые поля»: Вы можете задать формулу любой сложности, которая будет применяться автоматически.
  2. Ресурсы: СКД позволяет гибко настраивать расчет итогов. Например, в детальных записях вычисляется среднее, а в родительских узлах — сумма этих средних или их среднее арифметическое.
  3. Программный вывод: Результат можно выгрузить напрямую в объект ДеревоЗначений.

Посмотрим, как программно получить заполненное дерево с вычисляемыми полями из СКД:


// Создаем схему и настройки
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки());

// Выполняем компоновку
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(ИсточникДоступныхНастроекКомпоновкиДанных);

// Выводим в ДеревоЗначений
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ДеревоРезультат = Новый ДеревоЗначений;
ПроцессорВывода.УстановитьОбъект(ДеревоРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

Рекурсивный расчет итогов «снизу вверх»

Иногда данные в дерево загружаются из разных источников, и автоматический расчет в запросе невозможен. В таких случаях нам необходимо самостоятельно рассчитать значения в родительских узлах на основе дочерних. Разберем алгоритм рекурсивного обхода:

Для корректного расчета итогов (например, суммы продаж по категориям товаров) мы должны сначала вычислить значения в самых нижних элементах («листьях»), а затем передать эти значения «наверх» родителям.


Процедура РассчитатьИтогиДерева(СтрокиДерева)
    Для Каждого Строка Из СтрокиДерева Цикл
        // Если есть подчиненные строки, уходим вглубь (рекурсия)
        Если Строка.Строки.Количество() > 0 Тогда
            РассчитатьИтогиДерева(Строка.Строки);
            
            // После возврата из рекурсии суммируем данные дочерних строк
            Строка.ДеньПродажа = Строка.Строки.Итог("ДеньПродажа");
        Иначе
            // Расчет для конечного узла (листа)
            Если ЗначениеЗаполнено(Строка.Количество) Тогда
                 Строка.ДеньПродажа = Строка.Количество / КоличествоДнейПериода;
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Типизация колонок — залог стабильной работы

При создании колонок для вычисляемых полей программным способом крайне важно задавать ОписаниеТипов. Рассмотрим ситуацию: если вы добавите колонку без указания типа, 1С присвоит ей тип Произвольный. Это приведет к тому, что в интерфейсе вы не сможете настроить формат числа (количество знаков после запятой), а метод Итог() по этой колонке будет работать некорректно или вызывать ошибки.

Правильный способ добавления колонки:


МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
КвалификаторыЧисла = Новый КвалификаторыЧисла(15, 2);
ОписаниеТипаЧисло = Новый ОписаниеТипов(МассивТипов, , , КвалификаторыЧисла);

ДеревоЗначений.Колонки.Добавить("ДеньПродажа", ОписаниеТипаЧисло, "Продажи в день");

Отображение на управляемой форме

Если ваша задача — только отобразить расчетные данные в интерфейсе, не сохраняя их в структуре данных, проанализируем возможности управляемых форм. В отличие от обычных форм и события ПриВыводеСтроки, в управляемом приложении основной расчет должен происходить на сервере при заполнении коллекции.

Важный нюанс: Старайтесь избегать расчетов непосредственно в коде формы, если дерево содержит тысячи строк. Лучше всего подготовить данные в запросе на сервере, заполнить ими ДеревоЗначений и один раз передать готовую структуру на клиент. Это обеспечит высокую отзывчивость интерфейса.

Подведем итог: создание вычисляемых полей в дереве значений требует комплексного подхода. Мы разобрали, как исправить ошибки группировки в запросах, реализовали безопасный расчет с проверкой на ноль, рассмотрели мощь СКД и научились обходить дерево рекурсивно для подведения итогов. Используя эти методы, вы сможете строить сложные и производительные отчеты в формате деревьев.

← На главную