Как использовать вычисляемые поля в СКД и решить проблемы со связями наборов данных и сортировкой периодов?

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

Приветствуем вас, уважаемые разработчики! Система компоновки данных (СКД) в платформе 1С — это мощнейший декларативный инструмент для построения отчетов любой сложности. Однако при выходе за рамки простых однотабличных отчетов мы часто сталкиваемся со специфическим поведением платформы. Две классические проблемы, вызывающие ступор у многих программистов: невозможность обратиться к одному вычисляемому полю из другого и хаотичная сортировка периодов (например, внезапное появление «Января» посреди третьего квартала) при связывании нескольких наборов данных.

В этой статье мы подробно, по шагам разберем причины возникновения этих проблем и проанализируем наиболее эффективные практические способы их решения.

Раздел 1. Работа со связями наборов данных и особенности расчетов

Рассмотрим ситуацию, когда в нашем отчете задействованы два набора данных. Например, НаборДанных1 представляет собой таблицу с фактическими продажами, а НаборДанных2 — таблицу с плановыми показателями или информацией из периодического регистра сведений. Нам необходимо сопоставить эти данные и рассчитать отклонения.

Объединение таких таблиц настраивается на вкладке «Связи наборов данных». Важно помнить ключевую особенность СКД: связывание наборов данных выполняется на уровне процессора компоновки (в оперативной памяти сервера приложений 1С), а не на уровне СУБД. По сути, СКД выполняет аналог левого соединения (LEFT JOIN).

Это накладывает жесткие правила на логику сравнения полей в вычисляемых выражениях:

  1. Сравнение на уровне детальных записей: Мы можем использовать конструкцию ВЫБОР КОГДА... для детальных (строчных) записей только тогда, когда связь между наборами гарантирует уникальность сопоставления (связь «один к одному» или «многие к одному»). В выражении вычисляемого поля это выглядит стандартно:
    
    ВЫБОР
        КОГДА Набор1.Продажи > Набор2.План
            ТОГДА Набор1.Продажи - Набор2.План
        ИНАЧЕ 0
    КОНЕЦ
    
  2. Сравнение на уровне группировок (итогов): Если данные в наборах имеют разную степень детализации, прямое вычисление «в лоб» в детальных записях вернет некорректный результат. В таком случае сравнение и расчеты необходимо переносить на вкладку «Ресурсы», где мы оперируем агрегатными функциями: Сумма(Набор1.Продажи) - Сумма(Набор2.План)

Раздел 2. Ограничение на обращение к вычисляемым полям и способы его обхода

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


ВЫБОР
    КОГДА Продажи.Сумма > ВычисляемоеПоле1
        ТОГДА "План перевыполнен"
    ИНАЧЕ "План не выполнен"
КОНЕЦ

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

Проанализируем пять проверенных способов обойти это ограничение.

Способ 1. Перенос расчетов в текст запроса (Рекомендуемый метод)

Самый правильный с точки зрения производительности и чистоты архитектуры подход — отказаться от вычисляемых полей СКД и перенести всю логику на уровень СУБД. Для этого мы можем использовать возможности временных таблиц в запросе.

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


ВЫБРАТЬ
    Продажи.Контрагент КАК Контрагент,
    Продажи.Номенклатура КАК Номенклатура,
    ВЫБОР
        КОГДА Продажи.Сумма > 100000 ТОГДА Продажи.Сумма * 0.9
        ИНАЧЕ Продажи.Сумма
    КОНЕЦ КАК СуммаСоСкидкой
ПОМЕСТИТЬ ВТ_ПредварительныйРасчет
ИЗ
    РегистрНакопления.Продажи КАК Продажи
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Контрагент,
    ВТ.Номенклатура,
    ВТ.СуммаСоСкидкой,
    ВЫБОР
        КОГДА ВТ.СуммаСоСкидкой > 50000 ТОГДА "Крупная сделка"
        ИНАЧЕ "Стандартная сделка"
    КОНЕЦ КАК КатегорияСделки
ИЗ
    ВТ_ПредварительныйРасчет КАК ВТ

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

Способ 2. Дублирование кода выражений

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


// Выражение для ВычисляемоеПоле1:
ВЫБОР КОГДА Сумма > 0 ТОГДА Сумма / 4 ИНАЧЕ Сумма + 4 КОНЕЦ

// Выражение для ВычисляемоеПоле2 (где нам нужно было проверить равенство ВычисляемоеПоле1 числу 4):
ВЫБОР 
    КОГДА (ВЫБОР КОГДА Сумма > 0 ТОГДА Сумма / 4 ИНАЧЕ Сумма + 4 КОНЕЦ) = 4 
        ТОГДА "Минимум" 
    ИНАЧЕ "Обычное" 
КОНЕЦ

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

Способ 3. Расчет формул на вкладке «Ресурсы»

Ограничение на использование путей к данным вычисляемых полей не распространяется на ресурсы. Если вам не нужно выводить признак в детальных записях, а расчет требуется только для группировок (итогов), вы можете свободно писать формулы на вкладке ресурсов:

Сумма(ВычисляемоеПоле1) * Среднее(ВычисляемоеПоле2)

СКД сначала вычислит значения детальных записей для каждого вычисляемого поля, а затем корректно произведет расчет итоговой формулы ресурса.

Способ 4. Создание Пользовательских полей в настройках

В СКД есть разделение между вычисляемыми полями схемы и пользовательскими полями настроек. Если вы создадите Пользовательское поле-выражение на вкладке «Настройки» компоновщика данных, в его выражении вы сможете обращаться к любым вычисляемым полям схемы. Это легитимный механизм платформы, разработанный для конечных пользователей и разработчиков.

Способ 5. Использование общих модулей

Если формула получается громоздкой, вы можете вынести расчет на уровень кода 1С, создав экспортную функцию в общем модуле. Общий модуль должен иметь свойства «Сервер», «Вызов сервера» и «Клиент» (в зависимости от архитектуры выполнения отчета).

В вычисляемом поле СКД выражение примет лаконичный вид:

ИмяОбщегоМодуля.РассчитатьПоказатель(Сумма, Количество)

В коде модуля функция будет выглядеть стандартно:


Функция РассчитатьПоказатель(Сумма, Количество) Экспорт
    Если Количество = 0 Тогда
        Возврат 0;
    КонецЕсли;
    
    ПромежуточныйРезультат = Сумма / Количество;
    
    // Любая сложная зависимая логика
    Если ПромежуточныйРезультат > 100 Тогда
        Возврат ПромежуточныйРезультат * 1.2;
    Иначе
        Возврат ПромежуточныйРезультат;
    КонецЕсли;
КонецФункции

Обратите внимание: частые вызовы функций общих модулей для каждой строки детальных записей могут замедлить формирование отчета. Используйте этот метод обдуманно.

Раздел 3. Устранение проблем со структурой периодов и сортировкой колонок

Перейдем ко второму критическому вопросу: почему при связи двух наборов данных в структуре колонок отчета (например, по Кварталам и Месяцам) в третьем квартале внезапно появляется «Январь», а реальные месяцы квартала сортируются некорректно?

Проанализируем три основные причины и методы их исправления.

Причина 1. Появление значений NULL из-за левого соединения

Поскольку СКД связывает наборы данных по принципу LEFT JOIN, для записей третьего квартала (например, за июль) во втором наборе данных может просто не оказаться информации. В результате поле даты из второго набора вернет значение NULL.

Процессор компоновки данных при попытке вывести или сгруппировать значение NULL преобразует его в пустую дату — 01.01.0001 00:00:00. А пустая дата при форматировании в формат представления месяца выдает строку «Январь».

Решение: В запросах наборов данных всегда обрабатывайте потенциальные NULL с помощью функции ЕСТЬNULL. Задавайте базовые даты из основного (всегда заполненного) набора данных:

ЕСТЬNULL(Продажи.Период, ДАТАВРЕМЯ(1, 1, 1))

Причина 2. Нарушение иерархии в свойствах «Роли полей»

Если СКД выполняет автоматическое дополнение периодов, ей нужно явно указать логическую подчиненность дат. Без этого система не поймет, к какому кварталу относится конкретный месяц.

Разберем по шагам правильную настройку ролей полей на вкладке «Наборы данных»:

  1. Найдите поле, отвечающее за вывод месяца (например, Месяц).
  2. Кликните на колонку «Роль» для этого поля.
  3. В открывшемся окне установите тип периода (например, «Дополнительный период»).
  4. В поле «Родитель» обязательно выберите вышестоящий период — поле Квартал.
  5. Для поля Квартал родителем укажите поле Год.

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

Причина 3. Сортировка по строковому представлению вместо даты

Часто разработчики получают название месяца в виде текстовой строки (например, «Январь», «Февраль») непосредственно в запросе или через вычисляемое поле. Если сгруппировать колонки по такому строковому полю, СКД применит к ним стандартную алфавитную сортировку. В результате «Август» отобразится раньше «Июля», а «Январь» встанет первым независимо от квартала.

Решение:

  1. Всегда делайте группировку колонок по полноценному полю с типом Дата (например, приведенному к началу месяца с помощью функции НАЧАЛОПЕРИОДА(Период, МЕСЯЦ)).
  2. Чтобы пользователь видел понятное название месяца вместо даты, перейдите на вкладку «Наборы данных», выберите это поле и в колонке «Оформление» настройте параметр «Формат».
  3. Задайте форматную строку: ДФ='MMMM yyyy' (для вывода названия месяца и года) или ДФ='MMMM' (только название месяца).

При такой настройке СКД выполнит сортировку колонок хронологически (по значению даты), а пользователю покажет красивые и понятные строковые названия.

Заключение

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

← На главную