При разработке интерактивных отчетов и рабочих мест (например, для оперативного планирования закупок или продаж) часто возникает задача предоставить пользователю возможность корректировать числовые показатели прямо в ячейках отчета. При этом система должна моментально пересчитывать зависимые суммы по строке и агрегированные итоги вышестоящих группировок без долгого ожидания и перезапроса данных к базе.
Объект ТабличныйДокумент в платформе 1С изначально предназначен для отображения и печати плоских или иерархических данных, а не для работы в режиме табличного процессора с автоматическими реактивными формулами. Разберем технические и архитектурные способы реализации этой задачи.
Если вывод данных принципиально должен оставаться в элементе формы типа ТабличныйДокумент, то вместо полного суммирования всех строк отчета снизу вверх при каждом изменении ячейки применим алгоритм вычисления разницы (дельты). Сложность операции в этом случае снижается с полного обхода всей таблицы до обновления нескольких ячеек по цепочке родителей.
Разберем логику работы по шагам:
Истина и указываем тип значения (например, Число).ПриАктивизацииОбласти сохраняем текущее (старое) значение ячейки в реквизит формы.ПриИзмененииСодержимогоОбласти получаем новое введенное значение и находим разницу: Дельта = НовоеЗначение - СтароеЗначение.Сумма = Количество * Цена) и дельту для суммы.Чтобы не выполнять тяжелый поиск строк-родителей в момент редактирования, координаты родительских ячеек связываются заранее при компоновке отчета. Для этого в свойство ячейки Расшифровка (или в структуру параметров расшифровки) помещается массив номеров строк родительских группировок.
Посмотрим на пример клиентского кода обработчиков:
&НаКлиенте
Перем ПредыдущееЗначениеЯчейки;
&НаКлиенте
Процедура ТабличныйДокументПриАктивизацииОбласти(Элемент)
ТекущаяОбласть = Элемент.ТекущаяОбласть;
Если ТекущаяОбласть.СодержитЗначение Тогда
ПредыдущееЗначениеЯчейки = ТекущаяОбласть.Значение;
Иначе
ПредыдущееЗначениеЯчейки = 0;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ТабличныйДокументПриИзмененииСодержимогоОбласти(Элемент, Область)
Если Не Область.СодержитЗначение Тогда
Возврат;
КонецЕсли;
НовоеЗначение = Область.Значение;
Дельта = НовоеЗначение - ПредыдущееЗначениеЯчейки;
Если Дельта = 0 Тогда
Возврат;
КонецЕсли;
// Если в расшифровке ячейки хранится структура с массивом строк-родителей
Если ТипЗнч(Область.Расшифровка) = Тип("Структура") И Область.Расшифровка.Свойство("СтрокиРодителей") Тогда
НомерКолонки = Область.Лево;
Для Каждого НомерСтрокиРодителя Из Область.Расшифровка.СтрокиРодителей Цикл
ОбластьРодителя = Элемент.Область(НомерСтрокиРодителя, НомерКолонки);
ОбластьРодителя.Значение = ОбластьРодителя.Значение + Дельта;
ОбластьРодителя.Текст = Формат(ОбластьРодителя.Значение, "ЧДЦ=2");
КонецЦикла;
КонецЕсли;
ПредыдущееЗначениеЯчейки = НовоеЗначение;
КонецПроцедуры
Такой подход выполняется исключительно на клиенте через обращения к объекту ОбластьЯчеекТабличногоДокумента, не вызывая серверных контекстных вызовов, что обеспечивает мгновенный отклик интерфейса.
Если в отчете используется сложная математика расчета показателей или иерархия имеет множество пересекающихся итогов, пересчет вручную по ячейкам может стать источником ошибок. В этом случае применяется разделение слоя данных и слоя отображения:
ПроцессорКомпоновкиДанных, где в качестве источника выступает внешний набор данных (объект ТаблицаЗначений), а не тяжелый запрос к базе данных.Для компактных отчетов (от сотен до нескольких тысяч строк) компоновка по готовой таблице в оперативной памяти занимает доли секунды. При этом текущая активизированная область табличного документа сохраняет свой фокус и позицию курсора, обеспечивая комфортную работу без мерцания интерфейса.
При проектировании автоматизированных рабочих мест вместо отчета на СКД часто целесообразнее использовать обработку с элементом формы Таблица в режиме отображения Дерево.
Проанализируем преимущества такого подхода:
ДанныеФормыДерево изначально поддерживает строгую иерархическую структуру.Строка.Родитель, что позволяет организовывать пересчет итогов циклом подъема к корневому элементу без работы с номерами ячеек и расшифровками:
&НаКлиенте
Процедура ДеревоДанныхКоличествоПриИзменении(Элемент)
СтрокаДерева = Элементы.ДеревоДанных.ТекущиеДанные;
СтрокаДерева.Сумма = СтрокаДерева.Количество * СтрокаДерева.Цена;
// Рекурсивный или циклический пересчет итогов вверх по дереву
Родитель = СтрокаДерева.ПолучитьРодителя();
Пока Родитель <> Неопределено Цикл
СуммаПоРодителю = 0;
Для Каждого ПодчиненнаяСтрока Из Родитель.ПолучитьЭлементы() Цикл
СуммаПоРодителю = СуммаПоРодителю + ПодчиненнаяСтрока.Сумма;
КонецЦикла;
Родитель.Сумма = СуммаПоРодителю;
Родитель = Родитель.ПолучитьРодителя();
КонецЦикла;
КонецПроцедуры
Такая архитектура делает код прозрачным, упрощает валидацию вводимых значений и исключает сложности с версткой табличного документа.