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.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.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:
def __init__(self):
self.company_name = "Your Company Name"
self.company_address = "123 Main St, Anytown, USA"
self.client_name = "Client Name"
self.client_address = "Client Address"
self.hourly_rate = 60.00
self.invoice_number = 1
def __init__(self, company_name, company_address, client_name, client_address, hourly_rate, invoice_number):
self.company_name = company_name
self.company_address = company_address
self.client_name = client_name
self.client_address = client_address
self.hourly_rate = hourly_rate
self.invoice_number = invoice_number
def export_to_pdf(self, log_entries):
try:
filename = f"invoice_{self.invoice_number}.pdf"
c = canvas.Canvas(filename, pagesize=letter)
styles = getSampleStyleSheet()
def export_to_pdf(self, log_entries, filename):
doc = SimpleDocTemplate(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)
c.setFont("Helvetica", 10)
c.drawString(inch, 7.3 * inch, self.company_address)
],
[
Preformatted(f"BILL TO:\n{self.client_name}\n{self.client_address}\n", styles['Normal']),
Paragraph(f"Invoice Date: {datetime.date.today().strftime('%m/%d/%Y')}", styles['Normal']),
],
]
c.setFont("Helvetica-Bold", 12)
c.drawString(4.5 * inch, 7.5 * inch, "Invoice")
c.setFont("Helvetica", 10)
c.drawString(
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}")
header_table_style = TableStyle([
('ALIGN', (0, 0), (0, 0), 'LEFT'),
('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),
])
# --- Client Info ---
bill_to_y = 6.5 * inch # Starting y position for "Bill To:"
line_height = 0.2 * inch # Height for each line of text
header_table = Table(header_table_data, colWidths=[3*inch, 3*inch])
header_table.setStyle(header_table_style)
c.setFont("Helvetica-Bold", 12)
c.drawString(inch, bill_to_y, "Bill To:") # "Bill To:" label
data = [["Date", "Project", "Hours", "Rate", "Total"]]
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)
c.drawString(
inch, bill_to_y - line_height, self.client_name
) # Client Name
c.drawString(
inch, bill_to_y - 2 * line_height, self.client_address
) # Client Address
totals_style = ParagraphStyle(name='Totals', fontSize=10, alignment=TA_RIGHT)
subtotal = Paragraph(f"Subtotal: ${total_amount:.2f}", totals_style)
tax = Paragraph(f"Tax (0%): $0.00", totals_style)
total = Paragraph(f"Total: ${total_amount:.2f}", totals_style)
# --- Table ---
data = [["Task", "Project", "Hours", "Rate", "Total"]]
total_amount = 0
notes_style = ParagraphStyle(name='Notes', fontSize=10, alignment=TA_LEFT)
notes = Paragraph("Notes:\nThank you for your business!", notes_style)
for entry in log_entries:
hours = entry["duration"] / 3600
line_total = hours * self.hourly_rate
total_amount += line_total
data.append(
[
entry["task"],
entry["project"],
f"{hours:.2f}",
f"${self.hourly_rate:.2f}",
f"${line_total:.2f}",
]
)
elements = [
header_table,
Spacer(1, 12),
table,
Spacer(1, 12),
subtotal,
tax,
total,
Spacer(1, 12),
notes,
]
table = Table(data, colWidths=[1.5 * inch, 1.5 * inch, inch, inch, inch])
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}")
doc.build(elements)