При работе с аналитическими отчетами на базе Системы компоновки данных (СКД) часто возникает бизнес-требование: зафиксировать («заморозить») показатели отчета на определенный момент времени. Это критически важно для снабженцев, плановиков и сотрудников отдела материально-технического обеспечения (МТО). Зафиксированный отчет служит подтверждением того, на основании каких цифр (потребностей, остатков, заказов) принимались решения о закупках или производстве в конкретный день.
Стандартное сохранение отчета в формате табличного документа (.mxl) решает задачу фиксации цифр, но полностью ломает интерактивность: двойной клик по ячейке больше не открывает карточку номенклатуры, не показывает документы-регистраторы и не позволяет расшифровать показатель. С другой стороны, повторное формирование отчета за прошлую дату может выдать совершенно другие цифры из-за изменения исторических данных в базе (например, при проведении документов задним числом).
Давайте подробно разберем, как решить эту проблему в 1С, обойти технические ограничения платформы и предоставить пользователям удобный инструмент работы с архивными отчетами.
Для начала проанализируем, как работает механизм расшифровки в СКД. При выводе отчета платформа генерирует специальный служебный объект — ДанныеРасшифровкиКомпоновкиДанных. В самом табличном документе (в его ячейках) сохраняются только числовые идентификаторы расшифровки. Когда пользователь делает двойной клик по ячейке, платформа берет этот идентификатор, находит соответствующую ему запись в объекте данных расшифровки и на основе этого открывает форму объекта или выполняет перестроение отчета.
Если мы просто сохраняем табличный документ в справочнике или файле, объект данных расшифровки теряется. Идентификаторы в ячейках становятся «пустышками», ссылающимися в никуда. Кроме того, важно помнить о важном техническом ограничении: объект ПроцессорКомпоновкиДанных не поддерживает сериализацию. Попытка напрямую записать его в базу данных или поместить в ХранилищеЗначения вызовет критическую ошибку платформы.
Давайте рассмотрим три практических способа решения этой задачи: от простого сохранения расшифровок до полноценной заморозки данных с помощью внешних наборов.
Если пользователям достаточно базовой интерактивности (например, чтобы при двойном клике на номенклатуру открывалась ее карточка, а при клике на заказ — сам документ), мы можем сохранять данные расшифровки вместе с табличным документом.
Рассмотрим алгоритм реализации этого механизма шаг за шагом:
ДанныеРасшифровки.ХранилищеЗначения.Проанализируем пример кода для сохранения сформированного отчета:
// Код выполняется на сервере при сохранении отчета
ДанныеДляСохранения = Новый Структура;
ДанныеДляСохранения.Вставить("ТабличныйДокумент", ЭтотОбъект.Результат);
ДанныеДляСохранения.Вставить("ДанныеРасшифровки", ПолучитьИзВременногоХранилища(ЭтотОбъект.ДанныеРасшифровки));
НовыйАрхив = Справочники.АрхивныеОтчеты.СоздатьЭлемент();
НовыйАрхив.Наименование = "Расчет потребности от " + Формат(ТекущаяДата(), "ДФ=dd.MM.yyyy");
НовыйАрхив.ДанныеОтчета = Новый ХранилищеЗначения(ДанныеДляСохранения, Новый СжатиеДанных(9));
НовыйАрхив.Записать();
Теперь разберем, как восстановить этот отчет на форме просмотра архивных данных:
// Код выполняется на сервере при открытии архивного элемента
СтруктураДанных = СсылкаНаАрхив.ДанныеОтчета.Получить();
Если ТипЗнч(СтруктураДанных) = Тип("Структура") Тогда
// Восстанавливаем табличный документ на форме
ЭтотОбъект.Результат.Вывести(СтруктураДанных.ТабличныйДокумент);
// Помещаем сохраненные данные расшифровки во временное хранилище формы
АдресРасшифровки = ПоместитьВоВременноеХранилище(СтруктураДанных.ДанныеРасшифровки, ЭтотОбъект.УникальныйИдентификатор);
ЭтотОбъект.ДанныеРасшифровки = АдресРасшифровки;
КонецЕсли;
Важное предупреждение: Данный метод имеет существенный недостаток. Если пользователь попытается выполнить сложную расшифровку (например, через правую кнопку мыши выберет пункт «Расшифровать по...» и укажет другое поле), СКД будет вынуждена выполнить новый запрос к базе данных. В этот момент отчет построится по текущему состоянию базы данных, и зафиксированные «вчерашние» цифры изменятся на актуальные («поплывут»).
Чтобы гарантировать абсолютную неизменяемость данных при любых манипуляциях пользователя (группировка, фильтрация, детальная расшифровка по любым полям), необходимо перевести отчет на использование Набора данных - Объект (внешний источник данных в виде ТаблицыЗначений).
Суть метода заключается в следующем: при первичном расчете мы собираем все необходимые детальные данные запросом в плоскую таблицу. Эту таблицу мы архивируем. При просмотре архива СКД использует эту сохраненную таблицу в качестве источника. Поскольку все данные уже находятся в памяти, любые действия по перегруппировке или расшифровке СКД выполняет внутри этой локальной таблицы, не обращаясь к реальным таблицам базы данных.
Рассмотрим пример настройки и формирования такого отчета:
ТаблицаДанных.Проанализируем программный код вывода отчета через внешний набор данных:
// Пример формирования отчета по сохраненной таблице значений
Процедура ВывестиОтчетПоВнешнемуИсточнику(ТаблицаИсточник)
СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
// Передаем нашу зафиксированную таблицу значений во внешний набор данных
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ТаблицаДанных", ТаблицаИсточник);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);
РезультатТабличныйДокумент = Новый ТабличныйДокумент;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(РезультатТабличныйДокумент);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
// Выводим результат на форму
Результат.Вывести(РезультатТабличныйДокумент);
ЭтотОбъект.ДанныеРасшифровки = ПоместитьВоВременноеХранилище(ДанныеРасшифровки, УникальныйИдентификатор);
КонецПроцедуры
При таком подходе снабженцы могут крутить отчет как угодно: накладывать новые отборы, менять группировки местами, выводить детальные записи. Информация гарантированно не изменится, так как источником служит статичная, сохраненная в справочнике таблица значений.
Давайте посмотрим на ситуацию с точки зрения архитектуры крупных ERP-систем. Если отчет по расчету потребности имеет юридическую силу, на его основе подписываются спецификации и закупаются материалы на миллионы рублей, использование отчетов для хранения этих состояний считается антипаттерном.
Вместо этого рекомендуется использовать объект конфигурации Документ (например, «Расчет потребности в запасах»):
Этот подход сложнее в реализации, но он обеспечивает максимальную надежность хранения данных, учет истории изменений и интеграцию с другими бизнес-процессами компании.
Мы проанализировали проблему сохранения отчетов СКД с фиксацией данных и рассмотрели три варианта решения задачи. Для простых отчетов с минимальными требованиями к интерактивности подойдет Решение 1 с сохранением связки табличного документа и структуры расшифровки. Если пользователям необходим полноценный интерактивный анализ зафиксированных данных без риска их искажения, лучшим выбором будет Решение 2 с использованием СКД на базе внешних наборов данных. Для ответственных бизнес-процессов оптимальным решением является переход на полноценный объект конфигурации Документ.