Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "fmt" |
| 6 | "html/template" |
Serge Bazanski | 772a133 | 2021-02-17 20:28:51 +0100 | [diff] [blame] | 7 | "strings" |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 8 | "time" |
| 9 | |
Serge Bazanski | 97b5cd7 | 2023-07-28 17:14:50 +0000 | [diff] [blame] | 10 | wkhtml "github.com/SebastiaanKlippert/go-wkhtmltopdf" |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 11 | |
Sergiusz Bazanski | 1446e98 | 2019-07-21 16:04:23 +0200 | [diff] [blame] | 12 | pb "code.hackerspace.pl/hscloud/bgpwtf/invoice/proto" |
Sergiusz Bazanski | cea71e3 | 2019-07-21 15:30:08 +0200 | [diff] [blame] | 13 | "code.hackerspace.pl/hscloud/bgpwtf/invoice/templates" |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 14 | ) |
| 15 | |
| 16 | var ( |
Sergiusz Bazanski | a818ef2 | 2019-06-07 10:37:22 +0200 | [diff] [blame] | 17 | invTmpl map[string]*template.Template |
| 18 | |
| 19 | languages = []string{"en", "pl"} |
| 20 | defaultLanguage = "en" |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 21 | ) |
| 22 | |
| 23 | func init() { |
Sergiusz Bazanski | a818ef2 | 2019-06-07 10:37:22 +0200 | [diff] [blame] | 24 | invTmpl = make(map[string]*template.Template) |
| 25 | for _, language := range languages { |
| 26 | filename := fmt.Sprintf("invoice_%s.html", language) |
| 27 | a, err := templates.Asset(filename) |
| 28 | if err != nil { |
| 29 | panic(err) |
| 30 | } |
| 31 | invTmpl[language] = template.Must(template.New(filename).Parse(string(a))) |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 32 | } |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 33 | } |
| 34 | |
Sergiusz Bazanski | a818ef2 | 2019-06-07 10:37:22 +0200 | [diff] [blame] | 35 | func renderInvoicePDF(i *pb.Invoice, language string) ([]byte, error) { |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 36 | type item struct { |
| 37 | Title string |
| 38 | UnitPrice string |
| 39 | Qty string |
| 40 | VATRate string |
| 41 | TotalNet string |
| 42 | Total string |
| 43 | } |
| 44 | |
Serge Bazanski | 772a133 | 2021-02-17 20:28:51 +0100 | [diff] [blame] | 45 | symbols := "" |
| 46 | var parts []string |
| 47 | for _, code := range i.Data.SpCode { |
| 48 | parts = append(parts, code.String()) |
| 49 | } |
| 50 | for _, code := range i.GtuCode { |
| 51 | parts = append(parts, code.String()) |
| 52 | } |
| 53 | if len(parts) > 0 { |
| 54 | symbols = strings.Join(parts, ", ") + "." |
| 55 | } else { |
Serge Bazanski | 87ede25 | 2021-05-12 21:05:53 +0000 | [diff] [blame] | 56 | if language == "en" { |
| 57 | symbols = "<i>brak (none)</i>." |
| 58 | } else { |
| 59 | symbols = "<i>brak</i>." |
| 60 | } |
Serge Bazanski | 772a133 | 2021-02-17 20:28:51 +0100 | [diff] [blame] | 61 | } |
| 62 | |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 63 | data := struct { |
| 64 | InvoiceNumber string |
| 65 | InvoicerBilling []string |
| 66 | InvoicerVAT string |
| 67 | InvoicerCompanyNumber string |
| 68 | InvoiceeBilling []string |
| 69 | InvoiceeVAT string |
| 70 | Date time.Time |
| 71 | DueDate time.Time |
| 72 | IBAN string |
| 73 | SWIFT string |
| 74 | Proforma bool |
| 75 | ReverseVAT bool |
| 76 | USCustomer bool |
| 77 | Items []item |
| 78 | TotalNet string |
| 79 | VATTotal string |
| 80 | Total string |
| 81 | DeliveryCharge string |
Serge Bazanski | 772a133 | 2021-02-17 20:28:51 +0100 | [diff] [blame] | 82 | Symbols template.HTML |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 83 | }{ |
Sergiusz Bazanski | 3976e3c | 2019-05-01 15:27:49 +0200 | [diff] [blame] | 84 | InvoiceNumber: i.FinalUid, |
| 85 | Date: time.Unix(0, i.Date), |
| 86 | DueDate: time.Unix(0, i.DueDate), |
| 87 | IBAN: i.Data.Iban, |
| 88 | SWIFT: i.Data.Swift, |
| 89 | InvoicerVAT: i.Data.InvoicerVatId, |
| 90 | InvoicerCompanyNumber: i.Data.InvoicerCompanyNumber, |
| 91 | InvoiceeVAT: i.Data.CustomerVatId, |
| 92 | Proforma: i.State == pb.Invoice_STATE_PROFORMA, |
| 93 | ReverseVAT: i.Data.ReverseVat, |
| 94 | USCustomer: i.Data.UsCustomer, |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 95 | |
Sergiusz Bazanski | 3976e3c | 2019-05-01 15:27:49 +0200 | [diff] [blame] | 96 | InvoicerBilling: make([]string, len(i.Data.InvoicerBilling)), |
| 97 | InvoiceeBilling: make([]string, len(i.Data.CustomerBilling)), |
Serge Bazanski | 772a133 | 2021-02-17 20:28:51 +0100 | [diff] [blame] | 98 | Symbols: template.HTML(symbols), |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 99 | } |
| 100 | |
Sergiusz Bazanski | 3976e3c | 2019-05-01 15:27:49 +0200 | [diff] [blame] | 101 | for d, s := range i.Data.InvoicerBilling { |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 102 | data.InvoicerBilling[d] = s |
| 103 | } |
Sergiusz Bazanski | 3976e3c | 2019-05-01 15:27:49 +0200 | [diff] [blame] | 104 | for d, s := range i.Data.CustomerBilling { |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 105 | data.InvoiceeBilling[d] = s |
| 106 | } |
| 107 | |
Sergiusz Bazanski | 3976e3c | 2019-05-01 15:27:49 +0200 | [diff] [blame] | 108 | unit := i.Unit |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 109 | |
Sergiusz Bazanski | 3976e3c | 2019-05-01 15:27:49 +0200 | [diff] [blame] | 110 | for _, it := range i.Data.Item { |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 111 | data.Items = append(data.Items, item{ |
Sergiusz Bazanski | 3976e3c | 2019-05-01 15:27:49 +0200 | [diff] [blame] | 112 | Title: it.Title, |
| 113 | Qty: fmt.Sprintf("%d", it.Count), |
| 114 | UnitPrice: fmt.Sprintf(unit+"%.2f", float64(it.UnitPrice)/100), |
| 115 | VATRate: fmt.Sprintf("%.2f%%", float64(it.Vat)/1000), |
| 116 | TotalNet: fmt.Sprintf(unit+"%.2f", float64(it.TotalNet)/100), |
| 117 | Total: fmt.Sprintf(unit+"%.2f", float64(it.Total)/100), |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 118 | }) |
| 119 | } |
| 120 | |
Sergiusz Bazanski | 3976e3c | 2019-05-01 15:27:49 +0200 | [diff] [blame] | 121 | data.TotalNet = fmt.Sprintf(unit+"%.2f", float64(i.TotalNet)/100) |
| 122 | data.VATTotal = fmt.Sprintf(unit+"%.2f", float64(i.Total-i.TotalNet)/100) |
| 123 | data.Total = fmt.Sprintf(unit+"%.2f", float64(i.Total)/100) |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 124 | data.DeliveryCharge = fmt.Sprintf(unit+"%.2f", float64(0)) |
| 125 | |
Sergiusz Bazanski | a818ef2 | 2019-06-07 10:37:22 +0200 | [diff] [blame] | 126 | if _, ok := invTmpl[language]; !ok { |
| 127 | language = defaultLanguage |
| 128 | } |
| 129 | |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 130 | var b bytes.Buffer |
Sergiusz Bazanski | a818ef2 | 2019-06-07 10:37:22 +0200 | [diff] [blame] | 131 | err := invTmpl[language].Execute(&b, &data) |
Sergiusz Bazanski | fb18c99 | 2019-05-01 12:27:03 +0200 | [diff] [blame] | 132 | if err != nil { |
| 133 | return []byte{}, err |
| 134 | } |
| 135 | |
| 136 | pdfg, err := wkhtml.NewPDFGenerator() |
| 137 | if err != nil { |
| 138 | return []byte{}, err |
| 139 | } |
| 140 | pdfg.Dpi.Set(600) |
| 141 | pdfg.NoCollate.Set(false) |
| 142 | pdfg.PageSize.Set(wkhtml.PageSizeA4) |
| 143 | |
| 144 | pdfg.AddPage(wkhtml.NewPageReader(&b)) |
| 145 | |
| 146 | if err := pdfg.Create(); err != nil { |
| 147 | return []byte{}, err |
| 148 | } |
| 149 | return pdfg.Bytes(), nil |
| 150 | } |