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_table_data = [
[
Preformatted(f"BILL FROM:\n{self.company_name}\n{self.company_address}\n", styles['Normal']),
Paragraph(f"INVOICE # {self.invoice_number}", styles['Heading1']),
# --- Header --- ],
c.setFont("Helvetica-Bold", 16) [
c.drawString(inch, 7.5 * inch, self.company_name) Preformatted(f"BILL TO:\n{self.client_name}\n{self.client_address}\n", styles['Normal']),
c.setFont("Helvetica", 10) Paragraph(f"Invoice Date: {datetime.date.today().strftime('%m/%d/%Y')}", styles['Normal']),
c.drawString(inch, 7.3 * inch, self.company_address) ],
]
c.setFont("Helvetica-Bold", 12) header_table_style = TableStyle([
c.drawString(4.5 * inch, 7.5 * inch, "Invoice") ('ALIGN', (0, 0), (0, 0), 'LEFT'),
c.setFont("Helvetica", 10) ('ALIGN', (1, 0), (1, 0), 'RIGHT'),
c.drawString( ('ALIGN', (0, 1), (0, 1), 'LEFT'),
4.5 * inch, 7.3 * inch, f"Invoice Number: {self.invoice_number}" ('ALIGN', (1, 1), (1, 1), 'LEFT'),
) ('VALIGN', (0, 0), (-1, -1), 'TOP'),
current_date = datetime.datetime.now().strftime("%Y-%m-%d") ('TOPPADDING', (0, 0), (-1, -1), 0),
c.drawString(4.5 * inch, 7.1 * inch, f"Date: {current_date}") ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
('LEFTPADDING', (0, 0), (-1, -1), 0),
('RIGHTPADDING', (0, 0), (-1, -1), 0),
])
# --- Client Info --- header_table = Table(header_table_data, colWidths=[3*inch, 3*inch])
bill_to_y = 6.5 * inch # Starting y position for "Bill To:" header_table.setStyle(header_table_style)
line_height = 0.2 * inch # Height for each line of text
c.setFont("Helvetica-Bold", 12) data = [["Date", "Project", "Hours", "Rate", "Total"]]
c.drawString(inch, bill_to_y, "Bill To:") # "Bill To:" label total_amount = 0
for entry in log_entries:
hours = entry["duration"] / 3600
line_total = hours * self.hourly_rate
total_amount += line_total
data.append([
entry["start_time"].split(' ')[0],
entry["project"],
f"{hours:.2f}",
f"${self.hourly_rate:.2f}",
f"${line_total:.2f}",
])
table_style = TableStyle([
('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 = Table(data)
table.setStyle(table_style)
c.setFont("Helvetica", 10) totals_style = ParagraphStyle(name='Totals', fontSize=10, alignment=TA_RIGHT)
c.drawString( subtotal = Paragraph(f"Subtotal: ${total_amount:.2f}", totals_style)
inch, bill_to_y - line_height, self.client_name tax = Paragraph(f"Tax (0%): $0.00", totals_style)
) # Client Name total = Paragraph(f"Total: ${total_amount:.2f}", totals_style)
c.drawString(
inch, bill_to_y - 2 * line_height, self.client_address
) # Client Address
# --- Table --- notes_style = ParagraphStyle(name='Notes', fontSize=10, alignment=TA_LEFT)
data = [["Task", "Project", "Hours", "Rate", "Total"]] notes = Paragraph("Notes:\nThank you for your business!", notes_style)
total_amount = 0
for entry in log_entries: elements = [
hours = entry["duration"] / 3600 header_table,
line_total = hours * self.hourly_rate Spacer(1, 12),
total_amount += line_total table,
data.append( Spacer(1, 12),
[ subtotal,
entry["task"], tax,
entry["project"], total,
f"{hours:.2f}", Spacer(1, 12),
f"${self.hourly_rate:.2f}", notes,
f"${line_total:.2f}", ]
]
)
table = Table(data, colWidths=[1.5 * inch, 1.5 * inch, inch, inch, inch]) doc.build(elements)
style = TableStyle(
[
("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 ---
c.setFont("Helvetica-Bold", 12)
c.drawString(4 * inch, 3.5 * inch, "Subtotal:")
c.setFont("Helvetica", 12)
c.drawRightString(5.5 * inch, 3.5 * inch, f"${total_amount:.2f}")
c.setFont("Helvetica-Bold", 12)
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}")