При разработке отчетов на базе Системы компоновки данных (СКД) часто возникает задача постобработки полученного результата. Одной из таких задач является программная установка защиты на определенные колонки или ячейки табличного документа. Казалось бы, достаточно внести изменения в ДокументРезультат в конце процедуры ПриКомпоновкеРезультата, однако на практике разработчики сталкиваются с тем, что настройки «слетают» при изменении отборов пользователем или вовсе не применяются. В этой статье мы подробно разберем, как правильно реализовать этот механизм, проанализируем причины типовых ошибок и рассмотрим несколько способов решения задачи.
Когда мы работаем с СКД, платформа берет на себя большую часть работы по выводу данных. Если мы просто добавим код в процедуру ПриКомпоновкеРезультата, не отключая стандартную обработку, наши изменения в табличном документе могут быть перезаписаны или проигнорированы платформой в процессе финальной отрисовки формы. Рассмотрим ситуацию: нам нужно защитить колонку «Аналог» от редактирования, сохранив возможность изменять другие данные в отчете.
Основная сложность заключается в том, что при изменении настроек (например, отборов) отчет формируется заново. Если защита устанавливается по жестким координатам (номерам строк и колонок), то любое смещение структуры отчета приведет к тому, что защита будет установлена не на те ячейки. Выясним, как сделать решение универсальным.
Для того чтобы иметь полный контроль над ДокументомРезультатом, необходимо установить параметр СтандартнаяОбработка = Ложь. Это позволит нам самостоятельно управлять процессом компоновки и вывода. Разберем по шагам, как это реализовать в модуле объекта отчета.
Рассмотрим пример кода, который выполняет программную компоновку и последующую установку защиты:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
// Отключаем стандартный вывод платформы
СтандартнаяОбработка = Ложь;
// Получаем схему и настройки
СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
// Инициализируем данные расшифровки
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
// Создаем макет компоновки
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
// Инициализируем процессор компоновки (если есть внешние наборы данных — передаем их сюда)
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
// Очищаем документ и выводим результат
ДокументРезультат.Очистить();
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
// ТЕПЕРЬ ПЕРЕХОДИМ К ПОСТОБРАБОТКЕ
// Включаем общую защиту табличного документа (БЕЗ ЭТОГО ЗАЩИТА ЯЧЕЕК НЕ РАБОТАЕТ)
ДокументРезультат.Защита = Истина;
// Сначала снимем защиту со всех ячеек, чтобы потом включить ее только для нужной колонки
ВсеЯчейки = ДокументРезультат.Область();
ВсеЯчейки.Защита = Ложь;
// Ищем колонку по заголовку "Аналог"
ОбластьЗаголовка = ДокументРезультат.НайтиТекст("Аналог");
Если ТипЗнч(ОбластьЗаголовка) <> Тип("Неопределено") Тогда
НомерКолонки = ОбластьЗаголовка.Лево;
// Устанавливаем защиту на всю колонку по найденному индексу
ОбластьКолонки = ДокументРезультат.Область(, НомерКолонки, , НомерКолонки);
ОбластьКолонки.Защита = Истина;
КонецЕсли;
КонецПроцедуры
Проанализируем критически важные моменты, которые часто упускаются из виду:
Защита у отдельной области (ячейки или колонки) работает только тогда, когда свойство Защита всего ТабличногоДокумента установлено в значение Истина. По умолчанию при выводе СКД оно может быть сброшено.НайтиТекст("Заголовок") — простой, но не всегда надежный способ. Если в отчете есть группировки, в которых встречается это же слово, или если слово "Аналог" содержится в данных строк, поиск может вернуть не ту область. Более надежный способ — анализ макета или использование расшифровки.ПриКомпоновкеРезультата состояние элементов формы не обновляется мгновенно. Если защита не применилась, можно использовать обработчик ОбновлениеОтображения в модуле формы, однако злоупотреблять им не стоит из-за нагрузки на клиентскую часть.Если отчет очень большой (десятки тысяч строк), перебор ячеек после вывода может занять значительное время. В этом случае мы рекомендуем использовать поэлементный вывод. Рассмотрим этот подход: он позволяет устанавливать свойства ячеек прямо в момент их создания.
Проанализируем фрагмент кода для поэлементного вывода:
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.НачатьВывод();
Пока Истина Цикл
ЭлементРезультата = ПроцессорКомпоновкиДанных.Следующий();
Если ЭлементРезультата = Неопределено Тогда
Прервать;
КонецЕсли;
// Выводим элемент в документ
ПроцессорВывода.ВывестиЭлемент(ЭлементРезультата);
// Здесь можно анализировать тип элемента и устанавливать свойства
// Например, если это детальные записи, можно обратиться к текущей области
КонецЦикла;
ПроцессорВывода.ЗакончитьВывод();
Хотя этот метод сложнее в реализации (нужно точно определять, какая ячейка вывелась в данный момент), он является самым производительным и гибким для сложных отчетов.
Выясним причину, описанную автором вопроса: при включенном отборе всё работает, а при снятии — нет. Это происходит потому, что при изменении состава данных СКД перестраивает структуру ДокументРезультат. Если ваш код опирается на фиксированные координаты (например, ДокументРезультат.Область(4, 10)), то после снятия отбора нужная колонка может переместиться на другую позицию.
Решение: Всегда динамически определяйте номер колонки в каждой итерации формирования отчета. Никогда не закладывайтесь на то, что колонка «Для заказа» всегда будет под номером 12. Используйте метод НайтиТекст или перебор колонок в шапке (первые несколько строк документа) для поиска актуального индекса.
Если ваша цель — не просто запретить редактирование, а визуально выделить защищенную область, не забудьте добавить установку цвета фона. Это поможет пользователю сразу понять, какие данные являются расчетными (и защищенными), а какие — доступными для ввода.
// Пример визуального оформления
ОбластьКолонки.ЦветФона = WebЦвета.СветлоСерый;
ОбластьКолонки.ТолькоПросмотр = Истина; // Дополнительная мера безопасности
Подведем итог: для корректной работы защиты в отчетах СКД необходимо использовать программный вывод (СтандартнаяОбработка = Ложь), обязательно включать защиту всего табличного документа и динамически определять области для модификации. Это обеспечит стабильную работу отчета при любых настройках пользователя.