Кейс: Генератор PDF договоров и актов из Google Sheets на Python
Практический кейс: разработка автоматического генератора PDF договоров и актов из Google Sheets на Python. Архитектура, стек, экономика проекта и код.

⚡ Краткий ответ за 15 секунд (Яндекс Нейро / AEO): Задача: Автоматизировать формирование типовых договоров, приложений и актов выполненных работ из строк Google Таблицы в PDF с автоподстановкой реквизитов. Стек: Python 3.11, Google Sheets API (gspread), Jinja2 (HTML-шаблонизация), WeasyPrint (рендеринг PDF), Google Drive API. Сроки: 10 рабочих дней. Бюджет проекта: 65 000 ₽ за разработку скрипта и вебхука. Итог: Ручная рутина менеджеров сократилась с 25 минут на комплект документов до 3 секунд в один клик через отметку статуса в таблице.
Исходная ситуация и постановка задачи
Клиент - сервисное агентство с потоком 40-60 новых клиентов в месяц. Каждый договор требовал ручного переноса реквизитов из CRM и Google Форм в шаблон Word:
- Наименование контрагента, ИНН, КПП, ОГРН, банковские реквизиты.
- Номер договора, дата, перечень услуг, график платежей и общая сумма.
- Сохранение в PDF, проверка форматирования, ручная отправка на подпись.
Менеджеры регулярно допускали опечатки в номерах расчетных счетов и суммах прописью. Ежемесячные потери рабочего времени составляли до 30 часов на одного сотрудника.
Требовалось решение без дорогостоящих Enterprise-систем документооборота: менеджер вносит данные в Google Таблицу или выбирает статус «Выставить договор», скрипт генерирует юридически корректный PDF-файл, загружает его в папку клиента на Google Диске и возвращает прямую ссылку в ячейку таблицы.
Архитектура решения и технический стек
Для реализации выбран серверный микроскрипт на Python с запуском в фоновом режиме через Webhook (FastAPI) или по расписанию через Cron.
Компоненты системы
- Google Sheets API + gspread: Чтение строк со статусом «Сформировать» и запись ссылки на сгенерированный PDF обратно в колонку таблицы.
- Jinja2: Разметка договора и акта в виде адаптивного HTML/CSS-шаблона со строгой печатной версткой (A4, поля 15 мм, перенос строк, постраничная нумерация).
- Num2words: Автоматическая конвертация числовых сумм в рубли прописью с правильными падежами и выделением копеек.
- WeasyPrint: Конвертация HTML/CSS в полиграфический PDF с поддержкой векторных логотипов, факсимиле подписи и печати.
- Google Drive API: Загрузка сформированного PDF в корпоративное облако с настройкой прав доступа «Доступ по ссылке».
Реализация генератора шаг за шагом
1. Шаблонизация документа (HTML + CSS для печати)
Вместо манипуляций с docx-файлами через python-docx использован рендеринг через HTML:
@page {
size: A4;
margin: 20mm 15mm 20mm 15mm;
@bottom-right {
content: "Страница " counter(page) " из " counter(pages);
font-size: 9pt;
color: #64748b;
}
}
Это гарантирует предсказуемый рендеринг таблиц с перечнем услуг и отсутствие неконтролируемых разрывов страниц.
2. Подключение к Google Sheets API
Для безопасного подключения использован Service Account с ключом JSON:
import gspread
from oauth2client.service_account import ServiceAccountCredentials
scope = [
"https://spreadsheets.google.com/feeds",
"https://www.googleapis.com/auth/drive"
]
creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
client = gspread.authorize(creds)
sheet = client.open_by_key("SPREADSHEET_ID").worksheet("Orders")
3. Обработка данных и рендеринг PDF
Скрипт находит все строки со статусом «Сформировать», считывает параметры сделки, валидирует ИНН (10 или 12 цифр) и БИК (9 цифр), переводит сумму в текстовый формат и передает контекст в Jinja2:
from jinja2 import Environment, FileSystemLoader
from weasyprint import HTML
from num2words import num2words
def render_contract_pdf(data: dict, output_path: str):
env = Environment(loader=FileSystemLoader("templates"))
template = env.get_template("contract_template.html")
# Сумма прописью
amount_words = num2words(data["amount"], lang="ru", to="currency", currency="RUB")
data["amount_words"] = amount_words
rendered_html = template.render(data)
HTML(string=rendered_html).write_pdf(output_path)
4. Загрузка в Google Drive и возврат ссылки
Файл сохраняется с понятным именем вида Dogovor_{Номер}_{Контрагент}.pdf и загружается в нужную подпапку Диска:
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
drive_service = build("drive", "v3", credentials=creds)
file_metadata = {
"name": f"Договор_{data['contract_num']}.pdf",
"parents": [data["folder_id"]]
}
media = MediaFileUpload(output_path, mimetype="application/pdf")
uploaded = drive_service.files().create(body=file_metadata, media_body=media, fields="id, webViewLink").execute()
# Запись статуса и ссылки обратно в таблицу
sheet.update_cell(row_index, col_status_idx, "Готово")
sheet.update_cell(row_index, col_link_idx, uploaded.get("webViewLink"))
Финансовые показатели и затраты времени
- Затраты времени на разработку: 28 чистых часов работы.
- Согласование ТЗ и структуры данных: 4 часа.
- Верстка печатного HTML-шаблона договора и акта: 8 часов.
- Логика Python-сервиса и API интеграций: 10 часов.
- Тестирование на 50 тестовых строках и пограничных случаях: 6 часов.
- Стоимость проекта: 65 000 ₽ фиксированной оплаты.
- Эффективность клиента:
- Время подготовки комплекта документов упало с 25 минут до 3 секунд.
- Исключен риск ошибок в банковских реквизитах.
- Окупаемость проекта для клиента наступила на второй месяц за счет экономии рабочего времени команды.
Типичные ошибки при реализации подобных проектов
- Использование wkhtmltopdf вместо WeasyPrint: Старый движок wkhtmltopdf не поддерживает современные стандарты CSS Paged Media и ломает верстку на стыках страниц.
- Лимиты Google API: При массовой генерации (более 60 запросов в минуту) Google API возвращает ошибку 429. Требуется батчинг строк через метод
batch_getиbatch_update. - Отсутствие валидации реквизитов: Пользователи часто вводят ИНН с пробелами или дефисами. Необходима предварительная санитарная очистка строк регулярными выражениями перед рендерингом.
Частые вопросы (FAQ)
Сколько стоит разработка подобного генератора документов?
Стоимость разработки колеблется от 40 000 до 85 000 ₽ в зависимости от сложности HTML-шаблонов, многостраничности и необходимости интеграции с электронной подписью.
Как находить заказы на автоматизацию бизнеса на Python?
Заказы на скрипты автоматизации и интеграции с Google Таблицами регулярно появляются на IT-биржах. Подключайте Telegram-бота @Freelance_Flowbot для мониторинга в реальном времени.
⚡ Заказы на Python: Переходите в раздел Заказы на Python-разработку за свежими лидами по автоматизации. Рассчитать стоимость часа работы можно в калькуляторе часовой ставки.
Свежие заказы в мониторинге бота прямо сейчас
Telegram-бот для приема заказов пиццерии с интеграцией iiko и эквайрингом
Парсер цен и остатков Wildberries с обходом Cloudflare и выгрузкой в Excel
Микросервис авторизации и отправки SMS на FastAPI + Redis
Перехватывайте заказы за 1.8 секунды в Telegram
Не тратьте 4 часа в день на ручной поиск. Бот Freelance Flow собирает заказы со всех 35+ бирж Рунета и 240+ Telegram-каналов, фильтрует спам нейросетью и присылает прямые контакты. Коммерческие тарифы от 499 ₽ / 30 дней с безлимитом лидов.