Files
contracts/testgen/templates.py
T

240 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
templates.py — Шаблоны документов (python-docx).
build_contract(ctx) — Договор на оказание технологических услуг
build_spec(ctx) — Спецификация и стоимость услуг
build_addendum(ctx) — Дополнительное соглашение
Каждый документ начинается с параграфа-комментария:
«Данный файл: [тип], компания [X], номер [N], версия [V].
Содержит: [краткое описание]. Сгенерирован автоматически для тестирования.»
"""
from docx import Document
from docx.shared import Pt, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
import pools
# ═══════════════════════════════════════════════════════════════
# Хелперы
# ═══════════════════════════════════════════════════════════════
def _add_title(doc, text):
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(text)
run.bold = True
run.font.size = Pt(14)
def _add_heading(doc, text):
p = doc.add_paragraph()
run = p.add_run(text)
run.bold = True
run.font.size = Pt(12)
def _add_para(doc, text):
p = doc.add_paragraph(text)
return p
def _add_parties_table(doc, customer, provider, provider_dir, city, date):
"""Таблица реквизитов сторон (1×2): Заказчик | Исполнитель."""
table = doc.add_table(rows=1, cols=2)
table.style = 'Table Grid'
table.alignment = WD_TABLE_ALIGNMENT.CENTER
c0 = table.rows[0].cells[0]
c1 = table.rows[0].cells[1]
cust_info = f'{customer["legal"]}\nЮридический адрес:\n\n{customer.get("dir","")}\n_______________/ФИО/\n М.П.'
prov_info = f'{provider}\nЮридический адрес:\n{pools.ADDRESS}\n{provider_dir}\n_______________/Степаненко В.И./\n М.П.'
c0.text = f"Заказчик\n{cust_info}"
c1.text = f"Исполнитель\n{prov_info}"
def _add_services_table(doc, services, total_str, with_end_date=False, label="Абонентские услуги"):
"""Таблица услуг: №, Наименование, Цена, Объем, Сумма, Дата начала [+ Дата окончания] + Итого."""
_add_para(doc, f"{label}:")
cols = 7 if with_end_date else 6
header = ["№ п/п", "Наименование услуг", "Цена, руб. с НДС", "Объем услуг", "Сумма, руб. с НДС", "Планируемая дата начала оказания услуг"]
if with_end_date:
header.append("Дата окончания оказания услуг")
table = doc.add_table(rows=1, cols=cols)
table.style = 'Table Grid'
# Заголовок
for i, h in enumerate(header):
table.rows[0].cells[i].text = h
# Строки услуг
for s in services:
row = table.add_row()
cells = row.cells
cells[0].text = str(s["num"])
cells[1].text = s["name"]
cells[2].text = s["price_str"]
cells[3].text = str(s["qty"])
cells[4].text = s["sum_str"]
cells[5].text = s.get("date_start", pools.random_date(2026, 3, 3))
if with_end_date:
cells[6].text = s.get("date_end", "")
# Итого
row = table.add_row()
row.cells[0].text = ""
row.cells[1].text = f"Итого {label.lower()} в руб с учетом НДС"
row.cells[2].text = ""
row.cells[3].text = ""
row.cells[4].text = total_str
row.cells[5].text = "-"
if with_end_date:
row.cells[6].text = "-"
# ═══════════════════════════════════════════════════════════════
# Билдеры
# ═══════════════════════════════════════════════════════════════
def build_contract(ctx):
"""
ctx = {
company, number, date, company_idx,
services[], total, total_str,
comment (описание для тестов)
}
"""
doc = Document()
c = ctx["company"]
num = ctx["number"]
date = ctx["date"]
# ── Комментарий в manifest.json, не в документе ──
_add_title(doc, "Договор на оказание технологических услуг")
_add_para(doc, f"№ {num}")
_add_para(doc, f'{c["legal"]} именуемое в дальнейшем "Заказчик", в лице {c["dir"]} ФИО, действующего на основании Устава, '
f{pools.PROVIDER}, именуемое в дальнейшем "Исполнитель", в лице {pools.PROVIDER_DIR}, '
f'действующего на основании Устава, заключили настоящий Договор о нижеследующем.')
# Термины
_add_heading(doc, "ТЕРМИНЫ И ОПРЕДЕЛЕНИЯ")
terms = [
"Услуги – оказываемые Исполнителем Заказчику технологические услуги в области компьютерных и телекоммуникационных технологий.",
"Абонентские услуги – Услуги, указанные в Спецификации, предоставляемые ежемесячно.",
"Инсталляционные услуги – Услуги по установке и настройке оборудования Заказчика.",
"Технологическая площадка – специализированное помещение (ЦОД) по адресу: г. Москва, 1-я Стекольная 7с5.",
"Отчетный период – календарный месяц.",
]
for t in terms:
_add_para(doc, t)
# Предмет
_add_heading(doc, "ПРЕДМЕТ ДОГОВОРА")
_add_para(doc, f"Исполнитель обязуется оказывать Заказчику технологические Услуги, указанные в Спецификации, "
f"а Заказчик обязуется принимать и оплачивать Услуги. Место оказания: {pools.CITY}, {pools.ADDRESS}.")
# Стоимость и оплата
_add_heading(doc, "СТОИМОСТЬ УСЛУГ И ПОРЯДОК ОПЛАТЫ")
_add_para(doc, f"Стоимость Услуг указана в Спецификации (Приложение №1). "
f"Оплата ежемесячная, постоплатой 100%, на основании УПД и счета. "
f"Срок оплаты — 10 рабочих дней с даты выставления счета.")
# Прочие условия (сокращённо)
_add_heading(doc, "ОСОБЫЕ УСЛОВИЯ")
_add_para(doc, "Исполнитель вправе приостановить оказание Услуг при просрочке платежей более 10 рабочих дней. "
"Заказчик обязан соблюдать правила Технологической площадки.")
_add_heading(doc, "СРОК ДЕЙСТВИЯ")
_add_para(doc, "Договор вступает в силу с момента подписания. Начальный период — 12 месяцев. "
"Автоматическая пролонгация, если ни одна из Сторон не уведомит о прекращении за 30 дней.")
# Реквизиты
_add_heading(doc, "ЮРИДИЧЕСКИЕ АДРЕСА И РЕКВИЗИТЫ СТОРОН")
_add_para(doc, f"{pools.CITY} {date}г.")
_add_parties_table(doc, c, pools.PROVIDER, pools.PROVIDER_DIR, pools.CITY, date)
return doc
def build_spec(ctx):
"""
ctx = {
company, number, date, contract_date,
services[], total, total_str,
version (1 или 2),
label (название секции),
comment
}
Для v2 — slight differences в services (цены/объём/состав).
"""
doc = Document()
c = ctx["company"]
num = ctx["number"]
date = ctx["date"]
contract_date = ctx.get("contract_date", date)
ver = ctx.get("version", 1)
_add_title(doc, f"Приложение № 1")
_add_para(doc, f"к Договору на оказание технологических услуг № {num} от {contract_date} г.")
_add_para(doc, "")
_add_heading(doc, "Спецификация и стоимость услуг")
_add_para(doc, "Состав и стоимость Услуг")
label = ctx.get("label", f"Абонентские услуги с {date}")
_add_para(doc, label)
_add_para(doc, f"Общая стоимость Ежемесячных услуг составляет {ctx['total_str']} руб.")
_add_services_table(doc, ctx["services"], ctx["total_str"], with_end_date=True, label=label)
# Дата и город
_add_para(doc, "")
_add_para(doc, f"{pools.CITY} {date}г.")
return doc
def build_addendum(ctx):
"""
ctx = {
company, number, date, contract_date, addendum_num,
services_install[], install_total, install_total_str,
services_monthly[], monthly_total, monthly_total_str,
comment
}
"""
doc = Document()
c = ctx["company"]
num = ctx["number"]
date = ctx["date"]
contract_date = ctx.get("contract_date", date)
anum = ctx.get("addendum_num", 1)
_add_title(doc, f"Дополнительное Соглашение №{anum}")
_add_para(doc, f"к Договору № {num} от {contract_date}")
_add_para(doc, f"{pools.CITY} «{date.split('.')[0]}» {_month_name(int(date.split('.')[1]))} {date.split('.')[2]}")
_add_para(doc, f'{c["legal"]} именуемое в дальнейшем "Заказчик", в лице {c["dir"]} ФИО, '
f{pools.PROVIDER}, именуемое в дальнейшем "Исполнитель", в лице {pools.PROVIDER_DIR}, '
f'заключили настоящее Соглашение о нижеследующем:')
_add_heading(doc, "1. ПРЕДМЕТ ДОПОЛНИТЕЛЬНОГО СОГЛАШЕНИЯ")
_add_para(doc, "Изменить Спецификацию и стоимость Услуг. Утвердить новую Спецификацию в соответствии с Приложением №1 к настоящему Соглашению.")
if ctx.get("services_install"):
_add_para(doc, "")
_add_para(doc, f"Общая стоимость Инсталляционных услуг составляет {ctx['install_total_str']} руб.")
_add_services_table(doc, ctx["services_install"], ctx["install_total_str"], with_end_date=False, label="Разовые услуги")
if ctx.get("services_monthly"):
_add_para(doc, "")
_add_para(doc, f"Общая стоимость Ежемесячных услуг составляет {ctx['monthly_total_str']} руб.")
_add_services_table(doc, ctx["services_monthly"], ctx["monthly_total_str"], with_end_date=True, label="Абонентские услуги")
_add_heading(doc, "ПРОЧИЕ УСЛОВИЯ")
_add_para(doc, "Во всём остальном, что не урегулировано настоящим Соглашением, действуют положения Договора.")
_add_heading(doc, "ЮРИДИЧЕСКИЕ АДРЕСА И РЕКВИЗИТЫ СТОРОН")
_add_parties_table(doc, c, pools.PROVIDER, pools.PROVIDER_DIR, pools.CITY, date)
return doc
def _month_name(m):
months = ["", "января", "февраля", "марта", "апреля", "мая", "июня",
"июля", "августа", "сентября", "октября", "ноября", "декабря"]
return months[m] if 1 <= m <= 12 else str(m)