Заполняем документы в Microsoft Word при помощи Python. Часть 1

    Исполняем обязанности по получению сведений о своих бенефициарных владельцах


    Небольшая вводная


    Начиная с 21 декабря 2016 года вступили изменения в ФЗ РФ «О противодействии легализации (отмыванию) доходов, полученных преступным путем, и финансированию терроризма», касательно обязанности юридического лица по раскрытию информации о своих бенефициарных владельцах. В связи с этим, многие компании направляют запросы по цепочке владения с целью выяснения своих бенефициарных владельцев. Кто-то формирует запросы на бумаге, кто-то рассылает электронные письма.

    На наш взгляд, надлежащим доказательством исполнения обязанности «знай своего бенефициарного владельца» является наличие письма на бумаге с отметкой об отправке/вручении. Данные письма в идеале должны готовиться не реже одного раза в год. Если в ведении юриста находится всего несколько компаний, то составление писем не составляет особого труда. Но, если компаний больше 3-х десятков, составление писем превращается в уничтожающую позитив рутину. Дело усугубляется тем, что реквизиты писем постоянно меняются: подписанты увольняются, компании перерегистрируются, меняя адреса. Все это надо учитывать. Как здесь могут помочь навыки программирования на python?

    Очень просто — хорошо бы иметь программу, которая сама будет подставлять в письма необходимые реквизиты. В том числе формировать сами письма, не заставляя создавать документ за документом вручную. Попробуем.

    Структура письма в word. Модуль python docxtpl


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

    Текст письма от общества своему участнику/акционеру будет примерно следующим:



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

    Для начала в самом шаблоне письма Word вместо одного из полей, например, подписанта поставим переменную. Данная переменная должна быть на либо на англ. языке, либо на русском, но в одно слово.Также переменная должна быть обязательно заключена в двойные фигурные скобки. Выглядеть это будет примерно так:



    Сама программа будет иметь следующий вид:

    from docxtpl import DocxTemplate
    doc = DocxTemplate("шаблон.docx")
    context = { 'director' : "И.И.Иванов"}
    doc.render(context)
    doc.save("шаблон-final.docx")
    

    Вначале мы импортируем модуль для работы с документами формата Word. Далее мы открываем шаблон, и в поле директор, которое бы обозначили ранее в самом шаблоне, вносим ФИО директора. В конце документ сохраняется под новым именем.

    Таким образом, чтобы заполнить все поля в файле-шаблоне Word нам для начала необходимо определить все поля ввода в самом шаблоне скобками {} вместе с переменными и потом написать программу. Код будет примерно следующим:

    from docxtpl import DocxTemplate
    doc = DocxTemplate("шаблон.docx")
    context = { 'emitent' : 'ООО Ромашка', 'address1' : 'г. Москва, ул. Долгоруковская, д. 0', 'участник': 'ООО Участник', 'адрес_участника': 'г. Москва, ул. Полевая, д. 0', 'director': 'И.И. Иванов'}
    doc.render(context)
    doc.save("шаблон-final.docx")
    

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

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

    Комментарии 21

      +1
      Все это, конечно, хорошо… Но чем вас не устраивает стандартный режим «Слияние», существующий в MS WORD?

      Действия те же:
      1. Создаете шаблон письма и вставляете в нужные места поля.
      2. Создаете таблицу (базу) данных.
      3. Запускаете слияние.

        0
        меня эта фича спасла в армии от монотонного заполнения документов.
          0
          Ох… Надеюсь это была совсем не секретная часть…
            +1
            Перечислю ещё раз проблемы слияния:

            1. Расположение книги менять нельзя (иначе придётся заново связывать документ)
            2. По сети не комфортно работать более чем одному юзеру (т. к. будет занята книга)
            3. В поле с датой не должно быть записей с текстом (иначе отображаться будет целое число)
            4. Периодически слетают связи (особенно, если применяете к полям фильтры)
            5. В поля слияния нельзя вставить неразрывный пробел и дефис — chr(160) и chr(30)
            6. Как итог: обновление множества документов превращается в боль
              0
              Сделаю ремарку, что это не относится к программе в посте.
                0
                Описанные вами недостатки просто курам на смех.
                Вы просто не разбираетесь ни капли.
                Я слиянием заполнял документы с таким количеством переменных, что вам и этой «программе» не снилось, при этом с использованием форматирования, автонумерации, перекрестных ссылок и проч.
                А касаемо того, что вы тут понаписали:
                1. Чтобы «перепривязать» документ достаточно его открыть и указать новое расположение парой кликов. И программе на Пайтон, по-вашему, все равно где документ лежит, да? Она сама весь комп и сетевое окружение обыскивает в поисках нужного файла? Смешно. Перепривязать документ в программе на Пайтон будет сложнее и совершенно невозможно для простого пользователя.
                2. Что вы вообще несете? Excel для подписчиков Office 365 поддерживает совместное редактирование. В конце концов, можно в OneDrive сохранить книгу и редактировать всем офисом одновременно. А для чтения из файла при автозаполнении совместный доступ не нужен.
                3. Вообще хрень написали. Все ячейки с данными после редактирования надо форматировать в «Текст». Тогда все всегда будет отображаться, как надо, и экспортироваться в Ворд без проблем.
                4. Какие связи у вас слетают? Нейронов мозга? Структуру таблицы для заполнения менять нельзя, особенно строку заголовков (или придется менять переменные в вордовском шаблоне). Если вам надо применять фильтры или сделать более удобный интерфейс для ввода данных, выносите все на отдельный лист и там творите, что хотите, Ворд разрешает выбрать из какого листа импортировать данные.
                5. Вообще непонятно что, куда и зачем вы собрались вставлять. Текстовое содержимое импортируется из ячейки Excel, если в нем есть дефис, то он перенесется. Если вы хотели в само поле слияния добавить текст в Ворде, то сначала Shift+F9 нажимаете и можете писать, что угодно, кроме двойных кавычек (их писать либо двойным ' либо спецсимволом, чтобы автоматом заменить все кавычки на спецсимвол «кавычки», копируете в буфер код спецсимвола с соответствующей вкладки Ворда, далее Ctrl+F, заменить — найти ", заменить на «из буфера обмена» в панеле расширенных настроек).
                6. Только если вы — криворукий ламер, который не шарит в теме вообще, но считает своим долгом писать всякую фигню с видом знатока.
              0
              А почему не использовался макрос документа Word?
                +2
                Потому что использовался python. Вы можете изложить свое решение.
                  0
                  Изложил. Настойчиво рекомендую почитать о VBA, он явно потерял популярность после перехода на .net, но его возможностей более, чем достаточно для описанных тобою запросов по автоматизации. Мое решение работает по сети, с одновременным редактированием, на любой машине где стоит ворд или опенворд (макросы в опенворде тоже работают).

                  Я немного знаю питон, т.к. являюсь ведущим разработчиком компании пищущей софт на питоне, а это тот еще изврат. Меня всегда удивляет и удивляла ненужность решений на питоне (яве, бейсике… любой другой язык) там, где это не нужно.
                0
                А как насчет экспорта в PDF?
                  0
                  nikoloza, автор статьи походу вообще не разбирается в Word, а ты его спрашиваешь про экспорт. Пройти по полям и заполнить их содержимым из макроса VBA и сохранить в пдф это 2 строки, без питона и docx. А автор статьи скорее всего reportlab тебе установит и начнет PDF сущностями оперировать, поскольку разобраться в Word он не удосужился.

                  Итак. В Документе расставляются поля, в данном случае я это сделал через DocVarField.
                  После в макросе VBA это решается так:
                  Sub Macros()
                  
                  With ActiveDocument.Variables
                   .Item("EMITENT") = "Емитент"
                   .Item("ADDRESS1") = "Адрес"
                   .Item("DIRECTOR") = "Директор"
                  End With
                  
                  ActiveDocument.Fields.Update
                  ActiveDocument.SaveAs2 FileName:="newname.pdf", FileFormat:=wdFormatPDF
                  
                  End Sub


                  Всего две строки без определения параметров словаря, автор не сказал откуда он берет эти данные. сохранение возможно в любом вордовом формате. или перед этим можно диалог сохранения вызвать и выбрать.

                  Две строки, только Word без Питона. Как тебе такое Илон Маск?
                    0
                    А если питон и шаблоны на linux машине?
                      0
                      в другой статье приводил пример, как написать макрос, работающий на любой машине где есть либро офис, опен оффис или word. в данном случае Питон для заполнения шаблонов не нужен.
                        0
                        Подскажите ссылку, пожалуйста, на статью.
                          0
                          Я думал это твоя статья: habr.com/ru/post/455808, обе статьи — эта и вторая появились в одно время.
                            0
                            Нет, я просто мимо проходил.
                        0
                        vse rabotaet i na linux. seychas potestil na ubuntu 16.04 (raspberry).
                        odno «no». nado doustanovit` moduli cherez pip:
                        apt-get install libxml2-dev libxslt-dev
                        pip install lxml
                        pip install docxtpl
                        pip install openpyxl

                        i v samoy programme ne ispol`zovat` russie simvoli, i.e.:
                        from docxtpl import DocxTemplate
                        import openpyxl
                        
                        test=[]
                        wb = openpyxl.load_workbook('zaprosi.xlsx')
                        sheet=wb.get_active_sheet()
                        
                        for row in sheet['B3':'F7']:
                            for cellObj in row:
                                if cellObj.value==None or cellObj.value==" ":
                                    continue
                                #print(cellObj.value)
                                test.append(cellObj.value)
                        print(test)
                        
                        x=0
                        while x<len(test):
                            doc = DocxTemplate("shablon.docx")
                            context = { 'emitent' : test[x],'address1' : test[x+1],'uchastnik' : test[x+2],'address2' : test[x+3],'director' : test[x+4] }
                            doc.render(context)
                            doc.save(test[x]+'.docx')    
                            x+=5
                      0
                      добавлена конвертация в pdf. не совсем красиво, но смысл понятен:

                      import sys
                      import os
                      import comtypes.client
                      from docxtpl import DocxTemplate
                      
                      doc = DocxTemplate("шаблон.docx")
                      context = { 'emitent' : 'ООО Ромашка', 'address1' : 'г. Москва, ул. Долгоруковская, д. 0', 'участник': 'ООО Участник', 'адрес_участника': 'г. Москва, ул. Полевая, д. 0', 'director': 'И.И. Иванов'}
                      doc.render(context)
                      doc.save("final.docx")
                      
                      wdFormatPDF = 17
                      
                      in_file = os.path.abspath ("final.docx")
                      out_file = os.path.abspath("final.pdf")
                      
                      word = comtypes.client.CreateObject('Word.Application')
                      doc = word.Documents.Open(in_file)
                      doc.SaveAs(out_file, FileFormat=wdFormatPDF)
                      doc.Close()
                      word.Quit()
                        0
                        Сomtypes работает только в виндовс это аналог com+ addins в виндовом оффисе.
                        Сохранение в ворде в пдф через макрос делается в одну команду:

                        ActiveDocument.ExportAsFixedFormat OutputFileName:= _
                                filename, ExportFormat:=wdExportFormatPDF, _
                                OpenAfterExport:=False, OptimizeFor:=wdExportOptimizeForPrint, Range:= _
                                wdExportAllDocument, BitmapMissingFonts:= True
                        


                        Если я правильно помню, то это сработает для любой ос.

                        zoldaten,
                        если ты делаешь
                        word = comtypes.client.CreateObject('Word.Application')

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

                        wdFormatPDF = 17
                        in_file = os.path.abspath ("final.docx")
                        out_file = os.path.abspath("final.pdf")
                        word = comtypes.client.CreateObject('Word.Application')
                        doc = word.Documents.Open(in_file)
                        doc.Variables.Item("EMITENT") = "Емитент"
                        doc.Variables.Item("ADDRESS1") = "Адрес"
                        doc.Variables.Item("DIRECTOR") = "Директор"
                        doc.Fields.Update
                        doc.SaveAs(out_file, FileFormat=wdFormatPDF)
                        


                          0
                          Сomtypes работает только в виндовс

                          Это верное замечание.
                          то DocxTemplate уже ненужен, ты же фактически ворд запустил в питоне.

                          Да, ваш вариант красивее на python, ближе к PEPу. Уверен, что он также работает.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое