Skip to content

Commit 18fab66

Browse files
committed
Add VoBa CSV
1 parent 9b762cf commit 18fab66

5 files changed

Lines changed: 157 additions & 25 deletions

File tree

README.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,12 @@ Listen und Diagramme zeigen dir, wo eigentlich das Geld geblieben ist :thinking:
4040

4141
### Unterstützte Banken
4242

43-
- Comdirect
44-
- CSV Umsatzübersicht
45-
- PDF Finanzreport
46-
- Commerzbank
47-
- CSV Umsatzübersicht
48-
- PDF Kontoauszüge *(work in progress)*
49-
- Sparkasse Hannover
50-
- *(work in Progress)*
51-
- Volksbank Mittelhessen eG (Meine Bank)
52-
- *(work in Progress)*
43+
| Bank | CSV | PDF |
44+
|------------------------------|-------------------------------------|
45+
| Comdirect | 🟢 Umsatzübersicht | 🟢 Finanzreport |
46+
| Commerzbank | 🟢 Umsatzübersicht | 🟢 Kontoauszug |
47+
| Sparkasse Hannover |*planned* |*planned* |
48+
| Volksbank Mittelhessen eG | 🟢 Umsatzübersicht |*planned* |
5349

5450
### Workflow (CSV / PDF Imports)
5551

reader/Comdirect.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,6 @@ class Reader(Generic):
1414
Reader um aus übermittelten Daten Kontoführungsinformationen auszulesen.
1515
Dieser Reader ist speziell für die Daten angepasst, wie sie bei der Comidrect Bank vorkommen.
1616
"""
17-
def __init__(self): # pylint: disable=useless-parent-delegation
18-
"""
19-
Initialisiert eine Instanz der Reader-Klasse für Kontoumsätze der Comdirect Bank.
20-
"""
21-
#TODO: Es wird ggf. einen Usecase für super.__init__() in Zukunft geben
22-
super().__init__()
2317

2418
def from_csv(self, filepath):
2519
"""
@@ -47,7 +41,7 @@ def from_csv(self, filepath):
4741
# Skippe offene Buchungen
4842
continue
4943

50-
betrag = float(row['Umsatz in EUR'].replace(',', '.'))
44+
betrag = float(row['Umsatz in EUR'].replace(',', '.'))
5145
date_tx = datetime.datetime.strptime(
5246
date_tx, date_format
5347
).replace(tzinfo=datetime.timezone.utc).timestamp()
@@ -93,8 +87,9 @@ def from_pdf(self, filepath):
9387
pages="2-end",
9488
flavor="stream",
9589
row_tol=10,
96-
columns=["115,187,305,500"]*16 #TODO: Hack-araound: https://github.com/atlanhq/camelot/issues/357#issuecomment-520986016 # disable=line-too-long
90+
columns=["115,187,305,500"]*16
9791
)
92+
#TODO: Hack-araound: https://github.com/atlanhq/camelot/issues/357#issuecomment-520986016
9893

9994
if not tables:
10095
raise ValueError("No tables found in PDF file.")

reader/Commerzbank.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@ class Reader(Generic):
1212
Reader um aus übermittelten Daten Kontoführungsinformationen auszulesen.
1313
Dieser Reader ist speziell für die Daten angepasst, wie sie bei der Commerzbank vorkommen.
1414
"""
15-
def __init__(self): # pylint: disable=useless-parent-delegation
16-
"""
17-
Initialisiert eine Instanz der Reader-Klasse für Kontoumsätze der Commerzbank.
18-
"""
19-
#TODO: Es wird ggf. einen Usecase für super.__init__() in Zukunft geben
20-
super().__init__()
2115

2216
def from_pdf(self, filepath):
2317
"""
@@ -136,7 +130,7 @@ def _check_next_line_available(self, start_index, end_index, i):
136130

137131
if self.all_rows[start_index + i + 1][1] != '':
138132
return False
139-
133+
140134
if self.all_rows[start_index + i + 1][0].startswith('Buchungsdatum: '):
141135
return False
142136

reader/Volksbank_Mittelhessen.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#!/usr/bin/python3 # pylint: disable=invalid-name
2+
"""Reader für das Einlesen von Kontoumsätzen in den Formaten der Volksbank Mittelhessen."""
3+
4+
import datetime
5+
import csv
6+
7+
from reader.Generic import Reader as Generic
8+
9+
10+
class Reader(Generic):
11+
"""
12+
Reader um aus übermittelten Daten Kontoführungsinformationen auszulesen.
13+
Dieser Reader ist speziell für die Daten angepasst, wie sie bei der
14+
Volksbank Mittelhessen vorkommen.
15+
"""
16+
17+
def from_csv(self, filepath):
18+
"""
19+
Liest Kontoumsätze von Kontoauszügen ein,
20+
die im CSV Format von der Volksbank Mittelhessen herintergeladen wurden.
21+
22+
Returns:
23+
Liste mit Dictonaries, als Standard-Objekt mit allen
24+
ausgelesenen Kontoumsätzen entspricht.
25+
"""
26+
result = []
27+
with open(filepath, 'r', encoding='utf-8') as infile:
28+
29+
# Start Reading CSV content
30+
reader = csv.DictReader(infile, delimiter=';')
31+
date_format = "%d.%m.%Y"
32+
for row in reader:
33+
date_tx = row['Buchungstag']
34+
if date_tx == "offen" or not row['Betrag']:
35+
# Skippe offene Buchungen / Hinweise
36+
continue
37+
38+
betrag = float(row['Betrag'].replace(',', '.'))
39+
date_tx = datetime.datetime.strptime(
40+
date_tx, date_format
41+
).replace(tzinfo=datetime.timezone.utc).timestamp()
42+
valuta = datetime.datetime.strptime(
43+
row['Valutadatum'], date_format
44+
).replace(tzinfo=datetime.timezone.utc).timestamp()
45+
46+
line = {
47+
'date_tx': date_tx,
48+
'valuta': valuta,
49+
'art': row['Buchungstext'],
50+
'text_tx': row['Verwendungszweck'],
51+
'betrag': betrag,
52+
'gegenkonto': row['Name Zahlungsbeteiligter'],
53+
'currency': row['Waehrung'],
54+
'parsed': {},
55+
'category': None,
56+
'tags': None
57+
}
58+
59+
if row.get('Glaeubiger ID'):
60+
line['parsed']['Gläubiger-ID'] = row['Glaeubiger ID']
61+
62+
if row.get('Mandatsreferenz'):
63+
line['parsed']['Mandatsreferenz'] = row['Mandatsreferenz']
64+
65+
result.append(line)
66+
67+
return result
68+
69+
def from_pdf(self, filepath):
70+
"""
71+
Liest Kontoumsätze von Kontoauszügen ein,
72+
die im PDF Format von der Volksbank Mittelhessen ausgestellt worden sind.
73+
74+
Returns:
75+
Liste mit Dictonaries, als Standard-Objekt mit allen
76+
ausgelesenen Kontoumsätzen entspricht.
77+
"""
78+
raise NotImplementedError()
79+
80+
def from_http(self, url):
81+
"""
82+
Liest Kontoumsätze von einer Internetressource ein.
83+
"""
84+
raise NotImplementedError()
85+
86+
def _newline_replace(self, text_in:str) -> str:
87+
"""Ersetzt Newlines im Buchungsinformationen
88+
intelligent durch Leerzeichen oder nichts
89+
Args:
90+
text_in, str: Input Text aus Kontoauszug
91+
Return:
92+
str: Text ohne Newlines
93+
"""
94+
if not '\n' in text_in:
95+
return text_in
96+
97+
if text_in.index('\n') < 35:
98+
return text_in.replace('\n', ' ')
99+
100+
return text_in.replace('\n', '')
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/python3 # pylint: disable=invalid-name
2+
"""Testmodul für das Einlesen von Daten mit Hilfe des Volksbank Mittelhessen Readers"""
3+
4+
import os
5+
import sys
6+
import pytest
7+
8+
# Add Parent for importing from Modules
9+
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
10+
sys.path.append(parent_dir)
11+
12+
from helper import check_transaktion_list
13+
from reader.Volksbank_Mittelhessen import Reader as Volksbank_Mittelhessen
14+
15+
16+
def test_read_from_csv(test_app):
17+
"""Testet das Einlesen einer CSV Datei mit Kontoumsätzen"""
18+
test_file_csv = os.path.join('/tmp', 'volksbank-mittelhessen.csv')
19+
if not os.path.isfile(test_file_csv):
20+
# Test file not provided (sensitive data is not part of git repo)
21+
pytest.skip("Testfile /tmp/volksbank-mittelhessen.csv not found....skipping")
22+
23+
with test_app.app_context():
24+
transaction_list = Volksbank_Mittelhessen().from_csv(test_file_csv)
25+
26+
# Check Reader Ergebnisse
27+
check_transaktion_list(transaction_list)
28+
29+
30+
#def test_read_from_pdf(test_app):
31+
# """Testet das Einlesen einer PDF Datei mit Kontoumsätzen"""
32+
# test_file_pdf = os.path.join('/tmp', 'volksbank-mittelhessen.pdf')
33+
# if not os.path.isfile(test_file_pdf):
34+
# # Test file not provided (sensitive data is not part of git repo)
35+
# pytest.skip("Testfile /tmp/volksbank-mittelhessen.pdf not found....skipping")
36+
#
37+
# with test_app.app_context():
38+
# transaction_list = Volksbank_Mittelhessen().from_pdf(test_file_pdf)
39+
#
40+
# # Check Reader Ergebnisse
41+
# check_transaktion_list(transaction_list)
42+
43+
44+
@pytest.mark.skip(reason="Currently not implemented yet")
45+
def test_read_from_http(test_app):
46+
"""Testet das Einlesen Kontoumsätzen aus einer Online-Quelle"""
47+
return None

0 commit comments

Comments
 (0)