В процессе работы с информационными системами часто возникает необходимость показывать пользователям отчеты или документы, содержащие как общедоступную, так и конфиденциальную информацию. При этом требуется, чтобы на экране конфиденциальные данные были скрыты, но при печати отображались в полном объеме. Это позволяет соблюдать политику безопасности и разграничения доступа, когда, например, пользователь может видеть общую структуру документа, но не должен иметь доступ к конкретным персональным данным или суммам, которые предназначены только для печати на защищенном принтере или для лиц, обладающих соответствующими правами. Разберем, как можно реализовать такую функциональность в 1С:Предприятии 8.
Задача кажется нетривиальной, поскольку табличный документ — это единый объект, который используется как для отображения на экране, так и для вывода на печать. Однако, используя программные возможности платформы 1С, мы можем управлять содержимым и свойствами ячеек в зависимости от контекста их использования. Мы рассмотрим несколько подходов, которые позволят нам добиться желаемого эффекта, от самых простых до более надежных.
Один из наиболее прямолинейных и надежных способов решения задачи, который был выбран автором темы, — это формирование двух разных объектов ТабличныйДокумент. Один документ будет предназначен для отображения на экране, а другой — для печати. Этот подход обеспечивает четкое разделение данных и позволяет максимально гибко управлять содержимым.
Для отображения на экране: Мы формируем первый табличный документ. При его заполнении мы сознательно пропускаем или заменяем конфиденциальные данные на заглушки (например, пустые строки, звездочки или текст "Секретно"). Этот документ показываем пользователю на форме.
Для вывода на печать: Когда пользователь нажимает кнопку "Печать", мы формируем второй табличный документ. В этот документ мы уже включаем все конфиденциальные данные. После его формирования мы сразу же отправляем его на печать, минуя этап предварительного просмотра на экране.
Для реализации этого подхода нам потребуется настроить логику формирования документа таким образом, чтобы она зависела от целевого назначения. Обычно это делается в обработчике команды печати, которая может быть вынесена на форму отчета или документа.
Предположим, у нас есть процедура, которая формирует табличный документ.
Процедура СформироватьТабличныйДокумент(ТабДок, РежимВывода)
// РежимВывода: "Экран" или "Печать"
Макет = ПолучитьМакет("МакетОтчета");
ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
ОбластьДанных = Макет.ПолучитьОбласть("Данные");
ОбластьСекретныхДанных = Макет.ПолучитьОбласть("СекретныеДанные"); // Предполагаем, что есть такая область
ТабДок.Очистить();
ТабДок.Вывести(ОбластьЗаголовок);
// Пример получения данных
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Документ.Поле1 КАК Поле1,
| Документ.ПолеСекретное КАК ПолеСекретное
|ИЗ
| Документ.МойДокумент КАК Документ";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ОбластьДанных.Параметры.Заполнить(Выборка);
ТабДок.Вывести(ОбластьДанных);
// Обработка секретных данных
Если РежимВывода = "Печать" Тогда
// Для печати показываем секретные данные
ОбластьСекретныхДанных.Параметры.ПолеСекретное = Выборка.ПолеСекретное;
Иначе
// Для экрана скрываем секретные данные
ОбластьСекретныхДанных.Параметры.ПолеСекретное = "**********"; // Заглушка
КонецЕсли;
ТабДок.Вывести(ОбластьСекретныхДанных);
КонецЦикла;
КонецПроцедуры
Теперь мы можем использовать эту процедуру в наших командах:
// В обработчике команды "Сформировать" для отображения на экране
&НаКлиенте
Процедура СформироватьНаЭкране(Команда)
ТабДокЭкран = Новый ТабличныйДокумент;
СформироватьТабличныйДокументНаСервере(ТабДокЭкран, "Экран");
Элементы.ПолеТабличногоДокумента.УстановитьДокумент(ТабДокЭкран); // Предполагается, что на форме есть поле табличного документа
КонецПроцедуры
// В обработчике команды "Печать"
&НаКлиенте
Процедура Печать(Команда)
ТабДокПечать = Новый ТабличныйДокумент;
СформироватьТабличныйДокументНаСервере(ТабДокПечать, "Печать");
// Отправляем на печать без предварительного просмотра
ТабДокПечать.Печать(РежимИспользованияДиалогаПечати.НеИспользовать);
// Или с использованием диалога, но без возможности сохранения ТабДок
// ТабДокПечать.Печать(РежимИспользованияДиалогаПечати.Использовать);
КонецПроцедуры
// Серверная процедура для формирования документа
&НаСервере
Процедура СформироватьТабличныйДокументНаСервере(ТабДок, РежимВывода)
СформироватьТабличныйДокумент(ТабДок, РежимВывода);
КонецПроцедуры
Преимущества:
Надежность: Конфиденциальные данные никогда не попадают в табличный документ, который отображается на экране. Это значительно снижает риск утечки.
Гибкость: Мы можем использовать абсолютно разные макеты или логику заполнения для экранной и печатной версий.
Простота отладки: Код для экрана и печати четко разделен.
Недостатки:
Избыточность: В некоторых случаях приходится дважды выполнять одну и ту же или очень похожую логику формирования документа, что может немного увеличить время работы.
Поддержка: Если структура отчета часто меняется, возможно, придется вносить изменения в логику формирования обоих документов.
Этот подход предполагает использование одного и того же объекта ТабличныйДокумент, но его содержимое или свойства ячеек меняются непосредственно перед выводом на печать. Это может быть полезно, если структура документа очень сложна, и не хочется формировать его дважды.
Формирование для экрана: Сначала мы формируем табличный документ, в котором конфиденциальные ячейки либо остаются пустыми, либо заполняются заглушками, либо их текст окрашивается в цвет фона.
Модификация перед печатью: Когда пользователь инициирует печать (через специальную кнопку), мы программно находим нужные ячейки в уже сформированном табличном документе и:
Заполняем их конфиденциальными данными.
Изменяем цвет текста на видимый (например, черный).
Печать: Модифицированный табличный документ отправляется на печать.
Для реализации этого подхода нам потребуется:
Сохранить сформированный табличный документ в переменной формы или в реквизите формы, чтобы к нему можно было обратиться из разных обработчиков.
Создать отдельную команду "Печать с секретными данными", которая будет запускать процесс модификации и печати.
Рассмотрим пример:
// В обработчике команды "Сформировать" для отображения на экране
&НаКлиенте
Процедура СформироватьНаЭкране(Команда)
// Реквизит формы "ТабДокДляОтображения" типа ТабличныйДокумент
ТабДокДляОтображения = Новый ТабличныйДокумент;
СформироватьТабличныйДокументНаСервере(ТабДокДляОтображения, "Экран"); // Скрываем данные
Элементы.ПолеТабличногоДокумента.УстановитьДокумент(ТабДокДляОтображения);
КонецПроцедуры
// В обработчике команды "Печать с секретными данными"
&НаКлиенте
Процедура ПечатьССекретнымиДанными(Команда)
// Проверяем, что документ для отображения уже сформирован
Если ТабДокДляОтображения = Неопределено Тогда
Сообщить("Документ не сформирован для печати!");
Возврат;
КонецЕсли;
// Создаем копию текущего документа, чтобы не менять отображаемый на экране
ТабДокДляПечати = ТабДокДляОтображения.ПолучитьОбъект();
// Вызываем серверную процедуру для добавления секретных данных
ДобавитьСекретныеДанныеНаСервере(ТабДокДляПечати);
// Отправляем на печать
ТабДокДляПечати.Печать(РежимИспользованияДиалогаПечати.НеИспользовать);
КонецПроцедуры
// Серверная процедура для добавления секретных данных
&НаСервере
Процедура ДобавитьСекретныеДанныеНаСервере(ТабДок)
// Здесь мы должны знать, в каких ячейках находятся секретные данные.
// Это могут быть конкретные координаты или области по наименованию.
// Предположим, что у нас есть заранее определенный макет,
// и мы знаем, что ячейки с секретными данными находятся в определенной области.
Макет = ПолучитьМакет("МакетОтчета"); // Используем тот же макет, если нужно получить наименования областей
// Пример: Ищем ячейки по тексту заглушки или по имени области
Для Каждого Область ИЗ Макет.Области Цикл
Если Найти(Область.Имя, "СекретныеДанные") > 0 Тогда // Например, если имена областей содержат "СекретныеДанные"
Для НомерСтроки = Область.Верх По Область.Низ Цикл
Для НомерКолонки = Область.Лево По Область.Право Цикл
Ячейка = ТабДок.Область(НомерСтроки, НомерКолонки);
Если Не Ячейка = Неопределено И Ячейка.Текст = "**********" Тогда // Ищем заглушку
// Получаем настоящие секретные данные
// Здесь потребуется своя логика для сопоставления ячейки с данными.
// Возможно, нам нужно будет передать контекст текущей строки данных.
// Для примера, просто подставим текст:
Ячейка.Текст = "Это Секретные Данные (" + Строка(НомерСтроки) + "," + Строка(НомерКолонки) + ")";
Ячейка.ЦветТекста = Новый Цвет(0, 0, 0); // Черный цвет
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецЕсли;
КонецЦикла;
// Более продвинутый вариант - переформировать только секретные области
// Если у нас есть параметры в макете, например, "СекретноеПоле",
// мы можем найти соответствующие области и заполнить их новыми значениями.
// Это требует более сложной логики сопоставления данных.
// Пример с переформированием областей:
// Мы можем передавать идентификаторы строк или объектов,
// чтобы потом получить для них секретные данные.
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Документ.ПолеСекретное,
| Документ.Ссылка
|ИЗ
| Документ.МойДокумент КАК Документ
|ГДЕ
| Документ.Ссылка = &Ссылка"; // Предположим, что мы работаем с одним документом
// Обходим все строки в документе, ищем те, которые содержат секретные данные
Для НомерСтроки = 1 По ТабДок.ВысотаТаблицы Цикл
ОбластьСтроки = ТабДок.Область(НомерСтроки, 1, НомерСтроки, ТабДок.ШиринаТаблицы);
// Здесь потребуется более точная логика идентификации строки,
// например, по уникальному идентификатору, который мы скрытно добавили в ячейку.
// Или по наличию определенной заглушки в ячейке.
ЯчейкаСЗаглушкой = ОбластьСтроки.Найти("**********");
Если ЯчейкаСЗаглушкой <> Неопределено Тогда
// Извлекаем нужные данные из базы для этой строки
// Например, если в какой-то скрытой ячейке есть ссылка на объект
// ЯчейкаИД = ТабДок.Область(НомерСтроки, ОпределеннаяСкрытаяКолонка);
// Если ЯчейкаИД.Значение <> Неопределено Тогда
// Запрос.УстановитьПараметр("Ссылка", ЯчейкаИД.Значение);
// Выборка = Запрос.Выполнить().Выбрать();
// Если Выборка.Следующий() Тогда
// ЯчейкаСЗаглушкой.Текст = Выборка.ПолеСекретное;
// ЯчейкаСЗаглушкой.ЦветТекста = Новый Цвет(0, 0, 0);
// КонецЕсли;
// КонецЕсли;
// Для простоты примера:
ЯчейкаСЗаглушкой.Текст = "Конфиденциальные данные " + Строка(НомерСтроки);
ЯчейкаСЗаглушкой.ЦветТекста = Новый Цвет(0, 0, 0);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Преимущества:
Меньше дублирования кода: Основная логика формирования документа пишется один раз.
Эффективность памяти: Работаем с одним объектом ТабличныйДокумент (хотя для печати часто создается его копия).
Недостатки:
Менее надежно для экрана: Если мы просто скрываем текст цветом фона, пользователь может выделить ячейку (Ctrl+A) или скопировать содержимое (Ctrl+C), и данные станут видны. Если не применяется метод Вывод = Запретить.
Сложность сопоставления: Поиск нужных ячеек и сопоставление их с конфиденциальными данными может быть сложной задачей, особенно в динамических отчетах.
Этот подход наименее надежен, но наиболее прост в реализации, если не требуется строгая секретность от продвинутых пользователей. Он основан на изменении визуальных свойств ячеек табличного документа.
На экране:
Цвет текста конфиденциальных ячеек устанавливается равным цвету фона (например, белый текст на белом фоне). Это делает текст невидимым.
Для большей защиты можно использовать свойство Ячейка.Защита = Истина;, чтобы предотвратить редактирование содержимого ячейки.
Критически важно установить свойство Ячейка.Вывод = ТипВыводаЯчейкиТабличногоДокумента.Запретить;. Это запрещает выделение, копирование и сохранение содержимого ячейки через стандартные средства табличного документа.
При печати: Перед вызовом метода Печать() мы программно меняем цвет текста этих ячеек на видимый (например, черный) и, возможно, отключаем свойство Вывод = Запретить, если это требуется для печати.
В этом подходе мы модифицируем свойства ячеек в сформированном табличном документе.
// В процедуре формирования документа для отображения на экране
Процедура СформироватьТабличныйДокумент(ТабДок, РежимВывода)
// ... (логика формирования заголовков и обычных данных) ...
Макет = ПолучитьМакет("МакетОтчета");
ОбластьСекретныхДанных = Макет.ПолучитьОбласть("СекретныеДанные");
// Пример получения данных
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Документ.Поле1 КАК Поле1,
| Документ.ПолеСекретное КАК ПолеСекретное
|ИЗ
| Документ.МойДокумент КАК Документ";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// ... (вывод обычных данных) ...
ОбластьСекретныхДанных.Параметры.ПолеСекретное = Выборка.ПолеСекретное;
Если РежимВывода = "Экран" Тогда
// Для экрана делаем текст невидимым и защищаем ячейку
ОбластьСекретныхДанных.ЦветТекста = Новый Цвет(255, 255, 255); // Белый цвет текста
ОбластьСекретныхДанных.ЦветФона = Новый Цвет(255, 255, 255); // Белый цвет фона
ОбластьСекретныхДанных.Защита = Истина;
ОбластьСекретныхДанных.Вывод = ТипВыводаЯчейкиТабличногоДокумента.Запретить; // Важно!
Иначе
// Для печати делаем текст видимым
ОбластьСекретныхДанных.ЦветТекста = Новый Цвет(0, 0, 0); // Черный цвет текста
ОбластьСекретныхДанных.ЦветФона = Новый Цвет(255, 255, 255); // Белый цвет фона
ОбластьСекретныхДанных.Защита = Ложь;
ОбластьСекретныхДанных.Вывод = ТипВыводаЯчейкиТабличногоДокумента.Разрешить;
КонецЕсли;
ТабДок.Вывести(ОбластьСекретныхДанных);
КонецЦикла;
КонецПроцедуры
// Используем ту же логику для команд "Сформировать" и "Печать", как в Подходе 1,
// передавая соответствующий РежимВывода.
Важные аспекты свойства Вывод:
Свойство ЯчейкаТабличногоДокумента.Вывод (тип ТипВыводаЯчейкиТабличногоДокумента) имеет следующие значения:
Разрешить: Ячейка выводится обычным образом, ее содержимое доступно для выделения и копирования.Запретить: Содержимое ячейки не отображается и не доступно для выделения, копирования или сохранения в файл через стандартные средства табличного документа. Это очень важно для повышения безопасности, если вы используете подход с изменением цвета текста.НеИзменять: Поведение вывода определяется не этой ячейкой, а ее областью или табличным документом в целом.Использование Вывод = Запретить является ключевым элементом для повышения надежности этого подхода, так как оно предотвращает обход, связанный с выделением текста (Ctrl+A) или сохранением документа с последующим просмотром его содержимого.
Преимущества:
Простота: Легко реализовать, особенно если нужно скрывать небольшое количество ячеек.
Единый документ: Работаем с одним и тем же объектом ТабличныйДокумент.
Недостатки:
Относительная безопасность: Несмотря на Вывод = Запретить, всегда существует теоретический риск обхода (например, через скриншоты или специализированные программы чтения памяти, хотя это выходит за рамки типичных угроз).
Не подходит для больших объемов данных: Поиск и изменение свойств отдельных ячеек в большом документе может быть медленным и ресурсоемким.
Независимо от выбранного подхода, рассмотрим дополнительные аспекты, которые помогут усилить контроль и безопасность:
Мы настоятельно рекомендуем ограничивать доступ к функции печати конфиденциальных данных. Для этого используем стандартную ролевую модель 1С. Создадим отдельную роль (например, "РазрешенаПечатьСекретныхДанных") и назначим ее только тем пользователям, которые имеют право видеть и печатать полную информацию.
В коде, перед вызовом процедуры печати или формирования документа с секретными данными, мы можем выполнить проверку прав:
Если РольДоступна("РазрешенаПечатьСекретныхДанных") Тогда
// Разрешаем печать с секретными данными
// ...
Иначе
Сообщить("У вас нет прав на печать конфиденциальной информации!");
КонецЕсли;
Или просто не отображаем кнопку "Печать с секретными данными" для пользователей без соответствующей роли.
Если конфиденциальные документы должны печататься только на определенных, защищенных принтерах (например, в отдельном кабинете или на сетевом принтере с ограниченным доступом), мы можем программно указать имя принтера и запретить пользователю выбирать другой. Это предотвратит отправку секретных данных на виртуальный PDF-принтер или личный принтер пользователя.
ТабДок.ИмяПринтера = "Имя_Секретного_Принтера"; // Указываем конкретное имя принтера
ТабДок.Печать(РежимИспользованияДиалогаПечати.НеИспользовать); // Печатаем без диалога
Однако, здесь важно понимать, что администраторы могут переименовывать принтеры, поэтому такой контроль не является абсолютно надежным без должных административных мер по управлению печатью.
Для аудита и отслеживания потенциальных нарушений безопасности мы можем логировать каждую попытку печати конфиденциальных документов. В журнал регистрации 1С или в отдельный регистр сведений мы записываем:
Пользователя, который инициировал печать.
Дату и время операции.
Название документа/отчета.
Возможно, даже информацию о том, на какой принтер была отправлена печать (если это удалось определить).
// Пример логирования
ЗаписьЖурналаРегистрации(
"Печать секретных данных",
УровеньЖурналаРегистрации.Информация,
Метаданные.Документы.МойДокумент, // Или Метаданные.Отчеты.МойОтчет
СсылкаНаОбъектПечати,
"Пользователь " + Пользователи.ТекущийПользователь.Наименование + " распечатал документ с секретными данными.");
Наиболее надежный, но и наиболее сложный вариант — формировать табличный документ с конфиденциальными данными на сервере и отправлять его непосредственно на принтер, минуя даже временное попадание на клиентский компьютер. Этот подход требует использования серверных механизмов печати (например, через COM-соединение с установленным клиентом 1С на сервере печати) и тщательной настройки безопасности.
Секретные данные при таком подходе никогда не передаются на клиентскую машину, что исключает возможность их перехвата или просмотра пользователем. Этот метод обычно применяется в системах с очень высокими требованиями к безопасности.
Мы рассмотрели несколько методов сокрытия ячеек табличного документа 1С на экране с возможностью их отображения при печати. От выбора подхода зависит баланс между простотой реализации, гибкостью и уровнем безопасности.
Формирование двух отдельных табличных документов является наиболее надежным и гибким решением, особенно если конфиденциальность данных критична. Это позволяет четко разделить логику и данные для экрана и печати.
Динамическое изменение содержимого одного табличного документа предоставляет хороший компромисс, минимизируя дублирование кода, но требует внимательного контроля за скрытием данных на экране.
Визуальное скрытие и защита ячеек с использованием свойств ЦветТекста, Защита и, что особенно важно, Вывод = Запретить, является самым простым вариантом для реализации, но требует тщательного понимания его ограничений по безопасности.
Для любой из реализаций крайне важно дополнить функциональность разграничением прав доступа, программным контролем принтеров и логированием действий. Это создаст многоуровневую систему защиты и обеспечит соблюдение требований к безопасности информации.