This commit is contained in:
Luciano Milani
2026-07-02 12:47:55 +01:00
commit 5de37bb512
132 changed files with 28495 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
package pdf
import (
"fmt"
"os"
"path/filepath"
"github.com/go-pdf/fpdf"
)
type DocMeta struct {
CompanyName string
CompanyNIF string
CompanyAddress string
CompanyIBAN string
CompanyPhone string
CompanyEmail string
DocType string // "Fatura" or "Orçamento"
DocNumber string // e.g. "FAT/2026/001"
IssuedAt string // e.g. "30/06/2026"
ClientName string
ClientNIF string
VehiclePlate string
}
type LineItem struct {
Description string
Qty float64
UnitPrice float64
DiscountPct float64
Total float64
}
func Generate(meta DocMeta, items []LineItem, staffTotal float64, outPath string) error {
if err := os.MkdirAll(filepath.Dir(outPath), 0755); err != nil {
return fmt.Errorf("pdf: mkdir: %w", err)
}
f := fpdf.New("P", "mm", "A4", "")
f.AddPage()
f.SetMargins(15, 15, 15)
// Header
f.SetFont("Helvetica", "B", 18)
f.CellFormat(120, 10, meta.CompanyName, "", 0, "L", false, 0, "")
f.SetFont("Helvetica", "B", 14)
f.CellFormat(60, 10, meta.DocType, "", 1, "R", false, 0, "")
f.SetFont("Helvetica", "", 9)
if meta.CompanyNIF != "" {
f.CellFormat(120, 5, "NIF: "+meta.CompanyNIF, "", 0, "L", false, 0, "")
} else {
f.CellFormat(120, 5, "", "", 0, "L", false, 0, "")
}
f.SetFont("Helvetica", "", 11)
f.CellFormat(60, 5, meta.DocNumber, "", 1, "R", false, 0, "")
f.SetFont("Helvetica", "", 9)
if meta.CompanyAddress != "" {
f.MultiCell(120, 5, meta.CompanyAddress, "", "L", false)
}
f.Ln(3)
curY := f.GetY()
f.SetXY(135, curY-3)
f.CellFormat(60, 5, "Data: "+meta.IssuedAt, "", 1, "R", false, 0, "")
f.SetY(curY + 3)
f.Ln(3)
// Client block
if meta.ClientName != "" {
f.SetFont("Helvetica", "B", 9)
f.CellFormat(180, 5, "Cliente", "", 1, "L", false, 0, "")
f.SetFont("Helvetica", "", 9)
f.CellFormat(180, 5, meta.ClientName, "", 1, "L", false, 0, "")
if meta.ClientNIF != "" {
f.CellFormat(180, 5, "NIF: "+meta.ClientNIF, "", 1, "L", false, 0, "")
}
if meta.VehiclePlate != "" {
f.CellFormat(180, 5, "Matrícula: "+meta.VehiclePlate, "", 1, "L", false, 0, "")
}
f.Ln(4)
}
// Table header
f.SetFillColor(50, 50, 50)
f.SetTextColor(255, 255, 255)
f.SetFont("Helvetica", "B", 9)
f.CellFormat(90, 7, "Descricao", "1", 0, "L", true, 0, "")
f.CellFormat(20, 7, "Qtd.", "1", 0, "C", true, 0, "")
f.CellFormat(25, 7, "Preco Unit.", "1", 0, "R", true, 0, "")
f.CellFormat(20, 7, "Desc.%", "1", 0, "C", true, 0, "")
f.CellFormat(25, 7, "Total", "1", 1, "R", true, 0, "")
// Table rows
f.SetFillColor(245, 245, 245)
f.SetTextColor(0, 0, 0)
f.SetFont("Helvetica", "", 9)
fill := false
for _, item := range items {
f.CellFormat(90, 6, item.Description, "1", 0, "L", fill, 0, "")
f.CellFormat(20, 6, fmt.Sprintf("%.2f", item.Qty), "1", 0, "C", fill, 0, "")
f.CellFormat(25, 6, fmt.Sprintf("%.2f EUR", item.UnitPrice), "1", 0, "R", fill, 0, "")
f.CellFormat(20, 6, fmt.Sprintf("%.0f%%", item.DiscountPct), "1", 0, "C", fill, 0, "")
f.CellFormat(25, 6, fmt.Sprintf("%.2f EUR", item.Total), "1", 1, "R", fill, 0, "")
fill = !fill
}
// Totals
var subtotal float64
for _, item := range items {
subtotal += item.Total
}
grandTotal := subtotal + staffTotal
f.Ln(3)
f.SetFont("Helvetica", "", 9)
if staffTotal > 0 {
f.CellFormat(155, 6, "Subtotal pecas/servicos", "", 0, "R", false, 0, "")
f.CellFormat(25, 6, fmt.Sprintf("%.2f EUR", subtotal), "1", 1, "R", false, 0, "")
f.CellFormat(155, 6, "Mao de obra", "", 0, "R", false, 0, "")
f.CellFormat(25, 6, fmt.Sprintf("%.2f EUR", staffTotal), "1", 1, "R", false, 0, "")
}
f.SetFont("Helvetica", "B", 10)
f.CellFormat(155, 7, "TOTAL", "", 0, "R", false, 0, "")
f.CellFormat(25, 7, fmt.Sprintf("%.2f EUR", grandTotal), "1", 1, "R", false, 0, "")
// IBAN footer
if meta.CompanyIBAN != "" {
f.Ln(8)
f.SetFont("Helvetica", "", 8)
f.CellFormat(180, 5, "IBAN: "+meta.CompanyIBAN, "", 1, "C", false, 0, "")
}
return f.OutputFileAndClose(outPath)
}