В практике разработчика 1С:Предприятия нередко возникают ситуации, когда стандартная Система компоновки данных (СКД) оказывается избыточной или технические требования проекта обязывают формировать отчет вручную. При выводе данных в классический ТабличныйДокумент с использованием предопределенного макета начинающие разработчики часто сталкиваются с вопросом: как правильно рассчитать и вывести итоговые показатели по числовым колонкам (например, по суммам заказов, оплаты и задолженности)?
В этой статье мы подробно разберем три основных способа решения этой задачи, проанализируем их преимущества и недостатки, а также изучим профессиональные приемы верстки табличных документов, такие как контроль разбиения на страницы и сохранение формул при экспорте отчета в Excel.
Это самый очевидный и простой в реализации алгоритм. Мы объявляем переменные-аккумуляторы перед началом цикла вывода строк, на каждой итерации прибавляем к ним показатели текущей строки, а после завершения цикла передаем накопленные значения в параметры области итогов макета.
Разберем этот метод по шагам на практическом примере:
// Инициализируем переменные для накопления итогов
ИтогоСуммаЗаказа = 0;
ИтогоСуммаОплаты = 0;
ИтогоСуммаЗадолженности = 0;
// Получаем области макета
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
ОбластьИтого = Макет.ПолучитьОбласть("Итого");
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Заполняем параметры текущей строки данными из выборки
ОбластьСтрока.Параметры.Заполнить(Выборка);
ТабДок.Вывести(ОбластьСтрока);
// Аккумулируем итоги вручную в цикле
ИтогоСуммаЗаказа = ИтогоСуммаЗаказа + Выборка.СуммаЗаказа;
ИтогоСуммаОплаты = ИтогоСуммаОплаты + Выборка.СуммаОплаты;
ИтогоСуммаЗадолженности = ИтогоСуммаЗадолженности + Выборка.СуммаЗадолженности;
КонецЦикла;
// Заполняем параметры области итогов и выводим её в документ
ОбластьИтого.Параметры.СуммаЗаказа = ИтогоСуммаЗаказа;
ОбластьИтого.Параметры.СуммаОплаты = ИтогоСуммаОплаты;
ОбластьИтого.Параметры.СуммаЗадолженности = ИтогоСуммаЗадолженности;
ТабДок.Вывести(ОбластьИтого);
Преимущества метода: простота понимания кода и логики работы, отсутствие необходимости модифицировать текст запроса.
Недостатки: при обработке больших массивов данных ручное суммирование в цикле на встроенном языке может незначительно снижать общую производительность отчета.
Для того чтобы не писать громоздкие математические операции внутри цикла, мы можем выгрузить результат выполнения запроса в объект типа ТаблицаЗначений. Этот системный объект обладает встроенным высокооптимизированным методом Итог(), который мгновенно подсчитывает сумму по любой числовой колонке на уровне платформы.
Рассмотрим, как будет выглядеть код при таком подходе:
// Выгружаем результат запроса в промежуточную Таблицу Значений
ТаблицаДанных = Запрос.Выполнить().Выгрузить();
// Получаем готовые суммы по колонкам в одну строку кода без циклов
ИтогоСуммаЗаказа = ТаблицаДанных.Итог("СуммаЗаказа");
ИтогоСуммаОплаты = ТаблицаДанных.Итог("СуммаОплаты");
ИтогоСуммаЗадолженности = ТаблицаДанных.Итог("СуммаЗадолженности");
// Выводим строки данных
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
ОбластьСтрока.Параметры.Заполнить(СтрокаДанных);
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
// Выводим заполненный подвал отчета с итогами
ОбластьИтого = Макет.ПолучитьОбласть("Итого");
ОбластьИтого.Параметры.СуммаЗаказа = ИтогоСуммаЗаказа;
ОбластьИтого.Параметры.СуммаОплаты = ИтогоСуммаОплаты;
ОбластьИтого.Параметры.СуммаЗадолженности = ИтогоСуммаЗадолженности;
ТабДок.Вывести(ОбластьИтого);
Преимущества метода: лаконичный и чистый код, высокая скорость вычислений благодаря оптимизированным алгоритмам платформы 1С для работы с коллекциями в памяти.
Недостатки: метод требует выгрузки всего результата запроса в оперативную память сервера, что может быть нежелательно при работе с экстремально большими выборками данных.
Этот подход считается наиболее профессиональным с точки зрения архитектуры баз данных. Расчет итоговых показателей перекладывается на СУБД (например, MS SQL Server или PostgreSQL), которая возвращает в платформу уже агрегированные данные. Для этого в конце текста запроса используется конструкция ИТОГИ ... ПО ОБЩИЕ.
Проанализируем текст запроса и особенности обхода такой выборки. В тексте запроса мы указываем:
ВЫБРАТЬ
ЗаказКлиента.Ссылка КАК Заказ,
ЗаказКлиента.СуммаДокумента КАК СуммаЗаказа,
ЗаказКлиента.СуммаОплаты КАК СуммаОплаты,
ЗаказКлиента.СуммаДокумента - ЗаказКлиента.СуммаОплаты КАК СуммаЗадолженности
ИЗ
Документ.ЗаказКлиента КАК ЗаказКлиента
ГДЕ
ЗаказКлиента.Проведен = ИСТИНА
ИТОГИ
СУММА(СуммаЗаказа),
СУММА(СуммаОплаты),
СУММА(СуммаЗадолженности)
ПО
ОБЩИЕ
Поскольку структура результата запроса теперь содержит иерархический итог, обход выборки усложняется и должен выполняться строго по группировкам. Разберем по шагам программный код вывода:
РезультатЗапроса = Запрос.Выполнить();
// 1. Получаем выборку верхнего уровня (Общие итоги)
ВыборкаОбщиеИтоги = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Если ВыборкаОбщиеИтоги.Следующий() Тогда
// Переменные итогов считываются напрямую из первой и единственной строки верхнего уровня
ИтогоСуммаЗаказа = ВыборкаОбщиеИтоги.СуммаЗаказа;
ИтогоСуммаОплаты = ВыборкаОбщиеИтоги.СуммаОплаты;
ИтогоСуммаЗадолженности = ВыборкаОбщиеИтоги.СуммаЗадолженности;
// 2. Получаем детальные записи, подчиненные общим итогам
ВыборкаДетали = ВыборкаОбщиеИтоги.Выбрать();
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
Пока ВыборкаДетали.Следующий() Цикл
ОбластьСтрока.Параметры.Заполнить(ВыборкаДетали);
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
КонецЕсли;
// 3. Выводим область итогов
ОбластьИтого = Макет.ПолучитьОбласть("Итого");
ОбластьИтого.Параметры.СуммаЗаказа = ИтогоСуммаЗаказа;
ОбластьИтого.Параметры.СуммаОплаты = ИтогоСуммаОплаты;
ОбластьИтого.Параметры.СуммаЗадолженности = ИтогоСуммаЗадолженности;
ТабДок.Вывести(ОбластьИтого);
Преимущества метода: максимальная производительность СУБД при расчетах, идеальный вариант для работы с транзакционными таблицами больших объемов.
Недостатки: более сложная структура кода обхода выборки, требующая понимания принципов иерархического представления данных в 1С.
При ручном выводе табличного документа разработчики часто сталкиваются с эстетической проблемой: последняя строка детальных записей выводится на одной странице, а строка "Итого" переносится на следующую пустую страницу в одиночестве. Это выглядит непрофессионально.
Чтобы гарантировать красивый вывод отчета, мы должны использовать метод ПроверитьВывод(). Этот метод проверяет, помещается ли массив областей табличного документа на текущую страницу при печати. Если они не помещаются вместе, мы принудительно переносим последнюю строку данных вместе с итоговой строкой на новый лист.
Проанализируем пример реализации такой проверки:
ОбластьШапка = Макет.ПолучитьОбласть("ШапкаТаблицы");
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
ОбластьИтого = Макет.ПолучитьОбласть("Итого");
КоличествоСтрок = ТаблицаДанных.Количество();
Для Индекс = 0 По КоличествоСтрок - 1 Цикл
СтрокаДанных = ТаблицаДанных[Индекс];
ОбластьСтрока.Параметры.Заполнить(СтрокаДанных);
// Создаем массив проверки вывода
МассивПроверки = Новый Массив;
МассивПроверки.Добавить(ОбластьСтрока);
// Если это последняя строка, проверяем её вместе со строкой Итого
Если Индекс = КоличествоСтрок - 1 Тогда
МассивПроверки.Добавить(ОбластьИтого);
КонецЕсли;
// Проверяем, поместятся ли области на текущую страницу
Если Не ТабДок.ПроверитьВывод(МассивПроверки) Тогда
// Если не помещаются, осуществляем перенос и при необходимости дублируем шапку
ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
ТабДок.Вывести(ОбластьШапка);
КонецЕсли;
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
// Выводим итоги — теперь они гарантированно не оторвутся от таблицы
ТабДок.Вывести(ОбластьИтого);
Если пользователи вашего отчета планируют сохранять его в файл формата .xls или .xlsx для последующей аналитики, жестко прописанные числовые значения в строке "Итого" лишат документ гибкости. При изменении пользователем чисел в таблице Excel итоговые суммы не будут пересчитываться автоматически.
Для решения этой проблемы в 1С предусмотрена возможность программного назначения формул ячейкам табличного документа. Вы можете использовать формат R1C1 для автоматической трансляции формул при экспорте. Для этого ячейке в макете задается тип заполнения Параметр, а в коде заполняется свойство Формула:
// Предположим, строки таблицы начинаются со 2-й строки и заканчиваются на N-й строке
// Формула для Excel будет выглядеть как СУММ по вертикальному диапазону ячеек
СтрокаНачала = 2;
СтрокаКонца = ТаблицаДанных.Количество() + 1; // Координата последней детальной строки
ОбластьИтого = Макет.ПолучитьОбласть("Итого");
// Указываем формулу в формате R1C1
ОбластьИтого.Область("R1C2").Формула = "=SUM(R" + Формат(СтрокаНачала, "ЧГ=") + "C:R" + Формат(СтрокаКонца, "ЧГ=") + "C)";
Используя данный подход, при открытии сохраненного документа в Microsoft Excel или любом другом табличном процессоре пользователи получат полностью интерактивную и живую таблицу со стандартными формулами суммирования.