Как создать одну внешнюю обработку для разных конфигураций 1С и избежать ошибок синтаксического контроля

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

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

Рассмотрим подробнее, как обойти эти ограничения и заставить код работать в разных средах, используя приемы динамического программирования и особенности компиляции модулей 1С.

Почему возникает ошибка, если код не вызывается

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

Решение 1: Использование функций Вычислить() и Выполнить()

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

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


// Вместо этого (вызовет ошибку компиляции):
// Пользователь = глТекущийПользователь;

// Используем это:
Попытка
    Пользователь = Вычислить("глТекущийПользователь");
Исключение
    Пользователь = ПараметрыСеанса.ТекущийПользователь; // Альтернатива для новых конфигураций
КонецПопытки;

Аналогично можно вызывать процедуры через Выполнить(). Посмотрим на пример вызова метода общего модуля:


ТекстКода = "ОбщегоНазначения.ЗаполнитьШапкуДокумента(ДокументОбъект, глТекущийПользователь)";
Попытка
    Выполнить(ТекстКода);
Исключение
    ЗаписьЖурналаРегистрации("Ошибка универсальной обработки", УровеньЖурналаРегистрации.Предупреждение,,, ОписаниеОшибки());
КонецПопытки;

Решение 2: Динамическое получение общего модуля в переменную

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


// Определим переменную для модуля
М_ОбщегоНазначения = Неопределено;

// Попробуем инициализировать её
Попытка
    М_ОбщегоНазначения = Вычислить("ОбщегоНазначения");
Исключение
    // Модуль не найден, переменная останется Неопределено
КонецПопытки;

// Теперь вызываем методы безопасно
Если М_ОбщегоНазначения <> Неопределено Тогда
    М_ОбщегоНазначения.ЗаполнитьШапкуДокумента(Параметры);
КонецЕсли;

Этот подход позволяет сохранить читаемость кода и избежать многократного использования строковых конструкций.

Решение 3: Проверка метаданных перед вызовом

Чтобы код был действительно надежным, перед использованием Вычислить() или Выполнить(), рекомендуется программно проверить наличие объекта в метаданных. Это позволит избежать лишних исключений (Try-Catch), которые замедляют работу системы.

Выясним, как проверить существование общего модуля:


Функция ОбщийМодульСуществует(ИмяМодуля)
    Возврат Метаданные.ОбщиеМодули.Найти(ИмяМодуля) <> Неопределено;
КонецФункции

// Пример использования
Если ОбщийМодульСуществует("ОбщегоНазначения") Тогда
    Модуль = Вычислить("ОбщегоНазначения");
    Модуль.НужнаяПроцедура();
КонецЕсли;

Такой же подход применим к документам, справочникам и параметрам сеанса. Например, проверка наличия параметра сеанса ТекущийПользователь:


Если Метаданные.ПараметрыСеанса.Найти("ТекущийПользователь") <> Неопределено Тогда
    Значение = ПараметрыСеанса.ТекущийПользователь;
КонецЕсли;

Решение 4: Разделение кода по разным формам

Интересной особенностью платформы 1С является то, что модуль формы компилируется только в момент её открытия. Мы можем использовать это для создания конфигурационно-зависимых интерфейсов.

Рассмотрим алгоритм реализации:

  1. Создаем основную форму обработки, которая будет выполнять роль диспетчера.
  2. Создаем дополнительные формы, например Форма_УПП и Форма_ERP.
  3. В модуле каждой формы пишем специфичный код, использующий модули конкретной конфигурации.
  4. В основной форме в событии ПриОткрытии (или ПриСозданииНаСервере для управляемых форм) определяем тип конфигурации и открываем нужную форму.

В этом случае, если мы запустим обработку в "1С:ERP", форма для "1С:УПП" не будет загружена, и синтаксические ошибки в её модуле не помешают работе системы.

Решение 5: Использование макетов для хранения кода

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


ТекстСкрипта = ЭтотОбъект.ПолучитьМакет("Логика_БП").ПолучитьТекст();
Попытка
    Выполнить(ТекстСкрипта);
Исключение
    Сообщить("Не удалось выполнить специфическую логику: " + ОписаниеОшибки());
КонецПопытки;

Рекомендации по проектированию универсальных обработок

Для создания качественного продукта придерживайтесь следующих правил:

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

← На главную