Initial commit
This commit is contained in:
commit
6bcb596e81
|
|
@ -0,0 +1,5 @@
|
|||
wo-bisch-invoice
|
||||
makefont/makefont
|
||||
qr-images/*
|
||||
output/*
|
||||
yaml/*
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
mkinvoice will generate a PDF invoice from a YAML-file
|
||||
|
||||
It is the by-result of learning the Golang language, but with the
|
||||
purpose to generate PDF-Invoices for nbit Informatik GmbH
|
||||
|
||||
Font File can be generated with makefont
|
||||
|
||||
To generate the QRCode, following library is used:
|
||||
https://github.com/stapelberg/qrbill
|
||||
|
||||
Usage (Example):
|
||||
$ ./mkinvoice yaml/123456.yml
|
||||
|
||||
Joerg Lehmann, December 2020
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
metadata:
|
||||
invoice_nr: 123456789
|
||||
invoice_date: 11. Dezember 2020
|
||||
vat: 7.7
|
||||
account: CH92 0023 5235 5662 3601 G
|
||||
vat_number: CHE-105.687.350 MWST
|
||||
sender_address:
|
||||
name: nbit Informatik GmbH
|
||||
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
|
||||
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 2.7.2020
|
||||
quantity: 123.4
|
||||
price_per_unit: 32.10
|
||||
- text:
|
||||
- text: Monat Dezember 2020, Stunden gemäss beigelegtem Zeitnachweis
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Tp":"TrueType","Name":"DejaVuSans-Bold","Desc":{"Ascent":760,"Descent":-240,"CapHeight":760,"Flags":32,"FontBBox":{"Xmin":-1069,"Ymin":-415,"Xmax":1975,"Ymax":1175},"ItalicAngle":0,"StemV":120,"MissingWidth":600},"Up":-20,"Ut":44,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,348,456,521,838,696,1002,872,306,457,457,523,838,380,415,380,365,696,696,696,696,696,696,696,696,696,696,400,400,838,838,838,580,1000,774,762,734,830,683,683,821,837,372,372,775,637,995,837,850,733,850,770,720,682,812,774,1103,771,724,725,457,365,457,838,500,500,675,716,593,716,678,435,716,712,343,343,665,343,1042,712,687,716,716,493,595,478,712,652,924,645,652,582,712,365,712,838,600,696,600,380,435,657,1000,500,500,500,1440,720,412,1167,600,725,600,600,380,380,657,657,639,500,1000,500,1000,595,412,1094,600,582,724,348,456,696,696,636,696,365,500,500,1000,564,646,838,415,1000,500,500,838,438,438,500,736,636,380,500,438,564,646,1035,1035,1035,580,774,774,774,774,774,774,1085,734,683,683,683,683,372,372,372,372,838,837,850,850,850,850,850,838,850,812,812,812,812,724,738,719,675,675,675,675,675,675,1048,593,678,678,678,678,343,343,343,343,687,712,687,687,687,687,687,838,687,712,712,712,712,652,716,652],"Enc":"cp1252","Diff":"","File":"DejaVuSans-Bold.z","Size1":0,"Size2":0,"OriginalSize":705684,"N":0,"DiffN":0}
|
||||
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
{"Tp":"TrueType","Name":"DejaVuSans","Desc":{"Ascent":760,"Descent":-240,"CapHeight":760,"Flags":32,"FontBBox":{"Xmin":-1021,"Ymin":-463,"Xmax":1793,"Ymax":1232},"ItalicAngle":0,"StemV":70,"MissingWidth":600},"Up":-20,"Ut":44,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,318,401,460,838,636,950,780,275,390,390,500,838,318,361,318,337,636,636,636,636,636,636,636,636,636,636,337,337,838,838,838,531,1000,684,686,698,770,632,575,775,752,295,295,656,557,863,748,787,603,787,695,635,611,732,684,989,685,611,685,390,337,390,838,500,500,613,635,550,635,615,352,635,634,278,278,579,278,974,634,612,635,635,411,521,392,634,592,818,592,592,525,636,337,636,838,600,636,600,318,352,518,1000,500,500,500,1342,635,400,1070,600,685,600,600,318,318,518,518,590,500,1000,500,1000,521,400,1023,600,525,611,318,401,636,636,636,636,337,500,500,1000,471,612,838,361,1000,500,500,838,401,401,500,636,636,318,500,401,471,612,969,969,969,531,684,684,684,684,684,684,974,698,632,632,632,632,295,295,295,295,775,748,787,787,787,787,787,838,787,732,732,732,732,611,605,630,613,613,613,613,613,613,982,550,615,615,615,615,278,278,278,278,612,634,612,612,612,612,612,838,612,634,634,634,634,592,635,592],"Enc":"cp1252","Diff":"","File":"DejaVuSans.z","Size1":0,"Size2":0,"OriginalSize":757076,"N":0,"DiffN":0}
|
||||
Binary file not shown.
|
|
@ -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.
|
After Width: | Height: | Size: 9.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
|
|
@ -0,0 +1,506 @@
|
|||
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"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/stapelberg/qrbill"
|
||||
)
|
||||
|
||||
// Metadata type
|
||||
type Metadata struct {
|
||||
InvoiceNr string `yaml:"invoice_nr"`
|
||||
InvoiceDate string `yaml:"invoice_date"`
|
||||
Vat float64 `yaml:"vat"`
|
||||
Account string `yaml:"account"`
|
||||
VatNumber string `yaml:"vat_number"`
|
||||
}
|
||||
|
||||
// Address type
|
||||
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"`
|
||||
Email string `yaml:"email"`
|
||||
}
|
||||
|
||||
// InvoiceItem type
|
||||
type InvoiceItem struct {
|
||||
Text string `yaml:"text"`
|
||||
Quantity float64 `yaml:"quantity"`
|
||||
PricePerUnit float64 `yaml:"price_per_unit"`
|
||||
}
|
||||
|
||||
// InvoiceData type
|
||||
type InvoiceData struct {
|
||||
Metadata Metadata `yaml:"metadata"`
|
||||
SenderAddress Address `yaml:"sender_address"`
|
||||
BillingAddress Address `yaml:"billing_address"`
|
||||
InvoiceItems []InvoiceItem `yaml:"invoice_items"`
|
||||
}
|
||||
|
||||
/* global variable declaration */
|
||||
var pdf *gofpdf.Fpdf
|
||||
var yPos float64
|
||||
var invoiceData InvoiceData
|
||||
var progDir string
|
||||
var currentPage int
|
||||
var totalPages int
|
||||
var totalItemLines int
|
||||
var totalBruttoAmount 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 = 12
|
||||
const lineSpacing = 5
|
||||
const lineSpacingSmall = 4.5
|
||||
const lineSpacingPaymentSlipBig = 6
|
||||
const lineSpacingPaymentSlipBelowHeading = 2.8
|
||||
const lineSpacingPaymentSlipSmall = 3.175
|
||||
const addressTop = 50
|
||||
const metadataTopFirstPage = 70
|
||||
const metadataTopNotFirstPage = 30
|
||||
const tabstopLeft = 20
|
||||
const tabstopLeftAlt = 40
|
||||
const tabstopMetadata = 60
|
||||
const tabstopAddress = 120
|
||||
const tabstopLogo = 135
|
||||
const tabstopRight = 200
|
||||
const widthItemText = 96
|
||||
const widthQuantity = 28
|
||||
const widthPricePerUnit = 28
|
||||
const widthPrice = 28
|
||||
const tabstopQuantity = tabstopRight - widthPrice - widthPricePerUnit - widthQuantity
|
||||
const tabstopPricePerUnit = tabstopRight - widthPrice - widthPricePerUnit
|
||||
const tabstopPrice = tabstopRight - widthPrice
|
||||
const itemsTopFirstPage = 105
|
||||
const itemsTopNotFirstPage = 50
|
||||
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, sep string) string {
|
||||
p := message.NewPrinter(language.English)
|
||||
s := strings.ReplaceAll(p.Sprintf("%.2f", f), ",", sep)
|
||||
//fmt.Printf("--- s: @%s@\n", s)
|
||||
return s
|
||||
}
|
||||
|
||||
func readInvoiceData(filename string) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
//fmt.Printf("File contents: %s", data)
|
||||
|
||||
err = yaml.Unmarshal([]byte(data), &invoiceData)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %v", err)
|
||||
}
|
||||
|
||||
//fmt.Printf("--- t:\n%v\n\n", invoiceData)
|
||||
}
|
||||
|
||||
func writeText(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, 11, 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")
|
||||
pdf.SetFont("Dejavusans", "", defaultFontSize)
|
||||
currentPage = 0
|
||||
}
|
||||
|
||||
func printPageHeader(firstPage bool) {
|
||||
var opt gofpdf.ImageOptions
|
||||
|
||||
currentPage = currentPage + 1
|
||||
pdf.AddPage()
|
||||
yPos = marginTop
|
||||
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
|
||||
writeText(tabstopLeft, yPos, 0, invoiceData.SenderAddress.Name)
|
||||
pdf.SetFont("Dejavusans", "", defaultFontSize)
|
||||
yPos = yPos + lineSpacing
|
||||
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 {
|
||||
yPos = yPos + lineSpacing
|
||||
yPos = yPos + lineSpacing
|
||||
writeText(tabstopLeft, yPos, 0, "Telefon")
|
||||
writeText(tabstopLeftAlt, yPos, 0, invoiceData.SenderAddress.TelNo)
|
||||
yPos = yPos + lineSpacing
|
||||
writeText(tabstopLeft, yPos, 0, "E-Mail")
|
||||
writeText(tabstopLeftAlt, yPos, 0, invoiceData.SenderAddress.Email)
|
||||
yPos = yPos + lineSpacing
|
||||
writeText(tabstopLeft, yPos, 0, "MwSt. Nr.")
|
||||
writeText(tabstopLeftAlt, yPos, 0, invoiceData.Metadata.VatNumber)
|
||||
}
|
||||
|
||||
opt.ImageType = "png"
|
||||
opt.ReadDpi = true
|
||||
pdf.ImageOptions("logos/wo-bisch-logo-mit-schrift-12mm-400dpi.png", tabstopLogo, logoTop, 0, logoHeight, false, opt, 0, "")
|
||||
pdf.SetFont("Dejavusans", "", defaultFontSize)
|
||||
}
|
||||
|
||||
func printAddress() {
|
||||
yPos = addressTop
|
||||
writeText(tabstopAddress, yPos, 0, invoiceData.BillingAddress.Name)
|
||||
yPos = yPos + lineSpacing
|
||||
writeText(tabstopAddress, yPos, 0, invoiceData.BillingAddress.Street+" "+invoiceData.BillingAddress.StreetNumber)
|
||||
yPos = yPos + lineSpacing
|
||||
writeText(tabstopAddress, yPos, 0, invoiceData.BillingAddress.Zip+" "+invoiceData.BillingAddress.City)
|
||||
}
|
||||
|
||||
func printMetadata(firstPage bool) {
|
||||
yPos = metadataTopNotFirstPage
|
||||
if firstPage {
|
||||
yPos = metadataTopFirstPage
|
||||
pdf.SetFont("Dejavusans-Bold", "", 16)
|
||||
writeText(tabstopLeft, yPos, 0, "Rechnung")
|
||||
yPos = yPos + lineSpacing
|
||||
yPos = yPos + lineSpacing
|
||||
}
|
||||
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
|
||||
writeText(tabstopLeft, yPos, 0, "Rechnungsnummer:")
|
||||
pdf.SetFont("Dejavusans", "", defaultFontSize)
|
||||
writeText(tabstopMetadata, yPos, 0, invoiceData.Metadata.InvoiceNr)
|
||||
yPos = yPos + lineSpacing
|
||||
if firstPage {
|
||||
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
|
||||
writeText(tabstopLeft, yPos, 0, "Rechnungsdatum:")
|
||||
pdf.SetFont("Dejavusans", "", defaultFontSize)
|
||||
writeText(tabstopMetadata, yPos, 0, invoiceData.Metadata.InvoiceDate)
|
||||
yPos = yPos + lineSpacing
|
||||
}
|
||||
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
|
||||
writeText(tabstopLeft, yPos, 0, "Seite:")
|
||||
pdf.SetFont("Dejavusans", "", defaultFontSize)
|
||||
writeText(tabstopMetadata, yPos, 0, fmt.Sprintf("%d von %d", currentPage, totalPages))
|
||||
}
|
||||
|
||||
func printItemsHeader() {
|
||||
if currentPage == 1 {
|
||||
yPos = itemsTopFirstPage
|
||||
} else {
|
||||
yPos = itemsTopNotFirstPage
|
||||
}
|
||||
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
|
||||
writeText(tabstopLeft, yPos, 0, "Bezeichnung")
|
||||
writeText(tabstopQuantity, yPos, widthQuantity, "Menge", "TR")
|
||||
writeText(tabstopPricePerUnit, yPos, widthPricePerUnit, "Einheitspreis", "TR")
|
||||
writeText(tabstopPrice, yPos, widthPrice, "Preis", "TR")
|
||||
pdf.SetFont("Dejavusans", "", defaultFontSize)
|
||||
yPos = yPos + lineSpacing
|
||||
pdf.Line(tabstopLeft, yPos, tabstopRight, yPos)
|
||||
yPos = yPos + lineSpacing
|
||||
}
|
||||
|
||||
func maybeNewPage() {
|
||||
if yPos > maxYPos {
|
||||
printPageHeader(false)
|
||||
printMetadata(false)
|
||||
printItemsHeader()
|
||||
}
|
||||
}
|
||||
|
||||
func printItems() {
|
||||
for _, i := range invoiceData.InvoiceItems {
|
||||
if i.Quantity != 0 {
|
||||
writeText(tabstopQuantity, yPos, widthQuantity, fmt.Sprintf("%g", i.Quantity), "TR")
|
||||
writeText(tabstopPricePerUnit, yPos, widthPricePerUnit, fmt.Sprintf("%.2f", i.PricePerUnit), "TR")
|
||||
itemBruttoAmount := round5rappen(i.Quantity * i.PricePerUnit)
|
||||
totalBruttoAmount = totalBruttoAmount + itemBruttoAmount
|
||||
writeText(tabstopPrice, yPos, widthPrice, floatToString(itemBruttoAmount, "'"), "TR")
|
||||
}
|
||||
if i.Text != "" {
|
||||
lines := pdf.SplitText(i.Text, widthItemText)
|
||||
for _, il := range lines {
|
||||
totalItemLines = totalItemLines + 1
|
||||
writeText(tabstopLeft, yPos, 0, strings.ReplaceAll(il, "_", " "))
|
||||
yPos = yPos + lineSpacing
|
||||
maybeNewPage()
|
||||
}
|
||||
} else {
|
||||
totalItemLines = totalItemLines + 1
|
||||
yPos = yPos + lineSpacing
|
||||
maybeNewPage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printTotals() {
|
||||
yPos = totalsTop
|
||||
pdf.Line(tabstopRight-widthPrice, yPos+(lineSpacing/2), tabstopRight, yPos+(lineSpacing/2))
|
||||
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
|
||||
yPos = yPos + lineSpacing
|
||||
writeText(tabstopLeft, yPos, 0, "Summe (inkl. MwSt.)")
|
||||
pdf.SetFont("Dejavusans", "", defaultFontSize)
|
||||
writeText(tabstopPrice, yPos, widthPrice, floatToString(totalBruttoAmount, "'"), "TR")
|
||||
yPos = yPos + lineSpacing
|
||||
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
|
||||
writeText(tabstopLeft, yPos, 0, fmt.Sprintf("davon MwSt. %.1f%%", invoiceData.Metadata.Vat))
|
||||
totalNettoAmount := totalBruttoAmount / (1 + (invoiceData.Metadata.Vat / 100))
|
||||
mwstAmount := round5rappen(totalBruttoAmount - totalNettoAmount)
|
||||
pdf.SetFont("Dejavusans", "", defaultFontSize)
|
||||
writeText(tabstopPrice, yPos, widthPrice, floatToString(mwstAmount, "'"), "TR")
|
||||
yPos = yPos + lineSpacing
|
||||
yPos = yPos + lineSpacing
|
||||
pdf.SetFont("Dejavusans-Bold", "", defaultFontSize)
|
||||
writeText(tabstopLeft, yPos, 0, "Rechnungbetrag in CHF")
|
||||
totalInvoiceAmount = totalBruttoAmount
|
||||
writeText(tabstopPrice, yPos, widthPrice, floatToString(totalBruttoAmount, "'"), "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
|
||||
|
||||
var b []byte
|
||||
var err error
|
||||
qrch := qrchFromInvoiceData(invoiceData)
|
||||
bill, err := qrch.Encode()
|
||||
|
||||
code, err := bill.EncodeToImage()
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
opt.ImageType = "png"
|
||||
opt.ReadDpi = false
|
||||
|
||||
pdf.ImageOptions("qr-images/"+invoiceData.Metadata.InvoiceNr+".png", tabstopPaymentSlipMiddle-2, paymentSlipTop+13, 56, 56, false, opt, 0, "")
|
||||
}
|
||||
|
||||
func CreateInvoice() {
|
||||
totalBruttoAmount = 0
|
||||
totalItemLines = 0
|
||||
readInvoiceData(os.Args[1])
|
||||
setupInvoice()
|
||||
printPageHeader(true)
|
||||
printAddress()
|
||||
printMetadata(true)
|
||||
printItemsHeader()
|
||||
printItems()
|
||||
printTotals()
|
||||
printPaymentSlip()
|
||||
printQR()
|
||||
}
|
||||
|
||||
func main() {
|
||||
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
progDir = dir
|
||||
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Printf("usage: mkinvoice <yaml-file>\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// First Run to get total number of pages
|
||||
CreateInvoice()
|
||||
totalPages = pdf.PageNo()
|
||||
fmt.Printf("Total Pages is: %d\n", totalPages)
|
||||
fmt.Printf("Total Item Lines is: %d\n", totalItemLines)
|
||||
|
||||
// Second Run
|
||||
CreateInvoice()
|
||||
|
||||
err = pdf.OutputFileAndClose(filepath.Join(progDir, "output", invoiceData.Metadata.InvoiceNr+".pdf"))
|
||||
if err == nil {
|
||||
fmt.Printf("Successfully created invoice\n")
|
||||
} else {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jung-kurt/gofpdf"
|
||||
)
|
||||
|
||||
func main() {
|
||||
pdf := gofpdf.New("P", "mm", "A4", "")
|
||||
|
||||
pdf.SetFontLocation("fonts")
|
||||
errs := gofpdf.MakeFont("/usr/share/fonts/dejavu/DejaVuSans.ttf", "/home/joerg/go/src/github.com/jung-kurt/gofpdf/font/cp1252.map", "fonts", nil, true)
|
||||
if errs != nil {
|
||||
fmt.Println(errs)
|
||||
}
|
||||
errs2 := gofpdf.MakeFont("/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf", "/home/joerg/go/src/github.com/jung-kurt/gofpdf/font/cp1252.map", "fonts", nil, true)
|
||||
if errs2 != nil {
|
||||
fmt.Println(errs)
|
||||
}
|
||||
pdf.AddFont("DejaVuSans", "", "DejaVuSans.json")
|
||||
pdf.AddFont("DejaVuSans-Bold", "", "DejaVuSans-Bold.json")
|
||||
}
|
||||
Loading…
Reference in New Issue