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

311 lines
10 KiB
Go

package pdf
import (
"fmt"
"path/filepath"
"github.com/jung-kurt/gofpdf"
)
// Generator handles PDF generation for documents
type Generator struct {
pdf *gofpdf.Fpdf
outputDir string
headerText string
footerText string
docRef string
currentPage int
}
// NewGenerator creates a new PDF generator
func NewGenerator(outputDir string) *Generator {
pdf := gofpdf.New("P", "mm", "A4", "")
return &Generator{
pdf: pdf,
outputDir: outputDir,
headerText: "CMC TECHNOLOGIES",
footerText: "Copyright © %d CMC Technologies. All rights reserved.",
}
}
// AddPage adds a new page to the PDF
func (g *Generator) AddPage() {
g.pdf.AddPage()
g.currentPage++
}
// Page1Header adds the standard header for page 1
func (g *Generator) Page1Header() {
g.pdf.SetY(10)
// Set text color to blue
g.pdf.SetTextColor(0, 0, 152)
// Add logo if available (assuming logo is in static directory)
logoPath := filepath.Join("static", "images", "cmclogosmall.png")
// Try to add logo, but don't fail if it doesn't exist or isn't a proper image
g.pdf.ImageOptions(logoPath, 10, 10, 0, 28, false, gofpdf.ImageOptions{ImageType: "PNG"}, 0, "http://www.cmctechnologies.com.au")
// Company name
g.pdf.SetFont("Helvetica", "B", 30)
g.pdf.SetX(40)
g.pdf.CellFormat(0, 0, g.headerText, "", 1, "C", false, 0, "")
// Company details
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.SetY(22)
g.pdf.SetX(40)
g.pdf.CellFormat(0, 0, "PTY LIMITED ACN: 085 991 224 ABN: 47 085 991 224", "", 1, "C", false, 0, "")
// Draw horizontal line
g.pdf.SetDrawColor(0, 0, 0)
g.pdf.Line(43, 24, 200, 24)
// Contact details
g.pdf.SetTextColor(0, 0, 0)
g.pdf.SetY(32)
// Left column - labels
g.pdf.SetX(45)
g.pdf.MultiCell(30, 5, "Phone:\nFax:\nEmail:\nWeb Site:", "", "L", false)
// Middle column - values
g.pdf.SetY(32)
g.pdf.SetX(65)
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.MultiCell(55, 5, "+61 2 9669 4000\n+61 2 9669 4111\nsales@cmctechnologies.com.au\nwww.cmctechnologies.net.au", "", "L", false)
// Right column - address
g.pdf.SetY(32)
g.pdf.SetX(150)
g.pdf.MultiCell(52, 5, "Unit 19, 77 Bourke Rd\nAlexandria NSW 2015\nAUSTRALIA", "", "L", false)
// Engineering text
g.pdf.SetTextColor(0, 0, 152)
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.SetY(37)
g.pdf.SetX(10)
g.pdf.MultiCell(30, 5, "Engineering &\nIndustrial\nInstrumentation", "", "L", false)
}
// Page1Footer adds the standard footer
func (g *Generator) Page1Footer() {
g.pdf.SetY(-20)
// Footer line
g.pdf.SetDrawColor(0, 0, 0)
g.pdf.Line(10, g.pdf.GetY(), 200, g.pdf.GetY())
// Footer text
g.pdf.SetFont("Helvetica", "", 9)
g.pdf.SetY(-18)
g.pdf.CellFormat(0, 5, "CMC TECHNOLOGIES Provides Solutions in the Following Fields", "", 1, "C", false, 0, "")
// Color-coded services
g.pdf.SetY(-13)
g.pdf.SetX(10)
// First line of services
services := []struct {
text string
r, g, b int
}{
{"EXPLOSION PREVENTION AND PROTECTION", 153, 0, 10},
{"—", 0, 0, 0},
{"FIRE PROTECTION", 255, 153, 0},
{"—", 0, 0, 0},
{"PRESSURE RELIEF", 255, 0, 25},
{"—", 0, 0, 0},
{"VISION IN THE PROCESS", 0, 128, 30},
}
x := 15.0
for _, service := range services {
g.pdf.SetTextColor(service.r, service.g, service.b)
width := g.pdf.GetStringWidth(service.text)
g.pdf.SetX(x)
g.pdf.CellFormat(width, 5, service.text, "", 0, "L", false, 0, "")
x += width + 1
}
// Second line of services
g.pdf.SetY(-8)
g.pdf.SetX(60)
g.pdf.SetTextColor(47, 75, 224)
g.pdf.CellFormat(0, 5, "FLOW MEASUREMENT", "", 0, "L", false, 0, "")
g.pdf.SetX(110)
g.pdf.SetTextColor(171, 49, 248)
g.pdf.CellFormat(0, 5, "—PROCESS INSTRUMENTATION", "", 0, "L", false, 0, "")
}
// DetailsBox adds the document details box (quote/invoice details)
func (g *Generator) DetailsBox(docType, companyName, emailTo, attention, fromName, fromEmail, refNumber, yourRef, issueDate string) {
g.pdf.SetY(60)
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.SetTextColor(0, 0, 0)
// Create details table
lineHeight := 5.0
col1Width := 40.0
col2Width := 60.0
col3Width := 40.0
col4Width := 50.0
// Row 1
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.CellFormat(col1Width, lineHeight, "COMPANY NAME:", "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.CellFormat(col2Width, lineHeight, companyName, "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.CellFormat(col3Width, lineHeight, docType+" NO.:", "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.CellFormat(col4Width, lineHeight, refNumber, "1", 1, "L", false, 0, "")
// Row 2
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.CellFormat(col1Width, lineHeight, "EMAIL TO:", "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.CellFormat(col2Width, lineHeight, emailTo, "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.CellFormat(col3Width, lineHeight, "YOUR REFERENCE:", "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.CellFormat(col4Width, lineHeight, yourRef, "1", 1, "L", false, 0, "")
// Row 3
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.CellFormat(col1Width, lineHeight, "ATTENTION:", "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.CellFormat(col2Width, lineHeight, attention, "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.CellFormat(col3Width, lineHeight, "ISSUE DATE:", "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.CellFormat(col4Width, lineHeight, issueDate, "1", 1, "L", false, 0, "")
// Row 4
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.CellFormat(col1Width, lineHeight, "FROM:", "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.CellFormat(col2Width, lineHeight, fromName, "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.CellFormat(col3Width, lineHeight, "PAGES:", "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "", 10)
pageText := fmt.Sprintf("%d of {nb}", g.pdf.PageNo())
g.pdf.CellFormat(col4Width, lineHeight, pageText, "1", 1, "L", false, 0, "")
// Row 5
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.CellFormat(col1Width, lineHeight, "EMAIL:", "1", 0, "L", false, 0, "")
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.CellFormat(col2Width, lineHeight, fromEmail, "1", 0, "L", false, 0, "")
g.pdf.CellFormat(col3Width+col4Width, lineHeight, "", "1", 1, "L", false, 0, "")
}
// AddContent adds HTML content to the PDF
func (g *Generator) AddContent(content string) {
g.pdf.SetFont("Helvetica", "", 10)
g.pdf.SetTextColor(0, 0, 0)
g.pdf.SetY(g.pdf.GetY() + 10)
// Basic HTML to PDF conversion
// Note: gofpdf has limited HTML support, so we'll need to parse and convert manually
// For now, we'll just add the content as plain text
g.pdf.MultiCell(0, 5, content, "", "L", false)
}
// AddLineItemsTable adds a table of line items
func (g *Generator) AddLineItemsTable(items []LineItem, currencySymbol string, showGST bool) {
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.SetTextColor(0, 0, 0)
// Column widths
itemWidth := 15.0
qtyWidth := 15.0
descWidth := 100.0
unitWidth := 30.0
totalWidth := 30.0
// Table header
g.pdf.CellFormat(itemWidth, 7, "ITEM", "1", 0, "C", false, 0, "")
g.pdf.CellFormat(qtyWidth, 7, "QTY", "1", 0, "C", false, 0, "")
g.pdf.CellFormat(descWidth, 7, "DESCRIPTION", "1", 0, "C", false, 0, "")
g.pdf.CellFormat(unitWidth, 7, "UNIT PRICE", "1", 0, "C", false, 0, "")
g.pdf.CellFormat(totalWidth, 7, "TOTAL", "1", 1, "C", false, 0, "")
// Table rows
g.pdf.SetFont("Helvetica", "", 9)
subtotal := 0.0
for _, item := range items {
// Check if we need a new page
if g.pdf.GetY() > 250 {
g.AddPage()
g.pdf.SetFont("Helvetica", "B", 10)
g.pdf.CellFormat(itemWidth, 7, "ITEM", "1", 0, "C", false, 0, "")
g.pdf.CellFormat(qtyWidth, 7, "QTY", "1", 0, "C", false, 0, "")
g.pdf.CellFormat(descWidth, 7, "DESCRIPTION", "1", 0, "C", false, 0, "")
g.pdf.CellFormat(unitWidth, 7, "UNIT PRICE", "1", 0, "C", false, 0, "")
g.pdf.CellFormat(totalWidth, 7, "TOTAL", "1", 1, "C", false, 0, "")
g.pdf.SetFont("Helvetica", "", 9)
}
g.pdf.CellFormat(itemWidth, 6, item.ItemNumber, "1", 0, "C", false, 0, "")
g.pdf.CellFormat(qtyWidth, 6, item.Quantity, "1", 0, "C", false, 0, "")
g.pdf.CellFormat(descWidth, 6, item.Title, "1", 0, "L", false, 0, "")
g.pdf.CellFormat(unitWidth, 6, fmt.Sprintf("%s%.2f", currencySymbol, item.UnitPrice), "1", 0, "R", false, 0, "")
g.pdf.CellFormat(totalWidth, 6, fmt.Sprintf("%s%.2f", currencySymbol, item.TotalPrice), "1", 1, "R", false, 0, "")
subtotal += item.TotalPrice
}
// Totals
g.pdf.SetFont("Helvetica", "B", 10)
// Subtotal
g.pdf.SetX(g.pdf.GetX() + itemWidth + qtyWidth + descWidth)
g.pdf.CellFormat(unitWidth, 6, "SUBTOTAL:", "1", 0, "R", false, 0, "")
g.pdf.CellFormat(totalWidth, 6, fmt.Sprintf("%s%.2f", currencySymbol, subtotal), "1", 1, "R", false, 0, "")
// GST if applicable
if showGST {
gst := subtotal * 0.10
g.pdf.SetX(g.pdf.GetX() + itemWidth + qtyWidth + descWidth)
g.pdf.CellFormat(unitWidth, 6, "GST (10%):", "1", 0, "R", false, 0, "")
g.pdf.CellFormat(totalWidth, 6, fmt.Sprintf("%s%.2f", currencySymbol, gst), "1", 1, "R", false, 0, "")
// Total
total := subtotal + gst
g.pdf.SetX(g.pdf.GetX() + itemWidth + qtyWidth + descWidth)
g.pdf.CellFormat(unitWidth, 6, "TOTAL DUE:", "1", 0, "R", false, 0, "")
g.pdf.CellFormat(totalWidth, 6, fmt.Sprintf("%s%.2f", currencySymbol, total), "1", 1, "R", false, 0, "")
} else {
// Total without GST
g.pdf.SetX(g.pdf.GetX() + itemWidth + qtyWidth + descWidth)
g.pdf.CellFormat(unitWidth, 6, "TOTAL:", "1", 0, "R", false, 0, "")
g.pdf.CellFormat(totalWidth, 6, fmt.Sprintf("%s%.2f", currencySymbol, subtotal), "1", 1, "R", false, 0, "")
}
}
// Save saves the PDF to a file
func (g *Generator) Save(filename string) error {
g.pdf.AliasNbPages("")
outputPath := filepath.Join(g.outputDir, filename)
fmt.Printf("Generator.Save: Saving PDF to path: %s\n", outputPath)
err := g.pdf.OutputFileAndClose(outputPath)
if err != nil {
fmt.Printf("Generator.Save: Error saving PDF: %v\n", err)
} else {
fmt.Printf("Generator.Save: PDF saved successfully to: %s\n", outputPath)
}
return err
}
// LineItem represents a line item for the PDF
type LineItem struct {
ItemNumber string
Quantity string
Title string
UnitPrice float64
TotalPrice float64
}