Invoice

From ConTeXt wiki

< Sample documents | Letter

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