gifts2017

Документооборот 8: Расширяем возможности автозаполнения шаблонов документов в 1С или XML-инъекция

Опубликовал Александр Кунин (Aleksandr_K) в раздел Программирование - Практика программирования

Довелось недавно решать интересную задачу в которой необходимо было добавить внутреннему документу табличную часть и заполнять на основании такого внутреннего документа word'овский документ по шаблону автозаполнения. Конфигурацию в данном примере модифицировать не потребуется.
Кому интересно - прошу под кат.
Посмотрев на справку и примеры я пришел к выводу, что прямым путем такую задачу не решить. Пойдем в обход.
Анализ кода в конфигураторе показал мне такое место:
 
Модуль АвтозаполнениеШаблоновФайловКлиентСервер строка 743
 
Если Найти(СтрЗамены, Символы.ПС) Тогда
        СтрЗамены = СтрЗаменить(СтрЗамены, Символы.ПС, "");
        Если СтрЗамены <> ЧтениеXML.Значение Тогда
                ЗаписьXML.ЗаписатьБезОбработки(СтрЗамены);
        Иначе
                ЗаписьXML.ЗаписатьТекст(СтрЗамены);
        КонецЕсли;
Иначе
        ЗаписьXML.ЗаписатьТекст(СтрЗамены);
КонецЕсли; 
 
Вывод очень простой - если мы выполним условие - наличие переноса строки - то получим возможность записать то что нам нужно.
Данные код работает только при замене текста в документе, поэтому создаем в нашем документе текст для замены, например, РазделТаблицыТарифнойСетки.

Я не рекомендую использовать символы @#$ и т.д. в тексте замены, так как очень вероятна ситуация когда в структуре документа вы получите три отдельных текста (или "пробега"):

<w:t>@</w:t><w:t>Таблица</w:t><w:t>@</w:t>

и 1С вам будет упорно сообщать, что "@Таблица@" отсутствует в документе.

 
Итак приступим к формированию нашего РезультатаОбработки:
// Закроем открытые теги
РезультатОбработки = "</w:t></w:r></w:p>";
// Заголовок таблицы
РезультатОбработки = РезультатОбработки +
"<w:tbl>
 | <w:tblPr>
 |   <w:tblW w:w=""5000"" w:type=""pct""/> // Ширина таблицы во весь лист
 |   <w:tblBorders> // Границы
 |     <w:top w:val=""single"" w:sz=""4"" w:space=""0"" w:color=""auto""/>
 |     <w:left w:val=""single"" w:sz=""4"" w:space=""0"" w:color=""auto""/>
 |     <w:bottom w:val=""single"" w:sz=""4"" w:space=""0"" w:color=""auto""/>
 |     <w:right w:val=""single"" w:sz=""4"" w:space=""0"" w:color=""auto""/>
 |     <w:insideH w:val=""single"" w:sz=""4"" w:space=""0"" w:color=""auto""/>
 |     <w:insideV w:val=""single"" w:sz=""4"" w:space=""0"" w:color=""auto""/>
 |   </w:tblBorders>
 | </w:tblPr>
 | <w:tblGrid>
 |   <w:gridCol w:w=""10296""/>
 | </w:tblGrid>
 | <w:tr> // Первая строка - она же заголовок таблицы
 |   <w:tc> // Первая колонка
 |     <w:tcPr> // Параметры колонки
 |       <w:tcW w:w=""7"" w:type=""pct""/> // Ширина в процентах
 |       <w:vAlign w:val=""center"" /> // Вертикальное выравнивание
 |     </w:tcPr>
 |     <w:p> // Параграф
 |      <w:pPr> // Параметры параграфа
 |              <w:jc w:val=""center""/> // Горизонтальное выравнивание
 |      </w:pPr>
 |      <w:r> // "Пробег" 
 |          <w:rPr> // Параметры "пробега"
 |              <w:sz w:val=""24""/> // Размер шрифта в 0.5 пункта
 |              <w:b w:val=""1"" /> // Полужирный
 |          </w:rPr>
 |          <w:t>№ п/п</w:t> // Собственно сам текст
 |      </w:r>
 |  </w:p>
 |   </w:tc>
 |   <w:tc>
 |     <w:tcPr>
 |       <w:tcW w:w=""32"" w:type=""pct""/>
 |       <w:vAlign w:val=""center"" />
 |     </w:tcPr>
 |     <w:p>
 |      <w:pPr>
 |              <w:jc w:val=""center""/>
 |      </w:pPr>
 |      <w:r>
 |          <w:rPr>
 |              <w:sz w:val=""24""/>
 |              <w:b w:val=""1"" />
 |          </w:rPr>
 |          <w:t>Станция отправления</w:t>
 |      </w:r>
 |  </w:p>
 |   </w:tc>
 |   <w:tc>
 |     <w:tcPr>
 |       <w:tcW w:w=""32"" w:type=""pct""/>
 |       <w:vAlign w:val=""center"" />
 |     </w:tcPr>
 |     <w:p>
 |      <w:pPr>
 |              <w:jc w:val=""center""/>
 |      </w:pPr>
 |      <w:r>
 |          <w:rPr>
 |              <w:sz w:val=""24""/>
 |              <w:b w:val=""1"" />
 |          </w:rPr>
 |          <w:t>Станция назначения</w:t>
 |      </w:r>
 |  </w:p>
 |   </w:tc>
 |   <w:tc>
 |     <w:tcPr>
 |       <w:tcW w:w=""29"" w:type=""pct""/>
 |       <w:vAlign w:val=""center"" />
 |     </w:tcPr>
 |     <w:p>
 |      <w:pPr>
 |              <w:jc w:val=""center""/>
 |      </w:pPr>
 |      <w:r>
 |          <w:rPr>
 |              <w:sz w:val=""24""/>
 |              <w:b w:val=""1"" />
 |          </w:rPr>
 |          <w:t>Стоимость услуг (руб/тн без учета НДС)</w:t>
 |      </w:r>
 |  </w:p>
 |   </w:tc>
 | </w:tr>"; 
 
 
Далее нам понадобится шаблон строки:
ШаблонСтроки = "
 | <w:tr>
 |   <w:tc>
 |     <w:tcPr>
 |       <w:tcW w:w=""7"" w:type=""pct""/>
 |       <w:vAlign w:val=""center"" />
 |     </w:tcPr>
 |     <w:p>
 |      <w:pPr>
 |              <w:jc w:val=""center""/>
 |      </w:pPr>
 |      <w:r>
 |          <w:rPr>
 |              <w:sz w:val=""24""/>
 |          </w:rPr>
 |          <w:t>%1</w:t>
 |      </w:r>
 |  </w:p>
 |   </w:tc>
 |   <w:tc>
 |     <w:tcPr>
 |       <w:tcW w:w=""32"" w:type=""pct""/>
 |       <w:vAlign w:val=""center"" />
 |     </w:tcPr>
 |     <w:p>
 |      <w:pPr>
 |              <w:jc w:val=""center""/>
 |      </w:pPr>
 |      <w:r>
 |          <w:rPr>
 |              <w:sz w:val=""24""/>
 |          </w:rPr>
 |          <w:t>%2</w:t>
 |      </w:r>
 |  </w:p>
 |   </w:tc>
 |   <w:tc>
 |     <w:tcPr>
 |       <w:tcW w:w=""32"" w:type=""pct""/>
 |       <w:vAlign w:val=""center"" />
 |     </w:tcPr>
 |     <w:p>
 |      <w:pPr>
 |              <w:jc w:val=""center""/>
 |      </w:pPr>
 |      <w:r>
 |          <w:rPr>
 |              <w:sz w:val=""24""/>
 |          </w:rPr>
 |          <w:t>%3</w:t>
 |      </w:r>
 |  </w:p>
 |   </w:tc>
 |   <w:tc>
 |     <w:tcPr>
 |       <w:tcW w:w=""29"" w:type=""pct""/>
 |       <w:vAlign w:val=""center"" />
 |     </w:tcPr>
 |     <w:p>
 |      <w:pPr>
 |              <w:jc w:val=""center""/>
 |      </w:pPr>
 |      <w:r>
 |          <w:rPr>
 |              <w:sz w:val=""24""/>
 |          </w:rPr>
 |          <w:t>%4</w:t>
 |      </w:r>
 |  </w:p>
 |   </w:tc>
 | </w:tr>"; 
 
Здесь, думаю, вам все уже понятно.
Собственно, строки:

// Вывод строк
Для Каждого СтрокаТЧ Из ФактическийВладелецФайла.ТарифнаяСетка Цикл

    РезультатОбработки = РезультатОбработки
        + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
                ШаблонСтроки,
                СтрокаТЧ.НомерСтроки,
                СтрокаТЧ.СтанцияОтправления,
                СтрокаТЧ.СтанцияНазначения,
                Формат(СтрокаТЧ.Стоимость, "ЧДЦ=2"));

КонецЦикла;
// Закроем таблицу
РезультатОбработки = РезультатОбработки + " </w:tbl>"";
// Откроем теги, т.к. в структуре документа они закрываются сразу за вставкой.
РезультатОбработки = РезультатОбработки + "<w:p><w:r><w:t>";
 
Теперь мы уберем переносы строк внутри, которые мы использовали для удобства, и добавим в начало и конец для того, чтобы выполнился тот код, который позволит нам сформировать таблицу
 
// Добавим переносы строки для записи в xml без обработки
РезультатОбработки = Символы.ПС + СтрЗаменить(РезультатОбработки, Символы.ПС, "") + Символы.ПС;
 
Пример, где еще может пригодиться - например, формирование реестра связанных документов.

Необходимые условия:
Шаблон в формате *.docx 

Используемые материалы:

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Яков Коган (Yashazz) 19.02.14 11:37
Класс! Отличная идея. Кстати, навело на мысль, что в любые строковые реквизиты неограниченной длины можно пихать свои xml, в т.ч. содержащие многострочные данные, лопатить их сериализатором и иметь хранение "табличных" данных.
2. Александр Кунин (Aleksandr_K) 20.02.14 19:29
(1) Yashazz, или в ХранилищеЗначения, например в регистре хранящем настройки отчетов и обработок в УТ, КА и УПП.
Если интересно, могу написать статью об использовании данного регистра для хранения данных внешних обработок и отчетов в БД.
3. ivdic (ivdic) 25.02.14 10:45
неплохо бы еще вставлять в документ в нужное место печать и клеше (подпись руководителя)
4. Борис (soap) 25.02.14 12:16
Хороший подход ! Однозначно+
5. Александр Кунин (Aleksandr_K) 26.02.14 20:41
(3) Без доработки типовой тут уже не обойдется. А почему не хотите сделать это изначально в шаблоне для автозаполнения?
6. Кирилл Бондаренко (karapuzzzz) 26.06.14 10:34
У меня, конечно, не без доработок, но вот что реализовано:
1. Возможность вставлять таблицу в поле (а не заменять текст), что дает возможность перезаполнять документ сколь угодно раз
2. Таблицу можно создавать форматированной, что придает юзабилити. По сути создается табличный документ, который можно форматировать или программно или брать готовый шаблон из макета.
3. Вставлять картинку. При этом, картинка записывается в базе отдельным файлом.
4. Вставлять текст одного документа в другой.

Для реализации 3-4 пункта пришлось пойти на костыль: картинка или содержимое вставляется не непосредственно в поле документа, а рядом. При этом вокруг вставленного объекта создаются дополнительные поля на подобии открывающего и закрывающего тэга. Сделано это из-за ограничения поля, но для возможности перезаполнять документ неоднократно.

Сейчас я сильно занят, но когда более-менее освобожусь - обязательно напишу статью на эту тему.
7. Руслан Жданов (Atori-kun) 04.12.14 05:44
ФактическийВладелецФайла - Это ссылка на владельца файла, а ТарифнаяСетка - ? Это не дополнительный реквизит, значит все равно пришлось в конфигуратор лезть?
8. Руслан Жданов (Atori-kun) 04.12.14 05:47
(6) karapuzzzz, Время не появилось свободное?
9. Александр Кунин (Aleksandr_K) 04.12.14 12:39
(7) В конфигуратор лезть в любом случае придется.
ФактическийВладелецФайла - Ссылка на элемент справочника для которого формируется файл.
Тарифная сетка - не типовая табличная часть объекта
10. Виталий Хомутецкий (hvitaly) 03.05.16 11:42
Спасибо! Это то простое решение, которое я ищу последние полгода :)
Теперь можно написать функцию, в которую передавать заголовок и таблицу значений, чтобы она за кадром формировала структуру таблицы (чтобы не писать длинный текст каждый раз). Также, разобравшись в тегах Ворда можно в функцию передавать и некоторые параметры оформления.
Еще раз спасибо!
Aleksandr_K; +1 Ответить
11. Имя Сидоров (SidorovNN) 05.05.16 12:46
Добрый день!
Подскажите пожалуйста. Тег "</w:t></w:r></w:p>" формирует абзац с пустой строкой? Столкнулся со следующей проблемой. Если данные файла обновлять по кнопке "Заполнить файл данными документа", то текст, вставленный через теги, постоянно смещается на одну строку вниз.
12. Александр Кунин (Aleksandr_K) 05.05.16 16:43
(11) SidorovNN, данные теги закрывают текущий параграф, а добавляемые в конце - открывают новый параграф. Если стилями предусмотрен отступ - будет выглядеть как пустая строка.
Чтобы сказать что-то более детальное - хотелось бы увидеть структуру документа в месте вставки.
13. Имя Сидоров (SidorovNN) 05.05.16 16:56
(12) Aleksandr_K, во вложении файл. С помощью тегов заполняю поле "Получатель". При заполнении сначала вставляется пустая строка, потом сам текст. Если данные файла обновлять данными документа, то текст смещается вниз.
Прикрепленные файлы:
Бланк письма.docx
14. Имя Сидоров (SidorovNN) 12.05.16 11:35
(12) Aleksandr_K, Не удалось выяснить причину?
15. Дмитрий Иванов (Hateful72) 07.11.16 15:16
не актуально с релиза 2.1 где то. Общие модули автоподстановки немного переделали
Aleksandr_K; +1 Ответить
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа