go/svc/invoice: add shitty multilanguage support
diff --git a/go/svc/invoice/main.go b/go/svc/invoice/main.go
index 11733c4..4a80441 100644
--- a/go/svc/invoice/main.go
+++ b/go/svc/invoice/main.go
@@ -95,7 +95,7 @@
}
}
-func (s *service) invoicePDF(ctx context.Context, uid string) ([]byte, error) {
+func (s *service) invoicePDF(ctx context.Context, uid, language string) ([]byte, error) {
sealed, err := s.m.getSealedUid(ctx, uid)
if err != nil {
return nil, err
@@ -115,7 +115,7 @@
return nil, err
}
- rendered, err = renderInvoicePDF(invoice)
+ rendered, err = renderInvoicePDF(invoice, language)
if err != nil {
return nil, err
}
@@ -124,7 +124,7 @@
}
func (s *service) RenderInvoice(req *pb.RenderInvoiceRequest, srv pb.Invoicer_RenderInvoiceServer) error {
- rendered, err := s.invoicePDF(srv.Context(), req.Uid)
+ rendered, err := s.invoicePDF(srv.Context(), req.Uid, req.Language)
if err != nil {
if _, ok := status.FromError(err); ok {
return err
@@ -150,7 +150,11 @@
}
func (s *service) SealInvoice(ctx context.Context, req *pb.SealInvoiceRequest) (*pb.SealInvoiceResponse, error) {
- if err := s.m.sealInvoice(ctx, req.Uid); err != nil {
+ useProformaTime := false
+ if req.DateSource == pb.SealInvoiceRequest_DATE_SOURCE_PROFORMA {
+ useProformaTime = true
+ }
+ if err := s.m.sealInvoice(ctx, req.Uid, req.Language, useProformaTime); err != nil {
if _, ok := status.FromError(err); ok {
return nil, err
}
diff --git a/go/svc/invoice/model.go b/go/svc/invoice/model.go
index 701807a..51fe850 100644
--- a/go/svc/invoice/model.go
+++ b/go/svc/invoice/model.go
@@ -54,7 +54,7 @@
return err
}
-func (m *model) sealInvoice(ctx context.Context, uid string) error {
+func (m *model) sealInvoice(ctx context.Context, uid, language string, useProformaTime bool) error {
id, err := strconv.Atoi(uid)
if err != nil {
return status.Error(codes.InvalidArgument, "invalid uid")
@@ -81,6 +81,9 @@
`
sealTime := time.Now()
+ if useProformaTime {
+ sealTime = time.Unix(0, invoice.Date)
+ }
res, err := tx.Exec(q, id, sealTime.UnixNano())
if err != nil {
return err
@@ -101,11 +104,16 @@
}
invoice.State = pb.Invoice_STATE_SEALED
- invoice.FinalUid = fmt.Sprintf("FV/%s", finalUid)
+ // TODO(q3k): this should be configurable.
+ if language == "pl" {
+ invoice.FinalUid = fmt.Sprintf("FV/%s", finalUid)
+ } else {
+ invoice.FinalUid = fmt.Sprintf("%s", finalUid)
+ }
invoice.Date = sealTime.UnixNano()
calculateInvoiceData(invoice)
- pdfBlob, err := renderInvoicePDF(invoice)
+ pdfBlob, err := renderInvoicePDF(invoice, language)
if err != nil {
return err
}
@@ -142,7 +150,13 @@
?, ?
)
`
- res, err := m.db.Exec(sql, data, time.Now().UnixNano())
+
+ t := time.Now()
+ if id.Date != 0 {
+ t = time.Unix(0, id.Date)
+ }
+
+ res, err := m.db.Exec(sql, data, t.UnixNano())
if err != nil {
return "", err
}
@@ -216,7 +230,7 @@
}
if s.finalUid.Valid {
p.State = pb.Invoice_STATE_SEALED
- p.FinalUid = fmt.Sprintf("FV/%s", s.finalUid.String)
+ p.FinalUid = fmt.Sprintf("%s", s.finalUid.String)
p.Date = s.sealedTime.Int64
} else {
p.State = pb.Invoice_STATE_PROFORMA
diff --git a/go/svc/invoice/render.go b/go/svc/invoice/render.go
index 68e3472..03dcd77 100644
--- a/go/svc/invoice/render.go
+++ b/go/svc/invoice/render.go
@@ -13,18 +13,25 @@
)
var (
- invTmpl *template.Template
+ invTmpl map[string]*template.Template
+
+ languages = []string{"en", "pl"}
+ defaultLanguage = "en"
)
func init() {
- a, err := templates.Asset("invoice.html")
- if err != nil {
- panic(err)
+ invTmpl = make(map[string]*template.Template)
+ for _, language := range languages {
+ filename := fmt.Sprintf("invoice_%s.html", language)
+ a, err := templates.Asset(filename)
+ if err != nil {
+ panic(err)
+ }
+ invTmpl[language] = template.Must(template.New(filename).Parse(string(a)))
}
- invTmpl = template.Must(template.New("invoice.html").Parse(string(a)))
}
-func renderInvoicePDF(i *pb.Invoice) ([]byte, error) {
+func renderInvoicePDF(i *pb.Invoice, language string) ([]byte, error) {
type item struct {
Title string
UnitPrice string
@@ -95,8 +102,12 @@
data.Total = fmt.Sprintf(unit+"%.2f", float64(i.Total)/100)
data.DeliveryCharge = fmt.Sprintf(unit+"%.2f", float64(0))
+ if _, ok := invTmpl[language]; !ok {
+ language = defaultLanguage
+ }
+
var b bytes.Buffer
- err := invTmpl.Execute(&b, &data)
+ err := invTmpl[language].Execute(&b, &data)
if err != nil {
return []byte{}, err
}
diff --git a/go/svc/invoice/statusz.go b/go/svc/invoice/statusz.go
index 525d61e..0a64ce4 100644
--- a/go/svc/invoice/statusz.go
+++ b/go/svc/invoice/statusz.go
@@ -50,7 +50,12 @@
<td>{{ index .Data.CustomerBilling 0 }}</td>
<td>{{ .TotalNetPretty }}</td>
<td>
+ {{ if eq .State 2 }}
<a href="/debug/view?id={{ .Uid }}">View</a>
+ {{ else }}
+ <a href="/debug/view?id={{ .Uid }}&language=en">Preview (en)</a> |
+ <a href="/debug/view?id={{ .Uid }}&language=pl">Preview (pl)</a>
+ {{ end }}
</td>
</tr>
{{ end }}
@@ -90,7 +95,7 @@
})
m.HTTPMux().HandleFunc("/debug/view", func(w http.ResponseWriter, r *http.Request) {
- rendered, err := s.invoicePDF(r.Context(), r.URL.Query().Get("id"))
+ rendered, err := s.invoicePDF(r.Context(), r.URL.Query().Get("id"), r.URL.Query().Get("language"))
if err != nil {
fmt.Fprintf(w, "error: %v", err)
}
diff --git a/go/svc/invoice/templates/invoice_en.html b/go/svc/invoice/templates/invoice_en.html
new file mode 100644
index 0000000..fb2af50
--- /dev/null
+++ b/go/svc/invoice/templates/invoice_en.html
@@ -0,0 +1,189 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>Invoice 0001</title>
+ <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,700" rel="stylesheet">
+ <style type="text/css">
+body {
+ background-color: #fff;
+ font-family: 'Roboto', sans-serif;
+ font-size: 1em;
+
+ padding: 2em;
+}
+ul {
+ list-style: none;
+ padding: 0;
+}
+ul li {
+ margin-bottom: 0.2em;
+}
+
+@page {
+ size: A4;
+ margin: 0;
+}
+div.rhs {
+ float: right;
+ width: 50%;
+ text-align: right;
+}
+div.lhs {
+ float: left;
+ text-align: left;
+ width: 50%;
+ min-height: 35em;
+}
+div.metadata {
+ margin-top: 2em;
+}
+div.invoicee {
+ margin-top: 9em;
+}
+h1 {
+ font-size: 1.5em;
+ margin: 0;
+ text-transform: uppercase;
+}
+h2 {
+ font-size: 1.2em;
+ margin: 0;
+}
+table.items {
+ text-align: right;
+ border-spacing: 0px;
+ border-collapse: collapse;
+ border: 0;
+ width: 100%;
+}
+table.items td,th {
+ border: 1px solid black;
+}
+table.items tr:first-child {
+ background-color: #eee;
+ color: #111;
+ padding: 0.8em;
+ text-align: left;
+}
+table.items td {
+ background-color: #fff;
+}
+table.items td,th {
+ padding: 0.5em 1em 0.5em 1em;
+}
+td.lhead {
+ border: 0 !important;
+ text-align: right;
+ text-transform: uppercase;
+ background: rgba(0, 0, 0, 0) !important;
+}
+div.bgtext {
+ z-index: -10;
+ position: absolute;
+ top: 140mm;
+ left: 0;
+ width: 100%;
+}
+div.bgtext div {
+ text-align: center;
+ font-size: 10em;
+ color: #ddd;
+ -webkit-transform: rotate(-45deg);
+ text-transform: uppercase;
+}
+ </style>
+ </head>
+ <body>
+ {{ if .Proforma }}
+ <div class="bgtext"><div>Proforma</div></div>
+ {{ end }}
+ <div class="rhs">
+ <div class="invoicer">
+ <ul>
+ {{ range $i, $e := .InvoicerBilling }}
+ {{ if eq $i 0 }}
+ <li><b>{{ $e }}</b></li>
+ {{ else }}
+ <li>{{ $e }}</li>
+ {{ end }}
+ {{ end }}
+ {{ if .InvoicerCompanyNumber }}
+ <li>{{ .InvoicerCompanyNumber }}</li>
+ {{ end }}
+ <li><b>Tax Number:</b> {{ .InvoicerVAT }}</li>
+ </ul>
+ </div>
+ <div class="metadata">
+ <ul>
+ <li><b>Invoice number:</b> {{ .InvoiceNumber }}</li>
+ <li><b>Date:</b> {{ .Date.Format "2006/01/02" }}</li>
+ <li><b>Due date:</b> {{ .DueDate.Format "2006/01/02" }}</li>
+ <li><b>IBAN:</b> {{ .IBAN }}</li>
+ <li><b>SWIFT/BIC:</b> {{ .SWIFT }}</li>
+ </ul>
+ </div>
+ </div>
+ <div class="lhs">
+ <div class="invoicee">
+ {{ if .Proforma }}
+ <h1>Proforma Invoice</h1>
+ {{ else }}
+ <h1>VAT Invoice</h1>
+ {{ end }}
+ <ul>
+ {{ range $i, $e := .InvoiceeBilling }}
+ {{ if eq $i 0 }}
+ <li><b>{{ $e }}</b></li>
+ {{ else }}
+ <li>{{ $e }}</li>
+ {{ end }}
+ {{ end }}
+ {{ if .USCustomer }}
+ <li>EIN: {{ .InvoiceeVAT }}</li>
+ <li><b>(VAT zero rate)</b></li>
+ {{ else }}
+ <li><b>NIP:</b> {{ .InvoiceeVAT }}</li>
+ {{ end }}
+
+ {{ if .ReverseVAT }}
+ <li><b>(reverse charge applies)</b></li>
+ {{ end }}
+ </ul>
+ </div>
+ </div>
+ <div style="clear: both; height: 1em;"></div>
+ <table class="items">
+ <tr>
+ <th style="width: 60%;">Description</th>
+ <th>Price<br />(ex. VAT)</th>
+ <th>Qty</th>
+ <th>VAT rate</th>
+ <th>Total<br />(net)</th>
+ <th>Total<br />(inc. VAT)</th>
+ </tr>
+ {{ range .Items }}
+ <tr>
+ <td style="text-align: left;">{{ .Title }}</td>
+ <td>{{ .UnitPrice }}</td>
+ <td>{{ .Qty }}</td>
+ <td>{{ .VATRate }}</td>
+ <td>{{ .TotalNet }}</td>
+ <td>{{ .Total }}</td>
+ </tr>
+ {{ end }}
+ <tr>
+ <td colspan="5" class="lhead">Subtotal without VAT</td>
+ <td>{{ .TotalNet }}</td>
+ </tr>
+ <tr>
+ <td colspan="5" class="lhead">VAT Total{{ if .ReverseVAT }} (reverse charge applies){{ end }} {{ if .USCustomer }}(VAT zero rate){{ end }}</td>
+ <td>{{ .VATTotal }}</td>
+ </tr>
+ <tr>
+ <td colspan="5" class="lhead"><b>Total</b></td>
+ <td><b>{{ .Total }}</b></td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/go/svc/invoice/templates/invoice.html b/go/svc/invoice/templates/invoice_pl.html
similarity index 100%
rename from go/svc/invoice/templates/invoice.html
rename to go/svc/invoice/templates/invoice_pl.html