#!/usr/bin/env python3 import os import sys import yaml def debug(*args): sys.stderr.write('{}\n'.format(' '.join(str(x) for x in args))) def round(f): return int(f + 0.5) def euros(cents): return '%.02f' % (cents / 100.0) class Item: def __init__(self, itemdict): self.dict = dict(itemdict) bignum = 99999999 self.dict.update({ 'unit_net_cents': bignum, 'unit_vat_cents': bignum, 'unit_cents': bignum, 'total_net_cents': bignum, 'total_vat_cents': bignum, 'total_cents': bignum, }) self.compute() self.check() self.dict.update({ 'unit_net_euros': euros(self['unit_net_cents']), 'unit_vat_euros': euros(self['unit_vat_cents']), 'unit_euros': euros(self['unit_cents']), 'total_net_euros': euros(self['total_net_cents']), 'total_vat_euros': euros(self['total_vat_cents']), 'total_euros': euros(self['total_cents']), }) def __getitem__(self, key): return self.dict[key] def __setitem__(self, key, value): self.dict[key] = value def items(self): return self.dict.items() def check(self): count = self['count'] vatpc = int(self['vatpc'] / 100.0) unit_net_cents = self['unit_net_cents'] unit_vat_cents = self['unit_vat_cents'] unit_cents = self['unit_cents'] total_net_cents = self['total_net_cents'] total_vat_cents = self['total_vat_cents'] total_cents = self['total_cents'] assert unit_net_cents * count == total_net_cents, \ '{} * {} != {}'.format(unit_net_cents, count, total_net_cents) assert unit_net_cents + unit_vat_cents == unit_cents, \ '{} + {} != {}'.format(unit_net_cents, unit_vat_cents, unit_cents) assert total_cents == count * (unit_net_cents + unit_vat_cents), \ '{} != {} * ({} + {})'.format( total_net_cents, count, unit_net_cents, unit_vat_cents) if vatpc > 0: assert unit_cents == unit_net_cents + unit_vat_cents, \ '{} != {} + {}'.format(unit_cents, unit_net_cents, unit_vat_cents) assert total_vat_cents == count * unit_vat_cents, \ '{} != {} * {}'.format(total_vat_cents, count, unit_net_cents) assert round(total_cents * vatpc) == total_vat_cents, \ '{} * {} != {}'.format( total_cents, vatpc, total_vat_cents) def compute(self): count = self['count'] vatpc = float(self['vatpc']) / 100.0 unit_net_cents = self['unit_net_cents'] = round(self['unit'] * 100.0) unit_vat_cents = self['unit_vat_cents'] = round( unit_net_cents * vatpc / (1 - vatpc)) unit_cents = self['unit_cents'] = unit_net_cents + unit_vat_cents self['total_net_cents'] = unit_net_cents * count self['total_vat_cents'] = unit_vat_cents * count self['total_cents'] = count * unit_cents item_template = ( r'\raggedbottom #number# & ' r'\parbox[t]{5.5cm}{#desc#\\#date#} & ' r'\raggedbottom #count# & ' r'\raggedbottom #unit_net_euros# & ' r'\raggedbottom #unit_euros# & ' r'\raggedbottom #vatpc#\% & ' r'\raggedbottom #total_net_euros# & ' r'\raggedbottom #total_vat_euros# & ' r'\raggedbottom #total_euros# \\[2.2ex]' ) def cat(filename): with open(filename) as f: return f.read() def substitute_all(text, subst): for key, value in subst.items(): pattern = '#{}#'.format(key) text = str(value).join(text.split(pattern)) return text dirname = os.path.dirname(__file__) filename = os.path.join(dirname, 'template.tex') text = cat(filename) with open(sys.argv[1]) as f: company = yaml.safe_load(f) with open(sys.argv[2]) as f: invoice = yaml.safe_load(f) items = invoice['invoiceitems'] values = dict(company) values.update(invoice) itemtext = '' sum_net_cents = 0 sum_vat_cents = 0 sum_cents = 0 for item in items: it = Item(item) itemtext += substitute_all(item_template, it) sum_net_cents += it['total_net_cents'] sum_vat_cents += it['total_vat_cents'] sum_cents += it['total_cents'] values['itemtext'] = itemtext values['totalraw'] = euros(sum_net_cents) values['totalvat'] = euros(sum_vat_cents) values['totalsum'] = euros(sum_cents) values['amount'] = values['totalsum'] text = substitute_all(text, values) sys.stdout.write(text)