diff --git a/pdf_exporter.py b/pdf_exporter.py index f1d59ad..32f9670 100644 --- a/pdf_exporter.py +++ b/pdf_exporter.py @@ -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)