Как динамически вывести данные нескольких сотрудников в макет Word с сохранением форматирования?

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

При разработке внешних обработок и отчетов в 1С часто возникает задача вывода печатных форм в формате Microsoft Word (документы .doc или .docx). Если макет имеет фиксированную структуру, задача решается простой заменой параметров-маркеров. Однако ситуация усложняется, когда в документ необходимо вывести динамический список (например, список сотрудников в приказе), количество элементов в котором заранее неизвестно, причем для разных частей текста требуется сохранить разное оформление (выделение дат, ФИО или должностей жирным шрифтом).

В этой статье мы подробно разберем, как решить эту задачу. Мы проанализируем различные подходы — от классического использования COM-объектов (на примере реального кейса с форума) до современных кроссплатформенных стандартов с использованием Библиотеки стандартных подсистем (БСП).

Постановка задачи и анализ проблемы

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

1. С [ДатаНачала] по [ДатаОкончания] поручить [ЗамещающийСотрудник] исполнение обязанностей [ЗамещаемыйСотрудник]...

Если в обработке выбран один сотрудник, в документе должен появиться один пункт. Если сотрудников несколько — пункты должны идти друг за другом по порядку, с автонумерацией. Главная сложность заключается в том, что внутри одного абзаца используется разное оформление текста: даты и ФИО сотрудников должны быть выделены жирным шрифтом, а связующий текст — обычным. Простая замена текста по всему документу здесь не сработает, так как при вставке нового текста оформление может "слететь", а структура документа превратиться в хаотичный набор символов.

Вариант 1. Динамическое построение текста через COM-соединение

Рассмотрим решение, основанное на работе с COM-объектом Word.Application. Этот метод применим, если база 1С работает под управлением операционной системы Windows и на клиентском компьютере (или сервере) установлен пакет Microsoft Office.

Идея решения заключается в использовании одной опорной закладки (Bookmark) в шаблоне Word, которая указывает на место начала вывода списка. В цикле по сотрудникам мы позиционируемся на эту закладку, пишем форматированный текст частями, динамически создаем новые временные закладки для позиционирования курсора и сдвигаем рабочую область вниз.

Разберем типичную ошибку позиционирования, с которой часто сталкиваются разработчики на начальном этапе. Посмотрим на пример кода, который приводит к неверному результату ("смешиванию" текста):


// НЕПРАВИЛЬНЫЙ ПОДХОД — приводит к "каше" в итоговом документе
Для Каждого Параметр Из СтруктураПараметров.Значение Цикл
    ТекстСтрокиПриказа = Строка(Параметр.Номер) + ". С [ДатаНачала] по [ДатаОкончания] поручить [ЗамещающийСотрудникДолжностьдп]...";
    СсылкаМакет.Bookmarks("ТекстСтрокиПриказа").Select();
    СсылкаМакет.Application.Selection.TypeText(ТекстСтрокиПриказа);
    
    // Попытка создать закладки поверх уже вставленного текста
    СсылкаМакет.Bookmarks.Add("ДатаНачала", Word.Application.Selection.Range());
    СсылкаМакет.Bookmarks("ДатаНачала").Select();
    СсылкаМакет.Application.Selection.BoldRun();
    СсылкаМакет.Application.Selection.TypeText(Параметр.ДатаНачала);
    // ... дальнейший ввод текста ломает структуру и смещает координаты курсора
КонецЦикла;

Выясним причину этой ошибки. Метод TypeText() вставляет текст в текущую позицию курсора, смещая его в конец вставки. Если мы пытаемся вставить весь текст абзаца сразу, а затем программно искать внутри него места для замены параметров, координаты закладок сбиваются, и последующие вставки накладываются друг на друга, образуя нечитаемую смесь шрифтов и слов.

Чтобы избежать этой проблемы, необходимо выводить текст строго последовательно, небольшими фрагментами, сразу применяя форматирование к каждому фрагменту и пересоздавая управляющую закладку в конце только что вставленного текста. Разберем рабочее пошаговое решение:


// РАБОЧИЙ ВАРИАНТ: Последовательный вывод с динамическим пересозданием закладки
Для Каждого Параметр Из СтруктураПараметров.Значение Цикл
    
    // 1. Позиционируемся на базовую закладку начала строки
    СсылкаМакет.Bookmarks("ТекстСтрокиПриказа").Select();
    
    // 2. Выводим номер пункта и статичное начало строки (обычный шрифт)
    ТекстСтрокиПриказа = Строка(Параметр.Номер) + ". С ";
    СсылкаМакет.Application.Selection.TypeText(ТекстСтрокиПриказа);
    
    // 3. Динамически создаем закладку для даты начала, выделяем ее жирным и заполняем
    СсылкаМакет.Bookmarks.Add("ДатаНачала", Word.Application.Selection.Range());
    СсылкаМакет.Bookmarks("ДатаНачала").Select();
    СсылкаМакет.Application.Selection.BoldRun(); // Включаем жирный шрифт
    СсылкаМакет.Application.Selection.TypeText(Параметр.ДатаНачала);
    
    // 4. Возвращаем обычный шрифт (или создаем новую закладку для связующего текста)
    СсылкаМакет.Bookmarks.Add("по", Word.Application.Selection.Range());
    СсылкаМакет.Bookmarks("по").Select();
    СсылкаМакет.Application.Selection.BoldRun(); // Вызов BoldRun переключает состояние (вкл/выкл)
    СсылкаМакет.Application.Selection.TypeText(" по ");
    
    // 5. Выводим дату окончания (жирный шрифт)
    СсылкаМакет.Bookmarks.Add("ДатаОкончания", Word.Application.Selection.Range());
    СсылкаМакет.Bookmarks("ДатаОкончания").Select();
    СсылкаМакет.Application.Selection.BoldRun();
    СсылкаМакет.Application.Selection.TypeText(Параметр.ДатаОкончания);
    
    // 6. Выводим текст " поручить " (обычный шрифт)
    СсылкаМакет.Bookmarks.Add("поручить", Word.Application.Selection.Range());
    СсылкаМакет.Bookmarks("поручить").Select();
    СсылкаМакет.Application.Selection.BoldRun();
    СсылкаМакет.Application.Selection.TypeText(" поручить ");
    
    // 7. Должность замещающего сотрудника (обычный шрифт)
    СсылкаМакет.Bookmarks.Add("ЗамещающийСотрудникДолжностьдп", Word.Application.Selection.Range());
    СсылкаМакет.Bookmarks("ЗамещающийСотрудникДолжностьдп").Select();
    СсылкаМакет.Application.Selection.TypeText(Параметр.ЗамещающийСотрудникДолжностьдп + " ");
    
    // 8. ФИО замещающего сотрудника (жирный шрифт)
    СсылкаМакет.Bookmarks.Add("ЗамещающийСотрудникФИОдп", Word.Application.Selection.Range());
    СсылкаМакет.Bookmarks("ЗамещающийСотрудникФИОдп").Select();
    СсылкаМакет.Application.Selection.BoldRun();
    СсылкаМакет.Application.Selection.TypeText(Параметр.ЗамещающийСотрудникФИОдп);
    
    // 9. Окончание фразы с переносом строки (обычный шрифт)
    СсылкаМакет.Bookmarks.Add("исполнениеобязанностей", Word.Application.Selection.Range());
    СсылкаМакет.Bookmarks("исполнениеобязанностей").Select();
    СсылкаМакет.Application.Selection.BoldRun();
    
    // Добавляем символ переноса строки в конце абзаца (Символы.ПС)
    СсылкаМакет.Bookmarks.Add("ЗамещаемыйСотрудникДолжностьрп", Word.Application.Selection.Range());
    СсылкаМакет.Bookmarks("ЗамещаемыйСотрудникДолжностьрп").Select();
    ТекстОкончания = Параметр.ЗамещаемыйСотрудникДолжностьрп 
        + " в течение установленной продолжительности рабочего дня наряду с работой, "
        + "определённой трудовым договором." + Символы.ПС;
    СсылкаМакет.Application.Selection.TypeText(ТекстОкончания);
    
    // КРИТИЧЕСКИ ВАЖНО: Пересоздаем базовую закладку в текущей точке курсора для следующей итерации цикла
    СсылкаМакет.Bookmarks.Add("ТекстСтрокиПриказа", Word.Application.Selection.Range());
    
КонецЦикла;

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

Вариант 2. Альтернативный COM-метод: Копирование шаблона строки

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

Чтобы упростить поддержку, мы можем применить метод копирования готового шаблона абзаца:

  1. В макете Word создается отдельный блок (абзац), который оформляется в соответствии со всеми требованиями (настраиваются шрифты, жирность, отступы). Этот блок помечается закладкой ШаблонСтроки.
  2. В месте вывода списка создается целевая закладка МестоВывода.
  3. В коде 1С мы находим область ШаблонСтроки, копируем ее в буфер обмена с помощью вызова метода Selection.Copy().
  4. Переходим к закладке МестоВывода, вставляем шаблон из буфера нужное количество раз с помощью метода Selection.Paste().
  5. После вставки всех строк мы производим замену текстовых маркеров (например, автозаменой подстрок [ДатаНачала] на реальные значения) в полученных абзацах.

Этот подход значительно чище, так как весь длинный текст и его стили остаются в самом макете Word, а код 1С отвечает только за тиражирование фрагмента и заполнение параметров.

Вариант 3. Современный подход: Использование БСП (Библиотека стандартных подсистем)

Использование COM-технологии накладывает жесткие ограничения на архитектуру решения: код не будет работать на серверах под управлением Linux, а также на клиентских компьютерах без установленного пакета MS Office (например, при использовании macOS или тонкого клиента в веб-браузере).

В современных конфигурациях 1С эту проблему решает подсистема "Печать" из состава БСП. Она позволяет выполнять печать документов формата .docx на стороне сервера без привлечения внешних COM-компонентов.

Разберем по шагам, как настроить такой вывод:

  1. Подготовка макета в Word: В шаблоне документа вы размечаете области с помощью специальных тегов БСП. Повторяющаяся область (строка для каждого сотрудника) заключается в квадратные скобки со служебными ключевыми словами:
    {v8 Область.СтрокаПриказа}
    {v8 НомерСтроки}. С {v8 ДатаНачала} по {v8 ДатаОкончания} поручить {v8 ЗамещающийСотрудникФИО}...
    {/v8 Область.СтрокаПриказа}
    При этом вы можете применить жирный шрифт к самим параметрам {v8 ДатаНачала} прямо в редакторе Word.
  2. Программное заполнение в 1С: Вместо сложного манипулирования курсором используется простой и понятный API БСП:

// Пример заполнения макета DOCX через БСП
ДанныеОбъекта = Новый Структура; // Здесь собираются данные для шапки документа

// Инициализируем макет, сохраненный как двоичные данные
МакетИнфо = УправлениеПечатью.ИнициализироватьМакетОфисногоДокумента(ПолучитьМакет("МакетПриказа"), "DOCX");
ДокументИнфо = УправлениеПечатью.ИнициализироватьПечатнуюФорму("DOCX", МакетИнfo.Параметры);

// Заполняем шапку документа
УправлениеПечатью.ЗаполнитьПараметрыАдресации(ДокументИнфо.Параметры, ДанныеОбъекта);

// Выводим динамические строки в цикле
Для Каждого СтрокаСотрудник Из ТаблицаСотрудников Цикл
    
    // Получаем область строки приказа из макета
    ОбластьСтроки = УправлениеПечатью.ОбластьМакета(МакетИнфо, "СтрокаПриказа");
    
    // Заполняем параметры конкретной строки
    ОбластьСтроки.Параметры.Вставить("НомерСтроки", СтрокаСотрудник.Номер);
    ОбластьСтроки.Параметры.Вставить("ДатаНачала", Формат(СтрокаСотрудник.ДатаНачала, "ДФ=dd.MM.yyyy"));
    ОбластьСтроки.Параметры.Вставить("ДатаОкончания", Формат(СтрокаСотрудник.ДатаОкончания, "ДФ=dd.MM.yyyy"));
    ОбластьСтроки.Параметры.Вставить("ЗамещающийСотрудникФИО", СтрокаСотрудник.ФИО);
    
    // Присоединяем заполненную область к итоговому документу
    УправлениеПечатью.ПрисоединитьОбласть(ДокументИнфо, ОбластьСтроки);
    
КонецЦикла;

// Получаем готовый двоичный файл для сохранения или отправки на печать
ДвоичныеДанныеФайла = УправлениеПечатью.СформироватьДокумент(ДокументИнфо);
УправлениеПечатью.ОчиститьВременныеФайлы(МакетИнфо);

Преимущества использования БСП:

Сравнительный анализ методов

Проанализируем все рассмотренные варианты, чтобы выбрать оптимальный для вашей задачи:

Критерий сравнения COM-соединение (Вариант 1) Копирование шаблона (Вариант 2) Использование БСП (Вариант 3)
Зависимость от MS Office Да (обязателен на клиенте/сервере) Да (обязателен на клиенте/сервере) Нет (работает автономно)
Кроссплатформенность Нет (только Windows) Нет (только Windows) Да (Windows, Linux, macOS)
Скорость работы Низкая (медленный вызов методов COM) Средняя Высокая (прямая работа с архивом DOCX)
Удобство изменения макета Крайне низкое (код жестко завязан на фразы) Высокое (изменения вносятся в Word) Очень высокое (удобная разметка тегами)

Подводя итог, можно сделать вывод: если вы поддерживаете старую конфигурацию 1С и вынуждены работать через COM-соединение, используйте последовательный вывод по закладкам с обязательным пересозданием базовой закладки на каждой итерации (как показано в Варианте 1), либо метод копирования областей. Для всех современных конфигураций на платформе 1С 8.3 настоятельно рекомендуется использовать стандартные механизмы печати Библиотеки стандартных подсистем (БСП).

← На главную