├── .github └── workflows │ └── python-app.yml ├── .gitignore ├── LICENSE ├── README.md ├── dkb2homebank.py ├── dkb2homebankTest.py └── testfiles ├── cash.csv ├── cash_empty.csv ├── expected-output ├── cashHomebank.csv ├── giroHomebank.csv ├── tagesgeldHomebank.csv ├── visaHomebank.csv ├── visaNewHomebank.csv └── visaRangeHomebank.csv ├── giro.csv ├── tagesgeld.csv ├── unknown_format.csv ├── visa.csv ├── visaNew.csv └── visaRange.csv /.github/workflows/python-app.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python application 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python 3.8 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: 3.8 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install flake8 pytest 27 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 28 | - name: Lint with flake8 29 | run: | 30 | # stop the build if there are Python syntax errors or undefined names 31 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 32 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 33 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 34 | - name: Test with pytest 35 | run: | 36 | ./dkb2homebankTest.py 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | __pycache__/ 3 | *.pyc 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Hermann Vocke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dkb2homebank ![Python application](https://github.com/hamvocke/dkb2homebank/workflows/Python%20application/badge.svg) 2 | ============ 3 | 4 | This script converts CSV account reports from [Deutsche Kreditbank (DKB)](https://www.dkb.de) to a 5 | CSV format that can be imported by the personal finance software 6 | [Homebank](http://homebank.free.fr/). 7 | 8 | You can find further instructions on [my blog](http://www.hamvocke.com/blog/import-dkb-accounts-into-homebank/). 9 | 10 | How to run the script 11 | --------------------- 12 | 13 | The script can convert several kinds of CSV export formats you want to convert. Currently supported are: 14 | 15 | * DKB Cash (as of the legacy web portal) 16 | * DKB Visa (as of the legacy web portal) 17 | * DKB Giro (as of the new web portal introduced in 2023) 18 | * DKB Visa (as of the new web portal introduced in 2023) 19 | * DKB Tagesgeld (as of the new web portal introduced in 2023) 20 | 21 | I recommend generating CSV files via the new web portal. 22 | 23 | To convert a CSV file generated from the DKB portal run: 24 | 25 | ./dkb2homebank.py yourCsvFile.csv 26 | 27 | You can also choose an alternative path for your output file, if the standard in the working directory doesn't do it for you. Use `--output-file` or `-o` for that: 28 | 29 | ./dkb2homebank.py yourCsvFile.csv --output-file ~/Documents/Finances/import_to_homebank.csv 30 | 31 | 32 | Importing into Homebank 33 | ----------------------- 34 | Import the converted CSV file into Homebank by going to `File -> Import` and selecting the _output_ file you got when running your script. 35 | 36 | **Note**: If Homebank tells you that your CSV file is invalid, go to `Settings -> Import/Export` and make sure that the `Delimiter` is set to `semicolon` and try importing again. 37 | 38 | Requirements 39 | ------------ 40 | To run this script, you need Python 3.4 or higher. I've verified that the exported CSV can be imported successfully on Homebank *5.0.0* and above. 41 | 42 | Run the tests 43 | ------------- 44 | I have included a (admittedly very small) set of tests to help a little bit during development. 45 | These tests use Python's _unittest_ module and can be executed using: 46 | 47 | ./dkb2homebankTest.py 48 | 49 | You can also test the script manually by using the provided testfiles: 50 | 51 | ./dkb2homebank.py testfiles/cash.csv 52 | ./dkb2homebank.py testfiles/visa.csv 53 | ./dkb2homebank.py testfiles/giro.csv 54 | ./dkb2homebank.py testfiles/visaNew.csv 55 | 56 | -------------------------------------------------------------------------------- /dkb2homebank.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import argparse 4 | import csv 5 | import sys 6 | import os 7 | from enum import Enum 8 | from datetime import datetime 9 | 10 | 11 | class DKB(csv.Dialect): 12 | delimiter = ';' 13 | quotechar = '"' 14 | doublequote = True 15 | skipinitialspace = False 16 | lineterminator = '\n' 17 | quoting = csv.QUOTE_MINIMAL 18 | 19 | 20 | class InvalidInputException(Exception): 21 | """Exception for input CSVs that seem not to be valid DKB input files.""" 22 | def __init__(self, message): 23 | self.message = message 24 | 25 | 26 | csv.register_dialect("dkb", DKB) 27 | 28 | cash_field_names = ["buchungstag", 29 | "wertstellung", 30 | "buchungstext", 31 | "beguenstigter", 32 | "verwendungszweck", 33 | "kontonummer", 34 | "blz", 35 | "betrag", 36 | "glaeubigerID", 37 | "mandatsreferenz", 38 | "kundenreferenz"] 39 | 40 | old_visa_field_names = ["abgerechnet", 41 | "wertstellung", 42 | "belegdatum", 43 | "beschreibung", 44 | "betrag", 45 | "urspruenglicherBetrag"] 46 | 47 | new_visa_field_names = ["belegdatum", 48 | "wertstellung", 49 | "status", 50 | "beschreibung", 51 | "umsatztyp", 52 | "betrag", 53 | "fremdwaehrungsbetrag"] 54 | 55 | giro_field_names = ["buchungsdatum", 56 | "wertstellung", 57 | "status", 58 | "zahlungspflichtige*r", 59 | "zahlungsempfänger*in", 60 | "verwendungszweck", 61 | "umsatztyp", 62 | "IBAN", 63 | "betrag", 64 | "gläubiger-id", 65 | "mandatsreferenz", 66 | "kundenreferenz"] 67 | 68 | homebank_field_names = ["date", 69 | "paymode", 70 | "info", 71 | "payee", 72 | "memo", 73 | "amount", 74 | "category", 75 | "tags"] 76 | 77 | class CsvFileTypes(Enum): 78 | UNKNOWN = "unknown" # We failed to auto-detect the csv file's format 79 | CASH = "cash" # "Cash" account reports, generated by the pre-2023 web portal (ISO-8859-1) 80 | OLD_VISA = "old visa" # "Visa" account reports, generated by the pre-2023 web portal (ISO-8859-1) 81 | GIRO = "giro" # "Giro" and "Tagesgeld" account reports, generated by the new (2023) web portal (UTF-8-sig) 82 | NEW_VISA = "new visa" # "Visa" account reports, generated by the new (2023) web portal (UTF-8-sig) 83 | 84 | def detect_csv_format(file_path): 85 | header = None 86 | try: 87 | header = _read_header_line(file_path, "utf-8-sig") 88 | except UnicodeDecodeError: 89 | header = _read_header_line(file_path, "iso-8859-1") 90 | 91 | if header is None: 92 | return CsvFileTypes.UNKNOWN 93 | 94 | if header.startswith("\"Kreditkarte:\""): 95 | return CsvFileTypes.OLD_VISA 96 | 97 | if header.startswith("\"Kontonummer:\""): 98 | return CsvFileTypes.CASH 99 | 100 | # "Konto" is the old-style export, "Girokonto" was introduced around November 2024 101 | if header.startswith("\"Konto\"") or header.startswith("\"Girokonto\"") or header.startswith("\"Tagesgeld\""): 102 | return CsvFileTypes.GIRO 103 | 104 | if header.startswith("\"Karte\""): 105 | return CsvFileTypes.NEW_VISA 106 | 107 | return CsvFileTypes.UNKNOWN 108 | 109 | def _read_header_line(file_path, encoding): 110 | with open(file_path, 'r', encoding=encoding) as file: 111 | header = file.readline() 112 | file.seek(0) 113 | return header 114 | 115 | def _open_csv(file_path, field_names, encoding): 116 | with open(file_path, 'r', encoding=encoding) as file: 117 | dialect = csv.Sniffer().sniff(file.read(1024)) 118 | file.seek(0) 119 | return csv.DictReader(find_transaction_lines(file), dialect=dialect, fieldnames=field_names) 120 | 121 | def convert_cash(file_path, output_file="cashHomebank.csv"): 122 | """ 123 | Convert a DKB cash file (i.e. normal bank account) to a homebank-readable import CSV. 124 | 125 | :param file_path: file path of the file to be converted 126 | :param output_file: the output file path as a string 127 | """ 128 | reader = _open_csv(file_path, cash_field_names, 'iso-8859-1') 129 | with open(output_file, 'w', encoding='utf-8') as outfile: 130 | writer = csv.DictWriter(outfile, dialect='dkb', fieldnames=homebank_field_names) 131 | for row in reader: 132 | writer.writerow( 133 | { 134 | 'date': convert_date(row["buchungstag"]), 135 | 'paymode': 8, 136 | 'info': None, 137 | 'payee': row["beguenstigter"], 138 | 'memo': row["verwendungszweck"], 139 | 'amount': row["betrag"], 140 | 'category': None, 141 | 'tags': None 142 | }) 143 | 144 | def convert_old_visa(file_path, output_file="visaHomebank.csv"): 145 | """ 146 | Convert a DKB visa file to a homebank-readable import CSV. 147 | 148 | :param file_path: file path of the file to be converted 149 | :param output_file: the output file path as a string 150 | """ 151 | reader = _open_csv(file_path, old_visa_field_names, 'iso-8859-1') 152 | with open(output_file, 'w', encoding='utf-8') as outfile: 153 | writer = csv.DictWriter(outfile, dialect='dkb', fieldnames=homebank_field_names) 154 | for row in reader: 155 | writer.writerow( 156 | { 157 | 'date': convert_date(row["wertstellung"]), 158 | 'paymode': 1, 159 | 'info': None, 160 | 'payee': None, 161 | 'memo': row["beschreibung"], 162 | 'amount': row["betrag"], 163 | 'category': None, 164 | 'tags': None 165 | }) 166 | 167 | def convert_new_visa(file_path, output_file="visaHomebank.csv"): 168 | """ 169 | Convert a DKB visa file generated by the new (2023) web portal 170 | to a homebank-readable import CSV. 171 | 172 | :param file_path: file path of the file to be converted 173 | :param output_file: the output file path as a string 174 | """ 175 | reader = _open_csv(file_path, new_visa_field_names, 'utf-8-sig') 176 | with open(output_file, 'w', encoding='utf-8') as outfile: 177 | writer = csv.DictWriter(outfile, dialect='dkb', fieldnames=homebank_field_names) 178 | for row in reader: 179 | writer.writerow( 180 | { 181 | 'date': convert_short_date(row["wertstellung"]), 182 | 'paymode': 1, 183 | 'info': None, 184 | 'payee': None, 185 | 'memo': row["beschreibung"], 186 | 'amount': strip_currency(row["betrag"]), 187 | 'category': None, 188 | 'tags': None 189 | }) 190 | 191 | def strip_currency(currency_string): 192 | return currency_string.replace("€", "").strip() 193 | 194 | def convert_giro(file_path, output_file="giroHomebank.csv"): 195 | """ 196 | Convert a DKB giro file (i.e. the normal bank account file available in the 197 | new banking portal introduced in 2023) to a homebank-readable import CSV. 198 | 199 | :param file_path: file path of the file to be converted 200 | :param output_file: the output file path as a string 201 | """ 202 | reader = _open_csv(file_path, giro_field_names, 'utf-8-sig') 203 | with open(output_file, 'w', encoding='utf-8') as outfile: 204 | writer = csv.DictWriter(outfile, dialect='dkb', fieldnames=homebank_field_names) 205 | for row in reader: 206 | payee = f"{row.get('zahlungsempfänger*in')} {row.get('IBAN')}" 207 | 208 | if row.get('umsatztyp') == "Eingang": 209 | payee = f"{row.get('zahlungspflichtige*r')}" 210 | 211 | 212 | writer.writerow( 213 | { 214 | 'date': convert_short_date(row.get("buchungsdatum")), 215 | 'paymode': 8, 216 | 'info': None, 217 | 'payee': payee, 218 | 'memo': row.get("verwendungszweck"), 219 | 'amount': strip_currency(row.get("betrag")), 220 | 'category': None, 221 | 'tags': None 222 | }) 223 | 224 | def find_transaction_lines(file): 225 | """ 226 | Reduce the csv lines to the lines containing actual data relevant for the conversion. 227 | 228 | :param file: The export CSV from DKB to be converted 229 | :return: The lines containing the actual transaction data 230 | """ 231 | lines = file.readlines() 232 | i = 1 233 | for line in lines: 234 | # simple heuristic to find the csv header line. Both these strings 235 | # appear in headers of the cash, visa, and giro CSVs. 236 | if "Betrag" in line and "Wertstellung" in line: 237 | return lines[i:] 238 | i = i + 1 239 | 240 | raise ValueError("Can't convert CSV file without header line") 241 | 242 | 243 | def convert_date(date_string): 244 | """Convert the date_string to dd-mm-YYYY format.""" 245 | date = datetime.strptime(date_string, "%d.%m.%Y") 246 | return date.strftime('%d-%m-%Y') 247 | 248 | def convert_short_date(date_string): 249 | """Convert the date_string to dd-mm-YYYY format.""" 250 | date = datetime.strptime(date_string, "%d.%m.%y") 251 | return date.strftime('%d-%m-%Y') 252 | 253 | def setup_parser(): 254 | parser = argparse.ArgumentParser(description= 255 | "Convert a CSV export file from DKB online banking " 256 | "to a Homebank compatible CSV format.") 257 | parser.add_argument("filename", help="The CSV file to convert.") 258 | 259 | parser.add_argument("-o", "--output-file", help="choose where to store the output file (default: working directory)") 260 | parser.add_argument("-d", "--debug", action="store_true", help="output debug information") 261 | 262 | return parser.parse_args() 263 | 264 | 265 | def main(): 266 | args = setup_parser() 267 | csv_format = detect_csv_format(args.filename) 268 | 269 | print(f"Looks like we're trying to convert a {csv_format.value} CSV file") if args.debug else None 270 | 271 | if csv_format == CsvFileTypes.OLD_VISA: 272 | output = args.output_file or "visaHomebank.csv" 273 | convert_old_visa(args.filename, output) 274 | print(f"DKB Visa file converted. Output file: {output}") if args.debug else None 275 | elif csv_format == CsvFileTypes.CASH: 276 | output = args.output_file or "cashHomebank.csv" 277 | convert_cash(args.filename, output) 278 | print(f"DKB Cash file converted. Output file: {output}") if args.debug else None 279 | elif csv_format == CsvFileTypes.GIRO: 280 | output = args.output_file or "giroHomebank.csv" 281 | convert_giro(args.filename, output) 282 | print(f"DKB Giro file converted. Output file: {output}") if args.debug else None 283 | elif csv_format == CsvFileTypes.NEW_VISA: 284 | output = args.output_file or "visaHomebank.csv" 285 | convert_new_visa(args.filename, output) 286 | print(f"DKB Giro file converted. Output file: {output}") if args.debug else None 287 | elif csv_format == CsvFileTypes.UNKNOWN: 288 | print(f"Could not detect CSV file type. Are you sure this is a legitimate file?", file=sys.stderr) 289 | sys.exit(os.EX_USAGE) 290 | 291 | sys.exit(os.EX_OK) 292 | 293 | if __name__ == '__main__': 294 | main() 295 | -------------------------------------------------------------------------------- /dkb2homebankTest.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | from builtins import ResourceWarning 4 | import unittest 5 | import dkb2homebank 6 | import os 7 | import warnings 8 | import subprocess 9 | from difflib import unified_diff 10 | 11 | def fileContentEqual(file1, file2): 12 | with open(file1, "r") as f: 13 | expected_lines = f.readlines() 14 | with open(file2, "r") as f: 15 | actual_lines = f.readlines() 16 | 17 | diff = list(unified_diff(expected_lines, actual_lines)) 18 | 19 | return diff 20 | 21 | class DKB2HomebankTest(unittest.TestCase): 22 | def setUp(self): 23 | warnings.simplefilter("ignore", ResourceWarning) 24 | 25 | def testShouldConvertCashFile(self): 26 | dkb2homebank.convert_cash('testfiles/cash.csv', 'cashHomebank.csv') 27 | self.assertEqual([], fileContentEqual('testfiles/expected-output/cashHomebank.csv', 'cashHomebank.csv')) 28 | 29 | def testThrowErrorForEmptyCashFile(self): 30 | with self.assertRaises(ValueError) as context: 31 | dkb2homebank.convert_cash('testfiles/cash_empty.csv') 32 | self.assertTrue("Can't convert CSV file without header line" in str(context.exception)) 33 | 34 | def testShouldConvertOldVisaFile(self): 35 | dkb2homebank.convert_old_visa('testfiles/visa.csv', 'visaHomebank.csv') 36 | self.assertEqual([], fileContentEqual('testfiles/expected-output/visaHomebank.csv', 'visaHomebank.csv')) 37 | 38 | def testShouldConvertOldVisaFileWithRange(self): 39 | dkb2homebank.convert_old_visa('testfiles/visaRange.csv', 'visaHomebank.csv') 40 | self.assertEqual([], fileContentEqual('testfiles/expected-output/visaRangeHomebank.csv', 'visaHomebank.csv')) 41 | 42 | def testShouldConvertNewVisaFile(self): 43 | dkb2homebank.convert_new_visa('testfiles/visaNew.csv', 'visaHomebank.csv') 44 | self.assertEqual([], fileContentEqual('testfiles/expected-output/visaNewHomebank.csv', 'visaHomebank.csv')) 45 | 46 | def testShouldConvertGiroFile(self): 47 | dkb2homebank.convert_giro('testfiles/giro.csv', 'giroHomebank.csv') 48 | self.assertEqual([], fileContentEqual('testfiles/expected-output/giroHomebank.csv', 'giroHomebank.csv')) 49 | 50 | def testShouldConvertTagesgeldFile(self): 51 | dkb2homebank.convert_giro('testfiles/tagesgeld.csv', 'tagesgeldHomebank.csv') 52 | self.assertEqual([], fileContentEqual('testfiles/expected-output/tagesgeldHomebank.csv', 'tagesgeldHomebank.csv')) 53 | 54 | def testShouldDetectOldCashFile(self): 55 | format = dkb2homebank.detect_csv_format('testfiles/cash.csv') 56 | self.assertEqual(format, dkb2homebank.CsvFileTypes.CASH) 57 | 58 | def testShouldDetectOldVisaFile(self): 59 | format = dkb2homebank.detect_csv_format('testfiles/visa.csv') 60 | self.assertEqual(format, dkb2homebank.CsvFileTypes.OLD_VISA) 61 | 62 | def testShouldDetectNewGiroFile(self): 63 | format = dkb2homebank.detect_csv_format('testfiles/giro.csv') 64 | self.assertEqual(format, dkb2homebank.CsvFileTypes.GIRO) 65 | 66 | def testShouldDetectNewVisaFile(self): 67 | format = dkb2homebank.detect_csv_format('testfiles/visaNew.csv') 68 | self.assertEqual(format, dkb2homebank.CsvFileTypes.NEW_VISA) 69 | 70 | def tearDown(self): 71 | delete('cashHomebank.csv') 72 | delete('visaHomebank.csv') 73 | delete('giroHomebank.csv') 74 | delete('tagesgeldHomebank.csv') 75 | 76 | 77 | class DKB2HomebankFunctionalTest(unittest.TestCase): 78 | def testShouldConvertCash(self): 79 | result = subprocess.run(["./dkb2homebank.py", "testfiles/cash.csv"]) 80 | self.assertEqual(0, result.returncode) 81 | 82 | def testShouldConvertOldVisa(self): 83 | result = subprocess.run(["./dkb2homebank.py", "testfiles/visa.csv"]) 84 | self.assertEqual(0, result.returncode) 85 | 86 | def testShouldConvertNewVisa(self): 87 | result = subprocess.run(["./dkb2homebank.py", "testfiles/visaNew.csv"]) 88 | self.assertEqual(0, result.returncode) 89 | 90 | def testShouldConvertGiro(self): 91 | result = subprocess.run(["./dkb2homebank.py", "testfiles/giro.csv"]) 92 | self.assertEqual(0, result.returncode) 93 | 94 | def testShouldConvertTagesgeld(self): 95 | result = subprocess.run(["./dkb2homebank.py", "testfiles/tagesgeld.csv"]) 96 | self.assertEqual(0, result.returncode) 97 | 98 | def testShouldErrorWhenUsingUnknownFormat(self): 99 | result = subprocess.run(["./dkb2homebank.py", "testfiles/unknown_format.csv"]) 100 | self.assertEqual(64, result.returncode) 101 | 102 | def testShouldRunScriptWithOutputParameter(self): 103 | result = subprocess.run(["./dkb2homebank.py", "testfiles/cash.csv", "--output-file", "/tmp/dkb2homebank.csv"]) 104 | self.assertEqual(0, result.returncode) 105 | 106 | 107 | def tearDown(self): 108 | delete('cashHomebank.csv') 109 | delete('visaHomebank.csv') 110 | delete('giroHomebank.csv') 111 | delete('tagesgeldHomebank.csv') 112 | 113 | def delete(filename): 114 | if os.path.isfile(filename): 115 | os.remove(filename) 116 | 117 | if __name__ == '__main__': 118 | unittest.main() 119 | -------------------------------------------------------------------------------- /testfiles/cash.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamvocke/dkb2homebank/659377f3cb85f2e640f5194bc013a7521424ebf4/testfiles/cash.csv -------------------------------------------------------------------------------- /testfiles/cash_empty.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamvocke/dkb2homebank/659377f3cb85f2e640f5194bc013a7521424ebf4/testfiles/cash_empty.csv -------------------------------------------------------------------------------- /testfiles/expected-output/cashHomebank.csv: -------------------------------------------------------------------------------- 1 | 19-10-2018;8;;Foo Firma;;45,67;; 2 | 17-10-2018;8;;SOME ONLINE SHOP;Some Verwendungszweck;-16,78;; 3 | -------------------------------------------------------------------------------- /testfiles/expected-output/giroHomebank.csv: -------------------------------------------------------------------------------- 1 | 25-08-2023;8;;John Doe;;100.000,00;; 2 | 25-08-2023;8;;SOME COMPANY/SOMEWHERE//DE DE33330333331112223334;2023-08-24 Debitk.99 VISA Debit;-10,22;; 3 | 22-08-2023;8;;SOME ÖTHER COMPANY/SOMEWHERE//DE DE33330333331112223334;2023-08-24 Debitk.99 VISA Debit;-10,22;; 4 | 20-08-2023;8;; DE33330333331112223334;Some Depot Payment;-1.234,56;; 5 | 10-08-2023;8;;Some Company;LOHN 08/2023;2.345,67;; 6 | -------------------------------------------------------------------------------- /testfiles/expected-output/tagesgeldHomebank.csv: -------------------------------------------------------------------------------- 1 | 25-08-2023;8;;John Doe;;100.000,00;; 2 | 25-08-2023;8;;SOME COMPANY/SOMEWHERE//DE DE33330333331112223334;2023-08-24 Debitk.99 VISA Debit;-10,22;; 3 | 22-08-2023;8;;SOME ÖTHER COMPANY/SOMEWHERE//DE DE33330333331112223334;2023-08-24 Debitk.99 VISA Debit;-10,22;; 4 | 20-08-2023;8;; DE33330333331112223334;Some Depot Payment;-1.234,56;; 5 | 10-08-2023;8;;Some Company;LOHN 08/2023;2.345,67;; 6 | -------------------------------------------------------------------------------- /testfiles/expected-output/visaHomebank.csv: -------------------------------------------------------------------------------- 1 | 15-10-2018;1;;;SOME BANKS ATM;-100,00;; 2 | 11-10-2018;1;;;SOME WEBSHOP;-50,01;; 3 | 08-10-2018;1;;;ANOTHER BANKS ATM;-20,00;; 4 | 22-09-2018;1;;;SOME INTEREST;2,00;; 5 | -------------------------------------------------------------------------------- /testfiles/expected-output/visaNewHomebank.csv: -------------------------------------------------------------------------------- 1 | 21-10-2023;1;;;Ausgleich Kreditkarte gem;2,49;; 2 | 20-10-2023;1;;;Kartenpreis;-2,49;; 3 | 22-10-2022;1;;;GOOGLE *Stadia;10,04;; 4 | 22-10-2022;1;;;Google Payment IE LTD Sta;9,90;; 5 | 23-05-2022;1;;;GOOGLE*STADIA;-9,99;; 6 | 21-05-2022;1;;;Ausgleich Kreditkarte gem;157,90;; 7 | -------------------------------------------------------------------------------- /testfiles/expected-output/visaRangeHomebank.csv: -------------------------------------------------------------------------------- 1 | 02-10-2018;1;;;SOME WEBSHOP;-5,15;; 2 | -------------------------------------------------------------------------------- /testfiles/giro.csv: -------------------------------------------------------------------------------- 1 | "Girokonto";"DE33330333331112223334" 2 | "" 3 | "Kontostand vom 25.08.2023:";"1234,56 EUR" 4 | "" 5 | "Buchungsdatum";"Wertstellung";"Status";"Zahlungspflichtige*r";"Zahlungsempfänger*in";"Verwendungszweck";"Umsatztyp";"IBAN";"Betrag (€)";"Gläubiger-ID";"Mandatsreferenz";"Kundenreferenz" 6 | "25.08.23";"25.08.23";"Gebucht";"John Doe";"Paul Payee";"";"Eingang";"DE33330333331112223334";"100.000,00 €";"";"";"" 7 | "25.08.23";"25.08.23";"Gebucht";"ISSUER";"SOME COMPANY/SOMEWHERE//DE";"2023-08-24 Debitk.99 VISA Debit";"Ausgang";"DE33330333331112223334";"-10,22 €";"";"";"567890123456789" 8 | "22.08.23";"22.08.23";"Gebucht";"ISSUER";"SOME ÖTHER COMPANY/SOMEWHERE//DE";"2023-08-24 Debitk.99 VISA Debit";"Ausgang";"DE33330333331112223334";"-10,22 €";"";"";"567890123456789" 9 | "20.08.23";"20.08.23";"Gebucht";"Doe,John";"";"Some Depot Payment";"Ausgang";"DE33330333331112223334";"-1.234,56 €";"";"";"" 10 | "10.08.23";"10.08.23";"Gebucht";"Some Company";"J DOE";"LOHN 08/2023";"Eingang";"DE33330333331112223334";"2.345,67 €";"";"";"" 11 | -------------------------------------------------------------------------------- /testfiles/tagesgeld.csv: -------------------------------------------------------------------------------- 1 | "Tagesgeld";"DE71120300001027943842" 2 | 3 | "Kontostand vom 09.02.2025:";"1.234,45 €" 4 | "" 5 | "Buchungsdatum";"Wertstellung";"Status";"Zahlungspflichtige*r";"Zahlungsempfänger*in";"Verwendungszweck";"Umsatztyp";"IBAN";"Betrag (€)";"Gläubiger-ID";"Mandatsreferenz";"Kundenreferenz" 6 | "25.08.23";"25.08.23";"Gebucht";"John Doe";"Paul Payee";"";"Eingang";"DE33330333331112223334";"100.000,00";"";"";"" 7 | "25.08.23";"25.08.23";"Gebucht";"ISSUER";"SOME COMPANY/SOMEWHERE//DE";"2023-08-24 Debitk.99 VISA Debit";"Ausgang";"DE33330333331112223334";"-10,22";"";"";"567890123456789" 8 | "22.08.23";"22.08.23";"Gebucht";"ISSUER";"SOME ÖTHER COMPANY/SOMEWHERE//DE";"2023-08-24 Debitk.99 VISA Debit";"Ausgang";"DE33330333331112223334";"-10,22";"";"";"567890123456789" 9 | "20.08.23";"20.08.23";"Gebucht";"Doe,John";"";"Some Depot Payment";"Ausgang";"DE33330333331112223334";"-1.234,56";"";"";"" 10 | "10.08.23";"10.08.23";"Gebucht";"Some Company";"J DOE";"LOHN 08/2023";"Eingang";"DE33330333331112223334";"2.345,67";"";"";"" 11 | -------------------------------------------------------------------------------- /testfiles/unknown_format.csv: -------------------------------------------------------------------------------- 1 | "Whatever";"DE71120300001027943842" 2 | 3 | "Kontostand vom 09.02.2025:";"1.234,45 €" 4 | "" 5 | "Buchungsdatum";"Wertstellung";"Status";"Zahlungspflichtige*r";"Zahlungsempfänger*in";"Verwendungszweck";"Umsatztyp";"IBAN";"Betrag (€)";"Gläubiger-ID";"Mandatsreferenz";"Kundenreferenz" 6 | "25.08.23";"25.08.23";"Gebucht";"John Doe";"Paul Payee";"";"Eingang";"DE33330333331112223334";"100.000,00";"";"";"" 7 | -------------------------------------------------------------------------------- /testfiles/visa.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamvocke/dkb2homebank/659377f3cb85f2e640f5194bc013a7521424ebf4/testfiles/visa.csv -------------------------------------------------------------------------------- /testfiles/visaNew.csv: -------------------------------------------------------------------------------- 1 | "Karte";"Martin-Visa 1234 •••• •••• 9876" 2 | "" 3 | "Saldo vom 30.10.2023:";"-0 EUR" 4 | "" 5 | "Belegdatum";"Wertstellung";"Status";"Beschreibung";"Umsatztyp";"Betrag";"Fremdwährungsbetrag" 6 | "20.10.23";"21.10.23";"Gebucht";"Ausgleich Kreditkarte gem";"Lastschrift";"2,49 €";"" 7 | "20.10.23";"20.10.23";"Gebucht";"Kartenpreis";"Entgelt";"-2,49 €";"" 8 | "21.11.22";"22.10.22";"Gebucht";"GOOGLE *Stadia";"Gutschrift";"10,04 €";"" 9 | "21.11.22";"22.10.22";"Gebucht";"Google Payment IE LTD Sta";"Gutschrift";"9,90 €";"" 10 | "23.05.22";"23.05.22";"Gebucht";"GOOGLE*STADIA";"Im Geschäft";"-9,99 €";"" 11 | "20.05.22";"21.05.22";"Gebucht";"Ausgleich Kreditkarte gem";"Lastschrift";"157,90 €";"" 12 | -------------------------------------------------------------------------------- /testfiles/visaRange.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamvocke/dkb2homebank/659377f3cb85f2e640f5194bc013a7521424ebf4/testfiles/visaRange.csv --------------------------------------------------------------------------------