При разработке отчетов в системе компоновки данных (СКД) мы часто сталкиваемся с задачей расчета итогов, которые невозможно описать простой арифметической формулой. Классический пример — суммирование стажа. Если мы просто сложим дни, месяцы и годы, то получим некорректный результат (например, «15 месяцев» или «45 дней»), который необходимо нормализовать (перевести дни в месяцы, а месяцы — в годы).
В этой статье мы подробно разберем, как обойти ограничения поля «Выражение» в СКД (используя сложные выражения), правильно написать функцию для расчета итогов и настроить порядок вывода полей.
Вкладка Ресурсы в схеме компоновки данных позволяет задавать формулы для расчета итогов по группировкам. Однако поле Выражение имеет жесткие синтаксические ограничения:
Если ... Тогда ... КонецЕсли) и циклы.Автор вопроса попытался вставить сложный алгоритм нормализации стажа (с расчетом целых частей и остатков) прямо в поле ресурса, что технически невозможно. Рассмотрим правильный архитектурный подход к решению этой задачи.
Единственный способ выполнить сложный многострочный код в выражении СКД — это вынести логику в Общий модуль конфигурации. Чтобы СКД могла «увидеть» и выполнить нашу функцию, модуль должен соответствовать следующим требованиям:
Экспорт (правильность их вызова можно проверить через инструменты анализа конфигурации).Давайте напишем правильный алгоритм сложения стажа. Обычно в системах 1С (например, ЗУП) для таких расчетов принимается условность, что в месяце 30 дней. Это позволяет избежать путаницы с календарными днями (28, 29, 31) в общих итогах.
Пример кода для общего модуля (назовем его ОбщегоНазначенияКлиентСервер, создадим свой РасчетСтажаСервер или используем универсальные функции получения результата):
Функция СложитьСтаж(СуммаЛет, СуммаМесяцев, СуммаДней) Экспорт
// 1. Нормализуем дни (каждые 30 дней превращаем в 1 месяц)
ДопМесяцыИзДней = Цел(СуммаДней / 30);
ОстатокДней = СуммаДней % 30; // Остаток от деления
// 2. Добавляем полученные месяцы к общей сумме месяцев
ВсегоМесяцев = СуммаМесяцев + ДопМесяцыИзДней;
// 3. Нормализуем месяцы (каждые 12 месяцев превращаем в 1 год)
ДопГодыИзМесяцев = Цел(ВсегоМесяцев / 12);
ОстатокМесяцев = ВсегоМесяцев % 12;
// 4. Считаем итоговые годы
ИтогоЛет = СуммаЛет + ДопГодыИзМесяцев;
// 5. Формируем строку представления
// Здесь можно добавить склонение (год/года/лет) при необходимости
Результат = Строка(ИтогоЛет) + " г. " + Строка(ОстатокМесяцев) + " мес. " + Строка(ОстатокДней) + " дн.";
Возврат Результат;
КонецФункции
Это самый важный момент, в котором часто допускают ошибку. Рассмотрим два варианта записи выражения в СКД:
Вариант 1 (Ошибочный):
Сумма(РасчетСтажаСервер.СложитьСтаж(Лет, Месяцев, Дней))
Почему это не сработает? В этом случае система сначала для каждой строки детальных записей выполнит функцию (получит строку вида "1 г. 5 мес."), а потом попытается применить агрегатную функцию Сумма() к этим строкам. Складывать строки нельзя, возникнет ошибка или некорректный результат.
Вариант 2 (Правильный):
РасчетСтажаСервер.СложитьСтаж(Сумма(Лет), Сумма(Месяцев), Сумма(Дней))
Разберем, что здесь происходит:
Таким образом, в поле Выражение на вкладке «Ресурсы» необходимо прописать вызов функции, обернув поля набора данных в агрегатную функцию Сумма().
Если вы заметили, что расчет выполняется неточно (как упоминалось в обсуждении), проверьте два момента:
%) для получения дней и месяцев, которые не перешли в старший разряд, а функцию Цел() — для переноса в старший разряд.РАЗНОСТЬДАТ(Дата1, Дата2, "Месяц") в языке запросов может давать не совсем тот результат, который ожидается для стажа (она считает переходы границ месяца, а не полные месяцы). Для точного расчета стажа в запросе рекомендуется использовать специализированные механизмы или рассчитывать стаж в днях, а разбивку делать уже при выводе.В обсуждении также был поднят вопрос: «Почему в отчете даты лезут вперед?» (перед группировками или текстовыми полями).
Такое поведение связано с настройками макета СКД по умолчанию:
Подводя итог: для сложных расчетов в итогах СКД всегда используйте связку «Экспортная функция в общем модуле» + «Вызов функции с агрегатами внутри» (Функция(Сумма(Поле))). Это обеспечит чистоту кода и корректность вычислений.