Метод ПолучитьМакет работает только на сервере. Методы работы или заполнения Word-а работают как на клиенте, так и на сервере. Показывать пользователю документ (Visible = Истина; Activate();) нужно на клиенте. Получается, нам нужно получить документ на сервере и передать его на клиент, чтобы показать. В данной статье будет рассмотрено 2 способа передачи с сервера на клиент. 1 способ для ActiveDocument, второй для ДвоичныеДанные.
Немного об ActiveDocument
Что пишет о ActiveDocument 1С:
Технология ActiveDocument предназначена для редактирования документов внешними по отношению к 1С:Предприятию 8 редакторами.
Эта технология позволяет редактировать документы визуально (например, Word или Excel) непосредственно в окне 1С:Предприятия 8, при этом элементы пользовательского интерфейса (меню, панели команд и т.д.) заменяются на предоставляемые редактором. Документы могут быть предварительно отредактированы и сохранены в макетах конфигурации, а затем макеты могут использоваться пользователями как основы для создания окончательных версий документов.
Следует заметить, что использование макетов ActiveDocument в режиме Предприятия осуществляется только программно - визуальное редактирование возможно только на этапе создания и редактирования конфигурации.
Эта технология применяется в случае, когда в конфигурации необходимо хранить данные, редактируемые другим приложением - такие, например, как шаблоны для факсов или деловых писем, созданные в Microsoft Word, или шаблоны прайс-листов в Microsoft Excel. Такая необходимость возникает, как правило, при регламентировании формата документов (как во внутреннем документообороте, так и при обмене документами со сторонними организациями и клиентами), однако при отсутствии ограничений на формат документа рекомендуется использовать существующие в 1С:Предприятии 8 возможности по оформлению электронных и печатных документов.
Другими словами: в ДвоичныеДанные вы можете только «Загрузить из файла» и «Выгрузить в файл»
А ActiveDocument можно прямо в конфигураторе редактировать в привычном интерфейсе Microsoft Word. Выглядит это так:
Я писал базу с нуля, где было много шаблонов Word, которые постоянно приходилось переписывать. Было очень удобно в конфигураторе открывать фактически Word, редактировать, перезапускать отладку и смотреть, что получилось. Если у вас шаблон не так часто меняется, то можно в принципе использовать «Двоичные данные».
Передача ActiveDocument с сервера на клиент
Просто получить ActiveDocument на сервере и передать его на клиент не получится. Но можно документ полностью заполнить на сервере, на клиент передать уже полностью готовый документ, и на клиенте его просто запустить.
Пример кода:
&НаКлиенте
Процедура ПечатьПисьмаНаКлиенте(Команда)
СтруктураНаПечать = ПечатьПисьмаНаСервереНаФорме(Объект.Ссылка);
//Или СтруктураНаПечать = ПечатьПисьма(Объект.Ссылка);
ИмяВрем = ПолучитьИмяВременногоФайла(".docx");
СтруктураНаПечать.Записать(ИмяВрем);
ЗапуститьПриложение(ИмяВрем,,,);
//Или если у вас несколько документов то цикл
//Для Каждого СтруктураПараметров из СтруктураНаПечать Цикл
КонецПроцедуры
//Если функция ПечатьПисьма в модуле менеджера то нужно как-то к ней обратиться
&НаСервере
Функция ПечатьПисьмаНаСервереНаФорме(МассивОбъектов)
Возврат Документы.ПисьмоНаОплату.ПечатьПисьма(МассивОбъектов);
КонецФункции
&НаСервере
Функция ПечатьПисьма(МассивОбъектов) Экспорт
//Возможно какой-то запрос
//Пока Выборка.Следующий() Цикл
//Каким-либо образом получаем макет Active Document
//Если код в модуле менеджера то нужен код:
Макет = ПолучитьМакет("ПисьмоНаОплатуWord");
MSWord = Макет.Получить();
Попытка
Документ = MSWord.Application.Documents(1);
Документ.Activate();
//Далее каким-либо образом получаем данные и заполняем Word-овский документ
//Например
Selection = Документ.Content;
Selection.Find.Replacement.Font.Size = 14;
Selection.Find.Execute("[Название]",Истина,Истина,Ложь,,,Истина,,Истина,Выборка.НаименованиеПолное,2);
//Получим путь во временной директории для сохранения туда файла
ИмяВрем = ПолучитьИмяВременногоФайла(".docx");
//Сохраним туда файл
Документ.SaveAs(ИмяВрем);
//Преобразуем файл в двоичные данные для передачи на клиент
МойДокументВДвоичныхДанных = Новый ДвоичныеДанные(ИмяВрем);
//Если код исполняется в цикле можно заполнить структуру:
//СтруктураВсехДокументов.Вставить("Номер"+Строка(НомерПП),МойДокументВДвоичныхДанных);
//Закроем Word
MSWord.Application.Quit();
//УдалитьФайлы(ИмяВрем);
Исключение
// Если произойдет ошибка, выводятся данные об ошибке
// и объект закрывается.
Сообщение = Новый СообщениеПользователю();
Сообщение.Текст = ОписаниеОшибки();
Сообщение.Сообщить();
Если MSWord<>Неопределено Тогда
MSWord.Application.Quit();
КонецЕсли;
КонецПопытки;
//КонецЦикла;
Возврат МойДокументВДвоичныхДанных;
//Возврат СтруктураВсехДокументов;
КонецФункции // ПечатьПисьма()
При использовании этого кода на сервере должен быть установлен офисный пакет. Представленный код является шаблоном (примером) для дальнейшего программирования. Краткий анализ кода:
1. НаКлиенте мы просто получаем файл в виде двоичных данных с сервера, записываем во временную папку и открываем оттуда файл.
2. НаСервере мы получаем макет, его заполняем (дан пример заполнения через метод Find. Execute), сохраняем во временную папку (это будет папка на сервере вида: C:\Users\<Имя пользователя, под которым запущена служба 1С>\AppData\Local\Temp\v8_F0FB_7f3.docx), переводим файл в двоичные данные и передаём на клиент. Для сохранения документа можно использовать методы SaveAs, Save, SaveAs2, SaveAs2000.
В справке к методу ПолучитьИмяВременногоФайла написано:
Рекомендуется удалять временный файл самостоятельно после его использования, т.к. при активном создании файлов временные файлы могут занять значительную часть дискового пространства раньше, чем произойдет перезапуск платформы.
Данная рекомендация особенно актуальна для кода, исполняемого на сервере, так как регламент его эксплуатации может не предполагать регулярного перезапуска.
Опытном путём выявлено, что 1С-ка вырубается с ошибкой, если удалить файл, преобразованный в двоичные данные, и попробовать эти двоичные передать на клиент.
На сервере 1С:Предприятия произошла неисправимая ошибка. Приложение будет закрыто
Я пытался побороть эту ошибку, помещая во временное хранилище двоичные данные, переводя двоичные данные в Неопределено, но победить не удалось. Стоит заметить, что временный файл на сервере 1С-ка со временем сама удаляет корректно.
Возможная ошибка на сервере при вызове метода SaveAs:
Ошибка при вызове метода контекста (SaveAs)
Документ.SaveAs(ИмяВрем);
по причине:
Произошла исключительная ситуация (Microsoft Word): Ошибка команды
Мне поначалу эту ошибку не удалось победить, поэтому я стал использовать ДвоичныеДанные. Позже нашёл решение проблемы: необходимо по пути C:\Windows\SysWOW64\config\systemprofile\ и C:\Windows\System32\config\systemprofile\ создать папки Desktop. Туда никаких файлов никто не пишет, похоже, программе важен факт наличия этой папки. Решение нашёл по ссылке: http://devtrainingforum.v8.1c.ru/forum/thread.jsp?id=581998&threadtype=0
Спасибо огромное.
Создание папок
C:\Windows\SysWOW64\config\systemprofile\Desktop
C:\Windows\System32\config\systemprofile\Desktop
проблему решило. Тема закрыта.
Передача макет Word (Двоичные данные) с сервера на клиент
В случае передачи двоичных данных представлю практически весь код.
//Код в модуле менеджера
// Заполняет список команд печати.
//
// Параметры:
// КомандыПечати - ТаблицаЗначений - состав полей см. в функции УправлениеПечатью.СоздатьКоллекциюКомандПечати
//
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
// Письмо на оплату
КомандаПечати = КомандыПечати.Добавить();
КомандаПечати.МенеджерПечати = "Документ.ПисьмоНаОплату";
КомандаПечати.Идентификатор = "ПисьмоНаОплату";
КомандаПечати.Представление = НСтр("ru = 'Письмо на оплату'");
КомандаПечати = КомандыПечати.Добавить();
КомандаПечати.МенеджерПечати = "Документ.ПисьмоНаОплату";
КомандаПечати.Идентификатор = "ПисьмоНаОплатуWord";
КомандаПечати.Представление = НСтр("ru = 'Письмо на оплату (Word)'");
КомандаПечати.СписокФорм = "ФормаДокумента";
КонецПроцедуры
Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
ПараметрыВывода.ДоступнаПечатьПоКомплектно = Истина;
Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "ПисьмоНаОплату") Тогда
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(КоллекцияПечатныхФорм,"ПисьмоНаОплату" ,"Письмо на оплату" ,
ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Ложь),,"Документ.ПисьмоНаОплату.ПФ_MXL_ПисьмоНаОплату");
ИначеЕсли УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "ПисьмоНаОплатуWord") Тогда
КоллекцияПечатныхФорм[0].ТабличныйДокумент = Новый ТабличныйДокумент;
ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Истина)
КонецЕсли;
КонецПроцедуры
// Формирует печатную форму письма
//
// Параметры:
//
//
Функция ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Word) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
//Какой-то текст
Выборка = Запрос.Выполнить().Выбрать();
ПервыйДокумент = Истина;
СтруктураВсехДокументов = Новый Структура;
НомерПП = 0;
Пока Выборка.Следующий() Цикл
НомерПП = НомерПП+1;
//...
Если НЕ ПервыйДокумент Тогда
ТабДокумент.ВывестиГоризонтальныйРазделительСтраниц();
КонецЕсли;
СтруктураПараметров = Новый Структура;
//В переменную СтруктураПараметров попадают все значения которые надо вывести на печать
//...
Если Word Тогда
Для Каждого Параметр из СтруктураПараметров Цикл
СтруктураПараметров.Вставить(Параметр.Ключ,СтрЗаменить(Параметр.Значение,Символы.Таб,""));
КонецЦикла;
СтруктураВсехДокументов.Вставить("Номер"+Строка(НомерПП),СтруктураПараметров);
Иначе
// тут должен быть код для обычного макета.
КонецЕсли;
КонецЦикла;
Если Word Тогда
//Помещать во временное хранилище не обязательно
Возврат ПоместитьВоВременноеХранилище(СтруктураВсехДокументов);
Иначе
Возврат ТабДокумент;
КонецЕсли;
КонецФункции // ПечатьПисьма()
//Код на форме:
&НаКлиенте
Процедура Подключаемый_ВыполнитьКомандуПечати(Команда)
ОписаниеКоманды = УправлениеПечатьюКлиентПовтИсп.ОписаниеКомандыПечати(Команда.Имя, ЭтаФорма.Команды.Найти("АдресКомандПечатиВоВременномХранилище").Действие);
Если ОписаниеКоманды.Идентификатор = "ПисьмоНаОплатуWord" Тогда
Если Модифицированность Тогда
Если Не Записать() Тогда
Возврат;
КонецЕсли;
КонецЕсли;
СтруктураНаПечать = ПолучитьИзВременногоХранилища(ПечатьПисьма(Объект.Ссылка,Неопределено,Истина));
Для Каждого СтруктураПараметров из СтруктураНаПечать Цикл
ДвоичныеДанныеМакета = ПолучитьИзВременногоХранилища(ПолучитьМакетСКлиента("ПисьмоНаОплатуWord"));
ИмяВрем = ПолучитьИмяВременногоФайла(".docx");
ДвоичныеДанныеМакета.Записать(ИмяВрем);
Попытка
Документ = ПолучитьCOMОбъект(ИмяВрем);
Для Каждого Параметр из СтруктураПараметров.Значение Цикл
//В оригинале тут другой код, но для простаты пример:
Selection = Документ.Content;
Selection.Find.Execute("["+Параметр.Ключ+"]",Ложь,Истина,Ложь,,,Истина,,Ложь,Параметр.Значение,2);
КонецЦикла;
Документ.Application.Visible = Истина;
Документ.Application.WindowState = 2;
Документ.Application.WindowState = 1;
Документ.Activate();
Исключение
// Если произойдет ошибка, выводятся данные об ошибке
// и объект закрывается.
Сообщение = Новый СообщениеПользователю();
Сообщение.Текст = ОписаниеОшибки();
Сообщение.Сообщить();
Документ.Application.Quit();
КонецПопытки;
КонецЦикла;
Иначе
УправлениеПечатьюКлиент.ВыполнитьПодключаемуюКомандуПечати(Команда, ЭтаФорма, Объект);
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Word)
Возврат Документы.ПисьмоНаОплату.ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Word);
КонецФункции
&НаСервереБезКонтекста
Функция ПолучитьМакетСКлиента(Имя)
Возврат ПоместитьВоВременноеХранилище(Документы.ПисьмоНаОплату.ПолучитьМакет("ПисьмоНаОплатуWordДвоичныеДанные"));
КонецФункции
Краткий анализ кода:
Своим кодом я внедрился в СтандартныеПодсистемы.Печать. На форме документа 2 кнопки: «Письмо на оплату» (печать в табличный документ) и «Письмо на оплату (Word)» (печать в Word).
1. НаКлиенте на форме я получаю из модуля менеджера (функция ПечатьПисьма) структуру всех параметров. Далее получаю Word из макета (двоичные данные) и заполняю его на клиенте
2. Задача функции ПечатьПисьма модуля менеджера предназначена только для сбора сведений, которыми потом заполнится Word на клиенте.
Замечание: если вам адрес во временном хранилище нужен не сразу и единожды (как у меня), то корректней помещать во временное хранилище с уникальным идентификатором, дабы значение не удалилось пока не закрыли форму:
&НаСервере
Функция ПолучитьМакетСКлиента(Имя)
Возврат ПоместитьВоВременноеХранилище(Документы.ПисьмоНаОплату.ПолучитьМакет("ПисьмоНаОплатуWordДвоичныеДанные"), УникальныйИдентификатор);
КонецФункции
Альтернативы:
1. //infostart.ru/public/270277/ (ActiveDocument) - в данной статье рассмотрена возможность сохранить docx документ в общую папку на сервере. На клиент передаётся полный путь к файлу, инициализируется COM-объект, заполняется и показывается пользователю.
2. Можно сохранить файл Word в общую папку и с клиента его просто получать. Главное, чтобы файл никто не удалил.