В практике интеграции систем на платформе 1С:Предприятие часто возникает задача: сформировать печатную форму в одной информационной базе (например, в старой и нетиповой или, наоборот, в закрытой типовой конфигурации) и отобразить её в другой, современной базе на управляемых формах. При этом напрямую передать объект типа ТабличныйДокумент через COM-соединение невозможно, так как вызывающий процесс получит лишь COM-объект, с которым нельзя полноценно работать на клиенте базы-приемника.
В данной статье мы подробно разберем, как решить эту задачу, проанализируем основные проблемы, с которыми вы можете столкнуться, и рассмотрим три практических способа реализации: от простой сериализации до создания надежного моста через внешнюю обработку.
COM-соединение (с использованием объекта V83.COMConnector) накладывает жесткие ограничения на типы передаваемых данных. Напрямую между базами можно передавать только примитивные типы (числа, строки, даты, булево). Любые сложные объекты 1С (документы, справочники, табличные документы) передаются как ссылки на COM-объекты. Попытка вывести такой "чужой" табличный документ на форму в вашей базе приведет к ошибке отображения или полному отсутствию интерактивности.
Поэтому нам необходимо преобразовать табличный документ в формат, который можно легко передать в виде строки или файла, а затем воссоздать его в базе-приемнике.
Этот способ основан на преобразовании табличного документа в системную строку внутреннего формата 1С на стороне COM-сервера и последующем его восстановлении на стороне клиента.
Рассмотрим по шагам, как выглядит данный процесс в коде:
// Код на стороне базы-приемника (Управляемые формы)
&НаСервере
Функция ПолучитьТабличныйДокументЧерезCOMСериализацию(ПараметрыПодключения, ИдентификаторДокумента)
// 1. Устанавливаем COM-соединение с базой-источником
COMСоединение = Новый COMОбъект("V83.COMConnector");
Попытка
БазаИсточник = COMСоединение.Connect(ПараметрыПодключения);
Исключение
ВызватьИсключение "Не удалось подключиться к базе-источнику: " + ОписаниеОшибки();
КонецПопытки;
// 2. Находим нужный документ в базе-источнике (например, Реализацию товаров и услуг)
ДокументСсылкаCOM = БазаИсточник.Документы.РеализацияТоваровУслуг.НайтиПоРеквизиту("Номер", ИдентификаторДокумента);
Если ДокументСсылкаCOM.Пустая() Тогда
Возврат Неопределено;
КонецЕсли;
// 3. Формируем табличный документ на стороне источника и сериализуем его в строку.
// Используем системную функцию базы-источника через COM-объект.
СтрокаТабличногоДокумента = БазаИсточник.ЗначениеВСтрокуВнутр(
БазаИсточник.УчетНДС.ПечатьУниверсальногоПередаточногоДокумента(ДокументСсылкаCOM, Ложь)
);
// 4. Восстанавливаем табличный документ в локальной базе из полученной строки
ЛокальныйТабДок = ЗначениеИзСтрокиВнутр(СтрокаТабличногоДокумента);
Возврат ЛокальныйТабДок;
КонецФункции
РисунокТабличногоДокумента), сериализация через ЗначениеВСтрокуВнутр может отработать некорректно, потерять графические элементы или завершиться ошибкой преобразования.Чтобы избежать проблем с потерей структуры документа и корректно передать все элементы оформления (включая картинки), рекомендуется использовать выгрузку во временный файл родного формата 1С — MXL.
Проанализируем сценарий, когда обе базы (или серверы приложений) имеют доступ к общему сетевому ресурсу или работают на одном физическом сервере:
&НаСервере
Функция ПолучитьТабДокЧерезФайл(ПараметрыПодключения, ИдентификаторДокумента)
COMСоединение = Новый COMОбъект("V83.COMConnector");
БазаИсточник = COMСоединение.Connect(ПараметрыПодключения);
ДокументСсылкаCOM = БазаИсточник.Документы.РеализацияТоваровУслуг.НайтиПоРеквизиту("Номер", ИдентификаторДокумента);
// Вызываем формирование печатной формы на стороне источника
ТабДокCOM = БазаИсточник.УчетНДС.ПечатьУниверсальногоПередаточногоДокумента(ДокументСсылкаCOM, Ложь);
// Формируем путь к временному файлу, доступному для обоих серверов
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("mxl");
// Записываем табличный документ в файл на стороне COM-соединения
ТабДокCOM.Записать(ИмяВременногоФайла, БазаИсточник.ТипФайлаТабличногоДокумента.MXL);
// Читаем файл в локальный табличный документ
ЛокальныйТабДок = Новый ТабличныйДокумент;
ЛокальныйТабДок.Прочитать(ИмяВременногоФайла);
// Удаляем временный файл после использования
Попытка
УдалитьФайлы(ИмяВременногоФайла);
Исключение
// Логируем ошибку удаления, если файл заблокирован процессом
КонецПопытки;
Возврат ЛокальныйТабДок;
КонецФункции
Если серверы приложений баз физически разделены и у них нет общей папки, мы можем передать файл, упаковав его в объект ДвоичныеДанные, который отлично сериализуется через COM-соединение:
// На стороне базы-источника (в COM) преобразуем файл в двоичные данные:
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.MXL);
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
// Передаем ДвоичныеДанные в базу-приемник через COM-границу.
// В приемнике записываем их обратно на диск и открываем:
ДвоичныеДанныеФайла.Записать(ЛокальныйПутьКФайлу);
ЛокальныйТабДок.Прочитать(ЛокальныйПутьКФайлу);
Часто при попытке вызвать типовой метод печати (например, формирование УПД или ТОРГ-12) напрямую из общего модуля конфигурации-источника через COM, разработчики сталкиваются с ошибкой: "Метод объекта не обнаружен" или "Интерактивные операции недоступны".
Выясним причину такого поведения системы:
V83.COMConnector создается сеанс типа Внешнее соединение. В этом сеансе недоступны элементы клиентского контекста.#Если Клиент Тогда или обращаются к функциям управления принтером, выводу диалоговых окон выбора параметров печати. При вызове из внешнего соединения эти участки кода вызывают фатальные ошибки выполнения.Чтобы обойти ограничения контекста и не вносить изменения в типовую конфигурацию источника (которая должна оставаться на поддержке), применим архитектурный паттерн "Обработка-мост".
Суть метода заключается в создании внешней обработки (с расширением .epf), которая будет выступать в роли изолированного контейнера. Мы поместим её на диск сервера базы-источника. Внутри обработки мы напишем безопасный код формирования печатной формы, который гарантированно не вызовет клиентских методов.
Создадим внешнюю обработку в базе-источнике и напишем в модуле её объекта следующий экспортный метод:
Функция СформироватьИПолучитьУПД(ДокументСсылка, ПутьДляСохранения) Экспорт
// Здесь мы воссоздаем вызовы формирования печатной формы.
// Если в типовом коде были клиентские вызовы, мы обходим их вручную,
// подготавливая только структуру данных для печати.
ТабДок = Новый ТабличныйДокумент;
// Например, вызываем серверную функцию формирования макета
ДанныеДляПечати = УчетНДС.ПолучитьДанныеДляПечатиУПД(ДокументСсылка);
УчетНДС.ВывестиУПДВТабличныйДокумент(ТабДок, ДанныеДляПечати);
// Записываем полученный результат во временный файл на сервере источника
ТабДок.Записать(ПутьДляСохранения, ТипФайлаТабличногоДокумента.MXL);
Возврат Истина;
КонецФункции
Теперь в базе-приемнике мы программно подключаемся к источнику, открываем внешнюю обработку, передаем ей параметры и получаем готовый файл:
&НаСервере
Функция ПолучитьПечатнуюФормуЧерезОбработкуМост(ПараметрыПодключения, НомерДокумента, ДатаДокумента)
// Подключаемся к COM
COMСоединение = Новый COMОбъект("V83.COMConnector");
БазаИсточник = COMСоединение.Connect(ПараметрыПодключения);
// Находим документ-источник
ДокСсылкаCOM = БазаИсточник.Документы.РеализацияТоваровУслуг.НайтиПоНомеру(НомерДокумента, ДатаДокумента);
Если ДокСсылкаCOM.Пустая() Тогда
Возврат Неопределено;
КонецЕсли;
// Определяем пути к файлам.
// Предполагаем, что у серверов есть доступ к общей сетевой папке.
ПутьКШаблонуОбработки = "\\Server\Share\Bin\ПечатьУПДчерезCOM.epf";
ПутьКВременномуMXL = "\\Server\Share\Temp\temp_upd.mxl";
// Программно подключаем внешнюю обработку в сеансе COM
ОбработкаМост = БазаИсточник.ВнешниеОбработки.Создать(ПутьКШаблонуОбработки);
// Вызываем экспортную функцию обработки
РезультатВыполнения = ОбработкаМост.СформироватьИПолучитьУПД(ДокСсылкаCOM, ПутьКВременномуMXL);
ЛокальныйТабДок = Новый ТабличныйДокумент;
Если РезультатВыполнения Тогда
// Читаем сформированный файл в нашей локальной базе
ЛокальныйТабДок.Прочитать(ПутьКВременномуMXL);
// Удаляем временный файл с диска
Попытка
УдалитьФайлы(ПутьКВременномуMXL);
Исключение
КонецПопытки;
КонецЕсли;
Возврат ЛокальныйТабДок;
КонецФункции
.epf, не затрагивая конфигурацию приемника и источника.