diff --git a/CHANGELOG b/CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ *Correction bug 406903: no message when Skrooge can't open .skg file given on command line *Correction bug 406904: skrooge command-line help "--+[URL]" doesn't match its behavior *Correction bug 406741: QFX Date Import + *Feature: Import backend aqbanking allows to import accounts without an IBAN. -- Stephane MANKOWSKI xxxx diff --git a/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-aqbanking.desktop b/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-aqbanking.desktop --- a/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-aqbanking.desktop +++ b/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-aqbanking.desktop @@ -40,13 +40,13 @@ X-KDE-PluginInfo-Author=Bernhard Scheirle X-KDE-PluginInfo-Email=bernhard@scheirle.de X-KDE-PluginInfo-Name=aqbanking -X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Version=1.2.0 X-KDE-PluginInfo-Website=http://skrooge.org/ X-KDE-PluginInfo-Category=Plugins X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-SKROOGE-getaccounts=skrooge-sabb.py listaccounts X-SKROOGE-getaccountid="(.*)" X-SKROOGE-getbulk=skrooge-sabb.py bulkdownload --output_folder "%1" %parameter1 -X-SKROOGE-csvcolumns=date|mode|comment|payee|amount +X-SKROOGE-csvcolumns=date|mode|comment|payee|amount|unit diff --git a/plugins/import/skrooge_import_backend/skrooge-sabb.py b/plugins/import/skrooge_import_backend/skrooge-sabb.py --- a/plugins/import/skrooge_import_backend/skrooge-sabb.py +++ b/plugins/import/skrooge_import_backend/skrooge-sabb.py @@ -25,6 +25,12 @@ Changelog: +1.2.0 - 2019.04.28 + * Allow processing of accounts without an IBAN + (e.g credit card accounts). + In this case a fake IBAN is used: + XX00 + 1.1.0 - 2018.05.21 * Added command line parameter --terminal-emulator @@ -46,66 +52,90 @@ import tempfile from distutils.version import LooseVersion -__VERSION__ = "1.1.0" +__VERSION__ = "1.2.0" + class Account(object): def __init__(self): self.bank_number = "" self.account_number = "" self.iban = "" + self.fake_iban = False def toString(self): - return 'Account(' + self.bank_number + ', ' + self.account_number + ', ' + self.iban + ')' + return 'Account({}, {}, {})'.format(self.bank_number, + self.account_number, + self.iban) def isValid(self): - return self.bank_number != "" and self.account_number != "" and self.iban != "" + return self.bank_number != "" \ + and self.account_number != "" \ + and self.iban != "" + + @staticmethod + def _parse_single(file_content, regex): + match = regex.search(file_content) + if match: + return match.group(1) + return "" + + def parse(self, file_content): + regex_account_number = re.compile('accountNumber=\"(.*)\"') + regex_bank_number = re.compile('bankCode=\"(.*)\"') + regex_iban = re.compile('iban=\"(.*)\"') + + self.account_number = self._parse_single(file_content, regex_account_number) + self.bank_number = self._parse_single(file_content, regex_bank_number) + self.iban = self._parse_single(file_content, regex_iban) + + if self.iban == "": + self.fake_iban = True + self.iban = "XX00{}{}".format(self.bank_number, self.account_number) + + def matches(self, bank_number, account_number): + return self.bank_number == bank_number \ + and self.account_number == account_number class Accounts(object): def __init__(self): self._account_map = {} - def account_map_key(self, bank_number, account_number): + def _account_map_key(self, bank_number, account_number): return str(bank_number) + '.' + str(account_number) + def _update_account_map(self, account): + if not account.isValid(): + return + + overwrite = True + key = self._account_map_key(account.bank_number, account.account_number) + if key in self._account_map: + # Only overwrite the IBAN if the currently stored one is fake: + overwrite = self._account_map[key].fake_iban + + if overwrite: + self._account_map[key] = account + def _build_iban_map(self): self._account_map = {} - regex_account_number = re.compile('accountNumber=\"(.*)\"') - regex_bank_number = re.compile('bankCode=\"(.*)\"') - regex_iban = re.compile('iban=\"(.*)\"') - #regex_bic = re.compile('bic=\"(.*)\"') - accounts_folder = os.path.expanduser('~/.aqbanking/settings/accounts/') for file_name in os.listdir(accounts_folder): if not file_name.endswith('.conf'): continue file_path = os.path.join(accounts_folder, file_name) with open(file_path, 'r') as account_file: content = account_file.read() account = Account() - match = regex_account_number.search(content) - if match: - account.account_number = match.group(1) - match = regex_bank_number.search(content) - if match: - account.bank_number = match.group(1) - match = regex_iban.search(content) - if match: - account.iban = match.group(1) - # match = regex_bic.search(content) - # if match: - # account.bic = match.group(1) - # print(account.toString()) - if account.isValid(): - key = self.account_map_key(account.bank_number, account.account_number) - self._account_map[key] = account + account.parse(content) + self._update_account_map(account) def get_account(self, bank_number, account_number): if not self._account_map: self._build_iban_map() - key = self.account_map_key(bank_number, account_number) + key = self._account_map_key(bank_number, account_number) if key in self._account_map: return self._account_map[key] else: @@ -273,7 +303,7 @@ reader = csv.DictReader(aqbanking_output.splitlines(), dialect=AqDialectTransfairs) fieldnames_output = ['date', 'mode', 'comment', 'payee', 'amount', 'unit'] output, writer = self.get_csv_writer(fieldnames_output, generateHeader) - for record in reader: + for record in sorted(reader, key=lambda row: row['date']): row = {} row['date'] = record['date'] row['mode'] = record['purpose']