summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEkaitz Zarraga <ekaitz@elenq.tech>2020-07-20 19:10:33 +0200
committerEkaitz Zarraga <ekaitz@elenq.tech>2020-07-20 23:37:03 +0200
commitb1e27d12c7062b95cb5fa814b98e0055e77ed443 (patch)
tree1aed13ca93d08c01c55b1d0daabb134d489ebb28
parent29421ecbb48450bf22e33bdcf57ee1de7412b0be (diff)
Add subcommands sketch
-rw-r--r--README.md3
-rw-r--r--fracture/__main__.py65
-rw-r--r--fracture/invoice.py61
3 files changed, 94 insertions, 35 deletions
diff --git a/README.md b/README.md
index b6d0525..579d11f 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,8 @@ SQLite database.
Run `fracture configure` for configuration. It will automatically open a sample
configuration file for you. Once it's filled it will store the configuration
file in `$XDG_CONFIG_HOME/fracture/config` (`XDG_CONFIG_HOME` falls back to
-`$HOME/.config`).
+`$HOME/.config`). If configuration file is already set it will load the current
+configuration file for you to edit.
## Storage
diff --git a/fracture/__main__.py b/fracture/__main__.py
index 71ca639..b33dca4 100644
--- a/fracture/__main__.py
+++ b/fracture/__main__.py
@@ -1,7 +1,7 @@
from invoice import Invoice, Tax
import db
-import argparse
+from argparse import ArgumentParser as ap
import tempfile
import subprocess
import sqlite3
@@ -9,14 +9,19 @@ from configparser import ConfigParser
import os
from os import path
+CONFIG_FILE = ""
+
def load_config():
- basedir = os.environ.get('XDG_CONFIG_HOME', None) or \
+ basedir = os.environ.get("XDG_CONFIG_HOME", None) or \
path.join(os.environ["HOME"], ".config")
basedir = path.join(basedir, "fracture")
confile = path.join(basedir, "config")
+ global CONFIG_FILE
+ CONFIG_FILE = confile
+
conf = ConfigParser()
conf.read(confile)
@@ -40,6 +45,13 @@ def load_config():
Invoice.CURRENCY = conf["invoice"].get("currency", "€")
Invoice.CURRENCY_DECIMAL = conf["invoice"].getint("currency_decimal", 2)
+ # TEMPLATE ?
+ # TODO
+ # Or dump a json or something?
+ templatefile = conf["invoice"]["template"]
+ if not path.isabs(templatefile):
+ templatefile = path.join(confile, templatefile)
+
# INVOICE LEVEL TAXES (like IRPF in Spain)
tax = []
for k in conf["taxes"]:
@@ -48,7 +60,7 @@ def load_config():
# DATABASE
Invoice.DB_FILE = path.join(basedir, "invoice.db")
- if not os.path.exists(Invoice.DB_FILE):
+ if not path.exists(Invoice.DB_FILE):
db.create(Invoice.DB_FILE)
def call_editor(filename):
@@ -59,7 +71,7 @@ def call_editor(filename):
process.wait()
def edit(contents):
- """ Edit temporary file with initial `contents` and return it's edited
+ """ Edit temporary file with initial `contents` and return it"s edited
content """
with tempfile.NamedTemporaryFile(mode="w", delete=False) as t:
@@ -75,14 +87,47 @@ def edit(contents):
return edited_content
+def edit_config():
+ # TODO: Generate config file if not created
+ call_editor(CONFIG_FILE)
+
+def new_invoice():
+ num = Invoice.from_config( edit( Invoice().to_config() )).persist()
+ #
+ print(num)
+ # edit(Invoice.load(num).to_config())
+
+def summarize():
+ pass
if __name__ == "__main__":
load_config()
- # TODO
- a = Invoice()
- a.from_config( edit( a.to_config() ))
- num = a.persist()
- print(num)
- edit(Invoice.load(num).to_config())
+
+ # create the top-level parser
+ parser = ap(prog="fracture")
+ #parser.add_argument("--foo", action="store_true", help="foo help")
+ parser.set_defaults(func=lambda: parser.print_help())
+
+ # make subparsers
+ subparsers = parser.add_subparsers(title= "Subcommands",
+ help="sub-command help")
+
+ new_parser = subparsers.add_parser("new", aliases=["n"], help="a help")
+ new_parser.set_defaults(func=new_invoice)
+
+ configure_parser = subparsers.add_parser("configure", aliases=["c","conf"],
+ help="b help")
+ configure_parser.set_defaults(func=edit_config)
+
+
+ summary_parser = subparsers.add_parser("summary", aliases=["s", "sum"],
+ help="get summary")
+ summary_parser.add_argument("--xlsx", action="store_true", help="aaa")
+ summary_parser.add_argument("--quarter", action="store_true", help="aaa")
+ summary_parser.add_argument("--year", action="store_true", help="aaa")
+ summary_parser.set_defaults(func=summarize)
+
+ # parse
+ parser.parse_args().func()
diff --git a/fracture/invoice.py b/fracture/invoice.py
index f0ab2f7..4ea9d71 100644
--- a/fracture/invoice.py
+++ b/fracture/invoice.py
@@ -57,6 +57,7 @@ class Invoice:
self.taxes = taxes or Invoice.DEFAULT_TAXES
def set_series(self, series):
+ """ Series is an integer """
if series not in Invoice.SERIES.keys():
raise ValueError ("Not valid series for Invoice. Valid are %s"
% Invoice.SERIES)
@@ -100,12 +101,13 @@ class Invoice:
return strf.getvalue()
- def from_config(self, config):
+ @classmethod
+ def from_config(cls, config):
cfg = ConfigParser()
cfg.read_string(config)
# PRODUCTS SECTIONS
- self.products = ()
+ products = ()
for s in cfg.sections():
if not s.startswith("product"):
continue
@@ -113,32 +115,41 @@ class Invoice:
un = float(cfg[s]["units"])
pu = float(cfg[s]["price_unit"])
vat = float(cfg[s]["vat"])
- self.products += ( Product(desc,un,pu,vat) ,)
- if cfg[s]["description"] == Invoice.DEFAULT_PRODUCT_DESC:
+ products += ( Product(desc,un,pu,vat) ,)
+ if cfg[s]["description"] == cls.DEFAULT_PRODUCT_DESC:
raise ValueError("Product name not set")
- if len(self.products) == 0:
+ if len(products) == 0:
raise ValueError("No products assigned")
# TAXES SECTION
- self.taxes = ()
+ taxes = ()
if cfg.has_section("taxes"):
for x in cfg["taxes"]:
- self.taxes += ( Tax(x, float(cfg["taxes"][x])) ,)
+ taxes += ( Tax(x, float(cfg["taxes"][x])) ,)
# INVOICE SECTION
if not cfg.has_section("invoice"):
raise ValueError("[invoice] section needed")
- i = cfg["invoice"]
- self.set_series(int(i["series"]))
- self.date = datetime.strptime(i["date"], "%Y-%m-%d").date()
- self.notes = i["notes"]
- self.set_type(i["type"])
- self.customer= Customer(i["customer-name"],
- i["customer-address"],
- i["customer-id"])
- if i["customer-name"] == Invoice.DEFAULT_CUSTOMER_DESC:
+ i = cfg["invoice"]
+ series = int(i["series"])
+ date = datetime.strptime(i["date"], "%Y-%m-%d").date()
+ notes = i["notes"]
+ type = i["type"]
+ customer = Customer( i["customer-name"],
+ i["customer-address"],
+ i["customer-id"] )
+ if i["customer-name"] == cls.DEFAULT_CUSTOMER_DESC:
raise ValueError("Customer name not set")
+ return cls(
+ type,
+ series,
+ date,
+ notes,
+ products,
+ customer,
+ taxes)
+
def persist(self):
conn = sqlite3.connect(Invoice.DB_FILE)
c = conn.cursor()
@@ -185,17 +196,16 @@ class Invoice:
conn.close()
return self.id
- def load(id):
- with sqlite3.connect(Invoice.DB_FILE) as conn:
+ @classmethod
+ def load(cls, id):
+ with sqlite3.connect(cls.DB_FILE) as conn:
conn.row_factory = sqlite3.Row
- invoice = Invoice()
-
c = conn.cursor()
# PRODUCTS
products = ()
- c.execute("""SELECT * FROM products WHERE invoice_id = ?""", (id,))
+ c.execute("SELECT * FROM products WHERE invoice_id = ?", (id,))
res = c.fetchone()
while res is not None:
desc = res["description"]
@@ -207,16 +217,16 @@ class Invoice:
# TAXES
taxes = ()
- c.execute("""SELECT * FROM taxes WHERE invoice_id = ?""", (id,))
+ c.execute("SELECT * FROM taxes WHERE invoice_id = ?", (id,))
res = c.fetchone()
while res is not None:
taxes += (Tax(res["name"], res["ratio"]) ,)
res = c.fetchone()
# INVOICE
- c.execute("""SELECT * FROM invoices WHERE id = ?""", (id,))
+ c.execute("SELECT * FROM invoices WHERE id = ?", (id,))
res = c.fetchone()
- return Invoice(
+ return cls(
res["type"],
res["series"],
res["date"],
@@ -229,4 +239,7 @@ class Invoice:
def format(self):
# TODO
+ # https://bugs.python.org/issue35111
pass
+
+