* Invoice Forms
@ 2018-06-13 17:50 jdh
2018-06-14 10:48 ` Henning Hraban Ramm
2018-06-14 13:29 ` Peter Münster
0 siblings, 2 replies; 6+ messages in thread
From: jdh @ 2018-06-13 17:50 UTC (permalink / raw)
To: ntg-context
Is there a source of context document forms for invoices, (and other documents), that have been contributed for open modifications and use? If not it would be very beneficial to people to have such a depository. Please let me know.
Regards
___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the Wiki!
maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
webpage : http://www.pragma-ade.nl / http://context.aanhet.net
archive : https://bitbucket.org/phg/context-mirror/commits/
wiki : http://contextgarden.net
___________________________________________________________________________________
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Invoice Forms
2018-06-13 17:50 Invoice Forms jdh
@ 2018-06-14 10:48 ` Henning Hraban Ramm
2018-06-14 14:00 ` Henning Hraban Ramm
2018-06-14 13:29 ` Peter Münster
1 sibling, 1 reply; 6+ messages in thread
From: Henning Hraban Ramm @ 2018-06-14 10:48 UTC (permalink / raw)
To: mailing list for ConTeXt users
[-- Attachment #1: Type: text/plain, Size: 1025 bytes --]
Am 2018-06-13 um 19:50 schrieb jdh <dhenman@gmail.com>:
> Is there a source of context document forms for invoices, (and other documents), that have been contributed for open modifications and use? If not it would be very beneficial to people to have such a depository. Please let me know.
Dear unknown,
there’s nothing out there, AFAIK.
But I’m writing my invoices with ConTeXt, using a mixture of Shell, Python, Lua and ConTeXt, also mixed German/English:
* A bunch of Shell scripts call the main Python script with the right parameters.
* Python script/library manages customers and catalogue and fills a ConTeXt/Lua invoice template
* Python or Shell script calls ConTeXt, invoice calculations in Lua
I can provide you with the ConTeXt & Lua part, the Python stuff contains too much private information.
There’s no documentation and I don’t claim the code to be good.
Greetlings, Hraban
---
https://www.fiee.net
http://wiki.contextgarden.net
https://www.dreiviertelhaus.de
GPG Key ID 1C9B22FD
[-- Attachment #2: classy.lua --]
[-- Type: application/octet-stream, Size: 13852 bytes --]
-- class-based OO module for Lua
-- from https://github.com/siffiejoe/lua-classy
-- cache globals
local assert = assert
local V = assert( _VERSION )
local setmetatable = assert( setmetatable )
local select = assert( select )
local pairs = assert( pairs )
local ipairs = assert( ipairs )
local type = assert( type )
local error = assert( error )
local load = assert( load )
local s_rep = assert( string.rep )
local t_unpack = assert( V == "Lua 5.1" and unpack or table.unpack )
-- list of all metamethods that a user of this library is allowed to
-- add to a class
local allowed_metamethods = {
__add = true, __sub = true, __mul = true, __div = true,
__mod = true, __pow = true, __unm = true, __concat = true,
__len = true, __eq = true, __lt = true, __le = true, __call = true,
__tostring = true, __pairs = true, __ipairs = true, __gc = true,
__newindex = true, __metatable = true, __idiv = true, __band = true,
__bor = true, __bxor = true, __bnot = true, __shl = true,
__shr = true,
}
-- this metatable is (re-)used often:
local mode_k_meta = { __mode = "k" }
-- store information for every registered class (still in use)
-- [ cls ] = {
-- -- the name of the class
-- name = "clsname",
-- -- an array of superclasses in an order suitable for method
-- -- lookup, the first n are direct superclasses (parents)
-- super = { n = 2, super1, super2, super1_1, super1_2 },
-- -- a set of subclasses (value is "inheritance difference")
-- sub = { [ subcls1 ] = 1, [ subcls2 ] = 2 }, -- mode="k"
-- -- direct member functions/variables for this class
-- members = {},
-- -- the metatable for objects of this class
-- o_meta = { __index = {} },
-- -- the metatable for the class itself
-- c_meta = { __index = ..., __call = ..., __newindex = ... },
-- }
local classinfo = setmetatable( {}, mode_k_meta )
-- object constructor for the class if no custom __init function is
-- defined
local function default_constructor( meta )
return function()
return setmetatable( {}, meta )
end
end
-- object constructor for the class if a custom __init function is
-- available
local function init_constructor( meta, init )
return function( _, ... )
local o = setmetatable( {}, meta )
init( o, ... )
return o
end
end
-- propagate a changed method to a sub class
local function propagate_update( cls, key )
local info = classinfo[ cls ]
if info.members[ key ] ~= nil then
info.o_meta.__index[ key ] = info.members[ key ]
else
for i = 1, #info.super do
local val = classinfo[ info.super[ i ] ].members[ key ]
if val ~= nil then
info.o_meta.__index[ key ] = val
return
end
end
info.o_meta.__index[ key ] = nil
end
end
-- __newindex handler for class proxy tables, allowing to set certain
-- metamethods, initializers, and normal members. updates sub classes!
local function class_newindex( cls, key, val )
local info = classinfo[ cls ]
if allowed_metamethods[ key ] then
assert( info.o_meta[ key ] == nil,
"overwriting metamethods not allowed" )
info.o_meta[ key ] = val
elseif key == "__init" then
info.members.__init = val
info.o_meta.__index.__init = val
if type( val ) == "function" then
info.c_meta.__call = init_constructor( info.o_meta, val )
else
info.c_meta.__call = default_constructor( info.o_meta )
end
else
assert( key ~= "__class", "key '__class' is reserved" )
info.members[ key ] = val
propagate_update( cls, key )
for sub in pairs( info.sub ) do
propagate_update( sub, key )
end
end
end
-- __pairs/__ipairs metamethods for iterating members of classes
local function class_pairs( cls )
return pairs( classinfo[ cls ].o_meta.__index )
end
local function class_ipairs( cls )
return ipairs( classinfo[ cls ].o_meta.__index )
end
-- put the inheritance tree into a flat array using a width-first
-- iteration (similar to a binary heap); also set the "inheritance
-- difference" in superclasses
local function linearize_ancestors( cls, super, ... )
local n = select( '#', ... )
for i = 1, n do
local pcls = select( i, ... )
assert( classinfo[ pcls ], "invalid class" )
super[ i ] = pcls
end
super.n = n
local diff, newn = 1, n
for i,p in ipairs( super ) do
local pinfo = classinfo[ p ]
local psuper, psub = pinfo.super, pinfo.sub
if not psub[ cls ] then psub[ cls ] = diff end
for i = 1, psuper.n do
super[ #super+1 ] = psuper[ i ]
end
newn = newn + psuper.n
if i == n then
n, diff = newn, diff+1
end
end
end
-- create the necessary metadata for the class, setup the inheritance
-- hierarchy, set a suitable metatable, and return the class
local function create_class( _, name, ... )
assert( type( name ) == "string", "class name must be a string" )
local cls, index = {}, {}
local o_meta = {
__index = index,
__name = name,
}
local info = {
name = name,
super = { n = 0 },
sub = setmetatable( {}, mode_k_meta ),
members = {},
o_meta = o_meta,
c_meta = {
__index = index,
__newindex = class_newindex,
__call = default_constructor( o_meta ),
__pairs = class_pairs,
__ipairs = class_ipairs,
__name = "class",
__metatable = false,
},
}
linearize_ancestors( cls, info.super, ... )
for i = #info.super, 1, -1 do
for k,v in pairs( classinfo[ info.super[ i ] ].members ) do
if k ~= "__init" then index[ k ] = v end
end
end
index.__class = cls
classinfo[ cls ] = info
return setmetatable( cls, info.c_meta )
end
-- the exported class module
local M = {}
setmetatable( M, { __call = create_class } )
-- returns the class of an object
function M.of( o )
return type( o ) == "table" and o.__class or nil
end
-- returns the class name of an object or class
function M.name( oc )
if oc == nil then return nil end
oc = type( oc ) == "table" and oc.__class or oc
local info = classinfo[ oc ]
return info and info.name
end
-- checks if an object or class is in an inheritance
-- relationship with a given class
function M.is_a( oc, cls )
if oc == nil then return nil end
local info = assert( classinfo[ cls ], "invalid class" )
oc = type( oc ) == "table" and oc.__class or oc
if oc == cls then return 0 end
return info.sub[ oc ]
end
-- change the type of an object to the new class
function M.cast( o, newcls )
local info = classinfo[ newcls ]
if not info then
error( "invalid class" )
end
setmetatable( o, info.o_meta )
return o
end
local function make_delegate( cls, field, method )
cls[ method ] = function( self, ... )
local obj = self[ field ]
return obj[ method ]( obj, ... )
end
end
-- create delegation methods
function M.delegate( cls, fieldname, ... )
if type( (...) ) == "table" then
for k,v in pairs( (...) ) do
if cls[ k ] == nil and k ~= "__init" and
type( v ) == "function" then
make_delegate( cls, fieldname, k )
end
end
else
for i = 1, select( '#', ... ) do
local k = select( i, ... )
if cls[ k ] == nil and k ~= "__init" then
make_delegate( cls, fieldname, k )
end
end
end
return cls
end
-- multimethod stuff
do
-- store multimethods and map them to the meta-data
local mminfo = setmetatable( {}, mode_k_meta )
local erroffset = 0
if V == "Lua 5.1" then erroffset = 1 end
local function no_match2()
error( "no matching multimethod overload", 2+erroffset )
end
local function no_match3()
error( "no matching multimethod overload", 3+erroffset )
end
local function amb_call()
error( "ambiguous multimethod call", 3+erroffset )
end
local empty = {} -- just an empty table used as dummy
local FIRST_OL = 4 -- index of first overload specification
-- create a multimethod using the parameter indices given
-- as arguments for dynamic dispatch
function M.multimethod( ... )
local t, n = { ... }, select( '#', ... )
assert( n >= 1, "no polymorphic parameter for multimethod" )
local max = 0
for i = 1, n do
local x = t[ i ]
max = assert( x > max and x % 1 == 0 and x,
"invalid parameter overload specification" )
end
local mm_impl = { no_match2, t, max }
local function mm( ... )
return mm_impl[ 1 ]( mm_impl, ... )
end
mminfo[ mm ] = mm_impl
return mm
end
local function make_weak()
return setmetatable( {}, mode_k_meta )
end
local function calculate_cost( ol, ... )
local c = 0
for i = 1, select( '#', ... ) do
local a, pt = ol[ i ], select( i, ... )
if type( a ) == "table" then -- class table
local info = classinfo[ a ]
local diff = (pt == a) and 0 or info and info.sub[ pt ]
if not diff then return nil end
c = c + diff
else -- type name
if pt ~= a then return nil end
end
end
return c
end
local function select_impl( cost, f, amb, ol, ... )
local c = calculate_cost( ol, ... )
if c then
if cost then
if c < cost then
cost, f, amb = c, ol.func, false
elseif c == cost then
amb = true
end
else
cost, f, amb = c, ol.func, false
end
end
return cost, f, amb
end
local function collect_type_checkers( mm, a )
local funcs = {}, {}
for i = FIRST_OL, #mm do
local ol = mm[ i ]
for k,v in pairs( ol ) do
if type( k ) == "function" and
(a == nil or v[ a ]) and
not funcs[ k ] then
local j = #funcs+1
funcs[ j ] = k
funcs[ k ] = j
end
end
end
return funcs
end
local function c_varlist( t, m, prefix )
local n = #t
if m >= 1 then
t[ n+1 ] = prefix
t[ n+2 ] = 1
end
for i = 2, m do
local j = i*3+n
t[ j-3 ] = ","
t[ j-2 ] = prefix
t[ j-1 ] = i
end
end
local function c_typecheck( t, mm, funcs, j )
local n, ai = #t, mm[ 2 ][ j ]
t[ n+1 ] = " t=type(_"
t[ n+2 ] = ai
t[ n+3 ] = ")\n local t"
t[ n+4 ] = j
t[ n+5 ] = "=(t=='table' and _"
t[ n+6 ] = ai
t[ n+7 ] = ".__class) or "
local ltcs = collect_type_checkers( mm, j )
local m = #ltcs
for i = 1, m do
local k = i*5+n+3
t[ k ] = "tc"
t[ k+1 ] = funcs[ ltcs[ i ] ]
t[ k+2 ] = "(_"
t[ k+3 ] = ai
t[ k+4 ] = ") or "
end
t[ m*5+n+8 ] = "t\n"
end
local function c_cache( t, mm )
local c = #mm[ 2 ]
local n = #t
t[ n+1 ] = s_rep( "(", c-1 )
t[ n+2 ] = "cache"
for i = 1, c-1 do
local j = i*3+n
t[ j ] = "[t"
t[ j+1 ] = i
t[ j+2 ] = "] or empty)"
end
local j = c*3+n
t[ j ] = "[t"
t[ j+1 ] = c
t[ j+2 ] = "]\n"
end
local function c_costcheck( t, i, j )
local n = #t
t[ n+1 ] = " cost,f,is_amb=sel_impl(cost,f,is_amb,mm["
t[ n+2 ] = j+FIRST_OL-1
t[ n+3 ] = "],"
c_varlist( t, i, "t" )
t[ #t+1 ] = ")\n"
end
local function c_updatecache( t, i )
local n = #t
t[ n+1 ] = " if not t[t"
t[ n+2 ] = i
t[ n+3 ] = "] then t[t"
t[ n+4 ] = i
t[ n+5 ] = "]=mk_weak() end\n t=t[t"
t[ n+6 ] = i
t[ n+7 ] = "]\n"
end
local function recompile_and_call( mm, ... )
local n = #mm[ 2 ] -- number of polymorphic parameters
local tcs = collect_type_checkers( mm )
local code = {
"local type,cache,empty,mk_weak,sel_impl,no_match,amb_call"
}
if #tcs >= 1 then
code[ #code+1 ] = ","
end
c_varlist( code, #tcs, "tc" )
code[ #code+1 ] = "=...\nreturn function(mm,"
c_varlist( code, mm[ 3 ], "_" )
code[ #code+1 ] = ",...)\n local t\n"
for i = 1, n do
c_typecheck( code, mm, tcs, i )
end
code[ #code+1 ] = " local f="
c_cache( code, mm )
code[ #code+1 ] = [=[
if f==nil then
local is_amb,cost
]=]
for i = 1, #mm-FIRST_OL+1 do
c_costcheck( code, n, i )
end
code[ #code+1 ] = [=[
if f==nil then
no_match()
elseif is_amb then
amb_call()
end
t=cache
]=]
for i = 1, n-1 do
c_updatecache( code, i )
end
code[ #code+1 ] = " t[t"
code[ #code+1 ] = n
code[ #code+1 ] = "]=f\n end\n return f("
c_varlist( code, mm[ 3 ], "_" )
code[ #code+1 ] = ",...)\nend\n"
local i = 0
local function ld()
i = i + 1
return code[ i ]
end
--print( table.concat( code ) ) -- XXX
local f = assert( load( ld, "=[multimethod]" ) )(
type, make_weak(), empty, make_weak, select_impl, no_match3,
amb_call, t_unpack( tcs )
)
mm[ 1 ] = f
return f( mm, ... )
end
-- register a new overload for this multimethod
function M.overload( f, ... )
local mm = assert( type( f ) == "function" and mminfo[ f ],
"argument is not a multimethod" )
local i, n = 1, select( '#', ... )
local ol = {}
local func = assert( n >= 1 and select( n, ... ),
"missing function in overload specification" )
while i < n do
local a = select( i, ... )
local t = type( a )
if t == "string" then
ol[ #ol+1 ] = a
elseif t == "table" then
assert( classinfo[ a ], "invalid class" )
ol[ #ol+1 ] = a
else
assert( t == "function", "invalid overload specification" )
i = i + 1
assert( i < n, "missing function in overload specification" )
ol[ a ] = ol[ a ] or {}
ol[ #ol+1 ] = select( i, ... )
ol[ a ][ #ol ] = true
end
i = i + 1
end
assert( #mm[ 2 ] == #ol, "wrong number of overloaded parameters" )
ol.func = func
mm[ #mm+1 ] = ol
mm[ 1 ] = recompile_and_call
end
end
-- return module table
return M
[-- Attachment #3: invoicelib.lua --]
[-- Type: application/octet-stream, Size: 10977 bytes --]
require("table")
require("string")
local class = require("classy")
function table.copy(t)
local u = { }
for k, v in pairs(t) do u[k] = v end
return setmetatable(u, getmetatable(t))
end
-- ++++++++++++++++++++++
InvItem = class('InvItem')
function InvItem:__init(o, r)
-- print('InvItem:init', o)
self.count = 0
self.unit = ''
self.text = ''
self.tax = r.tax
self.taxmode = r.taxmode
self.amount = 0
self.typ = 'any' -- e.g. time, lump, text
self.discount = r.discount
for key, val in pairs(o) do
self[key] = val
end
end
function InvItem:validate(key, list)
-- is the item’s key value within the list?
if not list then
print(list, "is not a valid list but ", type(list))
return false
end
for _, v in pairs(list) do
if self[key] == v then
return true
end
end
print("value of '" .. key .. "' is not valid: '" .. self[key] .. "'" .. type(key))
return false
end
function InvItem:sum()
return self.count * self.amount * (100 - self.discount) / 100
end
function InvItem:taxsum()
-- included or added VAT
local sum = self:sum()
if self.taxmode == 'brutto' then
return sum - (sum / (1 + self.tax / 100))
else
return sum / 100 * self.tax
end
end
-- ++++++++++++++++++++++
Invoice = class('Invoice')
function Invoice:__init(o)
-- print('Invoice:init', o)
self._zerosums = { [7] = 0, [19] = 0, [0] = 0, hours = 0, netto = 0, brutto = 0, discount = 0 }
self.valid_tax = { 0, 7, 19 }
self.valid_typ = { 'any', 'time', 'lump', 'text', 'porto'}
self.sums = {}
self.nettosums = true -- rechne mit Netto-Summen // taxmode?
self.declaretax = true -- weise MwSt. aus
self.tax = 19
self.taxmode = 'brutto'
self.discount = 0 -- Rabatt in Prozent
self.printtitle = false
self.printprice = not tex.modes['lieferschein'] -- Lieferschein: false
self.printdiscounttitle = false
self.printhoursum = false
self.perhour = 50 -- Stundensatz
self.discount = 0.0 -- Rabatt in Prozent
self.alldiscounts = {}
self.currency1 = "€"
self.currency2 = "€" --"US\\$"
self.exchangerate = 1/1.0707 -- currency2-Preis = currency1-Preis * exchangerate
self.defaultunit = ""
self.timeunit = "h"
self.allunits = {} -- Liste der verwendeten Einheiten
self.items = {} -- Posten
self.language = "de"
self.numberseparator = { default = ".", de = ",", en = "." }
self.taxname = { default = "tax", de = "{MwSt․}", en = "VAT"}
self.sumname = { default = "∑", de = "gesamt", en = "sum" }
self.singlename = { default = "single", de = "einzeln", en = "single" }
self.amountname = { default = "", de = "{Anz․}", en = "{Qnt․}" }
self.textname = { default = "", de = "Bezeichnung", en = "title" }
self.discountname = { default = "", de = "Rabatt", en = "discount" }
self.bold = '\\bf '
self.sums = table.copy(self._zerosums)
for key, val in pairs(o) do
self[key] = val
end
end
function Invoice:addItem(o)
-- print('Invoice:addItem', o)
item = InvItem(o, self)
if not item:validate("tax", self.valid_tax) then
print("value of 'tax' is not valid: '" .. item.tax .. "'")
return nil
end
if not item:validate("typ", self.valid_typ) then
print("value of 'typ' is not valid: '" .. item.typ .. "'")
return nil
end
if item.typ == 'lump' then
item.count = 1
elseif item.typ == 'porto' then
item.count = 1
-- tax: like most of other items...
elseif item.typ == 'time' and o.amount == nil then
item.amount = self.perhour
end
if item.discount > 0 then
self.printdiscounttitle = true
end
table.insert(self.items, item)
-- self.items[#self.items+1] = item
return item
end
function Invoice:summarize()
-- calculate sums
self.sums = table.copy(self._zerosums)
for _, item in pairs(self.items) do
self.sums[item.tax] = self.sums[item.tax] + item:taxsum()
self.sums.netto = self.sums.netto + item:sum()
if self.taxmode == 'netto' then
self.sums.brutto = self.sums.brutto + item:sum() + item:taxsum()
else
self.sums.brutto = self.sums.brutto + item:sum()
end
if (item.typ == "time") or (item.unit == self.timeunit) then
self.sums.hours = self.sums.hours + item.count
end
self.allunits[item.unit] = true
if item.discount then
self.alldiscounts[item.discount] = true
self.sums.discount = self.sums.discount + item:sum() * item.discount/100
end
end
return self.sums
end
function Invoice:fmt_currency(amount, symbol)
-- format number to currency
if not symbol then
symbol = self.currency1
end
local sep = self.numberseparator[self.language]
return "" .. string.gsub(string.format('%.2f\\,%s', amount, symbol), "%.", sep)
end
function Invoice:fmt_percent(value, hideifempty)
-- format number to percent
if value == 0 and hideifempty then
return ""
end
return "" .. string.format('%d\\,\\%%', value)
end
function Invoice:fmt_number(value, hideifempty)
-- format number
if value == 0 and hideifempty then
return ""
end
local sep = self.numberseparator[self.language]
if tonumber(value) == math.floor(value) then
return "" .. string.format('%d', value)
end
return "" .. string.gsub(string.format('%0.2f', value), "%.", sep)
end
function Invoice:output()
local sep = self.numberseparator[self.language]
-- TODO: Unterscheidung brutto/netto, Rabatt; Einheit nur anzeigen, wenn sinnvoll
local fullwidth = 0.75 + 0.75 + 7.5 + 1.5 + 1.75 + 2.25
local countwidth = 0.75
local unitwidth = 0.5
local textwidth = 9.25
local taxwidth = 1.0
local singlepricewidth = 1.5
local discountwidth = 1.5
local sumwidth = 1.5
if #self.allunits == 1 then
unitwidth = 0
end
self:summarize()
print('DISCOUNT', self.sums.discount)
if self.sums.discount == 0 then
discountwidth = 0
end
if not self.declaretax then
taxwidth = 0
end
textwidth = fullwidth - countwidth - unitwidth - taxwidth - singlepricewidth - discountwidth - sumwidth
context.starttabulate({
string.format("|rg{%s}w(%scm)|lw(%scm)|lp(%scm)|rg{%s}w(%scm)|rg{%s}w(%scm)|rw(%scm)|rg{%s}w(%scm)|", sep, countwidth, unitwidth, textwidth, sep, taxwidth, sep, singlepricewidth, discountwidth, sep, sumwidth)
})
-- title
if self.printtitle then
context.NC(self.amountname[self.language])
context.NC()
context.NC()
context.NC(self.taxname[self.language])
context.NC(self.singlename[self.language])
if self.printdiscounttitle then
context.NC(self.discountname[self.language])
else
context.NC()
end
context.NC(self.sumname[self.language])
context.NC()
context.NR()
context.HL()
end
-- items
-- print into ConTeXt tabulate or table (old style)
for _, item in pairs(self.items) do
if item.typ == 'text' then
context.NC()
context.NC()
context.NC(item.text)
context.NC()
context.NC()
context.NC()
context.NC()
context.NR()
elseif item.typ == 'lump' then
context.NC()
context.NC()
context.NC(item.text)
if self.declaretax then
context.NC(self:fmt_percent(item.tax))
else
context.NC()
end
context.NC()
context.NC(self:fmt_percent(item.discount, true))
if self.printprice then
context.NC(self:fmt_currency(item:sum()))
else
context.NC()
end
context.NC()
context.NR()
elseif item.typ == 'porto' then
if self.printprice then
context.NC()
context.NC()
context.NC(item.text)
if self.declaretax then
context.NC(self:fmt_percent(item.tax))
else
context.NC()
end
context.NC()
context.NC(self:fmt_percent(item.discount, true))
context.NC(self:fmt_currency(item:sum()))
context.NC()
context.NR()
end
else
context.NC(self:fmt_number(item.count))
context.NC(item.unit)
context.NC(item.text)
if self.declaretax then
context.NC(self:fmt_percent(item.tax))
else
context.NC()
end
context.NC("à " .. self:fmt_currency(item.amount))
context.NC(self:fmt_percent(item.discount, true))
if self.printprice then
context.NC(self:fmt_currency(item:sum()))
else
context.NC()
end
context.NC()
context.NR()
end
end
context.HL()
if self.printprice then
-- sums
local endsum = self.sums.netto
if self.printhoursum then
context.NC(self:fmt_number(self.sums.hours))
context.NC(self.timeunit)
else
context.NC()
context.NC()
end
context.NC(self.sumname[self.language] .. ' ' .. self.taxmode)
context.NC()
context.NC()
context.NC()
context.NC(self.bold .. self:fmt_currency(endsum))
context.NC()
context.NR()
if self.taxmode == 'netto' then
endsum = self.sums.brutto
local brutto = self.sums.netto
for _, t in pairs(self.valid_tax) do
if self.sums[t] > 0 then
local tax = self.sums[t]
brutto = brutto + tax
context.NC()
context.NC()
context.NC(self.taxname[self.language])
context.NC(self:fmt_percent(t))
context.NC(self:fmt_currency(tax))
context.NC()
context.NC(self:fmt_currency(brutto))
context.NC()
context.NR()
end
end
context.NC()
context.NC()
context.NC(self.sumname[self.language] .. ' brutto')
context.NC()
context.NC()
context.NC()
context.NC(self.bold .. self:fmt_currency(endsum))
context.NC()
context.NR()
else
for _, t in pairs(self.valid_tax) do
if self.sums[t] > 0 then
context.NC()
context.NC()
context.NC('inkl. ' .. self.taxname[self.language])
context.NC(self:fmt_percent(t))
context.NC(self:fmt_currency(self.sums[t]))
context.NC()
context.NC()
context.NC()
context.NR()
end
end
end
if self.currency2 ~= self.currency1 then
local converted = endsum * self.exchangerate
context.NC()
context.NC()
context.NC()
context.NC()
context.NC()
context.NC()
context.NC("(" .. self:fmt_currency(converted, self.currency2) .. ")")
context.NC()
context.NR()
end
end
context.stoptabulate()
end
[-- Attachment #4: classy.lua --]
[-- Type: application/octet-stream, Size: 13852 bytes --]
-- class-based OO module for Lua
-- from https://github.com/siffiejoe/lua-classy
-- cache globals
local assert = assert
local V = assert( _VERSION )
local setmetatable = assert( setmetatable )
local select = assert( select )
local pairs = assert( pairs )
local ipairs = assert( ipairs )
local type = assert( type )
local error = assert( error )
local load = assert( load )
local s_rep = assert( string.rep )
local t_unpack = assert( V == "Lua 5.1" and unpack or table.unpack )
-- list of all metamethods that a user of this library is allowed to
-- add to a class
local allowed_metamethods = {
__add = true, __sub = true, __mul = true, __div = true,
__mod = true, __pow = true, __unm = true, __concat = true,
__len = true, __eq = true, __lt = true, __le = true, __call = true,
__tostring = true, __pairs = true, __ipairs = true, __gc = true,
__newindex = true, __metatable = true, __idiv = true, __band = true,
__bor = true, __bxor = true, __bnot = true, __shl = true,
__shr = true,
}
-- this metatable is (re-)used often:
local mode_k_meta = { __mode = "k" }
-- store information for every registered class (still in use)
-- [ cls ] = {
-- -- the name of the class
-- name = "clsname",
-- -- an array of superclasses in an order suitable for method
-- -- lookup, the first n are direct superclasses (parents)
-- super = { n = 2, super1, super2, super1_1, super1_2 },
-- -- a set of subclasses (value is "inheritance difference")
-- sub = { [ subcls1 ] = 1, [ subcls2 ] = 2 }, -- mode="k"
-- -- direct member functions/variables for this class
-- members = {},
-- -- the metatable for objects of this class
-- o_meta = { __index = {} },
-- -- the metatable for the class itself
-- c_meta = { __index = ..., __call = ..., __newindex = ... },
-- }
local classinfo = setmetatable( {}, mode_k_meta )
-- object constructor for the class if no custom __init function is
-- defined
local function default_constructor( meta )
return function()
return setmetatable( {}, meta )
end
end
-- object constructor for the class if a custom __init function is
-- available
local function init_constructor( meta, init )
return function( _, ... )
local o = setmetatable( {}, meta )
init( o, ... )
return o
end
end
-- propagate a changed method to a sub class
local function propagate_update( cls, key )
local info = classinfo[ cls ]
if info.members[ key ] ~= nil then
info.o_meta.__index[ key ] = info.members[ key ]
else
for i = 1, #info.super do
local val = classinfo[ info.super[ i ] ].members[ key ]
if val ~= nil then
info.o_meta.__index[ key ] = val
return
end
end
info.o_meta.__index[ key ] = nil
end
end
-- __newindex handler for class proxy tables, allowing to set certain
-- metamethods, initializers, and normal members. updates sub classes!
local function class_newindex( cls, key, val )
local info = classinfo[ cls ]
if allowed_metamethods[ key ] then
assert( info.o_meta[ key ] == nil,
"overwriting metamethods not allowed" )
info.o_meta[ key ] = val
elseif key == "__init" then
info.members.__init = val
info.o_meta.__index.__init = val
if type( val ) == "function" then
info.c_meta.__call = init_constructor( info.o_meta, val )
else
info.c_meta.__call = default_constructor( info.o_meta )
end
else
assert( key ~= "__class", "key '__class' is reserved" )
info.members[ key ] = val
propagate_update( cls, key )
for sub in pairs( info.sub ) do
propagate_update( sub, key )
end
end
end
-- __pairs/__ipairs metamethods for iterating members of classes
local function class_pairs( cls )
return pairs( classinfo[ cls ].o_meta.__index )
end
local function class_ipairs( cls )
return ipairs( classinfo[ cls ].o_meta.__index )
end
-- put the inheritance tree into a flat array using a width-first
-- iteration (similar to a binary heap); also set the "inheritance
-- difference" in superclasses
local function linearize_ancestors( cls, super, ... )
local n = select( '#', ... )
for i = 1, n do
local pcls = select( i, ... )
assert( classinfo[ pcls ], "invalid class" )
super[ i ] = pcls
end
super.n = n
local diff, newn = 1, n
for i,p in ipairs( super ) do
local pinfo = classinfo[ p ]
local psuper, psub = pinfo.super, pinfo.sub
if not psub[ cls ] then psub[ cls ] = diff end
for i = 1, psuper.n do
super[ #super+1 ] = psuper[ i ]
end
newn = newn + psuper.n
if i == n then
n, diff = newn, diff+1
end
end
end
-- create the necessary metadata for the class, setup the inheritance
-- hierarchy, set a suitable metatable, and return the class
local function create_class( _, name, ... )
assert( type( name ) == "string", "class name must be a string" )
local cls, index = {}, {}
local o_meta = {
__index = index,
__name = name,
}
local info = {
name = name,
super = { n = 0 },
sub = setmetatable( {}, mode_k_meta ),
members = {},
o_meta = o_meta,
c_meta = {
__index = index,
__newindex = class_newindex,
__call = default_constructor( o_meta ),
__pairs = class_pairs,
__ipairs = class_ipairs,
__name = "class",
__metatable = false,
},
}
linearize_ancestors( cls, info.super, ... )
for i = #info.super, 1, -1 do
for k,v in pairs( classinfo[ info.super[ i ] ].members ) do
if k ~= "__init" then index[ k ] = v end
end
end
index.__class = cls
classinfo[ cls ] = info
return setmetatable( cls, info.c_meta )
end
-- the exported class module
local M = {}
setmetatable( M, { __call = create_class } )
-- returns the class of an object
function M.of( o )
return type( o ) == "table" and o.__class or nil
end
-- returns the class name of an object or class
function M.name( oc )
if oc == nil then return nil end
oc = type( oc ) == "table" and oc.__class or oc
local info = classinfo[ oc ]
return info and info.name
end
-- checks if an object or class is in an inheritance
-- relationship with a given class
function M.is_a( oc, cls )
if oc == nil then return nil end
local info = assert( classinfo[ cls ], "invalid class" )
oc = type( oc ) == "table" and oc.__class or oc
if oc == cls then return 0 end
return info.sub[ oc ]
end
-- change the type of an object to the new class
function M.cast( o, newcls )
local info = classinfo[ newcls ]
if not info then
error( "invalid class" )
end
setmetatable( o, info.o_meta )
return o
end
local function make_delegate( cls, field, method )
cls[ method ] = function( self, ... )
local obj = self[ field ]
return obj[ method ]( obj, ... )
end
end
-- create delegation methods
function M.delegate( cls, fieldname, ... )
if type( (...) ) == "table" then
for k,v in pairs( (...) ) do
if cls[ k ] == nil and k ~= "__init" and
type( v ) == "function" then
make_delegate( cls, fieldname, k )
end
end
else
for i = 1, select( '#', ... ) do
local k = select( i, ... )
if cls[ k ] == nil and k ~= "__init" then
make_delegate( cls, fieldname, k )
end
end
end
return cls
end
-- multimethod stuff
do
-- store multimethods and map them to the meta-data
local mminfo = setmetatable( {}, mode_k_meta )
local erroffset = 0
if V == "Lua 5.1" then erroffset = 1 end
local function no_match2()
error( "no matching multimethod overload", 2+erroffset )
end
local function no_match3()
error( "no matching multimethod overload", 3+erroffset )
end
local function amb_call()
error( "ambiguous multimethod call", 3+erroffset )
end
local empty = {} -- just an empty table used as dummy
local FIRST_OL = 4 -- index of first overload specification
-- create a multimethod using the parameter indices given
-- as arguments for dynamic dispatch
function M.multimethod( ... )
local t, n = { ... }, select( '#', ... )
assert( n >= 1, "no polymorphic parameter for multimethod" )
local max = 0
for i = 1, n do
local x = t[ i ]
max = assert( x > max and x % 1 == 0 and x,
"invalid parameter overload specification" )
end
local mm_impl = { no_match2, t, max }
local function mm( ... )
return mm_impl[ 1 ]( mm_impl, ... )
end
mminfo[ mm ] = mm_impl
return mm
end
local function make_weak()
return setmetatable( {}, mode_k_meta )
end
local function calculate_cost( ol, ... )
local c = 0
for i = 1, select( '#', ... ) do
local a, pt = ol[ i ], select( i, ... )
if type( a ) == "table" then -- class table
local info = classinfo[ a ]
local diff = (pt == a) and 0 or info and info.sub[ pt ]
if not diff then return nil end
c = c + diff
else -- type name
if pt ~= a then return nil end
end
end
return c
end
local function select_impl( cost, f, amb, ol, ... )
local c = calculate_cost( ol, ... )
if c then
if cost then
if c < cost then
cost, f, amb = c, ol.func, false
elseif c == cost then
amb = true
end
else
cost, f, amb = c, ol.func, false
end
end
return cost, f, amb
end
local function collect_type_checkers( mm, a )
local funcs = {}, {}
for i = FIRST_OL, #mm do
local ol = mm[ i ]
for k,v in pairs( ol ) do
if type( k ) == "function" and
(a == nil or v[ a ]) and
not funcs[ k ] then
local j = #funcs+1
funcs[ j ] = k
funcs[ k ] = j
end
end
end
return funcs
end
local function c_varlist( t, m, prefix )
local n = #t
if m >= 1 then
t[ n+1 ] = prefix
t[ n+2 ] = 1
end
for i = 2, m do
local j = i*3+n
t[ j-3 ] = ","
t[ j-2 ] = prefix
t[ j-1 ] = i
end
end
local function c_typecheck( t, mm, funcs, j )
local n, ai = #t, mm[ 2 ][ j ]
t[ n+1 ] = " t=type(_"
t[ n+2 ] = ai
t[ n+3 ] = ")\n local t"
t[ n+4 ] = j
t[ n+5 ] = "=(t=='table' and _"
t[ n+6 ] = ai
t[ n+7 ] = ".__class) or "
local ltcs = collect_type_checkers( mm, j )
local m = #ltcs
for i = 1, m do
local k = i*5+n+3
t[ k ] = "tc"
t[ k+1 ] = funcs[ ltcs[ i ] ]
t[ k+2 ] = "(_"
t[ k+3 ] = ai
t[ k+4 ] = ") or "
end
t[ m*5+n+8 ] = "t\n"
end
local function c_cache( t, mm )
local c = #mm[ 2 ]
local n = #t
t[ n+1 ] = s_rep( "(", c-1 )
t[ n+2 ] = "cache"
for i = 1, c-1 do
local j = i*3+n
t[ j ] = "[t"
t[ j+1 ] = i
t[ j+2 ] = "] or empty)"
end
local j = c*3+n
t[ j ] = "[t"
t[ j+1 ] = c
t[ j+2 ] = "]\n"
end
local function c_costcheck( t, i, j )
local n = #t
t[ n+1 ] = " cost,f,is_amb=sel_impl(cost,f,is_amb,mm["
t[ n+2 ] = j+FIRST_OL-1
t[ n+3 ] = "],"
c_varlist( t, i, "t" )
t[ #t+1 ] = ")\n"
end
local function c_updatecache( t, i )
local n = #t
t[ n+1 ] = " if not t[t"
t[ n+2 ] = i
t[ n+3 ] = "] then t[t"
t[ n+4 ] = i
t[ n+5 ] = "]=mk_weak() end\n t=t[t"
t[ n+6 ] = i
t[ n+7 ] = "]\n"
end
local function recompile_and_call( mm, ... )
local n = #mm[ 2 ] -- number of polymorphic parameters
local tcs = collect_type_checkers( mm )
local code = {
"local type,cache,empty,mk_weak,sel_impl,no_match,amb_call"
}
if #tcs >= 1 then
code[ #code+1 ] = ","
end
c_varlist( code, #tcs, "tc" )
code[ #code+1 ] = "=...\nreturn function(mm,"
c_varlist( code, mm[ 3 ], "_" )
code[ #code+1 ] = ",...)\n local t\n"
for i = 1, n do
c_typecheck( code, mm, tcs, i )
end
code[ #code+1 ] = " local f="
c_cache( code, mm )
code[ #code+1 ] = [=[
if f==nil then
local is_amb,cost
]=]
for i = 1, #mm-FIRST_OL+1 do
c_costcheck( code, n, i )
end
code[ #code+1 ] = [=[
if f==nil then
no_match()
elseif is_amb then
amb_call()
end
t=cache
]=]
for i = 1, n-1 do
c_updatecache( code, i )
end
code[ #code+1 ] = " t[t"
code[ #code+1 ] = n
code[ #code+1 ] = "]=f\n end\n return f("
c_varlist( code, mm[ 3 ], "_" )
code[ #code+1 ] = ",...)\nend\n"
local i = 0
local function ld()
i = i + 1
return code[ i ]
end
--print( table.concat( code ) ) -- XXX
local f = assert( load( ld, "=[multimethod]" ) )(
type, make_weak(), empty, make_weak, select_impl, no_match3,
amb_call, t_unpack( tcs )
)
mm[ 1 ] = f
return f( mm, ... )
end
-- register a new overload for this multimethod
function M.overload( f, ... )
local mm = assert( type( f ) == "function" and mminfo[ f ],
"argument is not a multimethod" )
local i, n = 1, select( '#', ... )
local ol = {}
local func = assert( n >= 1 and select( n, ... ),
"missing function in overload specification" )
while i < n do
local a = select( i, ... )
local t = type( a )
if t == "string" then
ol[ #ol+1 ] = a
elseif t == "table" then
assert( classinfo[ a ], "invalid class" )
ol[ #ol+1 ] = a
else
assert( t == "function", "invalid overload specification" )
i = i + 1
assert( i < n, "missing function in overload specification" )
ol[ a ] = ol[ a ] or {}
ol[ #ol+1 ] = select( i, ... )
ol[ a ][ #ol ] = true
end
i = i + 1
end
assert( #mm[ 2 ] == #ol, "wrong number of overloaded parameters" )
ol.func = func
mm[ #mm+1 ] = ol
mm[ 1 ] = recompile_and_call
end
end
-- return module table
return M
[-- Attachment #5: invoicelib.lua --]
[-- Type: application/octet-stream, Size: 10977 bytes --]
require("table")
require("string")
local class = require("classy")
function table.copy(t)
local u = { }
for k, v in pairs(t) do u[k] = v end
return setmetatable(u, getmetatable(t))
end
-- ++++++++++++++++++++++
InvItem = class('InvItem')
function InvItem:__init(o, r)
-- print('InvItem:init', o)
self.count = 0
self.unit = ''
self.text = ''
self.tax = r.tax
self.taxmode = r.taxmode
self.amount = 0
self.typ = 'any' -- e.g. time, lump, text
self.discount = r.discount
for key, val in pairs(o) do
self[key] = val
end
end
function InvItem:validate(key, list)
-- is the item’s key value within the list?
if not list then
print(list, "is not a valid list but ", type(list))
return false
end
for _, v in pairs(list) do
if self[key] == v then
return true
end
end
print("value of '" .. key .. "' is not valid: '" .. self[key] .. "'" .. type(key))
return false
end
function InvItem:sum()
return self.count * self.amount * (100 - self.discount) / 100
end
function InvItem:taxsum()
-- included or added VAT
local sum = self:sum()
if self.taxmode == 'brutto' then
return sum - (sum / (1 + self.tax / 100))
else
return sum / 100 * self.tax
end
end
-- ++++++++++++++++++++++
Invoice = class('Invoice')
function Invoice:__init(o)
-- print('Invoice:init', o)
self._zerosums = { [7] = 0, [19] = 0, [0] = 0, hours = 0, netto = 0, brutto = 0, discount = 0 }
self.valid_tax = { 0, 7, 19 }
self.valid_typ = { 'any', 'time', 'lump', 'text', 'porto'}
self.sums = {}
self.nettosums = true -- rechne mit Netto-Summen // taxmode?
self.declaretax = true -- weise MwSt. aus
self.tax = 19
self.taxmode = 'brutto'
self.discount = 0 -- Rabatt in Prozent
self.printtitle = false
self.printprice = not tex.modes['lieferschein'] -- Lieferschein: false
self.printdiscounttitle = false
self.printhoursum = false
self.perhour = 50 -- Stundensatz
self.discount = 0.0 -- Rabatt in Prozent
self.alldiscounts = {}
self.currency1 = "€"
self.currency2 = "€" --"US\\$"
self.exchangerate = 1/1.0707 -- currency2-Preis = currency1-Preis * exchangerate
self.defaultunit = ""
self.timeunit = "h"
self.allunits = {} -- Liste der verwendeten Einheiten
self.items = {} -- Posten
self.language = "de"
self.numberseparator = { default = ".", de = ",", en = "." }
self.taxname = { default = "tax", de = "{MwSt․}", en = "VAT"}
self.sumname = { default = "∑", de = "gesamt", en = "sum" }
self.singlename = { default = "single", de = "einzeln", en = "single" }
self.amountname = { default = "", de = "{Anz․}", en = "{Qnt․}" }
self.textname = { default = "", de = "Bezeichnung", en = "title" }
self.discountname = { default = "", de = "Rabatt", en = "discount" }
self.bold = '\\bf '
self.sums = table.copy(self._zerosums)
for key, val in pairs(o) do
self[key] = val
end
end
function Invoice:addItem(o)
-- print('Invoice:addItem', o)
item = InvItem(o, self)
if not item:validate("tax", self.valid_tax) then
print("value of 'tax' is not valid: '" .. item.tax .. "'")
return nil
end
if not item:validate("typ", self.valid_typ) then
print("value of 'typ' is not valid: '" .. item.typ .. "'")
return nil
end
if item.typ == 'lump' then
item.count = 1
elseif item.typ == 'porto' then
item.count = 1
-- tax: like most of other items...
elseif item.typ == 'time' and o.amount == nil then
item.amount = self.perhour
end
if item.discount > 0 then
self.printdiscounttitle = true
end
table.insert(self.items, item)
-- self.items[#self.items+1] = item
return item
end
function Invoice:summarize()
-- calculate sums
self.sums = table.copy(self._zerosums)
for _, item in pairs(self.items) do
self.sums[item.tax] = self.sums[item.tax] + item:taxsum()
self.sums.netto = self.sums.netto + item:sum()
if self.taxmode == 'netto' then
self.sums.brutto = self.sums.brutto + item:sum() + item:taxsum()
else
self.sums.brutto = self.sums.brutto + item:sum()
end
if (item.typ == "time") or (item.unit == self.timeunit) then
self.sums.hours = self.sums.hours + item.count
end
self.allunits[item.unit] = true
if item.discount then
self.alldiscounts[item.discount] = true
self.sums.discount = self.sums.discount + item:sum() * item.discount/100
end
end
return self.sums
end
function Invoice:fmt_currency(amount, symbol)
-- format number to currency
if not symbol then
symbol = self.currency1
end
local sep = self.numberseparator[self.language]
return "" .. string.gsub(string.format('%.2f\\,%s', amount, symbol), "%.", sep)
end
function Invoice:fmt_percent(value, hideifempty)
-- format number to percent
if value == 0 and hideifempty then
return ""
end
return "" .. string.format('%d\\,\\%%', value)
end
function Invoice:fmt_number(value, hideifempty)
-- format number
if value == 0 and hideifempty then
return ""
end
local sep = self.numberseparator[self.language]
if tonumber(value) == math.floor(value) then
return "" .. string.format('%d', value)
end
return "" .. string.gsub(string.format('%0.2f', value), "%.", sep)
end
function Invoice:output()
local sep = self.numberseparator[self.language]
-- TODO: Unterscheidung brutto/netto, Rabatt; Einheit nur anzeigen, wenn sinnvoll
local fullwidth = 0.75 + 0.75 + 7.5 + 1.5 + 1.75 + 2.25
local countwidth = 0.75
local unitwidth = 0.5
local textwidth = 9.25
local taxwidth = 1.0
local singlepricewidth = 1.5
local discountwidth = 1.5
local sumwidth = 1.5
if #self.allunits == 1 then
unitwidth = 0
end
self:summarize()
print('DISCOUNT', self.sums.discount)
if self.sums.discount == 0 then
discountwidth = 0
end
if not self.declaretax then
taxwidth = 0
end
textwidth = fullwidth - countwidth - unitwidth - taxwidth - singlepricewidth - discountwidth - sumwidth
context.starttabulate({
string.format("|rg{%s}w(%scm)|lw(%scm)|lp(%scm)|rg{%s}w(%scm)|rg{%s}w(%scm)|rw(%scm)|rg{%s}w(%scm)|", sep, countwidth, unitwidth, textwidth, sep, taxwidth, sep, singlepricewidth, discountwidth, sep, sumwidth)
})
-- title
if self.printtitle then
context.NC(self.amountname[self.language])
context.NC()
context.NC()
context.NC(self.taxname[self.language])
context.NC(self.singlename[self.language])
if self.printdiscounttitle then
context.NC(self.discountname[self.language])
else
context.NC()
end
context.NC(self.sumname[self.language])
context.NC()
context.NR()
context.HL()
end
-- items
-- print into ConTeXt tabulate or table (old style)
for _, item in pairs(self.items) do
if item.typ == 'text' then
context.NC()
context.NC()
context.NC(item.text)
context.NC()
context.NC()
context.NC()
context.NC()
context.NR()
elseif item.typ == 'lump' then
context.NC()
context.NC()
context.NC(item.text)
if self.declaretax then
context.NC(self:fmt_percent(item.tax))
else
context.NC()
end
context.NC()
context.NC(self:fmt_percent(item.discount, true))
if self.printprice then
context.NC(self:fmt_currency(item:sum()))
else
context.NC()
end
context.NC()
context.NR()
elseif item.typ == 'porto' then
if self.printprice then
context.NC()
context.NC()
context.NC(item.text)
if self.declaretax then
context.NC(self:fmt_percent(item.tax))
else
context.NC()
end
context.NC()
context.NC(self:fmt_percent(item.discount, true))
context.NC(self:fmt_currency(item:sum()))
context.NC()
context.NR()
end
else
context.NC(self:fmt_number(item.count))
context.NC(item.unit)
context.NC(item.text)
if self.declaretax then
context.NC(self:fmt_percent(item.tax))
else
context.NC()
end
context.NC("à " .. self:fmt_currency(item.amount))
context.NC(self:fmt_percent(item.discount, true))
if self.printprice then
context.NC(self:fmt_currency(item:sum()))
else
context.NC()
end
context.NC()
context.NR()
end
end
context.HL()
if self.printprice then
-- sums
local endsum = self.sums.netto
if self.printhoursum then
context.NC(self:fmt_number(self.sums.hours))
context.NC(self.timeunit)
else
context.NC()
context.NC()
end
context.NC(self.sumname[self.language] .. ' ' .. self.taxmode)
context.NC()
context.NC()
context.NC()
context.NC(self.bold .. self:fmt_currency(endsum))
context.NC()
context.NR()
if self.taxmode == 'netto' then
endsum = self.sums.brutto
local brutto = self.sums.netto
for _, t in pairs(self.valid_tax) do
if self.sums[t] > 0 then
local tax = self.sums[t]
brutto = brutto + tax
context.NC()
context.NC()
context.NC(self.taxname[self.language])
context.NC(self:fmt_percent(t))
context.NC(self:fmt_currency(tax))
context.NC()
context.NC(self:fmt_currency(brutto))
context.NC()
context.NR()
end
end
context.NC()
context.NC()
context.NC(self.sumname[self.language] .. ' brutto')
context.NC()
context.NC()
context.NC()
context.NC(self.bold .. self:fmt_currency(endsum))
context.NC()
context.NR()
else
for _, t in pairs(self.valid_tax) do
if self.sums[t] > 0 then
context.NC()
context.NC()
context.NC('inkl. ' .. self.taxname[self.language])
context.NC(self:fmt_percent(t))
context.NC(self:fmt_currency(self.sums[t]))
context.NC()
context.NC()
context.NC()
context.NR()
end
end
end
if self.currency2 ~= self.currency1 then
local converted = endsum * self.exchangerate
context.NC()
context.NC()
context.NC()
context.NC()
context.NC()
context.NC()
context.NC("(" .. self:fmt_currency(converted, self.currency2) .. ")")
context.NC()
context.NR()
end
end
context.stoptabulate()
end
[-- Attachment #6: Type: text/plain, Size: 492 bytes --]
___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the Wiki!
maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
webpage : http://www.pragma-ade.nl / http://context.aanhet.net
archive : https://bitbucket.org/phg/context-mirror/commits/
wiki : http://contextgarden.net
___________________________________________________________________________________
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Invoice Forms
2018-06-13 17:50 Invoice Forms jdh
2018-06-14 10:48 ` Henning Hraban Ramm
@ 2018-06-14 13:29 ` Peter Münster
1 sibling, 0 replies; 6+ messages in thread
From: Peter Münster @ 2018-06-14 13:29 UTC (permalink / raw)
To: ConTeXt users
On Wed, Jun 13 2018, jdh wrote:
> Is there a source of context document forms for invoices, (and other
> documents), that have been contributed for open modifications and use? If not
> it would be very beneficial to people to have such a depository. Please let me
> know.
These pages can help perhaps:
- https://mailman.ntg.nl/pipermail/ntg-context/2008/031680.html
- https://mailman.ntg.nl/pipermail/ntg-context/2009/037920.html
- https://mailman.ntg.nl/pipermail/ntg-context/2014/076641.html
--
Peter
___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the Wiki!
maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
webpage : http://www.pragma-ade.nl / http://context.aanhet.net
archive : https://bitbucket.org/phg/context-mirror/commits/
wiki : http://contextgarden.net
___________________________________________________________________________________
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Invoice Forms
2018-06-14 10:48 ` Henning Hraban Ramm
@ 2018-06-14 14:00 ` Henning Hraban Ramm
2018-06-14 14:17 ` Floris van Manen
0 siblings, 1 reply; 6+ messages in thread
From: Henning Hraban Ramm @ 2018-06-14 14:00 UTC (permalink / raw)
To: mailing list for ConTeXt users
[-- Attachment #1: Type: text/plain, Size: 1260 bytes --]
Am 2018-06-14 um 12:48 schrieb Henning Hraban Ramm <texml@fiee.net>:
> Am 2018-06-13 um 19:50 schrieb jdh <dhenman@gmail.com>:
>
>> Is there a source of context document forms for invoices, (and other documents), that have been contributed for open modifications and use? If not it would be very beneficial to people to have such a depository. Please let me know.
>
> Dear unknown,
> there’s nothing out there, AFAIK.
>
> But I’m writing my invoices with ConTeXt, using a mixture of Shell, Python, Lua and ConTeXt, also mixed German/English:
> * A bunch of Shell scripts call the main Python script with the right parameters.
> * Python script/library manages customers and catalogue and fills a ConTeXt/Lua invoice template
> * Python or Shell script calls ConTeXt, invoice calculations in Lua
>
> I can provide you with the ConTeXt & Lua part, the Python stuff contains too much private information.
> There’s no documentation and I don’t claim the code to be good.
>
> <classy.lua><invoicelib.lua><classy.lua><invoicelib.lua>
Sorry, didn’t check what I attached. Here are the missing tex files.
Greetlings, Hraban
---
https://www.fiee.net
http://wiki.contextgarden.net
https://www.dreiviertelhaus.de
GPG Key ID 1C9B22FD
[-- Attachment #2: environment.tex --]
[-- Type: application/octet-stream, Size: 3356 bytes --]
\mainlanguage[de]
\usemodule[letter]
\startluacode
require("invoicelib")
\stopluacode
\setuplanguage [en] [date={year, –, mm, –, dd}] % Datum nach ISO 8601
\setupinterlinespace[3.0ex] % default: 2.8ex
\setbreakpoints[compound] % break at / and -
\setupletteroptions[language=german, whitespace=1.5ex]
\setuptabulate[distance=0pt]
\usetypescriptfile [type-computer-modern-unicode,]
\usetypescript [computer-modern-unicode-concrete]
\setupbodyfont [computer-modern-unicode-concrete,rm,10pt]
\setupletteroptions[bodyfont={computer-modern-unicode-concrete,rm,10pt}]
\setupletter[
% Sender address in envelope window
backaddress={}
]
\startnotmode[nologo]
% Define logo for the first page
\defineletterelement[layer][head][fiee]%
{\externalfigure[briefbogen]}
% Other logo for subsequent (right) pages
\defineletterelement[layer][nexthead][fiee]%
{\externalfigure[briefbogen][height=2cm,width=6cm]}
% We put our logo in the head
\setupletterlayer[head,nexthead]
[preset=lefttop,
alternative=fiee,
x=0mm,y=0mm,
]
\setupletterlayer[nexthead][state=right]
\stopnotmode
\setupletter[
company={},
name={},
street={},
city={},
phone={},
mobile={},
email={},
web={}
]
% center around the :
\defineletterelement[layer][location][fiee]%
{\hskip5em \fieevis
\setuptabulate[bodyfont=small]
\starttabulate[|Irw(5em)|Ip|]%[distance=0pt,bodyfont=small]
%\NC \strut \NC{\definedfont[SerifItalic at 10pt]\correspondenceparameter{company}}\NC\NR
\NC Who \NC\correspondenceparameter{name} \NC\NR
\NC Where \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
\stoptabulate}
\setupletterlayer[location]
[alternative=fiee,
x=147mm,y=45mm
]
% 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 % ex 169mm
\bTD[width=\dimexpr164mm-\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|]%[distance=0pt,bodyfont=small]
\NC Accounts \NC \NC\NR
\NC Tax No. \NC ... \NC\NR
\NC UStID \NC ... \NC\NR
\NC IBAN \NC ... \NC\NR
\NC BIC \NC ... \NC\NR
\stoptabulate}
\setupletterlayer[foot][
preset=leftbottom,
x=122mm, %x=127mm,
y=24mm, %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]
[-- Attachment #3: rechnung_tpl.tex --]
[-- Type: application/octet-stream, Size: 667 bytes --]
%%
\input environment.tex
\setupletter[
toname={},
toaddress={},
subject={},
date={}
]
\startletter
\strut
{
\setuptabulate[distance=.25em]
\startluacode
local rg = Invoice{
printtitle = true,
tax = 19,
declaretax = true,
taxmode = "netto",
perhour = 50
}
-- rg:addItem{count=1, unit="x", text="Sample", tax=19, amount=12.3}
-- rg:addItem{count=0.25, unit="h", text="Work", typ="time"}
-- rg:addItem{text="Project ConTeXt", typ="text"}
-- rg:addItem{text="Something", amount=500, typ="lump"}
rg:output()
\stopluacode
}
Payable...
\blank[2*big]
Best regards,
\blank[small]
\externalfigure[signature]%%
Your Provider
\stopletter
[-- Attachment #4: Type: text/plain, Size: 492 bytes --]
___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the Wiki!
maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
webpage : http://www.pragma-ade.nl / http://context.aanhet.net
archive : https://bitbucket.org/phg/context-mirror/commits/
wiki : http://contextgarden.net
___________________________________________________________________________________
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Invoice Forms
2018-06-14 14:00 ` Henning Hraban Ramm
@ 2018-06-14 14:17 ` Floris van Manen
2018-06-14 14:50 ` Henning Hraban Ramm
0 siblings, 1 reply; 6+ messages in thread
From: Floris van Manen @ 2018-06-14 14:17 UTC (permalink / raw)
To: mailing list for ConTeXt users
[-- Attachment #1.1: Type: text/plain, Size: 2111 bytes --]
almost…
what is \fieevis ?
\670>:letter:layer:location:fiee ... 5em \fieevis
\setuptabulate [bodyfont=s...
l.4 }
> On 14 Jun 2018, at 16:00, Henning Hraban Ramm <texml@fiee.net> wrote:
>
> Am 2018-06-14 um 12:48 schrieb Henning Hraban Ramm <texml@fiee.net>:
>
>> Am 2018-06-13 um 19:50 schrieb jdh <dhenman@gmail.com>:
>>
>>> Is there a source of context document forms for invoices, (and other documents), that have been contributed for open modifications and use? If not it would be very beneficial to people to have such a depository. Please let me know.
>>
>> Dear unknown,
>> there’s nothing out there, AFAIK.
>>
>> But I’m writing my invoices with ConTeXt, using a mixture of Shell, Python, Lua and ConTeXt, also mixed German/English:
>> * A bunch of Shell scripts call the main Python script with the right parameters.
>> * Python script/library manages customers and catalogue and fills a ConTeXt/Lua invoice template
>> * Python or Shell script calls ConTeXt, invoice calculations in Lua
>>
>> I can provide you with the ConTeXt & Lua part, the Python stuff contains too much private information.
>> There’s no documentation and I don’t claim the code to be good.
>>
>> <classy.lua><invoicelib.lua><classy.lua><invoicelib.lua>
>
> Sorry, didn’t check what I attached. Here are the missing tex files.
>
> Greetlings, Hraban
> ---
> https://www.fiee.net
> http://wiki.contextgarden.net
> https://www.dreiviertelhaus.de
> GPG Key ID 1C9B22FD
> <environment.tex><rechnung_tpl.tex>___________________________________________________________________________________
> If your question is of interest to others as well, please add an entry to the Wiki!
>
> maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
> webpage : http://www.pragma-ade.nl / http://context.aanhet.net
> archive : https://bitbucket.org/phg/context-mirror/commits/
> wiki : http://contextgarden.net
> ___________________________________________________________________________________
[-- Attachment #1.2: Message signed with OpenPGP using GPGMail --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 492 bytes --]
___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the Wiki!
maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
webpage : http://www.pragma-ade.nl / http://context.aanhet.net
archive : https://bitbucket.org/phg/context-mirror/commits/
wiki : http://contextgarden.net
___________________________________________________________________________________
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Invoice Forms
2018-06-14 14:17 ` Floris van Manen
@ 2018-06-14 14:50 ` Henning Hraban Ramm
0 siblings, 0 replies; 6+ messages in thread
From: Henning Hraban Ramm @ 2018-06-14 14:50 UTC (permalink / raw)
To: mailing list for ConTeXt users
Am 2018-06-14 um 16:17 schrieb Floris van Manen <vm@klankschap.nl>:
> almost…
> what is \fieevis ?
>
>
> \670>:letter:layer:location:fiee ... 5em \fieevis
> \setuptabulate [bodyfont=s...
> l.4 }
>
Sorry, missed that. It’s a macro that contains my company name, just delete it.
Best, Hraban
___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the Wiki!
maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
webpage : http://www.pragma-ade.nl / http://context.aanhet.net
archive : https://bitbucket.org/phg/context-mirror/commits/
wiki : http://contextgarden.net
___________________________________________________________________________________
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2018-06-14 14:50 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-13 17:50 Invoice Forms jdh
2018-06-14 10:48 ` Henning Hraban Ramm
2018-06-14 14:00 ` Henning Hraban Ramm
2018-06-14 14:17 ` Floris van Manen
2018-06-14 14:50 ` Henning Hraban Ramm
2018-06-14 13:29 ` Peter Münster
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).