diff options
-rw-r--r-- | fracture/__main__.py | 34 | ||||
-rw-r--r-- | fracture/invoice.py | 67 |
2 files changed, 76 insertions, 25 deletions
diff --git a/fracture/__main__.py b/fracture/__main__.py index d0305ab..c51c556 100644 --- a/fracture/__main__.py +++ b/fracture/__main__.py @@ -1,6 +1,7 @@ -from invoice import Invoice, Tax +from invoice import Invoice, Tax, Product import db +from csv import DictWriter import datetime from functools import wraps from argparse import ArgumentParser as ap @@ -27,6 +28,10 @@ def load_config(): conf = ConfigParser() conf.read(confile) + # VAT-types + for k in conf["vat"]: + Product.VATS += (float(conf["vat"][k]),) + # SERIES for k in conf["series"]: Invoice.SERIES[int(k)] = conf["series"][k] @@ -55,10 +60,10 @@ def load_config(): templatefile = path.join(confile, templatefile) # INVOICE LEVEL TAXES (like IRPF in Spain) - tax = [] + tax = () for k in conf["taxes"]: - tax.append(Tax(k, conf.getfloat("taxes",k))) - Invoice.DEFAULT_TAXES = tuple(tax) + tax += (Tax(k, conf.getfloat("taxes",k)),) + Invoice.DEFAULT_TAXES = tax # DATABASE Invoice.DB_FILE = path.join(basedir, "invoice.db") @@ -112,7 +117,26 @@ def new_invoice(): @command def summarize(xlsx=False, year=None, quarter=None): invoices = Invoice.load_by_date(year, quarter) - rows = map(lambda x: x.to_row(), invoices) + rows = sorted((map(lambda x: x.to_row(), invoices)), + key=lambda x: x["type"]) + + keys = list(rows[0].keys()) + for r in rows: + for k in r.keys(): + if k not in keys: + # TODO: + # Inserts all the taxes at the end, but this is not cool + # because it needs information about how is the invoice + # arranged + # Maybe move this to a static function in invoices and call it + # summary and make it work with some logic like: create the + # keys first and then go setting them in order + keys.insert(-1, k) + import sys + wrtr = DictWriter(sys.stdout, keys) + wrtr.writeheader() + for r in rows: + wrtr.writerow(r) if __name__ == "__main__": diff --git a/fracture/invoice.py b/fracture/invoice.py index 84c0f9b..b3fa6b6 100644 --- a/fracture/invoice.py +++ b/fracture/invoice.py @@ -1,6 +1,7 @@ import sqlite3 from datetime import date, datetime, timedelta from configparser import ConfigParser +from collections import OrderedDict import io class Tax: @@ -9,6 +10,7 @@ class Tax: self.ratio = ratio class Product: + VATS = () def __init__(self, description, units=1.0, price_unit=0.0, vat = 0.21): if len(description) == 0: @@ -16,7 +18,15 @@ class Product: self.description = description self.units = units self.price_unit = price_unit - self.vat = vat + if vat in self.VATS: + self.vat = vat + else: + raise ValueError("Product: No valid VAT. Valid are:"+\ + str(Product.VATS)) + def calc_base(self): + return self.price_unit * self.units + def calc_charged_vat(self): + return self.calc_base() * self.vat class Customer: def __init__(self, name, id = "", address = ""): @@ -323,26 +333,43 @@ class Invoice: pass def to_row(self): - psum = ((i.price_unit*i.units, - i.price_unit*i.units*i.vat) for i in self.products) - base = 0 - vat = 0 - for i in psum: - base += i[0] - vat += i[1] - - row = { - "type": self.type, - "series": self.series, - "id": self.format_id(), - "date": self.date.strftime("%Y-%m-%d"), - "customer_id": self.customer.id, - "customer_name": self.customer.name, - "base": round(base, self.CURRENCY_DECIMAL), - "vat": round(vat, self.CURRENCY_DECIMAL) - } + row = OrderedDict( + type = self.type, + id = self.format_id(), + date = self.date.strftime("%Y-%m-%d"), + customer_id = self.customer.id, + customer_name = self.customer.name, + ) + + total_base = 0 + total_charged = 0 + + vats = sorted(Product.VATS) + to_base_key = lambda v: "base("+str(v*100)+"%)" + to_vat_key = lambda v: "VAT("+str(v*100)+"%)" + for vat in vats: + ps = filter(lambda p: p.vat == vat, self.products) + + vat_base = 0 + charged_vat = 0 + for product in ps: + vat_base += product.calc_base() + charged_vat += product.calc_charged_vat() + row[to_base_key(vat)] = round(vat_base, self.CURRENCY_DECIMAL) + row[to_vat_key(vat)] = round(charged_vat, self.CURRENCY_DECIMAL) + + total_base += vat_base + total_charged += vat_base + charged_vat + + row["base(TOTAL)"] = round(total_base, self.CURRENCY_DECIMAL) + + to_tax_key = lambda tax: tax.name + "("+ str(tax.ratio*100) +"%)" for i in self.taxes: - row["tax-" + i.name] = round(i.ratio * base, self.CURRENCY_DECIMAL) + tax_amount = i.ratio * total_base + row[to_tax_key(i)] = round(tax_amount, self.CURRENCY_DECIMAL) + total_charged += tax_amount + + row["TOTAL"] = round(total_charged, self.CURRENCY_DECIMAL) return row |