145 lines
4.1 KiB
Go
145 lines
4.1 KiB
Go
package invoice
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"html/template"
|
|
"strings"
|
|
|
|
"git.schreifuchs.ch/lou-taylor/accounting/model"
|
|
"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 New(amount string, receiver model.Entity, payee *model.Entity) Invoice {
|
|
i := Invoice{
|
|
ReceiverIBAN: receiver.IBAN,
|
|
IsQrIBAN: false,
|
|
ReceiverName: receiver.Name,
|
|
ReceiverStreet: receiver.Address.Street,
|
|
ReceiverNumber: receiver.Address.Number,
|
|
ReceiverZIPCode: receiver.Address.ZIPCode,
|
|
ReceiverPlace: receiver.Address.Place,
|
|
ReceiverCountry: receiver.Address.Country,
|
|
Reference: generateReference(),
|
|
Amount: amount,
|
|
Currency: "CHF",
|
|
}
|
|
if payee != nil {
|
|
i.PayeeName = payee.Name
|
|
i.PayeeStreet = payee.Address.Street
|
|
i.PayeeNumber = payee.Address.Number
|
|
i.PayeeZIPCode = payee.Address.ZIPCode
|
|
i.PayeePlace = payee.Address.Place
|
|
i.PayeeCountry = payee.Address.Country
|
|
}
|
|
return i
|
|
}
|
|
|
|
func (i Invoice) GetQR() (src template.URL, 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 = template.URL(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
|
|
}
|