- Importiere neue Kontoumsätze oder wähle die Übersicht aus, an der du weiterarbeiten möchtest. Das Konto wird bei einem Import automatisch erstellt.
-
+
+
+ Importiere neue Kontoumsätze oder wähle die Übersicht aus, an der du weiterarbeiten möchtest.
+
+
@@ -48,7 +52,11 @@
Pynance Parser
-
+
+
+
Das Konto wird bei einem Import automatisch erstellt.
+
+
- Datei hier ablegen oder auswählen (JSON)
+ .json
+
-
Diese Datei kann auch im Server-Backend abgelegt werden, um für alle verfügbar zu sein.
From c86d1a0e260f1301cc49eb88a242ba8eeb407334 Mon Sep 17 00:00:00 2001
From: Michael Prior
Date: Mon, 24 Nov 2025 17:41:55 +0100
Subject: [PATCH 05/31] =?UTF-8?q?More=20Text;=20Todos=20f=C3=BCr=20Screens?=
=?UTF-8?q?ize=20Designs?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/static/js/functions.js | 2 +-
app/templates/iban.html | 2 ++
app/templates/macros.html | 2 +-
app/templates/tx.html | 6 ++----
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/app/static/js/functions.js b/app/static/js/functions.js
index 63f19f5..59ffe79 100644
--- a/app/static/js/functions.js
+++ b/app/static/js/functions.js
@@ -202,7 +202,7 @@ function concatURI(value_dict, formData){
* configured with a callback to handle its response.
*
* @param {function} callback - A callback function to handle the response.
- * Receives the response text and stsatus code as arguments.
+ * Receives the response text and status code as arguments.
* @returns {XMLHttpRequest} - The newly created XMLHttpRequest object.
*/
function createAjax(callback) {
diff --git a/app/templates/iban.html b/app/templates/iban.html
index fa8a95a..ae00deb 100644
--- a/app/templates/iban.html
+++ b/app/templates/iban.html
@@ -34,6 +34,8 @@
{% if ibans %}
{% for iban in ibans %}
@@ -37,7 +37,7 @@
Pynance Parser
Gruppen
-
+
{% if groups %}
{% for group in groups %}
diff --git a/app/templates/tx.html b/app/templates/tx.html
index 5293967..cd75f4d 100644
--- a/app/templates/tx.html
+++ b/app/templates/tx.html
@@ -110,9 +110,8 @@
-
-
+
diff --git a/app/ui.py b/app/ui.py
index 74e65c3..6a2e4c1 100644
--- a/app/ui.py
+++ b/app/ui.py
@@ -330,7 +330,7 @@ def read_input(self, uri, bank='Generic', data_format=None):
"""
# Format
if data_format is None:
- #TODO: Logik zum Erraten des Datentyps, #10
+ # Fallback to CSV text
data_format = 'csv'
# Reader
From a399f94fb45602c5a29d0750f882c1b7f019d0af Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Fri, 5 Dec 2025 21:26:57 +0100
Subject: [PATCH 07/31] Fix test with new code
---
tests/test_integ_basics.py | 2 +-
tests/test_integ_more_routes.py | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/tests/test_integ_basics.py b/tests/test_integ_basics.py
index 5c9362c..969cebe 100644
--- a/tests/test_integ_basics.py
+++ b/tests/test_integ_basics.py
@@ -203,7 +203,7 @@ def test_save_meta(test_app):
}
parameters = json.dumps(parameters).encode('utf-8')
files = {
- 'file-input': (io.BytesIO(parameters), 'commerzbank.csv'),
+ 'settings-input': (io.BytesIO(parameters), 'commerzbank.csv'),
'bank': 'Commerzbank'
}
result = client.post(
diff --git a/tests/test_integ_more_routes.py b/tests/test_integ_more_routes.py
index 233648a..8aeaebb 100644
--- a/tests/test_integ_more_routes.py
+++ b/tests/test_integ_more_routes.py
@@ -39,7 +39,8 @@ def test_upload_file_route(test_app):
)
# Check Response
- assert result.status_code == 201, \
+ # (If other test run already, data is til present and duplicates won't be inserted)
+ assert result.status_code in (200, 201), \
f"Die Seite hat den Upload nicht wie erwartet verarbeitet: {result.text}"
assert result.json.get('filename') == 'commerzbank.csv', \
"Angaben zum Upload wurden nicht gefunden"
From cde5a5719999bf2bb0550ee8fe631d78edb0f6a8 Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Tue, 9 Dec 2025 14:34:12 +0100
Subject: [PATCH 08/31] Disable autofill on hidden inputs
---
app/templates/macros.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/templates/macros.html b/app/templates/macros.html
index 3bdccdf..850acda 100644
--- a/app/templates/macros.html
+++ b/app/templates/macros.html
@@ -89,9 +89,9 @@
{% for tag in transaction.tags %}
-
+
{{ tag }}
{% endfor %}
From 5ae2e0fc833e248e31bbf74ad4992d55833a0950 Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Sun, 14 Dec 2025 21:56:53 +0100
Subject: [PATCH 16/31] VB Hessen Importer angefangen
---
reader/Volksbank_Mittelhessen.py | 16 +++++++++++--
...test_unit_reader_Volksbank-Mittelhessen.py | 24 +++++++++----------
2 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/reader/Volksbank_Mittelhessen.py b/reader/Volksbank_Mittelhessen.py
index 37b8816..89b87ec 100644
--- a/reader/Volksbank_Mittelhessen.py
+++ b/reader/Volksbank_Mittelhessen.py
@@ -3,6 +3,7 @@
import datetime
import csv
+import camelot
from reader.Generic import Reader as Generic
@@ -75,8 +76,19 @@ def from_pdf(self, filepath):
Liste mit Dictonaries, als Standard-Objekt mit allen
ausgelesenen Kontoumsätzen entspricht.
"""
- raise NotImplementedError()
-
+ tables = camelot.read_pdf(
+ filepath,
+ pages="all", # End -1
+ flavor="stream",
+ table_areas=["60,567,573,51"],
+ columns=["75,112,440,526"],
+ strip_text='\n', # übernommen von Commerzbank, da ähnliches Layout
+ layout_kwargs={ # übernommen von Commerzbank, da ähnliches Layout
+ "char_margin": 2,
+ "word_margin": 0.5,
+ },
+ )
+
def from_http(self, url):
"""
Liest Kontoumsätze von einer Internetressource ein.
diff --git a/tests/test_unit_reader_Volksbank-Mittelhessen.py b/tests/test_unit_reader_Volksbank-Mittelhessen.py
index 01f720f..dbf293a 100644
--- a/tests/test_unit_reader_Volksbank-Mittelhessen.py
+++ b/tests/test_unit_reader_Volksbank-Mittelhessen.py
@@ -27,18 +27,18 @@ def test_read_from_csv(test_app):
check_transaktion_list(transaction_list)
-#def test_read_from_pdf(test_app):
-# """Testet das Einlesen einer PDF Datei mit Kontoumsätzen"""
-# test_file_pdf = os.path.join('/tmp', 'volksbank-mittelhessen.pdf')
-# if not os.path.isfile(test_file_pdf):
-# # Test file not provided (sensitive data is not part of git repo)
-# pytest.skip("Testfile /tmp/volksbank-mittelhessen.pdf not found....skipping")
-#
-# with test_app.app_context():
-# transaction_list = Volksbank_Mittelhessen().from_pdf(test_file_pdf)
-#
-# # Check Reader Ergebnisse
-# check_transaktion_list(transaction_list)
+def test_read_from_pdf(test_app):
+ """Testet das Einlesen einer PDF Datei mit Kontoumsätzen"""
+ test_file_pdf = os.path.join('/tmp', 'volksbank-mittelhessen.pdf')
+ if not os.path.isfile(test_file_pdf):
+ # Test file not provided (sensitive data is not part of git repo)
+ pytest.skip("Testfile /tmp/volksbank-mittelhessen.pdf not found....skipping")
+
+ with test_app.app_context():
+ transaction_list = Volksbank_Mittelhessen().from_pdf(test_file_pdf)
+
+ # Check Reader Ergebnisse
+ check_transaktion_list(transaction_list)
@pytest.mark.skip(reason="Currently not implemented yet")
From 61ceab78d01ea5cf59a0a533b07863ba32148a04 Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Sun, 14 Dec 2025 22:06:23 +0100
Subject: [PATCH 17/31] Important notes
---
reader/Volksbank_Mittelhessen.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/reader/Volksbank_Mittelhessen.py b/reader/Volksbank_Mittelhessen.py
index 89b87ec..b982647 100644
--- a/reader/Volksbank_Mittelhessen.py
+++ b/reader/Volksbank_Mittelhessen.py
@@ -80,7 +80,7 @@ def from_pdf(self, filepath):
filepath,
pages="all", # End -1
flavor="stream",
- table_areas=["60,567,573,51"],
+ table_areas=["60,629,573,51"],
columns=["75,112,440,526"],
strip_text='\n', # übernommen von Commerzbank, da ähnliches Layout
layout_kwargs={ # übernommen von Commerzbank, da ähnliches Layout
@@ -88,6 +88,8 @@ def from_pdf(self, filepath):
"word_margin": 0.5,
},
)
+ # Begin at: "alter Kontostand vom"
+ # End at: "neuer Kontostand vom"
def from_http(self, url):
"""
From 7671c6191396e63e9caf1c2981e0f4af7b9ec709 Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Mon, 15 Dec 2025 22:13:57 +0100
Subject: [PATCH 18/31] Add VB Import
---
README.md | 10 +--
Reader.md | 54 +++++++++++++
reader/Volksbank_Mittelhessen.py | 134 ++++++++++++++++++++++++++-----
tests/helper.py | 2 +-
4 files changed, 172 insertions(+), 28 deletions(-)
create mode 100644 Reader.md
diff --git a/README.md b/README.md
index 7e65f5b..11b3f8a 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ Listen und Diagramme zeigen dir, wo eigentlich das Geld geblieben ist :thinking:
| Comdirect | 🟢 Umsatzübersicht | 🟢 Finanzreport |
| Commerzbank | 🟢 Umsatzübersicht | 🟢 Kontoauszug |
| Sparkasse Hannover | ⚫ *planned* | ⚫ *planned* |
-| Volksbank Mittelhessen eG | 🟢 Umsatzübersicht | ⚫ *planned* |
+| Volksbank Mittelhessen eG | 🟢 Umsatzübersicht | 🟢 Kontoauszug |
### Workflow (CSV / PDF Imports)
@@ -92,13 +92,7 @@ pytest
## Entwickeln von neuen Readern
-- Erstelle einen neuen Test unter `tests/`
- - (kopiere am besten `tests/test_unit_reader_Comdirect.py`)
-- Erstelle ein neues Skript unter `reader/`
- - (kopiere am besten `reader/Generic.py`)
-- Passe die Logik im Test so an, dass dieser ausgeführt wird, wenn eine Testdatei vorhanden ist.
-- Entwickle deinen Reader und teste ihn dabei immer wieder mit `pytest -svx tests/test_unit_reader_*.py`
-- Pushe **keine** Testdaten (Kontoumsätze) ins Repo!
+Deine Bank fehlt noch in der Support Tabelle? [So erstellst du einen weiteren Reader](Reader.md).
## Entwickeln neuer `parser` / `rules`
diff --git a/Reader.md b/Reader.md
new file mode 100644
index 0000000..e5d4613
--- /dev/null
+++ b/Reader.md
@@ -0,0 +1,54 @@
+## Entwickeln von neuen Readern
+
+### Erstelle notwendige Dateien
+
+Neben dem Modul benätigst du noch einen Test. Ziel des Tests ist es, dass lokal eine Beispiel Datei importiert wird, wenn sie vorhanden ist und im Ergebnis geprüft wird, ob die eingelesenen Einträge sinnvoll sind. Eine Prüfung nach Transaktionsinhalten findet nicht statt.
+
+**Lade keine privaten Kontoauszüge in das Repository hoch !**
+
+Kopiere dazu am besten `tests/test_unit_reader_Comdirect.py` und...
+
+- Passe den Dateinamen der Beispieldatei an
+- Entscheide, ggf. eine Testfunktion zu überspringen mit `@pytest.mark.skip(reason="Currently not implemented yet")`
+
+Kopiere als nächstes `reader/Generic.py` und
+
+- Passe den Dateinamen der Beispieldatei an
+- Entwickle deinen Reader (siehe unten) und teste ihn zwischendurch/am Ende mit `pytest -svx tests/test_unit_reader_*.py`
+
+Der Test führt die `from_csv` bzw. `from_pdf` Funktion aus und überprüft, ob das Format richtig ist und die Werte der Transaktionen über entsprechende Keys mit sinnvollen Werten verfügen.
+
+### Logik des Readers erstellen
+
+Ziel der Funktion `from_csv` bzw. `from_pdf` ist es, eine Liste von Transaktionen in Form eines Dictionaries einzulesen, wobei die Keys der Vorgabe des [allgemeinen Models für Transaktionen](Models.md) folgt.
+
+Für das Einlesen einer PDF ist dabei mehr Aufwand notwendig. Hierbei kommt das Modul `camelot` zum Einsatz.
+
+Für die Entwicklung der richtigen Einstellungen wird neben dem Python Modul auch das CLI Programm empfohlen. Installation und Konfigurationen findest du in der [Dokumentation](https://camelot-py.readthedocs.io/en/master/).
+
+#### Step by Step
+
+- Lege eine Beispieldatei nach `/tmp/bank.pdf`
+- Nutze den `grid` Modus im CLI, um angezeigt zu bekommen, was `camelot` mit den jeweiligen Einstellungen für Tabelleninhalte finden würde.
+- Übertrage die erfolgreichen Flags und Einstellungen in den Python Aufruf im Reader-Modul
+- Lasse dir dort erst alle eingelesenen Zeileninhalte ausgeben (während eines `pytests`) und entscheide, wie du die Inhalte ggf. umformen, zusammenfassen oder trennen musst, um das [erwartete Muster](Models.md) zu erhalten.
+
+Ein guter Start ist folgender Aufruf:
+
+```
+camelot -plot grid /tmp/bank.pdf
+```
+
+Nützliche Flags sind `-p` für einzelne Seiten (schneller bzw. können so auch Edgecases geprüft werden), `-C` um die Koordinaten von Spalten festzulegen (meistens erforderlich) und `-T` um den Bereich festzulegen, wo nach einer Tabelle geprüft werden soll (meistens erforderlich). Ein fortgeschrittenerer Aufruf könnte daher so aussehen:
+
+```
+camelot -p 1,6 stream -C "75,112,440,526" -T "60,629,573,51" -plot grid /tmp/bank.pdf
+```
+
+Am Schluss kann noch der Wert eingestellt werden, ab dem Buchstaben vertikal oder horizontal zu einem Wort oder einer Zeile zusammengefasst oder schon getrennt werden. Mit dem Argument `layout_kwargs` in der Python-Methode können Argumente an den darunterliegenden `PDFMiner` durchgereicht werden, was genau diese Einstellungen ermöglicht.
+
+Die Dokumentation dieser möglichen Werte findet man in der [Dokumentation des PDFMiner](https://pdfminersix.readthedocs.io/en/latest/reference/composable.html). Der [Commerbank.py Reader](reader/Commerzbank.py) nutzt diese wegen der bestimmten Schriftart und Zeichenabständen im PDF.
+
+## Stelle einen Pull Request
+
+Beschreibe, welche Features du hinzugefügt oder verbessert hast. Pytest und Pylint werden hier geprüft. Das Testen des neuen Imports kann nur bei dir erfolgen, da die Maintainer in der Regel über keine Testdateien dafür verfügen. Teste daher sorgfältig.
diff --git a/reader/Volksbank_Mittelhessen.py b/reader/Volksbank_Mittelhessen.py
index b982647..df544a3 100644
--- a/reader/Volksbank_Mittelhessen.py
+++ b/reader/Volksbank_Mittelhessen.py
@@ -3,6 +3,7 @@
import datetime
import csv
+import re
import camelot
from reader.Generic import Reader as Generic
@@ -82,33 +83,128 @@ def from_pdf(self, filepath):
flavor="stream",
table_areas=["60,629,573,51"],
columns=["75,112,440,526"],
- strip_text='\n', # übernommen von Commerzbank, da ähnliches Layout
- layout_kwargs={ # übernommen von Commerzbank, da ähnliches Layout
- "char_margin": 2,
- "word_margin": 0.5,
- },
+ split_text=True,
+ #layout_kwargs={ # übernommen von Commerzbank, da ähnliches Layout
+ # "char_margin": 2,
+ # "word_margin": 0.5,
+ #},
)
- # Begin at: "alter Kontostand vom"
- # End at: "neuer Kontostand vom"
-
+
+ # Tabellen aller Seiten zusammenfügen
+ self.all_rows = []
+ for t in tables:
+ if not t.data:
+ continue
+
+ self.all_rows.extend(t.data)
+
+ # Start bei den Kontoumsätzen
+ start_index = 0
+ end_index = len(self.all_rows)
+ date_tx_year = None
+ for row in self.all_rows:
+
+ if row[2].replace(' ', '').lower().startswith('alterkontostand'):
+ # Last row before transactions
+ start_index = self.all_rows.index(row) + 1
+ date_tx_year = row[2][-4:] # Jahr für die Transaktionen merken
+
+ if row[2].replace(' ', '').lower().startswith('neuerkontostand'):
+ # First row after transactions + final line
+ end_index = self.all_rows.index(row) - 1
+ break
+
+ if date_tx_year is None or re.match(r'^\d{4}$', date_tx_year) is None:
+ raise ValueError("Konnte Jahr der Transaktionen nicht ermitteln.")
+
+ result = []
+ enumerated_table = enumerate(self.all_rows[start_index:end_index])
+
+ # Zeilen anhand der Datumsspalte zusammenfügen
+ re_datecheck = re.compile(r'^\d{2}\.\d{2}')
+
+ for i, row in enumerated_table:
+
+ if not re_datecheck.match(row[0]) and not re_datecheck.match(row[1]):
+ continue # Skip Header and unvalid Rows
+
+ # Positives 'Haben' oder negatives 'Soll'
+ betrag = f'-{row[3]}' if row[3] else row[4]
+ betrag = betrag[:-2].replace('.', '').replace(',', '.')
+
+ line = {
+ 'date_tx': datetime.datetime.strptime(
+ f"{row[0]}{date_tx_year}", "%d.%m.%Y"
+ ).replace(tzinfo=datetime.timezone.utc).timestamp(),
+ 'valuta': datetime.datetime.strptime(
+ f"{row[1]}{date_tx_year}", "%d.%m.%Y"
+ ).replace(tzinfo=datetime.timezone.utc).timestamp(),
+ 'art': row[2],
+ 'text_tx': "",
+ 'betrag': float(betrag),
+ 'gegenkonto': "",
+ 'currency': "EUR",
+ 'parsed': {},
+ 'category': None,
+ 'tags': None
+ }
+
+ if not line['betrag']:
+ continue # Skip Null-Buchungen
+
+ # Mehrzeilige Buchungstexte zusammenfügen
+ while self._check_next_line_available(start_index, end_index, i):
+ # 1. There are more lines in the table
+ # 2. Next line belongs to this transaction
+ # 3. First next line is: Gegenkonto
+ prev_line_len = len(row[3])
+ i, row = next(enumerated_table)
+ if not line['text_tx']:
+ line['gegenkonto'] = row[2]
+ line['text_tx'] = row[2]
+ continue
+
+ line['text_tx'] += self._how_to_glue(prev_line_len, row[2])
+
+ result.append(line)
+
+ return result
+
def from_http(self, url):
"""
Liest Kontoumsätze von einer Internetressource ein.
"""
raise NotImplementedError()
- def _newline_replace(self, text_in:str) -> str:
- """Ersetzt Newlines im Buchungsinformationen
- intelligent durch Leerzeichen oder nichts
+ def _how_to_glue(self, prev_line_len, text_tx):
+ """Stellt ein oder kein Leerzeichen an das Ende
+ der Zeichenkette, um die nächste Zeile passend entgegenzunehmen."""
+ if prev_line_len < 54:
+ return ' ' + text_tx
+
+ return text_tx
+
+ def _check_next_line_available(self, start_index, end_index, i):
+ """
+ Hilfsmethode um zu prüfen, ob die nächste Zeile im
+ ausgelesenen PDF-Dokument noch verfügbar ist und zur selben Buchung gehört.
+
Args:
- text_in, str: Input Text aus Kontoauszug
- Return:
- str: Text ohne Newlines
+ start_index (int): Startindex der Kontoumsätze
+ end_index (int): Endindex der Kontoumsätze
+ i (int): Aktueller Index in der Iteration
+ Returns:
+ bool: True, wenn die nächste Zeile noch verfügbar ist, sonst False
"""
- if not '\n' in text_in:
- return text_in
+ next_lines_index = start_index + i + 1
+ if next_lines_index >= end_index:
+ return False
+
+ if self.all_rows[next_lines_index][2] == '':
+ return False
- if text_in.index('\n') < 35:
- return text_in.replace('\n', ' ')
+ if self.all_rows[next_lines_index][0] != '' or \
+ self.all_rows[next_lines_index][1] != '':
+ return False
- return text_in.replace('\n', '')
+ return True
diff --git a/tests/helper.py b/tests/helper.py
index 7d49b72..97bc00d 100644
--- a/tests/helper.py
+++ b/tests/helper.py
@@ -50,7 +50,7 @@ def check_transaktion_list(tx_list):
# Buchungsart (optional, aber bei Generic mit dabei)
buchungs_art = entry.get('art')
assert isinstance(buchungs_art, str) and len(buchungs_art), \
- f"'art' wurde nicht oder falsch erkannt: {gegenkonto}"
+ f"'art' wurde nicht oder falsch erkannt: {buchungs_art}"
# Währung (optional, aber bei Generic mit dabei)
currency = entry.get('currency')
From 75e2010f8ff1a2053092c261119aa600ac520d61 Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Mon, 15 Dec 2025 22:14:08 +0100
Subject: [PATCH 19/31] JS float mit Decimals
---
app/static/js/iban.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/static/js/iban.js b/app/static/js/iban.js
index b63cfc1..11027f8 100644
--- a/app/static/js/iban.js
+++ b/app/static/js/iban.js
@@ -111,6 +111,9 @@ function fillTxDetails(result) {
td.appendChild(a_link);
}
+ } else if (key == 'betrag') {
+ td.innerHTML = r[key].toFixed(2);
+
} else {
td.innerHTML = r[key];
From 77c91acfd15ff5732cf66c4263f98e70645c814d Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Mon, 15 Dec 2025 22:17:34 +0100
Subject: [PATCH 20/31] pylint setting
---
.github/workflows/python-app.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml
index 69910d9..319c3df 100644
--- a/.github/workflows/python-app.yml
+++ b/.github/workflows/python-app.yml
@@ -85,7 +85,7 @@ jobs:
#
# -- Lint and parse Output
#
- pylint_output=$(PYTHONPATH=. pylint . --recursive=y --disable=W0511,R0903 --score=y)
+ pylint_output=$(PYTHONPATH=. pylint . --recursive=y --disable=W0511,R0903,R0801 --score=y)
exitcode=$?
score=$(sed -n '$s/[^0-9]*\([0-9.]*\).*/\1/p' <<< "$pylint_output")
#
From be413adf92d8998eda18fc487976f0bd582b9d94 Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Wed, 17 Dec 2025 22:11:03 +0100
Subject: [PATCH 21/31] Readme angepasst
---
README.md | 87 ++++++++++++++++++++++++++++---------------------------
1 file changed, 44 insertions(+), 43 deletions(-)
diff --git a/README.md b/README.md
index 11b3f8a..0c9cd63 100644
--- a/README.md
+++ b/README.md
@@ -5,39 +5,43 @@
Analyse und Darstellung von Kontoumsätzen bei mehreren Banken.
-## Features
+## Get Started
+
+### Setup
+
+```
+python3.12 -m venv .venv
+source .venv/bin/activate
+.venv/bin/python3.12 -m ensurepip --upgrade # (optional)
+pip install -r requirements.txt
+.venv/bin/python3.12 app/server.py
+```
-### Parsing
+### Start
-Importiere Kontoumsätzen aus Dateien im Format unterstützter Banken (Exports von Umsatzübersichten als CSV, Kontoauszüge als PDF). Für Auswertung der Ausgaben von Zeit zu Zeit.
+- Importiere Kontoumsätze über CSV Listen oder PDF Kontoauszüge deiner Bank ([unterstützte Banken](#unterstützte-banken))
+- Erstelle eine Gruppe mehrerer Konten, um alle diese Umsätze in einer Übersicht zu sehen
+- Wende vorgefertigte oder eigene Regeln für das automatische Taggen und Kategorisieren deiner Umsätze an
+- Suche und Filtere deine Umsätze nach einer Vielzahl möglicher Kriterien
+- Lerne mehr über deinen Cashflow durch die Übersicht der statistischen Auswertungen. Hier kannst du alle oder nur gefilterte Umsätze berücksichtigen.
-Modulare Importer können nach und nach für verschiedene Banken oder spezielle Formate entwickelt werden. Füge einen Importer für deine Bank hinzu :wink:
+## Features
-### Analyse
+Die Funktionen des PynanceParsers setzen stark auf Reproduzierbarkeit. Das bedeutet, dass du beliebig oft gleiche Daten löschen und reimportieren kannst und halbautomatisch wieder die gleichen Ergebnisse (einmalige Transaktionen, Tagging, Kategorien, Statistiken) erhälts. Ein manuelles Editieren ist zwar möglich, aber die Ausnahme.
- Keine doppelten Imports *(Datum, Text und Betrag bilden eine einmalige Kombination)*
- Automatisches Extrahieren von Zusatzinformationen einer Transaktion durch Muster *(RegEx parst Kerninformationen)*
- Automatisches und/oder manuelles Taggen von Umsätzen *(Regelbasiert: RegEx + Zusatzinformationen)*
-- Automatisches und/oder manuelles Kategorisieren von Umsätzen *(Regelbasiert: RegEx + Tags und weitere Indikatoren)*
-- Übersicht über alle Transaktionen *(Vielseitige Filtermöglichkeiten)*
-- Statistische Auswertung auf dem angereicherten Datensatz vieler Transaktionen *(interaktive Grafiken)*
-
-Hinterlegte Regeln können die extrahierten Informationen, weitere Umsatzinformationen und weitere RegExes berücksichtigen und ermöglichen so komplexe Bewertungen einfach zu erstellen.
-
-Ein Tagging findet anschließend auf angereicherten Informationen regelbasiert statt und kann außerdem auch manuell erfolgen.
-
-Auf dieser Grundlage werden Umsätze Kategorisiert wobei auch das händisch editiert werden kann.
+- Automatisches und/oder manuelles Kategorisieren von Umsätzen *(Regelbasiert: RegEx + Tags + Zusatzinformationen)*
+- Übersicht über alle Transaktionen einer oder mehrerer Konten *(Vielseitige Filtermöglichkeiten; Kontogruppen)*
+- Statistische Auswertung auf dem angereicherten Datensatz *(Kontextabhängige Statistken)*
### Darstellung
- Kontohistorie
-- Transaktionsansicht
+- Transaktionsdetails
- Statistiken/Verteilungen/Verläufe
-Listen und Diagramme zeigen dir, wo eigentlich das Geld geblieben ist :thinking:
-
-## Misc
-
### Unterstützte Banken
| Bank | CSV | PDF |
@@ -47,6 +51,10 @@ Listen und Diagramme zeigen dir, wo eigentlich das Geld geblieben ist :thinking:
| Sparkasse Hannover | ⚫ *planned* | ⚫ *planned* |
| Volksbank Mittelhessen eG | 🟢 Umsatzübersicht | 🟢 Kontoauszug |
+Ist deine Bank noch nicht dabei? Den modularen Import kannst du mit [überschaubaren Aufwand](#entwickeln-von-neuen-readern) für deine Bank erweitern.
+
+## Hinweise
+
### Workflow (CSV / PDF Imports)
Umsätze können sich beim Import überschneiden oder mehrfach hochgeladen werden: Transaktionen werden in der Regel nicht doppelt importiert.
@@ -58,28 +66,32 @@ Daher sollte man beachten:
- Regeln nicht auf zwingend vorhandene Leerzeichen auszulegen
- Beim Wechsel eines Formats (PDF / CSV) keine Überschneidungen zu haben (PDF zuerst, dann fehlende Transaktionen selektieren und via CSV exportieren - alternativ bei einem Format bleiben)
-### Tagging- und Kategorisierungsregeln
+### Default Tagging- und Kategorisierungsregeln
In diesem Repository werden nur Basis-Regeln mitgeliefert, da speziellere und genauere Regeln sehr individuell auf einzelne Personen zugeschnitten sind. So schreibt zum Beispiel eine Versicherung die Versichertennummer mit in die Abbuchungen, was einen sehr guten Tagging-Indikator darstellt, jedoch nur für einen speziellen Nutzer dieses Programms. Das schreiben eigener Regeln ist daher unumgänglich, um bessere Ergebnisse zu erzielen.
-Für diesen Zweck gibt es aber die Möglichkeit im Frontend Regeln auszuprobieren, ohne dass Umsätze geändert werden. Neue Regeln können ebenfalls über die Oberfläche temporär hochgeladen werden (bis zum Neustart des Servers) oder dauerhaft im Ordner `settings/rule` abgelegt werden. Die Dateien hier werden in alphabetisch sortierter Reihenfolge geladen (angefangen bei `00-*`), wobei spätere Regeln ggf. bestehende Regeln überschreiben können. Im Rwepository werden nur die Default-Regeln angepasst. Auf diese Weise können eigene Regeln gepflegt werden, ohne dass sie bei Updates verloren gehen.
+## Anpassungen / Contribution
+**You're Welcome !** :tada:
-## Contribution
+Erstelle einen Reader für verschiedene Formate deiner Bank oder ergänze die `parser` und `rules`.
-You're Welcome !
+### Entwickeln neuer `parser` / `rules`
-Erstelle einen Reader für verschiedene Formate deiner Bank oder ergänze die `parser` und `rules`.
+Für diesen Zweck gibt es die Möglichkeit im Frontend Regeln auszuprobieren, ohne dass Umsätze geändert werden. Neue Regeln können ebenfalls über die Oberfläche temporär hochgeladen werden (bis zum Neustart des Servers) oder dauerhaft im Ordner `settings/rule` abgelegt werden. Die Dateien hier werden in alphabetisch sortierter Reihenfolge geladen (angefangen bei `00-*`), wobei spätere Regeln ggf. bestehende Regeln überschreiben können. Im Repository werden nur die Default-Regeln angepasst. Auf diese Weise können eigene Regeln gepflegt werden, ohne dass sie bei Updates verloren gehen.
-## Setup
+**Wenn du neue Regeln für dieses Repository beitragen möchtest, gehst du wie folgt vor:**
-```
-python3.12 -m venv .venv
-source .venv/bin/activate
-.venv/bin/python3.12 -m ensurepip --upgrade # (optional)
-pip install -r requirements.txt
-.venv/bin/python3.12 app/server.py
-```
+- Erstelle einen Fork des Repositories
+- Erstelle Testdaten, auf die die neuen Regeln treffen können
+ - (am einfachsten ist eine JSON Datei wie `tests/commerzbank.json`)
+- Erstelle einen Test wie in (`test_unit_handler_Tags.py`: `test_parsing_regex()`)
+ - Tests helfen beim entwickeln, können aber auch durch die Maintainer während des Pull Request erstellt werden
+- Stelle einen Pull Request
+
+### Entwickeln von neuen Readern
+
+Deine Bank fehlt noch in der Support Tabelle? Stelle einen Pull Request mit einem neuen Reader. [So kannst du ihn erstellen.](Reader.md).
### Testumgebung
@@ -89,14 +101,3 @@ pip install -r requirements.txt
pip install -r tests/requirements.txt
pytest
```
-
-## Entwickeln von neuen Readern
-
-Deine Bank fehlt noch in der Support Tabelle? [So erstellst du einen weiteren Reader](Reader.md).
-
-## Entwickeln neuer `parser` / `rules`
-
-- Erstelle Testdaten, auf die die neuen Regeln treffen können
- - (am einfachsten ist eine JSON Datei wie `tests/commerzbank.json`)
-- Erstelle einen Test wie in (`test_unit_handler_Tags.py`: `test_parsing_regex()`)
- - Tests helfen beim entwickeln, können aber auch durch mich beim Pull Request erstellt werden
From 3d9e04acc8f3c286f9f16fc6f57a796577f630d5 Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Wed, 17 Dec 2025 22:29:25 +0100
Subject: [PATCH 22/31] Emoji
---
README.md | 32 ++++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index 0c9cd63..464b22f 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,8 @@
Analyse und Darstellung von Kontoumsätzen bei mehreren Banken.
+**This repo is german but you are welcome to add your language to the frontend.**
+
## Get Started
### Setup
@@ -29,12 +31,30 @@ pip install -r requirements.txt
Die Funktionen des PynanceParsers setzen stark auf Reproduzierbarkeit. Das bedeutet, dass du beliebig oft gleiche Daten löschen und reimportieren kannst und halbautomatisch wieder die gleichen Ergebnisse (einmalige Transaktionen, Tagging, Kategorien, Statistiken) erhälts. Ein manuelles Editieren ist zwar möglich, aber die Ausnahme.
-- Keine doppelten Imports *(Datum, Text und Betrag bilden eine einmalige Kombination)*
-- Automatisches Extrahieren von Zusatzinformationen einer Transaktion durch Muster *(RegEx parst Kerninformationen)*
-- Automatisches und/oder manuelles Taggen von Umsätzen *(Regelbasiert: RegEx + Zusatzinformationen)*
-- Automatisches und/oder manuelles Kategorisieren von Umsätzen *(Regelbasiert: RegEx + Tags + Zusatzinformationen)*
-- Übersicht über alle Transaktionen einer oder mehrerer Konten *(Vielseitige Filtermöglichkeiten; Kontogruppen)*
-- Statistische Auswertung auf dem angereicherten Datensatz *(Kontextabhängige Statistken)*
+👉 **Keine doppelten Imports**
+
+ *(Datum, Text und Betrag bilden eine einmalige Kombination)*
+
+👉 **Automatisches Extrahieren von Zusatzinformationen**
+
+ *(RegEx parst Kerninformationen)*
+
+👉 **Automatisches und/oder manuelles Taggen**
+
+ *(Regelbasiert: RegEx + Zusatzinformationen)*
+
+👉 **Automatisches und/oder manuelles Kategorisieren**
+
+ *(Regelbasiert: RegEx + Tags + Zusatzinformationen)*
+
+👉 **Übersicht über alle Transaktionen**
+
+ *(vielseitige Filtermöglichkeiten in einem Konto oder einer Kontogruppe)*
+
+👉 **Statistische Auswertung auf dem angereicherten Datensatz**
+
+ *(Kontextabhängige Statistken)*
+
### Darstellung
From 90907bfdd2f5be091eb38c7dc3b6e68fe756e0dc Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Wed, 17 Dec 2025 22:30:21 +0100
Subject: [PATCH 23/31] Language hint
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 464b22f..77f788b 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,9 @@
-darkgreen)

-Analyse und Darstellung von Kontoumsätzen bei mehreren Banken.
+*This repo is german but you are welcome to add your language to the frontend.*
-**This repo is german but you are welcome to add your language to the frontend.**
+Analyse und Darstellung von Kontoumsätzen bei mehreren Banken.
## Get Started
From 42564e2d24a5884dea6d30481f82e013457d45dd Mon Sep 17 00:00:00 2001
From: Pitastic
Date: Tue, 23 Dec 2025 22:05:09 +0100
Subject: [PATCH 24/31] Add Gegenkonto filter
---
app/routes.py | 4 +++-
app/static/js/functions.js | 6 ++++++
app/templates/macros.html | 34 +++++++++++++++++++++++-----------
app/ui.py | 11 +++++++++++
handler/MongoDb.py | 2 +-
handler/TinyDb.py | 29 +++++++++++++++--------------
6 files changed, 59 insertions(+), 27 deletions(-)
diff --git a/app/routes.py b/app/routes.py
index 3fa45c3..335efb0 100644
--- a/app/routes.py
+++ b/app/routes.py
@@ -50,7 +50,9 @@ def iban(iban) -> str:
Startseite in einem Konto.
Args (uri):
- iban, str: IBAN zu der die Einträge angezeigt werden sollen.
+ iban, str: IBAN zu der die Einträge angezeigt werden sollen.
+ text, str (query): Volltextsuche im Betreff mit RegEx Support
+ gegenkonto, str (query): Volltextsuche im Gegenkonto mit RegEx Support
startDate, str (query): Startdatum (Y-m-d) für die Anzeige der Einträge
endDate, str (query): Enddatum (Y-m-d) für die Anzeige der Einträge
category, str (query): Kategorie-Filter
diff --git a/app/static/js/functions.js b/app/static/js/functions.js
index e828779..a69a62f 100644
--- a/app/static/js/functions.js
+++ b/app/static/js/functions.js
@@ -51,6 +51,12 @@ function getFilteredList() {
arg_concat = '&';
}
+ const peer = document.getElementById('filter-peer').value;
+ if (peer) {
+ query_args = query_args + arg_concat + 'gegenkonto=' + peer;
+ arg_concat = '&';
+ }
+
const category = document.getElementById('filter-cat').value;
if (category) {
query_args = query_args + arg_concat + 'category=' + category;
diff --git a/app/templates/macros.html b/app/templates/macros.html
index b389454..d64497b 100644
--- a/app/templates/macros.html
+++ b/app/templates/macros.html
@@ -19,7 +19,7 @@
{{ transaction.date_tx | ctime }}
({{ transaction.valuta | ctime }})
-
{{ transaction.text_tx[:75] }}{% if transaction.text_tx|length > 60 %} ...{%endif%}
+
{{ transaction.text_tx[:90] }}{% if transaction.text_tx|length > 60 %} ...{%endif%}