Compare commits

..

10 Commits

12 changed files with 328 additions and 124 deletions

1
.gitignore vendored
View File

@ -1,6 +1,5 @@
mkinvoice
makefont/makefont
temp/*
qr-images/*
output/*
yaml/*

View File

@ -5,12 +5,10 @@ purpose to generate PDF-Invoices for nbit Informatik GmbH
Font File can be generated with makefont
Payment Slip is generated using https://github.com/claudep/swiss-qr-bill
Installation / Upgrade
$ pip install --user qrbill -U
To generate the QRCode, following library is used:
https://github.com/stapelberg/qrbill
Usage (Example):
$ ./mkinvoice yaml/123456.yml
Joerg Lehmann, April 2020
Joerg Lehmann, December 2020

View File

@ -1,26 +1,27 @@
---
metadata:
invoice_nr: 6060900045
invoice_info: Rechnung Nummer 6060900045
invoice_date: 2. April 2020
invoice_nr: 123456789
invoice_date: 11. Dezember 2020
vat: 7.7
account: CH92 0023 5235 5662 3601 G
due_date: 2019-10-31
vat_number: CHE-105.687.350 MWST
sender_address:
name: nbit Informatik GmbH
street: Kirchweg 2
street: Kirchweg
streetnumber: 2
zip: 3510
city: Konolfingen
tel_no: +41 31 792 00 40
email: joerg.lehmann@nbit.ch
billing_address:
name: Coopers Group GmbH
street: Seestrasse 72b
street: Seestrasse
streetnumber: 72b
zip: 6052
city: Hergiswil
invoice_items:
- text: Arbeitseinsatz von Jörg Lehmann als Linux Engineer bei Post_CH_AG gemäss IT Beratungsdienstleistungsvertrag vom 28.1.2020
quantity: 142
price_per_unit: 115
- text: Arbeitseinsatz von Jörg Lehmann als Linux Engineer bei Post_CH_AG gemäss IT Beratungsdienstleistungsvertrag vom 2.7.2020
quantity: 123.4
price_per_unit: 32.10
- text:
- text: Monat Mai 2020, Stunden gemäss beigelegtem Zeitnachweis
- text: Monat Dezember 2020, Stunden gemäss beigelegtem Zeitnachweis

16
go.mod Normal file
View File

@ -0,0 +1,16 @@
module gitlab.com/drpuur/mkinvoice
go 1.17
require (
github.com/jung-kurt/gofpdf v1.16.2
github.com/stapelberg/qrbill v0.1.9
golang.org/x/text v0.3.7
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb // indirect
github.com/makiuchi-d/gozxing v0.0.0-20200903113411-25f730ed83da // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)

32
go.sum Normal file
View File

@ -0,0 +1,32 @@
github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb h1:EVl3FJLQCzSbgBezKo/1A4ADnJ4mtJZ0RvnNzDJ44nY=
github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
github.com/makiuchi-d/gozxing v0.0.0-20200903113411-25f730ed83da h1:OgNu1PPD9EvZckyKDAc8DA4KymNXuc6vaCLsdOGyjOE=
github.com/makiuchi-d/gozxing v0.0.0-20200903113411-25f730ed83da/go.mod h1:WoI7z45M7ZNA5BJxiJHaB+x7+k8S/3phW5Y13IR4yWY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/stapelberg/qrbill v0.1.9 h1:HMNta6gG/ljRame+Q7C6AUb/eFnLygqpNNKjICmVBxc=
github.com/stapelberg/qrbill v0.1.9/go.mod h1:+R3uLeV1AjSSHsJ7vdNDA/meZ/PSfQ06TGOO7yFp4EI=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="100"
height="60"
id="svg2">
<defs
id="defs4">
<marker
refX="0"
refY="0"
orient="auto"
id="Scissors"
style="overflow:visible">
<path
d="M 9.0898857,-3.6061018 C 8.1198849,-4.7769976 6.3697607,-4.7358294 5.0623558,-4.2327734 l -8.2124046,3.0779029 c -2.3882933,-1.3067135 -4.7482873,-0.9325372 -4.7482873,-1.5687873 0,-0.4973164 0.4566662,-0.3883222 0.3883068,-1.6831941 -0.065635,-1.2432767 -1.3635771,-2.1630796 -2.5903987,-2.0816435 -1.227271,-0.00735 -2.499439,0.9331613 -2.510341,2.2300611 -0.09143,1.3063864 1.007209,2.5196896 2.306764,2.6052316 1.5223406,0.2266616 4.218258,-0.6955566 5.482945,1.57086006 -0.9422847,1.73825774 -2.6140244,1.74307674 -4.1255107,1.65607034 -1.2548743,-0.072235 -2.7620933,0.2873979 -3.3606483,1.5208605 -0.578367,1.1820862 -0.0112,2.8646022 1.316749,3.226412 1.3401912,0.4918277 3.1806689,-0.129711 3.4993722,-1.6707242 0.2456585,-1.187823 -0.5953659,-1.7459574 -0.2725074,-2.1771537 0.2436135,-0.32536 1.7907806,-0.1368452 4.5471053,-1.3748244 L 5.6763468,4.2330688 C 6.8000164,4.5467672 8.1730685,4.5362646 9.1684433,3.4313614 L -0.05164093,-0.05372222 9.0898857,-3.6061018 z m -18.3078016,-1.900504 c 1.294559,0.7227998 1.1888392,2.6835702 -0.1564272,3.0632889 -1.2165179,0.423661 -2.7710269,-0.7589694 -2.3831779,-2.0774648 0.227148,-1.0818519 1.653387,-1.480632 2.5396051,-0.9858241 z m 0.056264,8.0173649 c 1.3508301,0.4988648 1.1214429,2.7844356 -0.2522207,3.091609 -0.9110594,0.3163391 -2.2135494,-0.1387976 -2.3056964,-1.2121394 -0.177609,-1.305055 1.356085,-2.4841482 2.5579171,-1.8794696 z"
id="schere"
style="marker-start:none" />
</marker>
</defs>
<g
transform="translate(0,-992.36218)"
id="layer1">
<g
transform="matrix(4.2610846,-1.2351263,1.2351263,4.2610846,-1337.7659,-2994.9736)"
id="g4593">
<path
d="m 59.731665,956.40057 c -0.609784,-1.39286 -2.303491,-1.83556 -3.698902,-1.71226 l -8.742604,0.69566 c -1.935736,-1.91425 -4.307466,-2.2049 -4.132136,-2.81651 0.137044,-0.47806 0.545993,-0.24745 0.837105,-1.51102 0.279511,-1.21323 -0.714709,-2.45509 -1.916471,-2.71488 -1.177727,-0.34526 -2.659814,0.20827 -3.027677,1.45195 -0.447882,1.23061 0.273869,2.69969 1.499535,3.14003 1.400938,0.6374 4.246607,0.49379 4.837778,3.02096 -1.384807,1.41129 -2.993148,0.95525 -4.422137,0.4551 -1.186382,-0.41524 -2.734347,-0.48487 -3.649629,0.53589 -0.881717,0.97694 -0.800158,2.7506 0.376675,3.46434 1.15277,0.8421 3.093263,0.7518 3.824279,-0.64172 0.563472,-1.07414 -0.09119,-1.84242 0.337995,-2.16796 0.32384,-0.24563 1.759155,0.36194 4.749906,-0.0686 l 7.684693,5.46398 c 0.993719,0.61119 2.316503,0.97947 3.577814,0.19164 l -7.902726,-5.8909 9.766502,-0.89574 z m -17.075242,-6.87194 c 1.045256,1.05155 0.403306,2.90727 -0.994512,2.90157 -1.286163,0.072 -2.454591,-1.49318 -1.718425,-2.65375 0.516476,-0.97737 1.997384,-0.96769 2.712937,-0.24782 z m -2.155235,7.72245 c 1.161058,0.85179 0.310724,2.98566 -1.0944,2.9024 -0.962958,0.053 -2.089598,-0.7434 -1.882399,-1.80058 0.188897,-1.30347 1.988129,-2.01427 2.976799,-1.10182 z"
id="path4599"
style="marker-start:none" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

BIN
logos/scissors-rotated.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
logos/scissors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -1,28 +1,29 @@
package main
import (
"bytes"
"fmt"
"github.com/jung-kurt/gofpdf"
"golang.org/x/text/language"
"golang.org/x/text/message"
"gopkg.in/yaml.v2"
"image/png"
"io/ioutil"
"log"
"math"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/stapelberg/qrbill"
)
// Metadata type
type Metadata struct {
InvoiceNr string `yaml:"invoice_nr"`
InvoiceInfo string `yaml:"invoice_info"`
InvoiceDate string `yaml:"invoice_date"`
Vat float64 `yaml:"vat"`
Account string `yaml:"account"`
DueDate string `yaml:"due_date"`
VatNumber string `yaml:"vat_number"`
}
@ -30,6 +31,7 @@ type Metadata struct {
type Address struct {
Name string `yaml:"name"`
Street string `yaml:"street"`
StreetNumber string `yaml:"streetnumber"`
Zip string `yaml:"zip"`
City string `yaml:"city"`
TelNo string `yaml:"tel_no"`
@ -63,11 +65,19 @@ var totalNetAmount float64
var totalInvoiceAmount float64
const defaultFontSize = 9
const smallFontSize = 7
const paymentSlipTitleFontSize = 11
const paymentSlipHeadingFontSize = 6
const paymentSlipValuesFontSize = 8
const marginTop = 7
const logoTop = 6
const logoHeight = 20
const lineSpacing = 5
const lineSpacingSmall = 3.5
const lineSpacingTextsmall = 3
const lineSpacingSmall = 4.5
const lineSpacingPaymentSlipBig = 6
const lineSpacingPaymentSlipBelowHeading = 2.8
const lineSpacingPaymentSlipSmall = 3.175
const addressTop = 50
const metadataTopFirstPage = 70
const metadataTopNotFirstPage = 30
@ -86,17 +96,29 @@ const tabstopPricePerUnit = tabstopRight - widthPrice - widthPricePerUnit
const tabstopPrice = tabstopRight - widthPrice
const itemsTopFirstPage = 105
const itemsTopNotFirstPage = 50
const totalsTop = 165
const totalsTop = 158
const maxYPos = 265
const paymentSlipTop = 193.5
const paymentSlipWaehrung = 270
const paymentSlipDashLeft = 60.5
const tabstopPaymentSlipLeft = 5
const tabstopPaymentSlipLeft2 = 17
const tabstopPaymentSlipLeft3 = 40
const tabstopPaymentSlipMiddle = 68
const tabstopPaymentSlipMiddle2 = 80
const tabstopPaymentSlipRight = 125
const scissorsTop = 200
const widthA4 = 210
const lengthA4 = 297
func round5rappen(f float64) float64 {
return (math.Round(f*20) / 20)
}
func floatToString(f float64) string {
func floatToString(f float64, sep string) string {
p := message.NewPrinter(language.English)
s := strings.ReplaceAll(p.Sprintf("%.2f", f), ",", "'")
fmt.Printf("--- s: @%s@\n", s)
s := strings.ReplaceAll(p.Sprintf("%.2f", f), ",", sep)
//fmt.Printf("--- s: @%s@\n", s)
return s
}
@ -113,8 +135,7 @@ func readInvoiceData(filename string) {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t:\n%v\n\n", invoiceData)
fmt.Printf("%s\n", invoiceData.BillingAddress.Name)
//fmt.Printf("--- t:\n%v\n\n", invoiceData)
}
func writeText(x float64, y float64, w float64, text string, alignStr ...string) {
@ -127,9 +148,20 @@ func writeText(x float64, y float64, w float64, text string, alignStr ...string)
pdf.CellFormat(w, 11, tr(text), "", 0, align, false, 0, "")
}
func writeTextsmall(x float64, y float64, w float64, text string, alignStr ...string) {
tr := pdf.UnicodeTranslatorFromDescriptor("")
align := "LT"
if len(alignStr) > 0 {
align = alignStr[0]
}
pdf.SetXY(x, y)
pdf.CellFormat(w, 6, tr(text), "", 0, align, false, 0, "")
}
func setupInvoice() {
pdf = gofpdf.New("P", "mm", "A4", "")
pdf.SetMargins(0, 0, 0)
pdf.SetAutoPageBreak(false, 0)
pdf.SetFontLocation("fonts")
pdf.AddFont("Dejavusans", "", "DejaVuSans.json")
pdf.AddFont("Dejavusans-Bold", "", "DejaVuSans-Bold.json")
@ -147,7 +179,7 @@ func printPageHeader(firstPage bool) {
writeText(tabstopLeft, yPos, 0, invoiceData.SenderAddress.Name)
pdf.SetFont("Dejavusans", "", defaultFontSize)
yPos = yPos + lineSpacing
writeText(tabstopLeft, yPos, 0, invoiceData.SenderAddress.Street)
writeText(tabstopLeft, yPos, 0, invoiceData.SenderAddress.Street+" "+invoiceData.SenderAddress.StreetNumber)
yPos = yPos + lineSpacing
writeText(tabstopLeft, yPos, 0, invoiceData.SenderAddress.Zip+" "+invoiceData.SenderAddress.City)
if firstPage {
@ -173,7 +205,7 @@ func printAddress() {
yPos = addressTop
writeText(tabstopAddress, yPos, 0, invoiceData.BillingAddress.Name)
yPos = yPos + lineSpacing
writeText(tabstopAddress, yPos, 0, invoiceData.BillingAddress.Street)
writeText(tabstopAddress, yPos, 0, invoiceData.BillingAddress.Street+" "+invoiceData.BillingAddress.StreetNumber)
yPos = yPos + lineSpacing
writeText(tabstopAddress, yPos, 0, invoiceData.BillingAddress.Zip+" "+invoiceData.BillingAddress.City)
}
@ -233,13 +265,24 @@ func maybeNewPage() {
func printItems() {
for _, i := range invoiceData.InvoiceItems {
if i.Quantity != 0 {
writeText(tabstopQuantity, yPos, widthQuantity, fmt.Sprintf("%.1f", i.Quantity), "TR")
writeText(tabstopQuantity, yPos, widthQuantity, fmt.Sprintf("%g", i.Quantity), "TR")
writeText(tabstopPricePerUnit, yPos, widthPricePerUnit, fmt.Sprintf("%.2f", i.PricePerUnit), "TR")
itemNetAmount := round5rappen(i.Quantity * i.PricePerUnit)
totalNetAmount = totalNetAmount + itemNetAmount
writeText(tabstopPrice, yPos, widthPrice, floatToString(itemNetAmount), "TR")
writeText(tabstopPrice, yPos, widthPrice, floatToString(itemNetAmount, "'"), "TR")
}
if i.Text != "" {
if strings.HasPrefix(i.Text, "@") {
pdf.SetFont("Dejavusans", "", smallFontSize)
textwithoutprefix := i.Text[1:]
lines := pdf.SplitText(textwithoutprefix, widthItemText)
for _, il := range lines {
totalItemLines = totalItemLines + 1
writeTextsmall(tabstopLeft, yPos, 0, strings.ReplaceAll(il, "_", " "))
yPos = yPos + lineSpacingTextsmall
maybeNewPage()
}
pdf.SetFont("Dejavusans", "", defaultFontSize)
} else if i.Text != "" {
lines := pdf.SplitText(i.Text, widthItemText)
for _, il := range lines {
totalItemLines = totalItemLines + 1
@ -257,53 +300,186 @@ func printItems() {
func printTotals() {
yPos = totalsTop
pdf.Line(tabstopRight-widthPrice, yPos, tabstopRight, yPos)
pdf.Line(tabstopRight-widthPrice, yPos+(lineSpacing/2), tabstopRight, yPos+(lineSpacing/2))
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
yPos = yPos + lineSpacing
writeText(tabstopLeft, yPos, 0, "Netto Betrag")
writeText(tabstopPrice, yPos, widthPrice, floatToString(totalNetAmount), "TR")
yPos = yPos + lineSpacing
writeText(tabstopLeft, yPos, 0, "Summe")
pdf.SetFont("Dejavusans", "", defaultFontSize)
writeText(tabstopPrice, yPos, widthPrice, floatToString(totalNetAmount, "'"), "TR")
yPos = yPos + lineSpacing
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
writeText(tabstopLeft, yPos, 0, fmt.Sprintf("MwSt. %.1f%%", invoiceData.Metadata.Vat))
mwstAmount := round5rappen(totalNetAmount * invoiceData.Metadata.Vat / 100)
writeText(tabstopPrice, yPos, widthPrice, floatToString(mwstAmount), "TR")
pdf.SetFont("Dejavusans", "", defaultFontSize)
writeText(tabstopPrice, yPos, widthPrice, floatToString(mwstAmount, "'"), "TR")
yPos = yPos + lineSpacing
yPos = yPos + lineSpacing
writeText(tabstopLeft, yPos, 0, "Total Betrag sFr.")
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
writeText(tabstopLeft, yPos, 0, "Rechnungbetrag in CHF")
totalInvoiceAmount = totalNetAmount + mwstAmount
writeText(tabstopPrice, yPos, widthPrice, floatToString(totalNetAmount+mwstAmount), "TR")
writeText(tabstopPrice, yPos, widthPrice, floatToString(totalNetAmount+mwstAmount, "'"), "TR")
yPos = yPos + lineSpacingSmall
pdf.SetFont("Dejavusans", "", smallFontSize)
writeText(tabstopLeft, yPos, 0, "30 Tage netto")
}
func qrchFromInvoiceData(id InvoiceData) *qrbill.QRCH {
return &qrbill.QRCH{
CdtrInf: qrbill.QRCHCdtrInf{
IBAN: id.Metadata.Account,
Cdtr: qrbill.Address{
AdrTp: qrbill.AddressType(string(qrbill.AddressTypeStructured)),
Name: id.SenderAddress.Name,
StrtNmOrAdrLine1: id.SenderAddress.Street,
BldgNbOrAdrLine2: id.SenderAddress.StreetNumber,
PstCd: id.SenderAddress.Zip,
TwnNm: id.SenderAddress.City,
Ctry: "CH",
},
},
CcyAmt: qrbill.QRCHCcyAmt{
Amt: fmt.Sprintf("%.2f", totalInvoiceAmount),
Ccy: "CHF",
},
UltmtDbtr: qrbill.Address{
AdrTp: qrbill.AddressType(string(qrbill.AddressTypeStructured)),
Name: id.BillingAddress.Name,
StrtNmOrAdrLine1: id.BillingAddress.Street,
BldgNbOrAdrLine2: id.BillingAddress.StreetNumber,
PstCd: id.BillingAddress.Zip,
TwnNm: id.BillingAddress.City,
Ctry: "CH",
},
RmtInf: qrbill.QRCHRmtInf{
Tp: "NON", // Reference type
Ref: "", // Reference
AddInf: qrbill.QRCHRmtInfAddInf{
Ustrd: "Rechnung Nummer " + id.Metadata.InvoiceNr,
},
},
}
}
func printPaymentSlip() {
var opt gofpdf.ImageOptions
pdf.SetDashPattern([]float64{0.8, 0.8}, 0)
pdf.Line(0, paymentSlipTop, widthA4, paymentSlipTop)
pdf.Line(paymentSlipDashLeft, paymentSlipTop, paymentSlipDashLeft, lengthA4)
opt.ImageType = "png"
pdf.ImageOptions("logos/scissors-rotated.png", paymentSlipDashLeft-1.5, paymentSlipTop+10, 3, 3, false, opt, 0, "")
pdf.SetFont("Helvetica", "B", paymentSlipTitleFontSize)
yPos = paymentSlipTop + 7
writeText(tabstopPaymentSlipLeft, yPos, 0, "Empfangsschein")
yPos = yPos + lineSpacingPaymentSlipBig
pdf.SetFont("Helvetica", "B", paymentSlipHeadingFontSize)
writeText(tabstopPaymentSlipLeft, yPos, 0, "Konto / Zahlbar an")
yPos = yPos + lineSpacingPaymentSlipBelowHeading
pdf.SetFont("Helvetica", "", paymentSlipValuesFontSize)
writeText(tabstopPaymentSlipLeft, yPos, 0, invoiceData.Metadata.Account)
yPos = yPos + lineSpacingPaymentSlipSmall
writeText(tabstopPaymentSlipLeft, yPos, 0, invoiceData.SenderAddress.Name)
yPos = yPos + lineSpacingPaymentSlipSmall
writeText(tabstopPaymentSlipLeft, yPos, 0, invoiceData.SenderAddress.Street+" "+invoiceData.SenderAddress.StreetNumber)
yPos = yPos + lineSpacingPaymentSlipSmall
writeText(tabstopPaymentSlipLeft, yPos, 0, invoiceData.SenderAddress.Zip+" "+invoiceData.SenderAddress.City)
yPos = yPos + lineSpacingPaymentSlipSmall
yPos = yPos + lineSpacingPaymentSlipSmall
pdf.SetFont("Helvetica", "B", paymentSlipHeadingFontSize)
writeText(tabstopPaymentSlipLeft, yPos, 0, "Zahlbar durch")
yPos = yPos + lineSpacingPaymentSlipBelowHeading
pdf.SetFont("Helvetica", "", paymentSlipValuesFontSize)
writeText(tabstopPaymentSlipLeft, yPos, 0, invoiceData.BillingAddress.Name)
yPos = yPos + lineSpacingPaymentSlipSmall
writeText(tabstopPaymentSlipLeft, yPos, 0, invoiceData.BillingAddress.Street+" "+invoiceData.BillingAddress.StreetNumber)
yPos = yPos + lineSpacingPaymentSlipSmall
writeText(tabstopPaymentSlipLeft, yPos, 0, invoiceData.BillingAddress.Zip+" "+invoiceData.BillingAddress.City)
yPos = paymentSlipWaehrung
pdf.SetFont("Helvetica", "B", paymentSlipHeadingFontSize)
writeText(tabstopPaymentSlipLeft, yPos, 0, "Währung")
writeText(tabstopPaymentSlipLeft2, yPos, 0, "Betrag")
yPos = yPos + lineSpacingPaymentSlipBelowHeading
pdf.SetFont("Helvetica", "", paymentSlipValuesFontSize)
writeText(tabstopPaymentSlipLeft, yPos, 0, "CHF")
writeText(tabstopPaymentSlipLeft2, yPos, 0, floatToString(totalInvoiceAmount, " "))
yPos = yPos + lineSpacingPaymentSlipSmall
yPos = yPos + lineSpacingPaymentSlipSmall
pdf.SetFont("Helvetica", "B", paymentSlipHeadingFontSize)
writeText(tabstopPaymentSlipLeft3, yPos, 0, "Annahmestelle")
pdf.SetFont("Helvetica", "B", paymentSlipTitleFontSize)
yPos = paymentSlipTop + 7
writeText(tabstopPaymentSlipMiddle, yPos, 0, "Zahlteil")
yPos = paymentSlipWaehrung
pdf.SetFont("Helvetica", "B", paymentSlipHeadingFontSize)
writeText(tabstopPaymentSlipMiddle, yPos, 0, "Währung")
writeText(tabstopPaymentSlipMiddle2, yPos, 0, "Betrag")
yPos = yPos + lineSpacingPaymentSlipBelowHeading
pdf.SetFont("Helvetica", "", paymentSlipValuesFontSize)
writeText(tabstopPaymentSlipMiddle, yPos, 0, "CHF")
writeText(tabstopPaymentSlipMiddle2, yPos, 0, floatToString(totalInvoiceAmount, " "))
yPos = paymentSlipTop + 7
pdf.SetFont("Helvetica", "B", paymentSlipHeadingFontSize)
writeText(tabstopPaymentSlipRight, yPos, 0, "Konto / Zahlbar an")
yPos = yPos + lineSpacingPaymentSlipBelowHeading
pdf.SetFont("Helvetica", "", paymentSlipValuesFontSize)
writeText(tabstopPaymentSlipRight, yPos, 0, invoiceData.Metadata.Account)
yPos = yPos + lineSpacingPaymentSlipSmall
writeText(tabstopPaymentSlipRight, yPos, 0, invoiceData.SenderAddress.Name)
yPos = yPos + lineSpacingPaymentSlipSmall
writeText(tabstopPaymentSlipRight, yPos, 0, invoiceData.SenderAddress.Street+" "+invoiceData.SenderAddress.StreetNumber)
yPos = yPos + lineSpacingPaymentSlipSmall
writeText(tabstopPaymentSlipRight, yPos, 0, invoiceData.SenderAddress.Zip+" "+invoiceData.SenderAddress.City)
yPos = yPos + (lineSpacingPaymentSlipSmall * 1.5)
pdf.SetFont("Helvetica", "B", paymentSlipHeadingFontSize)
writeText(tabstopPaymentSlipRight, yPos, 0, "Zusätzliche Informationen")
yPos = yPos + lineSpacingPaymentSlipBelowHeading
pdf.SetFont("Helvetica", "", paymentSlipValuesFontSize)
writeText(tabstopPaymentSlipRight, yPos, 0, "Rechnung Nummer "+invoiceData.Metadata.InvoiceNr)
yPos = yPos + (lineSpacingPaymentSlipSmall * 1.5)
pdf.SetFont("Helvetica", "B", paymentSlipHeadingFontSize)
writeText(tabstopPaymentSlipRight, yPos, 0, "Zahlbar durch")
yPos = yPos + lineSpacingPaymentSlipBelowHeading
pdf.SetFont("Helvetica", "", paymentSlipValuesFontSize)
writeText(tabstopPaymentSlipRight, yPos, 0, invoiceData.BillingAddress.Name)
yPos = yPos + lineSpacingPaymentSlipSmall
writeText(tabstopPaymentSlipRight, yPos, 0, invoiceData.BillingAddress.Street+" "+invoiceData.BillingAddress.StreetNumber)
yPos = yPos + lineSpacingPaymentSlipSmall
writeText(tabstopPaymentSlipRight, yPos, 0, invoiceData.BillingAddress.Zip+" "+invoiceData.BillingAddress.City)
}
func printQR() {
var opt gofpdf.ImageOptions
cmd := exec.Command(filepath.Join(progDir, "qrbill.sh"),
"--account", invoiceData.Metadata.Account,
"--amount", floatToString(totalInvoiceAmount),
"--creditor-name", invoiceData.SenderAddress.Name,
"--creditor-street", invoiceData.SenderAddress.Street,
"--creditor-postalcode", invoiceData.SenderAddress.Zip,
"--creditor-city", invoiceData.SenderAddress.City,
"--extra-infos", invoiceData.Metadata.InvoiceInfo,
"--debtor-name", invoiceData.BillingAddress.Name,
"--debtor-street", invoiceData.BillingAddress.Street,
"--debtor-postalcode", invoiceData.BillingAddress.Zip,
"--debtor-city", invoiceData.BillingAddress.City,
"--due-date", invoiceData.Metadata.DueDate,
"--language", "de")
cmd.Env = append(os.Environ(),
"INVNO="+invoiceData.Metadata.InvoiceNr,
)
stdoutStderr, err := cmd.CombinedOutput()
fmt.Printf("%s\n", stdoutStderr)
var b []byte
var err error
qrch := qrchFromInvoiceData(invoiceData)
bill, err := qrch.Encode()
code, err := bill.EncodeToImage()
if err != nil {
log.Fatal(err)
log.Printf("%s", err)
return
}
var buf bytes.Buffer
if err := png.Encode(&buf, code); err != nil {
log.Printf("%s", err)
return
}
b = buf.Bytes()
err = ioutil.WriteFile("qr-images/"+invoiceData.Metadata.InvoiceNr+".png", b, 0644)
if err != nil {
// handle error
}
fmt.Printf("%s\n", stdoutStderr)
opt.ImageType = "png"
opt.ReadDpi = true
pdf.ImageOptions("qr-images/"+invoiceData.Metadata.InvoiceNr+".png", 0, 200, -1, -1, false, opt, 0, "")
opt.ReadDpi = false
pdf.ImageOptions("qr-images/"+invoiceData.Metadata.InvoiceNr+".png", tabstopPaymentSlipMiddle-2, paymentSlipTop+13, 56, 56, false, opt, 0, "")
}
func CreateInvoice() {
@ -317,6 +493,7 @@ func CreateInvoice() {
printItemsHeader()
printItems()
printTotals()
printPaymentSlip()
printQR()
}

View File

@ -1,40 +0,0 @@
#!/bin/bash
mydir="$(dirname $0)"
if [ -z "${INVNO}" ]; then
echo "ERROR: INVNO must be set as ENV variable"
exit 1
fi
echo "INVNO: ${INVNO}"
# should be called with following arguments (example)
# INVNO must be set as ENV variable
# --account "CH92 0023 5235 5662 3601 G"
# --amount 123.00
# --creditor-name "nbit Informatik GmbH"
# --creditor-street "Kirchweg 2"
# --creditor-postalcode "3510"
# --creditor-city "Konolfingen"
# --extra-infos "Rechnung Nummer ${INVNO}"
# --debtor-name "Wilhelm Tell"
# --debtor-street "Marktgasse 28"
# --debtor-postalcode "9400"
# --debtor-city "Rorschach"
# --due-date "2019-10-31"
# --language "de"
qrbill "$@" --output ${mydir}/temp/${INVNO}.svg
if [ $? -ne 0 ]; then
echo "ERROR: cannot create qrbill image"
exit 2
fi
#convert ${mydir}/temp/${INVNO}.svg ${mydir}/qr-images/${INVNO}.jpg
#inkscape ${mydir}/temp/${INVNO}.svg --export-width=794 --export-height=397 --export-filename ${mydir}/qr-images/${INVNO}.png
inkscape ${mydir}/temp/${INVNO}.svg --export-dpi=300 --export-filename ${mydir}/qr-images/${INVNO}.png
#cairosvg ${mydir}/temp/${INVNO}.svg -o ${mydir}/qr-images/${INVNO}.png
if [ $? -eq 0 ]; then
echo blabla
#rm ${mydir}/temp/${INVNO}.svg
fi

View File

@ -1,16 +0,0 @@
#!/bin/bash
mydir="$(dirname $0)"
export INVNO=123456789
${mydir}/qrbill.sh --account "CH92 0023 5235 5662 3601 G" \
--amount 123.00 \
--creditor-name "nbit Informatik GmbH" \
--creditor-street "Kirchweg 2" \
--creditor-postalcode "3510" \
--creditor-city "Konolfingen" \
--extra-infos "Rechnung Nummer 123456789" \
--debtor-name "Wilhelm Tell" \
--debtor-street "Marktgasse 28" \
--debtor-postalcode "9400" \
--debtor-city "Rorschach" \
--due-date "2019-10-31" \
--language "de"