Как глобально перехватить открытие формы в 1С 7.7 без изменения модулей объектов?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

В практике разработчика на платформе 1С:Предприятие 7.7 часто возникает задача ограничить доступ к определенным формам документов или справочников на основе сложных условий. Традиционный подход подразумевает вставку кода в процедуру ПриОткрытии каждого конкретного объекта. Однако, если объектов сотни, такой метод становится крайне трудоемким и неудобным для поддержки. В этой статье мы подробно разберем, как реализовать глобальный перехватчик открытия форм, не прибегая к программированию в каждой форме документа, используя возможности внешних компонент FormEx и 1C++.

Метод 1: Использование трансляции событий в FormEx

Рассмотрим наиболее современный и простой в реализации способ, который появился в поздних версиях компоненты FormEx. Разработчики реализовали механизм, позволяющий «пробрасывать» локальные предопределенные процедуры форм непосредственно в Глобальный модуль конфигурации.

Проанализируем ситуацию: нам нужно запретить открытие формы до того, как она будет отрисована на экране. Для этого в Глобальном модуле необходимо создать процедуру с зарезервированным именем ПриОткрытии. Посмотрим, как выглядит программный код для такой реализации:


// В Глобальном модуле
Процедура ПриОткрытии(КонтекстФормы) Экспорт
    // Получаем расширение формы для анализа объекта
    Расширение = СоздатьОбъект("РасширениеФормы");
    Расширение.УстановитьФорму(КонтекстФормы.Форма);
    
    ТипОбъекта = Расширение.ПолныйТипОбъекта();
    
    // Проверим условие, например, запретим открытие для определенных видов документов
    Если ТипОбъекта = "Документ.Реализация" Тогда
        Если КонтекстФормы.ДатаДок < РабочаяДата() Тогда
            Предупреждение("Открытие старых документов реализации запрещено!");
            СтатусВозврата(0); // Отменяем открытие формы
            Возврат;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Разберем по шагам, что происходит в этом коде. При попытке открытия любой формы платформа вызывает эту глобальную процедуру (благодаря перехвату FormEx). В качестве параметра передается КонтекстФормы. Используя объект РасширениеФормы, мы можем точно определить, какой именно объект пытается открыться, и, при необходимости, вызвать СтатусВозврата(0). Это полностью предотвращает появление окна на экране.

Метод 2: Использование классов-перехватчиков (Hooker) в 1С++

Теперь рассмотрим более продвинутый метод, который часто называют «высшей математикой» в мире 7.7 — использование классов-перехватчиков из состава компоненты 1С++. Этот метод позволяет перехватывать события на уровне системных вызовов 1С.

Выясним причину, почему этот метод считается надежным: он работает даже тогда, когда штатные средства пасуют, и позволяет внедряться в процесс до инициализации модуля формы. Для работы нам потребуется создать файл описания классов defcls.prm в каталоге базы данных.

Проанализируем структуру файла defcls.prm:


class ПерехватчикСобытий = Перехват.ert {
    hooker
};

Затем создадим внешнюю обработку Перехват.ert, которая будет выступать в роли класса. В ее модуле пропишем перехват события открытия:


// В модуле класса Перехват.ert
Процедура ПослеСозданияОбъекта()
    // Регистрируем перехват
    Перехватчик = СоздатьОбъект("Hooker");
    Перехватчик.ЗагрузитьВГМ(Контекст); 
КонецПроцедуры

Функция Pre_OnOpen(Конт) Экспорт
    // Данный метод вызывается ПЕРЕД штатной процедурой ПриОткрытии
    Сообщить("Попытка открытия объекта: " + Конт.ТекущийОбъект());
    
    // Если нужно запретить открытие:
    // Возврат 0; 
    
    Возврат 1;
КонецФункции

Рассмотрим подробнее преимущества этого способа. Метод Pre_OnOpen позволяет не просто закрыть форму, а вмешаться в процесс до того, как 1С начнет обрабатывать локальный модуль ПриОткрытии. Это критически важно, если в локальном модуле прописаны тяжелые запросы или модальные предупреждения, которые вы не хотите выполнять.

Метод 3: Использование недокументированной процедуры ПриОткрытииФормыДокумента

В некоторых версиях FormEx существует скрытая возможность использования процедуры ПриОткрытииФормыДокумента. Посмотрим на пример её объявления, который позволяет получить низкоуровневые идентификаторы открываемых объектов.


Процедура ПриОткрытииФормыДокумента(ФлагСтандартнойОбработки, ИдВидаДок, ИдОбъекта, ИдЖурнала, П4, ИдКопируемого)
    // ИдВидаДок и ИдОбъекта - это внутренние 36-ричные ID платформы.
    // Для преобразования в привычные значения используем MetaDataWork.
    
    Если ФлагСтандартнойОбработки = 1 Тогда
        // Проверяем условия доступа
        Если МояПроверкаПрав(ИдВидаДок, ИдОбъекта) = 0 Тогда
            ФлагСтандартнойОбработки = 0; // Запрещаем открытие на корню
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Проанализируем ситуацию с параметрами: здесь вместо привычных объектов типа ДокументОбъект приходят их строковые идентификаторы. Чтобы превратить ИдОбъекта в полноценную ссылку, можно использовать объект MetaDataWork и его метод IDToValue или воспользоваться функцией ЗначениеИзСтрокиВнутр, сформировав соответствующую сигнатуру типа {"O","0","0","ID","0","0","0"}.

Сравнение подходов и практические рекомендации

Рассмотрим, какой метод выбрать в зависимости от ваших задач:

  1. FormEx (Трансляция событий): Идеально подходит для быстрого внедрения. Требует минимум кода в Глобальном модуле. Важный момент: убедитесь, что вы используете версию FormEx не ниже 2.5.0.191, иначе возможны проблемы с двойным вызовом событий удаления.
  2. 1С++ (Hooker): Рекомендуется для сложных систем с развитой объектной моделью. Позволяет организовать чистый код в отдельных классах, не перегружая Глобальный модуль лишней логикой.
  3. Инсертеры (вспомогательные утилиты): Если вы все же решили модифицировать формы, используйте специализированные утилиты (например, Inserter), которые позволяют массово добавить вызов вашей глобальной функции (например, ГлПроверкаДоступа(Контекст)) во все модули форм за один проход.

Выясним причину возможных ошибок при использовании глобальных перехватчиков. Основная проблема — это рекурсия или конфликты с модальными окнами. Если в глобальном перехватчике вы вызываете Предупреждение(), это может привести к тому, что фокус окна будет потерян. Всегда старайтесь сначала выполнить СтатусВозврата(0), и только после этого выводить информационные сообщения пользователю.

В завершение проанализируем производительность. Глобальный перехват добавляет незначительную задержку при открытии каждой формы, так как код выполняется при каждом событии. Поэтому внутри таких процедур категорически не рекомендуется выполнять сложные запросы к базе данных или тяжелые циклы по метаданным. Используйте кэширование прав или предварительно подготовленные списки ограничений в объектах типа ТаблицаЗначений или СписокЗначений, созданных при старте системы.

← На главную