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

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

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

Особую остроту проблеме придает невозможность создания клиентских процедур в МодулеОбъекта. Весь код модуля объекта всегда исполняется на сервере, а взаимодействие с локальными ресурсами пользователя (например, работа с COM-объектами MS Word или Excel) требует исполнения кода именно на стороне клиента. В этом материале мы разберем, как избежать дублирования кода и организовать «единое ядро» для клиентской логики во внешнем отчете.

Анализ проблемы: почему нельзя просто перенести код?

Разберем ситуацию: у нас есть внешний отчет, в котором реализовано формирование печатной формы через COM-объект Word. Этот код должен выполняться на клиенте. Если мы создаем две формы (Обычную и Управляемую), нам приходится копировать один и тот же объемный блок кода в модули обеих форм. Это нарушает принцип DRY (Don't Repeat Yourself) и крайне усложняет поддержку: любое изменение в алгоритме формирования документа придется вносить дважды.

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

Решение 1: Использование метода Выполнить() и динамического кода

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

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

Рассмотрим пример реализации. В МодулеОбъекта создадим функцию:


Функция ПолучитьТекстПроцедурыПечати() Экспорт
    // Формируем текст кода. Важно удвоить внутренние кавычки 
    // и использовать вертикальную черту для многострочных строк.
    Текст = "
    |Отказ = Ложь;
    |Попытка
    |    MSWord = Новый COMОбъект(""Word.Application"");
    |Исключение
    |    Сообщить(""Ошибка запуска Word!"");
    |    Отказ = Истина;
    |КонецПопытки;
    |
    |Если Не Отказ Тогда
    |    Док = MSWord.Documents.Add();
    |    MSWord.Visible = Истина;
    |    // Дополнительная логика формирования документа...
    |    Состояние(""Документ сформирован успешно"");
    |КонецЕсли;
    |";
    Возврат Текст;
КонецФункции

Теперь выясним, как вызвать этот код из разных типов форм. В обычной форме (толстый клиент) вызов будет выглядеть максимально просто:


Процедура ДействиеСформировать(Кнопка)
    ТекстКода = ЭтотОбъект.ПолучитьТекстПроцедурыПечати();
    Выполнить(ТекстКода);
КонецПроцедуры

В управляемой форме нам потребуется сначала получить объект отчета на сервере, а затем выполнить полученную строку на клиенте:


&НаСервере
Функция ПолучитьКодССервера()
    // Преобразуем данные формы в объект для доступа к модулю
    ОбъектОтчет = РеквизитФормыВЗначение("Объект");
    Возврат ОбъектОтчет.ПолучитьТекстПроцедурыПечати();
КонецФункции

&НаКлиенте
Процедура СформироватьДокумент(Команда)
    ТекстКода = ПолучитьКодССервера();
    Выполнить(ТекстКода);
КонецПроцедуры

Важный нюанс: метод Выполнить() не поддерживает оператор Возврат. Если в вашем исходном коде были выходы из процедуры по условию, их необходимо заменить на структуру Если...Тогда...КонецЕсли или использовать флаги (как показано в примере с переменной Отказ).

Решение 2: Использование макета «Текстовый документ»

Посмотрим на более удобный вариант хранения кода. Если писать код внутри строковой переменной в модуле, мы теряем подсветку синтаксиса и проверку ошибок. Чтобы этого избежать, можно использовать Макет типа «Текстовый документ».

  1. Создадим в отчете макет с именем БиблиотекаКлиентскогоКода.
  2. Скопируем туда чистый код 1С (без лишних кавычек и вертикальных черт).
  3. В модуле объекта напишем функцию для чтения этого макета:

Функция ПолучитьТекстИзМакета() Экспорт
    Макет = ЭтотОбъект.ПолучитьМакет("БиблиотекаКлиентскогоКода");
    Возврат Макет.ПолучитьТекст();
КонецФункции

Теперь при вызове Выполнить() мы будем получать структурированный и легко читаемый код. Это значительно упрощает отладку, так как вы можете просто скопировать текст из макета в любой модуль, проверить его на наличие ошибок и вернуть обратно.

Решение 3: Перенос логики на сервер (генерация DOCX)

Проанализируем ситуацию глубже. Если основной причиной использования клиентского кода является работа с MS Word через COM, то в современных конфигурациях от этого стоит отказаться в пользу серверной генерации документов.

Формат .docx представляет собой ZIP-архив с XML-файлами внутри. Платформа 1С позволяет работать с ZIP-архивами (ЗаписьZipФайла, ЧтениеZipФайла) и XML. Рассмотрим преимущества такого подхода:

На клиенте (в любой форме) останется лишь одна строка для открытия файла:

ПолучитьФайл(АдресХранилища, "Отчет.docx", Истина);

Рекомендации по передаче контекста

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

Чтобы сделать код универсальным, рекомендуем использовать структуру параметров. Рассмотрим пример:


// В вызывающей форме
ПараметрыСкрипта = Новый Структура;
ПараметрыСкрипта.Вставить("ДатаОтчета", Объект.Дата);
ПараметрыСкрипта.Вставить("СписокТоваров", Объект.Товары.Выгрузить());

ТекстКода = "
|Для Каждого Строка Из ПараметрыСкрипта.СписокТоваров Цикл
|    Сообщить(Строка.Наименование + "" на дату "" + ПараметрыСкрипта.ДатаОтчета);
|КонецЦикла;
|";

Выполнить(ТекстКода);

Таким образом, мы обеспечиваем чистоту кода и минимизируем риск возникновения ошибок из-за отсутствия необходимых переменных в контексте формы.

Выводы

Мы рассмотрели несколько способов организации общего клиентского кода во внешнем отчете. Выбор конкретного метода зависит от сложности задачи:

  1. Для простых манипуляций с интерфейсом или COM-объектами достаточно использования макета с текстом кода и метода Выполнить().
  2. Для формирования сложных документов оптимальным является перенос логики на сервер с использованием прямой работы с форматами Office Open XML.
  3. При использовании динамического кода всегда уделяйте внимание обработке исключений (блоки Попытка...Исключение), так как отладка такого кода затруднена.

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

← На главную