Содержание

  1. Проблема ручного документооборота
  2. Подходы к автоматизации
  3. Автоматизация Word с python-docx
  4. Шаблоны с Jinja2
  5. Генерация PDF
  6. Автоотправка документов
  7. Полный workflow

Менеджер открывает Word, копирует прошлый договор, вручную меняет реквизиты, название компании, сумму, срок. Потом проверяет — не забыл ли где-то старые данные. 30–40 минут на каждый документ, и это при условии что не было ошибок.

Автоматизация сокращает это до 30 секунд и полностью исключает ошибки. Разберём как это сделать.

1. Проблема ручного документооборота

Ручное создание документов — это не просто потеря времени. Это системная проблема которая стоит денег:

  • Ошибки в реквизитах — неправильный ИНН, адрес или расчётный счёт могут сделать документ юридически недействительным
  • Задержки — клиент ждёт договор часами пока менеджер не дошёл до задачи
  • Несогласованность — разные менеджеры используют разные версии шаблонов
  • Потеря документов — файлы в разных папках, нет единого архива
Считаем экономию: если менеджер создаёт 10 договоров в день по 30 минут каждый — это 5 часов ежедневно. При зарплате 60 000 ₽/мес это 18 750 ₽ в месяц уходит только на заполнение документов. Автоматизация окупается за 2–3 недели.

2. Подходы к автоматизации

Есть несколько способов автоматизировать создание документов — выбор зависит от задачи:

  • python-docx — генерация Word-документов из шаблонов. Идеально если нужно сохранить форматирование Word
  • Jinja2 + python-docx — продвинутый вариант с условиями и циклами в шаблоне
  • WeasyPrint / ReportLab — генерация PDF из HTML или кода. Для красивых счетов и актов
  • Google Docs API — если работаете в Google Workspace
  • docxtpl — самый простой способ для Word-шаблонов с переменными

3. Автоматизация Word с python-docx

Самый частый сценарий — есть шаблон договора в Word, нужно подставить данные клиента. Используем библиотеку docxtpl которая расширяет python-docx шаблонизатором Jinja2.

Сначала подготовим шаблон — в Word-документе заменяем переменные данные на метки вида {{ переменная }}:

# Содержимое шаблона договора (contract_template.docx)
# В Word-документе заменяем:
#   "ООО Ромашка" → {{ company_name }}
#   "7701234567" → {{ inn }}
#   "85 000 рублей" → {{ amount }} рублей
#   "30 дней" → {{ term }} дней
# pip install docxtpl

from docxtpl import DocxTemplate
from datetime import date
import os

def generate_contract(client_data: dict, output_path: str):
    """Генерация договора из шаблона"""
    doc = DocxTemplate('templates/contract_template.docx')

    # Контекст — данные для подстановки
    context = {
        'contract_number': client_data['id'],
        'contract_date': date.today().strftime('%d.%m.%Y'),
        'company_name': client_data['company'],
        'inn': client_data['inn'],
        'kpp': client_data['kpp'],
        'address': client_data['address'],
        'director': client_data['director'],
        'amount': f'{client_data["price"]:,.0f}'.replace(',', ' '),
        'amount_words': amount_to_words(client_data['price']),
        'term': client_data['term_days'],
        'services': client_data['services'],  # список услуг
    }

    doc.render(context)
    doc.save(output_path)
    return output_path

# Использование
client = {
    'id': '2025-127',
    'company': 'ООО Ромашка',
    'inn': '7701234567',
    'kpp': '770101001',
    'address': 'г. Москва, ул. Ленина, 1',
    'director': 'Иванов И.И.',
    'price': 85000,
    'term_days': 30,
    'services': ['Разработка Telegram-бота', 'Интеграция с CRM'],
}

path = generate_contract(client, f'contracts/contract_{client["id"]}.docx')
print(f'Договор создан: {path}')

4. Расширенные шаблоны с Jinja2

docxtpl поддерживает полный синтаксис Jinja2 прямо в Word-шаблоне. Можно использовать условия и циклы:

# В Word-шаблоне можно писать:

# Условие — разные тексты для юрлиц и физлиц
# {% if is_company %}
#   {{ company_name }}, ИНН {{ inn }}
# {% else %}
#   {{ full_name }}, паспорт {{ passport }}
# {% endif %}

# Цикл — список услуг в таблице
# {% for service in services %}
#   {{ loop.index }}. {{ service.name }} — {{ service.price }} ₽
# {% endfor %}

# В Python-коде:
context = {
    'is_company': True,
    'company_name': 'ООО Ромашка',
    'services': [
        {'name': 'Разработка бота', 'price': 50000},
        {'name': 'Интеграция с CRM', 'price': 35000},
    ]
}

5. Конвертация в PDF

После генерации Word-документа часто нужен PDF. На сервере Linux это делается через LibreOffice:

import subprocess
import os

def docx_to_pdf(docx_path: str) -> str:
    """Конвертация DOCX в PDF через LibreOffice"""
    output_dir = os.path.dirname(docx_path)
    subprocess.run([
        'libreoffice', '--headless', '--convert-to', 'pdf',
        '--outdir', output_dir, docx_path
    ], check=True, capture_output=True)

    pdf_path = docx_path.replace('.docx', '.pdf')
    return pdf_path

# Генерируем договор и конвертируем в PDF
docx_path = generate_contract(client, 'contract.docx')
pdf_path = docx_to_pdf(docx_path)
print(f'PDF готов: {pdf_path}')
Альтернатива: если LibreOffice неудобен — используйте WeasyPrint для генерации PDF прямо из HTML-шаблона. Это даёт больше контроля над внешним видом, но требует HTML-вёрстки шаблона.

6. Автоматическая отправка документов

Финальный шаг — отправить готовый документ клиенту автоматически:

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

def send_contract(client_email: str, contract_pdf: str, contract_num: str):
    msg = MIMEMultipart()
    msg['From'] = 'contracts@company.ru'
    msg['To'] = client_email
    msg['Subject'] = f'Договор №{contract_num} — Гиперсайт'

    body = f"""
Здравствуйте!

Направляем договор №{contract_num} для ознакомления и подписания.

Документ во вложении.

С уважением,
Команда Гиперсайт
+7 (922) 290-23-72
"""
    msg.attach(MIMEText(body, 'plain', 'utf-8'))

    # Прикладываем PDF
    with open(contract_pdf, 'rb') as f:
        part = MIMEBase('application', 'pdf')
        part.set_payload(f.read())
    encoders.encode_base64(part)
    part.add_header(
        'Content-Disposition',
        f'attachment; filename="Договор_{contract_num}.pdf"'
    )
    msg.attach(part)

    with smtplib.SMTP_SSL('smtp.yandex.ru', 465) as server:
        server.login('contracts@company.ru', 'пароль_приложения')
        server.send_message(msg)
    print(f'Договор отправлен на {client_email}')

7. Полный workflow за 30 секунд

Собираем всё вместе — от данных в CRM до письма клиенту:

def process_deal(deal_id: int):
    """Полный workflow: сделка в CRM → договор → PDF → email"""

    # 1. Получаем данные из amoCRM
    amo = AmoCRM(AMO_DOMAIN, ACCESS_TOKEN)
    deal = amo.get_lead(deal_id)
    contact = amo.get_contact(deal['contact_id'])

    # 2. Генерируем договор
    contract_num = f'2025-{deal_id}'
    docx_path = generate_contract({
        'id': contract_num,
        'company': contact['company'],
        'inn': get_field(contact, 'ИНН'),
        'price': deal['price'],
        'term_days': 30,
    }, f'contracts/contract_{contract_num}.docx')

    # 3. Конвертируем в PDF
    pdf_path = docx_to_pdf(docx_path)

    # 4. Отправляем клиенту
    send_contract(contact['email'], pdf_path, contract_num)

    # 5. Обновляем сделку в CRM
    amo.update_lead(deal_id, {
        'custom_fields_values': [{
            'field_code': 'CONTRACT_NUMBER',
            'values': [{'value': contract_num}]
        }]
    })

    print(f'Готово! Договор {contract_num} отправлен клиенту')

# Запуск — менеджер нажимает кнопку в CRM или это триггерится автоматически
process_deal(12345)

Нужна автоматизация документооборота под ключ?

Настраиваем автогенерацию договоров, актов и счетов с интеграцией CRM. От 25 000 ₽, срок 1–2 недели.

Получить оценку