diff options
author | Ekaitz Zarraga <ekaitz@elenq.tech> | 2020-08-08 14:48:10 +0200 |
---|---|---|
committer | Ekaitz Zarraga <ekaitz@elenq.tech> | 2020-08-08 14:48:10 +0200 |
commit | 2ccbf8b690c4ea052ec3388bfabeb4b6943a74db (patch) | |
tree | 1be225f631fa600592f0ffcf957a95cabd79bf8a /fracture/invoice.py | |
parent | af0515ac909db46ec1fedc8f3cf9d884ff21ce31 (diff) |
Basic output control: summary, latex and json
Diffstat (limited to 'fracture/invoice.py')
-rw-r--r-- | fracture/invoice.py | 121 |
1 files changed, 64 insertions, 57 deletions
diff --git a/fracture/invoice.py b/fracture/invoice.py index e23d257..d795340 100644 --- a/fracture/invoice.py +++ b/fracture/invoice.py @@ -9,6 +9,10 @@ class Conf: # MOVE ALL THE CONFIG HERE CURRENCY = None CURRENCY_DECIMAL = 0 + @classmethod + def round(cls, num): + return round(num, cls.CURRENCY_DECIMAL) + class Tax: def __init__(self, name="", ratio = 0.0): @@ -34,16 +38,24 @@ class Product: else: raise ValueError("Product: No valid VAT. Valid are:"+\ str(Product.VATS)) - def calc_base(self): + @property + def total(self): + return self.base+self.charged_vat + @property + def base(self): return self.price_unit * self.units - def calc_charged_vat(self): - return self.calc_base() * self.vat + @property + def charged_vat(self): + return self.base * self.vat def to_dict(self): return { "description": self.description, "units": self.units, - "price-unit": round(self.price_unit, Conf.CURRENCY_DECIMAL), - "vat": self.vat + "price-unit": Conf.round(self.price_unit), + "vat": self.vat, + "vat-charged": Conf.round(self.charged_vat), + "base": Conf.round(self.base), + "total": Conf.round(self.total) } class Customer: @@ -71,10 +83,10 @@ class Invoice: products = None, customer = None, taxes = None, - id = None): + number = None): # Initializes to empty state if not set self.set_type(type or tuple(Invoice._TYPES)[0]) - self.id = id + self.number = number self.set_series(series or tuple(Invoice.SERIES.keys())[0]) self.date = idate or date.today() self.notes = notes or "" @@ -96,8 +108,9 @@ class Invoice: % Invoice._TYPES) self.type = type - def format_id(self): - return Invoice.ID_FORMAT(self.series, self.date, self.id) + @property + def id(self): + return Invoice.ID_FORMAT(self.series, self.date, self.number) def __repr__(self): return "<Invoice object>\n\t"+\ @@ -180,21 +193,10 @@ class Invoice: @classmethod - def load_by_idrepr(cls, id_repr, type="sent"): - with sqlite3.connect(cls.DB_FILE) as conn: - conn.row_factory = sqlite3.Row - - c = conn.cursor() - c.execute("""SELECT id, series, type FROM invoices - WHERE id_repr = ? AND type = ?""", (id_repr, type)) - res = c.fetchone() - if res is None: - return None - return cls.load(res["id"], res["series"], type) - - @classmethod - def load(cls, id, series, type="sent"): + def load(cls, id, type="sent", debug=False): with sqlite3.connect(cls.DB_FILE) as conn: + if debug: + conn.set_trace_callback(print) conn.row_factory = sqlite3.Row c = conn.cursor() @@ -202,8 +204,7 @@ class Invoice: # PRODUCTS products = () c.execute("""SELECT * FROM products WHERE invoice_id = ? - AND invoice_series = ? - AND invoice_type = ?""", (id, series, type)) + AND invoice_type = ?""", (id, type)) res = c.fetchone() while res is not None: desc = res["description"] @@ -216,8 +217,7 @@ class Invoice: # TAXES taxes = () c.execute("""SELECT * FROM taxes WHERE invoice_id = ? - AND invoice_series = ? - AND invoice_type = ?""", (id, series, type)) + AND invoice_type = ?""", (id, type)) res = c.fetchone() while res is not None: taxes += (Tax(res["name"], res["ratio"]) ,) @@ -225,11 +225,10 @@ class Invoice: # INVOICE c.execute("""SELECT * FROM invoices WHERE id = ? - AND series = ? - AND type = ?""", (id, series, type)) + AND type = ?""", (id, type)) res = c.fetchone() return cls( - id = id, + number = res["number"], type = res["type"], series = res["series"], idate = datetime.strptime(res["date"],"%Y-%m-%d").date(), @@ -249,13 +248,12 @@ class Invoice: conn.row_factory = sqlite3.Row c = conn.cursor() - c.execute(("SELECT id, series, type FROM invoices " + cond), vals) - return tuple( cls.load( int(r["id"]), - int(r["series"]), - r["type"] ) for r in c.fetchall() ) + c.execute(("SELECT id, type FROM invoices " + cond), vals) + return tuple( cls.load( r["id"], r["type"] ) \ + for r in c.fetchall() ) @classmethod - def latest_id(cls, series, type, debug=False): + def latest_number(cls, series, type, debug=False): with sqlite3.connect(cls.DB_FILE) as conn: if debug: conn.set_trace_callback(print) @@ -263,15 +261,15 @@ class Invoice: conn.row_factory = sqlite3.Row c = conn.cursor() - c.execute("""SELECT id FROM invoices + c.execute("""SELECT number FROM invoices WHERE series = ? AND type = ? - ORDER BY(id) DESC LIMIT 1""", (series, type)) + ORDER BY(number) DESC LIMIT 1""", (series, type)) res = c.fetchall() - latest_id = 0 + latest_number = 0 if len(res) != 0: - latest_id = int(res[0]["id"]) - return latest_id + latest_number = int(res[0]["number"]) + return latest_number @classmethod def load_by_date(cls, year, quarter=None): @@ -297,14 +295,14 @@ class Invoice: def persist(self): conn = sqlite3.connect(Invoice.DB_FILE) - if self.id is None: - self.id = self.latest_id(self.series, self.type) + 1 + if self.number is None: + self.number = self.latest_number(self.series, self.type) + 1 c = conn.cursor() c.execute("""INSERT INTO invoices ( type, series, + number, id, - id_repr, date, notes, customer_id, @@ -313,8 +311,8 @@ class Invoice: ) VALUES (?,?,?,?,?,?,?,?,?)""", ( self.type, self.series, + self.number, self.id, - self.format_id(), self.date.strftime("%Y-%m-%d"), self.notes, self.customer.id, @@ -324,15 +322,13 @@ class Invoice: for p in self.products: c.execute("""INSERT INTO products ( invoice_id, - invoice_series, invoice_type, description, units, price_unit, vat - ) VALUES (?,?,?,?,?,?,?)""",( + ) VALUES (?,?,?,?,?,?)""",( self.id, - self.series, self.type, p.description, p.units, @@ -342,36 +338,47 @@ class Invoice: for x in self.taxes: c.execute("""INSERT INTO taxes ( invoice_id, - invoice_series, invoice_type, name, ratio - ) VALUES (?,?,?,?,?)""", ( + ) VALUES (?,?,?,?)""", ( self.id, - self.series, self.type, x.name, x.ratio)) conn.commit() conn.close() - return self.id + return (self.id, self.type) + @property + def vat_charged(self): + return sum(p.charged_vat for p in self.products) + @property + def base(self): + return sum(p.base for p in self.products) + @property + def total(self): + return sum(p.total for p in self.products) + \ + sum(self.base*t.ratio for t in self.taxes) def to_dict(self): return { "products": tuple(p.to_dict() for p in self.products), - "taxes": tuple(t.to_dict() for t in self.taxes), + "taxes": tuple(dict(t.to_dict(), applied=Conf.round(self.base*t.ratio)) for t in self.taxes), "type": self.type, - "id": self.format_id(), + "id": self.id, "date": self.date.strftime("%Y-%m-%d"), "customer": { "id": self.customer.id, "name": self.customer.name, "address": self.customer.address, }, + "base": self.base, "notes": self.notes, + "total": Conf.round(self.total), + "vat-charged": self.vat_charged } def to_json(self): return json.dumps(self.to_dict()) @@ -380,7 +387,7 @@ class Invoice: row = OrderedDict( type = self.type, - id = self.format_id(), + id = self.id, date = self.date.strftime("%Y-%m-%d"), customer_id = self.customer.id, customer_name = self.customer.name, @@ -398,9 +405,9 @@ class Invoice: 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, Conf.CURRENCY_DECIMAL) + vat_base += product.base + charged_vat += product.charged_vat + row[to_base_key(vat)] = round(vat_base, Conf.CURRENCY_DECIMAL) row[to_vat_key(vat)] = round(charged_vat, Conf.CURRENCY_DECIMAL) total_base += vat_base |