При разработке универсальных обработок или расширений для 1С:Предприятие программисты часто сталкиваются с ситуацией, когда один и тот же код должен работать на разных версиях платформы (например, переход с 8.2 на 8.3) или в разных режимах запуска (тонкий и толстый клиент). Основная сложность заключается в том, что некоторые объекты, такие как КонструкторЗапроса, доступны только в определенных условиях. Если просто написать код для новой версии, старая версия платформы выдаст ошибку "Тип не определен" еще на этапе открытия формы, так как компилятор пытается проверить синтаксис всех процедур модуля.
В этой статье мы подробно разберем, как обойти ограничения компилятора, правильно определить версию программы и обеспечить стабильную работу кода в любых условиях. Мы рассмотрим несколько стратегий: от простых инструкций препроцессора до динамического выполнения кода.
Рассмотрим классическую ситуацию. Нам нужно открыть КонструкторЗапроса. В толстом клиенте версии 8.2 это делалось модально, а в тонком клиенте версии 8.3 — через описание оповещения. Казалось бы, достаточно написать условие:
Если ЭтоВерсия83 Тогда
Конструктор = Новый КонструкторЗапроса;
Конструктор.Показать(Оповещение);
Иначе
Конструктор = Новый КонструкторЗапроса;
Конструктор.ОткрытьМодально();
КонецЕсли;
Проблема в том, что когда эта форма открывается в Тонком клиенте старой версии, платформа анализирует весь модуль целиком. Обнаружив конструкцию Новый КонструкторЗапроса, компилятор понимает, что такого типа в данной среде не существует, и прерывает работу с ошибкой. Нам нужно "спрятать" этот код от компилятора, чтобы он проверялся только в момент реального исполнения.
Проанализируем самый эффективный способ обхода синтаксического контроля — метод Выполнить(). Этот оператор принимает строку текста и исполняет ее как фрагмент кода. Поскольку для компилятора это просто текстовая строка, он не проверяет типы данных внутри нее до момента выполнения.
Разберем, как применить этот подход для нашей задачи:
Процедура ОткрытьКонструктор()
ТекстКода = "";
Если Найти(СистемнаяИнформация.ВерсияПриложения, "8.3") > 0 Тогда
// Код для 8.3
ТекстКода = "
|Конструктор = Новый КонструкторЗапроса;
|Оповещение = Новый ОписаниеОповещения(""ЗавершитьРедактирование"", ЭтотОбъект);
|Конструктор.Показать(Оповещение);";
Иначе
// Код для 8.2 (Толстый клиент)
ТекстКода = "
|Конструктор = Новый КонструкторЗапроса;
|Конструктор.ОткрытьМодально();";
КонецЕсли;
Попытка
Выполнить(ТекстКода);
Исключение
Сообщить("В данной версии или клиенте запуск конструктора невозможен!");
КонецПопытки;
КонецПроцедуры
Использование Выполнить() позволяет нам динамически подставлять нужный синтаксис. Важный момент: внутри строки Выполнить нельзя объявлять переменные, которые должны быть доступны в основном модуле позже, если они не были определены заранее.
Вместо того чтобы сразу пытаться создать объект, мы можем проверить, знает ли текущая среда вообще о существовании такого типа. Для этого используем функцию Тип(). Если тип не поддерживается (например, КонструкторЗапроса в веб-клиенте), функция вернет Неопределено.
Проанализируем пример безопасного создания объекта:
Если Тип("КонструкторЗапроса") <> Тип("Неопределено") Тогда
// Используем Вычислить, чтобы создать объект без прямой ссылки в коде
Конструктор = Вычислить("Новый КонструкторЗапроса");
// Далее работаем с объектом
КонецЕсли;
Функция Вычислить() похожа на Выполнить(), но она возвращает результат выражения. Это очень удобно для создания объектов "на лету" без риска вызвать ошибку компиляции Тип не определен.
Рассмотрим ситуацию, когда нам нужно разделить код не по версии платформы, а по типу клиента (Тонкий, Толстый, Веб, Сервер). Для этого идеально подходят инструкции #Если. Они выполняются еще до компиляции модуля, и код, не попадающий под условие, просто вырезается.
#Если ТолстыйКлиентОбычноеПриложение Или ТолстыйКлиентУправляемоеПриложение Тогда
// Этот код увидит только толстый клиент
Конструктор = Новый КонструкторЗапроса;
Конструктор.ОткрытьМодально();
#Иначе
// Код для тонкого клиента 8.3.5+
Конструктор = Новый КонструкторЗапроса;
Оповещение = Новый ОписаниеОповещения("Завершение", ЭтотОбъект);
Конструктор.Показать(Оповещение);
#КонецЕсли
Однако помните: препроцессор работает на уровне типов клиентов, но он не умеет различать минорные версии платформы (например, 8.3.10 и 8.3.12). Для этого нужно комбинировать препроцессор с программной проверкой СистемнаяИнформация.ВерсияПриложения.
Чтобы код вел себя по-разному на разных сборках платформы, выясним причину поведения через получение номера версии. Мы можем использовать встроенные функции для анализа строки версии.
Функция ПолучитьВерсию()
СтрВерсии = СистемнаяИнформация.ВерсияПриложения; // Например, "8.3.15.1830"
Возврат СтрВерсии;
КонецФункции
// Пример использования
Если ОбщегоНазначенияКлиентСервер.СравнитьВерсии(ПолучитьВерсию(), "8.3.8.0") >= 0 Тогда
// Применяем новые методы (например, ВыполнитьПакетСПромежуточнымиДанными)
// Обязательно через Выполнить() или в отдельном модуле!
КонецЕсли;
Разберем по шагам наиболее профессиональный метод решения проблемы совместимости. Вместо того чтобы загромождать одну форму условиями и методами Выполнить(), мы можем вынести платформозависимый код в разные общие модули.
РаботаСКонструкторомТолстый (в свойствах ставим только "Толстый клиент").РаботаСКонструкторомТонкий (в свойствах только "Тонкий клиент").Поскольку платформа компилирует только те модули, которые разрешены для текущего клиента, ошибка Тип не определен не возникнет в тонком клиенте, так как модуль для толстого клиента просто не будет загружен.
Важно учитывать, что в Веб-клиенте методы Выполнить() и Вычислить() могут быть заблокированы настройками безопасности браузера или самой платформы. Если ваша задача — обеспечить работу и в браузере, старайтесь максимально использовать серверный код. Выясним причину: на сервере доступен практически весь функционал платформы, и вы можете подготовить данные там, передав на клиент уже готовый результат (например, текст запроса), избегая создания визуальных объектов вроде КонструкторЗапроса непосредственно в коде клиента.
Подведем итог. Для создания универсального кода, работающего на разных версиях 1С, мы рекомендуем следующий алгоритм:
Применяя эти методы, мы обеспечиваем гибкость и долговечность наших программных решений, делая их устойчивыми к обновлениям платформы 1С.