Invoice
Jump to navigation
Jump to search
This is my invoice setup, it works with MkIV as of 2014-01.
It’s an application of Wolfgang’s letter module with a lot of tweaks that I could never think of on my own.
The code isn’t pretty or modular or readily usable for you, but works for me and might get you over some hurds.
As you see, I need my invoices in Euros or Swiss Francs. And I need to charge some work by time and some by fixed amounts. If you need number of pieces etc., it should be easy to adapt.
Big thanks to Wolfgang and all the other helpful power users on NTG-ConTeXt!
Lua functions
File invoicefunctions.tex
\startluacode userdata = userdata or {} userdata.invoice = { amount = 0, hours = 0, perhour = 50, currency1 = "€", currency2 = "SFr", exchangerate=1.22, items = {} } function userdata.RegisterTimeItem(text, hours) if tex.systemmodes.trialtypesetting then return end table.insert(userdata.invoice.items, {text=text, hours=hours, amount=0}) userdata.invoice.hours = userdata.invoice.hours + hours userdata.invoice.amount = userdata.invoice.amount + hours * userdata.invoice.perhour end function userdata.RegisterAmountItem(text, amount) if tex.systemmodes.trialtypesetting then return end table.insert(userdata.invoice.items, {text=text, hours=0, amount=amount}) userdata.invoice.amount = userdata.invoice.amount + amount end function userdata.RegisterTextItem(text) if tex.systemmodes.trialtypesetting then return end table.insert(userdata.invoice.items, {text=text, hours=0, amount=0}) end function userdata.numberformat(amount) -- replace decimal point with comma (= German format) return string.gsub(string.format("\%.2f", amount), "%.", ",") end function userdata.InvoiceTimeLine(text, hours, printperhour) context.NC(text) context.NC(userdata.numberformat(hours) .. "\\,h") if printperhour then context.NC("à " .. userdata.numberformat(userdata.invoice.perhour) .. "\\," .. userdata.invoice.currency1 .. "/h") else context.NC() end context.NC(userdata.numberformat(hours * userdata.invoice.perhour) .. "\\," .. userdata.invoice.currency1) context.NC() context.NR() end function userdata.InvoiceAmountLine(text, amount) context.NC(text) context.NC() context.NC() context.NC(userdata.numberformat(amount) .. "\\," .. userdata.invoice.currency1) context.NC() context.NR() end function userdata.InvoiceTextLine(text) context.NC(text) context.NC() context.NC() context.NC() context.NC() context.NR() end function userdata.InvoiceSumLine(text, printperhour) context.HL() context.NC("\\bf " .. text) if userdata.invoice.hours > 0 then context.NC(userdata.numberformat(userdata.invoice.hours) .. "\\,h") else context.NC() end if printperhour then context.NC("à " .. userdata.numberformat(userdata.invoice.perhour) .. "\\," .. userdata.invoice.currency1 .. "/h") else context.NC() end context.NC("\\bf " .. userdata.numberformat(userdata.invoice.amount) .. "\\," .. userdata.invoice.currency1) context.NC() context.NR() if userdata.invoice.currency1 ~= userdata.invoice.currency2 then context.NC() context.NC() context.NC() context.NC("(" .. userdata.numberformat(userdata.invoice.amount * userdata.invoice.exchangerate) .. "\\," .. userdata.invoice.currency2 .. ")") context.NC() context.NR() end end function userdata.Invoice() local amountsum, hoursum = 0,0 local printperhour printperhour = (userdata.invoice.amount ~= (userdata.invoice.hours * userdata.invoice.perhour)) context.starttabulate({"|lw(8cm)|rg(,)w(2cm)|rg(,)w(2cm)|rg(,)w(3cm)|"}) for no, item in ipairs(userdata.invoice.items) do if item.hours ~= 0 and item.amount == 0 then userdata.InvoiceTimeLine(item.text, item.hours, printperhour) hoursum = hoursum + item.hours amountsum = amountsum + item.hours * userdata.invoice.perhour elseif item.amount ~= 0 and item.hours == 0 then userdata.InvoiceAmountLine(item.text, item.amount) amountsum = amountsum + item.amount else userdata.InvoiceTextLine(item.text) end end printperhour = (hoursum > 0) and (amountsum == (hoursum * userdata.invoice.perhour)) --userdata.invoice.hours = hoursum --userdata.invoice.amount = amountsum userdata.InvoiceSumLine("gesamt", printperhour) context.stoptabulate() end \stopluacode
Letter setup
File invoicesetup.tex
\mainlanguage[de] \usemodule[letter] \setuplanguage [de] [date={year, –, mm, –, dd}] % ISO 8601 date \setupbodyfont[rm,10pt] \setupinterlinespace[3.0ex] % default: 2.8ex \setbreakpoints[compound] % break at / and - \setupletteroptions [language=german, % de bodyfont={rm,10pt}, whitespace=1.5ex, %3ex ] \setuptabulate[distance=0pt] \def\MyCompany{{\it MyCompany}} \useexternalfigure[logo][logo-head] \useexternalfigure[schriftzug][logo-type] \setupletter[ % Sender address in envelope window backaddress={\MyCompany\ · My Name · Samplestr.\,11 · CH-1234 Village} ] % Define logo for the first page \defineletterelement[layer][head][fiee]% {\framed[background=logo,height=35mm,frame=off,align=right]% {\externalfigure[schriftzug]}} % Other logo for subsequent (right) pages \defineletterelement[layer][nexthead][fiee]% {\externalfigure[logo][height=2cm, width=6cm]} % We put our logo in the head \setupletterlayer[head,nexthead][ x=128mm, y=15mm, alternative=fiee, ] \setupletterlayer[nexthead][state=right] \setupletter[ name={My Name}, street={Samplestrasse 11}, city={CH-1234 Village}, phone={+41 11 23\,45\,67}, mobile={+41 00 99\,88\,88\,88}, email={me@mycompany.com}, web={www.example.com}, skype={example} ] % center around the : \defineletterelement[layer][location][fiee]% {\setuptabulate[bodyfont=small] \starttabulate[|Irw(5em)|Ip|] \NC person \NC\correspondenceparameter{name} \NC\NR \NC address \NC\correspondenceparameter{street}\\\correspondenceparameter{city}\NC\NR \NC phone \NC\correspondenceparameter{phone}\\\correspondenceparameter{mobile} \NC\NR \NC internet \NC\correspondenceparameter{email}\\\correspondenceparameter{web} \NC\NR \NC skype \NC\correspondenceparameter{skype} \NC\NR \stoptabulate} \setupletterlayer[location] [alternative=fiee, x=152mm, y=30mm ] % Our own recipient setup without too much space before the address \defineletterelement[layer][address][fiee]% {\correspondenceparameter{toname}\\ \correspondenceparameter{toaddress} \par} \setupletterlayer[address][alternative=fiee] % Subject and date on the same line, date below logo \startsetups[letter:section:subject] \bTABLE[frame=off] \bTR \bTD[width=\dimexpr169mm-\backspace\relax]\correspondenceparameter{subject}\eTD \bTD{\tf\correspondenceparameter{date}}\eTD \eTR \eTABLE \stopsetups \setuplettersection[subject][alternative=setups] % account information at the foot, below the logo \defineletterelement[layer][foot][fiee]% {\setuptabulate[bodyfont=small] \starttabulate[|Irw(39mm)|Ip|] \NC accounts \NC \NC\NR \NC account \NC 123\,456\,789\\Postbank, BLZ 360\,100\,00 \NC\NR \NC IBAN \NC DE00\,3601\,0000\,0123\,4567\,89 \NC\NR \NC BIC \NC PBNKDEFF \NC\NR \stoptabulate} \setupletterlayer[foot][ preset=leftbottom, x=127mm, y=34mm, alternative=fiee] % switch off reference line \setupletterlayer[reference][state=stop] % Move the text a bit up \setupletterlayout[firstpage][topspace=10cm] % Adjust text start on subsequent pages \setupletterlayout[secondpage][topspace=3cm] % Move marks to the paper rim (won't print on most printers) \setupletterlayer[topmark,botmark,cutmark][x=-2mm]
Invoice
File invoice.tex
\input invoicesetup.tex \input invoicefunctions.tex \setupletter[ % Recipient toname={Pragma ADE\\Mr.\\,Hans Hagen},toaddress={Ridderstraat 27\\8061GH Hasselt NL}, % here I keep commented all of my few customers subject={Invoice No.\,99/2014} %subject={Reminder on invoice No.\,1/2014} ] \startletter Special services in December 2013: { \setuptabulate[distance=.25em] \startluacode userdata.invoice.perhour = 111 --userdata.invoice.currency1 = "SFr" userdata.RegisterTextItem("Some project description") userdata.RegisterAmountItem("Data handling, flat-rate", 500) userdata.RegisterTimeItem("Design of issue 1 of Some magazine", 42.5) userdata.Invoice() \stopluacode } Due on 1.\,1.\2014. Earlier is nicer. \blank[2*big] \MyCompany\ thanks for your order. \blank[2*big] Best regards, \externalfigure[signature] My Name \stopletter