
- Set up FastAPI application with CORS support - Configure SQLite database connection - Create database models for users, clients, invoices, and line items - Set up Alembic for database migrations - Implement JWT-based authentication system - Create basic CRUD endpoints for users, clients, and invoices - Add PDF generation functionality - Implement activity logging - Update README with project information
93 lines
2.9 KiB
Python
93 lines
2.9 KiB
Python
from pathlib import Path
|
|
from reportlab.lib.pagesizes import letter
|
|
from reportlab.pdfgen import canvas
|
|
from app.models.invoice import Invoice
|
|
from app.models.client import Client
|
|
from datetime import datetime
|
|
|
|
PDF_DIR = Path("/app") / "storage" / "pdfs"
|
|
PDF_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
def generate_invoice_pdf(invoice: Invoice, client: Client) -> str:
|
|
"""
|
|
Generate a PDF for an invoice and return the path to the PDF file.
|
|
"""
|
|
# Create a unique filename for the PDF
|
|
filename = f"invoice_{invoice.invoice_number}_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf"
|
|
pdf_path = PDF_DIR / filename
|
|
|
|
# Create a PDF using ReportLab
|
|
c = canvas.Canvas(str(pdf_path), pagesize=letter)
|
|
|
|
# Add invoice information to the PDF
|
|
c.setFont("Helvetica-Bold", 18)
|
|
c.drawString(50, 750, f"Invoice #{invoice.invoice_number}")
|
|
|
|
c.setFont("Helvetica", 12)
|
|
c.drawString(50, 730, f"Status: {invoice.status}")
|
|
c.drawString(50, 715, f"Issued Date: {invoice.issued_date}")
|
|
c.drawString(50, 700, f"Due Date: {invoice.due_date}")
|
|
|
|
# Add client information
|
|
c.setFont("Helvetica-Bold", 14)
|
|
c.drawString(50, 670, "Client Information")
|
|
|
|
c.setFont("Helvetica", 12)
|
|
c.drawString(50, 655, f"Name: {client.name}")
|
|
c.drawString(50, 640, f"Email: {client.email}")
|
|
if client.company:
|
|
c.drawString(50, 625, f"Company: {client.company}")
|
|
if client.address:
|
|
c.drawString(50, 610, f"Address: {client.address}")
|
|
if client.phone:
|
|
c.drawString(50, 595, f"Phone: {client.phone}")
|
|
|
|
# Add invoice items
|
|
c.setFont("Helvetica-Bold", 14)
|
|
c.drawString(50, 560, "Invoice Items")
|
|
|
|
c.setFont("Helvetica", 12)
|
|
y = 540
|
|
c.drawString(50, y, "Description")
|
|
c.drawString(300, y, "Quantity")
|
|
c.drawString(370, y, "Unit Price")
|
|
c.drawString(450, y, "Total")
|
|
|
|
y -= 20
|
|
total = 0
|
|
|
|
for item in invoice.items:
|
|
item_total = item.quantity * item.unit_price
|
|
total += item_total
|
|
|
|
c.drawString(50, y, item.description)
|
|
c.drawString(300, y, str(item.quantity))
|
|
c.drawString(370, y, f"${item.unit_price:.2f}")
|
|
c.drawString(450, y, f"${item_total:.2f}")
|
|
|
|
y -= 15
|
|
|
|
# Add a page break if needed
|
|
if y < 50:
|
|
c.showPage()
|
|
c.setFont("Helvetica-Bold", 14)
|
|
c.drawString(50, 750, "Invoice Items (continued)")
|
|
c.setFont("Helvetica", 12)
|
|
y = 730
|
|
|
|
# Add total
|
|
c.setFont("Helvetica-Bold", 14)
|
|
c.drawString(370, y - 20, "Total:")
|
|
c.drawString(450, y - 20, f"${total:.2f}")
|
|
|
|
# Add footer with notes
|
|
if invoice.notes:
|
|
c.setFont("Helvetica-Bold", 12)
|
|
c.drawString(50, 50, "Notes:")
|
|
c.setFont("Helvetica", 10)
|
|
c.drawString(50, 35, invoice.notes)
|
|
|
|
c.save()
|
|
|
|
return str(pdf_path) |