Как реализовать расчет долей в процентах для строк и групп в Табличном документе 1С

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

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

Метод 1: Прямой расчет при обходе коллекции данных

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

Алгоритм действий следующий:

  1. Сначала необходимо вычислить итоговую сумму по всей колонке, которая будет принята за 100%.
  2. Затем мы проходим циклом по каждой строке и делим значение текущей строки на общий итог, умножая результат на 100.
  3. Обязательно добавляем проверку на равенство общего итога нулю, чтобы избежать критической ошибки «Деление на 0».

Посмотрим на пример кода, реализующий этот подход:


// Предположим, у нас есть ТаблицаЗначений "тзДанные" с колонками "Сумма" и "Долька"
ОбщийИтог = тзДанные.Итог("Сумма");

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

Этот метод хорошо работает для небольших объемов данных, однако он не учитывает иерархию (группировки) и проблемы округления.

Метод 2: Использование итогов в запросе

Более профессиональный и быстрый способ — переложить расчет итогов на уровень СУБД. Использование конструкции ИТОГИ в языке запросов 1С позволяет получить иерархическую структуру данных, где на каждом уровне группировки уже посчитаны суммы. Это значительно упрощает расчет процентов для групп.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Продажи.Номенклатура.Родитель КАК Группа,
    |   Продажи.Номенклатура КАК Номенклатура,
    |   СУММА(Продажи.СуммаОборот) КАК Сумма
    |ИЗ
    |   РегистрНакопления.Продажи.Обороты КАК Продажи
    |СГРУППИРОВАТЬ ПО
    |   Продажи.Номенклатура.Родитель,
    |   Продажи.Номенклатура
    |ИТОГИ
    |   СУММА(Сумма)
    |ПО
    |   ОБЩИЕ,
    |   Группа";

РезультатЗапроса = Запрос.Выполнить();

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

Оптимизация через пакетные запросы

Если данных очень много, выгрузка их в ТаблицуЗначений для постобработки может замедлить работу системы. В таких случаях мы используем временные таблицы. Сначала вычисляем общую сумму во временную таблицу, а затем в основном запросе соединяем таблицу данных с этой временной таблицей (кросс-соединение). Это позволяет получить общий итог в каждой строке результата запроса и рассчитать процент прямо в поле выбора ВЫБРАТЬ Сумма / ИтоговаяТаблица.ОбщаяСумма * 100.

Решение проблемы «лишней копейки» при округлении

Одной из самых частых проблем является ситуация, когда сумма всех рассчитанных процентов не равна 100% из-за округления каждой строки (например, получается 99.99%). В профессиональной разработке 1С мы применяем метод распределения остатка.

Рассмотрим пошагово алгоритм распределения:

  1. Рассчитываем и округляем доли для всех строк, кроме последней.
  2. Накапливаем сумму этих округленных долей в отдельной переменной.
  3. Для последней строки значение рассчитываем как 100 - НакопленнаяСумма.

Пример программной реализации контроля округления:


ВсегоСтрок = тзДанные.Количество();
СуммаПроцентовБезПоследней = 0;

Для Индекс = 0 По ВсегоСтрок - 2 Цикл
    ТекущаяСтрока = тзДанные[Индекс];
    ТекущаяСтрока.Процент = Окр((ТекущаяСтрока.Сумма / ОбщийИтог) * 100, 2);
    СуммаПроцентовБезПоследней = СуммаПроцентовБезПоследней + ТекущаяСтрока.Процент;
КонецЦикла;

// Досчитываем последнюю строку, чтобы в сумме всегда было ровно 100.00
Если ВсегоСтрок > 0 Тогда
    тзДанные[ВсегоСтрок - 1].Процент = 100 - СуммаПроцентовБезПоследней;
КонецЕсли;

Оформление в Табличном документе

Для того чтобы пользователь видел красивые данные, важно правильно настроить вывод в ТабличныйДокумент. Мы настоятельно рекомендуем использовать свойство ячейки Формат. Это позволяет хранить в ячейке точное число, но отображать его с символом процента и нужным количеством знаков после запятой.

Посмотрим, как задать формат программно:

Если мы хотим отобразить число 12.34 как "12,34%", используем форматную строку: ЧФ=0,00'%'. Если же данные в базе хранятся в долях единицы (например, 0.1234), то в формате можно указать коэффициент масштабирования ЧС=0 и формат ЧЦ=5; ЧДЦ=2.

Также полезно использовать условное оформление. Если процент в строке превышает определенный порог (например, 50% — «крупный клиент»), мы можем выделить ячейку жирным шрифтом или цветом, обращаясь к области табличного документа:


Область = Макет.ПолучитьОбласть("Строка");
Если ТекущийПроцент > 50 Тогда
    Область.Область("Процент").ЦветТекста = ЦветаСтиля.ОсобыйТекст;
КонецЕсли;
ТабДок.Вывести(Область);

Производительность и клиент-серверное взаимодействие

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

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

← На главную