cmc-sales/go/internal/cmc/pdf/templates.go

293 lines
8.5 KiB
Go
Raw Normal View History

2025-07-02 05:04:36 -07:00
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 {
2026-01-13 03:13:06 -08:00
Document *db.Document
Quote interface{} // Quote specific data
Enquiry *db.Enquiry
Customer *db.Customer
Contact interface{} // Contact data
User *db.GetUserRow
LineItems []db.GetLineItemsTableRow
Currency interface{} // Currency data
CurrencySymbol string
ShowGST bool
2025-07-02 05:04:36 -07:00
CommercialComments 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)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// First page with header
gen.AddPage()
gen.Page1Header()
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Extract data for details box
2025-07-06 14:34:12 -07:00
companyName := "" // TODO: Get from customer data
if data.Customer != nil {
companyName = data.Customer.Name
}
2026-01-13 03:13:06 -08:00
emailTo := "" // TODO: Get from contact
2025-07-02 05:04:36 -07:00
attention := "" // TODO: Get from contact
fromName := fmt.Sprintf("%s %s", data.User.FirstName, data.User.LastName)
fromEmail := data.User.Email
2026-01-13 03:13:06 -08:00
2025-07-06 14:34:12 -07:00
// Use CMC reference as the quote number
quoteNumber := data.Document.CmcReference
2025-07-02 05:04:36 -07:00
if data.Document.Revision > 0 {
2025-07-06 14:34:12 -07:00
quoteNumber = fmt.Sprintf("%s.%d", quoteNumber, data.Document.Revision)
2025-07-02 05:04:36 -07:00
}
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
yourReference := fmt.Sprintf("Enquiry on %s", data.Document.Created.Format("2 Jan 2006"))
2025-07-06 14:34:12 -07:00
issueDate := data.Document.Created.Format("2 January 2006")
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Add details box
2025-07-06 14:34:12 -07:00
gen.DetailsBox("QUOTE", companyName, emailTo, attention, fromName, fromEmail, quoteNumber, yourReference, issueDate)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Add page content if any
// TODO: Add document pages content
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
gen.Page1Footer()
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// 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)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Convert line items
pdfItems := make([]LineItem, len(data.LineItems))
for i, item := range data.LineItems {
unitPrice := 0.0
totalPrice := 0.0
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Parse prices
if item.GrossUnitPrice.Valid {
fmt.Sscanf(item.GrossUnitPrice.String, "%f", &unitPrice)
}
if item.GrossPrice.Valid {
fmt.Sscanf(item.GrossPrice.String, "%f", &totalPrice)
}
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
pdfItems[i] = LineItem{
ItemNumber: item.ItemNumber,
Quantity: item.Quantity,
Title: item.Title,
UnitPrice: unitPrice,
TotalPrice: totalPrice,
}
}
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Add line items table
gen.AddLineItemsTable(pdfItems, data.CurrencySymbol, data.ShowGST)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// 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)
}
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// TODO: Add terms and conditions page
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Generate filename
2025-07-06 14:34:12 -07:00
filename := quoteNumber
2025-07-02 05:04:36 -07:00
if data.Document.Revision > 0 {
2025-07-06 14:34:12 -07:00
filename = fmt.Sprintf("%s_%d.pdf", quoteNumber, data.Document.Revision)
2025-07-02 05:04:36 -07:00
} else {
2025-07-06 14:34:12 -07:00
filename = fmt.Sprintf("%s.pdf", quoteNumber)
2025-07-02 05:04:36 -07:00
}
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// 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 {
2026-01-13 03:13:06 -08:00
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
BillTo string
ShipTo string
ShippingDetails string
CustomerOrderNumber string
JobTitle string
PaymentTerms string
CustomerABN string
Subtotal float64
GSTAmount float64
Total float64
2025-07-02 05:04:36 -07:00
}
2026-01-13 03:13:06 -08:00
// GenerateInvoicePDF generates a PDF for an invoice matching old TCPDF layout
2025-07-02 05:04:36 -07:00
func GenerateInvoicePDF(data *InvoicePDFData, outputDir string) (string, error) {
gen := NewGenerator(outputDir)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// First page with header
gen.AddPage()
gen.Page1Header()
2026-01-13 03:13:06 -08:00
// Title - TAX INVOICE
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(3)
// Shipping/Billing Box with payment details (matches old TCPDF layout)
gen.AddInvoiceShippingBillingBox(data)
// Details table - CUSTOMER ORDER NO, JOB #, FOB, PAYMENT TERMS, ABN
gen.pdf.Ln(2)
gen.AddInvoiceDetailsTable(data)
gen.pdf.Ln(3)
// Line items table with freight and totals (matches old TCPDF layout)
gen.AddInvoiceLineItemsTableWithShipping(data)
2025-07-02 05:04:36 -07:00
gen.Page1Footer()
2026-01-13 03:13:06 -08:00
// 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
2025-07-02 05:04:36 -07:00
}
2026-01-13 03:13:06 -08:00
return filename, nil
2025-07-02 05:04:36 -07:00
}
// PurchaseOrderPDFData contains all data needed to generate a purchase order PDF
type PurchaseOrderPDFData struct {
2026-01-13 03:13:06 -08:00
Document *db.Document
PurchaseOrder *db.PurchaseOrder
Principle *db.Principle
LineItems []db.GetLineItemsTableRow
Currency interface{} // Currency data
CurrencySymbol string
ShowGST bool
2025-07-02 05:04:36 -07:00
}
// GeneratePurchaseOrderPDF generates a PDF for a purchase order
func GeneratePurchaseOrderPDF(data *PurchaseOrderPDFData, outputDir string) (string, error) {
gen := NewGenerator(outputDir)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// First page with header
gen.AddPage()
gen.Page1Header()
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Extract data for details box
companyName := data.Principle.Name
2026-01-13 03:13:06 -08:00
emailTo := "" // TODO: Get from principle contact
2025-07-02 05:04:36 -07:00
attention := "" // TODO: Get from principle contact
2026-01-13 03:13:06 -08:00
fromName := "" // TODO: Get from user
2025-07-02 05:04:36 -07:00
fromEmail := "" // TODO: Get from user
poNumber := data.PurchaseOrder.Title
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
yourReference := data.PurchaseOrder.PrincipleReference
issueDate := data.PurchaseOrder.IssueDate.Format("Monday, 2 January 2006")
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Add details box
gen.DetailsBox("PURCHASE ORDER", companyName, emailTo, attention, fromName, fromEmail, poNumber, yourReference, issueDate)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// 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)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
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, "")
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
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)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
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)
}
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
gen.Page1Footer()
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// 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)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Convert line items
pdfItems := make([]LineItem, len(data.LineItems))
for i, item := range data.LineItems {
unitPrice := 0.0
totalPrice := 0.0
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Parse prices
if item.GrossUnitPrice.Valid {
fmt.Sscanf(item.GrossUnitPrice.String, "%f", &unitPrice)
}
if item.GrossPrice.Valid {
fmt.Sscanf(item.GrossPrice.String, "%f", &totalPrice)
}
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
pdfItems[i] = LineItem{
ItemNumber: item.ItemNumber,
Quantity: item.Quantity,
Title: item.Title,
UnitPrice: unitPrice,
TotalPrice: totalPrice,
}
}
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Add line items table
gen.AddLineItemsTable(pdfItems, data.CurrencySymbol, data.ShowGST)
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// 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)
}
2026-01-13 03:13:06 -08:00
2025-07-02 05:04:36 -07:00
// Save PDF
err := gen.Save(filename)
return filename, err
2026-01-13 03:13:06 -08:00
}