При формировании печатных форм договоров, коммерческих предложений и соглашений разработчики часто сталкиваются с необходимостью сохранить сложное исходное форматирование документа. Переносить многостраничный текст в табличный документ 1С вручную долго и трудоемко, а макет типа ActiveDocument нередко вызывает трудности при программном заполнении и может работать медленно. Разберем по шагам, как правильно подготовить шаблон в Microsoft Word, загрузить его в конфигурацию 1С, быстро заполнить реквизитами из базы данных и отправить на печать.
Прежде чем приступать к написанию кода в 1С, подготовим файл шаблона. Существует два основных способа разметки мест для вставки данных:
[НомерДоговора], [ДатаДоговора], [НаименованиеКонтрагента]. Этот способ наиболее прост и нагляден.DocVariable с уникальным именем. Значение такого поля можно изменять напрямую через коллекцию переменных объекта Word.Макет типа ActiveDocument напрямую интегрируется с COM-сервером Word при обращении через метод Получить(). Однако такой подход часто приводит к длительным задержкам инициализации интерфейса и зависаниям. Оптимальным решением является использование макета с типом Двоичные данные или хранение файла в реквизите с типом ХранилищеЗначения.
Выясним причину ошибки: объект ДвоичныеДанные не имеет метода Получить(). Чтобы открыть такой документ в Word, его необходимо сначала выгрузить во временный файл на диске с помощью метода Записать().
Рассмотрим классический и надежный алгоритм: извлекаем двоичные данные шаблона, сохраняем их во временный файл, открываем файл в фоновом режиме через COMОбъект("Word.Application"), заменяем ключевые текстовые метки на актуальные данные и отправляем документ на печать.
// 1. Получаем макет двоичных данных и записываем во временный файл
МакетДвоичныеДанные = ПолучитьМакет("МакетДоговор");
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("docx");
МакетДвоичныеДанные.Записать(ИмяВременногоФайла);
// 2. Инициализируем COM-соединение с приложением Word
Попытка
MSWord = Новый COMОбъект("Word.Application");
Исключение
Сообщить("Не удалось запустить Microsoft Word: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Попытка
// Открываем документ в невидимом фоновом режиме
MSWord.Application.Visible = Ложь;
Документ = MSWord.Documents.Open(ИмяВременногоФайла);
// 3. Выполняем поиск и замену маркеров
Замена = Документ.Content.Find;
НаименованиеКонтрагента = "ООО ""Торгуем всем""";
НомерДоговора = "№ 128/24";
// Заменяем номер договора
Замена.Execute("[НомерДоговора]", Ложь, Истина, Ложь, , , Истина, , Ложь, НомерДоговора, 2);
// Заменяем контрагента
Замена.Execute("[НаименованиеКонтрагента]", Ложь, Истина, Ложь, , , Истина, , Ложь, НаименованиеКонтрагента, 2);
// 4. Отправляем на печать или открываем пользователю
ПечататьНаПринтер = Истина;
Если ПечататьНаПринтер Тогда
// Отправка сразу на принтер по умолчанию
Документ.PrintOut();
// Даем время спулеру печати принять задание перед закрытием
Документ.Close(Ложь);
MSWord.Quit();
Иначе
// Отображаем заполненный документ пользователю
MSWord.Application.Visible = Истина;
MSWord.Activate();
КонецЕсли;
Исключение
Сообщить("Ошибка при обработке документа: " + ОписаниеОшибки());
MSWord.Quit(Ложь);
КонецПопытки;
// Удаляем временный файл при необходимости
УдалитьФайлы(ИмяВременногоФайла);
Обратите внимание на последний параметр метода Find.Execute — значение 2 (константа wdReplaceAll), которое предписывает заменить сразу все вхождения маркера в документе.
Проанализируем второй вариант, когда в шаблоне Word используются поля типа DocVariable. Этот подход удобен тем, что в шаблоне визуально не отображаются служебные скобки.
// Задаем значения переменным документа
Документ.Variables("Контрагент").Value = "ООО ""Торгуем всем""";
Документ.Variables("ИНН").Value = "7701234567";
Документ.Variables("Сумма").Value = "150 000 руб.";
// Обязательно обновляем поля документа, иначе новые значения не отобразятся на листах
Документ.Fields.Update();
Важный нюанс: при изменении свойства Variables("Имя").Value значение в тексте документа не перерисовывается само по себе. Обязательно вызывайте Документ.Fields.Update(), чтобы обновить все поля перед печатью.
Если в документе много реквизитов, сквозной поиск по всему тексту методом Find.Execute может занять ощутимое время. Быстрее всего работает прямая подстановка в предварительно расставленные закладки:
Если Документ.Bookmarks.Exists("Контрагент") Тогда
Документ.Bookmarks("Контрагент").Range.Text = "ООО ""Торгуем всем""";
КонецЕсли;
Разберем типичные проблемы, упомянутые в обсуждении, и способы их решения:
Activate() или интерактивный объект Selection. Если другое приложение перехватывает фокус операционной системы, интерактивная работа завершается аварийно. Для стабильной работы в фоне следует манипулировать объектом Range (например, Документ.Content) и избегать вызова Activate() до момента, пока документ не сформирован окончательно.Find.Execute в Word COM не принимает строковые параметры длиной более 255 символов. Если реквизит (например, полный юридический адрес с реквизитами банка) превышает этот лимит, вставляйте его через закладки или разбивайте на несколько параметров.Исключение всегда вызывайте MSWord.Quit(Ложь), чтобы в диспетчере задач Windows не накапливались зависшие фоновые процессы WINWORD.EXE.