Includes: API (Express/TypeScript), Astro site, Python workers, document generators, FCC compliance tools, Canada CRTC formation, Ansible infrastructure, and deployment scripts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1355 lines
52 KiB
Python
1355 lines
52 KiB
Python
"""
|
|
Performance West — DOCX Template Generator
|
|
|
|
Generates all DOCX templates used by the document generation pipeline.
|
|
Each template contains Jinja2 placeholders ({{ variable_name }}) that are
|
|
filled at runtime by DocxBuilder.
|
|
|
|
Usage:
|
|
python scripts/templates/create_templates.py
|
|
|
|
Output:
|
|
scripts/templates/*.docx (9 template files)
|
|
|
|
After generation, upload templates to MinIO:
|
|
mc cp scripts/templates/*.docx minio/performancewest/templates/
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from docx import Document
|
|
from docx.shared import Inches, Pt, RGBColor, Cm, Emu
|
|
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
from docx.enum.table import WD_TABLE_ALIGNMENT
|
|
from docx.enum.section import WD_ORIENT
|
|
from docx.oxml.ns import qn, nsdecls
|
|
from docx.oxml import parse_xml
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Constants
|
|
# ---------------------------------------------------------------------------
|
|
|
|
OUTPUT_DIR = Path(__file__).parent
|
|
COMPANY_NAME = "Performance West Inc."
|
|
COMPANY_ADDRESS = "525 Randall Ave Ste 100-1195, Cheyenne, WY 82001"
|
|
COMPANY_PHONE = "1-888-411-0383"
|
|
COMPANY_WEBSITE = "performancewest.net"
|
|
|
|
# Brand colors
|
|
NAVY = RGBColor(0x2D, 0x4E, 0x78) # pw-700
|
|
DARK_NAVY = RGBColor(0x1A, 0x36, 0x5D) # pw-900
|
|
LIGHT_GRAY = RGBColor(0x9C, 0xA3, 0xAF)
|
|
MEDIUM_GRAY = RGBColor(0x6B, 0x72, 0x80)
|
|
BLACK = RGBColor(0x00, 0x00, 0x00)
|
|
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
|
|
|
|
CONFIDENTIALITY_NOTICE = (
|
|
"CONFIDENTIAL — This document is prepared by Performance West Inc. for the "
|
|
"exclusive use of the intended recipient. It does not constitute legal advice. "
|
|
"Unauthorized distribution is prohibited."
|
|
)
|
|
|
|
DISCLAIMER = (
|
|
"DISCLAIMER: This document is prepared by Performance West Inc. for compliance "
|
|
"consulting purposes only. It does not constitute legal advice, legal "
|
|
"representation, or create an attorney-client relationship. For legal matters, "
|
|
"consult a licensed attorney in your jurisdiction."
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def set_cell_shading(cell, color_hex: str):
|
|
"""Set background shading on a table cell."""
|
|
shading = parse_xml(
|
|
f'<w:shd {nsdecls("w")} w:fill="{color_hex}" w:val="clear"/>'
|
|
)
|
|
cell._tc.get_or_add_tcPr().append(shading)
|
|
|
|
|
|
def add_page_number(footer):
|
|
"""Add 'Page X of Y' field to a footer."""
|
|
para = footer.paragraphs[0] if footer.paragraphs else footer.add_paragraph()
|
|
para.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
|
|
# "Page "
|
|
run = para.add_run("Page ")
|
|
run.font.size = Pt(8)
|
|
run.font.color.rgb = LIGHT_GRAY
|
|
|
|
# PAGE field
|
|
fld_char_begin = parse_xml(f'<w:fldChar {nsdecls("w")} w:fldCharType="begin"/>')
|
|
run_page = para.add_run()
|
|
run_page._r.append(fld_char_begin)
|
|
instr = parse_xml(f'<w:instrText {nsdecls("w")} xml:space="preserve"> PAGE </w:instrText>')
|
|
run_page2 = para.add_run()
|
|
run_page2._r.append(instr)
|
|
fld_char_end = parse_xml(f'<w:fldChar {nsdecls("w")} w:fldCharType="end"/>')
|
|
run_page3 = para.add_run()
|
|
run_page3._r.append(fld_char_end)
|
|
|
|
# " of "
|
|
run_of = para.add_run(" of ")
|
|
run_of.font.size = Pt(8)
|
|
run_of.font.color.rgb = LIGHT_GRAY
|
|
|
|
# NUMPAGES field
|
|
fld_char_begin2 = parse_xml(f'<w:fldChar {nsdecls("w")} w:fldCharType="begin"/>')
|
|
run_np = para.add_run()
|
|
run_np._r.append(fld_char_begin2)
|
|
instr2 = parse_xml(f'<w:instrText {nsdecls("w")} xml:space="preserve"> NUMPAGES </w:instrText>')
|
|
run_np2 = para.add_run()
|
|
run_np2._r.append(instr2)
|
|
fld_char_end2 = parse_xml(f'<w:fldChar {nsdecls("w")} w:fldCharType="end"/>')
|
|
run_np3 = para.add_run()
|
|
run_np3._r.append(fld_char_end2)
|
|
|
|
|
|
def setup_document(title: str) -> Document:
|
|
"""Create a new Document with standard Performance West formatting."""
|
|
doc = Document()
|
|
|
|
# -- Default font: Times New Roman 11pt --
|
|
style = doc.styles["Normal"]
|
|
style.font.name = "Times New Roman"
|
|
style.font.size = Pt(11)
|
|
style.font.color.rgb = BLACK
|
|
style.paragraph_format.space_after = Pt(6)
|
|
style.paragraph_format.line_spacing = 1.15
|
|
|
|
# -- Heading styles --
|
|
for level in range(1, 4):
|
|
h = doc.styles[f"Heading {level}"]
|
|
h.font.name = "Times New Roman"
|
|
h.font.color.rgb = NAVY
|
|
h.font.bold = True
|
|
if level == 1:
|
|
h.font.size = Pt(18)
|
|
h.paragraph_format.space_before = Pt(18)
|
|
h.paragraph_format.space_after = Pt(12)
|
|
elif level == 2:
|
|
h.font.size = Pt(14)
|
|
h.paragraph_format.space_before = Pt(14)
|
|
h.paragraph_format.space_after = Pt(8)
|
|
else:
|
|
h.font.size = Pt(12)
|
|
h.paragraph_format.space_before = Pt(10)
|
|
h.paragraph_format.space_after = Pt(6)
|
|
|
|
# -- Margins --
|
|
for section in doc.sections:
|
|
section.top_margin = Cm(2.54)
|
|
section.bottom_margin = Cm(2.54)
|
|
section.left_margin = Cm(2.54)
|
|
section.right_margin = Cm(2.54)
|
|
|
|
# -- Header --
|
|
header = doc.sections[0].header
|
|
header.is_linked_to_previous = False
|
|
h_para = header.paragraphs[0] if header.paragraphs else header.add_paragraph()
|
|
h_para.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
|
run = h_para.add_run(COMPANY_NAME)
|
|
run.font.size = Pt(9)
|
|
run.font.color.rgb = NAVY
|
|
run.font.bold = True
|
|
run2 = h_para.add_run(f" | {COMPANY_PHONE}")
|
|
run2.font.size = Pt(8)
|
|
run2.font.color.rgb = LIGHT_GRAY
|
|
|
|
# -- Footer --
|
|
footer = doc.sections[0].footer
|
|
footer.is_linked_to_previous = False
|
|
|
|
# Confidentiality notice
|
|
conf_para = footer.add_paragraph()
|
|
conf_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
conf_run = conf_para.add_run(CONFIDENTIALITY_NOTICE)
|
|
conf_run.font.size = Pt(7)
|
|
conf_run.font.italic = True
|
|
conf_run.font.color.rgb = LIGHT_GRAY
|
|
|
|
# Page number
|
|
add_page_number(footer)
|
|
|
|
return doc
|
|
|
|
|
|
def add_cover_page(doc: Document, title: str, placeholders: dict[str, str]):
|
|
"""Add a branded cover page with Jinja2 placeholders."""
|
|
# Spacer
|
|
spacer = doc.add_paragraph()
|
|
spacer.space_after = Pt(72)
|
|
|
|
# Title
|
|
title_para = doc.add_paragraph()
|
|
title_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
title_run = title_para.add_run(title)
|
|
title_run.font.size = Pt(28)
|
|
title_run.font.color.rgb = NAVY
|
|
title_run.font.bold = True
|
|
|
|
# Placeholders on cover
|
|
for label, placeholder in placeholders.items():
|
|
p = doc.add_paragraph()
|
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
r = p.add_run(f"{label}: {placeholder}")
|
|
r.font.size = Pt(12)
|
|
r.font.color.rgb = MEDIUM_GRAY
|
|
|
|
# Branding
|
|
brand = doc.add_paragraph()
|
|
brand.space_before = Pt(48)
|
|
brand.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
br = brand.add_run(COMPANY_NAME)
|
|
br.font.size = Pt(10)
|
|
br.font.color.rgb = NAVY
|
|
|
|
addr = doc.add_paragraph()
|
|
addr.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
ar = addr.add_run(f"{COMPANY_ADDRESS} | {COMPANY_PHONE}")
|
|
ar.font.size = Pt(8)
|
|
ar.font.color.rgb = LIGHT_GRAY
|
|
|
|
doc.add_page_break()
|
|
|
|
|
|
def add_article(doc: Document, number: int, title: str, body_lines: list[str]):
|
|
"""Add an article/section with heading and body paragraphs."""
|
|
doc.add_heading(f"Article {number}: {title}", level=1)
|
|
for line in body_lines:
|
|
doc.add_paragraph(line)
|
|
|
|
|
|
def add_section(doc: Document, title: str, body_lines: list[str], level: int = 1):
|
|
"""Add a section with heading and body paragraphs."""
|
|
doc.add_heading(title, level=level)
|
|
for line in body_lines:
|
|
doc.add_paragraph(line)
|
|
|
|
|
|
def add_signature_block(doc: Document, signer_label: str = "{{ member_name }}"):
|
|
"""Add a signature line."""
|
|
doc.add_paragraph("")
|
|
p = doc.add_paragraph()
|
|
p.add_run("_" * 50)
|
|
name_p = doc.add_paragraph()
|
|
r = name_p.add_run(signer_label)
|
|
r.font.size = Pt(11)
|
|
|
|
date_p = doc.add_paragraph()
|
|
date_p.add_run("Date: ____________________")
|
|
|
|
|
|
def add_table(doc: Document, headers: list[str], rows: list[list[str]]):
|
|
"""Add a formatted table."""
|
|
table = doc.add_table(rows=1 + len(rows), cols=len(headers))
|
|
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
|
|
|
# Header row
|
|
for i, header in enumerate(headers):
|
|
cell = table.rows[0].cells[i]
|
|
cell.text = header
|
|
for para in cell.paragraphs:
|
|
para.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
for run in para.runs:
|
|
run.font.bold = True
|
|
run.font.color.rgb = WHITE
|
|
run.font.size = Pt(10)
|
|
set_cell_shading(cell, "2D4E78")
|
|
|
|
# Data rows
|
|
for r_idx, row in enumerate(rows):
|
|
for c_idx, text in enumerate(row):
|
|
cell = table.rows[r_idx + 1].cells[c_idx]
|
|
cell.text = text
|
|
for para in cell.paragraphs:
|
|
for run in para.runs:
|
|
run.font.size = Pt(10)
|
|
|
|
return table
|
|
|
|
|
|
def add_disclaimer(doc: Document):
|
|
"""Add the standard disclaimer at the end."""
|
|
doc.add_paragraph("")
|
|
p = doc.add_paragraph()
|
|
r = p.add_run(DISCLAIMER)
|
|
r.font.size = Pt(8)
|
|
r.font.italic = True
|
|
r.font.color.rgb = LIGHT_GRAY
|
|
|
|
|
|
def save_template(doc: Document, name: str):
|
|
"""Save a template to the output directory."""
|
|
path = OUTPUT_DIR / f"{name}.docx"
|
|
doc.save(str(path))
|
|
print(f" Created: {path}")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Template Generators
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def create_operating_agreement():
|
|
"""Generate the standard LLC Operating Agreement template."""
|
|
doc = setup_document("Operating Agreement")
|
|
|
|
add_cover_page(doc, "Operating Agreement", {
|
|
"Entity": "{{ entity_name }}",
|
|
"State": "{{ state_name }}",
|
|
"Formation Date": "{{ formation_date }}",
|
|
})
|
|
|
|
doc.add_heading("Operating Agreement of {{ entity_name }}", level=1)
|
|
doc.add_paragraph(
|
|
"This Operating Agreement (the \"Agreement\") of {{ entity_name }}, "
|
|
"a limited liability company organized under the laws of the State of "
|
|
"{{ state_name }}, is entered into and effective as of {{ formation_date }}, "
|
|
"by and among the Members identified herein."
|
|
)
|
|
|
|
# Article I: Formation
|
|
add_article(doc, 1, "Formation", [
|
|
"1.1 Name. The name of the Company is {{ entity_name }}.",
|
|
"1.2 Formation. The Company was formed on {{ formation_date }} by filing "
|
|
"Articles of Organization with the Secretary of State of {{ state_name }}.",
|
|
"1.3 Principal Office. The principal office of the Company shall be at "
|
|
"{{ principal_address }}.",
|
|
"1.4 Registered Agent. The Company's registered agent is {{ registered_agent }} "
|
|
"at {{ registered_agent_address }}.",
|
|
"1.5 Term. The Company shall continue until dissolved in accordance with this "
|
|
"Agreement or applicable law.",
|
|
"1.6 Purpose. The Company is formed for any lawful purpose permitted under "
|
|
"the laws of the State of {{ state_name }}.",
|
|
])
|
|
|
|
# Article II: Members
|
|
add_article(doc, 2, "Members", [
|
|
"2.1 Initial Members. The initial Members of the Company and their respective "
|
|
"ownership interests are as follows:",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Member Name", "Ownership %", "Capital Contribution", "Membership Units"],
|
|
[
|
|
["{{ members[0].name }}", "{{ members[0].ownership_pct }}", "{{ members[0].capital }}", "{{ members[0].units }}"],
|
|
["{{ members[1].name }}", "{{ members[1].ownership_pct }}", "{{ members[1].capital }}", "{{ members[1].units }}"],
|
|
],
|
|
)
|
|
|
|
doc.add_paragraph(
|
|
"{% for member in members %}\n"
|
|
"{{ member.name }} — {{ member.ownership_pct }}% ownership\n"
|
|
"{% endfor %}"
|
|
)
|
|
|
|
# Article III: Management
|
|
add_article(doc, 3, "Management", [
|
|
"3.1 Management Structure. The Company shall be {{ management_type }}-managed.",
|
|
"3.2 Manager(s). {% if management_type == 'manager' %}The Manager(s) of the Company "
|
|
"shall be {{ managers }}.{% else %}All Members shall have the authority to manage the "
|
|
"business and affairs of the Company.{% endif %}",
|
|
"3.3 Voting. Each Member shall have voting rights proportional to their ownership "
|
|
"interest. Decisions requiring a vote shall be decided by a majority of ownership "
|
|
"interests, unless otherwise specified in this Agreement.",
|
|
"3.4 Officers. The Manager(s) or Members may appoint officers as needed.",
|
|
])
|
|
|
|
# Article IV: Capital Contributions
|
|
add_article(doc, 4, "Capital Contributions", [
|
|
"4.1 Initial Contributions. Each Member shall contribute the amount set forth "
|
|
"in Article 2.",
|
|
"4.2 Additional Contributions. No Member shall be required to make additional "
|
|
"capital contributions without the unanimous consent of all Members.",
|
|
"4.3 No Interest. No interest shall be paid on capital contributions.",
|
|
"4.4 Capital Accounts. A separate capital account shall be maintained for each "
|
|
"Member in accordance with applicable tax regulations.",
|
|
])
|
|
|
|
# Article V: Distributions
|
|
add_article(doc, 5, "Distributions", [
|
|
"5.1 Distributions. Distributions shall be made at such times and in such "
|
|
"amounts as determined by the Members. Distributions shall be allocated to "
|
|
"Members in proportion to their ownership interests.",
|
|
"5.2 Tax Distributions. The Company shall make quarterly tax distributions "
|
|
"to the Members sufficient to cover their estimated tax liability arising "
|
|
"from Company income.",
|
|
])
|
|
|
|
# Article VI: Meetings
|
|
add_article(doc, 6, "Meetings", [
|
|
"6.1 Annual Meeting. The Members shall hold an annual meeting at a time "
|
|
"and place determined by the Members.",
|
|
"6.2 Special Meetings. Any Member may call a special meeting with at least "
|
|
"10 days' written notice to all other Members.",
|
|
"6.3 Quorum. A quorum shall consist of Members holding a majority of the "
|
|
"total ownership interests.",
|
|
"6.4 Action Without Meeting. Any action that may be taken at a meeting may "
|
|
"be taken without a meeting if the Members holding the required votes consent "
|
|
"in writing.",
|
|
])
|
|
|
|
# Article VII: Transfer of Interest
|
|
add_article(doc, 7, "Transfer of Interest", [
|
|
"7.1 Restriction on Transfer. No Member may sell, assign, transfer, or "
|
|
"encumber their membership interest without the prior written consent of "
|
|
"Members holding a majority of the remaining ownership interests.",
|
|
"7.2 Right of First Refusal. Before any transfer, the selling Member must "
|
|
"first offer their interest to the remaining Members on the same terms.",
|
|
"7.3 Permitted Transfers. A Member may transfer their interest to a trust "
|
|
"for the benefit of the Member or the Member's immediate family without "
|
|
"the consent of other Members.",
|
|
])
|
|
|
|
# Article VIII: Dissolution
|
|
add_article(doc, 8, "Dissolution", [
|
|
"8.1 Events of Dissolution. The Company shall dissolve upon: (a) the written "
|
|
"consent of Members holding a majority of ownership interests; (b) the entry "
|
|
"of a judicial decree of dissolution; or (c) any event that makes it unlawful "
|
|
"for the Company to continue.",
|
|
"8.2 Winding Up. Upon dissolution, the Company's affairs shall be wound up "
|
|
"in accordance with applicable law. Assets shall be distributed in the "
|
|
"following order: (a) to creditors; (b) to Members for unreturned capital "
|
|
"contributions; (c) to Members in proportion to ownership interests.",
|
|
])
|
|
|
|
# Article IX: Miscellaneous
|
|
add_article(doc, 9, "Miscellaneous", [
|
|
"9.1 Governing Law. This Agreement shall be governed by the laws of the "
|
|
"State of {{ state_name }}.",
|
|
"9.2 Amendments. This Agreement may be amended only by the written consent "
|
|
"of Members holding a majority of ownership interests.",
|
|
"9.3 Severability. If any provision of this Agreement is found invalid, "
|
|
"the remaining provisions shall remain in full force and effect.",
|
|
"9.4 Entire Agreement. This Agreement constitutes the entire agreement among "
|
|
"the Members and supersedes all prior agreements.",
|
|
"9.5 Indemnification. The Company shall indemnify each Member and Manager "
|
|
"against all liabilities incurred in connection with Company business, "
|
|
"except for willful misconduct or gross negligence.",
|
|
])
|
|
|
|
# Article X: Signatures
|
|
add_article(doc, 10, "Signatures", [
|
|
"IN WITNESS WHEREOF, the undersigned Members have executed this Operating "
|
|
"Agreement as of the date first written above.",
|
|
])
|
|
|
|
doc.add_paragraph(
|
|
"{% for member in members %}"
|
|
)
|
|
add_signature_block(doc, "{{ member.name }}")
|
|
doc.add_paragraph(
|
|
"{% endfor %}"
|
|
)
|
|
|
|
add_disclaimer(doc)
|
|
save_template(doc, "operating-agreement")
|
|
|
|
|
|
def create_privacy_policy():
|
|
"""Generate the Website Privacy Policy template."""
|
|
doc = setup_document("Privacy Policy")
|
|
|
|
add_cover_page(doc, "Privacy Policy", {
|
|
"Company": "{{ company_name }}",
|
|
"Website": "{{ website_url }}",
|
|
"Effective Date": "{{ effective_date }}",
|
|
})
|
|
|
|
doc.add_heading("Privacy Policy", level=1)
|
|
doc.add_paragraph(
|
|
"Last updated: {{ effective_date }}"
|
|
)
|
|
doc.add_paragraph(
|
|
"{{ company_name }} (\"Company,\" \"we,\" \"us,\" or \"our\") operates the "
|
|
"website {{ website_url }} (the \"Site\"). This Privacy Policy explains how "
|
|
"we collect, use, disclose, and safeguard your information when you visit "
|
|
"our Site."
|
|
)
|
|
|
|
add_section(doc, "1. Information We Collect", [
|
|
"We collect information that you provide directly to us, including:",
|
|
"- Name, email address, phone number, and mailing address",
|
|
"- Company name and business information",
|
|
"- Payment information (processed securely by third-party processors)",
|
|
"- Communications you send to us (e.g., support requests)",
|
|
"",
|
|
"We automatically collect certain information when you visit our Site:",
|
|
"- IP address and browser type",
|
|
"- Pages viewed and time spent on each page",
|
|
"- Referring website or search terms",
|
|
"- Device information and operating system",
|
|
])
|
|
|
|
add_section(doc, "2. How We Use Your Information", [
|
|
"We use collected information to:",
|
|
"- Process orders and provide requested services",
|
|
"- Communicate with you about your account or services",
|
|
"- Send marketing communications (with your consent)",
|
|
"- Improve our website and services",
|
|
"- Comply with legal obligations",
|
|
"- Detect and prevent fraud",
|
|
])
|
|
|
|
add_section(doc, "3. Information Sharing", [
|
|
"We do not sell your personal information. We may share information with:",
|
|
"- Service providers who assist in operating our business",
|
|
"- State agencies as required for business formation services",
|
|
"- Legal authorities when required by law",
|
|
"- Business partners with your explicit consent",
|
|
])
|
|
|
|
add_section(doc, "4. California Privacy Rights (CCPA/CPRA)", [
|
|
"{{ ccpa_section }}",
|
|
])
|
|
|
|
add_section(doc, "5. Cookies and Tracking", [
|
|
"We use cookies and similar technologies to:",
|
|
"- Maintain your session and preferences",
|
|
"- Analyze site usage (via privacy-respecting analytics)",
|
|
"- Improve site performance",
|
|
"",
|
|
"You may disable cookies in your browser settings. Some site features may "
|
|
"not function properly without cookies.",
|
|
])
|
|
|
|
add_section(doc, "6. Data Security", [
|
|
"We implement reasonable administrative, technical, and physical security "
|
|
"measures to protect your personal information, including:",
|
|
"- Encryption of data in transit (TLS) and at rest",
|
|
"- Access controls and authentication requirements",
|
|
"- Regular security assessments",
|
|
"",
|
|
"No method of transmission over the Internet is 100% secure. We cannot "
|
|
"guarantee absolute security of your data.",
|
|
])
|
|
|
|
add_section(doc, "7. Children's Privacy", [
|
|
"Our Site is not directed to individuals under 13 years of age. We do not "
|
|
"knowingly collect personal information from children under 13. If we learn "
|
|
"we have collected such information, we will take steps to delete it.",
|
|
])
|
|
|
|
add_section(doc, "8. Changes to This Policy", [
|
|
"We may update this Privacy Policy from time to time. We will notify you of "
|
|
"any material changes by posting the new policy on our Site with a revised "
|
|
"effective date.",
|
|
])
|
|
|
|
add_section(doc, "9. Contact Us", [
|
|
"If you have questions about this Privacy Policy, contact us at:",
|
|
"",
|
|
"{{ company_name }}",
|
|
"{{ physical_address }}",
|
|
"Email: {{ contact_email }}",
|
|
])
|
|
|
|
add_disclaimer(doc)
|
|
save_template(doc, "privacy-policy")
|
|
|
|
|
|
def create_breach_response_plan():
|
|
"""Generate the Data Breach Response Plan template."""
|
|
doc = setup_document("Data Breach Response Plan")
|
|
|
|
add_cover_page(doc, "Data Breach Response Plan", {
|
|
"Company": "{{ company_name }}",
|
|
"Contact": "{{ contact_name }}",
|
|
})
|
|
|
|
doc.add_heading("Data Breach Response Plan", level=1)
|
|
doc.add_paragraph(
|
|
"Prepared for {{ company_name }}"
|
|
)
|
|
|
|
add_section(doc, "1. Purpose", [
|
|
"This Data Breach Response Plan (\"Plan\") establishes procedures for "
|
|
"{{ company_name }} to follow in the event of a suspected or confirmed data "
|
|
"breach involving personal information. The Plan is designed to ensure rapid "
|
|
"containment, thorough investigation, timely notification, and effective "
|
|
"remediation.",
|
|
])
|
|
|
|
add_section(doc, "2. Scope", [
|
|
"This Plan applies to all employees, contractors, and agents of "
|
|
"{{ company_name }} who create, receive, maintain, or transmit personal "
|
|
"information. It covers all forms of personal information, whether in "
|
|
"electronic or physical form.",
|
|
])
|
|
|
|
add_section(doc, "3. Incident Response Team (IRT)", [
|
|
"The following individuals comprise the Incident Response Team:",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Role", "Name", "Phone", "Email"],
|
|
[
|
|
["IRT Lead", "{{ irt_members[0].name }}", "{{ irt_members[0].phone }}", "{{ irt_members[0].email }}"],
|
|
["IT Security", "{{ irt_members[1].name }}", "{{ irt_members[1].phone }}", "{{ irt_members[1].email }}"],
|
|
["Legal Counsel", "{{ irt_members[2].name }}", "{{ irt_members[2].phone }}", "{{ irt_members[2].email }}"],
|
|
["Communications", "{{ irt_members[3].name }}", "{{ irt_members[3].phone }}", "{{ irt_members[3].email }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "4. Incident Classification", [
|
|
"Incidents shall be classified by severity:",
|
|
"",
|
|
"Level 1 (Low): Suspected but unconfirmed breach with no evidence of data "
|
|
"exfiltration. Fewer than 100 records potentially affected.",
|
|
"",
|
|
"Level 2 (Medium): Confirmed breach with limited data exposure. 100-1,000 "
|
|
"records affected. No financial or health data involved.",
|
|
"",
|
|
"Level 3 (High): Confirmed breach involving sensitive data (SSN, financial, "
|
|
"health). More than 1,000 records affected, or any number of records "
|
|
"involving highly sensitive data.",
|
|
"",
|
|
"Level 4 (Critical): Large-scale breach affecting more than 10,000 records, "
|
|
"or involving data that poses immediate risk of identity theft or financial harm.",
|
|
])
|
|
|
|
add_section(doc, "5. Response Procedures", [
|
|
"5.1 Discovery and Reporting",
|
|
"Any employee who suspects a data breach must immediately notify the IRT Lead "
|
|
"at {{ contact_name }}. Do not attempt to investigate independently or "
|
|
"communicate externally about the incident.",
|
|
"",
|
|
"5.2 Containment (0-4 hours)",
|
|
"- Isolate affected systems from the network",
|
|
"- Preserve forensic evidence (do not wipe or rebuild)",
|
|
"- Change compromised credentials",
|
|
"- Engage IT Security for initial assessment",
|
|
"",
|
|
"5.3 Investigation (4-48 hours)",
|
|
"- Determine the type and scope of data compromised",
|
|
"- Identify the attack vector or root cause",
|
|
"- Assess the number of affected individuals",
|
|
"- Document all findings in the incident log",
|
|
"",
|
|
"5.4 Eradication and Recovery (48-72 hours)",
|
|
"- Remove the cause of the breach",
|
|
"- Patch vulnerabilities",
|
|
"- Restore systems from clean backups",
|
|
"- Verify system integrity before returning to production",
|
|
])
|
|
|
|
add_section(doc, "6. Notification Requirements", [
|
|
"Notification timelines and requirements vary by state. The following "
|
|
"summarizes requirements for states where {{ company_name }} operates:",
|
|
"",
|
|
"{{ state_notification_requirements }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["State", "Notification Deadline", "AG Notification", "Threshold"],
|
|
[
|
|
["{{ state_reqs[0].state }}", "{{ state_reqs[0].deadline }}", "{{ state_reqs[0].ag_notify }}", "{{ state_reqs[0].threshold }}"],
|
|
["{{ state_reqs[1].state }}", "{{ state_reqs[1].deadline }}", "{{ state_reqs[1].ag_notify }}", "{{ state_reqs[1].threshold }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "7. Communication Templates", [
|
|
"7.1 Individual Notification Letter",
|
|
"A template notification letter is maintained as Appendix A and shall be "
|
|
"customized for each incident based on the type of data compromised.",
|
|
"",
|
|
"7.2 Regulator Notification",
|
|
"Attorney General notifications shall follow the format specified by each "
|
|
"state's data breach notification statute.",
|
|
"",
|
|
"7.3 Media Statement",
|
|
"Public statements shall be reviewed by Legal Counsel and Communications "
|
|
"before release.",
|
|
])
|
|
|
|
add_section(doc, "8. Post-Incident Review", [
|
|
"Within 30 days of incident resolution, the IRT shall conduct a "
|
|
"post-incident review covering:",
|
|
"- Timeline of events",
|
|
"- Effectiveness of response procedures",
|
|
"- Root cause analysis",
|
|
"- Lessons learned and recommendations",
|
|
"- Updates to this Plan",
|
|
])
|
|
|
|
add_disclaimer(doc)
|
|
save_template(doc, "breach-response-plan")
|
|
|
|
|
|
def create_flsa_audit_report():
|
|
"""Generate the FLSA/Wage & Hour Audit Report template."""
|
|
doc = setup_document("FLSA Audit Report")
|
|
|
|
add_cover_page(doc, "FLSA / Wage & Hour\nAudit Report", {
|
|
"Company": "{{ company_name }}",
|
|
"Audit Date": "{{ audit_date }}",
|
|
"Employees Reviewed": "{{ employee_count }}",
|
|
})
|
|
|
|
doc.add_heading("FLSA / Wage & Hour Audit Report", level=1)
|
|
doc.add_paragraph("Prepared for {{ company_name }} | {{ audit_date }}")
|
|
|
|
add_section(doc, "1. Executive Summary", [
|
|
"{{ executive_summary }}",
|
|
])
|
|
|
|
add_section(doc, "2. Scope and Methodology", [
|
|
"This audit reviewed {{ company_name }}'s compliance with the Fair Labor "
|
|
"Standards Act (FLSA) and applicable state wage and hour laws. The review "
|
|
"encompassed {{ employee_count }} employees across all departments.",
|
|
"",
|
|
"The audit methodology included:",
|
|
"- Review of employee classification records (exempt vs. non-exempt)",
|
|
"- Analysis of overtime calculation and payment practices",
|
|
"- Examination of timekeeping and recordkeeping procedures",
|
|
"- Review of pay stubs and payroll records",
|
|
"- Interviews with HR personnel and department managers",
|
|
])
|
|
|
|
add_section(doc, "3. Employee Classification Analysis", [
|
|
"{{ classification_analysis }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Position", "Current Classification", "Recommended Classification", "Risk Level"],
|
|
[
|
|
["{{ positions[0].title }}", "{{ positions[0].current }}", "{{ positions[0].recommended }}", "{{ positions[0].risk }}"],
|
|
["{{ positions[1].title }}", "{{ positions[1].current }}", "{{ positions[1].recommended }}", "{{ positions[1].risk }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "4. Overtime Compliance Analysis", [
|
|
"{{ overtime_analysis }}",
|
|
])
|
|
|
|
add_section(doc, "5. Recordkeeping Review", [
|
|
"{{ recordkeeping_review }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Requirement", "Status", "Finding", "Priority"],
|
|
[
|
|
["Time records maintained", "{{ records[0].status }}", "{{ records[0].finding }}", "{{ records[0].priority }}"],
|
|
["Wage notices provided", "{{ records[1].status }}", "{{ records[1].finding }}", "{{ records[1].priority }}"],
|
|
["Pay stubs compliant", "{{ records[2].status }}", "{{ records[2].finding }}", "{{ records[2].priority }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "6. Remediation Plan", [
|
|
"{{ remediation_plan }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Finding", "Action Required", "Responsible Party", "Deadline", "Priority"],
|
|
[
|
|
["{{ actions[0].finding }}", "{{ actions[0].action }}", "{{ actions[0].owner }}", "{{ actions[0].deadline }}", "{{ actions[0].priority }}"],
|
|
["{{ actions[1].finding }}", "{{ actions[1].action }}", "{{ actions[1].owner }}", "{{ actions[1].deadline }}", "{{ actions[1].priority }}"],
|
|
],
|
|
)
|
|
|
|
add_disclaimer(doc)
|
|
save_template(doc, "flsa-audit-report")
|
|
|
|
|
|
def create_contractor_assessment():
|
|
"""Generate the Contractor Classification Assessment template."""
|
|
doc = setup_document("Contractor Classification Assessment")
|
|
|
|
add_cover_page(doc, "Independent Contractor\nClassification Assessment", {
|
|
"Company": "{{ company_name }}",
|
|
"Worker": "{{ worker_name }}",
|
|
"Assessment Date": "{{ assessment_date }}",
|
|
})
|
|
|
|
doc.add_heading("Independent Contractor Classification Assessment", level=1)
|
|
doc.add_paragraph(
|
|
"Assessment of {{ worker_name }} for {{ company_name }} | {{ assessment_date }}"
|
|
)
|
|
|
|
add_section(doc, "1. Worker Information", [
|
|
"Worker Name: {{ worker_name }}",
|
|
"Engagement Start Date: {{ engagement_start }}",
|
|
"Job Title/Role: {{ job_title }}",
|
|
"Current Classification: {{ current_classification }}",
|
|
"Compensation Structure: {{ compensation_structure }}",
|
|
])
|
|
|
|
add_section(doc, "2. IRS 20-Factor Analysis", [
|
|
"The IRS uses a 20-factor test (Revenue Ruling 87-41) to determine worker "
|
|
"classification. The following analysis applies these factors to the "
|
|
"engagement between {{ company_name }} and {{ worker_name }}.",
|
|
"",
|
|
"{{ irs_analysis }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Factor", "Description", "Indicates", "Notes"],
|
|
[
|
|
["1. Instructions", "Level of control over how work is done", "{{ irs_factors[0].indicates }}", "{{ irs_factors[0].notes }}"],
|
|
["2. Training", "Whether company provides training", "{{ irs_factors[1].indicates }}", "{{ irs_factors[1].notes }}"],
|
|
["3. Integration", "Integration into business operations", "{{ irs_factors[2].indicates }}", "{{ irs_factors[2].notes }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "3. DOL Economic Reality Test", [
|
|
"The Department of Labor uses the economic reality test to determine whether "
|
|
"a worker is an employee under the FLSA.",
|
|
"",
|
|
"{{ dol_analysis }}",
|
|
])
|
|
|
|
add_section(doc, "4. State-Specific Analysis", [
|
|
"{{ state_analysis }}",
|
|
])
|
|
|
|
add_section(doc, "5. Risk Assessment", [
|
|
"Overall Risk Level: {{ risk_level }}",
|
|
"",
|
|
"Factors contributing to risk assessment:",
|
|
"{{ risk_factors }}",
|
|
])
|
|
|
|
add_section(doc, "6. Recommendation", [
|
|
"{{ recommendation }}",
|
|
])
|
|
|
|
add_section(doc, "7. Next Steps", [
|
|
"If reclassification is recommended:",
|
|
"- Calculate back taxes and benefits owed",
|
|
"- Prepare amended tax filings (Form 941-X, Form W-2c)",
|
|
"- Update employment agreements and policies",
|
|
"- Implement prospective changes within 30 days",
|
|
"",
|
|
"If current classification is appropriate:",
|
|
"- Document supporting factors",
|
|
"- Review and update independent contractor agreement",
|
|
"- Schedule periodic reassessment (recommended annually)",
|
|
])
|
|
|
|
add_disclaimer(doc)
|
|
save_template(doc, "contractor-assessment")
|
|
|
|
|
|
def create_handbook_review():
|
|
"""Generate the Employee Handbook Review Report template."""
|
|
doc = setup_document("Employee Handbook Review Report")
|
|
|
|
add_cover_page(doc, "Employee Handbook\nReview Report", {
|
|
"Company": "{{ company_name }}",
|
|
"Review Date": "{{ review_date }}",
|
|
})
|
|
|
|
doc.add_heading("Employee Handbook Review Report", level=1)
|
|
doc.add_paragraph("Prepared for {{ company_name }} | {{ review_date }}")
|
|
|
|
add_section(doc, "1. Executive Summary", [
|
|
"{{ summary }}",
|
|
])
|
|
|
|
add_section(doc, "2. Review Methodology", [
|
|
"This review assessed {{ company_name }}'s employee handbook against:",
|
|
"- Federal employment law requirements (FLSA, FMLA, ADA, Title VII, OSHA)",
|
|
"- State-specific employment law requirements",
|
|
"- Industry best practices for employee handbooks",
|
|
"- Recent regulatory changes and court decisions",
|
|
"",
|
|
"Each policy was evaluated for legal compliance, clarity, completeness, "
|
|
"and consistency with actual company practices.",
|
|
])
|
|
|
|
add_section(doc, "3. Detailed Findings", [
|
|
"{{ findings }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Policy Area", "Status", "Finding", "Severity", "Reference"],
|
|
[
|
|
["{{ finding_items[0].area }}", "{{ finding_items[0].status }}", "{{ finding_items[0].finding }}", "{{ finding_items[0].severity }}", "{{ finding_items[0].reference }}"],
|
|
["{{ finding_items[1].area }}", "{{ finding_items[1].status }}", "{{ finding_items[1].finding }}", "{{ finding_items[1].severity }}", "{{ finding_items[1].reference }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "4. Gap Analysis", [
|
|
"The following required policies are missing from the current handbook:",
|
|
"",
|
|
"{{ gap_analysis }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Required Policy", "Legal Basis", "Priority", "Template Available"],
|
|
[
|
|
["{{ gaps[0].policy }}", "{{ gaps[0].legal_basis }}", "{{ gaps[0].priority }}", "{{ gaps[0].template }}"],
|
|
["{{ gaps[1].policy }}", "{{ gaps[1].legal_basis }}", "{{ gaps[1].priority }}", "{{ gaps[1].template }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "5. Recommendations", [
|
|
"{{ recommendations }}",
|
|
])
|
|
|
|
add_section(doc, "6. Prioritized Action Plan", [
|
|
"Critical (within 30 days):",
|
|
"{{ critical_items }}",
|
|
"",
|
|
"High (within 60 days):",
|
|
"{{ high_items }}",
|
|
"",
|
|
"Medium (within 90 days):",
|
|
"{{ medium_items }}",
|
|
"",
|
|
"Low (within 180 days):",
|
|
"{{ low_items }}",
|
|
])
|
|
|
|
add_disclaimer(doc)
|
|
save_template(doc, "handbook-review")
|
|
|
|
|
|
def create_ccpa_audit_report():
|
|
"""Generate the CCPA/CPRA Compliance Audit Report template."""
|
|
doc = setup_document("CCPA/CPRA Compliance Audit Report")
|
|
|
|
add_cover_page(doc, "CCPA/CPRA Compliance\nAudit Report", {
|
|
"Company": "{{ company_name }}",
|
|
"Audit Date": "{{ audit_date }}",
|
|
})
|
|
|
|
doc.add_heading("CCPA/CPRA Compliance Audit Report", level=1)
|
|
doc.add_paragraph("Prepared for {{ company_name }} | {{ audit_date }}")
|
|
|
|
add_section(doc, "1. Executive Summary", [
|
|
"{{ executive_summary }}",
|
|
])
|
|
|
|
add_section(doc, "2. Applicability Assessment", [
|
|
"The California Consumer Privacy Act (CCPA), as amended by the California "
|
|
"Privacy Rights Act (CPRA), applies to for-profit businesses that:",
|
|
"- Have annual gross revenue exceeding $25 million",
|
|
"- Buy, sell, or share the personal information of 100,000+ California "
|
|
"residents, households, or devices",
|
|
"- Derive 50% or more of annual revenue from selling/sharing personal information",
|
|
"",
|
|
"Assessment of {{ company_name }}'s applicability:",
|
|
"{{ applicability_assessment }}",
|
|
])
|
|
|
|
add_section(doc, "3. Data Inventory", [
|
|
"{{ data_inventory }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Data Category", "Collected", "Source", "Purpose", "Shared With", "Retention"],
|
|
[
|
|
["{{ inventory[0].category }}", "{{ inventory[0].collected }}", "{{ inventory[0].source }}", "{{ inventory[0].purpose }}", "{{ inventory[0].shared }}", "{{ inventory[0].retention }}"],
|
|
["{{ inventory[1].category }}", "{{ inventory[1].collected }}", "{{ inventory[1].source }}", "{{ inventory[1].purpose }}", "{{ inventory[1].shared }}", "{{ inventory[1].retention }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "4. Privacy Notice Review", [
|
|
"{{ privacy_notice_review }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Requirement", "Status", "Finding", "Recommendation"],
|
|
[
|
|
["Notice at collection", "{{ notice[0].status }}", "{{ notice[0].finding }}", "{{ notice[0].recommendation }}"],
|
|
["Right to know", "{{ notice[1].status }}", "{{ notice[1].finding }}", "{{ notice[1].recommendation }}"],
|
|
["Right to delete", "{{ notice[2].status }}", "{{ notice[2].finding }}", "{{ notice[2].recommendation }}"],
|
|
["Right to opt-out", "{{ notice[3].status }}", "{{ notice[3].finding }}", "{{ notice[3].recommendation }}"],
|
|
["Right to correct", "{{ notice[4].status }}", "{{ notice[4].finding }}", "{{ notice[4].recommendation }}"],
|
|
["Right to limit use", "{{ notice[5].status }}", "{{ notice[5].finding }}", "{{ notice[5].recommendation }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "5. Consumer Rights Assessment", [
|
|
"{{ consumer_rights_assessment }}",
|
|
])
|
|
|
|
add_section(doc, "6. Vendor/Service Provider Assessment", [
|
|
"{{ vendor_assessment }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Vendor", "Data Shared", "DPA in Place", "CCPA Clause", "Risk Level"],
|
|
[
|
|
["{{ vendors[0].name }}", "{{ vendors[0].data }}", "{{ vendors[0].dpa }}", "{{ vendors[0].ccpa_clause }}", "{{ vendors[0].risk }}"],
|
|
["{{ vendors[1].name }}", "{{ vendors[1].data }}", "{{ vendors[1].dpa }}", "{{ vendors[1].ccpa_clause }}", "{{ vendors[1].risk }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "7. Remediation Plan", [
|
|
"{{ remediation_plan }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Finding", "Action Required", "Owner", "Deadline", "Status"],
|
|
[
|
|
["{{ ccpa_actions[0].finding }}", "{{ ccpa_actions[0].action }}", "{{ ccpa_actions[0].owner }}", "{{ ccpa_actions[0].deadline }}", "{{ ccpa_actions[0].status }}"],
|
|
["{{ ccpa_actions[1].finding }}", "{{ ccpa_actions[1].action }}", "{{ ccpa_actions[1].owner }}", "{{ ccpa_actions[1].deadline }}", "{{ ccpa_actions[1].status }}"],
|
|
],
|
|
)
|
|
|
|
add_disclaimer(doc)
|
|
save_template(doc, "ccpa-audit-report")
|
|
|
|
|
|
def create_tcpa_audit_report():
|
|
"""Generate the TCPA Consent Audit Report template."""
|
|
doc = setup_document("TCPA Consent Audit Report")
|
|
|
|
add_cover_page(doc, "TCPA Consent\nAudit Report", {
|
|
"Company": "{{ company_name }}",
|
|
"Audit Date": "{{ audit_date }}",
|
|
})
|
|
|
|
doc.add_heading("TCPA Consent Audit Report", level=1)
|
|
doc.add_paragraph("Prepared for {{ company_name }} | {{ audit_date }}")
|
|
|
|
add_section(doc, "1. Executive Summary", [
|
|
"{{ executive_summary }}",
|
|
])
|
|
|
|
add_section(doc, "2. Regulatory Background", [
|
|
"The Telephone Consumer Protection Act (TCPA), 47 U.S.C. 227, restricts "
|
|
"telemarketing calls, auto-dialed calls, prerecorded/artificial voice "
|
|
"messages, text messages, and unsolicited faxes. Violations carry statutory "
|
|
"damages of $500-$1,500 per violation.",
|
|
"",
|
|
"Key requirements include:",
|
|
"- Prior express written consent for marketing calls/texts using an ATDS "
|
|
"or prerecorded voice",
|
|
"- Prior express consent for non-marketing calls using an ATDS",
|
|
"- Maintaining an internal Do-Not-Call (DNC) list",
|
|
"- Honoring the National DNC Registry",
|
|
"- Identifying the caller and providing opt-out mechanisms",
|
|
])
|
|
|
|
add_section(doc, "3. Consent Analysis", [
|
|
"{{ consent_analysis }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Consent Type", "Required For", "Current Status", "Compliant", "Notes"],
|
|
[
|
|
["Express written", "Marketing ATDS/prerecorded", "{{ consent[0].status }}", "{{ consent[0].compliant }}", "{{ consent[0].notes }}"],
|
|
["Express (oral)", "Non-marketing ATDS", "{{ consent[1].status }}", "{{ consent[1].compliant }}", "{{ consent[1].notes }}"],
|
|
["None (exempted)", "Manual dialing, existing relationship", "{{ consent[2].status }}", "{{ consent[2].compliant }}", "{{ consent[2].notes }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "4. Do-Not-Call Compliance", [
|
|
"{{ dnc_compliance }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["DNC Requirement", "Status", "Finding"],
|
|
[
|
|
["Internal DNC list maintained", "{{ dnc[0].status }}", "{{ dnc[0].finding }}"],
|
|
["National DNC Registry subscribed", "{{ dnc[1].status }}", "{{ dnc[1].finding }}"],
|
|
["DNC requests honored within 30 days", "{{ dnc[2].status }}", "{{ dnc[2].finding }}"],
|
|
["DNC records retained 5+ years", "{{ dnc[3].status }}", "{{ dnc[3].finding }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "5. Campaign Review", [
|
|
"{{ campaign_review }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Campaign", "Channel", "Consent Type", "Volume", "Compliant", "Issues"],
|
|
[
|
|
["{{ campaigns[0].name }}", "{{ campaigns[0].channel }}", "{{ campaigns[0].consent }}", "{{ campaigns[0].volume }}", "{{ campaigns[0].compliant }}", "{{ campaigns[0].issues }}"],
|
|
["{{ campaigns[1].name }}", "{{ campaigns[1].channel }}", "{{ campaigns[1].consent }}", "{{ campaigns[1].volume }}", "{{ campaigns[1].compliant }}", "{{ campaigns[1].issues }}"],
|
|
],
|
|
)
|
|
|
|
add_section(doc, "6. Remediation Plan", [
|
|
"{{ remediation_plan }}",
|
|
])
|
|
|
|
add_table(doc,
|
|
["Finding", "Action Required", "Owner", "Deadline", "Priority"],
|
|
[
|
|
["{{ tcpa_actions[0].finding }}", "{{ tcpa_actions[0].action }}", "{{ tcpa_actions[0].owner }}", "{{ tcpa_actions[0].deadline }}", "{{ tcpa_actions[0].priority }}"],
|
|
["{{ tcpa_actions[1].finding }}", "{{ tcpa_actions[1].action }}", "{{ tcpa_actions[1].owner }}", "{{ tcpa_actions[1].deadline }}", "{{ tcpa_actions[1].priority }}"],
|
|
],
|
|
)
|
|
|
|
add_disclaimer(doc)
|
|
save_template(doc, "tcpa-audit-report")
|
|
|
|
|
|
def create_invoice():
|
|
"""Generate the Service Invoice template."""
|
|
doc = setup_document("Invoice")
|
|
|
|
# No cover page for invoices — more compact format
|
|
|
|
# Company header
|
|
brand = doc.add_paragraph()
|
|
brand.alignment = WD_ALIGN_PARAGRAPH.LEFT
|
|
br = brand.add_run(COMPANY_NAME)
|
|
br.font.size = Pt(16)
|
|
br.font.color.rgb = NAVY
|
|
br.font.bold = True
|
|
|
|
addr = doc.add_paragraph()
|
|
ar = addr.add_run(f"{COMPANY_ADDRESS}\n{COMPANY_PHONE} | {COMPANY_WEBSITE}")
|
|
ar.font.size = Pt(9)
|
|
ar.font.color.rgb = MEDIUM_GRAY
|
|
|
|
# Invoice title
|
|
inv_title = doc.add_paragraph()
|
|
inv_title.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
|
inv_run = inv_title.add_run("INVOICE")
|
|
inv_run.font.size = Pt(24)
|
|
inv_run.font.color.rgb = NAVY
|
|
inv_run.font.bold = True
|
|
|
|
# Invoice metadata
|
|
meta = doc.add_paragraph()
|
|
meta.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
|
meta.add_run("Invoice #: {{ invoice_number }}\n").font.size = Pt(10)
|
|
meta.add_run("Date: {{ invoice_date }}\n").font.size = Pt(10)
|
|
meta.add_run("Due Date: {{ due_date }}").font.size = Pt(10)
|
|
|
|
doc.add_paragraph("")
|
|
|
|
# Bill To
|
|
doc.add_heading("Bill To:", level=2)
|
|
doc.add_paragraph("{{ customer_name }}")
|
|
doc.add_paragraph("{{ customer_address }}")
|
|
|
|
doc.add_paragraph("")
|
|
|
|
# Line items table
|
|
add_table(doc,
|
|
["Description", "Quantity", "Unit Price", "Amount"],
|
|
[
|
|
["{{ line_items[0].description }}", "{{ line_items[0].quantity }}", "{{ line_items[0].unit_price }}", "{{ line_items[0].amount }}"],
|
|
["{{ line_items[1].description }}", "{{ line_items[1].quantity }}", "{{ line_items[1].unit_price }}", "{{ line_items[1].amount }}"],
|
|
["{{ line_items[2].description }}", "{{ line_items[2].quantity }}", "{{ line_items[2].unit_price }}", "{{ line_items[2].amount }}"],
|
|
],
|
|
)
|
|
|
|
doc.add_paragraph("")
|
|
|
|
# Totals
|
|
totals = doc.add_paragraph()
|
|
totals.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
|
totals.add_run("Subtotal: {{ subtotal }}\n").font.size = Pt(11)
|
|
|
|
discount_line = doc.add_paragraph()
|
|
discount_line.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
|
discount_line.add_run("Discount: {{ discount }}\n").font.size = Pt(11)
|
|
|
|
total_line = doc.add_paragraph()
|
|
total_line.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
|
total_run = total_line.add_run("TOTAL: {{ total }}")
|
|
total_run.font.size = Pt(14)
|
|
total_run.font.bold = True
|
|
total_run.font.color.rgb = NAVY
|
|
|
|
doc.add_paragraph("")
|
|
|
|
# Payment instructions
|
|
add_section(doc, "Payment Instructions", [
|
|
"Payment is due within 30 days of invoice date.",
|
|
"",
|
|
"Accepted payment methods:",
|
|
"- ACH / Wire Transfer",
|
|
"- Credit Card (3% processing fee applies)",
|
|
"- Check (payable to Performance West Inc.)",
|
|
"",
|
|
"For questions about this invoice, contact billing@performancewest.net.",
|
|
])
|
|
|
|
# Thank you
|
|
thanks = doc.add_paragraph()
|
|
thanks.space_before = Pt(24)
|
|
thanks.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
thanks_run = thanks.add_run("Thank you for your business.")
|
|
thanks_run.font.italic = True
|
|
thanks_run.font.color.rgb = MEDIUM_GRAY
|
|
|
|
save_template(doc, "invoice")
|
|
|
|
|
|
def create_fcc_compliance_report():
|
|
"""Generate the FCC Carrier Compliance Checkup Report template."""
|
|
doc = setup_document("FCC Carrier Compliance Checkup Report")
|
|
|
|
add_cover_page(doc, "FCC Carrier Compliance\nCheckup Report", {
|
|
"Prepared for": "{{ customer_name }}",
|
|
"Entity": "{{ entity_name }}",
|
|
"FRN": "{{ frn }}",
|
|
"Order": "{{ order_number }}",
|
|
"Date": "{{ date }}",
|
|
})
|
|
|
|
doc.add_page_break()
|
|
|
|
# Executive Summary
|
|
doc.add_heading("Executive Summary", level=1)
|
|
doc.add_paragraph("{{ executive_summary }}")
|
|
|
|
# Section 1: CORES Registration
|
|
doc.add_heading("1. CORES Registration Status", level=1)
|
|
t1 = doc.add_table(rows=4, cols=2)
|
|
t1.style = "Table Grid"
|
|
for i, (label, placeholder) in enumerate([
|
|
("Registration Status", "{{ cores_status }}"),
|
|
("Red-Light Status", "{{ cores_red_light }}"),
|
|
("Entity Name on File", "{{ cores_entity_name }}"),
|
|
("Address on File", "{{ cores_address }}"),
|
|
]):
|
|
t1.rows[i].cells[0].paragraphs[0].add_run(label).bold = True
|
|
t1.rows[i].cells[1].text = placeholder
|
|
|
|
doc.add_paragraph()
|
|
doc.add_paragraph("{{ cores_detail }}")
|
|
|
|
# Section 2: RMD Filing Status
|
|
doc.add_heading("2. Robocall Mitigation Database (RMD) Status", level=1)
|
|
t2 = doc.add_table(rows=5, cols=2)
|
|
t2.style = "Table Grid"
|
|
for i, (label, placeholder) in enumerate([
|
|
("RMD Registration", "{{ rmd_status }}"),
|
|
("RMD Number", "{{ rmd_number }}"),
|
|
("Last Certification", "{{ rmd_last_cert }}"),
|
|
("Recertification Due", "{{ rmd_recert_due }}"),
|
|
("Removal Status", "{{ rmd_removal_status }}"),
|
|
]):
|
|
t2.rows[i].cells[0].paragraphs[0].add_run(label).bold = True
|
|
t2.rows[i].cells[1].text = placeholder
|
|
|
|
doc.add_paragraph()
|
|
doc.add_paragraph("{{ rmd_detail }}")
|
|
|
|
# Section 3: STIR/SHAKEN
|
|
doc.add_heading("3. STIR/SHAKEN Compliance", level=1)
|
|
t3 = doc.add_table(rows=3, cols=2)
|
|
t3.style = "Table Grid"
|
|
for i, (label, placeholder) in enumerate([
|
|
("Implementation Type", "{{ stir_shaken_type }}"),
|
|
("Certificate Authority", "{{ stir_shaken_cert }}"),
|
|
("Status", "{{ stir_shaken_status }}"),
|
|
]):
|
|
t3.rows[i].cells[0].paragraphs[0].add_run(label).bold = True
|
|
t3.rows[i].cells[1].text = placeholder
|
|
|
|
doc.add_paragraph()
|
|
doc.add_paragraph("{{ stir_shaken_detail }}")
|
|
|
|
# Section 4: CPNI
|
|
doc.add_heading("4. CPNI Annual Certification Status", level=1)
|
|
t4 = doc.add_table(rows=2, cols=2)
|
|
t4.style = "Table Grid"
|
|
for i, (label, placeholder) in enumerate([
|
|
("Certification Status", "{{ cpni_status }}"),
|
|
("Due Date", "{{ cpni_due_date }}"),
|
|
]):
|
|
t4.rows[i].cells[0].paragraphs[0].add_run(label).bold = True
|
|
t4.rows[i].cells[1].text = placeholder
|
|
|
|
doc.add_paragraph()
|
|
doc.add_paragraph("{{ cpni_detail }}")
|
|
|
|
# Section 5: 499-A
|
|
doc.add_heading("5. Form 499-A Status", level=1)
|
|
t5 = doc.add_table(rows=3, cols=2)
|
|
t5.style = "Table Grid"
|
|
for i, (label, placeholder) in enumerate([
|
|
("Filing Status", "{{ f499a_status }}"),
|
|
("Due Date", "{{ f499a_due_date }}"),
|
|
("Filer ID", "{{ f499a_filer_id }}"),
|
|
]):
|
|
t5.rows[i].cells[0].paragraphs[0].add_run(label).bold = True
|
|
t5.rows[i].cells[1].text = placeholder
|
|
|
|
doc.add_paragraph()
|
|
doc.add_paragraph("{{ f499a_detail }}")
|
|
|
|
# Section 6: 499-Q
|
|
doc.add_heading("6. Form 499-Q Status", level=1)
|
|
doc.add_paragraph("{{ f499q_detail }}")
|
|
|
|
# Section 7: Carrier Classification Summary
|
|
doc.add_heading("7. Carrier Classification Summary", level=1)
|
|
t7 = doc.add_table(rows=6, cols=2)
|
|
t7.style = "Table Grid"
|
|
for i, (label, placeholder) in enumerate([
|
|
("Carrier Category", "{{ carrier_category }}"),
|
|
("Infrastructure Type", "{{ infra_type }}"),
|
|
("Wholesale Provider", "{{ is_wholesale }}"),
|
|
("UCaaS Provider", "{{ ucaas_provider }}"),
|
|
("STIR/SHAKEN Status", "{{ classification_stir_shaken }}"),
|
|
("De Minimis", "{{ is_deminimis }}"),
|
|
]):
|
|
t7.rows[i].cells[0].paragraphs[0].add_run(label).bold = True
|
|
t7.rows[i].cells[1].text = placeholder
|
|
|
|
# Section 8: Recommended Actions
|
|
doc.add_heading("8. Recommended Actions", level=1)
|
|
doc.add_paragraph(
|
|
"The following actions are recommended to bring this entity into "
|
|
"full FCC compliance, listed by priority:"
|
|
)
|
|
doc.add_paragraph("{{ recommended_actions }}")
|
|
|
|
# Appendix
|
|
doc.add_page_break()
|
|
doc.add_heading("Appendix: Attached Documents", level=1)
|
|
doc.add_paragraph("{{ appendix_index }}")
|
|
|
|
# Disclaimer
|
|
doc.add_paragraph()
|
|
disc_para = doc.add_paragraph()
|
|
disc_run = disc_para.add_run(DISCLAIMER)
|
|
disc_run.font.size = Pt(8)
|
|
disc_run.font.italic = True
|
|
disc_run.font.color.rgb = LIGHT_GRAY
|
|
|
|
save_template(doc, "fcc_compliance_report_template")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Main
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def main():
|
|
print(f"Generating DOCX templates in {OUTPUT_DIR}/\n")
|
|
|
|
generators = [
|
|
("Operating Agreement", create_operating_agreement),
|
|
("Privacy Policy", create_privacy_policy),
|
|
("Breach Response Plan", create_breach_response_plan),
|
|
("FLSA Audit Report", create_flsa_audit_report),
|
|
("Contractor Assessment", create_contractor_assessment),
|
|
("Handbook Review", create_handbook_review),
|
|
("CCPA Audit Report", create_ccpa_audit_report),
|
|
("TCPA Audit Report", create_tcpa_audit_report),
|
|
("Invoice", create_invoice),
|
|
("FCC Compliance Report", create_fcc_compliance_report),
|
|
]
|
|
|
|
for name, gen_func in generators:
|
|
try:
|
|
gen_func()
|
|
except Exception as e:
|
|
print(f" ERROR generating {name}: {e}")
|
|
raise
|
|
|
|
print(f"\nDone. Generated {len(generators)} templates.")
|
|
print("\nTo upload to MinIO:")
|
|
print(f" mc cp {OUTPUT_DIR}/*.docx minio/performancewest/templates/")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|