from datetime import datetime from pathlib import Path from typing import Optional from jinja2 import Environment, FileSystemLoader from weasyprint import HTML from app.models.invoice import Invoice class InvoiceGenerator: """Service for generating invoice PDFs.""" def __init__(self): # Set up templates directory self.templates_dir = Path(__file__).parent.parent / "templates" self.templates_dir.mkdir(exist_ok=True) # Set up output directory self.output_dir = Path("/app/storage/invoices") self.output_dir.mkdir(parents=True, exist_ok=True) # Set up Jinja2 environment self.env = Environment( loader=FileSystemLoader(str(self.templates_dir)), autoescape=True ) def _ensure_template_exists(self): """Ensure the invoice template exists.""" template_path = self.templates_dir / "invoice.html" if not template_path.exists(): # Create a simple invoice template template_content = """
{{ user.company_name or user.full_name }}
{{ user.address }}
{{ user.email }}
{{ user.phone_number }}
{{ customer.name }}
{{ customer.address }}
{{ customer.email }}
{{ customer.phone }}
Invoice Number: {{ invoice.invoice_number }}
Issue Date: {{ invoice.issue_date.strftime('%d %b %Y') }}
Due Date: {{ invoice.due_date.strftime('%d %b %Y') }}
Status: {{ invoice.status.value.upper() }}
Description | Quantity | Unit Price | Tax Rate | Tax Amount | Total |
---|---|---|---|---|---|
{{ item.description }} | {{ item.quantity }} | ${{ "%.2f"|format(item.unit_price) }} | {{ "%.2f"|format(item.tax_rate) }}% | ${{ "%.2f"|format(item.tax_amount) }} | ${{ "%.2f"|format(item.total) }} |
Subtotal | ${{ "%.2f"|format(invoice.subtotal) }} |
Tax | ${{ "%.2f"|format(invoice.tax_amount) }} |
Discount | ${{ "%.2f"|format(invoice.discount) }} |
Total | ${{ "%.2f"|format(invoice.total) }} |
{{ invoice.notes }}
{{ invoice.terms }}