В практике разработки на платформе 1С (особенно в версиях 8.2 и 8.3 с использованием обычных форм) часто возникает задача тонкой настройки прав доступа. Одной из самых проблемных зон является право «Вывод». Платформа устроена таким образом, что это право регулирует сразу три действия: печать документа, сохранение его в файл (Excel, PDF и др.) и копирование данных в буфер обмена. Если мы отключаем это право, пользователь теряет возможность не только распечатать отчет, но и просто скопировать строку. Однако бизнесу часто требуется разрешить копирование (например, для переноса артикула в другую систему), жестко запретив при этом выгрузку всего отчета целиком. Разберем, как обойти это ограничение программными средствами.
Проанализируем стандартное поведение платформы. Когда у пользователя в роли снята галочка «Вывод», для любого объекта типа ТабличныйДокумент блокируются методы Записать(), Напечатать(), а также системное сочетание клавиш Ctrl+C. Попытка программного вызова записи в файл приведет к ошибке нарушения прав доступа. Наша задача — создать альтернативный механизм копирования, который будет считывать данные из ячеек напрямую из оперативной памяти и передавать их в системный буфер Windows, минуя стандартный механизм «Вывода» 1С.
Первым делом нам необходимо понять, что именно пользователь выделил в отчете. В обычном приложении у элемента формы «Поле табличного документа» есть свойство ВыделенныеОбласти. Это коллекция, которая содержит одну или несколько областей, выбранных пользователем.
Рассмотрим алгоритм обхода этих областей. Нам нужно собрать значения всех ячеек в одну текстовую строку, сохраняя структуру таблицы (чтобы при вставке в Excel данные распределились по ячейкам, а не слиплись в одну кучу). Для этого мы будем использовать символ табуляции в качестве разделителя колонок и символ перевода строки для разделения строк таблицы.
Разберем пример кода для сбора данных:
Функция ПолучитьТекстИзВыделенныхОбластей(ТабДок)
Результат = "";
Для Каждого Область Из ТабДок.ВыделенныеОбласти Цикл
// Определяем границы выделения
Для НомерСтроки = Область.Верх По Область.Низ Цикл
СтрокаДляБуфера = "";
Для НомерКолонки = Область.Лево По Область.Право Цикл
Ячейка = ТабДок.Область(НомерСтроки, НомерКолонки);
ТекстЯчейки = СокрЛП(Ячейка.Текст);
// Добавляем табуляцию, если это не первая колонка
Если СтрокаДляБуфера = "" Тогда
СтрокаДляБуфера = ТекстЯчейки;
Иначе
СтрокаДляБуфера = СтрокаДляБуфера + Символы.Таб + ТекстЯчейки;
КонецЕсли;
КонецЦикла;
// Добавляем перевод строки
Если Результат = "" Тогда
Результат = СтрокаДляБуфера;
Иначе
Результат = Результат + Символы.ПС + СтрокаДляБуфера;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Возврат Результат;
КонецФункции
После того как мы получили текстовую строку, нам нужно «заставить» операционную систему принять ее в буфер обмена. Поскольку встроенных средств прямой работы с буфером в 1С 8.2/8.3 нет (кроме платформенных механизмов, которые у нас заблокированы), мы воспользуемся внешними объектами Windows.
Наиболее стабильным и современным способом является использование объекта htmlfile. Этот метод не требует открытия видимых окон браузера и работает быстрее, чем эмуляция нажатия клавиш через SendKeys.
Рассмотрим реализацию помещения текста в буфер:
Процедура ПоместитьТекстВБуферОбмена(Текст)
Попытка
ОбъектHTML = Новый COMОбъект("htmlfile");
ОбъектHTML.ParentWindow.ClipboardData.SetData("Text", Текст);
Исключение
Сообщить("Не удалось получить доступ к буферу обмена: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Проанализируем, почему это работает. Объект htmlfile — это часть движка Internet Explorer, которая доступна в любой системе Windows. Метод SetData работает напрямую с API операционной системы. Для 1С это выглядит как обычное использование COM-объекта, и система прав 1С не отслеживает, что данные уходят именно в буфер обмена.
Теперь нам нужно дать пользователю возможность вызвать этот код. Так как Ctrl+C заблокирован на уровне платформы, нам нужно создать свою кнопку или пункт меню. Посмотрим на два способа реализации.
Способ А: Командная панель
Добавим в командную панель формы кнопку «Копировать данные». В действии этой кнопки пропишем вызов наших процедур:
Процедура КоманднаяПанельКопировать(Кнопка)
ТекстДляКопирования = ПолучитьТекстИзВыделенныхОбластей(ЭлементыФормы.ПолеТабличногоДокумента);
Если ЗначениеЗаполнено(ТекстДляКопирования) Тогда
ПоместитьТекстВБуферОбмена(ТекстДляКопирования);
КонецЕсли;
КонецПроцедуры
Способ Б: Подмена контекстного меню
Это более изящный метод. В свойствах элемента ПолеТабличногоДокумента отключим свойство «Автоматическое контекстное меню». Затем создадим свое собственное контекстное меню, где будет только один пункт — «Копировать выделенное». Таким образом, пользователь даже не увидит кнопок «Печать» или «Сохранить как», которые обычно присутствуют в стандартном меню 1С.
Ситуация усложняется, если пользователи работают в терминальной сессии, а буфер обмена между сервером и их локальным компьютером закрыт политиками безопасности. Выясним причину: в этом случае никакие программные методы не помогут передать данные на локальную машину. Однако наш метод позволит копировать данные внутри сервера. Например, пользователь сможет скопировать данные из отчета 1С и вставить их в Excel, открытый в той же терминальной сессии. Для многих организаций этого достаточно, так как сохраняется контроль над данными (файлы не покидают сервер).
Если по каким-то причинам объект htmlfile недоступен, можно использовать старый проверенный метод через WScript.Shell. Он менее надежен, так как имитирует нажатие клавиш и чувствителен к раскладке клавиатуры и фокусу окна, но его полезно знать как запасной вариант.
Процедура КопироватьЧерезSendKeys()
WshShell = Новый COMОбъект("WScript.Shell");
// Имитация Ctrl+C.
// Внимание: метод может не сработать при запрещенном праве "Вывод"
WshShell.SendKeys("^c");
КонецПроцедуры
Важно понимать, что SendKeys просто посылает сигнал нажатия клавиш. Если право «Вывод» у пользователя отозвано, платформа 1С проигнорирует этот сигнал точно так же, как и физическое нажатие кнопок на клавиатуре. Поэтому метод с прямым перебором ячеек (Шаг 1) и использованием htmlfile (Шаг 2) является единственным гарантированным решением.
Мы выяснили, что разделение прав на печать и копирование в 1С возможно только путем создания собственного механизма обработки данных. Подведем итоги нашего решения:
ТабличныйДокумент.Записать() по-прежнему будет выдавать ошибку, что защищает от массовой выгрузки данных в файлы.Символы.Таб позволяет сохранять табличную структуру при вставке в сторонние редакторы.CF_HTML, что значительно сложнее в реализации.Таким образом, комбинируя программный обход ячеек и возможности COM-объектов Windows, мы получаем гибкий инструмент, позволяющий удовлетворить специфические требования безопасности без потери удобства работы пользователей.