Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:30:10 +08:00
commit f0bd18fb4e
824 changed files with 331919 additions and 0 deletions

View File

@@ -0,0 +1,256 @@
#!/usr/bin/env python3
"""
Invoice Template - Complete example of a professional invoice
This template demonstrates:
- Company header with logo placement
- Client information
- Invoice details table
- Calculations (subtotal, tax, total)
- Professional styling
- Terms and conditions footer
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER
from datetime import datetime
def create_invoice(
filename,
invoice_number,
invoice_date,
due_date,
company_info,
client_info,
items,
tax_rate=0.0,
notes="",
terms="Payment due within 30 days.",
logo_path=None
):
"""
Create a professional invoice PDF.
Args:
filename: Output PDF filename
invoice_number: Invoice number (e.g., "INV-2024-001")
invoice_date: Date of invoice (datetime or string)
due_date: Payment due date (datetime or string)
company_info: Dict with company details
{'name': 'Company Name', 'address': 'Address', 'phone': 'Phone', 'email': 'Email'}
client_info: Dict with client details (same structure as company_info)
items: List of dicts with item details
[{'description': 'Item', 'quantity': 1, 'unit_price': 100.00}, ...]
tax_rate: Tax rate as decimal (e.g., 0.08 for 8%)
notes: Additional notes to client
terms: Payment terms
logo_path: Path to company logo image (optional)
"""
# Create document
doc = SimpleDocTemplate(filename, pagesize=letter,
rightMargin=0.5*inch, leftMargin=0.5*inch,
topMargin=0.5*inch, bottomMargin=0.5*inch)
# Container for elements
story = []
styles = getSampleStyleSheet()
# Create custom styles
title_style = ParagraphStyle(
'InvoiceTitle',
parent=styles['Heading1'],
fontSize=24,
textColor=colors.HexColor('#2C3E50'),
spaceAfter=12,
)
header_style = ParagraphStyle(
'Header',
parent=styles['Normal'],
fontSize=10,
textColor=colors.HexColor('#34495E'),
)
# --- HEADER SECTION ---
header_data = []
# Company info (left side)
company_text = f"""
<b><font size="14">{company_info['name']}</font></b><br/>
{company_info.get('address', '')}<br/>
Phone: {company_info.get('phone', '')}<br/>
Email: {company_info.get('email', '')}
"""
# Invoice title and number (right side)
invoice_text = f"""
<b><font size="16" color="#2C3E50">INVOICE</font></b><br/>
<font size="10">Invoice #: {invoice_number}</font><br/>
<font size="10">Date: {invoice_date}</font><br/>
<font size="10">Due Date: {due_date}</font>
"""
if logo_path:
logo = Image(logo_path, width=1.5*inch, height=1*inch)
header_data = [[logo, Paragraph(company_text, header_style), Paragraph(invoice_text, header_style)]]
header_table = Table(header_data, colWidths=[1.5*inch, 3*inch, 2.5*inch])
else:
header_data = [[Paragraph(company_text, header_style), Paragraph(invoice_text, header_style)]]
header_table = Table(header_data, colWidths=[4.5*inch, 2.5*inch])
header_table.setStyle(TableStyle([
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('ALIGN', (-1, 0), (-1, -1), 'RIGHT'),
]))
story.append(header_table)
story.append(Spacer(1, 0.3*inch))
# --- CLIENT INFORMATION ---
client_label = Paragraph("<b>Bill To:</b>", header_style)
client_text = f"""
<b>{client_info['name']}</b><br/>
{client_info.get('address', '')}<br/>
Phone: {client_info.get('phone', '')}<br/>
Email: {client_info.get('email', '')}
"""
client_para = Paragraph(client_text, header_style)
client_table = Table([[client_label, client_para]], colWidths=[1*inch, 6*inch])
story.append(client_table)
story.append(Spacer(1, 0.3*inch))
# --- ITEMS TABLE ---
# Table header
items_data = [['Description', 'Quantity', 'Unit Price', 'Amount']]
# Calculate items
subtotal = 0
for item in items:
desc = item['description']
qty = item['quantity']
price = item['unit_price']
amount = qty * price
subtotal += amount
items_data.append([
desc,
str(qty),
f"${price:,.2f}",
f"${amount:,.2f}"
])
# Create items table
items_table = Table(items_data, colWidths=[3.5*inch, 1*inch, 1.5*inch, 1*inch])
items_table.setStyle(TableStyle([
# Header row
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#34495E')),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, 0), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, 0), 11),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
# Data rows
('BACKGROUND', (0, 1), (-1, -1), colors.white),
('ALIGN', (1, 1), (-1, -1), 'RIGHT'),
('ALIGN', (0, 1), (0, -1), 'LEFT'),
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
('FONTSIZE', (0, 1), (-1, -1), 10),
('TOPPADDING', (0, 1), (-1, -1), 6),
('BOTTOMPADDING', (0, 1), (-1, -1), 6),
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
]))
story.append(items_table)
story.append(Spacer(1, 0.2*inch))
# --- TOTALS SECTION ---
tax_amount = subtotal * tax_rate
total = subtotal + tax_amount
totals_data = [
['Subtotal:', f"${subtotal:,.2f}"],
]
if tax_rate > 0:
totals_data.append([f'Tax ({tax_rate*100:.1f}%):', f"${tax_amount:,.2f}"])
totals_data.append(['<b>Total:</b>', f"<b>${total:,.2f}</b>"])
totals_table = Table(totals_data, colWidths=[5*inch, 2*inch])
totals_table.setStyle(TableStyle([
('ALIGN', (0, 0), (-1, -1), 'RIGHT'),
('FONTNAME', (0, 0), (-1, -2), 'Helvetica'),
('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, -1), 11),
('TOPPADDING', (0, 0), (-1, -1), 6),
('LINEABOVE', (1, -1), (1, -1), 2, colors.HexColor('#34495E')),
]))
story.append(totals_table)
story.append(Spacer(1, 0.4*inch))
# --- NOTES ---
if notes:
notes_style = ParagraphStyle('Notes', parent=styles['Normal'], fontSize=9)
story.append(Paragraph(f"<b>Notes:</b><br/>{notes}", notes_style))
story.append(Spacer(1, 0.2*inch))
# --- TERMS ---
terms_style = ParagraphStyle('Terms', parent=styles['Normal'],
fontSize=9, textColor=colors.grey)
story.append(Paragraph(f"<b>Payment Terms:</b><br/>{terms}", terms_style))
# Build PDF
doc.build(story)
return filename
# Example usage
if __name__ == "__main__":
# Sample data
company = {
'name': 'Acme Corporation',
'address': '123 Business St, Suite 100\nNew York, NY 10001',
'phone': '(555) 123-4567',
'email': 'info@acme.com'
}
client = {
'name': 'John Doe',
'address': '456 Client Ave\nLos Angeles, CA 90001',
'phone': '(555) 987-6543',
'email': 'john@example.com'
}
items = [
{'description': 'Web Design Services', 'quantity': 1, 'unit_price': 2500.00},
{'description': 'Content Writing (10 pages)', 'quantity': 10, 'unit_price': 50.00},
{'description': 'SEO Optimization', 'quantity': 1, 'unit_price': 750.00},
{'description': 'Hosting Setup', 'quantity': 1, 'unit_price': 200.00},
]
create_invoice(
filename="sample_invoice.pdf",
invoice_number="INV-2024-001",
invoice_date="January 15, 2024",
due_date="February 15, 2024",
company_info=company,
client_info=client,
items=items,
tax_rate=0.08,
notes="Thank you for your business! We appreciate your prompt payment.",
terms="Payment due within 30 days. Late payments subject to 1.5% monthly fee.",
logo_path=None # Set to your logo path if available
)
print("Invoice created: sample_invoice.pdf")

View File

@@ -0,0 +1,343 @@
#!/usr/bin/env python3
"""
Report Template - Complete example of a professional multi-page report
This template demonstrates:
- Cover page
- Table of contents
- Multiple sections with headers
- Charts and graphs integration
- Tables with data
- Headers and footers
- Professional styling
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.lib import colors
from reportlab.platypus import (
BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer,
Table, TableStyle, PageBreak, KeepTogether, TableOfContents
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics.charts.linecharts import HorizontalLineChart
from datetime import datetime
def header_footer(canvas, doc):
"""Draw header and footer on each page (except cover)"""
canvas.saveState()
# Skip header/footer on cover page (page 1)
if doc.page > 1:
# Header
canvas.setFont('Helvetica', 9)
canvas.setFillColor(colors.grey)
canvas.drawString(inch, letter[1] - 0.5*inch, "Quarterly Business Report")
canvas.line(inch, letter[1] - 0.55*inch, letter[0] - inch, letter[1] - 0.55*inch)
# Footer
canvas.drawString(inch, 0.5*inch, f"Generated: {datetime.now().strftime('%B %d, %Y')}")
canvas.drawRightString(letter[0] - inch, 0.5*inch, f"Page {doc.page - 1}")
canvas.restoreState()
def create_report(filename, report_data):
"""
Create a comprehensive business report.
Args:
filename: Output PDF filename
report_data: Dict containing report information
{
'title': 'Report Title',
'subtitle': 'Report Subtitle',
'author': 'Author Name',
'date': 'Date',
'sections': [
{
'title': 'Section Title',
'content': 'Section content...',
'subsections': [...],
'table': {...},
'chart': {...}
},
...
]
}
"""
# Create document with custom page template
doc = BaseDocTemplate(filename, pagesize=letter,
rightMargin=72, leftMargin=72,
topMargin=inch, bottomMargin=inch)
# Define frame for content
frame = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height - 0.5*inch, id='normal')
# Create page template with header/footer
template = PageTemplate(id='normal', frames=[frame], onPage=header_footer)
doc.addPageTemplates([template])
# Get styles
styles = getSampleStyleSheet()
# Custom styles
title_style = ParagraphStyle(
'ReportTitle',
parent=styles['Title'],
fontSize=28,
textColor=colors.HexColor('#2C3E50'),
spaceAfter=20,
alignment=TA_CENTER,
)
subtitle_style = ParagraphStyle(
'ReportSubtitle',
parent=styles['Normal'],
fontSize=14,
textColor=colors.grey,
alignment=TA_CENTER,
spaceAfter=30,
)
heading1_style = ParagraphStyle(
'CustomHeading1',
parent=styles['Heading1'],
fontSize=18,
textColor=colors.HexColor('#2C3E50'),
spaceAfter=12,
spaceBefore=12,
)
heading2_style = ParagraphStyle(
'CustomHeading2',
parent=styles['Heading2'],
fontSize=14,
textColor=colors.HexColor('#34495E'),
spaceAfter=10,
spaceBefore=10,
)
body_style = ParagraphStyle(
'ReportBody',
parent=styles['BodyText'],
fontSize=11,
alignment=TA_JUSTIFY,
spaceAfter=12,
leading=14,
)
# Build story
story = []
# --- COVER PAGE ---
story.append(Spacer(1, 2*inch))
story.append(Paragraph(report_data['title'], title_style))
story.append(Paragraph(report_data.get('subtitle', ''), subtitle_style))
story.append(Spacer(1, inch))
# Cover info table
cover_info = [
['Prepared by:', report_data.get('author', '')],
['Date:', report_data.get('date', datetime.now().strftime('%B %d, %Y'))],
['Period:', report_data.get('period', 'Q4 2023')],
]
cover_table = Table(cover_info, colWidths=[2*inch, 4*inch])
cover_table.setStyle(TableStyle([
('ALIGN', (0, 0), (0, -1), 'RIGHT'),
('ALIGN', (1, 0), (1, -1), 'LEFT'),
('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, -1), 11),
('TOPPADDING', (0, 0), (-1, -1), 6),
]))
story.append(cover_table)
story.append(PageBreak())
# --- TABLE OF CONTENTS ---
toc = TableOfContents()
toc.levelStyles = [
ParagraphStyle(name='TOCHeading1', fontSize=14, leftIndent=20, spaceBefore=10, spaceAfter=5),
ParagraphStyle(name='TOCHeading2', fontSize=12, leftIndent=40, spaceBefore=3, spaceAfter=3),
]
story.append(Paragraph("Table of Contents", heading1_style))
story.append(toc)
story.append(PageBreak())
# --- SECTIONS ---
for section in report_data.get('sections', []):
# Section heading
section_title = section['title']
story.append(Paragraph(f'<a name="{section_title}"/>{section_title}', heading1_style))
# Add to TOC
toc.addEntry(0, section_title, doc.page)
# Section content
if 'content' in section:
for para in section['content'].split('\n\n'):
if para.strip():
story.append(Paragraph(para.strip(), body_style))
story.append(Spacer(1, 0.2*inch))
# Subsections
for subsection in section.get('subsections', []):
story.append(Paragraph(subsection['title'], heading2_style))
if 'content' in subsection:
story.append(Paragraph(subsection['content'], body_style))
story.append(Spacer(1, 0.1*inch))
# Add table if provided
if 'table_data' in section:
table = create_section_table(section['table_data'])
story.append(table)
story.append(Spacer(1, 0.2*inch))
# Add chart if provided
if 'chart_data' in section:
chart = create_section_chart(section['chart_data'])
story.append(chart)
story.append(Spacer(1, 0.2*inch))
story.append(Spacer(1, 0.3*inch))
# Build PDF (twice for TOC to populate)
doc.multiBuild(story)
return filename
def create_section_table(table_data):
"""Create a styled table for report sections"""
data = table_data['data']
table = Table(data, colWidths=table_data.get('colWidths'))
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#34495E')),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, 0), 11),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -1), colors.white),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.lightgrey]),
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
('FONTSIZE', (0, 1), (-1, -1), 10),
]))
return table
def create_section_chart(chart_data):
"""Create a chart for report sections"""
chart_type = chart_data.get('type', 'bar')
drawing = Drawing(400, 200)
if chart_type == 'bar':
chart = VerticalBarChart()
chart.x = 50
chart.y = 30
chart.width = 300
chart.height = 150
chart.data = chart_data['data']
chart.categoryAxis.categoryNames = chart_data.get('categories', [])
chart.valueAxis.valueMin = 0
# Style bars
for i in range(len(chart_data['data'])):
chart.bars[i].fillColor = colors.HexColor(['#3498db', '#e74c3c', '#2ecc71'][i % 3])
elif chart_type == 'line':
chart = HorizontalLineChart()
chart.x = 50
chart.y = 30
chart.width = 300
chart.height = 150
chart.data = chart_data['data']
chart.categoryAxis.categoryNames = chart_data.get('categories', [])
# Style lines
for i in range(len(chart_data['data'])):
chart.lines[i].strokeColor = colors.HexColor(['#3498db', '#e74c3c', '#2ecc71'][i % 3])
chart.lines[i].strokeWidth = 2
drawing.add(chart)
return drawing
# Example usage
if __name__ == "__main__":
report = {
'title': 'Quarterly Business Report',
'subtitle': 'Q4 2023 Performance Analysis',
'author': 'Analytics Team',
'date': 'January 15, 2024',
'period': 'October - December 2023',
'sections': [
{
'title': 'Executive Summary',
'content': """
This report provides a comprehensive analysis of our Q4 2023 performance.
Overall, the quarter showed strong growth across all key metrics, with
revenue increasing by 25% year-over-year and customer satisfaction
scores reaching an all-time high of 4.8/5.0.
Key highlights include the successful launch of three new products,
expansion into two new markets, and the completion of our digital
transformation initiative.
""",
'subsections': [
{
'title': 'Key Achievements',
'content': 'Successfully launched Product X with 10,000 units sold in first month.'
}
]
},
{
'title': 'Financial Performance',
'content': """
The financial results for Q4 exceeded expectations across all categories.
Revenue growth was driven primarily by strong product sales and increased
market share in key regions.
""",
'table_data': {
'data': [
['Metric', 'Q3 2023', 'Q4 2023', 'Change'],
['Revenue', '$2.5M', '$3.1M', '+24%'],
['Profit', '$500K', '$680K', '+36%'],
['Expenses', '$2.0M', '$2.4M', '+20%'],
],
'colWidths': [2*inch, 1.5*inch, 1.5*inch, 1*inch]
},
'chart_data': {
'type': 'bar',
'data': [[2.5, 3.1], [0.5, 0.68], [2.0, 2.4]],
'categories': ['Q3', 'Q4']
}
},
{
'title': 'Market Analysis',
'content': """
Market conditions remained favorable throughout the quarter, with
strong consumer confidence and increasing demand for our products.
""",
'chart_data': {
'type': 'line',
'data': [[100, 120, 115, 140, 135, 150]],
'categories': ['Oct', 'Nov', 'Dec', 'Oct', 'Nov', 'Dec']
}
},
]
}
create_report("sample_report.pdf", report)
print("Report created: sample_report.pdf")