При разработке внешних печатных форм (ВПФ) для 1С:Зарплата и управление персоналом 3.1 программисты часто сталкиваются с ситуацией, когда механизм выбора макета работает нестабильно. Типичный сценарий: в обработке предусмотрено несколько двоичных макетов (например, разные трудовые договоры для разных должностей), но при последовательной печати документов программа «запоминает» первый открытый макет и подставляет его во все последующие вызовы, игнорируя условия логики. Рассмотрим подробнее, почему это происходит и как правильно организовать работу с COM-объектом Word.Application.
Разберем фрагмент кода, который чаще всего становится источником ошибки. В сообщении автора темы мы видим следующую строку:
ДокументЗаполнение = Документ.Application.Documents(1);
Это классическая ловушка при работе с Microsoft Word через COM-соединение. Проанализируем ситуацию: когда вы обращаетесь к Documents(1), вы просите Word дать вам самый первый документ в коллекции открытых файлов. Если у пользователя на компьютере уже запущен Word с каким-то личным документом, то именно он будет иметь индекс 1. Ваша печатная форма начнет записывать данные (номера договоров, ФИО сотрудников) в этот сторонний файл, а свежесозданный макет останется пустым или вообще не будет задействован.
Решение: Никогда не используйте индексы для обращения к документу, который вы только что открыли. Метод Open() сам возвращает ссылку на объект документа. Правильный подход выглядит так:
// Вместо ДокументЗаполнение = Документ.Application.Documents(1);
// Используем объект, возвращенный методом Open
WordApp = Новый COMОбъект("Word.Application");
НовыйДокумент = WordApp.Documents.Open(ИмяВременногоФайла);
// Теперь работаем напрямую с переменной НовыйДокумент
НовыйДокумент.Variables.Item("ДоговорНомер").Value = "123";
Выясним причину, по которой макет может «залипать» даже при закрытии окна пользователем. В коде автора процедура СобратьМакет создает новый экземпляр Word.Application, но завершает процесс (метод Quit()) только в блоке Исключение. Если печать прошла успешно, процесс winword.exe может остаться висеть в памяти как фоновый процесс (так называемый «зомби-процесс»).
При следующем вызове печатной формы 1С может попытаться подключиться к уже существующему экземпляру Word, который всё еще держит в памяти старый макет или блокирует временные файлы в папке %TEMP%. Это приводит к тому, что новые данные либо не записываются, либо открывается копия старого файла из кэша.
Для предотвращения этой ситуации разберем правильный алгоритм завершения работы:
Word.Quit() в конце кода.В представленном коде используется стандартный метод ПолучитьИмяВременногоФайла("doc"). Рассмотрим, к чему это может привести. Если по какой-то причине файл не был удален после предыдущего сеанса печати (например, из-за блокировки зависшим процессом Word), 1С может столкнуться с конфликтом имен или открыть «автосохраненную» копию старого макета.
Проанализируем шаги для безопасной записи макета:
УдалитьФайлы(ИмяВременногоФайла) после того, как Word успешно открыл документ, или в самом конце процедуры (после закрытия Word).В функции ФормированиеДоговораПоШаблону автор обращается к реквизиту Объект.Документ.Должность. Посмотрим на этот момент с точки зрения архитектуры ВПФ. Если внешняя обработка открыта как форма, и пользователь выбирает разные документы в поле на форме, данные в Объект могут не обновляться автоматически.
При разработке ВПФ по стандартам БСП (Библиотека стандартных подсистем) рекомендуется получать данные объекта не из реквизитов формы, а по ссылке, переданной в массив объектов для печати. Рассмотрим пример правильного получения макета на сервере:
&НаСервере
Функция ПолучитьНужныйМакет(СсылкаНаОбъект)
// Получаем данные непосредственно из базы по ссылке
ДанныеДокумента = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаНаОбъект, "Должность, ГрафикРаботы");
Если ДанныеДокумента.Должность.Наименование = "Водитель" Тогда
ИмяМакета = "Водитель";
Иначе
ИмяМакета = "Общий";
КонецЕсли;
// Обращаемся к макету обработки
ЭтотОбъект = РеквизитФормыВЗначение("Объект");
АктивныйДокумент = ЭтотОбъект.ПолучитьМакет(ИмяМакета);
Возврат ПоместитьВоВременноеХранилище(АктивныйДокумент, ЭтаФорма.УникальныйИдентификатор);
КонецФункции
После того как мы заполнили DocVariables в Word, необходимо заставить приложение отобразить эти данные. Автор использует Документ.Fields.UpDate(). Важно помнить, что этот метод обновляет поля в основном теле документа, но может «забыть» про колонтитулы или надписи (TextBox).
Если ваши данные из 1С должны попасть в "шапку" или "подвал" документа Word, проанализируем расширенный метод обновления:
// Обновление полей во всех разделах документа (включая колонтитулы)
Для Каждого Раздел Из Документ.Sections Цикл
Раздел.Headers.Item(1).Range.Fields.Update(); // Верхний колонтитул
Раздел.Footers.Item(1).Range.Fields.Update(); // Нижний колонтитул
КонецЦикла;
Документ.Fields.Update(); // Основной текст
Подводя итог, можно сказать, что проблема автора была вызвана сочетанием некорректного обращения к коллекции документов Documents(1) и отсутствием контроля за жизненным циклом COM-объекта. Чтобы ваша печатная форма всегда выводила актуальный макет, придерживайтесь следующих правил:
Соблюдение этих принципов гарантирует стабильную работу интеграции 1С с офисными приложениями и избавит пользователей от путаницы в печатных формах.