2025-07-02 05:04:36 -07:00
|
|
|
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", "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
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)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// Set text color to blue
|
|
|
|
|
g.pdf.SetTextColor(0, 0, 152)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// Add logo if available (assuming logo is in static directory)
|
2026-01-13 04:26:37 -08:00
|
|
|
logoPath := filepath.Join("static", "images", "CMC-Mobile-Logo.png")
|
2025-07-02 05:04:36 -07:00
|
|
|
// Try to add logo, but don't fail if it doesn't exist or isn't a proper image
|
2025-07-06 14:34:12 -07:00
|
|
|
g.pdf.ImageOptions(logoPath, 10, 10, 0, 28, false, gofpdf.ImageOptions{ImageType: "PNG"}, 0, "http://www.cmctechnologies.com.au")
|
|
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// Company name
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 30)
|
|
|
|
|
g.pdf.SetX(40)
|
|
|
|
|
g.pdf.CellFormat(0, 0, g.headerText, "", 1, "C", false, 0, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// Draw horizontal line
|
|
|
|
|
g.pdf.SetDrawColor(0, 0, 0)
|
|
|
|
|
g.pdf.Line(43, 24, 200, 24)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2026-01-13 04:26:37 -08:00
|
|
|
// Contact details in table format
|
2025-07-02 05:04:36 -07:00
|
|
|
g.pdf.SetTextColor(0, 0, 0)
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 10)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2026-01-13 04:26:37 -08:00
|
|
|
startY := 32.0
|
|
|
|
|
labelX := 45.0
|
|
|
|
|
valueX := 65.0
|
|
|
|
|
addressX := 150.0
|
|
|
|
|
lineHeight := 5.0
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2026-01-13 04:26:37 -08:00
|
|
|
// Row 1: Phone
|
|
|
|
|
g.pdf.SetXY(labelX, startY)
|
|
|
|
|
g.pdf.Cell(20, lineHeight, "Phone:")
|
|
|
|
|
g.pdf.SetXY(valueX, startY)
|
|
|
|
|
g.pdf.Cell(55, lineHeight, "+61 2 9669 4000")
|
|
|
|
|
g.pdf.SetXY(addressX, startY)
|
|
|
|
|
g.pdf.Cell(52, lineHeight, "Unit 19, 77 Bourke Rd")
|
|
|
|
|
|
|
|
|
|
// Row 2: Fax
|
|
|
|
|
g.pdf.SetXY(labelX, startY+lineHeight)
|
|
|
|
|
g.pdf.Cell(20, lineHeight, "Fax:")
|
|
|
|
|
g.pdf.SetXY(valueX, startY+lineHeight)
|
|
|
|
|
g.pdf.Cell(55, lineHeight, "+61 2 9669 4111")
|
|
|
|
|
g.pdf.SetXY(addressX, startY+lineHeight)
|
|
|
|
|
g.pdf.Cell(52, lineHeight, "Alexandria NSW 2015")
|
|
|
|
|
|
|
|
|
|
// Row 3: Email
|
|
|
|
|
g.pdf.SetXY(labelX, startY+lineHeight*2)
|
|
|
|
|
g.pdf.Cell(20, lineHeight, "Email:")
|
|
|
|
|
g.pdf.SetXY(valueX, startY+lineHeight*2)
|
|
|
|
|
g.pdf.Cell(55, lineHeight, "sales@cmctechnologies.com.au")
|
|
|
|
|
g.pdf.SetXY(addressX, startY+lineHeight*2)
|
|
|
|
|
g.pdf.Cell(52, lineHeight, "AUSTRALIA")
|
|
|
|
|
|
|
|
|
|
// Row 4: Web Site
|
|
|
|
|
g.pdf.SetXY(labelX, startY+lineHeight*3)
|
|
|
|
|
g.pdf.Cell(20, lineHeight, "Web Site:")
|
|
|
|
|
g.pdf.SetXY(valueX, startY+lineHeight*3)
|
|
|
|
|
g.pdf.Cell(55, lineHeight, "www.cmctechnologies.net.au")
|
|
|
|
|
|
|
|
|
|
// Engineering text on left
|
2025-07-02 05:04:36 -07:00
|
|
|
g.pdf.SetTextColor(0, 0, 152)
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 10)
|
2026-01-13 04:26:37 -08:00
|
|
|
engineeringX := 10.0
|
|
|
|
|
engineeringY := 37.0
|
|
|
|
|
g.pdf.SetXY(engineeringX, engineeringY)
|
|
|
|
|
g.pdf.Cell(30, lineHeight, "Engineering &")
|
|
|
|
|
g.pdf.SetXY(engineeringX, engineeringY+lineHeight)
|
|
|
|
|
g.pdf.Cell(30, lineHeight, "Industrial")
|
|
|
|
|
g.pdf.SetXY(engineeringX, engineeringY+lineHeight*2)
|
|
|
|
|
g.pdf.Cell(30, lineHeight, "Instrumentation")
|
|
|
|
|
|
|
|
|
|
// Reset text color to black for subsequent content
|
|
|
|
|
g.pdf.SetTextColor(0, 0, 0)
|
2025-07-02 05:04:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Page1Footer adds the standard footer
|
|
|
|
|
func (g *Generator) Page1Footer() {
|
|
|
|
|
g.pdf.SetY(-20)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// Footer line
|
|
|
|
|
g.pdf.SetDrawColor(0, 0, 0)
|
|
|
|
|
g.pdf.Line(10, g.pdf.GetY(), 200, g.pdf.GetY())
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// Color-coded services
|
|
|
|
|
g.pdf.SetY(-13)
|
|
|
|
|
g.pdf.SetX(10)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// First line of services
|
|
|
|
|
services := []struct {
|
2025-07-06 14:34:12 -07:00
|
|
|
text string
|
2025-07-02 05:04:36 -07:00
|
|
|
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},
|
|
|
|
|
}
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
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
|
|
|
|
|
}
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// Create details table
|
|
|
|
|
lineHeight := 5.0
|
|
|
|
|
col1Width := 40.0
|
|
|
|
|
col2Width := 60.0
|
|
|
|
|
col3Width := 40.0
|
|
|
|
|
col4Width := 50.0
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// Column widths
|
|
|
|
|
itemWidth := 15.0
|
|
|
|
|
qtyWidth := 15.0
|
|
|
|
|
descWidth := 100.0
|
|
|
|
|
unitWidth := 30.0
|
|
|
|
|
totalWidth := 30.0
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// Table rows
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
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)
|
|
|
|
|
}
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
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, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
subtotal += item.TotalPrice
|
|
|
|
|
}
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// Totals
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 10)
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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, "")
|
2025-07-06 14:34:12 -07:00
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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, "")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-12 03:00:48 -08:00
|
|
|
// AddTermsAndConditions adds a T&C page at the end of the PDF
|
|
|
|
|
// This renders a simple text page indicating T&Cs apply
|
|
|
|
|
func (g *Generator) AddTermsAndConditions() {
|
|
|
|
|
g.AddPage()
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 14)
|
2026-01-13 04:26:37 -08:00
|
|
|
g.pdf.SetTextColor(171, 49, 248) // Purple/magenta color matching old format
|
2026-01-12 03:00:48 -08:00
|
|
|
g.pdf.SetY(20)
|
|
|
|
|
g.pdf.CellFormat(0, 10, "TERMS AND CONDITIONS", "", 1, "C", false, 0, "")
|
|
|
|
|
g.pdf.Ln(5)
|
|
|
|
|
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
2026-01-13 04:26:37 -08:00
|
|
|
g.pdf.SetTextColor(171, 49, 248) // Keep purple for body text
|
2026-01-12 03:00:48 -08:00
|
|
|
g.pdf.SetLeftMargin(15)
|
|
|
|
|
g.pdf.SetRightMargin(15)
|
|
|
|
|
|
|
|
|
|
// Add disclaimer text
|
|
|
|
|
disclaimerText := `These Terms and Conditions apply to all quotations, invoices, purchase orders, and other documents issued by CMC TECHNOLOGIES.
|
|
|
|
|
By accepting this document, you agree to be bound by these terms.
|
|
|
|
|
|
|
|
|
|
Payment Terms: Invoices are due within 30 days of issue unless otherwise agreed in writing.
|
|
|
|
|
|
|
|
|
|
Delivery: Delivery dates are estimates only and not guaranteed. CMC TECHNOLOGIES is not liable for delays in delivery.
|
|
|
|
|
|
|
|
|
|
Intellectual Property: All designs, drawings, and specifications remain the property of CMC TECHNOLOGIES unless otherwise agreed.
|
|
|
|
|
|
|
|
|
|
Limitation of Liability: CMC TECHNOLOGIES shall not be liable for any indirect, incidental, or consequential damages arising out of or related to this document or transaction.
|
|
|
|
|
|
|
|
|
|
Entire Agreement: This document and any attached terms constitute the entire agreement between the parties.
|
|
|
|
|
|
|
|
|
|
For full terms and conditions, please refer to our website or contact CMC TECHNOLOGIES directly.`
|
|
|
|
|
|
|
|
|
|
g.pdf.MultiCell(0, 4, disclaimerText, "", "L", false)
|
2026-01-13 04:26:37 -08:00
|
|
|
g.pdf.SetTextColor(0, 0, 0) // Reset to black
|
2026-01-12 03:00:48 -08:00
|
|
|
}
|
|
|
|
|
|
2026-01-12 05:21:57 -08:00
|
|
|
// AddQuoteDetailsTable adds quote commercial details table (delivery time, payment terms, etc.)
|
|
|
|
|
func (g *Generator) AddQuoteDetailsTable(data *QuotePDFData) {
|
|
|
|
|
g.pdf.Ln(8)
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 10)
|
|
|
|
|
g.pdf.CellFormat(0, 5, "QUOTE DETAILS", "", 1, "L", false, 0, "")
|
|
|
|
|
g.pdf.Ln(2)
|
|
|
|
|
|
|
|
|
|
// Create a simple table for quote details
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
|
|
|
|
g.pdf.SetLeftMargin(15)
|
|
|
|
|
|
|
|
|
|
// Set column widths for a 3-column table
|
|
|
|
|
colWidth := 50.0
|
|
|
|
|
|
|
|
|
|
// Row 1: Delivery Time | Payment Terms | Days Valid
|
|
|
|
|
g.pdf.SetX(15)
|
|
|
|
|
g.pdf.SetLeftMargin(15)
|
|
|
|
|
|
|
|
|
|
if data.DeliveryTime != "" {
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 8)
|
|
|
|
|
g.pdf.CellFormat(colWidth, 4, "Delivery Time:", "L", 0, "L", false, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 8)
|
|
|
|
|
g.pdf.CellFormat(colWidth, 4, data.DeliveryTime, "", 0, "L", false, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 8)
|
|
|
|
|
g.pdf.CellFormat(colWidth, 4, "Payment Terms:", "", 0, "L", false, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 8)
|
|
|
|
|
g.pdf.CellFormat(0, 4, data.PaymentTerms, "R", 1, "L", false, 0, "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Row 2: Delivery Point | Days Valid
|
|
|
|
|
if data.DeliveryPoint != "" || data.DaysValid > 0 {
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 8)
|
|
|
|
|
g.pdf.CellFormat(colWidth, 4, "Delivery Point:", "L", 0, "L", false, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 8)
|
|
|
|
|
g.pdf.CellFormat(colWidth, 4, data.DeliveryPoint, "", 0, "L", false, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 8)
|
|
|
|
|
daysStr := ""
|
|
|
|
|
if data.DaysValid > 0 {
|
|
|
|
|
daysStr = fmt.Sprintf("%d days", data.DaysValid)
|
|
|
|
|
}
|
|
|
|
|
g.pdf.CellFormat(colWidth, 4, "Valid For:", "", 0, "L", false, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 8)
|
|
|
|
|
g.pdf.CellFormat(0, 4, daysStr, "R", 1, "L", false, 0, "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g.pdf.SetLeftMargin(10)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-12 05:04:42 -08:00
|
|
|
// AddInvoiceAddressBoxes adds the Sold To / Delivery Address boxes for invoices
|
|
|
|
|
func (g *Generator) AddInvoiceAddressBoxes(data *InvoicePDFData) {
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
|
|
|
|
|
|
|
|
|
// Top row with headers and invoice details
|
|
|
|
|
x := g.pdf.GetX()
|
|
|
|
|
y := g.pdf.GetY()
|
|
|
|
|
|
|
|
|
|
// Left: Sold To header
|
|
|
|
|
g.pdf.SetXY(x, y)
|
|
|
|
|
g.pdf.SetFillColor(242, 242, 242)
|
|
|
|
|
g.pdf.CellFormat(58, 5, "Sold To / Invoice Address:", "1", 0, "L", true, 0, "")
|
|
|
|
|
|
|
|
|
|
// Middle: Delivery Address header
|
|
|
|
|
g.pdf.CellFormat(58, 5, "Delivery Address:", "1", 0, "L", true, 0, "")
|
|
|
|
|
|
|
|
|
|
// Right: Invoice details (starts here, spans 2 rows)
|
|
|
|
|
g.pdf.SetXY(x+122, y)
|
|
|
|
|
g.pdf.SetFont("Helvetica", "U", 11)
|
|
|
|
|
g.pdf.CellFormat(0, 5, fmt.Sprintf("CMC INVOICE#: %s", data.Invoice.Title), "", 1, "L", false, 0, "")
|
|
|
|
|
|
|
|
|
|
// Second row with address content
|
|
|
|
|
y += 5
|
|
|
|
|
g.pdf.SetXY(x, y)
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
|
|
|
|
|
|
|
|
|
// Sold To content
|
|
|
|
|
billToHeight := 30.0
|
|
|
|
|
g.pdf.MultiCell(58, 4, data.BillTo, "1", "L", false)
|
|
|
|
|
soldToEndY := g.pdf.GetY()
|
|
|
|
|
|
|
|
|
|
// Ship To content
|
|
|
|
|
g.pdf.SetXY(x+58, y)
|
|
|
|
|
g.pdf.MultiCell(58, 4, data.ShipTo, "1", "L", false)
|
|
|
|
|
shipToEndY := g.pdf.GetY()
|
|
|
|
|
|
|
|
|
|
// Ensure both cells have same height
|
|
|
|
|
maxEndY := soldToEndY
|
|
|
|
|
if shipToEndY > maxEndY {
|
|
|
|
|
maxEndY = shipToEndY
|
|
|
|
|
}
|
|
|
|
|
if maxEndY < y+billToHeight {
|
|
|
|
|
maxEndY = y + billToHeight
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add remaining invoice details on the right
|
|
|
|
|
g.pdf.SetXY(x+122, y)
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
2026-01-13 04:26:37 -08:00
|
|
|
g.pdf.Cell(0, 4, fmt.Sprintf("Date: %s", data.IssueDateString))
|
|
|
|
|
g.pdf.SetXY(x+122, y+4)
|
|
|
|
|
g.pdf.Cell(0, 4, "Page: 1 of {nb}")
|
2026-01-12 05:04:42 -08:00
|
|
|
|
|
|
|
|
g.pdf.Ln(2)
|
|
|
|
|
g.pdf.SetFont("Helvetica", "U", 9)
|
|
|
|
|
g.pdf.CellFormat(0, 4, "MAKE PAYMENT TO:", "", 1, "L", false, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 8)
|
|
|
|
|
|
2026-01-13 04:26:37 -08:00
|
|
|
// Bank details based on currency in table format
|
|
|
|
|
bankY := g.pdf.GetY()
|
|
|
|
|
bankLineHeight := 3.0
|
|
|
|
|
switch data.CurrencyCode {
|
2026-01-12 05:04:42 -08:00
|
|
|
case "EUR":
|
2026-01-13 04:26:37 -08:00
|
|
|
g.pdf.SetXY(10, bankY)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Account Name: CMC Technologies Pty Ltd")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Account Number/IBAN: 06200015682004")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight*2)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Branch code: 06200")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight*3)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "SWIFT Code/BIC: CTBAAU2S")
|
|
|
|
|
g.pdf.SetY(bankY + bankLineHeight*4)
|
2026-01-12 05:04:42 -08:00
|
|
|
case "GBP":
|
2026-01-13 04:26:37 -08:00
|
|
|
g.pdf.SetXY(10, bankY)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Account Name: CMC Technologies Pty Ltd")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Account Number/IBAN: 06200015642694")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight*2)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Branch code: 06200")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight*3)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "SWIFT Code/BIC: CTBAAU2S")
|
|
|
|
|
g.pdf.SetY(bankY + bankLineHeight*4)
|
2026-01-12 05:04:42 -08:00
|
|
|
case "USD":
|
2026-01-13 04:26:37 -08:00
|
|
|
g.pdf.SetXY(10, bankY)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Account Name: CMC Technologies Pty Ltd")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Account Number/IBAN: 06200015681984")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight*2)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Branch code: 06200")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight*3)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "SWIFT Code/BIC: CTBAAU2S")
|
|
|
|
|
g.pdf.SetY(bankY + bankLineHeight*4)
|
2026-01-12 05:04:42 -08:00
|
|
|
default: // AUD and others
|
2026-01-13 04:26:37 -08:00
|
|
|
g.pdf.SetXY(10, bankY)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Account Name: CMC Technologies Pty Ltd")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Bank Number BSB#: 062-458")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight*2)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "Account Number: 10067982")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight*3)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "SWIFT Code: CTBAAU2S")
|
|
|
|
|
g.pdf.SetXY(10, bankY+bankLineHeight*4)
|
|
|
|
|
g.pdf.Cell(0, bankLineHeight, "IBAN: 06245810067982")
|
|
|
|
|
g.pdf.SetY(bankY + bankLineHeight*5)
|
2026-01-12 05:04:42 -08:00
|
|
|
}
|
2026-01-13 04:26:37 -08:00
|
|
|
|
|
|
|
|
g.pdf.SetY(maxEndY + 2)
|
2026-01-12 05:04:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddInvoiceDetailsTable adds the order number, job, FOB, payment terms, ABN table
|
|
|
|
|
func (g *Generator) AddInvoiceDetailsTable(data *InvoicePDFData) {
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 9)
|
|
|
|
|
g.pdf.SetFillColor(242, 242, 242)
|
|
|
|
|
|
|
|
|
|
// Header row
|
|
|
|
|
colWidth := 38.0
|
|
|
|
|
g.pdf.CellFormat(colWidth, 5, "CUSTOMER ORDER NO", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(colWidth, 5, "CMC JOB #", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(colWidth, 5, "INCOTERMS 2010", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(colWidth, 5, "PAYMENT TERMS", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(colWidth, 5, "CUSTOMER ABN", "1", 1, "C", true, 0, "")
|
|
|
|
|
|
|
|
|
|
// Data row
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
|
|
|
|
g.pdf.CellFormat(colWidth, 5, data.CustomerOrderNumber, "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(colWidth, 5, data.JobTitle, "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(colWidth, 5, data.FOB, "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(colWidth, 5, data.PaymentTerms, "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(colWidth, 5, data.CustomerABN, "1", 1, "C", false, 0, "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddInvoiceLineItemsHeader adds just the header row for line items table
|
|
|
|
|
func (g *Generator) AddInvoiceLineItemsHeader(currencyCode string) {
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 9)
|
|
|
|
|
g.pdf.SetFillColor(242, 242, 242)
|
|
|
|
|
|
|
|
|
|
g.pdf.CellFormat(15, 5, "ITEM NO.", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(15, 5, "QTY", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(100, 5, "DESCRIPTION", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(30, 5, "UNIT PRICE", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(30, 5, "TOTAL PRICE", "1", 1, "C", true, 0, "")
|
|
|
|
|
|
|
|
|
|
// Currency row
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 8)
|
|
|
|
|
g.pdf.CellFormat(15, 4, "", "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(15, 4, "", "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(100, 4, "", "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(30, 4, currencyCode, "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(30, 4, currencyCode, "1", 1, "C", false, 0, "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddInvoiceLineItemsContent adds line items and totals for invoices
|
|
|
|
|
func (g *Generator) AddInvoiceLineItemsContent(data *InvoicePDFData) {
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
2026-01-13 04:26:37 -08:00
|
|
|
g.pdf.SetTextColor(0, 0, 0)
|
|
|
|
|
|
|
|
|
|
pageNum := 1
|
|
|
|
|
firstPageBreak := false
|
2026-01-12 05:04:42 -08:00
|
|
|
|
|
|
|
|
// Line items
|
2026-01-13 04:26:37 -08:00
|
|
|
for i, item := range data.LineItems {
|
|
|
|
|
// Check if we need a new page (leave room for footer/totals)
|
|
|
|
|
if g.pdf.GetY() > 240 && i > 0 {
|
|
|
|
|
g.AddPage()
|
|
|
|
|
if !firstPageBreak {
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 12)
|
|
|
|
|
g.pdf.CellFormat(0, 6, "CONTINUED: "+data.Invoice.Title, "", 1, "L", false, 0, "")
|
|
|
|
|
g.pdf.Ln(4)
|
|
|
|
|
firstPageBreak = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Re-draw header on new page
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 9)
|
|
|
|
|
g.pdf.SetFillColor(242, 242, 242)
|
|
|
|
|
g.pdf.CellFormat(15, 5, "ITEM NO.", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(15, 5, "QTY", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(100, 5, "DESCRIPTION", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(30, 5, "UNIT PRICE", "1", 0, "C", true, 0, "")
|
|
|
|
|
g.pdf.CellFormat(30, 5, "TOTAL PRICE", "1", 1, "C", true, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-12 05:04:42 -08:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g.pdf.CellFormat(15, 5, item.ItemNumber, "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(15, 5, item.Quantity, "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(100, 5, item.Title, "1", 0, "L", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(30, 5, fmt.Sprintf("%s%.2f", data.CurrencySymbol, unitPrice), "1", 0, "C", false, 0, "")
|
|
|
|
|
g.pdf.CellFormat(30, 5, fmt.Sprintf("%s%.2f", data.CurrencySymbol, totalPrice), "1", 1, "C", false, 0, "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Freight details and totals
|
|
|
|
|
g.pdf.SetFillColor(242, 242, 242)
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 9)
|
|
|
|
|
|
|
|
|
|
// Freight details (left side, spans 2 rows)
|
|
|
|
|
y := g.pdf.GetY()
|
|
|
|
|
g.pdf.CellFormat(130, 5, "FREIGHT DETAILS:", "1", 0, "L", true, 0, "")
|
|
|
|
|
|
|
|
|
|
// Subtotal
|
|
|
|
|
g.pdf.CellFormat(30, 5, "SUBTOTAL", "1", 0, "L", true, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
|
|
|
|
subtotalStr := fmt.Sprintf("%v", data.Subtotal)
|
|
|
|
|
if val, ok := data.Subtotal.(float64); ok {
|
|
|
|
|
subtotalStr = fmt.Sprintf("%s%.2f", data.CurrencySymbol, val)
|
|
|
|
|
}
|
|
|
|
|
g.pdf.CellFormat(30, 5, subtotalStr, "1", 1, "C", false, 0, "")
|
|
|
|
|
|
|
|
|
|
// Shipping details text (left side)
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 8)
|
|
|
|
|
g.pdf.SetXY(10, y+5)
|
|
|
|
|
g.pdf.MultiCell(130, 4, data.ShippingDetails, "1", "L", false)
|
|
|
|
|
shippingEndY := g.pdf.GetY()
|
|
|
|
|
|
|
|
|
|
// GST row
|
|
|
|
|
g.pdf.SetXY(140, y+5)
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 9)
|
|
|
|
|
g.pdf.SetFillColor(242, 242, 242)
|
|
|
|
|
g.pdf.CellFormat(30, 5, "GST (10%)", "1", 0, "L", true, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
|
|
|
|
gstStr := fmt.Sprintf("%v", data.GSTAmount)
|
|
|
|
|
if val, ok := data.GSTAmount.(float64); ok {
|
|
|
|
|
gstStr = fmt.Sprintf("%s%.2f", data.CurrencySymbol, val)
|
|
|
|
|
}
|
|
|
|
|
g.pdf.CellFormat(30, 5, gstStr, "1", 1, "C", false, 0, "")
|
|
|
|
|
|
|
|
|
|
// Total row
|
|
|
|
|
g.pdf.SetFont("Helvetica", "B", 9)
|
|
|
|
|
g.pdf.CellFormat(130, 5, "", "", 0, "L", false, 0, "")
|
|
|
|
|
g.pdf.SetFillColor(242, 242, 242)
|
|
|
|
|
g.pdf.CellFormat(30, 5, "TOTAL DUE", "1", 0, "L", true, 0, "")
|
|
|
|
|
g.pdf.SetFont("Helvetica", "", 9)
|
|
|
|
|
totalStr := fmt.Sprintf("%v", data.Total)
|
|
|
|
|
if val, ok := data.Total.(float64); ok {
|
|
|
|
|
totalStr = fmt.Sprintf("%s%.2f", data.CurrencySymbol, val)
|
|
|
|
|
}
|
|
|
|
|
g.pdf.CellFormat(30, 5, totalStr, "1", 1, "C", false, 0, "")
|
|
|
|
|
|
|
|
|
|
// Make sure we're past the shipping details
|
|
|
|
|
if g.pdf.GetY() < shippingEndY {
|
|
|
|
|
g.pdf.SetY(shippingEndY)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-02 05:04:36 -07:00
|
|
|
// 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
|
2025-07-06 14:34:12 -07:00
|
|
|
}
|