From mboxrd@z Thu Jan 1 00:00:00 1970 X-Msuck: nntp://news.gmane.io/gmane.comp.tex.context/101002 Path: news.gmane.org!.POSTED!not-for-mail From: Henning Hraban Ramm Newsgroups: gmane.comp.tex.context Subject: Re: Invoice Forms Date: Thu, 14 Jun 2018 12:48:40 +0200 Message-ID: <32399723-4C93-479A-8591-675753D2FE88@fiee.net> References: <20180613105026.2607@sparky> Reply-To: mailing list for ConTeXt users NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\)) Content-Type: multipart/mixed; boundary="Apple-Mail=_74FD0812-0ECE-4CAB-BB0C-B28BFEB58480" X-Trace: blaine.gmane.org 1528973247 5372 195.159.176.226 (14 Jun 2018 10:47:27 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Thu, 14 Jun 2018 10:47:27 +0000 (UTC) To: mailing list for ConTeXt users Original-X-From: ntg-context-bounces@ntg.nl Thu Jun 14 12:47:23 2018 Return-path: Envelope-to: gctc-ntg-context-518@m.gmane.org Original-Received: from zapf.boekplan.nl ([5.39.185.232] helo=zapf.ntg.nl) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1fTPmk-0001In-Up for gctc-ntg-context-518@m.gmane.org; Thu, 14 Jun 2018 12:47:23 +0200 Original-Received: from localhost (localhost [127.0.0.1]) by zapf.ntg.nl (Postfix) with ESMTP id C007B3617D; Thu, 14 Jun 2018 12:48:55 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at zapf.boekplan.nl Original-Received: from zapf.ntg.nl ([127.0.0.1]) by localhost (zapf.ntg.nl [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id kgMAx9Xf0MC9; Thu, 14 Jun 2018 12:48:55 +0200 (CEST) Original-Received: from zapf.ntg.nl (localhost [IPv6:::1]) by zapf.ntg.nl (Postfix) with ESMTP id 186E636196; Thu, 14 Jun 2018 12:48:55 +0200 (CEST) Original-Received: from localhost (localhost [127.0.0.1]) by zapf.ntg.nl (Postfix) with ESMTP id 6FDBD3616A for ; Thu, 14 Jun 2018 12:48:54 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at zapf.boekplan.nl Original-Received: from zapf.ntg.nl ([127.0.0.1]) by localhost (zapf.ntg.nl [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id NV8KxxOl93jn for ; Thu, 14 Jun 2018 12:48:53 +0200 (CEST) Original-Received: from fiee.net (feronia.fiee.net [46.163.112.221]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by zapf.ntg.nl (Postfix) with ESMTPS id A0CC136162 for ; Thu, 14 Jun 2018 12:48:43 +0200 (CEST) Original-Received: from [192.168.178.36] (p5B293766.dip0.t-ipconnect.de [91.41.55.102]) by mail.fiee.net (Postfix) with ESMTPSA id BB87480062 for ; Thu, 14 Jun 2018 12:48:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fiee.net; s=default; t=1528973323; bh=tniimFHYmWTGe3pHjJT3VLGSkTi/klm8K314bCkkp3M=; l=62475; h=From:Subject:To; b=poLTXInqg1XHZdHjbtHQrI9DdPMt4EBYiTCYRVcEti3IgbJOsq2Fr/QzMDcPqMe6e spuMwrTUoYFGy1FKx+BBUvXkVibmQIKnhiVePDNvKcRzRzSoS9fqVQg6a3C3oiZgdH UdVsPgHimmy74SY3J8kH8+IED+SFZyZo1hcZL1a4= Authentication-Results: feronia.fiee.net; spf=pass (sender IP is 91.41.55.102) smtp.mailfrom=texml@fiee.net smtp.helo=[192.168.178.36] Received-SPF: pass (feronia.fiee.net: connection is authenticated) In-Reply-To: <20180613105026.2607@sparky> X-Mailer: Apple Mail (2.1878.6) X-BeenThere: ntg-context@ntg.nl X-Mailman-Version: 2.1.16 Precedence: list List-Id: mailing list for ConTeXt users List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ntg-context-bounces@ntg.nl Original-Sender: "ntg-context" Xref: news.gmane.org gmane.comp.tex.context:101002 Archived-At: --Apple-Mail=_74FD0812-0ECE-4CAB-BB0C-B28BFEB58480 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=windows-1252 Am 2018-06-13 um 19:50 schrieb jdh : > 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=92s nothing out there, AFAIK. But I=92m 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=92s no documentation and I don=92t claim the code to be good. Greetlings, Hraban --- https://www.fiee.net http://wiki.contextgarden.net https://www.dreiviertelhaus.de GPG Key ID 1C9B22FD --Apple-Mail=_74FD0812-0ECE-4CAB-BB0C-B28BFEB58480 Content-Disposition: attachment; filename=classy.lua Content-Type: application/octet-stream; x-unix-mode=0644; name="classy.lua" Content-Transfer-Encoding: 7bit -- 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 --Apple-Mail=_74FD0812-0ECE-4CAB-BB0C-B28BFEB58480 Content-Disposition: attachment; filename=invoicelib.lua Content-Type: application/octet-stream; name="invoicelib.lua" Content-Transfer-Encoding: quoted-printable require("table")=0Arequire("string")=0Alocal=20class=20=3D=20= require("classy")=0A=0Afunction=20table.copy(t)=0A=20=20local=20u=20=3D=20= {=20}=0A=20=20for=20k,=20v=20in=20pairs(t)=20do=20u[k]=20=3D=20v=20end=0A= =20=20return=20setmetatable(u,=20getmetatable(t))=0Aend=0A=0A--=20= ++++++++++++++++++++++=0A=0AInvItem=20=3D=20class('InvItem')=0A=0A= function=20InvItem:__init(o,=20r)=0A=20=20--=20print('InvItem:init',=20= o)=0A=20=20self.count=20=3D=200=0A=20=20self.unit=20=3D=20''=0A=20=20= self.text=20=3D=20''=0A=20=20self.tax=20=3D=20r.tax=0A=20=20self.taxmode=20= =3D=20r.taxmode=0A=20=20self.amount=20=3D=200=0A=20=20self.typ=20=3D=20= 'any'=20--=20e.g.=20time,=20lump,=20text=0A=20=20self.discount=20=3D=20= r.discount=0A=20=20for=20key,=20val=20in=20pairs(o)=20do=0A=20=20=20=20= self[key]=20=3D=20val=0A=20=20end=0Aend=0A=0Afunction=20= InvItem:validate(key,=20list)=0A=20=20--=20is=20the=20item=E2=80=99s=20= key=20value=20within=20the=20list?=0A=20=20if=20not=20list=20then=0A=20=20= =20=20print(list,=20"is=20not=20a=20valid=20list=20but=20",=20= type(list))=0A=20=20=20=20return=20false=0A=20=20end=0A=20=20for=20_,=20= v=20in=20pairs(list)=20do=0A=20=20=20=20if=20self[key]=20=3D=3D=20v=20= then=0A=20=20=20=20=20=20return=20true=0A=20=20=20=20end=0A=20=20end=0A=20= =20print("value=20of=20'"=20..=20key=20..=20"'=20is=20not=20valid:=20'"=20= ..=20self[key]=20..=20"'"=20..=20type(key))=0A=20=20return=20false=0Aend=0A= =0Afunction=20InvItem:sum()=0A=20=20return=20self.count=20*=20= self.amount=20*=20(100=20-=20self.discount)=20/=20100=0Aend=0A=0A= function=20InvItem:taxsum()=0A=20=20--=20included=20or=20added=20VAT=0A=20= =20local=20sum=20=3D=20self:sum()=0A=20=20if=20self.taxmode=20=3D=3D=20= 'brutto'=20then=0A=20=20=20=20return=20sum=20-=20(sum=20/=20(1=20+=20= self.tax=20/=20100))=0A=20=20else=0A=20=20=20=20return=20sum=20/=20100=20= *=20self.tax=0A=20=20end=0Aend=0A=0A--=20++++++++++++++++++++++=0A=0A= Invoice=20=3D=20class('Invoice')=0A=0Afunction=20Invoice:__init(o)=0A=20=20= --=20print('Invoice:init',=20o)=0A=20=20self._zerosums=20=3D=20{=20[7]=20= =3D=200,=20[19]=20=3D=200,=20[0]=20=3D=200,=20hours=20=3D=200,=20netto=20= =3D=200,=20brutto=20=3D=200,=20discount=20=3D=200=20}=0A=20=20= self.valid_tax=20=3D=20{=200,=207,=2019=20}=0A=20=20self.valid_typ=20=3D=20= {=20'any',=20'time',=20'lump',=20'text',=20'porto'}=0A=20=20self.sums=20= =3D=20{}=0A=20=20self.nettosums=20=3D=20true=20--=20rechne=20mit=20= Netto-Summen=20//=20taxmode?=0A=20=20self.declaretax=20=3D=20true=20--=20= weise=20MwSt.=20aus=0A=20=20self.tax=20=3D=2019=0A=20=20self.taxmode=20=3D= =20'brutto'=0A=20=20self.discount=20=3D=200=20--=20Rabatt=20in=20Prozent=0A= =20=20self.printtitle=20=3D=20false=0A=20=20self.printprice=20=3D=20not=20= tex.modes['lieferschein']=20--=20Lieferschein:=20false=0A=20=20= self.printdiscounttitle=20=3D=20false=0A=20=20self.printhoursum=20=3D=20= false=0A=20=20self.perhour=20=3D=2050=20--=20Stundensatz=0A=20=20= self.discount=20=3D=200.0=20--=20Rabatt=20in=20Prozent=0A=20=20= self.alldiscounts=20=3D=20{}=0A=20=20self.currency1=20=3D=20"=E2=82=AC"=0A= =20=20self.currency2=20=3D=20"=E2=82=AC"=20--"US\\$"=0A=20=20= self.exchangerate=20=3D=201/1.0707=20--=20currency2-Preis=20=3D=20= currency1-Preis=20*=20exchangerate=0A=20=20self.defaultunit=20=3D=20""=0A= =20=20self.timeunit=20=3D=20"h"=0A=20=20self.allunits=20=3D=20{}=20--=20= Liste=20der=20verwendeten=20Einheiten=0A=20=20self.items=20=3D=20{}=20--=20= Posten=0A=20=20self.language=20=3D=20"de"=0A=20=20self.numberseparator=20= =3D=20{=20default=20=3D=20".",=20de=20=3D=20",",=20en=20=3D=20"."=20}=0A=20= =20self.taxname=20=3D=20{=20default=20=3D=20"tax",=20de=20=3D=20= "{MwSt=E2=80=A4}",=20en=20=3D=20"VAT"}=0A=20=20self.sumname=20=3D=20{=20= default=20=3D=20"=E2=88=91",=20de=20=3D=20"gesamt",=20en=20=3D=20"sum"=20= }=0A=20=20self.singlename=20=3D=20{=20default=20=3D=20"single",=20de=20=3D= =20"einzeln",=20en=20=3D=20"single"=20}=0A=20=20self.amountname=20=3D=20= {=20default=20=3D=20"",=20de=20=3D=20"{Anz=E2=80=A4}",=20en=20=3D=20= "{Qnt=E2=80=A4}"=20}=0A=20=20self.textname=20=3D=20{=20default=20=3D=20= "",=20de=20=3D=20"Bezeichnung",=20en=20=3D=20"title"=20}=0A=20=20= self.discountname=20=3D=20{=20default=20=3D=20"",=20de=20=3D=20"Rabatt",=20= en=20=3D=20"discount"=20}=0A=20=20self.bold=20=3D=20'\\bf=20'=0A=0A=20=20= self.sums=20=3D=20table.copy(self._zerosums)=0A=20=20for=20key,=20val=20= in=20pairs(o)=20do=0A=20=20=20=20self[key]=20=3D=20val=0A=20=20end=0Aend=0A= =0Afunction=20Invoice:addItem(o)=0A=20=20--=20print('Invoice:addItem',=20= o)=0A=20=20item=20=3D=20InvItem(o,=20self)=0A=20=20if=20not=20= item:validate("tax",=20self.valid_tax)=20then=0A=20=20=20=20print("value=20= of=20'tax'=20is=20not=20valid:=20'"=20..=20item.tax=20..=20"'")=0A=20=20=20= =20return=20nil=0A=20=20end=0A=20=20if=20not=20item:validate("typ",=20= self.valid_typ)=20then=0A=20=20=20=20print("value=20of=20'typ'=20is=20= not=20valid:=20'"=20..=20item.typ=20..=20"'")=0A=20=20=20=20return=20nil=0A= =20=20end=0A=20=20if=20item.typ=20=3D=3D=20'lump'=20then=0A=20=20=20=20= item.count=20=3D=201=0A=20=20elseif=20item.typ=20=3D=3D=20'porto'=20then=0A= =20=20=20=20item.count=20=3D=201=0A=20=20=20=20--=20tax:=20like=20most=20= of=20other=20items...=0A=20=20elseif=20item.typ=20=3D=3D=20'time'=20and=20= o.amount=20=3D=3D=20nil=20then=0A=20=20=20=20item.amount=20=3D=20= self.perhour=0A=20=20end=0A=20=20if=20item.discount=20>=200=20then=0A=20=20= =20=20self.printdiscounttitle=20=3D=20true=0A=20=20end=0A=20=20= table.insert(self.items,=20item)=0A=20=20--=20self.items[#self.items+1]=20= =3D=20item=0A=20=20return=20item=0Aend=0A=0Afunction=20= Invoice:summarize()=0A=20=20--=20calculate=20sums=0A=20=20self.sums=20=3D=20= table.copy(self._zerosums)=0A=20=20for=20_,=20item=20in=20= pairs(self.items)=20do=0A=20=20=20=20self.sums[item.tax]=20=3D=20= self.sums[item.tax]=20+=20item:taxsum()=0A=20=20=20=20self.sums.netto=20= =3D=20self.sums.netto=20+=20item:sum()=0A=20=20=20=20if=20self.taxmode=20= =3D=3D=20'netto'=20then=0A=20=20=20=20=20=20=20=20self.sums.brutto=20=3D=20= self.sums.brutto=20+=20item:sum()=20+=20item:taxsum()=0A=20=20=20=20else=0A= =20=20=20=20=20=20=20=20self.sums.brutto=20=3D=20self.sums.brutto=20+=20= item:sum()=0A=20=20=20=20end=0A=20=20=20=20if=20(item.typ=20=3D=3D=20= "time")=20or=20(item.unit=20=3D=3D=20self.timeunit)=20then=0A=20=20=20=20= =20=20self.sums.hours=20=3D=20self.sums.hours=20+=20item.count=0A=20=20=20= =20end=0A=20=20=20=20self.allunits[item.unit]=20=3D=20true=0A=20=20=20=20= if=20item.discount=20then=0A=20=20=20=20=20=20=20=20= self.alldiscounts[item.discount]=20=3D=20true=0A=20=20=20=20=20=20=20=20= self.sums.discount=20=3D=20self.sums.discount=20+=20item:sum()=20*=20= item.discount/100=0A=20=20=20=20end=0A=20=20end=0A=20=20return=20= self.sums=0Aend=0A=0Afunction=20Invoice:fmt_currency(amount,=20symbol)=0A= =20=20--=20format=20number=20to=20currency=0A=20=20if=20not=20symbol=20= then=0A=20=20=20=20symbol=20=3D=20self.currency1=0A=20=20end=0A=20=20= local=20sep=20=3D=20self.numberseparator[self.language]=0A=20=20return=20= ""=20..=20string.gsub(string.format('%.2f\\,%s',=20amount,=20symbol),=20= "%.",=20sep)=0Aend=0A=0Afunction=20Invoice:fmt_percent(value,=20= hideifempty)=0A=20=20--=20format=20number=20to=20percent=0A=20=20if=20= value=20=3D=3D=200=20and=20hideifempty=20then=0A=20=20=20=20=20=20return=20= ""=0A=20=20end=0A=20=20return=20""=20..=20string.format('%d\\,\\%%',=20= value)=0Aend=0A=0Afunction=20Invoice:fmt_number(value,=20hideifempty)=0A=20= =20--=20format=20number=0A=20=20if=20value=20=3D=3D=200=20and=20= hideifempty=20then=0A=20=20=20=20=20=20return=20""=0A=20=20end=0A=20=20= local=20sep=20=3D=20self.numberseparator[self.language]=0A=0A=20=20if=20= tonumber(value)=20=3D=3D=20math.floor(value)=20then=0A=20=20=20=20return=20= ""=20..=20string.format('%d',=20value)=0A=20=20end=0A=20=20return=20""=20= ..=20string.gsub(string.format('%0.2f',=20value),=20"%.",=20sep)=0Aend=0A= =0Afunction=20Invoice:output()=0A=20=20local=20sep=20=3D=20= self.numberseparator[self.language]=0A=20=20--=20TODO:=20Unterscheidung=20= brutto/netto,=20Rabatt;=20Einheit=20nur=20anzeigen,=20wenn=20sinnvoll=0A=20= =20local=20fullwidth=20=3D=200.75=20+=200.75=20+=207.5=20+=201.5=20+=20= 1.75=20+=202.25=0A=20=20local=20countwidth=20=3D=200.75=0A=20=20local=20= unitwidth=20=3D=200.5=0A=20=20local=20textwidth=20=3D=209.25=0A=20=20= local=20taxwidth=20=3D=201.0=0A=20=20local=20singlepricewidth=20=3D=20= 1.5=0A=20=20local=20discountwidth=20=3D=201.5=0A=20=20local=20sumwidth=20= =3D=201.5=0A=20=20if=20#self.allunits=20=3D=3D=201=20then=0A=20=20=20=20=20= =20unitwidth=20=3D=200=0A=20=20end=0A=20=20self:summarize()=0A=20=20= print('DISCOUNT',=20self.sums.discount)=0A=20=20if=20self.sums.discount=20= =3D=3D=200=20then=0A=20=20=20=20=20=20discountwidth=20=3D=200=0A=20=20= end=0A=20=20if=20not=20self.declaretax=20then=0A=20=20=20=20=20=20= taxwidth=20=3D=200=0A=20=20end=0A=20=20textwidth=20=3D=20fullwidth=20-=20= countwidth=20-=20unitwidth=20-=20taxwidth=20-=20singlepricewidth=20-=20= discountwidth=20-=20sumwidth=0A=20=20context.starttabulate({=0A=20=20=20=20= string.format("|rg{%s}w(%scm)|lw(%scm)|lp(%scm)|rg{%s}w(%scm)|rg{%s}w(%scm= )|rw(%scm)|rg{%s}w(%scm)|",=20sep,=20countwidth,=20unitwidth,=20= textwidth,=20sep,=20taxwidth,=20sep,=20singlepricewidth,=20= discountwidth,=20sep,=20sumwidth)=0A=20=20})=0A=20=20--=20title=0A=20=20= if=20self.printtitle=20then=0A=20=20=20=20=20=20= context.NC(self.amountname[self.language])=0A=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20= context.NC(self.taxname[self.language])=0A=20=20=20=20=20=20= context.NC(self.singlename[self.language])=0A=20=20=20=20=20=20if=20= self.printdiscounttitle=20then=0A=20=20=20=20=20=20=20=20= context.NC(self.discountname[self.language])=0A=20=20=20=20=20=20else=0A=20= =20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20end=0A=20=20=20=20=20= =20context.NC(self.sumname[self.language])=0A=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20context.NR()=0A=20=20=20=20=20=20= context.HL()=0A=20=20end=0A=20=20--=20items=0A=20=20--=20print=20into=20= ConTeXt=20tabulate=20or=20table=20(old=20style)=0A=20=20for=20_,=20item=20= in=20pairs(self.items)=20do=0A=20=20=20=20if=20item.typ=20=3D=3D=20= 'text'=20then=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20context.NC(item.text)=0A=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20= context.NR()=0A=20=20=20=20elseif=20item.typ=20=3D=3D=20'lump'=20then=0A=20= =20=20=20=20=20context.NC()=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20= =20=20context.NC(item.text)=0A=20=20=20=20=20=20if=20self.declaretax=20= then=0A=20=20=20=20=20=20=20=20context.NC(self:fmt_percent(item.tax))=0A=20= =20=20=20=20=20else=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20= =20=20end=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20= context.NC(self:fmt_percent(item.discount,=20true))=0A=20=20=20=20=20=20= if=20self.printprice=20then=0A=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_currency(item:sum()))=0A=20=20=20=20=20=20else=0A=20=20= =20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20end=0A=20=20=20=20= =20=20context.NC()=0A=20=20=20=20=20=20context.NR()=0A=20=20=20=20elseif=20= item.typ=20=3D=3D=20'porto'=20then=0A=20=20=20=20=20=20if=20= self.printprice=20then=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20= =20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20= context.NC(item.text)=0A=20=20=20=20=20=20=20=20if=20self.declaretax=20= then=0A=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_percent(item.tax))=0A=20=20=20=20=20=20=20=20else=0A=20= =20=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20end=0A=20= =20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20= context.NC(self:fmt_percent(item.discount,=20true))=0A=20=20=20=20=20=20=20= =20context.NC(self:fmt_currency(item:sum()))=0A=20=20=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20=20=20context.NR()=0A=20=20=20=20=20=20= end=0A=20=20=20=20else=0A=20=20=20=20=20=20= context.NC(self:fmt_number(item.count))=0A=20=20=20=20=20=20= context.NC(item.unit)=0A=20=20=20=20=20=20context.NC(item.text)=0A=20=20=20= =20=20=20if=20self.declaretax=20then=0A=20=20=20=20=20=20=20=20= context.NC(self:fmt_percent(item.tax))=0A=20=20=20=20=20=20else=0A=20=20=20= =20=20=20=20=20context.NC()=0A=20=20=20=20=20=20end=0A=20=20=20=20=20=20= context.NC("=C3=A0=20"=20..=20self:fmt_currency(item.amount))=0A=20=20=20= =20=20=20context.NC(self:fmt_percent(item.discount,=20true))=0A=20=20=20=20= =20=20if=20self.printprice=20then=0A=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_currency(item:sum()))=0A=20=20=20=20=20=20else=0A=20=20= =20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20end=0A=20=20=20=20= =20=20context.NC()=0A=20=20=20=20=20=20context.NR()=0A=20=20=20=20end=0A=20= =20end=0A=20=20context.HL()=0A=20=20if=20self.printprice=20then=0A=20=20=20= =20=20=20--=20sums=0A=20=20=20=20=20=20local=20endsum=20=3D=20= self.sums.netto=0A=20=20=20=20=20=20if=20self.printhoursum=20then=0A=20=20= =20=20=20=20=20=20context.NC(self:fmt_number(self.sums.hours))=0A=20=20=20= =20=20=20=20=20context.NC(self.timeunit)=0A=20=20=20=20=20=20else=0A=20=20= =20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20= =20=20=20=20=20end=0A=20=20=20=20=20=20= context.NC(self.sumname[self.language]=20..=20'=20'=20..=20self.taxmode)=0A= =20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20context.NC()=0A=20=20=20= =20=20=20context.NC()=0A=20=20=20=20=20=20context.NC(self.bold=20..=20= self:fmt_currency(endsum))=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20= =20=20context.NR()=0A=20=20=20=20=20=20if=20self.taxmode=20=3D=3D=20= 'netto'=20then=0A=20=20=20=20=20=20=20=20endsum=20=3D=20self.sums.brutto=0A= =20=20=20=20=20=20=20=20local=20brutto=20=3D=20self.sums.netto=0A=20=20=20= =20=20=20=20=20for=20_,=20t=20in=20pairs(self.valid_tax)=20do=0A=20=20=20= =20=20=20=20=20=20=20if=20self.sums[t]=20>=200=20then=0A=20=20=20=20=20=20= =20=20=20=20=20=20local=20tax=20=3D=20self.sums[t]=0A=20=20=20=20=20=20=20= =20=20=20=20=20brutto=20=3D=20brutto=20+=20tax=0A=20=20=20=20=20=20=20=20= =20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC(self.taxname[self.language])=0A=20=20=20=20=20=20=20=20=20=20=20= =20context.NC(self:fmt_percent(t))=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_currency(tax))=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_currency(brutto))=0A=20=20=20=20=20=20=20=20=20=20=20= =20context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20context.NR()=0A=20=20= =20=20=20=20=20=20=20=20end=0A=20=20=20=20=20=20=20=20end=0A=20=20=20=20=20= =20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20= =20=20=20=20context.NC(self.sumname[self.language]=20..=20'=20brutto')=0A= =20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20= =20context.NC(self.bold=20..=20self:fmt_currency(endsum))=0A=20=20=20=20=20= =20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NR()=0A=20=20=20=20= =20=20else=0A=20=20=20=20=20=20=20=20for=20_,=20t=20in=20= pairs(self.valid_tax)=20do=0A=20=20=20=20=20=20=20=20=20=20if=20= self.sums[t]=20>=200=20then=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20= =20=20=20=20=20=20=20=20=20context.NC('inkl.=20'=20..=20= self.taxname[self.language])=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_percent(t))=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_currency(self.sums[t]))=0A=20=20=20=20=20=20=20=20=20= =20=20=20context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20context.NC()=0A= =20=20=20=20=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20= =20=20=20=20context.NR()=0A=20=20=20=20=20=20=20=20=20=20end=0A=20=20=20=20= =20=20=20=20end=0A=20=20=20=20=20=20end=0A=20=20=20=20=20=20if=20= self.currency2=20~=3D=20self.currency1=20then=0A=20=20=20=20=20=20=20=20= local=20converted=20=3D=20endsum=20*=20self.exchangerate=0A=20=20=20=20=20= =20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20= =20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20= =20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20= =20=20=20=20=20=20context.NC("("=20..=20self:fmt_currency(converted,=20= self.currency2)=20..=20")")=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20= =20=20=20=20=20=20context.NR()=0A=20=20=20=20=20=20end=0A=20=20end=0A=20=20= context.stoptabulate()=0Aend=0A= --Apple-Mail=_74FD0812-0ECE-4CAB-BB0C-B28BFEB58480 Content-Disposition: attachment; filename=classy.lua Content-Type: application/octet-stream; x-unix-mode=0644; name="classy.lua" Content-Transfer-Encoding: 7bit -- 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 --Apple-Mail=_74FD0812-0ECE-4CAB-BB0C-B28BFEB58480 Content-Disposition: attachment; filename=invoicelib.lua Content-Type: application/octet-stream; name="invoicelib.lua" Content-Transfer-Encoding: quoted-printable require("table")=0Arequire("string")=0Alocal=20class=20=3D=20= require("classy")=0A=0Afunction=20table.copy(t)=0A=20=20local=20u=20=3D=20= {=20}=0A=20=20for=20k,=20v=20in=20pairs(t)=20do=20u[k]=20=3D=20v=20end=0A= =20=20return=20setmetatable(u,=20getmetatable(t))=0Aend=0A=0A--=20= ++++++++++++++++++++++=0A=0AInvItem=20=3D=20class('InvItem')=0A=0A= function=20InvItem:__init(o,=20r)=0A=20=20--=20print('InvItem:init',=20= o)=0A=20=20self.count=20=3D=200=0A=20=20self.unit=20=3D=20''=0A=20=20= self.text=20=3D=20''=0A=20=20self.tax=20=3D=20r.tax=0A=20=20self.taxmode=20= =3D=20r.taxmode=0A=20=20self.amount=20=3D=200=0A=20=20self.typ=20=3D=20= 'any'=20--=20e.g.=20time,=20lump,=20text=0A=20=20self.discount=20=3D=20= r.discount=0A=20=20for=20key,=20val=20in=20pairs(o)=20do=0A=20=20=20=20= self[key]=20=3D=20val=0A=20=20end=0Aend=0A=0Afunction=20= InvItem:validate(key,=20list)=0A=20=20--=20is=20the=20item=E2=80=99s=20= key=20value=20within=20the=20list?=0A=20=20if=20not=20list=20then=0A=20=20= =20=20print(list,=20"is=20not=20a=20valid=20list=20but=20",=20= type(list))=0A=20=20=20=20return=20false=0A=20=20end=0A=20=20for=20_,=20= v=20in=20pairs(list)=20do=0A=20=20=20=20if=20self[key]=20=3D=3D=20v=20= then=0A=20=20=20=20=20=20return=20true=0A=20=20=20=20end=0A=20=20end=0A=20= =20print("value=20of=20'"=20..=20key=20..=20"'=20is=20not=20valid:=20'"=20= ..=20self[key]=20..=20"'"=20..=20type(key))=0A=20=20return=20false=0Aend=0A= =0Afunction=20InvItem:sum()=0A=20=20return=20self.count=20*=20= self.amount=20*=20(100=20-=20self.discount)=20/=20100=0Aend=0A=0A= function=20InvItem:taxsum()=0A=20=20--=20included=20or=20added=20VAT=0A=20= =20local=20sum=20=3D=20self:sum()=0A=20=20if=20self.taxmode=20=3D=3D=20= 'brutto'=20then=0A=20=20=20=20return=20sum=20-=20(sum=20/=20(1=20+=20= self.tax=20/=20100))=0A=20=20else=0A=20=20=20=20return=20sum=20/=20100=20= *=20self.tax=0A=20=20end=0Aend=0A=0A--=20++++++++++++++++++++++=0A=0A= Invoice=20=3D=20class('Invoice')=0A=0Afunction=20Invoice:__init(o)=0A=20=20= --=20print('Invoice:init',=20o)=0A=20=20self._zerosums=20=3D=20{=20[7]=20= =3D=200,=20[19]=20=3D=200,=20[0]=20=3D=200,=20hours=20=3D=200,=20netto=20= =3D=200,=20brutto=20=3D=200,=20discount=20=3D=200=20}=0A=20=20= self.valid_tax=20=3D=20{=200,=207,=2019=20}=0A=20=20self.valid_typ=20=3D=20= {=20'any',=20'time',=20'lump',=20'text',=20'porto'}=0A=20=20self.sums=20= =3D=20{}=0A=20=20self.nettosums=20=3D=20true=20--=20rechne=20mit=20= Netto-Summen=20//=20taxmode?=0A=20=20self.declaretax=20=3D=20true=20--=20= weise=20MwSt.=20aus=0A=20=20self.tax=20=3D=2019=0A=20=20self.taxmode=20=3D= =20'brutto'=0A=20=20self.discount=20=3D=200=20--=20Rabatt=20in=20Prozent=0A= =20=20self.printtitle=20=3D=20false=0A=20=20self.printprice=20=3D=20not=20= tex.modes['lieferschein']=20--=20Lieferschein:=20false=0A=20=20= self.printdiscounttitle=20=3D=20false=0A=20=20self.printhoursum=20=3D=20= false=0A=20=20self.perhour=20=3D=2050=20--=20Stundensatz=0A=20=20= self.discount=20=3D=200.0=20--=20Rabatt=20in=20Prozent=0A=20=20= self.alldiscounts=20=3D=20{}=0A=20=20self.currency1=20=3D=20"=E2=82=AC"=0A= =20=20self.currency2=20=3D=20"=E2=82=AC"=20--"US\\$"=0A=20=20= self.exchangerate=20=3D=201/1.0707=20--=20currency2-Preis=20=3D=20= currency1-Preis=20*=20exchangerate=0A=20=20self.defaultunit=20=3D=20""=0A= =20=20self.timeunit=20=3D=20"h"=0A=20=20self.allunits=20=3D=20{}=20--=20= Liste=20der=20verwendeten=20Einheiten=0A=20=20self.items=20=3D=20{}=20--=20= Posten=0A=20=20self.language=20=3D=20"de"=0A=20=20self.numberseparator=20= =3D=20{=20default=20=3D=20".",=20de=20=3D=20",",=20en=20=3D=20"."=20}=0A=20= =20self.taxname=20=3D=20{=20default=20=3D=20"tax",=20de=20=3D=20= "{MwSt=E2=80=A4}",=20en=20=3D=20"VAT"}=0A=20=20self.sumname=20=3D=20{=20= default=20=3D=20"=E2=88=91",=20de=20=3D=20"gesamt",=20en=20=3D=20"sum"=20= }=0A=20=20self.singlename=20=3D=20{=20default=20=3D=20"single",=20de=20=3D= =20"einzeln",=20en=20=3D=20"single"=20}=0A=20=20self.amountname=20=3D=20= {=20default=20=3D=20"",=20de=20=3D=20"{Anz=E2=80=A4}",=20en=20=3D=20= "{Qnt=E2=80=A4}"=20}=0A=20=20self.textname=20=3D=20{=20default=20=3D=20= "",=20de=20=3D=20"Bezeichnung",=20en=20=3D=20"title"=20}=0A=20=20= self.discountname=20=3D=20{=20default=20=3D=20"",=20de=20=3D=20"Rabatt",=20= en=20=3D=20"discount"=20}=0A=20=20self.bold=20=3D=20'\\bf=20'=0A=0A=20=20= self.sums=20=3D=20table.copy(self._zerosums)=0A=20=20for=20key,=20val=20= in=20pairs(o)=20do=0A=20=20=20=20self[key]=20=3D=20val=0A=20=20end=0Aend=0A= =0Afunction=20Invoice:addItem(o)=0A=20=20--=20print('Invoice:addItem',=20= o)=0A=20=20item=20=3D=20InvItem(o,=20self)=0A=20=20if=20not=20= item:validate("tax",=20self.valid_tax)=20then=0A=20=20=20=20print("value=20= of=20'tax'=20is=20not=20valid:=20'"=20..=20item.tax=20..=20"'")=0A=20=20=20= =20return=20nil=0A=20=20end=0A=20=20if=20not=20item:validate("typ",=20= self.valid_typ)=20then=0A=20=20=20=20print("value=20of=20'typ'=20is=20= not=20valid:=20'"=20..=20item.typ=20..=20"'")=0A=20=20=20=20return=20nil=0A= =20=20end=0A=20=20if=20item.typ=20=3D=3D=20'lump'=20then=0A=20=20=20=20= item.count=20=3D=201=0A=20=20elseif=20item.typ=20=3D=3D=20'porto'=20then=0A= =20=20=20=20item.count=20=3D=201=0A=20=20=20=20--=20tax:=20like=20most=20= of=20other=20items...=0A=20=20elseif=20item.typ=20=3D=3D=20'time'=20and=20= o.amount=20=3D=3D=20nil=20then=0A=20=20=20=20item.amount=20=3D=20= self.perhour=0A=20=20end=0A=20=20if=20item.discount=20>=200=20then=0A=20=20= =20=20self.printdiscounttitle=20=3D=20true=0A=20=20end=0A=20=20= table.insert(self.items,=20item)=0A=20=20--=20self.items[#self.items+1]=20= =3D=20item=0A=20=20return=20item=0Aend=0A=0Afunction=20= Invoice:summarize()=0A=20=20--=20calculate=20sums=0A=20=20self.sums=20=3D=20= table.copy(self._zerosums)=0A=20=20for=20_,=20item=20in=20= pairs(self.items)=20do=0A=20=20=20=20self.sums[item.tax]=20=3D=20= self.sums[item.tax]=20+=20item:taxsum()=0A=20=20=20=20self.sums.netto=20= =3D=20self.sums.netto=20+=20item:sum()=0A=20=20=20=20if=20self.taxmode=20= =3D=3D=20'netto'=20then=0A=20=20=20=20=20=20=20=20self.sums.brutto=20=3D=20= self.sums.brutto=20+=20item:sum()=20+=20item:taxsum()=0A=20=20=20=20else=0A= =20=20=20=20=20=20=20=20self.sums.brutto=20=3D=20self.sums.brutto=20+=20= item:sum()=0A=20=20=20=20end=0A=20=20=20=20if=20(item.typ=20=3D=3D=20= "time")=20or=20(item.unit=20=3D=3D=20self.timeunit)=20then=0A=20=20=20=20= =20=20self.sums.hours=20=3D=20self.sums.hours=20+=20item.count=0A=20=20=20= =20end=0A=20=20=20=20self.allunits[item.unit]=20=3D=20true=0A=20=20=20=20= if=20item.discount=20then=0A=20=20=20=20=20=20=20=20= self.alldiscounts[item.discount]=20=3D=20true=0A=20=20=20=20=20=20=20=20= self.sums.discount=20=3D=20self.sums.discount=20+=20item:sum()=20*=20= item.discount/100=0A=20=20=20=20end=0A=20=20end=0A=20=20return=20= self.sums=0Aend=0A=0Afunction=20Invoice:fmt_currency(amount,=20symbol)=0A= =20=20--=20format=20number=20to=20currency=0A=20=20if=20not=20symbol=20= then=0A=20=20=20=20symbol=20=3D=20self.currency1=0A=20=20end=0A=20=20= local=20sep=20=3D=20self.numberseparator[self.language]=0A=20=20return=20= ""=20..=20string.gsub(string.format('%.2f\\,%s',=20amount,=20symbol),=20= "%.",=20sep)=0Aend=0A=0Afunction=20Invoice:fmt_percent(value,=20= hideifempty)=0A=20=20--=20format=20number=20to=20percent=0A=20=20if=20= value=20=3D=3D=200=20and=20hideifempty=20then=0A=20=20=20=20=20=20return=20= ""=0A=20=20end=0A=20=20return=20""=20..=20string.format('%d\\,\\%%',=20= value)=0Aend=0A=0Afunction=20Invoice:fmt_number(value,=20hideifempty)=0A=20= =20--=20format=20number=0A=20=20if=20value=20=3D=3D=200=20and=20= hideifempty=20then=0A=20=20=20=20=20=20return=20""=0A=20=20end=0A=20=20= local=20sep=20=3D=20self.numberseparator[self.language]=0A=0A=20=20if=20= tonumber(value)=20=3D=3D=20math.floor(value)=20then=0A=20=20=20=20return=20= ""=20..=20string.format('%d',=20value)=0A=20=20end=0A=20=20return=20""=20= ..=20string.gsub(string.format('%0.2f',=20value),=20"%.",=20sep)=0Aend=0A= =0Afunction=20Invoice:output()=0A=20=20local=20sep=20=3D=20= self.numberseparator[self.language]=0A=20=20--=20TODO:=20Unterscheidung=20= brutto/netto,=20Rabatt;=20Einheit=20nur=20anzeigen,=20wenn=20sinnvoll=0A=20= =20local=20fullwidth=20=3D=200.75=20+=200.75=20+=207.5=20+=201.5=20+=20= 1.75=20+=202.25=0A=20=20local=20countwidth=20=3D=200.75=0A=20=20local=20= unitwidth=20=3D=200.5=0A=20=20local=20textwidth=20=3D=209.25=0A=20=20= local=20taxwidth=20=3D=201.0=0A=20=20local=20singlepricewidth=20=3D=20= 1.5=0A=20=20local=20discountwidth=20=3D=201.5=0A=20=20local=20sumwidth=20= =3D=201.5=0A=20=20if=20#self.allunits=20=3D=3D=201=20then=0A=20=20=20=20=20= =20unitwidth=20=3D=200=0A=20=20end=0A=20=20self:summarize()=0A=20=20= print('DISCOUNT',=20self.sums.discount)=0A=20=20if=20self.sums.discount=20= =3D=3D=200=20then=0A=20=20=20=20=20=20discountwidth=20=3D=200=0A=20=20= end=0A=20=20if=20not=20self.declaretax=20then=0A=20=20=20=20=20=20= taxwidth=20=3D=200=0A=20=20end=0A=20=20textwidth=20=3D=20fullwidth=20-=20= countwidth=20-=20unitwidth=20-=20taxwidth=20-=20singlepricewidth=20-=20= discountwidth=20-=20sumwidth=0A=20=20context.starttabulate({=0A=20=20=20=20= string.format("|rg{%s}w(%scm)|lw(%scm)|lp(%scm)|rg{%s}w(%scm)|rg{%s}w(%scm= )|rw(%scm)|rg{%s}w(%scm)|",=20sep,=20countwidth,=20unitwidth,=20= textwidth,=20sep,=20taxwidth,=20sep,=20singlepricewidth,=20= discountwidth,=20sep,=20sumwidth)=0A=20=20})=0A=20=20--=20title=0A=20=20= if=20self.printtitle=20then=0A=20=20=20=20=20=20= context.NC(self.amountname[self.language])=0A=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20= context.NC(self.taxname[self.language])=0A=20=20=20=20=20=20= context.NC(self.singlename[self.language])=0A=20=20=20=20=20=20if=20= self.printdiscounttitle=20then=0A=20=20=20=20=20=20=20=20= context.NC(self.discountname[self.language])=0A=20=20=20=20=20=20else=0A=20= =20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20end=0A=20=20=20=20=20= =20context.NC(self.sumname[self.language])=0A=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20context.NR()=0A=20=20=20=20=20=20= context.HL()=0A=20=20end=0A=20=20--=20items=0A=20=20--=20print=20into=20= ConTeXt=20tabulate=20or=20table=20(old=20style)=0A=20=20for=20_,=20item=20= in=20pairs(self.items)=20do=0A=20=20=20=20if=20item.typ=20=3D=3D=20= 'text'=20then=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20context.NC(item.text)=0A=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20= context.NR()=0A=20=20=20=20elseif=20item.typ=20=3D=3D=20'lump'=20then=0A=20= =20=20=20=20=20context.NC()=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20= =20=20context.NC(item.text)=0A=20=20=20=20=20=20if=20self.declaretax=20= then=0A=20=20=20=20=20=20=20=20context.NC(self:fmt_percent(item.tax))=0A=20= =20=20=20=20=20else=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20= =20=20end=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20= context.NC(self:fmt_percent(item.discount,=20true))=0A=20=20=20=20=20=20= if=20self.printprice=20then=0A=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_currency(item:sum()))=0A=20=20=20=20=20=20else=0A=20=20= =20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20end=0A=20=20=20=20= =20=20context.NC()=0A=20=20=20=20=20=20context.NR()=0A=20=20=20=20elseif=20= item.typ=20=3D=3D=20'porto'=20then=0A=20=20=20=20=20=20if=20= self.printprice=20then=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20= =20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20= context.NC(item.text)=0A=20=20=20=20=20=20=20=20if=20self.declaretax=20= then=0A=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_percent(item.tax))=0A=20=20=20=20=20=20=20=20else=0A=20= =20=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20end=0A=20= =20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20= context.NC(self:fmt_percent(item.discount,=20true))=0A=20=20=20=20=20=20=20= =20context.NC(self:fmt_currency(item:sum()))=0A=20=20=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20=20=20context.NR()=0A=20=20=20=20=20=20= end=0A=20=20=20=20else=0A=20=20=20=20=20=20= context.NC(self:fmt_number(item.count))=0A=20=20=20=20=20=20= context.NC(item.unit)=0A=20=20=20=20=20=20context.NC(item.text)=0A=20=20=20= =20=20=20if=20self.declaretax=20then=0A=20=20=20=20=20=20=20=20= context.NC(self:fmt_percent(item.tax))=0A=20=20=20=20=20=20else=0A=20=20=20= =20=20=20=20=20context.NC()=0A=20=20=20=20=20=20end=0A=20=20=20=20=20=20= context.NC("=C3=A0=20"=20..=20self:fmt_currency(item.amount))=0A=20=20=20= =20=20=20context.NC(self:fmt_percent(item.discount,=20true))=0A=20=20=20=20= =20=20if=20self.printprice=20then=0A=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_currency(item:sum()))=0A=20=20=20=20=20=20else=0A=20=20= =20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20end=0A=20=20=20=20= =20=20context.NC()=0A=20=20=20=20=20=20context.NR()=0A=20=20=20=20end=0A=20= =20end=0A=20=20context.HL()=0A=20=20if=20self.printprice=20then=0A=20=20=20= =20=20=20--=20sums=0A=20=20=20=20=20=20local=20endsum=20=3D=20= self.sums.netto=0A=20=20=20=20=20=20if=20self.printhoursum=20then=0A=20=20= =20=20=20=20=20=20context.NC(self:fmt_number(self.sums.hours))=0A=20=20=20= =20=20=20=20=20context.NC(self.timeunit)=0A=20=20=20=20=20=20else=0A=20=20= =20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20= =20=20=20=20=20end=0A=20=20=20=20=20=20= context.NC(self.sumname[self.language]=20..=20'=20'=20..=20self.taxmode)=0A= =20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20context.NC()=0A=20=20=20= =20=20=20context.NC()=0A=20=20=20=20=20=20context.NC(self.bold=20..=20= self:fmt_currency(endsum))=0A=20=20=20=20=20=20context.NC()=0A=20=20=20=20= =20=20context.NR()=0A=20=20=20=20=20=20if=20self.taxmode=20=3D=3D=20= 'netto'=20then=0A=20=20=20=20=20=20=20=20endsum=20=3D=20self.sums.brutto=0A= =20=20=20=20=20=20=20=20local=20brutto=20=3D=20self.sums.netto=0A=20=20=20= =20=20=20=20=20for=20_,=20t=20in=20pairs(self.valid_tax)=20do=0A=20=20=20= =20=20=20=20=20=20=20if=20self.sums[t]=20>=200=20then=0A=20=20=20=20=20=20= =20=20=20=20=20=20local=20tax=20=3D=20self.sums[t]=0A=20=20=20=20=20=20=20= =20=20=20=20=20brutto=20=3D=20brutto=20+=20tax=0A=20=20=20=20=20=20=20=20= =20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC(self.taxname[self.language])=0A=20=20=20=20=20=20=20=20=20=20=20= =20context.NC(self:fmt_percent(t))=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_currency(tax))=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_currency(brutto))=0A=20=20=20=20=20=20=20=20=20=20=20= =20context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20context.NR()=0A=20=20= =20=20=20=20=20=20=20=20end=0A=20=20=20=20=20=20=20=20end=0A=20=20=20=20=20= =20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20= =20=20=20=20context.NC(self.sumname[self.language]=20..=20'=20brutto')=0A= =20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20= =20context.NC(self.bold=20..=20self:fmt_currency(endsum))=0A=20=20=20=20=20= =20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NR()=0A=20=20=20=20= =20=20else=0A=20=20=20=20=20=20=20=20for=20_,=20t=20in=20= pairs(self.valid_tax)=20do=0A=20=20=20=20=20=20=20=20=20=20if=20= self.sums[t]=20>=200=20then=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20= =20=20=20=20=20=20=20=20=20context.NC('inkl.=20'=20..=20= self.taxname[self.language])=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_percent(t))=0A=20=20=20=20=20=20=20=20=20=20=20=20= context.NC(self:fmt_currency(self.sums[t]))=0A=20=20=20=20=20=20=20=20=20= =20=20=20context.NC()=0A=20=20=20=20=20=20=20=20=20=20=20=20context.NC()=0A= =20=20=20=20=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20= =20=20=20=20context.NR()=0A=20=20=20=20=20=20=20=20=20=20end=0A=20=20=20=20= =20=20=20=20end=0A=20=20=20=20=20=20end=0A=20=20=20=20=20=20if=20= self.currency2=20~=3D=20self.currency1=20then=0A=20=20=20=20=20=20=20=20= local=20converted=20=3D=20endsum=20*=20self.exchangerate=0A=20=20=20=20=20= =20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20=20= =20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20=20= =20=20=20=20=20context.NC()=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20= =20=20=20=20=20=20context.NC("("=20..=20self:fmt_currency(converted,=20= self.currency2)=20..=20")")=0A=20=20=20=20=20=20=20=20context.NC()=0A=20=20= =20=20=20=20=20=20context.NR()=0A=20=20=20=20=20=20end=0A=20=20end=0A=20=20= context.stoptabulate()=0Aend=0A= --Apple-Mail=_74FD0812-0ECE-4CAB-BB0C-B28BFEB58480 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Content-Disposition: inline X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fX19fX19fX18KSWYgeW91ciBxdWVzdGlvbiBpcyBvZiBpbnRlcmVz dCB0byBvdGhlcnMgYXMgd2VsbCwgcGxlYXNlIGFkZCBhbiBlbnRyeSB0byB0aGUgV2lraSEKCm1h aWxsaXN0IDogbnRnLWNvbnRleHRAbnRnLm5sIC8gaHR0cDovL3d3dy5udGcubmwvbWFpbG1hbi9s aXN0aW5mby9udGctY29udGV4dAp3ZWJwYWdlICA6IGh0dHA6Ly93d3cucHJhZ21hLWFkZS5ubCAv IGh0dHA6Ly9jb250ZXh0LmFhbmhldC5uZXQKYXJjaGl2ZSAgOiBodHRwczovL2JpdGJ1Y2tldC5v cmcvcGhnL2NvbnRleHQtbWlycm9yL2NvbW1pdHMvCndpa2kgICAgIDogaHR0cDovL2NvbnRleHRn YXJkZW4ubmV0Cl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f --Apple-Mail=_74FD0812-0ECE-4CAB-BB0C-B28BFEB58480--