diff --git a/Pipfile b/Pipfile index c44c061..4d410dd 100644 --- a/Pipfile +++ b/Pipfile @@ -10,6 +10,7 @@ hyrule = "*" click = "*" pyyaml = "*" toml = "*" +pendulum = "*" [dev-packages] black = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 8f617f2..5632b17 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "992c78da0e149bb7005444fea87ffee6720be3627c3a3f3e646f989e655f3b1b" + "sha256": "83a875f43288c3f36bedc6f8dff4569b0db7cc8b610c3080725210d1589eaaa0" }, "pipfile-spec": 6, "requires": { @@ -63,6 +63,49 @@ "index": "pypi", "version": "==0.1" }, + "pendulum": { + "hashes": [ + "sha256:0731f0c661a3cb779d398803655494893c9f581f6488048b3fb629c2342b5394", + "sha256:1245cd0075a3c6d889f581f6325dd8404aca5884dea7223a5566c38aab94642b", + "sha256:29c40a6f2942376185728c9a0347d7c0f07905638c83007e1d262781f1e6953a", + "sha256:2d1619a721df661e506eff8db8614016f0720ac171fe80dda1333ee44e684087", + "sha256:318f72f62e8e23cd6660dbafe1e346950281a9aed144b5c596b2ddabc1d19739", + "sha256:33fb61601083f3eb1d15edeb45274f73c63b3c44a8524703dc143f4212bf3269", + "sha256:3481fad1dc3f6f6738bd575a951d3c15d4b4ce7c82dce37cf8ac1483fde6e8b0", + "sha256:4c9c689747f39d0d02a9f94fcee737b34a5773803a64a5fdb046ee9cac7442c5", + "sha256:7c5ec650cb4bec4c63a89a0242cc8c3cebcec92fcfe937c417ba18277d8560be", + "sha256:94b1fc947bfe38579b28e1cccb36f7e28a15e841f30384b5ad6c5e31055c85d7", + "sha256:9702069c694306297ed362ce7e3c1ef8404ac8ede39f9b28b7c1a7ad8c3959e3", + "sha256:b06a0ca1bfe41c990bbf0c029f0b6501a7f2ec4e38bfec730712015e8860f207", + "sha256:b6c352f4bd32dff1ea7066bd31ad0f71f8d8100b9ff709fb343f3b86cee43efe", + "sha256:c501749fdd3d6f9e726086bf0cd4437281ed47e7bca132ddb522f86a1645d360", + "sha256:c807a578a532eeb226150d5006f156632df2cc8c5693d778324b43ff8c515dd0", + "sha256:db0a40d8bcd27b4fb46676e8eb3c732c67a5a5e6bfab8927028224fbced0b40b", + "sha256:de42ea3e2943171a9e95141f2eecf972480636e8e484ccffaf1e833929e9e052", + "sha256:e95d329384717c7bf627bf27e204bc3b15c8238fa8d9d9781d93712776c14002", + "sha256:f5e236e7730cab1644e1b87aca3d2ff3e375a608542e90fe25685dae46310116", + "sha256:f888f2d2909a414680a29ae74d0592758f2b9fcdee3549887779cd4055e975db", + "sha256:fb53ffa0085002ddd43b6ca61a7b34f2d4d7c3ed66f931fe599e1a531b42af9b" + ], + "index": "pypi", + "version": "==2.1.2" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" + }, + "pytzdata": { + "hashes": [ + "sha256:3efa13b335a00a8de1d345ae41ec78dd11c9f8807f522d39850f2dd828681540", + "sha256:e1e14750bcf95016381e4d472bad004eef710f2d6417240904070b3d6654485f" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2020.1" + }, "pyyaml": { "hashes": [ "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", @@ -109,6 +152,14 @@ ], "version": "==0.7.8" }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, "toml": { "hashes": [ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", diff --git a/scripts/tqwgp b/scripts/tqwgp index 51a1d0c..24314b9 100644 --- a/scripts/tqwgp +++ b/scripts/tqwgp @@ -3,5 +3,5 @@ if [[ ! $@ ]]; then python3 -m tqwgp_parser --help else - python3 -m tqwgp_parser $@ + python3 -m tqwgp_parser "$@" fi diff --git a/tqwgp_parser/__main__.py b/tqwgp_parser/__main__.py index bf878fb..5d7e98e 100644 --- a/tqwgp_parser/__main__.py +++ b/tqwgp_parser/__main__.py @@ -19,6 +19,7 @@ import json import toml import io +import pendulum from contextlib import redirect_stdout from .files.loaders import load_document_from_project, load_document_with_inheritance from . import parse_quote, parse_invoices @@ -235,6 +236,21 @@ def show( help="Enable recursive documents discovery (from the project specifier).", default=False, ) +@click.option( + "--date-format", + default="YYYY-MM-DD", + help="The date format used for parsing dates (using Pendulum). Set to None to disable altogether.", +) +@click.option( + "--date-locale", + default="en", + help="The date locale used for parsing dates (using Pendulum).", +) +@click.option( + "--date-csv-format", + default="YYYY-MM-DD", + help="The date format to use in CSV export (using Pendulum).", +) def csv( project_specifier, file_format="yaml", @@ -244,6 +260,9 @@ def csv( enable_parsing=True, recursive=False, debug=False, + date_format=None, + date_locale=None, + date_csv_format=None, ): """Load and extract parsed documents to CSV for the project specifier""" loaded_documents = discover_and_loads_documents( @@ -258,25 +277,45 @@ def csv( csv_ouput = io.StringIO() csv_writer = csv_lib.writer(csv_ouput, quoting=csv_lib.QUOTE_MINIMAL) csv_writer.writerow([ - "Project", "Reference", "Date", "Title", "Provider name", "Client name", "Total excl VAT", + "Project", "Reference", "Date (input)", "Date (parsed)", "Title", "Provider name", "Client name", "Total excl VAT", "VAT amount", "Total incl VAT", "Lines count" ]) + all_invoices = [] for document in loaded_documents["documents"]: if document["document_type"] == "invoice": for invoice in document["parsed_document"]["invoices"]: - csv_writer.writerow([ - document["project_name"], - invoice["number"], - invoice["date"], - invoice["title"], - invoice["sect"]["name"], - invoice["client"]["name"], - invoice["price"]["total_vat_excl"], - invoice["price"]["vat"], - invoice["price"]["total_vat_incl"], - len(invoice["lines"]), - ]) + all_invoices.append({ + "invoice": invoice, + "document": document, + # TODO Include date parsing in core parser. + # Allows to set format in definitions? + # "DD MMMM YYYY" + "parsed_date": pendulum.from_format(invoice["date"], date_format, locale=date_locale) if date_format else None, + }) + if date_format: + # By date. + sorted_invoices = sorted(all_invoices, key=lambda i: i['parsed_date']) + else: + # By reference. + sorted_invoices = sorted(all_invoices, key=lambda i: i['invoice']['number']) + for invoice_entry in sorted_invoices: + invoice = invoice_entry["invoice"] + document = invoice_entry["document"] + csv_writer.writerow([ + document["project_name"], + invoice["number"], + invoice["date"], + invoice_entry["parsed_date"].format(date_csv_format) if invoice_entry["parsed_date"] and date_csv_format else "-", + invoice["title"], + invoice["sect"]["name"], + invoice["client"]["name"], + # TODO Options to round, change numeric character (,.), ... + invoice["price"]["total_vat_excl"], + invoice["price"]["vat"], + invoice["price"]["total_vat_incl"], + len(invoice["lines"]), + ]) print(csv_ouput.getvalue())