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