package pdf import ( "fmt" "time" "code.springupsoftware.com/cmc/cmc-sales/internal/cmc/db" ) // QuotePDFData contains all data needed to generate a quote PDF type QuotePDFData struct { Document *db.Document Quote interface{} // Quote specific data Enquiry *db.Enquiry Customer *db.Customer EmailTo string Attention string User *db.GetUserRow LineItems []db.GetLineItemsTableRow Currency interface{} // Currency data CurrencySymbol string CurrencyCode string ShowGST bool CommercialComments string DeliveryTime string PaymentTerms string DaysValid int DeliveryPoint string ExchangeRate string CustomsDuty string GSTPhrase string SalesEngineer string BillTo string ShipTo string IssueDateString string Pages []string } // GenerateQuotePDF generates a PDF for a quote func GenerateQuotePDF(data *QuotePDFData, outputDir string) (string, error) { fmt.Printf("GenerateQuotePDF called with outputDir: %s\n", outputDir) gen := NewGenerator(outputDir) // First page with header gen.AddPage() gen.Page1Header() // Extract data for details box companyName := "" if data.Customer != nil { companyName = data.Customer.Name } emailTo := data.EmailTo attention := data.Attention fromName := fmt.Sprintf("%s %s", data.User.FirstName, data.User.LastName) fromEmail := data.User.Email // Use CMC reference as the quote number quoteNumber := data.Document.CmcReference if data.Document.Revision > 0 { quoteNumber = fmt.Sprintf("%s.%d", quoteNumber, data.Document.Revision) } yourReference := fmt.Sprintf("Enquiry on %s", data.Document.Created.Format("2 Jan 2006")) issueDate := data.IssueDateString if issueDate == "" { issueDate = data.Document.Created.Format("2 January 2006") } // Add details box gen.DetailsBox("QUOTE", companyName, emailTo, attention, fromName, fromEmail, quoteNumber, yourReference, issueDate) // Add page content if any for i, content := range data.Pages { if i == 0 { // Content under first page header gen.AddContent(content) gen.Page1Footer() } else { gen.AddPage() // Continued header gen.pdf.SetFont("Helvetica", "B", 12) gen.pdf.CellFormat(0, 6, "CONTINUED: "+data.Document.CmcReference, "", 1, "L", false, 0, "") gen.pdf.Ln(4) gen.AddContent(content) } } if len(data.Pages) == 0 { gen.Page1Footer() } // Add pricing page gen.AddPage() gen.pdf.SetFont("Helvetica", "B", 14) gen.pdf.CellFormat(0, 10, "PRICING & SPECIFICATIONS", "", 1, "C", false, 0, "") gen.pdf.Ln(5) // Convert line items pdfItems := make([]LineItem, len(data.LineItems)) for i, item := range data.LineItems { unitPrice := 0.0 totalPrice := 0.0 // Parse prices if item.GrossUnitPrice.Valid { fmt.Sscanf(item.GrossUnitPrice.String, "%f", &unitPrice) } if item.GrossPrice.Valid { fmt.Sscanf(item.GrossPrice.String, "%f", &totalPrice) } pdfItems[i] = LineItem{ ItemNumber: item.ItemNumber, Quantity: item.Quantity, Title: item.Title, UnitPrice: unitPrice, TotalPrice: totalPrice, } } // Add line items table gen.AddLineItemsTable(pdfItems, data.CurrencySymbol, data.ShowGST) // Add commercial comments if any if data.CommercialComments != "" { gen.pdf.Ln(10) gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(0, 5, "COMMERCIAL COMMENTS", "", 1, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 9) gen.pdf.MultiCell(0, 5, data.CommercialComments, "", "L", false) } // Add quote details table (delivery time, payment terms, etc.) if data.DeliveryTime != "" || data.PaymentTerms != "" || data.DaysValid > 0 { gen.AddQuoteDetailsTable(data) } // Add terms and conditions page gen.AddTermsAndConditions() // Generate filename filename := quoteNumber if data.Document.Revision > 0 { filename = fmt.Sprintf("%s_%d.pdf", quoteNumber, data.Document.Revision) } else { filename = fmt.Sprintf("%s.pdf", quoteNumber) } // Save PDF fmt.Printf("Saving PDF with filename: %s to outputDir: %s\n", filename, outputDir) err := gen.Save(filename) if err != nil { fmt.Printf("Error saving PDF: %v\n", err) } else { fmt.Printf("PDF saved successfully: %s\n", filename) } return filename, err } // InvoicePDFData contains all data needed to generate an invoice PDF type InvoicePDFData struct { Document *db.Document Invoice *db.Invoice Enquiry *db.Enquiry Customer *db.Customer Job interface{} // Job data LineItems []db.GetLineItemsTableRow Currency interface{} // Currency data CurrencySymbol string CurrencyCode string ShowGST bool ShipVia string FOB string IssueDate time.Time IssueDateString string EmailTo string Attention string FromName string FromEmail string YourReference string BillTo string ShipTo string ShippingDetails string CustomerOrderNumber string JobTitle string PaymentTerms string CustomerABN string Subtotal interface{} // Can be float or "TBA" GSTAmount interface{} Total interface{} } // GenerateInvoicePDF generates a PDF for an invoice func GenerateInvoicePDF(data *InvoicePDFData, outputDir string) (string, error) { gen := NewGenerator(outputDir) // First page with header gen.AddPage() gen.Page1Header() // Title gen.pdf.SetFont("Helvetica", "B", 18) gen.pdf.SetTextColor(0, 0, 0) gen.pdf.CellFormat(0, 10, "TAX INVOICE", "", 1, "C", false, 0, "") gen.pdf.Ln(5) // Sold To / Delivery Address boxes gen.AddInvoiceAddressBoxes(data) // More details table (Order Number, Job, FOB, Payment Terms, ABN) gen.AddInvoiceDetailsTable(data) gen.pdf.Ln(5) // Line items table header gen.AddInvoiceLineItemsHeader(data.CurrencyCode) gen.Page1Footer() // Add line items on new page(s) gen.AddPage() gen.pdf.SetFont("Helvetica", "B", 12) gen.pdf.CellFormat(0, 6, "CONTINUED: "+data.Invoice.Title, "", 1, "L", false, 0, "") gen.pdf.Ln(4) // Add line items content gen.AddInvoiceLineItemsContent(data) // Add terms and conditions page gen.AddTermsAndConditions() // Generate filename and save filename := fmt.Sprintf("%s.pdf", data.Invoice.Title) if err := gen.Save(filename); err != nil { return "", err } return filename, nil } // PurchaseOrderPDFData contains all data needed to generate a purchase order PDF type PurchaseOrderPDFData struct { Document *db.Document PurchaseOrder *db.PurchaseOrder Principle *db.Principle LineItems []db.GetLineItemsTableRow Currency interface{} // Currency data CurrencySymbol string CurrencyCode string ShowGST bool Subtotal float64 GSTAmount float64 Total float64 IssueDateString string } // GeneratePurchaseOrderPDF generates a PDF for a purchase order func GeneratePurchaseOrderPDF(data *PurchaseOrderPDFData, outputDir string) (string, error) { gen := NewGenerator(outputDir) // First page with header gen.AddPage() gen.Page1Header() // Extract data for details box companyName := data.Principle.Name emailTo := "" // TODO: Get from principle contact attention := "" // TODO: Get from principle contact fromName := "" // TODO: Get from user fromEmail := "" // TODO: Get from user poNumber := data.PurchaseOrder.Title yourReference := data.PurchaseOrder.PrincipleReference issueDate := data.IssueDateString if issueDate == "" { issueDate = data.PurchaseOrder.IssueDate.Format("Monday, 2 January 2006") } // Add details box gen.DetailsBox("PURCHASE ORDER", companyName, emailTo, attention, fromName, fromEmail, poNumber, yourReference, issueDate) // Add PO specific details gen.pdf.Ln(5) gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(40, 5, "Ordered From:", "", 0, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.MultiCell(0, 5, data.PurchaseOrder.OrderedFrom, "", "L", false) gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(40, 5, "Dispatch By:", "", 0, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.CellFormat(0, 5, data.PurchaseOrder.DispatchBy, "", 1, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(40, 5, "Deliver To:", "", 0, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.MultiCell(0, 5, data.PurchaseOrder.DeliverTo, "", "L", false) if data.PurchaseOrder.ShippingInstructions != "" { gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(0, 5, "Shipping Instructions:", "", 1, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.MultiCell(0, 5, data.PurchaseOrder.ShippingInstructions, "", "L", false) } gen.Page1Footer() // Add line items page gen.AddPage() gen.pdf.SetFont("Helvetica", "B", 14) gen.pdf.CellFormat(0, 10, "ORDER DETAILS", "", 1, "C", false, 0, "") gen.pdf.Ln(5) // Convert line items pdfItems := make([]LineItem, len(data.LineItems)) for i, item := range data.LineItems { unitPrice := 0.0 totalPrice := 0.0 // Parse prices if item.GrossUnitPrice.Valid { fmt.Sscanf(item.GrossUnitPrice.String, "%f", &unitPrice) } if item.GrossPrice.Valid { fmt.Sscanf(item.GrossPrice.String, "%f", &totalPrice) } pdfItems[i] = LineItem{ ItemNumber: item.ItemNumber, Quantity: item.Quantity, Title: item.Title, UnitPrice: unitPrice, TotalPrice: totalPrice, } } // Add line items table with totals gen.AddLineItemsTable(pdfItems, data.CurrencySymbol, data.ShowGST) // Add totals section gen.pdf.Ln(5) gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(120, 5, "SUBTOTAL:", "T", 0, "R", false, 0, "") gen.pdf.CellFormat(0, 5, fmt.Sprintf("%s%.2f", data.CurrencySymbol, data.Subtotal), "T", 1, "R", false, 0, "") if data.ShowGST && data.GSTAmount > 0 { gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.CellFormat(120, 5, "GST:", "", 0, "R", false, 0, "") gen.pdf.CellFormat(0, 5, fmt.Sprintf("%s%.2f", data.CurrencySymbol, data.GSTAmount), "", 1, "R", false, 0, "") } gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(120, 5, "TOTAL:", "T", 0, "R", false, 0, "") gen.pdf.CellFormat(0, 5, fmt.Sprintf("%s%.2f", data.CurrencySymbol, data.Total), "T", 1, "R", false, 0, "") // Add terms and conditions page gen.AddTermsAndConditions() // Generate filename filename := poNumber if data.Document.Revision > 0 { filename = fmt.Sprintf("%s-Rev%d.pdf", data.PurchaseOrder.Title, data.Document.Revision) } else { filename = fmt.Sprintf("%s.pdf", data.PurchaseOrder.Title) } // Save PDF err := gen.Save(filename) return filename, err } // PackingListPDFData contains data for a packing list type PackingListPDFData struct { Document *db.Document Customer *db.Customer Title string JobTitle string IssueDate string IssueDateString string ShipVia string FOB string LineItems []db.GetLineItemsTableRow CurrencySymbol string CurrencyCode string ShowGST bool } // GeneratePackingListPDF generates a PDF for a packing list func GeneratePackingListPDF(data *PackingListPDFData, outputDir string) (string, error) { gen := NewGenerator(outputDir) // Header gen.AddPage() gen.Page1Header() gen.pdf.SetFont("Helvetica", "B", 16) gen.pdf.CellFormat(0, 10, "PACKING LIST", "", 1, "C", false, 0, "") gen.pdf.Ln(5) // Details box with proper title and date issueDate := data.IssueDateString if issueDate == "" { issueDate = time.Now().Format("2 January 2006") } refNumber := data.Title if refNumber == "" { refNumber = fmt.Sprintf("PL-%d", data.Document.ID) } gen.DetailsBox("PACKING LIST", data.Customer.Name, "", "", "", "", refNumber, "", issueDate) // Add shipping details section gen.pdf.Ln(5) if data.JobTitle != "" { gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(40, 5, "Job Reference:", "", 0, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.CellFormat(0, 5, data.JobTitle, "", 1, "L", false, 0, "") } if data.ShipVia != "" { gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(40, 5, "Ship Via:", "", 0, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.CellFormat(0, 5, data.ShipVia, "", 1, "L", false, 0, "") } if data.FOB != "" { gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(40, 5, "FOB:", "", 0, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.CellFormat(0, 5, data.FOB, "", 1, "L", false, 0, "") } gen.Page1Footer() // Line items gen.AddPage() pdfItems := make([]LineItem, len(data.LineItems)) for i, item := range data.LineItems { unitPrice := 0.0 totalPrice := 0.0 if item.GrossUnitPrice.Valid { fmt.Sscanf(item.GrossUnitPrice.String, "%f", &unitPrice) } if item.GrossPrice.Valid { fmt.Sscanf(item.GrossPrice.String, "%f", &totalPrice) } pdfItems[i] = LineItem{ItemNumber: item.ItemNumber, Quantity: item.Quantity, Title: item.Title, UnitPrice: unitPrice, TotalPrice: totalPrice} } gen.AddLineItemsTable(pdfItems, data.CurrencySymbol, data.ShowGST) // Add terms and conditions page gen.AddTermsAndConditions() filename := data.Title if filename == "" { filename = fmt.Sprintf("PL-%d", data.Document.ID) } filename = fmt.Sprintf("%s.pdf", filename) err := gen.Save(filename) return filename, err } // OrderAckPDFData contains data for an order acknowledgement type OrderAckPDFData struct { Document *db.Document Customer *db.Customer Title string JobTitle string IssueDate string IssueDateString string ShipVia string FOB string EstimatedDelivery string LineItems []db.GetLineItemsTableRow CurrencySymbol string CurrencyCode string ShowGST bool } // GenerateOrderAckPDF generates a PDF for an order acknowledgement func GenerateOrderAckPDF(data *OrderAckPDFData, outputDir string) (string, error) { gen := NewGenerator(outputDir) // Header gen.AddPage() gen.Page1Header() gen.pdf.SetFont("Helvetica", "B", 16) gen.pdf.CellFormat(0, 10, "ORDER ACKNOWLEDGEMENT", "", 1, "C", false, 0, "") gen.pdf.Ln(5) // Details box with proper title and date issueDate := data.IssueDateString if issueDate == "" { issueDate = time.Now().Format("2 January 2006") } refNumber := data.Title if refNumber == "" { refNumber = fmt.Sprintf("OA-%d", data.Document.ID) } gen.DetailsBox("ORDER ACK", data.Customer.Name, "", "", "", "", refNumber, "", issueDate) // Add shipping/delivery details section gen.pdf.Ln(5) if data.JobTitle != "" { gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(40, 5, "Job Reference:", "", 0, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.CellFormat(0, 5, data.JobTitle, "", 1, "L", false, 0, "") } if data.ShipVia != "" { gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(40, 5, "Ship Via:", "", 0, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.CellFormat(0, 5, data.ShipVia, "", 1, "L", false, 0, "") } if data.FOB != "" { gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(40, 5, "FOB:", "", 0, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.CellFormat(0, 5, data.FOB, "", 1, "L", false, 0, "") } if data.EstimatedDelivery != "" { gen.pdf.SetFont("Helvetica", "B", 10) gen.pdf.CellFormat(40, 5, "Est. Delivery:", "", 0, "L", false, 0, "") gen.pdf.SetFont("Helvetica", "", 10) gen.pdf.CellFormat(0, 5, data.EstimatedDelivery, "", 1, "L", false, 0, "") } gen.Page1Footer() // Line items gen.AddPage() pdfItems := make([]LineItem, len(data.LineItems)) for i, item := range data.LineItems { unitPrice := 0.0 totalPrice := 0.0 if item.GrossUnitPrice.Valid { fmt.Sscanf(item.GrossUnitPrice.String, "%f", &unitPrice) } if item.GrossPrice.Valid { fmt.Sscanf(item.GrossPrice.String, "%f", &totalPrice) } pdfItems[i] = LineItem{ItemNumber: item.ItemNumber, Quantity: item.Quantity, Title: item.Title, UnitPrice: unitPrice, TotalPrice: totalPrice} } gen.AddLineItemsTable(pdfItems, data.CurrencySymbol, data.ShowGST) // Add terms and conditions page gen.AddTermsAndConditions() filename := data.Title if filename == "" { filename = fmt.Sprintf("OA-%d", data.Document.ID) } filename = fmt.Sprintf("%s.pdf", filename) err := gen.Save(filename) return filename, err }