From 24e895102b3c36f1839f9e31f01a6a4c56316a01 Mon Sep 17 00:00:00 2001 From: Joerg Lehmann Date: Sat, 27 Jun 2020 21:16:26 +0200 Subject: [PATCH] first workable version, still to be tuned... --- 6060900045.yml | 26 ++++ README.md | 8 + logos/nbit-logo-40x20mm-400dpi.png | Bin 0 -> 12863 bytes mkinvoice.go | 230 +++++++++++++++++++++-------- qrbill.sh | 7 +- 5 files changed, 210 insertions(+), 61 deletions(-) create mode 100644 6060900045.yml create mode 100644 logos/nbit-logo-40x20mm-400dpi.png diff --git a/6060900045.yml b/6060900045.yml new file mode 100644 index 0000000..75977e6 --- /dev/null +++ b/6060900045.yml @@ -0,0 +1,26 @@ +--- +metadata: + invoice_nr: 6060900045 + invoice_info: Rechnung Nummer 6060900045 + invoice_date: 2. April 2020 + vat: 7.7 + account: CH92 0023 5235 5662 3601 G + due_date: 2019-10-31 +sender_address: + name: nbit Informatik GmbH + street: Kirchweg 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 + 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: + - text: Monat Mai 2020, Stunden gemäss beigelegtem Zeitnachweis diff --git a/README.md b/README.md index 41e8eaf..54e5d5a 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,12 @@ 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 + +Usage (Example): +$ ./mkinvoice yaml/123456.yml + Joerg Lehmann, April 2020 diff --git a/logos/nbit-logo-40x20mm-400dpi.png b/logos/nbit-logo-40x20mm-400dpi.png new file mode 100644 index 0000000000000000000000000000000000000000..7e3dd6d43e3ad1661de7e96cfd59bc71d21f36a9 GIT binary patch literal 12863 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 0 { align = alignStr[0] } pdf.SetXY(x, y) - pdf.CellFormat(0, 11, tr(text), "", 0, align, false, 0, "") + pdf.CellFormat(w, 11, tr(text), "", 0, align, false, 0, "") } func setupInvoice() { @@ -101,46 +129,115 @@ func setupInvoice() { pdf.SetFontLocation("fonts") pdf.AddFont("Dejavusans", "", "DejaVuSans.json") pdf.AddFont("Dejavusans-Bold", "", "DejaVuSans-Bold.json") - pdf.SetFont("Dejavusans", "", 10) + 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", "", 10) - writeText(tabstopLeft, yPos, "nbit Informatik GmbH") - pdf.SetFont("Dejavusans", "", 10) - yPos = yPos + lineSpacing - writeText(tabstopLeft, yPos, "Kirchweg 2") - yPos = yPos + lineSpacing - writeText(tabstopLeft, yPos, "3510 Konolfingen") - yPos = yPos + lineSpacing - yPos = yPos + lineSpacing - writeText(tabstopLeft, yPos, "Tel.") - writeText(tabstopLeftAlt, yPos, "+41 31 792 00 40") - yPos = yPos + lineSpacing - writeText(tabstopLeft, yPos, "EMail") - writeText(tabstopLeftAlt, yPos, "joerg.lehmann@nbit.ch") - yPos = yPos + lineSpacing + pdf.SetFont("Dejavusans-Bold", "", fontSizeSmall) + writeText(tabstopLeft, yPos, 0, "nbit Informatik GmbH") + pdf.SetFont("Dejavusans", "", fontSizeSmall) + yPos = yPos + lineSpacingSmall + writeText(tabstopLeft, yPos, 0, "Kirchweg 2") + yPos = yPos + lineSpacingSmall + writeText(tabstopLeft, yPos, 0, "3510 Konolfingen") + yPos = yPos + lineSpacingSmall + yPos = yPos + lineSpacingSmall + writeText(tabstopLeft, yPos, 0, "Tel.") + writeText(tabstopLeftAlt, yPos, 0, "+41 31 792 00 40") + yPos = yPos + lineSpacingSmall + writeText(tabstopLeft, yPos, 0, "EMail") + writeText(tabstopLeftAlt, yPos, 0, "joerg.lehmann@nbit.ch") + yPos = yPos + lineSpacingSmall opt.ImageType = "png" opt.ReadDpi = true - pdf.ImageOptions("logos/nbit-logo.png", tabstopLogo, logoTop, 0, logoHeight, false, opt, 0, "") + pdf.ImageOptions("logos/nbit-logo-40x20mm-400dpi.png", tabstopLogo, logoTop, 0, logoHeight, false, opt, 0, "") + pdf.SetFont("Dejavusans", "", defaultFontSize) } func printAddress() { - fmt.Printf("Blabla: %s\n", invoiceData.BillingAddress.Name) yPos = addressTop - writeText(tabstopAddress, yPos, invoiceData.BillingAddress.Name) + writeText(tabstopAddress, yPos, 0, invoiceData.BillingAddress.Name) yPos = yPos + lineSpacing - writeText(tabstopAddress, yPos, invoiceData.BillingAddress.Street) - yPos = yPos + lineSpacing - writeText(tabstopAddress, yPos, invoiceData.BillingAddress.Zip+" "+invoiceData.BillingAddress.City) + writeText(tabstopAddress, yPos, 0, invoiceData.BillingAddress.Street) yPos = yPos + lineSpacing + writeText(tabstopAddress, yPos, 0, invoiceData.BillingAddress.Zip+" "+invoiceData.BillingAddress.City) +} - pdf.Line(tabstopLeft, line1Top, tabstopRight, line1Top) +func printMetadataFirstPage() { + yPos = metadataTop + 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 + pdf.SetFont("Dejavusans-Bold", "", defaultFontSize) + writeText(tabstopLeft, yPos, 0, "Rechnungsdatum:") + pdf.SetFont("Dejavusans", "", defaultFontSize) + writeText(tabstopMetadata, yPos, 0, invoiceData.Metadata.InvoiceDate) +} + +func printItemsHeader() { + yPos = itemsTop + 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 printItems() { + for _, i := range invoiceData.InvoiceItems { + if i.Quantity != 0 { + writeText(tabstopQuantity, yPos, widthQuantity, fmt.Sprintf("%.1f", 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") + } + if i.Text != "" { + lines := pdf.SplitText(i.Text, widthItemText) + for _, il := range lines { + writeText(tabstopLeft, yPos, 0, strings.ReplaceAll(il, "_", " ")) + yPos = yPos + lineSpacing + } + } else { + yPos = yPos + lineSpacing + } + } +} + +func printTotals() { + yPos = totalsTop + pdf.Line(tabstopRight-widthPrice, yPos, tabstopRight, yPos) + pdf.SetFont("Dejavusans-Bold", "", defaultFontSize) + yPos = yPos + lineSpacing + writeText(tabstopLeft, yPos, 0, "Netto Betrag") + writeText(tabstopPrice, yPos, widthPrice, floatToString(totalNetAmount), "TR") + yPos = yPos + lineSpacing + yPos = yPos + lineSpacing + 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") + yPos = yPos + lineSpacing + yPos = yPos + lineSpacing + writeText(tabstopLeft, yPos, 0, "Total Betrag sFr.") + totalInvoiceAmount = totalNetAmount + mwstAmount + writeText(tabstopPrice, yPos, widthPrice, floatToString(totalNetAmount+mwstAmount), "TR") } func printQR() { @@ -148,7 +245,7 @@ func printQR() { cmd := exec.Command(filepath.Join(progDir, "qrbill.sh"), "--account", invoiceData.Metadata.Account, - "--amount", "123.00", + "--amount", floatToString(totalInvoiceAmount), "--creditor-name", invoiceData.SenderAddress.Name, "--creditor-street", invoiceData.SenderAddress.Street, "--creditor-postalcode", invoiceData.SenderAddress.Zip, @@ -170,9 +267,22 @@ func printQR() { } fmt.Printf("%s\n", stdoutStderr) - opt.ImageType = "jpeg" + opt.ImageType = "png" opt.ReadDpi = true - pdf.ImageOptions("qr-images/"+invoiceData.Metadata.InvoiceNr+".jpg", 0, 200, 0, 0, false, opt, 0, "") + pdf.ImageOptions("qr-images/"+invoiceData.Metadata.InvoiceNr+".png", 0, 200, 0, 0, false, opt, 0, "") +} + +func CreateInvoice() { + totalNetAmount = 0 + readInvoiceData(os.Args[1]) + setupInvoice() + printPageHeader(true) + printAddress() + printMetadataFirstPage() + printItemsHeader() + printItems() + printTotals() + printQR() } func main() { @@ -187,11 +297,13 @@ func main() { os.Exit(1) } - readInvoiceData(os.Args[1]) - setupInvoice() - printPageHeader(true) - printAddress() - printQR() + // First Run to get total number of pages + CreateInvoice() + totalPages = pdf.PageNo() + fmt.Printf("Total Pages is: %d\n", totalPages) + + // Second Run + CreateInvoice() err = pdf.OutputFileAndClose(filepath.Join(progDir, "output", invoiceData.Metadata.InvoiceNr+".pdf")) if err == nil { diff --git a/qrbill.sh b/qrbill.sh index de90492..3098de0 100755 --- a/qrbill.sh +++ b/qrbill.sh @@ -30,7 +30,10 @@ if [ $? -ne 0 ]; then exit 2 fi -convert ${mydir}/temp/${INVNO}.svg ${mydir}/qr-images/${INVNO}.jpg +#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 +#cairosvg ${mydir}/temp/${INVNO}.svg -o ${mydir}/qr-images/${INVNO}.png if [ $? -eq 0 ]; then - rm ${mydir}/temp/${INVNO}.svg + echo blabla + #rm ${mydir}/temp/${INVNO}.svg fi