Работа с Word из 1С. Работа с таблицами




Одно дело, когда заранее известно количество строк в таблице. И решение, соответственно, тоже простое: создать в шаблоне таблицу и заполнить переменные программно.
Но вот совсем другой подход, когда количество строк или колонок неизвестно.
В этом примере продемонстрировано, как можно создать табличную часть в документе Word.
Выбор, какой из перечисленных далее методов использовать, за Вами.

Одно дело, когда заранее известно количество строк в таблице. И решение, соответственно, тоже простое: создать в шаблоне таблицу и заполнить переменные программно. Ссылка на данный пример //infostart.ru/public/18940/

Но вот совсем другой подход, когда количество строк или колонок неизвестно.

История «Как я к этому пришел»

Появилась необходимость распечатывать договора по каждому контрагенту, а как известно договор может быть 1 или несколько.

Тогда, еще в 2006 году вопрос решился 5 шаблонами, в табличных частях которых было от 1 до 5 строк (ограничились 5 договорами). Вопрос решился и забылся. Но вот вопрос стал актуален по причине печати нового договора, в котором задействована номенклатура, а количество строк ведь может переваливать добрую сотню наименований. И следовательно  — выход в создании динамической таблицы в шаблоне Word, который и описан двумя самыми простыми способами.

Есть 2 подхода к созданию таблицы.

1) Вам нужна таблица с равной шириной колонок, так сказать созданная автоматически.

2) У вас есть шаблон таблицы (только шапка и 1 строка).

Код создания таблицы с автоформатом.

Процедура СоздатьПример1(Кнопка)
НовоеНапоминание = ПолучитьМакет("Пример1");

Массив = Новый Массив(5);//сделаем таблицу из 5 строк + шапка

MsWord = НовоеНапоминание.Получить();


//макет является ActiveDocwment с загруженым в него Word-файлом
Попытка
//Вставление таблицы с 10 строки
MsWord.Application.Visible = 0;
Word = MsWord.Application;
Док=Word.Documents(1);
Док.Activate();

//подготовка таблицы:
//Вариант №1 Самый простой
Word.Selection.MoveDown(,10); //10 строка - первая строка таблицы
//Конец Варианта №1

//Вариант №2
Word.Bookmarks("ff").Select(); //Где ff - это набор любых символов, вплоть до непечатаемых;)
//Конец Варианта №2

Табл= Word.Selection.Range;
//Формируем заголовок:
Табл.InsertAfter("Сумма*Валюта*Назначение*Дата долга*Вид документа*Номер документа* *Дата оплаты");
Табл.InsertParagraphAfter();
//сама таблица
Для й = 1 по Массив.Количество() Цикл
Табл.InsertAfter(массЗнчСтрокиТЧ[0]+"*"+массЗнчСтрокиТЧ[1]+"*"+массЗнчСтрокиТЧ[2]+"*"+массЗнчСтрокиТЧ[3]+"*"+ массЗнчСтрокиТЧ[4]+ "*"+массЗнчСтрокиТЧ[5]+ "*"+массЗнчСтрокиТЧ[6]+ "*"+массЗнчСтрокиТЧ[7]);
Табл.InsertParagraphAfter();
КонецЦикла;

Табл.ConvertToTable("*");

//Происходит конвертация строки в таблицу

//не нравятся звездочки - используйте другой символ
Док.Tables(1).AutoFormat(1);

ПечатныйНомер = "-"+Формат(ТекущаяДата(),"ДФ=yyyy.MM.dd")+"-"+Формат(ДатаОтчета,"ДФ=yyyy.MM.dd");
Файл = КаталогВременныхФайлов() + "" + ПечатныйНомер + ".doc";

Док.SaveAs(Файл);
Док.Close(0);

//МассивДокументов.Добавить(Файл);  //добавлял в массив путь к файлу и после всех манипуляций открывал каждый файл
ЗапуститьПриложение("""" + Файл + """");

Исключение
Сообщить(ОписаниеОшибки());
Word.Quit();
КонецПопытки;

КонецПроцедуры

Код создания таблицы с наследуемым форматом.

Процедура СоздатьПример2(Кнопка)
НовоеНапоминание = ПолучитьМакет("Пример2");

Массив = Новый Массив(5);//добавим 4 строки

MsWord = НовоеНапоминание.Получить();
//макет является ActiveDocwment с загруженым в него Word-файлом
Попытка

MsWord.Application.Visible = 0;
Word = MsWord.Application;
Док=Word.Documents(1);
Док.Activate();


Если Массив.Количество()>1 Тогда
//Переместиться вниз на 10 строк от начала документа
Word.Selection.MoveDown(,10); //10 строка - первая строка таблицы
//назад на 1 символ (окончание строки таблицы)
Word.Selection.MoveLeft(,1);
//кво документов которые необходимо забить в табличную часть
Word.Selection.InsertRows(Массив.Количество()-1);

Word.Selection.MoveLeft(,1);
Для й = 11 по Массив.Количество()+9 Цикл
ЗаполнитьСтрокуТЧ(0,Word);
Word.Selection.MoveRight(,1);
КонецЦикла;
КонецЕсли;

ПечатныйНомер = "-"+Формат(ТекущаяДата(),"ДФ=yyyy.MM.dd")+"-"+Формат(ДатаОтчета,"ДФ=yyyy.MM.dd");

//Таблицу заполнять не буду, только шапку

Замена = Док.Content.Find;
Замена.Execute("", Ложь, Истина, Ложь, , , Истина, , Ложь, "ООО 'Пупкин inc.'");

Замена = Док.Content.Find;
Замена.Execute("", Ложь, Истина, Ложь, , , Истина, , Ложь, Формат(ТекущаяДата(),"Л=uk; ДЛФ=D"));

Замена = Док.Content.Find;
Замена.Execute("", Ложь, Истина, Ложь, , , Истина, , Ложь, ПечатныйНомер );

Файл = КаталогВременныхФайлов() + "" + ПечатныйНомер + ".doc";

Док.SaveAs(Файл);
Док.Close(0);

//МассивДокументов.Добавить(Файл);  //добавлял в массив путь к файлу и после всех манипуляций открывал каждый файл
ЗапуститьПриложение("""" + Файл + """");
Исключение
Сообщить(ОписаниеОшибки());
Word.Quit();
КонецПопытки;

КонецПроцедуры

Процедура ЗаполнитьСтрокуТЧ(знчЭл,Word)

Если знчЭл массЗнчСтрокиТЧ.Количество()-1 Тогда
текЭлем = массЗнчСтрокиТЧ[знчЭл];
Rng = Word.Selection.Range;
Rng.InsertAfter(текЭлем);
ДлинаЗнч = СтрДлина(текЭлем)+1;

Word.Selection.MoveRight(,ДлинаЗнч);

Если знчЭл < массЗнчСтрокиТЧ.Количество()-1 Тогда
знчЭл = знчЭл + 1;
ЗаполнитьСтрокуТЧ(знчЭл,Word);
КонецЕсли;
КонецЕсли;

КонецПроцедуры


56 Comments

  1. JIGIT

    По возможности — пример…

    Заранее благодарен

    Reply ↓
  2. endym

    Мне не сложно;) будет вечерком.

    Reply ↓
  3. endym

    Добавил простенький примерчик. Жду комментов))))

    Reply ↓
  4. Evg-Lylyk

    Покрасьте код http://infostart.ru/public/19856/

    еще скриншоты бы не помешали

    Reply ↓
  5. endym

    Код подкрашу, а вот скриншоты к чему? это статья.

    Reply ↓
  6. Душелов

    (5) На статью не тянет.

    Reply ↓
  7. endym

    Это пример, часть работающего кода. Решил поделиться с теми, кому это может пригодиться. Зачем выпендриваться и придираться к словам? (6)

    Reply ↓
  8. Evg-Lylyk

    (7) Материал нормальный только пожалуйста оформите нормально

    Раскрасте с помощью обработки http://infostart.ru/public/19856/

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

    Reply ↓
  9. endym

    (8) я то и раскрасил вот той обработкой.

    а что показывать в скриншотах? листочек word? 😉 это же пример.. так сказать заготовка.

    Если уж так надо — могу сделать 4 скрина: 2 (по 1 на каждый вариант) «до» и «после».

    Причина почему я выложил — нигде толком не было объяснения как же быть в ситуации когда нужна таблица в договоре, а количество строк/столбцов варьируется.

    З.Ы. подобные ссылки видны справа.

    использовал материал команд VBA для Word.

    Reply ↓
  10. Душелов

    (7) На момент написания комментария кода не было.

    А было 5 строк текста и приложенная обработка — это больше к «программе» относилось, чем статье. И последите за словами, перед Вами никто не «выпендривался»

    Reply ↓
  11. endym

    (10) Изначально выложил только код.

    После попросили (1) пример, для чего прикрепил обработку.

    Reply ↓
  12. Поручик

    Автор, глянь на это

    http://infostart.ru/public/18940/

    В своё время здорово выручила.

    Reply ↓
  13. endym

    (12) видел, и что? это готовый шаблон, который надо заполнять ручками. а если не знать сколько реквизитов, т.е. выбирать через * все поля? как тогда?

    в данном примере(если можно так назвать) , повторюсь, описывается возможность создания строк в ЛЮБОМ МЕСТЕ даже не зная сколько строк выйдет всего.

    Если есть пожелания — выслушаю. ссылку добавлю в шапку.

    Reply ↓
  14. KVS

    Полезный пример. Спасибо

    Reply ↓
  15. Boroda

    Спасибо! Пример очень пригодится и в качестве ученого материала. и в качестве его практической реализации в отчётах, которых последнее время треуется всё больше и сложнее.

    Reply ↓
  16. Vovanich

    {ВнешняяОбработка.ПечатьАктаНаСписание.МодульОбъекта(225)}: Ошибка при вызове метода контекста (InsertRows): Произошла исключительная ситуация (Microsoft Word): Метод или свойство недоступны, поскольку некоторые или все объекты не ссылаются на таблицу. Как раз нужно сделать ВПФ Требования накладной в макете Ворд. Вставил твой код и вот такая ошибка.

    Reply ↓
  17. endym

    (16) Очень интересно. хотелось бы узнать как появилась такая ошибка. Можно по-подробнее?

    Reply ↓
  18. Vovanich

    Сорри…Вопрос снят таблицу в макете разместил не там где надо вот он и поругался.

    Reply ↓
  19. Vovanich

    Материал очень полезный, но пожалста можно привести пример кода который будет в создаваемую таблицу в макете запихивать данные из моей ТабЗнач.В общем задача такая сделать ВПФ списания материалов из документа Требование накладная. Запросом все данные получил нарисовал стандартный табличный макетик, но заказчик хочет чтоб в Ворде это все выводилось..Если не трудно пособите.

    Reply ↓
  20. endym

    (19)

    не трудно, надо только ваши исходники;))

    Reply ↓
  21. Vovanich

    куда обработку положить?или прям в сообщение код вставить?

    Reply ↓
  22. endym

    (21)весь в работе.

    Сегодня посмотрел обработку по печати Акта.

    А где Ваш макет word’овский? Ведь ресурс чтобы помогать а не делать за кого-то работу)

    Reply ↓
  23. kuz.mina

    Спасибо за материал! но у меня возникла проблема:

    В документе 5 листов, таблица на последнем(150 строка), но дальше чем на 44 строке(последняя строка первого листа) программа не видит таблицу:

    {ВнешняяОбработка.Договор_поставки.МодульОбъекта(363)}: Ошибка при вызове метода контекста (InsertRows): Произошла исключительная ситуация (Microsoft Word): Метод или свойство недоступны, поскольку некоторые или все объекты не ссылаются на таблицу.

    Как добавить таблицу на последний лист?

    Reply ↓
  24. endym

    (23)Как добавить таблицу на последний лист?

    Можно воспользоваться «шаблоном таблицы» или если используете таблицу с автоформатом — тогда перейти на последнюю страницу командой контрл+ енд

    Reply ↓
  25. kuz.mina

    (24) я как раз и делаю через шаблон. если таблица находится не на первой странице, программа ее не находит

    Reply ↓
  26. endym

    (25) Значит word не находит текст для замены, хоть на 99 странице будет текст в таблице.

    данный механизм успешно работает при заполнении договоров купли-продажи в 15 и более листов)

    Reply ↓
  27. stanru2

    Добрый день!

    Спасибо за полезный пример, мне он помог.

    По второй части хочу уточнить вот какой момент.

    Если документ ворд состоит из некоего текста и шаблона таблицы, и текст динамически генерирует одинэсом, то заранее неизвестно, в каком месте документа будет таблица. Поэтому использовать

    Word.Selection.MoveDown(,10); //10 строка — первая строка таблицы

    не получится. Предлагаю поставить в первой пустой ячейке таблицы закладку, назвать ее, к примеру, ff и использовать так:

    Word.Bookmarks(«ff»).Select();

    ЗЫ и строку в таблице можно добавлять по мере необходимости 🙂 Но это вкусовщина уже, работает и так, и эдак.

    Reply ↓
  28. endym

    (27)Полностью согласен, но! в моем примере таблица на десятой строке;)

    можно даже вставить непечатаемый символ и отслеживать его. Мой пример не является аксиомой, а лишь механизмом для дальнейшего развития темы;)

    UPD: (27) добавил в шапке в первом примере 😉

    Reply ↓
  29. stanru2

    (28) и этот пример очень полезен! Пожалуй, один из самых наглядных среди n страниц поиска яндекса. Мне ранее не требовалось делать экспорт в вород, благодаря примеру разобрался быстро.

    Можно еще в конце статьи добавить ссылок на объектную модель ворда.

    А не подскажете, есть ли где пример «пряморукого» импорта из ворда (надо импортировать не тупо текст, текст получается в одном свойстве. Надо анализировать формат текста и в соотв. с ним разносить строки по реквизитам)? Перерыл пол-гугла, не нашел ничего толкового. В результате сделал перебор текста построчно через paragraphs. В каждом параграфе у первого слова проверяется формат, и дальше уже дело техники. Работает прямо скажем небыстро 🙁 А хочется сделать максимально хорошо.

     тд = новый ТекстовыйДокумент;
    текст = док.Content.FormattedText;
    
    ЭлементыФормы.Индикатор1.МаксимальноеЗначение = текст.paragraphs.Count;
    
    
    для с = 1 по ЭлементыФормы.Индикатор1.МаксимальноеЗначение цикл
    
    ЭлементыФормы.Индикатор1.Значение = с;
    текСтр = текст.paragraphs(с).Range.Text;
    если (текст.paragraphs(с).Range.Words(1).Underline = 1) тогда
    текСлово = сокрлп(текст.paragraphs(с).Range.Words(1).Text);
    если (текСлово<>»») и (текСлово<>Символы.ВК) тогда
    текСтр = «$»+текСтр;
    КонецЕсли;
    КонецЕсли;
    тд.ДобавитьСтроку(текСтр);
    
    КонецЦикла;

    Показать

    Reply ↓
  30. alecs2004

    Полезный пример. Спасибо

    Reply ↓
  31. necropunk

    Автору жирный плюс. Опередил ты меня — недавно только закончил обработку, с печатью произвольного числа целой кучи разных таблиц, намучался изрядно.

    Reply ↓
  32. Alex Prikol

    спасибо.

    видно, автор копнул глубоко, кое что пригодится

    Reply ↓
  33. endym

    (29) я уже где-то встречал парсер ворда, думаю что он не сложнее парсера mt940 и подобных форматов;)

    у Word’a есть команды на VBA, которыми можно позиционироваться на каждом элементе, будь то слово, таблица, рисунок…

    Reply ↓
  34. stanru2

    (33) у меня определенные трудности были не с тем, как обработать данные, а с тем, как их получить 🙂 Объектная модель ворда для меня непривычна и незнакома, вот и сложности возникли 🙂

    Reply ↓
  35. SERJ_1CC

    То что надо, именно это и искал, примерчик очень хороший… Благодарю плюсом!!!

    Reply ↓
  36. gurovvv

    спасибо!

    просто,удобно. для начала обучения работы с вордом — отличный вариант.

    Reply ↓
  37. pavel_pss

    Предлагаю так же посмотреть и мой вариант http://infostart.ru/public/95012/

    Reply ↓
  38. endym

    (37) apalon_pss, мне кажется или это лишь часть описанного в моей статье?;))

    Reply ↓
  39. lelusha

    спасибо! простой пример и очень понятно. отличный вариант. Автору спасибо, поможет для обучения работы с вордом —

    Reply ↓
  40. Гость

    А как в макете настроить чтобы применить вариант: Word.Bookmarks(«ff»).Select(); //Где ff — это набор любых символов

    Reply ↓
  41. endym

    Нужно просто ввести понравившиеся вам символы в любом месте макета.

    Word.Bookmarks(«ff»).Select() — это команда ищет в макете символы ff

    Reply ↓
  42. gull22

    Дорога ложка к обеду, спасибо за информацию.

    Reply ↓
  43. monkbest

    (41) нет, она не ищет символы ff. она ищет метку с именем ff :)) вставлять метки в документе <> написать имя в текте документа

    смотрите http://prosto1s.ru/index.php/24-pechat-v-word-chast-2-metod-zameny-tegov-klonirovanie-abzatsev-stok-tablitsy

    там написано как добавлять метки

    Reply ↓
  44. endym

    (43) monkbest, именно ищет текст и заменяет его на все что захочет пользователь.

    З.Ы. метки это совсем другое;) матчасть рулит

    Reply ↓
  45. monkbest

    (44) Word.Bookmarks(«ff»).Select();

    Bookmarks — коллекция меток документа

    через скобочки с именем метки, можно получить саму метку

    у неё есть метод select, который возвращает выделение области от начала метки до конца метки

    это выделение можно скопировать в буфер, вырезать, вставить на его место то что уже лежит в буфере (cut() copy() paste())

    Reply ↓
  46. script

    Вот интересно как сделать две таблицы программно ?

    По метке мы заменили одну таблицу, а если нужно вставить еще одну ниже.

    Или таблицы нужно создавать в цикле, одна под одной как быть?

    Метка уже не сработает, потому что ее перезаписали и на ее месте находится первая таблица.

    Получается что метка служит всего навсего начальной областью куда нужно вставить первую таблицу но что дальше?

    Reply ↓
  47. endym

    Если Вы заранее знаете что там будет еще одна таблица — тогда добавить еще метку и обработать по уже известному алгоритму)

    Метки обрабатываются последовательно.

    Reply ↓
  48. kuza_87

    Друзья, кто-нибудь подскажет, можно ли в текст документа вставить специальный символ? Например символ бесконечности, евро или фунта стерлингов.

    Reply ↓
  49. endym

    (48) kuza_87,

    Можно. Используй комбинацию Аль+

    Reply ↓
  50. DedMoroz1983

    Долго формируется таблица.

    Reply ↓
  51. pyrkin_vanya

    А как заполнить данные в таблице из табличной части нужного объекта?

    Reply ↓
  52. pyrkin_vanya

    (27) не получается определить закладку. У меня пишет, что метод объекта не обнаружен Bookmarks. Попробовал заменить в вашей обработке, тоже самое. Может можно как-то еще определить с какой строки начинается таблица? Или может я просто не правильно пишу?

    Word.Bookmarks(«ff»).Select();

    Reply ↓
  53. endym

    (52) «ff» это любой текст.

    Можете вставить хоть «Тут должна быть таблица»

    и в макете эта фраза должна присутствовать.

    А какой офис установлен?

    Reply ↓
  54. pyrkin_vanya

    (53) я уж не совсем дурачок :). Я создал закладку на первой строке таблицы с названием ff. Офис 16 года. Не работает. Если честно, то метод слишком запутан. Я реализовал по-другому. Все чудесно работает. Но у меня четкое количество колонок. Хотя, думаю, если поковыряться то можно и произвольное сделать 🙂

     НомерТаблицы = 1;
    ТаблицаТоваров = Word.ActiveDocument.Tables(НомерТаблицы);
    Сч = 1;
    Для каждого ТекСтрока_Товар Из СсылкаНаДокумент.Товары Цикл
    Сч = Сч + 1;
    СтрокаТаблицы = ТаблицаТоваров.Rows.Add();
    
    // Номенклатура
    Word.Application.ActiveDocument.Tables(НомерТаблицы).Rows(Сч).Cells(1).Range.Text = Строка(ТекСтрока_Товар.Номенклатура);
    // ЕдИзм
    Word.Application.ActiveDocument.Tables(НомерТаблицы).Rows(Сч).Cells(2).Range.Text = Строка(ТекСтрока_Товар.Номенклатура.ЕдиницаИзмерения);
    // КолВо
    Word.Application.ActiveDocument.Tables(НомерТаблицы).Rows(Сч).Cells(3).Range.Text = Строка(Формат(ТекСтрока_Товар.КоличествоУпаковок, «ЧДЦ=; ЧГ=0»));
    // Цена
    Word.Application.ActiveDocument.Tables(НомерТаблицы).Rows(Сч).Cells(4).Range.Text = Строка(Формат(ТекСтрока_Товар.Цена, «ЧЦ=15; ЧДЦ=2»));
    // Сумма
    Word.Application.ActiveDocument.Tables(НомерТаблицы).Rows(Сч).Cells(5).Range.Text = Строка(Формат(ТекСтрока_Товар.Сумма, «ЧЦ=15; ЧДЦ=2»));
    
    // выделяем область яцеек с 1 по 5 для всех строк убираем шрифт «Жирный»
    Word.Application.ActiveDocument.Range(Word.Application.ActiveDocument.Tables(НомерТаблицы).Cell(Сч,1).Range.Start,Word.Application.ActiveDocument.Tables(НомерТаблицы).Cell(Сч,5).Range.End).Font.Bold = Ложь;
    
    // выделяем область яцеек с 1 по 1. Параметр варьируется от 0 до 3. (левый край, центр, правый край, по ширине  соответственно). Номенклатура
    Word.Application.ActiveDocument.Range(Word.Application.ActiveDocument.Tables(НомерТаблицы).Cell(Сч,1).Range.Start,Word.Application.ActiveDocument.Tables(НомерТаблицы).Cell(Сч,1).Range.End).ParagraphFormat.Alignment = 0;
    
    // выделяем область яцеек с 1 по 1. Параметр варьируется от 0 до 3. (левый край, центр, правый край, по ширине  соответственно). Номенклатура
    Word.Application.ActiveDocument.Range(Word.Application.ActiveDocument.Tables(НомерТаблицы).Cell(Сч,4).Range.Start,Word.Application.ActiveDocument.Tables(НомерТаблицы).Cell(Сч,5).Range.End).ParagraphFormat.Alignment = 2;
    КонецЦикла;
    

    Показать

    Reply ↓
  55. pyrkin_vanya

    (54) Все оказалось куда проще. Достаточно прописать это для создания колонки

    КолонкаТаблицы = ТаблицаТоваров.Columns.Add();

    Я как 1С-ник не могу добавлять строку или колонку как процедуру. Машинально переменную пишу 🙂 Хотя можно и без нее.

    Reply ↓
  56. endym

    (55) Возможно за 8 лет офис и обрел новые функции VBA, хотя не исключено что я мог это пропустить;)

    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *