refactor: Complete overhaul of the pdf_exporter

This commit is contained in:
Blake Ridgway 2025-04-22 11:37:00 -05:00
parent a849b79a98
commit 4b025b8517

View file

@ -1,115 +1,96 @@
# timelogix/pdf_exporter.py
import datetime
from reportlab.lib.pagesizes import letter from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas
from reportlab.lib import colors
from reportlab.platypus import Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Preformatted
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
from reportlab.lib import colors
import datetime
class PDFExporter: class PDFExporter:
def __init__(self): def __init__(self, company_name, company_address, client_name, client_address, hourly_rate, invoice_number):
self.company_name = "Your Company Name" self.company_name = company_name
self.company_address = "123 Main St, Anytown, USA" self.company_address = company_address
self.client_name = "Client Name" self.client_name = client_name
self.client_address = "Client Address" self.client_address = client_address
self.hourly_rate = 60.00 self.hourly_rate = hourly_rate
self.invoice_number = 1 self.invoice_number = invoice_number
def export_to_pdf(self, log_entries): def export_to_pdf(self, log_entries, filename):
try: doc = SimpleDocTemplate(filename, pagesize=letter)
filename = f"invoice_{self.invoice_number}.pdf" styles = getSampleStyleSheet()
c = canvas.Canvas(filename, pagesize=letter)
styles = getSampleStyleSheet()
# --- Header --- header_table_data = [
c.setFont("Helvetica-Bold", 16) [
c.drawString(inch, 7.5 * inch, self.company_name) Preformatted(f"BILL FROM:\n{self.company_name}\n{self.company_address}\n", styles['Normal']),
c.setFont("Helvetica", 10) Paragraph(f"INVOICE # {self.invoice_number}", styles['Heading1']),
c.drawString(inch, 7.3 * inch, self.company_address)
c.setFont("Helvetica-Bold", 12) ],
c.drawString(4.5 * inch, 7.5 * inch, "Invoice") [
c.setFont("Helvetica", 10) Preformatted(f"BILL TO:\n{self.client_name}\n{self.client_address}\n", styles['Normal']),
c.drawString( Paragraph(f"Invoice Date: {datetime.date.today().strftime('%m/%d/%Y')}", styles['Normal']),
4.5 * inch, 7.3 * inch, f"Invoice Number: {self.invoice_number}" ],
) ]
current_date = datetime.datetime.now().strftime("%Y-%m-%d")
c.drawString(4.5 * inch, 7.1 * inch, f"Date: {current_date}")
# --- Client Info --- header_table_style = TableStyle([
bill_to_y = 6.5 * inch # Starting y position for "Bill To:" ('ALIGN', (0, 0), (0, 0), 'LEFT'),
line_height = 0.2 * inch # Height for each line of text ('ALIGN', (1, 0), (1, 0), 'RIGHT'),
('ALIGN', (0, 1), (0, 1), 'LEFT'),
('ALIGN', (1, 1), (1, 1), 'LEFT'),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('TOPPADDING', (0, 0), (-1, -1), 0),
('BOTTOMPADDING', (0, 0), (-1, -1), 0),
('LEFTPADDING', (0, 0), (-1, -1), 0),
('RIGHTPADDING', (0, 0), (-1, -1), 0),
])
c.setFont("Helvetica-Bold", 12) header_table = Table(header_table_data, colWidths=[3*inch, 3*inch])
c.drawString(inch, bill_to_y, "Bill To:") # "Bill To:" label header_table.setStyle(header_table_style)
c.setFont("Helvetica", 10) data = [["Date", "Project", "Hours", "Rate", "Total"]]
c.drawString( total_amount = 0
inch, bill_to_y - line_height, self.client_name for entry in log_entries:
) # Client Name hours = entry["duration"] / 3600
c.drawString( line_total = hours * self.hourly_rate
inch, bill_to_y - 2 * line_height, self.client_address total_amount += line_total
) # Client Address data.append([
entry["start_time"].split(' ')[0],
entry["project"],
f"{hours:.2f}",
f"${self.hourly_rate:.2f}",
f"${line_total:.2f}",
])
# --- Table --- table_style = TableStyle([
data = [["Task", "Project", "Hours", "Rate", "Total"]] ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
total_amount = 0 ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
('GRID', (0, 0), (-1, -1), 1, colors.black),
])
table = Table(data)
table.setStyle(table_style)
for entry in log_entries: totals_style = ParagraphStyle(name='Totals', fontSize=10, alignment=TA_RIGHT)
hours = entry["duration"] / 3600 subtotal = Paragraph(f"Subtotal: ${total_amount:.2f}", totals_style)
line_total = hours * self.hourly_rate tax = Paragraph(f"Tax (0%): $0.00", totals_style)
total_amount += line_total total = Paragraph(f"Total: ${total_amount:.2f}", totals_style)
data.append(
[
entry["task"],
entry["project"],
f"{hours:.2f}",
f"${self.hourly_rate:.2f}",
f"${line_total:.2f}",
]
)
table = Table(data, colWidths=[1.5 * inch, 1.5 * inch, inch, inch, inch]) notes_style = ParagraphStyle(name='Notes', fontSize=10, alignment=TA_LEFT)
style = TableStyle( notes = Paragraph("Notes:\nThank you for your business!", notes_style)
[
("BACKGROUND", (0, 0), (-1, 0), colors.grey),
("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke),
("ALIGN", (0, 0), (-1, -1), "CENTER"),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("BOTTOMPADDING", (0, 0), (-1, 0), 12),
("BACKGROUND", (0, 1), (-1, -1), colors.beige),
("GRID", (0, 0), (-1, -1), 1, colors.black),
]
)
table.setStyle(style)
table.wrapOn(c, letter[0] - 2 * inch, letter[1] - 2 * inch)
table.drawOn(c, inch, 4 * inch)
# --- Totals --- elements = [
c.setFont("Helvetica-Bold", 12) header_table,
c.drawString(4 * inch, 3.5 * inch, "Subtotal:") Spacer(1, 12),
c.setFont("Helvetica", 12) table,
c.drawRightString(5.5 * inch, 3.5 * inch, f"${total_amount:.2f}") Spacer(1, 12),
subtotal,
tax,
total,
Spacer(1, 12),
notes,
]
c.setFont("Helvetica-Bold", 12) doc.build(elements)
c.drawString(4 * inch, 3.3 * inch, "Tax (0%):")
c.setFont("Helvetica", 12)
c.drawRightString(5.5 * inch, 3.3 * inch, "$0.00")
c.setFont("Helvetica-Bold", 12)
c.drawString(4 * inch, 3.1 * inch, "Total:")
c.setFont("Helvetica", 12)
c.drawRightString(5.5 * inch, 3.1 * inch, f"${total_amount:.2f}")
# --- Notes ---
c.setFont("Helvetica", 10)
c.drawString(inch, 2 * inch, "Notes:")
c.drawString(inch, 1.8 * inch, "Thank you for your business!")
c.save()
print(f"Exported to PDF successfully as {filename}!")
except Exception as e:
print(f"Error exporting to PDF: {e}")