cmc-sales/go/internal/cmc/documents/order_ack_builder.go

205 lines
5.9 KiB
Go
Raw Normal View History

package documents
import (
"bytes"
"fmt"
"html/template"
"path/filepath"
"strconv"
)
// OrderAckLineItemTemplateData represents an order ack line item for template rendering
type OrderAckLineItemTemplateData struct {
Title string
Description template.HTML
Quantity string
UnitPrice float64
UnitPriceText string
Discount float64
TotalPrice float64
TotalPriceText string
HasTextPrices bool
}
// BuildOrderAckHTML generates the complete HTML for an order acknowledgement using templates
func (g *HTMLDocumentGenerator) BuildOrderAckHTML(data *OrderAckPDFData, totalPages int, currentPage int) string {
orderAckNumber := data.OrderAcknowledgement.CmcReference
// Calculate totals
subtotal := 0.0
lineItemsData := []OrderAckLineItemTemplateData{}
for _, item := range data.LineItems {
unitPrice := 0.0
totalPrice := 0.0
discount := 0.0
unitPriceText := ""
totalPriceText := ""
// Check for text price strings first
hasTextPrices := false
if item.UnitPriceString.Valid && item.UnitPriceString.String != "" {
hasTextPrices = true
unitPriceText = item.UnitPriceString.String
} else if item.GrossUnitPrice.Valid {
unitPrice, _ = strconv.ParseFloat(item.GrossUnitPrice.String, 64)
}
if item.GrossPriceString.Valid && item.GrossPriceString.String != "" {
hasTextPrices = true
totalPriceText = item.GrossPriceString.String
// Don't add text prices to subtotal
} else if item.GrossPrice.Valid {
totalPrice, _ = strconv.ParseFloat(item.GrossPrice.String, 64)
subtotal += totalPrice
}
if item.DiscountAmountTotal.Valid {
discount, _ = strconv.ParseFloat(item.DiscountAmountTotal.String, 64)
}
lineItemsData = append(lineItemsData, OrderAckLineItemTemplateData{
Title: item.Title,
Description: template.HTML(item.Description),
Quantity: item.Quantity,
UnitPrice: unitPrice,
UnitPriceText: unitPriceText,
Discount: discount,
TotalPrice: totalPrice,
TotalPriceText: totalPriceText,
HasTextPrices: hasTextPrices || item.HasTextPrices,
})
}
gstAmount := 0.0
total := subtotal
if data.ShowGST {
gstAmount = subtotal * 0.1
total = subtotal + gstAmount
}
// Prepare template data
templateData := struct {
OrderAckNumber string
CompanyName string
EmailTo string
Attention string
IssueDateString string
YourReference string
JobTitle string
BillTo template.HTML
ShipTo template.HTML
ShipVia string
FOB string
PaymentTerms string
CustomerABN string
CurrencyCode string
CurrencySymbol string
LineItems []OrderAckLineItemTemplateData
Subtotal float64
GSTAmount float64
Total float64
ShowGST bool
PageCount int
CurrentPage int
FreightDetails template.HTML
LogoDataURI string
}{
OrderAckNumber: orderAckNumber,
CompanyName: data.Customer.Name,
EmailTo: data.EmailTo,
Attention: data.Attention,
IssueDateString: data.IssueDateString,
YourReference: data.YourReference,
JobTitle: data.JobTitle,
BillTo: template.HTML(data.BillTo),
ShipTo: template.HTML(data.ShipTo),
ShipVia: data.ShipVia,
FOB: data.FOB,
PaymentTerms: data.PaymentTerms,
CustomerABN: "",
CurrencyCode: data.CurrencyCode,
CurrencySymbol: data.CurrencySymbol,
LineItems: lineItemsData,
Subtotal: subtotal,
GSTAmount: gstAmount,
Total: total,
ShowGST: data.ShowGST,
PageCount: totalPages,
CurrentPage: currentPage,
FreightDetails: template.HTML(data.FreightDetails),
LogoDataURI: g.loadLogo("quote_logo.png"),
}
// Define template functions
funcMap := template.FuncMap{
"formatPrice": func(price float64, textPrice string) template.HTML {
// If a text price string is provided, use it as-is
if textPrice != "" {
return template.HTML(textPrice)
}
// Otherwise format the numeric price
formatted := FormatPriceWithCommas(fmt.Sprintf("%.2f", price))
return template.HTML(fmt.Sprintf("%s%s", data.CurrencySymbol, formatted))
},
"formatDiscount": func(discount float64, hasTextPrices bool) template.HTML {
if hasTextPrices {
return template.HTML("-")
}
// Show 0.00 when no discount, otherwise prefix with minus
if discount <= 0 {
return template.HTML(fmt.Sprintf("%s0.00", data.CurrencySymbol))
}
formatted := FormatPriceWithCommas(fmt.Sprintf("%.2f", discount))
return template.HTML(fmt.Sprintf("-%s%s", data.CurrencySymbol, formatted))
},
"formatTotal": func(amount float64) template.HTML {
formatted := FormatPriceWithCommas(fmt.Sprintf("%.2f", amount))
return template.HTML(fmt.Sprintf("%s%s", data.CurrencySymbol, formatted))
},
}
// Parse and execute template
possiblePathSets := [][]string{
{
filepath.Join("internal", "cmc", "documents", "templates", "order-acknowledgement.html"),
filepath.Join("internal", "cmc", "documents", "templates", "header.html"),
},
{
filepath.Join("go", "internal", "cmc", "documents", "templates", "order-acknowledgement.html"),
filepath.Join("go", "internal", "cmc", "documents", "templates", "header.html"),
},
{
filepath.Join("templates", "pdf", "order-acknowledgement.html"),
filepath.Join("templates", "pdf", "header.html"),
},
{
"/app/templates/pdf/order-acknowledgement.html",
"/app/templates/pdf/header.html",
},
}
var tmpl *template.Template
var err error
for _, pathSet := range possiblePathSets {
tmpl, err = template.New("order-acknowledgement.html").Funcs(funcMap).ParseFiles(pathSet...)
if err == nil {
break
}
}
if tmpl == nil || err != nil {
fmt.Printf("Error parsing template from any path: %v\n", err)
return ""
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, templateData); err != nil {
fmt.Printf("Error executing template: %v\n", err)
return ""
}
return buf.String()
}