feat: pdf

This commit is contained in:
u80864958
2025-08-22 15:59:26 +02:00
parent 11e7b6445c
commit 7c6916f3a1
14 changed files with 445 additions and 84 deletions

118
invoice/invoice.go Normal file
View File

@@ -0,0 +1,118 @@
package invoice
import (
"bytes"
"encoding/base64"
"fmt"
"html/template"
"strings"
"github.com/skip2/go-qrcode"
)
// Invoice contains all necessary information for the generation of an invoice.
type Invoice struct {
ReceiverIBAN string `yaml:"receiver_iban" default:"CH44 3199 9123 0008 8901 2"`
IsQrIBAN bool `yaml:"is_qr_iban" default:"true"`
ReceiverName string `yaml:"receiver_name" default:"Robert Schneider AG"`
ReceiverStreet string `yaml:"receiver_street" default:"Rue du Lac"`
ReceiverNumber string `yaml:"receiver_number" default:"12"`
ReceiverZIPCode string `yaml:"receiver_zip_code" default:"2501"`
ReceiverPlace string `yaml:"receiver_place" default:"Biel"`
ReceiverCountry string `yaml:"receiver_country" default:"CH"`
PayeeName string `yaml:"payee_name" default:"Pia-Maria Rutschmann-Schnyder"`
PayeeStreet string `yaml:"payee_street" default:"Grosse Marktgasse"`
PayeeNumber string `yaml:"payee_number" default:"28"`
PayeeZIPCode string `yaml:"payee_zip_code" default:"9400"`
PayeePlace string `yaml:"payee_place" default:"Rorschach"`
PayeeCountry string `yaml:"payee_country" default:"CH"`
Reference string `yaml:"reference" default:"21 00000 00003 13947 14300 09017"`
AdditionalInfo string `yaml:"additional_info" default:"Rechnung Nr. 3139 für Gartenarbeiten"`
Amount string `yaml:"amount" default:"3 949.75"`
Currency string `yaml:"currency" default:"CHF"`
}
func (i Invoice) GetQR() (src string, err error) {
content, err := i.qrContent()
if err != nil {
return
}
qr, err := qrcode.New(content, qrcode.Medium)
if err != nil {
return
}
qr.DisableBorder = true
png, err := qr.PNG(512)
if err != nil {
return
}
b64 := base64.StdEncoding.EncodeToString(png)
src = fmt.Sprintf(`data:image/png;base64,%s`, b64)
return
}
// noPayee returns true if no fields of the payee are set
func (i Invoice) noPayee() bool {
return i.PayeeName == "" && i.PayeeStreet == "" && i.PayeeZIPCode == "" && i.PayeePlace == ""
}
func (i Invoice) qrContent() (string, error) {
qrTpl := `SPC\r
0200\r
1\r
{{ .iban }}\r
S\r
{{ .inv.ReceiverName }}\r
{{ .inv.ReceiverStreet }}\r
{{ .inv.ReceiverNumber }}\r
{{ .inv.ReceiverZIPCode }}\r
{{ .inv.ReceiverPlace }}\r
{{ .inv.ReceiverCountry }}\r
\r
\r
\r
\r
\r
\r
\r
{{ .amount }}\r
{{ .inv.Currency }}\r
{{ .payeeAdrType }}\r
{{ .inv.PayeeName }}\r
{{ .inv.PayeeStreet }}\r
{{ .inv.PayeeNumber }}\r
{{ .inv.PayeeZIPCode }}\r
{{ .inv.PayeePlace }}\r
{{ .inv.PayeeCountry }}\r
{{ .refType }}\r
{{ .reference }}\r
{{ .inv.AdditionalInfo }}\r
EPD
`
refType := "QRR"
if !i.IsQrIBAN {
refType = "SCOR"
}
payeeAdrType := "S"
if i.noPayee() {
payeeAdrType = ""
}
data := map[string]any{
"inv": i,
"iban": strings.ReplaceAll(i.ReceiverIBAN, " ", ""),
"amount": strings.ReplaceAll(i.Amount, " ", ""),
"payeeAdrType": payeeAdrType,
"reference": strings.ReplaceAll(i.Reference, " ", ""),
"refType": refType,
}
tpl, err := template.New("qr-content").Parse(qrTpl)
if err != nil {
return "", fmt.Errorf("error while creating qr template: %s", err)
}
var buf bytes.Buffer
if err := tpl.Execute(&buf, data); err != nil {
return "", fmt.Errorf("error while applying data to qr template: %s", err)
}
rsl := strings.ReplaceAll(buf.String(), "\\r", "\r")
return rsl, nil
}