diff --git a/README.rst b/README.rst index 1e1d1c3..87b4c2d 100644 --- a/README.rst +++ b/README.rst @@ -1,9 +1,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Sample plugin for ofxstatement +ING Belgium plugin for ofxstatement ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This project provides a boilerplate for custom plugins for ofxstatement. - `ofxstatement`_ is a tool to convert proprietary bank statement to OFX format, suitable for importing to GnuCash. Plugin for ofxstatement parses a particular proprietary bank statement format and produces common data @@ -16,75 +14,7 @@ Users of ofxstatement have developed several plugins for their banks. They are listed on main `ofxstatement`_ site. If your bank is missing, you can develop your own plugin. -Setting up development environment -================================== - -It is recommended to use ``virtualenv`` make a clean development environment. -Setting up dev environment for writing a plugin is easy:: - - $ git clone https://github.com/kedder/ofxstatement-sample ofxstatement-yourbank - $ cd ofxstatement-yourbank - $ virtualenv -p python3 --no-site-packages .venv - $ . .venv/bin/activate - (.venv)$ python setup.py develop - -This will download all the dependencies and install them into your virtual -environment. After this, you should be able to do:: - - (.venv)$ ofxstatement list-plugins - The following plugins are available: - - sample Sample plugin (for developers only) - - - -Your own plugin -=============== - -To create your own plugin, follow these steps: - -* Edit ``setup.py`` and provide relevant metadata for your plugin. Pay - close attention to ``entry_points`` parameter to ``setup`` function: it - lists plugins you are registering within ofxstatement. Give meaningful - name to the plugin and provide plugin class name -* Replace contents of ``README.rst`` with description of your plugin -* Rename ``ofxstatement/plugins/sample.py`` to match plugin package name - you have provided in ``entry_points`` parameter. -* Open renamed sample.py and rename ``SamplePlugin`` and ``SampleParser`` - classes to match your plugin class name. -* Now, draw the rest of the owl (c). - -.. _ofxstatement-sample: https://github.com/kedder/ofxstatement-sample - -Your ``StatementParser`` is the main object that does all the hard work. It -has only one public method: ``parse()``, that should return -``ofxstatement.statement.Statement`` object, filled with data from given input. -The default implementation, however, splits this work into two parts: -``split_records()`` to split the whole file into logical parts, e.g. -transaction records, and ``parse_record()`` to extract information from -individual record. See ``src/ofxstatement/parser.py`` for details. If your -statement' format looks like CSV file, you might find ``CsvStatementParser`` -class useful: it simplifies mapping bettween CSV columns and ``StatementLine`` -attributes. - -``Plugin`` interface consists only of ``get_parser()`` method, that returns -configured StatementParser object for given input filename. Docstrings on -Plugin class is also useful for describing the purpose of your plugin. First -line of it is visible in ``ofxstatement list-plugins`` output. - -Testing -======= - -Test your code as you would do with any other project. To make sure -ofxstatement is still able to load your plugin, run:: - - (.venv)$ ofxstatement list-plugins - -You should be able to see your plugin listed. - -After you are done -================== +Usage +===== -After your plugin is ready, feel free to open an issue on `ofxstatement`_ -project to include your plugin in "known plugin list". That would hopefully -make life of other clients of your bank easier. + $ ofxstatement convert -t ingbe input.csv output.ofx \ No newline at end of file diff --git a/setup.py b/setup.py index 109827c..a3aa771 100755 --- a/setup.py +++ b/setup.py @@ -9,12 +9,12 @@ with open('README.rst') as f: long_description = f.read() -setup(name='ofxstatement-sample', +setup(name='ofxstatement-be-ing', version=version, - author="Andrey Lebedev", - author_email="andrey@lebedev.lt", - url="https://github.com/kedder/ofxstatement", - description=("Sample plugin for ofxstatement"), + author="Theodore Marescaux", + author_email="theo.public@gmail.com", + url="https://github.com/TheoMarescaux/ofxstatement-be-ing", + description=("OFXStatement plugin for ING (Belgium)"), long_description=long_description, license="GPLv3", keywords=["ofx", "banking", "statement"], @@ -32,7 +32,7 @@ namespace_packages=["ofxstatement", "ofxstatement.plugins"], entry_points={ 'ofxstatement': - ['sample = ofxstatement.plugins.sample:SamplePlugin'] + ['ingbe = ofxstatement.plugins.ingbe:IngBePlugin'] }, install_requires=['ofxstatement'], include_package_data=True, diff --git a/src/ofxstatement/plugins/ingbe.py b/src/ofxstatement/plugins/ingbe.py new file mode 100644 index 0000000..1fdc56a --- /dev/null +++ b/src/ofxstatement/plugins/ingbe.py @@ -0,0 +1,72 @@ +import csv + +from ofxstatement import statement +from ofxstatement.plugin import Plugin +from ofxstatement.parser import CsvStatementParser +from ofxstatement.parser import StatementParser +from ofxstatement.statement import StatementLine + +class IngBePlugin(Plugin): + """ING Belgium Plugin + """ + + def get_parser(self, filename): + f = open(filename, 'r', encoding=self.settings.get("charset", "ISO-8859-1")) + parser = IngBeParser(f) + #parser.statement.bank_id = "ING Belgium" + #parser.statement.bank_id = self.settings.get('bank', 'ING Belgium') + return parser + +class IngBeParser(CsvStatementParser): + + date_format = "%d/%m/%Y" + mappings = { + 'check_no': 3, + 'date': 4, + 'payee': 2, + 'memo': 9, + 'amount': 6 + } + + def parse(self): + """Main entry point for parsers + + super() implementation will call to split_records and parse_record to + process the file. + """ + stmt = super(IngBeParser, self).parse() + statement.recalculate_balance(stmt) + return stmt + + def split_records(self): + """Return iterable object consisting of a line per transaction + """ + + reader = csv.reader(self.fin) + next(reader, None) + return reader + + def parse_record(self, line): + """Parse given transaction line and return StatementLine object + """ + + # Remove non CSV cr*p and zero-value notifications + if(line[5] and not (line[6]=="0" and line[7]=="0")): + transaction_id = line[3] + date = line[4] + date_value = line[5] + account_to = line[2] + currency = line[8] + + # fix amount - Well done ING, mixing the comma as decimal separator and as CSV delimiter. Way to go... + line[6] = line[6]+"."+line[7] + amount = line[6] + + # Pack info in description + line[9] = line[9]+"-"+line[10]+"-"+line[11] + description = line[9] + + stmtline = super(IngBeParser, self).parse_record(line) + stmtline.trntype = 'DEBIT' if stmtline.amount < 0 else 'CREDIT' + + return stmtline diff --git a/src/ofxstatement/plugins/sample.py b/src/ofxstatement/plugins/sample.py deleted file mode 100644 index 75129cb..0000000 --- a/src/ofxstatement/plugins/sample.py +++ /dev/null @@ -1,36 +0,0 @@ -from ofxstatement.plugin import Plugin -from ofxstatement.parser import StatementParser -from ofxstatement.statement import StatementLine - - -class SamplePlugin(Plugin): - """Sample plugin (for developers only) - """ - - def getParser(self, filename): - return SampleParser(filename) - - -class SampleParser(StatementParser): - def __init__(self, filename): - self.filename = filename - - def parse(self): - """Main entry point for parsers - - super() implementation will call to split_records and parse_record to - process the file. - """ - with open(self.filename, "r") as f: - self.input = f - return super(SampleParser, self).parse() - - def split_records(self): - """Return iterable object consisting of a line per transaction - """ - return [] - - def parse_record(self, line): - """Parse given transaction line and return StatementLine object - """ - return StatementLine()