From 0ce37e9aa937ae2adbf4515c7fd30a2978b55f00 Mon Sep 17 00:00:00 2001 From: Joerg Lehmann Date: Mon, 28 Dec 2020 21:24:23 +0100 Subject: [PATCH] pure golang version --- README.md | 8 +- logos/Scissors_icon_black.svg | 37 ++++++ logos/nbit-logo.png | Bin 5344 -> 0 bytes logos/scissors-rotated.png | Bin 0 -> 10051 bytes logos/scissors.png | Bin 0 -> 8122 bytes mkinvoice.go | 231 ++++++++++++++++++++++++++++------ qrbill.sh | 39 ------ testqrbill.sh | 16 --- 8 files changed, 232 insertions(+), 99 deletions(-) create mode 100644 logos/Scissors_icon_black.svg delete mode 100644 logos/nbit-logo.png create mode 100644 logos/scissors-rotated.png create mode 100644 logos/scissors.png delete mode 100755 qrbill.sh delete mode 100755 testqrbill.sh diff --git a/README.md b/README.md index 54e5d5a..fdfb729 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/logos/Scissors_icon_black.svg b/logos/Scissors_icon_black.svg new file mode 100644 index 0000000..1be188c --- /dev/null +++ b/logos/Scissors_icon_black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + diff --git a/logos/nbit-logo.png b/logos/nbit-logo.png deleted file mode 100644 index f171c3a903660d02c0982f18d448897d75b76ec8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5344 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

F|i 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 T6))}r9G<$0j&ijU?9KlG>+f%u literal 0 HcmV?d00001 diff --git a/logos/scissors.png b/logos/scissors.png new file mode 100644 index 0000000000000000000000000000000000000000..abc2ca9c7e8265f8531f3372e4c633590643a7e8 GIT binary patch literal 8122 zcmXYWbzIZm_y2+rkk}|`855*MV$uSmOF-rnkkLv=m*k{IODMdhq?M3OT57bQlmbq2 zAc%BG2?*c$`ThQQ#pB-dI_IAAdft1_eV+I`Cc2FD-1GndfKgu$X$}AYGbrynmuM*W zUWRx<%7e~J@4hbppc?(}0!~!b_)!|U{It=27M{+2fp$JlfWW{&smD*;d>!n(oTNN` zTynNlxB&nGU45j+-Jsmf*}z=O-aq}jBOK4?kVv4}7#eRE-`4YMqz5XGN@|14aM*`) z8zu;kAsyt#kjZ^L9P)?HB*#cZ0i@X-AR*99DPeqe`KaF)A>kExG$-pjoVyp+@o>b= z$!R-mSn<hqHfL4Brelfkh)hHi;+@ySzeD6eHK0_mDWLey3JjJWtQ9E$z5u?8ET!uQ zk?kqO$0KPS;Awik;B81+ z7Z1Q1EFl~!V&>6G&%1PyiNooYo&d5yzv<>hoBAw>9O0&^I9#2}tsSlfWnjS=z?xxy zFDM_0EJ>2bhS1IL^m7RSY=8TAA(4*3ao_^hc7iF4n;_WwlR5(iTYMwrb!~y?m}OT5 zAA2gw1`%xl60dY2)&zfOVY1ibDGe2jkw=&2v&#j40#B9mj3H^Dsr0ji032&PXG@(!5ClBrT+lLOEZ(TbDDs29CRk~ zE?#7+Gd4kP(XX?u2`xQHoz?gYXks|NiEYgSB}V>*Es4X-FGXQf<7*)KDHB^${BM#4 zFW!-Cy1}IJxm(Ja!Ni|3yiqv4vIC$nt7*3~O$&H6lQc$*B{KF{NpIM^_B5x3!_ouf zN#()w$(xl#H9CG>MK^FDt8LsKd{ppl6a=DQ`*nAkI1EVXzK}|gI_%=#>K`k#em-M| z!DF;Q zhQaSJqp7Cj5up8r-=d5nOT@?>XK>s9I+6^%J>@PC zyH!69EY4>0a&x@4V6gc0l#tD8mydl_)vn_p<{#y>D@OHZ@ZKAe(wV|{6t4E4)hKV&2PL~R4v$IA%b$im=+24I^!8-B)33rxn zsy+;d|H+J+qB|O18t$p(fW(`r-k@q!ofY_=hQYpnW3?KcDc|?+ zT?R9f-X|-Pey=viJKrmOiec#gcKv}1NI_`blu2EkW%_IBn5hyh-MpUofv7`NlC-zM zFjoD36A)2eN^~Kmyz>NaXSSR4sr{1LO$%b(^MmZDd=Ap@xo+%JkU zA}b?TB1gOhIBJABGUtwNlJC5>t7PV_ zk3U&nx~Tvz`zr>26>8t5%0dj$Wlii%O(WraC~6b%LwINuzY=$32Xx^-1IdXI?a;Tk~ zT1C9&P#!a@N;#AbY;MV3m1KZCZjjnxaKCioO;*0W@|tT|%ha8;9^&!0R2O%?6yaqQ z@6zj4RM27rUJ_5&wZS?>mhaXE=M`;UZ{f069ose{23!uEUs$Pq*VsK0{!@}6ZSieq z#U0)_RpiKN!@}c#h>y_te3%Uv`D%z>EQH50#30B#3=)+)J91Jrd0gb_HUGEsS{ zo7FzrpFFp8_owp+bl37xjnK_j_CHTdN*?6l1(zf2Exojue_xk)g*W-zaiZLc7Ha4; zG)sm}1&i;^=IWKU(_1HpftY5!#84^Hk?OTwH`ADTAMW@iyyeK&sPjGi3iy-Ys1ZRn z!ax^z47LAUVaUDYCmsmg4hUMv`qYL2x@+wFHdyVgCX}Fb$v1-bM8XEvMuj0Rov!j1Hr3~@`inT2%g-IBSz zTTxz)h$dV-J@P?S-9SB>eQz|mv?#PS<%QLQ5*tMD`x zF3=gY@)yg8!n>KN(%2mQu$p&qrBQ?ROt#+_y6^dqEcGKeVfWLn_<@)@2U{vNJ)b&2 z132Hi{`r)hz!$X8RL`x$cJWmw{9%J}(+F;$;=8|1$hKPSn8v^}* ziQI%QqrtnZMR{@CaeAnpC=K*#0fKJ+j@W}#GjwOK)Uvb#x_ieW&llnq)YaT?C$iD3 zTi>sqLjgcSNu=1mvrri@D=p21fcUZUng^S~Iy_sBMHYxexJ8C)7m+xcXe@sj`^teX zKup&hGICMHvWzBb>*|I=vvaRd*${aSIv@(ReP^Dnw1&2JG_ zO#)f|8q#cyABrAYE{m!J2`nB(O9G`pAB2N=Urf=8vu^*mWvX{8Fzw~tLIe*1(8NgM z%Zbf%vjYF#jex3yQiS_XZdm@JvyPjOL66*tv}MwN@znj%(i4jK=C0VGwY<32#T>Fa z(@I~3c|#*Rwvk`li1VJ)*CMu#O#DBcyVPE)c$+_;(7ahJ+wu9)l6&GUjd+(?>{mT! zjmd-CHB>433EJ#WYgsRVCS6l}ow3DF;ODIBv`zFRhK|H@CEz^hiQvgkl$nF@4g$f?*@R+AhK2*Zl=)XW%_h&WpBmO}?GL^p>l?BWdw5snd1^eH8=#wO>Z&43!2Q z5OxJ{-REf&mFe^qqdCcsK>1z#3~UuA+T$*4{v?s&Zk3FY9o{bprhh zk>EjXBi0?78-;8c1Q-477v}{_R*Vfy>p@jQ`ip-Gkza8WH(L0o)x-2jeqDRJ`Yu#n zimTakIWCFVMtq8sPhyP)-qv-1lCq@{A{PIrL>#CQ;j{iPZVu%CGY#%5%#1o~@pw^E;^gUg-x`WbCx0H_=dk!VnjDoZM#X4_EwI7Iqw{5WQt_9-8MOT?wn^#0=m zrb&Wapwx)N!7KPfH%vFW-(H{<-o^n4v0)7oi48HOtl4a473>7p^h@5&8nT&fmF9L(m#wt+sSG!dkI{1b>X!!jNZTJZtt~L? z&z%*}(5&38mOFwJjH8T1v`)(mPd@<5LDJCkEW-u^YaHy0vS*}@k+$VbE{OKv*0(iB_ht^x<`(_P4pI3~}B)8fp~qPjpHZ`>5n zGpO&Ltfucc+fP(Q4;5vBR-oFFc(4~6s>w9b=W8`(F`i!9QhP1vuGE`eHteWTHR`xKlO!cVyz*!$ z^FT5Y?{V#2zd^e+V@`^mf?t2WMlN`UIaKFZ=CQyoSHnxE$VOSqJZ=mpnP!7YZvW4n zIssO~;`_>%&G|6%Qf_ypAe=0e3N?i0kPQh$bhZIlj_ z(e5cwBkg?dcIRsPg|OG z3$OHs`C>RWM9=!z+(AhB3}GakRsrzvTBuOT$0rh0TU3X)OCKTWt9cseRsD)fKBAIn zO*Br+67?A5iPX`auh~{pnu+!Bd*?dHo32!1ibZI}K08@piy+Lr@CV_=Kq1c!ZBjOq zb&7|j1PnSL`bJCCUJg`DXnD?lL<5DyFU3WB&ae&ih@j=Mf;e0cnZ66*mk^}6Y>b4< z_>&%r7V@aYbR4s^Fx+Q=q){CJgdy#ayGUk93AVJmSLi>BgciSfBPDD<(q8BJ0awzu z8tuu;zS$4*vIXT&n!}JEcOjG#LjNiE*`dv^ zlqRFP5MuPbj_ziT5lFb~8g?600BwiFb2Ae4FwB_iUcem`{hoP~?LCS%M-^h+ag}KuEST@B_)SEwYA?tS`Sj-$$yi`WtcKxWPZ3oUoPkvewl;bgi zzfK`_AIL;|UM@lkeTseW$~&wjf>9?sV;<6{7(!GjaY}z(kNrKcb!X(X^vsH9&}WR* zD)WzN-KN*115LwqA8}+)jt21(M(+EJZ^`o9(n0=+vD*oT74rV+Fxw~1+D;^FEooxp z1AwP*(AD8s*o$d_2V0NzB@q6Mt{@?*FELuhw7|-%8cj+1hOMsnz9$wKHw=PY>3AgY zNrkLN)**{lLG&_=BNs~1B3@F^StD|QJG8Hf*vw`yrko)~Jd516O$<{KNMgLtNTmQ! z=$@%~59k1f~C$BPJ z?|pFF^EK@upuuHzWLeJ6LZ1gaLRf2NFt})I+V|I3J@qx5KednfmTmPkkLh;fmm?)R zEek$_?b*uxdW1s2e8>L9p7Z5N>+J9)&!lKP?kc=_TdNYLFTVKb9Z zcl$|4rX{|rwjn-SP0UL*a|{fy87mG}AayzhK1H0;Vn=mmtRz9~hMEwwu z-<_|o8fToEiC@E~EI)E*G;D0R_)s#+<}NwGNYYE}`Gp~~c166pZXnV5bNGVp78Kp_ z*AOl`%6{va6T_P31;1#;Mc@&Of{``pJH~tVzsp_Rb)LQZC-#-QA|TetEJIsc7|%7s z68|tfvt2jV245&MkaC|g_CrmwHI!Y~#Ruxe^}R;?g>V6(s{7HF${f-8p?)|cfT>&Q zWi@yOHjxwUFBZ{%$TcWmkf$#~P9?_k+t%XB>_-${IY1Q{^b$O7c-?9k_!ettktQZ@ zH|;VHnn~7So~lzje5|-na+wkd(~ z;UiKTG1EfP=Tz_BsBlNdiSD@Lh45jjiz)*JfGh_m)zG*72J!QWl4QfhEGHS14GKK3p%JYNv6v+*Zc?~xnEf42|XUU>H||4=t3 z&VD4?*G_0d;q%eJE37I5olRkb@{ZdX)4Z|X{w1WCEY-2-;VQz<;q4n0ADPROSwR)~ zbGj!6DxUGxQb*pQ%p$ohdi4rmDVjN<(0@Y0Ky~4$Zp;z+2>H2_f3e&WQB~}#Ivkg7 zVhu0i#^_eDeQ}BQlwg0MT^8G!(!Jf;5w*GdJfNQVqKR*PiNczB-(%j0sgA;hJWJ@Z z{S10X(_pijP)1?DR*scPV~%dj$=vRJ_r`zNB&}uYH~52?x^zT1{{LR4|NREZ;#j$< z<>Swy+z0M92ID`}u2Kjn`!Uu^=EEzmTytMM0`PZE@>BEBOxGk+RVK&bEkE3!V4NOHbMDeQPo|%rLZ;{7CIp)n0kv|HTth#T$ z+F>j!dY=ul22{0unb_Zeo6>dYwO1^h=RR6lIA$40pt`}<#1fSAPwX$w|4Nn64j~Ti zl5x8x3qOo5U34AME)1|??G&zYr?tjf_A<^s5)*T)tr4|BNT=*%k~G?(2LCzW0@QA{ zxmxyU@Zo7u%^?a4JjRqHpX(M+|98wtu-=ku7M1xs@ED3=p5^Mjw7UD$IWxYpVp#<- zkEr-*Q_2Qi=5s=Vem8z38W`zb7#^%RsiAr;kw$^!>+aNnGgc3-&lFeK;oVeP7dl_G zn)MxkU*7rKtf)v~Mz}^fPmgvb1~%nOaQroP-5Bto&1dczf!0*v(1ZCB739H+i~DmU z?e|VB4O#t$lzo%kLl=x1@lfpMwXLHg+4XV`5w=-3RXo@ztV7ZRo>MB77)o+vzZ_I> zvo>JFH*`!F7226=mH237b&(RUC87ceH77tx!7S0b?@gK+mOni8zwnWMCGO1BduOdUr0 z<{xiLU+%9i=Z0FXt#TZ+{`|?ST8c+BvfTw8CJ`? zs(U)L+zLX0A#^Tl;PK>j{cz4DJKwc^9G9=;8g zTHrZ^J20UlS5|#X)6s00Y z@SVtxNCBFU_mD-a@tdnM>5tgEzh3brEF7NmkFfq061LbsfLcNSY7Wy=#@w{doCN4& zY1hI*9kAPWrvpKp{^CO`i+aLZOd==LHk8=~*>$ra^OET(n^z!Pb#DmibA7UdB7$RQw)RX%e2F_u<3ohzM|303GD{b`Pf!o_?K*RP1pe379w0Xw>I} zd0)b%cSbq=KMC8g+emm|d$N^QFnUi^q)a_L;Nhv^m-s{+yjm6& zdBasp5sVh1lvKQ>xA+FkF)bymoDjMN?iub+x*zAgzNHOC}3@`j|{Y1iEM2+jGC z74O1VV2lkuUNy2VXG^3M!1QK#hA9P;sg4Ok7%0>az76I}|5x{^n)UWn;lVnv4o{co z6NAqD;D!T-%`A}k4XzoUQDHKt<2ILpI)?XLDV?sWRqrmcgr%Y9?{|IQ85%!Bg})ya zsOE^ax?iF<86;um=x;x2%);XK?+vQ*Hi`k37pIV^6xU)xCCAb!&xvPjIBMO# zxlbuX_ek5x*PSO1DX~Hzx{ue4P~)`gcNIEg^G_O&r&3>3a!^8OVj|t^LKH#M?$I}d iJ#m0ipvyLN0p7m%{1|LnZ$YWM1N5~`knc1x&;AdETt{I5 literal 0 HcmV?d00001 diff --git a/mkinvoice.go b/mkinvoice.go index 30ee6ff..7564c3d 100644 --- a/mkinvoice.go +++ b/mkinvoice.go @@ -1,18 +1,21 @@ 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 @@ -26,12 +29,13 @@ type Metadata struct { // Address type type Address struct { - Name string `yaml:"name"` - Street string `yaml:"street"` - Zip string `yaml:"zip"` - City string `yaml:"city"` - TelNo string `yaml:"tel_no"` - Email string `yaml:"email"` + 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 @@ -62,11 +66,17 @@ 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 = 4.5 +const lineSpacingPaymentSlipBig = 6 +const lineSpacingPaymentSlipBelowHeading = 2.8 +const lineSpacingPaymentSlipSmall = 3.175 const addressTop = 50 const metadataTopFirstPage = 70 const metadataTopNotFirstPage = 30 @@ -87,14 +97,26 @@ const itemsTopFirstPage = 105 const itemsTopNotFirstPage = 50 const totalsTop = 158 const maxYPos = 265 +const paymentSlipTop = 190 +const paymentSlipWaehrung = 270 +const paymentSlipDashLeft = 63 +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), ",", "'") + s := strings.ReplaceAll(p.Sprintf("%.2f", f), ",", sep) //fmt.Printf("--- s: @%s@\n", s) return s } @@ -128,6 +150,7 @@ func writeText(x float64, y float64, w float64, text string, alignStr ...string) 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") @@ -145,7 +168,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 { @@ -171,7 +194,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) } @@ -196,7 +219,7 @@ func printMetadata(firstPage bool) { 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) @@ -235,7 +258,7 @@ func printItems() { 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 != "" { lines := pdf.SplitText(i.Text, widthItemText) @@ -255,57 +278,186 @@ func printItems() { func printTotals() { yPos = totalsTop - pdf.Line(tabstopRight-widthPrice, yPos + (lineSpacing / 2), tabstopRight, yPos + (lineSpacing / 2)) + pdf.Line(tabstopRight-widthPrice, yPos+(lineSpacing/2), tabstopRight, yPos+(lineSpacing/2)) pdf.SetFont("Dejavusans-Bold", "", defaultFontSize) yPos = yPos + lineSpacing writeText(tabstopLeft, yPos, 0, "Summe") pdf.SetFont("Dejavusans", "", defaultFontSize) - writeText(tabstopPrice, yPos, widthPrice, floatToString(totalNetAmount), "TR") + 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) pdf.SetFont("Dejavusans", "", defaultFontSize) - writeText(tabstopPrice, yPos, widthPrice, floatToString(mwstAmount), "TR") -yPos = yPos + lineSpacing + 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 = 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", "Rechnung Nummer " + invoiceData.Metadata.InvoiceNr, - "--debtor-name", invoiceData.BillingAddress.Name, - "--debtor-street", invoiceData.BillingAddress.Street, - "--debtor-postalcode", invoiceData.BillingAddress.Zip, - "--debtor-city", invoiceData.BillingAddress.City, - "--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 } opt.ImageType = "png" - opt.ReadDpi = true - pdf.ImageOptions("qr-images/"+invoiceData.Metadata.InvoiceNr+".png", 0, 193, -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() { @@ -319,6 +471,7 @@ func CreateInvoice() { printItemsHeader() printItems() printTotals() + printPaymentSlip() printQR() } diff --git a/qrbill.sh b/qrbill.sh deleted file mode 100755 index 5ebea3f..0000000 --- a/qrbill.sh +++ /dev/null @@ -1,39 +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 - rm ${mydir}/temp/${INVNO}.svg -fi diff --git a/testqrbill.sh b/testqrbill.sh deleted file mode 100755 index 326af6c..0000000 --- a/testqrbill.sh +++ /dev/null @@ -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"