Как вызвать экспортную функцию из модуля объекта обработки из общего модуля 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Понимание различий: Модуль менеджера и Модуль объекта

Прежде чем мы рассмотрим конкретные решения, очень важно понимать фундаментальную разницу между модулем менеджера и модулем объекта в контексте обработок (и других объектов метаданных 1С).

  1. Модуль менеджера обработки (Обработки.ВашаОбработка)

    Этот модуль относится ко всему объекту метаданных "Обработка". Функции и процедуры, объявленные в этом модуле как экспортные, доступны статически, то есть без предварительного создания экземпляра обработки. Вы можете обращаться к ним напрямую через имя объекта метаданных, например, Обработки.ВашаОбработка.НужнаяФункцияМенеджера(). Модуль менеджера используется для функций, которые не зависят от конкретного экземпляра данных обработки, например, для получения общих сведений об обработке, проверки прав доступа или реализации фабричных методов. Критически важно: у внешней обработки модуль менеджера отсутствует. Это одна из ключевых причин возникновения проблем, описанных автором.

  2. Модуль объекта обработки (ОбъектОбработки)

    Этот модуль относится к конкретному экземпляру обработки. Функции и процедуры, объявленные в нем как экспортные, становятся доступными только после того, как вы создадите экземпляр объекта обработки. Вызов будет выглядеть так: ОбъектОбработки = Обработки.ВашаОбработка.Создать(); ОбъектОбработки.НужнаяФункцияОбъекта(). Модуль объекта предназначен для функций, которые оперируют данными или логикой, специфичной для данного экземпляра обработки – например, для выполнения основной бизнес-логики, работы с реквизитами формы или выполнения специфических алгоритмов обработки данных.

Основная проблема, с которой столкнулся автор, заключалась в попытке вызвать функцию из модуля объекта как функцию модуля менеджера (Обработка.НужнаяФункция()) после переноса её туда из модуля менеджера, но без создания самого объекта обработки.

Решение 1: Вызов функции из модуля объекта для обработки, встроенной в конфигурацию

Когда обработка является частью вашей конфигурации (то есть находится в дереве метаданных), и вы хотите вызвать экспортную функцию, расположенную в её модуле объекта, из общего модуля, мы не сможем обратиться к ней напрямую через метаданные.

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

Чтобы обойти эту ситуацию и получить доступ к функциям модуля объекта, нам необходимо сначала создать экземпляр объекта обработки. Это позволит нам работать с обработкой как с полноценным объектом и вызывать её экспортные методы.

Рассмотрим подробнее шаги и пример кода:

  1. Создаем экземпляр объекта обработки: Используем метод Создать() для получения объекта.
  2. Вызываем нужную экспортную функцию: После получения объекта мы можем обращаться к его экспортным процедурам и функциям.

Пример кода для вызова встроенной обработки:


// В ОбщемМодуле (например, ОбщийМодуль_НашФункционал)
Процедура ВыполнитьФункциюОбработки() Экспорт
    // 1. Создаем экземпляр объекта обработки
    ОбъектОбработки = Обработки.ВашаОбработка.Создать();

    // 2. Вызываем экспортную функцию из модуля объекта
    Результат = ОбъектОбработки.НужнаяФункцияИзМодуляОбъекта();
    
    // Дальнейшая логика с результатом...
    Сообщить("Результат выполнения функции: " + Результат);

КонецПроцедуры

// Пример модуля объекта обработки "ВашаОбработка"
// В Модуле объекта обработки "ВашаОбработка"
Функция НужнаяФункцияИзМодуляОбъекта() Экспорт
    // Здесь находится логика, которую мы хотим вызвать
    Возврат "Функция из модуля объекта выполнена!";
КонецФункции

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

Решение 2: Вызов функции из модуля объекта для внешней обработки

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

Для доступа к функциям внешней обработки, её объект также нужно получить, но уже другими способами. Рассмотрим пример внешней обработки и два основных подхода:

  1. Использование Библиотеки Стандартных Подсистем (БСП)

    Если ваша внешняя обработка зарегистрирована в справочнике "ДополнительныеОтчетыИОбработки" (это стандартный механизм БСП для работы с внешними отчетами и обработками), мы можем получить её объект, используя специализированные функции БСП. Этот подход является наиболее предпочтительным, так как он обеспечивает централизованное управление внешними объектами, их версионирование и права доступа.

    
    // В ОбщемМодуле (например, ОбщийМодуль_НашФункционал)
    Процедура ВыполнитьФункциюВнешнейОбработкиЧерезБСП() Экспорт
        // Предполагаем, что внешняя обработка зарегистрирована в справочнике
        // "ДополнительныеОтчетыИОбработки" под именем "Моя обработка"
    
        // 1. Получаем ссылку на элемент справочника "ДополнительныеОтчетыИОбработки"
        ВнешняяОбработкаСсылка = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию("Моя обработка");
    
        Если ВнешняяОбработкаСсылка = Неопределено Тогда
            Сообщить("Внешняя обработка 'Моя обработка' не найдена в справочнике.");
            Возврат;
        КонецЕсли;
    
        // 2. Получаем объект внешней обработки через функционал БСП
        // Метод ОбъектВнешнейОбработки() находится в ОбщемМодуле "ДополнительныеОтчетыИОбработки"
        Попытка
            ВнешняяОбработкаОбъект = ДополнительныеОтчетыИОбработки.ОбъектВнешнейОбработки(ВнешняяОбработкаСсылка);
        Исключение
            Сообщить("Ошибка при получении объекта внешней обработки: " + ОписаниеОшибки());
            Возврат;
        КонецПопытки;
    
        Если ВнешняяОбработкаОбъект <> Неопределено Тогда
            // 3. Вызываем экспортную функцию из модуля объекта внешней обработки
            Результат = ВнешняяОбработкаОбъект.МояКласснаяФункция();
            Сообщить("Результат выполнения функции внешней обработки: " + Результат);
        Иначе
            Сообщить("Не удалось получить объект внешней обработки.");
        КонецЕсли;
    
    КонецПроцедуры
    
    // Пример модуля объекта внешней обработки (.epf)
    // В Модуле объекта внешней обработки
    Функция МояКласснаяФункция() Экспорт
        // Логика функции внешней обработки
        Возврат "Функция из модуля объекта внешней обработки выполнена!";
    КонецФункции
    
  2. Программная загрузка внешней обработки из файла

    Если обработка не зарегистрирована в БСП или нам нужно динамически загрузить её из файла по заданному пути (например, для тестирования новой версии или если обработка предназначена для одноразового использования без постоянной регистрации), мы можем сделать это программно.

    
    // В ОбщемМодуле (например, ОбщийМодуль_НашФункционал)
    Процедура ВыполнитьФункциюВнешнейОбработкиИзФайла(ПутьКФайлу) Экспорт
        // ПутьКФайлу, например: "C:\МоиОбработки\ВашаОбработка.epf"
    
        // 1. Создаем объект внешней обработки из файла
        Попытка
            ВнешняяОбработка = ВнешниеОтчетыИОбработки.Создать(ПутьКФайлу);
        Исключение
            Сообщить("Ошибка при загрузке внешней обработки из файла: " + ОписаниеОшибки());
            Возврат;
        КонецПопытки;
        
        Если ВнешняяОбработка <> Неопределено Тогда
            // 2. Вызываем экспортную функцию из модуля объекта загруженной обработки
            Результат = ВнешняяОбработка.НужнаяФункция();
            Сообщить("Результат выполнения функции загруженной обработки: " + Результат);
        Иначе
            Сообщить("Не удалось загрузить внешнюю обработку из файла.");
        КонецЕсли;
    
    КонецПроцедуры
    
    // Пример модуля объекта внешней обработки (.epf)
    // В Модуле объекта внешней обработки
    Функция НужнаяФункция() Экспорт
        // Логика функции внешней обработки
        Возврат "Функция из модуля объекта внешней обработки, загруженной из файла, выполнена!";
    КонецФункции
    

    Особенности и риски загрузки из файла:

    • Актуальность версии: При каждом вызове ВнешниеОтчетыИОбработки.Создать(ПутьКФайлу) платформа будет загружать текущую версию файла. Это удобно для отладки, но может быть рискованно в промышленных системах, если не контролировать версии файлов.
    • Производительность: Постоянная загрузка файла может быть менее производительной, чем работа с зарегистрированной обработкой или встроенной.
    • Контекст выполнения: Необходимо учитывать, где будет доступен указанный путь к файлу (например, на сервере 1С или на клиентской машине).

Альтернативный подход: Использование прокси-функции в модуле менеджера

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

Рассмотрим по шагам, как это реализовать:

  1. В модуле менеджера обработки:

    Создаем экспортную функцию (или процедуру), которая будет доступна для внешнего вызова. Внутри этой функции мы создаем экземпляр объекта обработки и вызываем нужную функцию уже из его модуля объекта.

    
    // В Модуле менеджера обработки "ВашаОбработка"
    Функция НужнаяФункция() Экспорт
        // 1. Создаем экземпляр объекта обработки
        ОбъектОбработки = ЭтотОбъект.Создать(); // или Обработки.ВашаОбработка.Создать();
    
        // 2. Вызываем функцию из модуля объекта
        Возврат ОбъектОбработки.РеализацияНужнойФункции();
    КонецФункции
    
  2. В модуле объекта обработки:

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

    
    // В Модуле объекта обработки "ВашаОбработка"
    Функция РеализацияНужнойФункции() Экспорт
        // Здесь находится вся необходимая бизнес-логика
        Возврат "Логика функции реализована в модуле объекта!";
    КонецФункции
    
  3. В общем модуле:

    Вызов из общего модуля остается прежним, так как он обращается к функции в модуле менеджера, который теперь служит прокси.

    
    // В ОбщемМодуле
    Процедура ВызватьФункциюЧерезПрокси() Экспорт
        // Вызываем функцию, которая на самом деле является прокси
        Результат = Обработки.ВашаОбработка.НужнаяФункция();
        Сообщить("Результат вызова через прокси: " + Результат);
    КонецПроцедуры
    

Недостатки этого подхода:

Рекомендации по организации кода и рефакторингу

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

  1. Определяйте место функции:

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

  2. Рефакторинг при ненужной инициализации:

    Если вызов Создать() приводит к выполнению большого объема нежелательного кода (например, открывается форма, выполняются сложные запросы), это часто указывает на необходимость рефакторинга обработки. Рассмотрим варианты:

    • Разделение логики: Выделите общую или фоновую логику в отдельные общие модули или сервисные объекты, которые не требуют полной инициализации обработки.
    • Параметризация: Сделайте так, чтобы инициализирующий код выполнялся только при определенных условиях или при вызове специального метода инициализации, а не автоматически при создании объекта.
  3. Использование расширений:

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

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

← На главную