# Invoice Document PDF

> Generating invoices, contracts, forms, receipts, and business documents as professional PDFs

- Skill: `gabrielmoreira/invoice-document-pdf` (Agent Skill)
- Install (CLI): `npx skillmds@latest add gabrielmoreira/invoice-document-pdf`
- Raw SKILL.md: https://api.skillmd.com/api/skills/gabrielmoreira/invoice-document-pdf/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: gabrielmoreira (https://skillmd.com/u/gabrielmoreira)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/gabrielmoreira/invoice-document-pdf

---


# Invoice & Document PDF Generation

Generate professional business documents — invoices, contracts, receipts, forms — as polished PDFs. This skill covers templates, data-driven generation, batch processing, digital signatures, and workflow automation.

---

## Invoice PDF Structure

A professional invoice follows a standard layout that makes it easy to process, pay, and reconcile.

### Standard Invoice Anatomy

```text
┌───────────────────────────────────────────┐
│  [LOGO]           INVOICE                  │
│  Your Company     #INV-2025-0042          │
│  123 Business Rd                           │
│  City, State ZIP                           │
├─────────────────┬─────────────────────────┤
│  Bill To:       │  Invoice Details:        │
│  Client Name    │  Date: 2025-01-15       │
│  Client Address │  Due: 2025-02-14        │
│  City, State    │  Terms: Net 30          │
│                 │  PO #: PO-2025-001      │
├─────────────────┴─────────────────────────┤
│  # │ Description        │ Qty │ Rate│ Amt │
│  ───┼───────────────────┼─────┼─────┼─────┤
│  1 │ Web Development   │  40 │ 150 │$6,000│
│  2 │ UI/UX Design      │  20 │ 125 │$2,500│
│  3 │ DevOps Setup      │   8 │ 175 │$1,400│
├────────────────────────┴─────┴─────┴─────┤
│  Subtotal:                          $9,900 │
│  Tax (8%):                            $792 │
│  Discount (5%):                      -$495 │
│  Total:                            $10,197 │
├───────────────────────────────────────────┤
│  Payment Information:                      │
│  Bank: First National Bank                 │
│  Account: XXXX-XXXX-1234                   │
│  Routing: 021000021                        │
│  PayPal: pay@company.com                   │
├───────────────────────────────────────────┤
│  Terms & Notes:                            │
│  Payment due within 30 days.               │
│  Late payment subject to 1.5%/mo fee.      │
└───────────────────────────────────────────┘
```

### Invoice Data Model

```python
"""Invoice data model with validation."""

from dataclasses import dataclass, field
from datetime import datetime, timedelta
from decimal import Decimal
from typing import Optional, list
import uuid


@dataclass
class LineItem:
    """A single line item on an invoice."""
    description: str
    quantity: Decimal
    unit_price: Decimal
    sku: Optional[str] = None
    
    @property
    def amount(self) -> Decimal:
        return self.quantity * self.unit_price


@dataclass
class InvoiceData:
    """Complete invoice data structure."""
    # Invoice identifiers
    invoice_number: str
    po_number: Optional[str] = None
    
    # Dates
    issue_date: str = field(default_factory=lambda: datetime.now().strftime("%Y-%m-%d"))
    due_date: Optional[str] = None
    payment_terms: str = "Net 30"
    
    # Seller (Your company)
    seller_name: str = ""
    seller_address: str = ""
    seller_city: str = ""
    seller_state: str = ""
    seller_zip: str = ""
    seller_phone: str = ""
    seller_email: str = ""
    seller_logo_path: Optional[str] = None
    tax_id: Optional[str] = None
    
    # Buyer (Client)
    client_name: str = ""
    client_address: str = ""
    client_city: str = ""
    client_state: str = ""
    client_zip: str = ""
    client_email: Optional[str] = None
    
    # Line items
    line_items: list[LineItem] = field(default_factory=list)
    
    # Financial
    tax_rate: Decimal = Decimal("0")
    discount_rate: Decimal = Decimal("0")
    discount_description: str = "Discount"
    currency_symbol: str = "$"
    
    # Payment
    bank_name: Optional[str] = None
    bank_account: Optional[str] = None
    bank_routing: Optional[str] = None
    payment_instructions: Optional[str] = None
    
    # Notes
    notes: Optional[str] = None
    terms: Optional[str] = None
    
    def __post_init__(self):
        if not self.due_date:
            due = datetime.now() + timedelta(days=30)
            self.due_date = due.strftime("%Y-%m-%d")
        if not self.invoice_number:
            self.invoice_number = f"INV-{datetime.now().strftime('%Y%m')}-{uuid.uuid4().hex[:6].upper()}"
    
    @property
    def subtotal(self) -> Decimal:
        return sum(item.amount for item in self.line_items)
    
    @property
    def tax_amount(self) -> Decimal:
        return self.subtotal * self.tax_rate
    
    @property
    def discount_amount(self) -> Decimal:
        return self.subtotal * self.discount_rate
    
    @property
    def total(self) -> Decimal:
        return self.subtotal + self.tax_amount - self.discount_amount
```

---

## 2. HTML/CSS Print Templates for Invoices

### Invoice Print Template

```html
<!-- templates/invoice.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Invoice {{ invoice.invoice_number }}</title>
<style>
/* Page setup */
@page {
  size: A4;
  margin: 1.5cm 2cm 2cm 2cm;
  
  @bottom-center {
    content: "Invoice {{ invoice.invoice_number }} — Page " counter(page);
    font-size: 8pt;
    color: #999;
    font-family: 'Helvetica', 'Arial', sans-serif;
  }
}

body {
  font-family: 'Helvetica', 'Arial', sans-serif;
  font-size: 9.5pt;
  color: #333;
  line-height: 1.5;
  margin: 0;
  padding: 0;
}

/* Header section */
.invoice-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 2cm;
  border-bottom: 3px solid #1a1a2e;
  padding-bottom: 0.5cm;
}

.invoice-header .seller-info {
  flex: 1;
}

.invoice-header .seller-info h1 {
  font-size: 11pt;
  color: #1a1a2e;
  margin: 0 0 4px 0;
  font-weight: bold;
}

.invoice-header .seller-info p {
  margin: 1px 0;
  font-size: 9pt;
  color: #666;
}

.invoice-header .invoice-title {
  text-align: right;
}

.invoice-header .invoice-title h2 {
  font-size: 24pt;
  color: #1a1a2e;
  margin: 0;
  letter-spacing: 3px;
  text-transform: uppercase;
}

.invoice-header .invoice-title .invoice-number {
  font-size: 12pt;
  color: #e94560;
  font-weight: bold;
  margin-top: 4px;
}

.invoice-header .invoice-title .invoice-number span {
  color: #333;
  font-weight: normal;
}

/* Billing section */
.billing-section {
  display: flex;
  justify-content: space-between;
  margin-bottom: 1.5cm;
}

.bill-to, .invoice-details {
  width: 45%;
}

.bill-to h3, .invoice-details h3 {
  font-size: 9pt;
  color: #999;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin: 0 0 6px 0;
  border-bottom: 1px solid #ddd;
  padding-bottom: 4px;
}

.bill-to p, .invoice-details p {
  margin: 2px 0;
  font-size: 9pt;
}

.invoice-details table {
  width: 100%;
  border-collapse: collapse;
}

.invoice-details td {
  padding: 2px 0;
  font-size: 9pt;
}

.invoice-details td:first-child {
  color: #999;
  width: 40%;
}

.invoice-details td:last-child {
  text-align: right;
  font-weight: bold;
}

/* Line items table */
.items-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1cm;
}

.items-table thead th {
  background: #1a1a2e;
  color: white;
  padding: 8px 10px;
  font-size: 8.5pt;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  text-align: left;
}

.items-table thead th:last-child {
  text-align: right;
}

.items-table thead th:nth-child(3),
.items-table thead th:nth-child(4) {
  text-align: right;
}

.items-table tbody td {
  padding: 8px 10px;
  border-bottom: 1px solid #eee;
  font-size: 9pt;
}

.items-table tbody td:last-child {
  text-align: right;
  font-weight: bold;
}

.items-table tbody td:nth-child(3),
.items-table tbody td:nth-child(4) {
  text-align: right;
}

.items-table tbody tr:nth-child(even) {
  background: #f8f9fa;
}

.items-table tbody tr:last-child td {
  border-bottom: 2px solid #1a1a2e;
}

/* Totals section */
.totals-section {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 1.5cm;
}

.totals-table {
  width: 40%;
  border-collapse: collapse;
}

.totals-table td {
  padding: 4px 10px;
  font-size: 9pt;
}

.totals-table td:first-child {
  text-align: right;
  color: #666;
  width: 50%;
}

.totals-table td:last-child {
  text-align: right;
  font-weight: bold;
  width: 50%;
}

.totals-table .total-row td {
  border-top: 2px solid #1a1a2e;
  font-size: 11pt;
  font-weight: bold;
  padding-top: 8px;
  color: #1a1a2e;
}

.totals-table .discount-row td {
  color: #e94560;
}

/* Payment section */
.payment-section {
  background: #f8f9fa;
  border: 1px solid #dee2e6;
  padding: 12px 16px;
  margin-bottom: 1cm;
  page-break-inside: avoid;
}

.payment-section h3 {
  font-size: 9pt;
  color: #1a1a2e;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin: 0 0 8px 0;
}

.payment-section p {
  margin: 2px 0;
  font-size: 8.5pt;
}

/* Notes section */
.notes-section {
  font-size: 8.5pt;
  color: #666;
  margin-top: 1cm;
  border-top: 1px solid #ddd;
  padding-top: 0.5cm;
}

.notes-section h3 {
  font-size: 8pt;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: #999;
  margin: 0 0 4px 0;
}

/* Logo */
.logo {
  max-height: 1.5cm;
  max-width: 4cm;
}

/* QR Code placement */
.qr-code {
  position: absolute;
  top: 1cm;
  right: 2cm;
  width: 2cm;
  height: 2cm;
}
</style>
</head>
<body>

<div class="invoice-header">
  <div class="seller-info">
    {% if invoice.seller_logo_path %}
    <img src="{{ invoice.seller_logo_path }}" class="logo" alt="Logo">
    {% endif %}
    <h1>{{ invoice.seller_name }}</h1>
    <p>{{ invoice.seller_address }}</p>
    <p>{{ invoice.seller_city }}, {{ invoice.seller_state }} {{ invoice.seller_zip }}</p>
    {% if invoice.seller_phone %}<p>Phone: {{ invoice.seller_phone }}</p>{% endif %}
    {% if invoice.seller_email %}<p>Email: {{ invoice.seller_email }}</p>{% endif %}
    {% if invoice.tax_id %}<p>Tax ID: {{ invoice.tax_id }}</p>{% endif %}
  </div>
  <div class="invoice-title">
    <h2>Invoice</h2>
    <div class="invoice-number">#<span>{{ invoice.invoice_number }}</span></div>
  </div>
</div>

<div class="billing-section">
  <div class="bill-to">
    <h3>Bill To</h3>
    <p><strong>{{ invoice.client_name }}</strong></p>
    <p>{{ invoice.client_address }}</p>
    <p>{{ invoice.client_city }}, {{ invoice.client_state }} {{ invoice.client_zip }}</p>
    {% if invoice.client_email %}<p>{{ invoice.client_email }}</p>{% endif %}
  </div>
  <div class="invoice-details">
    <h3>Invoice Details</h3>
    <table>
      <tr><td>Invoice Date</td><td>{{ invoice.issue_date }}</td></tr>
      <tr><td>Due Date</td><td>{{ invoice.due_date }}</td></tr>
      <tr><td>Payment Terms</td><td>{{ invoice.payment_terms }}</td></tr>
      {% if invoice.po_number %}
      <tr><td>PO Number</td><td>{{ invoice.po_number }}</td></tr>
      {% endif %}
    </table>
  </div>
</div>

<table class="items-table">
  <thead>
    <tr>
      <th style="width: 5%;">#</th>
      <th style="width: 45%;">Description</th>
      <th style="width: 10%;">Qty</th>
      <th style="width: 15%;">Rate</th>
      <th style="width: 15%;">Amount</th>
    </tr>
  </thead>
  <tbody>
    {% for item in invoice.line_items %}
    <tr>
      <td>{{ loop.index }}</td>
      <td>{{ item.description }}{% if item.sku %}<br><small>SKU: {{ item.sku }}</small>{% endif %}</td>
      <td>{{ item.quantity }}</td>
      <td>{{ invoice.currency_symbol }}{{ "{:,.2f}".format(item.unit_price) }}</td>
      <td>{{ invoice.currency_symbol }}{{ "{:,.2f}".format(item.amount) }}</td>
    </tr>
    {% endfor %}
  </tbody>
</table>

<div class="totals-section">
  <table class="totals-table">
    <tr><td>Subtotal</td><td>{{ invoice.currency_symbol }}{{ "{:,.2f}".format(invoice.subtotal) }}</td></tr>
    {% if invoice.discount_rate > 0 %}
    <tr class="discount-row">
      <td>{{ invoice.discount_description }} ({{ "{:.0%}".format(invoice.discount_rate) }})</td>
      <td>-{{ invoice.currency_symbol }}{{ "{:,.2f}".format(invoice.discount_amount) }}</td>
    </tr>
    {% endif %}
    {% if invoice.tax_rate > 0 %}
    <tr><td>Tax ({{ "{:.0%}".format(invoice.tax_rate) }})</td><td>{{ invoice.currency_symbol }}{{ "{:,.2f}".format(invoice.tax_amount) }}</td></tr>
    {% endif %}
    <tr class="total-row"><td>Total</td><td>{{ invoice.currency_symbol }}{{ "{:,.2f}".format(invoice.total) }}</td></tr>
  </table>
</div>

<div class="payment-section">
  <h3>Payment Information</h3>
  {% if invoice.bank_name %}<p><strong>Bank:</strong> {{ invoice.bank_name }}</p>{% endif %}
  {% if invoice.bank_account %}<p><strong>Account:</strong> {{ invoice.bank_account }}</p>{% endif %}
  {% if invoice.bank_routing %}<p><strong>Routing:</strong> {{ invoice.bank_routing }}</p>{% endif %}
  {% if invoice.payment_instructions %}<p>{{ invoice.payment_instructions }}</p>{% endif %}
</div>

<div class="notes-section">
  {% if invoice.notes %}
  <h3>Notes</h3>
  <p>{{ invoice.notes }}</p>
  {% endif %}
  {% if invoice.terms %}
  <h3>Terms</h3>
  <p>{{ invoice.terms }}</p>
  {% endif %}
</div>

</body>
</html>
```

---

## 3. Python Invoice Generation

### ReportLab Invoice Generator

```python
"""Professional invoice generation with ReportLab."""

from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm, mm
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    Image, PageBreak, KeepTogether
)
from reportlab.lib.enums import TA_RIGHT, TA_LEFT, TA_CENTER
from decimal import Decimal, ROUND_HALF_UP
from datetime import datetime, timedelta


class InvoiceGenerator:
    """Generate professional invoice PDFs using ReportLab."""
    
    def __init__(self, company_info: dict):
        self.company = company_info
        self.styles = getSampleStyleSheet()
        self._setup_styles()
    
    def _setup_styles(self):
        """Set up custom paragraph styles."""
        self.styles.add(ParagraphStyle(
            'InvoiceTitle',
            fontName='Helvetica-Bold',
            fontSize=28,
            textColor=colors.HexColor('#1a1a2e'),
            alignment=TA_RIGHT,
            spaceAfter=4,
        ))
        self.styles.add(ParagraphStyle(
            'CompanyName',
            fontName='Helvetica-Bold',
            fontSize=11,
            textColor=colors.HexColor('#1a1a2e'),
            spaceAfter=2,
        ))
        self.styles.add(ParagraphStyle(
            'SmallText',
            fontName='Helvetica',
            fontSize=8.5,
            textColor=colors.HexColor('#666666'),
            spaceAfter=1,
        ))
        self.styles.add(ParagraphStyle(
            'SectionLabel',
            fontName='Helvetica-Bold',
            fontSize=8,
            textColor=colors.HexColor('#999999'),
            spaceBefore=8,
            spaceAfter=4,
        ))
    
    def _header_table(self, invoice_data: dict) -> Table:
        """Create the invoice header with company info and title."""
        # Company info
        company_cells = [
            [Paragraph(self.company['name'], self.styles['CompanyName'])],
            [Paragraph(self.company['address'], self.styles['SmallText'])],
            [Paragraph(
                f"{self.company['city']}, {self.company['state']} {self.company['zip']}",
                self.styles['SmallText']
            )],
        ]
        if self.company.get('phone'):
            company_cells.append([Paragraph(f"Phone: {self.company['phone']}", self.styles['SmallText'])])
        if self.company.get('email'):
            company_cells.append([Paragraph(f"Email: {self.company['email']}", self.styles['SmallText'])])
        
        company_table = Table(company_cells, colWidths=[8*cm])
        company_table.setStyle(TableStyle([
            ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ('LEFTPADDING', (0, 0), (-1, -1), 0),
            ('RIGHTPADDING', (0, 0), (-1, -1), 0),
            ('TOPPADDING', (0, 0), (-1, -1), 1),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 1),
        ]))
        
        # Invoice title
        title_cells = [
            [Paragraph('INVOICE', self.styles['InvoiceTitle'])],
            [Paragraph(f"# {invoice_data['invoice_number']}", ParagraphStyle(
                'InvNumber', parent=self.styles['SmallText'],
                fontSize=12, textColor=colors.HexColor('#e94560'),
                alignment=TA_RIGHT, fontName='Helvetica-Bold',
            ))],
        ]
        title_table = Table(title_cells, colWidths=[8*cm])
        title_table.setStyle(TableStyle([
            ('ALIGN', (0, 0), (-1, -1), 'RIGHT'),
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ('RIGHTPADDING', (0, 0), (-1, -1), 0),
        ]))
        
        header = Table([[company_table, title_table]], colWidths=[8*cm, 8*cm])
        header.setStyle(TableStyle([
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ('LINEBELOW', (0, 0), (-1, 0), 3, colors.HexColor('#1a1a2e')),
            ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
        ]))
        return header
    
    def _billing_section(self, invoice_data: dict) -> Table:
        """Create the bill-to and invoice details section."""
        bill_to = [
            [Paragraph('BILL TO', self.styles['SectionLabel'])],
            [Paragraph(f"<b>{invoice_data['client_name']}</b>", self.styles['SmallText'])],
            [Paragraph(invoice_data['client_address'], self.styles['SmallText'])],
            [Paragraph(
                f"{invoice_data['client_city']}, {invoice_data['client_state']} {invoice_data['client_zip']}",
                self.styles['SmallText']
            )],
        ]
        if invoice_data.get('client_email'):
            bill_to.append([Paragraph(invoice_data['client_email'], self.styles['SmallText'])])
        
        bill_table = Table(bill_to, colWidths=[8*cm])
        bill_table.setStyle(TableStyle([
            ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ('LEFTPADDING', (0, 0), (-1, -1), 0),
            ('TOPPADDING', (0, 0), (-1, -1), 1),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 1),
            ('LINEBELOW', (0, 0), (0, 0), 1, colors.HexColor('#dddddd')),
        ]))
        
        # Invoice details
        details_data = [
            [Paragraph('INVOICE DETAILS', self.styles['SectionLabel']),
             Paragraph('', self.styles['SmallText'])],
        ]
        detail_fields = [
            ('Invoice Date', invoice_data['issue_date']),
            ('Due Date', invoice_data['due_date']),
            ('Payment Terms', invoice_data.get('payment_terms', 'Net 30')),
        ]
        if invoice_data.get('po_number'):
            detail_fields.append(('PO Number', invoice_data['po_number']))
        
        for label, value in detail_fields:
            details_data.append([
                Paragraph(label, self.styles['SmallText']),
                Paragraph(f"<b>{value}</b>", ParagraphStyle(
                    'DetailValue', parent=self.styles['SmallText'],
                    alignment=TA_RIGHT,
                )),
            ])
        
        details_table = Table(details_data, colWidths=[3.5*cm, 4.5*cm])
        details_table.setStyle(TableStyle([
            ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ('TOPPADDING', (0, 0), (-1, -1), 1),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 1),
            ('LINEBELOW', (0, 0), (-1, 0), 1, colors.HexColor('#dddddd')),
        ]))
        
        billing = Table([[bill_table, details_table]], colWidths=[8*cm, 8*cm])
        billing.setStyle(TableStyle([
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
        ]))
        return billing
    
    def _items_table(self, items: list[dict], currency: str = "$") -> Table:
        """Create the line items table."""
        header_row = ['#', 'Description', 'Qty', 'Rate', 'Amount']
        data = [header_row]
        
        for idx, item in enumerate(items, 1):
            data.append([
                str(idx),
                item['description'],
                str(item['quantity']),
                f"{currency}{item['unit_price']:,.2f}",
                f"{currency}{item['amount']:,.2f}",
            ])
        
        col_widths = [0.8*cm, 8.5*cm, 1.5*cm, 2.5*cm, 2.7*cm]
        table = Table(data, colWidths=col_widths, repeatRows=1)
        
        style_cmds = [
            # Header
            ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#1a1a2e')),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
            ('FONTSIZE', (0, 0), (-1, 0), 8.5),
            ('ALIGN', (0, 0), (0, -1), 'CENTER'),
            ('ALIGN', (2, 0), (-1, -1), 'RIGHT'),
            ('ALIGN', (1, 1), (1, -1), 'LEFT'),
            
            # Body
            ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
            ('FONTSIZE', (0, 1), (-1, -1), 9),
            ('TOPPADDING', (0, 0), (-1, -1), 6),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
            ('LEFTPADDING', (0, 0), (-1, -1), 6),
            ('RIGHTPADDING', (0, 0), (-1, -1), 6),
            
            # Grid
            ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#dee2e6')),
            ('LINEBELOW', (0, 0), (-1, 0), 2, colors.HexColor('#1a1a2e')),
            ('LINEBELOW', (0, -1), (-1, -1), 2, colors.HexColor('#1a1a2e')),
            
            # Alternating colors
            *[('BACKGROUND', (0, i), (-1, i),
               colors.HexColor('#f8f9fa') if i % 2 == 0 else colors.white)
              for i in range(1, len(data))],
        ]
        
        table.setStyle(TableStyle(style_cmds))
        return table
    
    def _totals_section(self, subtotal: Decimal, tax_rate: Decimal,
                        discount_rate: Decimal, currency: str = "$") -> Table:
        """Create the totals section."""
        data = [
            ['Subtotal', f"{currency}{subtotal:,.2f}"],
        ]
        
        if discount_rate > 0:
            discount_amt = subtotal * discount_rate
            data.append([
                f'Discount ({discount_rate*100:.0f}%)',
                f"-{currency}{discount_amt:,.2f}",
            ])
        
        if tax_rate > 0:
            tax_amt = subtotal * tax_rate
            data.append([
                f'Tax ({tax_rate*100:.0f}%)',
                f"{currency}{tax_amt:,.2f}",
            ])
        
        total = subtotal * (1 + tax_rate - discount_rate)
        data.append(['Total', f"{currency}{total:,.2f}"])
        
        table = Table(data, colWidths=[5*cm, 3*cm])
        
        style_cmds = [
            ('ALIGN', (0, 0), (0, -1), 'RIGHT'),
            ('ALIGN', (1, 0), (1, -1), 'RIGHT'),
            ('FONTNAME', (0, 0), (-1, -2), 'Helvetica'),
            ('FONTSIZE', (0, 0), (-1, -2), 9),
            ('TOPPADDING', (0, 0), (-1, -1), 3),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 3),
            
            # Total row
            ('LINEABOVE', (0, -1), (-1, -1), 2, colors.HexColor('#1a1a2e')),
            ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
            ('FONTSIZE', (0, -1), (-1, -1), 11),
            ('TEXTCOLOR', (0, -1), (-1, -1), colors.HexColor('#1a1a2e')),
        ]
        
        if discount_rate > 0:
            style_cmds += [
                ('TEXTCOLOR', (0, 1), (1, 1), colors.HexColor('#e94560')),
            ]
        
        table.setStyle(TableStyle(style_cmds))
        return table
    
    def _payment_section(self, invoice_data: dict) -> Table:
        """Create the payment information section."""
        cells = [[Paragraph('PAYMENT INFORMATION', self.styles['SectionLabel'])]]
        
        if invoice_data.get('bank_name'):
            cells.append([Paragraph(
                f"<b>Bank:</b> {invoice_data['bank_name']}",
                self.styles['SmallText']
            )])
        if invoice_data.get('bank_account'):
            cells.append([Paragraph(
                f"<b>Account:</b> {invoice_data['bank_account']}",
                self.styles['SmallText']
            )])
        if invoice_data.get('bank_routing'):
            cells.append([Paragraph(
                f"<b>Routing:</b> {invoice_data['bank_routing']}",
                self.styles['SmallText']
            )])
        if invoice_data.get('payment_instructions'):
            cells.append([Paragraph(
                invoice_data['payment_instructions'],
                self.styles['SmallText']
            )])
        
        table = Table(cells, colWidths=[16*cm])
        table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), colors.HexColor('#f8f9fa')),
            ('BOX', (0, 0), (-1, -1), 1, colors.HexColor('#dee2e6')),
            ('TOPPADDING', (0, 0), (-1, -1), 4),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
            ('LEFTPADDING', (0, 0), (-1, -1), 12),
            ('RIGHTPADDING', (0, 0), (-1, -1), 12),
            ('TOPPADDING', (0, 0), (0, 0), 8),
        ]))
        return table
    
    def generate(self, invoice_data: dict, output_path: str):
        """Generate the complete invoice PDF."""
        doc = SimpleDocTemplate(
            output_path,
            pagesize=A4,
            leftMargin=2*cm,
            rightMargin=2*cm,
            topMargin=1.5*cm,
            bottomMargin=2*cm,
            title=f"Invoice {invoice_data['invoice_number']}",
            author=self.company['name'],
        )
        
        story = []
        
        # Header
        story.append(self._header_table(invoice_data))
        story.append(Spacer(1, 0.5*cm))
        
        # Billing section
        story.append(self._billing_section(invoice_data))
        story.append(Spacer(1, 0.5*cm))
        
        # Line items
        for item in invoice_data.get('line_items', []):
            item['amount'] = item['quantity'] * item['unit_price']
        story.append(self._items_table(
            invoice_data.get('line_items', []),
            invoice_data.get('currency_symbol', '$')
        ))
        story.append(Spacer(1, 0.3*cm))
        
        # Totals
        story.append(self._totals_section(
            invoice_data['subtotal'],
            invoice_data.get('tax_rate', Decimal('0')),
            invoice_data.get('discount_rate', Decimal('0')),
            invoice_data.get('currency_symbol', '$'),
        ))
        story.append(Spacer(1, 0.5*cm))
        
        # Payment info
        story.append(self._payment_section(invoice_data))
        story.append(Spacer(1, 0.3*cm))
        
        # Notes
        if invoice_data.get('notes') or invoice_data.get('terms'):
            notes_cells = []
            if invoice_data.get('notes'):
                notes_cells.append([Paragraph('NOTES', self.styles['SectionLabel'])])
                notes_cells.append([Paragraph(invoice_data['notes'], self.styles['SmallText'])])
            if invoice_data.get('terms'):
                notes_cells.append([Paragraph('TERMS', self.styles['SectionLabel'])])
                notes_cells.append([Paragraph(invoice_data['terms'], self.styles['SmallText'])])
            
            notes_table = Table(notes_cells, colWidths=[16*cm])
            notes_table.setStyle(TableStyle([
                ('LINEABOVE', (0, 0), (-1, 0), 1, colors.HexColor('#dddddd')),
                ('TOPPADDING', (0, 0), (-1, -1), 4),
            ]))
            story.append(notes_table)
        
        doc.build(story)
        print(f"Invoice generated: {output_path}")


# Usage
if __name__ == '__main__':
    generator = InvoiceGenerator({
        'name': 'Cosmic Stack Labs',
        'address': '123 Innovation Drive',
        'city': 'San Francisco',
        'state': 'CA',
        'zip': '94105',
        'phone': '(555) 123-4567',
        'email': 'billing@cosmicstack.com',
    })
    
    invoice = {
        'invoice_number': 'INV-2025-0042',
        'issue_date': '2025-01-15',
        'due_date': '2025-02-14',
        'payment_terms': 'Net 30',
        'po_number': 'PO-2025-001',
        'client_name': 'Acme Corporation',
        'client_address': '456 Business Ave, Suite 200',
        'client_city': 'New York',
        'client_state': 'NY',
        'client_zip': '10001',
        'client_email': 'ap@acmecorp.com',
        'line_items': [
            {'description': 'Web Development - Frontend React Implementation', 'quantity': 40, 'unit_price': Decimal('150.00')},
            {'description': 'UI/UX Design - Dashboard Redesign', 'quantity': 20, 'unit_price': Decimal('125.00')},
            {'description': 'DevOps Setup - CI/CD Pipeline Configuration', 'quantity': 8, 'unit_price': Decimal('175.00')},
            {'description': 'Database Optimization - Query Performance Tuning', 'quantity': 12, 'unit_price': Decimal('200.00')},
        ],
        'subtotal': Decimal('9900.00'),
        'tax_rate': Decimal('0.08'),
        'discount_rate': Decimal('0.05'),
        'currency_symbol': '$',
        'bank_name': 'First National Bank',
        'bank_account': 'XXXX-XXXX-1234',
        'bank_routing': '021000021',
        'payment_instructions': 'Please include invoice number with payment.',
        'notes': 'Thank you for your business!',
        'terms': 'Payment due within 30 days. Late payment subject to 1.5% monthly fee.',
    }
    
    generator.generate(invoice, 'invoice_2025_0042.pdf')
```

### FPDF Invoice Generator (Lightweight Alternative)

```python
"""Lightweight invoice generation with FPDF2."""

from fpdf import FPDF
from decimal import Decimal


class InvoicePDF(FPDF):
    """Simple invoice PDF generator using FPDF."""
    
    def header(self):
        self.set_font('Helvetica', 'B', 10)
        self.set_text_color(26, 26, 46)
        self.cell(0, 8, 'INVOICE', align='R', new_x="LMARGIN", new_y="NEXT")
        self.line(10, self.get_y(), 200, self.get_y())
        self.ln(5)
    
    def footer(self):
        self.set_y(-15)
        self.set_font('Helvetica', 'I', 8)
        self.set_text_color(128, 128, 128)
        self.cell(0, 10, f'Page {self.page_no()}/{{nb}}', align='C')
    
    def company_info(self, name, address, city, state, zip_code, phone, email):
        self.set_font('Helvetica', 'B', 12)
        self.set_text_color(26, 26, 46)
        self.cell(0, 6, name, new_x="LMARGIN", new_y="NEXT")
        self.set_font('Helvetica', '', 9)
        self.set_text_color(102, 102, 102)
        self.cell(0, 4, address, new_x="LMARGIN", new_y="NEXT")
        self.cell(0, 4, f'{city}, {state} {zip_code}', new_x="LMARGIN", new_y="NEXT")
        self.cell(0, 4, f'Phone: {phone} | Email: {email}', new_x="LMARGIN", new_y="NEXT")
        self.ln(5)
    
    def bill_to(self, client_name, client_address, client_city, client_state, client_zip):
        self.set_font('Helvetica', 'B', 9)
        self.set_text_color(153, 153, 153)
        self.cell(0, 5, 'BILL TO', new_x="LMARGIN", new_y="NEXT")
        self.set_font('Helvetica', 'B', 10)
        self.set_text_color(51, 51, 51)
        self.cell(0, 5, client_name, new_x="LMARGIN", new_y="NEXT")
        self.set_font('Helvetica', '', 9)
        self.cell(0, 4, client_address, new_x="LMARGIN", new_y="NEXT")
        self.cell(0, 4, f'{client_city}, {client_state} {client_zip}', new_x="LMARGIN", new_y="NEXT")
        self.ln(5)
    
    def invoice_details(self, inv_number, issue_date, due_date, terms, po_number=None):
        self.set_font('Helvetica', 'B', 9)
        self.set_text_color(153, 153, 153)
        self.cell(0, 5, 'INVOICE DETAILS', new_x="LMARGIN", new_y="NEXT")
        
        details = [
            ('Invoice #:', inv_number),
            ('Date:', issue_date),
            ('Due:', due_date),
            ('Terms:', terms),
        ]
        if po_number:
            details.append(('PO #:', po_number))
        
        for label, value in details:
            self.set_font('Helvetica', '', 9)
            self.set_text_color(102, 102, 102)
            self.cell(30, 5, label)
            self.set_font('Helvetica', 'B', 9)
            self.set_text_color(51, 51, 51)
            self.cell(0, 5, value, new_x="LMARGIN", new_y="NEXT")
        self.ln(5)
    
    def line_items(self, items, currency='$'):
        """Add line items table."""
        # Table header
        self.set_fill_color(26, 26, 46)
        self.set_text_color(255, 255, 255)
        self.set_font('Helvetica', 'B', 9)
        
        col_widths = [10, 85, 20, 30, 35]
        headers = ['#', 'Description', 'Qty', 'Rate', 'Amount']
        
        for i, header in enumerate(headers):
            self.cell(col_widths[i], 8, header, border=1, align='C' if i != 1 else 'L', fill=True)
        self.ln()
        
        # Table body
        self.set_text_color(51, 51, 51)
        fill = False
        for idx, item in enumerate(items, 1):
            if fill:
                self.set_fill_color(248, 249, 250)
            else:
                self.set_fill_color(255, 255, 255)
            
            self.set_font('Helvetica', '', 9)
            self.cell(col_widths[0], 7, str(idx), border=1, align='C', fill=True)
            self.cell(col_widths[1], 7, item['description'], border=1, align='L', fill=True)
            self.cell(col_widths[2], 7, str(item['quantity']), border=1, align='R', fill=True)
            self.cell(col_widths[3], 7, f"{currency}{item['unit_price']:,.2f}", border=1, align='R', fill=True)
            self.cell(col_widths[4], 7, f"{currency}{item['amount']:,.2f}", border=1, align='R', fill=True)
            self.ln()
            fill = not fill
        
        # Bottom line
        self.line(10, self.get_y(), 200, self.get_y())
        self.ln(3)
    
    def totals(self, subtotal, tax_rate, discount_rate, currency='$'):
        """Add totals section."""
        self.set_font('Helvetica', '', 9)
        
        items = [
            ('Subtotal:', f"{currency}{subtotal:,.2f}"),
        ]
        
        if discount_rate > 0:
            discount_amt = subtotal * discount_rate
            items.append((f'Discount ({discount_rate*100:.0f}%):', f"-{currency}{discount_amt:,.2f}"))
        
        if tax_rate > 0:
            tax_amt = subtotal * tax_rate
            items.append((f'Tax ({tax_rate*100:.0f}%):', f"{currency}{tax_amt:,.2f}"))
        
        total = subtotal * (1 + tax_rate - discount_rate)
        items.append(('Total:', f"{currency}{total:,.2f}"))
        
        # Right-align totals
        x_start = 130
        for label, value in items:
            self.set_x(x_start)
            self.set_text_color(102, 102, 102)
            self.cell(35, 6, label, align='R')
            self.set_text_color(51, 51, 51)
            self.set_font('Helvetica', 'B' if 'Total' in label else '', 9)
            self.cell(35, 6, value, align='R', new_x="LMARGIN", new_y="NEXT")
        
        self.ln(5)
    
    def payment_info(self, bank_name, account, routing, instructions=None):
        """Add payment information block."""
        self.set_fill_color(248, 249, 250)
        self.set_font('Helvetica', 'B', 9)
        self.set_text_color(26, 26, 46)
        self.cell(0, 6, 'PAYMENT INFORMATION', border=1, fill=True, new_x="LMARGIN", new_y="NEXT")
        
        self.set_font('Helvetica', '', 9)
        self.set_text_color(51, 51, 51)
        
        info = [
            (f'Bank: {bank_name}', ''),
            (f'Account: {account}', ''),
            (f'Routing: {routing}', ''),
        ]
        if instructions:
            info.append((instructions, ''))
        
        for label, _ in info:
            self.cell(0, 5, label, border=1, new_x="LMARGIN", new_y="NEXT")
        
        self.ln(5)


# Usage
pdf = InvoicePDF()
pdf.alias_nb_pages()
pdf.add_page()
pdf.company_info('Cosmic Stack Labs', '123 Innovation Drive',
                 'San Francisco', 'CA', '94105', '(555) 123-4567', 'billing@cosmicstack.com')
pdf.bill_to('Acme Corporation', '456 Business Ave, Suite 200', 'New York', 'NY', '10001')
pdf.invoice_details('INV-2025-0042', '2025-01-15', '2025-02-14', 'Net 30', 'PO-2025-001')

items = [
    {'description': 'Web Development Services', 'quantity': 40, 'unit_price': 150.00,
     'amount': 6000.00},
    {'description': 'UI/UX Design', 'quantity': 20, 'unit_price': 125.00, 'amount': 2500.00},
    {'description': 'DevOps Setup', 'quantity': 8, 'unit_price': 175.00, 'amount': 1400.00},
]
pdf.line_items(items)
pdf.totals(Decimal('9900.00'), Decimal('0.08'), Decimal('0.05'))
pdf.payment_info('First National Bank', 'XXXX-XXXX-1234', '021000021',
                 'Please include invoice number with payment.')
pdf.output('fpdf_invoice.pdf')
print("FPDF invoice generated: fpdf_invoice.pdf")
```

---

## 4. Receipt and Contract Templates

### Receipt Template

```html
<!-- templates/receipt.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
@page { size: A5; margin: 1cm; }
body { font-family: 'Helvetica', sans-serif; font-size: 9pt; color: #333; }
.header { text-align: center; margin-bottom: 0.5cm; }
.header h1 { font-size: 16pt; color: #1a1a2e; margin: 0; }
.header p { color: #999; font-size: 8pt; margin: 2px 0; }
.receipt-info { margin-bottom: 0.5cm; }
.receipt-info table { width: 100%; }
.receipt-info td { padding: 2px 0; font-size: 8pt; }
.receipt-info td:last-child { text-align: right; font-weight: bold; }
.items-table { width: 100%; border-collapse: collapse; margin-bottom: 0.5cm; }
.items-table th { background: #1a1a2e; color: white; padding: 4px 6px; font-size: 7pt; text-align: left; }
.items-table th:last-child, .items-table th:nth-child(3) { text-align: right; }
.items-table td { padding: 4px 6px; border-bottom: 1px solid #eee; font-size: 8pt; }
.items-table td:last-child, .items-table td:nth-child(3) { text-align: right; }
.total { text-align: right; font-size: 11pt; font-weight: bold; color: #1a1a2e; }
.thank-you { text-align: center; color: #999; font-size: 8pt; margin-top: 0.5cm; border-top: 1px dashed #ddd; padding-top: 0.3cm; }
</style>
</head>
<body>
<div class="header">
  <h1>Receipt</h1>
  <p>{{ store_name }} | {{ store_location }}</p>
</div>
<div class="receipt-info">
  <table>
    <tr><td>Receipt #:</td><td>{{ receipt_number }}</td></tr>
    <tr><td>Date:</td><td>{{ date }}</td></tr>
    <tr><td>Cashier:</td><td>{{ cashier }}</td></tr>
  </table>
</div>
<table class="items-table">
  <thead><tr>
    <th>Item</th><th>Qty</th><th>Price</th><th>Total</th>
  </tr></thead>
  <tbody>
    {% for item in items %}
    <tr>
      <td>{{ item.name }}</td>
      <td>{{ item.qty }}</td>
      <td>\${{ "{:,.2f}".format(item.price) }}</td>
      <td>\${{ "{:,.2f}".format(item.total) }}</td>
    </tr>
    {% endfor %}
  </tbody>
</table>
<div class="total">Total: \${{ "{:,.2f}".format(total) }}</div>
<div class="thank-you">Thank you for your purchase!</div>
</body>
</html>
```

### Contract PDF Generation

```python
"""Generate professional contract PDFs with signature blocks."""

from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    PageBreak, KeepTogether
)
from reportlab.lib.styles import ParagraphStyle
from datetime import datetime


def generate_contract(output_path: str, contract_data: dict):
    """Generate a PDF contract with signature blocks."""
    doc = SimpleDocTemplate(
        output_path, pagesize=A4,
        leftMargin=2.5*cm, rightMargin=2.5*cm,
        topMargin=2.5*cm, bottomMargin=2.5*cm,
        title=contract_data.get('title', 'Agreement'),
        author=contract_data.get('party_a', ''),
    )
    
    styles = {}
    styles['title'] = ParagraphStyle(
        'ContractTitle', fontName='Helvetica-Bold',
        fontSize=18, textColor=colors.HexColor('#1a1a2e'),
        spaceAfter=6, alignment=1,
    )
    styles['subtitle'] = ParagraphStyle(
        'ContractSubtitle', fontName='Helvetica',
        fontSize=10, textColor=colors.HexColor('#666666'),
        spaceAfter=20, alignment=1,
    )
    styles['h2'] = ParagraphStyle(
        'ContractH2', fontName='Helvetica-Bold',
     

…(truncated)
