Ingest comdirect #5

Merged
mathias2784 merged 31 commits from ingest-comdirect into master 2026-09-13 20:27:16 +00:00
2 changed files with 56 additions and 19 deletions
Showing only changes of commit b93fd35f43 - Show all commits
+45 -18
View File
@@ -102,28 +102,51 @@ class Category(BaseModel):
return f'Category.from_str("{self.__str__()}")' return f'Category.from_str("{self.__str__()}")'
def _parse_date_german(date_str: str) -> date | None:
if date_str in ["offen", "neu", "--"]:
return None
day, month, year = list(map(int, date_str.split(".")))
return date(day=day, month=month, year=year)
def _parse_decimal_german(decimal_str: str) -> Decimal:
return Decimal(decimal_str.replace(".", "").replace(",", "."))
class ComdirectBase(BaseModel): class ComdirectBase(BaseModel):
date_book: str = Field(alias="Buchungstag", examples=["07.09.2026"]) date_val: date | None = Field(alias="Wertstellung (Valuta)", examples=["07.09.2026"])
amount_eur: str = Field(alias="Umsatz in EUR", examples=["32,43"]) amount_eur: Decimal = Field(alias="Umsatz in EUR", examples=["32,43"])
@field_validator("date_val", mode="before")
@classmethod
def parse_date_val(cls, date_str: str) -> date | None:
return _parse_date_german(date_str)
@field_validator("amount_eur", mode="before")
@classmethod
def parse_amount(cls, decimal_str: str) -> Decimal:
return _parse_decimal_german(decimal_str)
class ComdirectCommon(ComdirectBase): class ComdirectBaseBook(ComdirectBase):
date_book: date | None = Field(alias="Buchungstag", examples=["07.09.2026"])
@field_validator("date_book", mode="before")
@classmethod
def parse_date_book(cls, date_str: str) -> date | None:
return _parse_date_german(date_str)
class ComdirectGiro(ComdirectBaseBook):
type: str = Field( type: str = Field(
alias="Vorgang", examples=["Lastschrift / Belastung", "Kartenumsatz"] alias="Vorgang", examples=["Lastschrift / Belastung", "Kartenumsatz"]
) )
text: str = Field(alias="Buchungstext") text: str = Field(alias="Buchungstext")
class ComdirectGiro(ComdirectCommon): class ComdirectCreditCard(ComdirectBaseBook):
# "Buchungstag";"Wertstellung (Valuta)";"Vorgang";"Buchungstext";"Umsatz in EUR";
# "Buchungstag";"Wertstellung (Valuta)";"Vorgang";"Buchungstext";"Umsatz in EUR";
date_val: str = Field(alias="Wertstellung (Valuta)", examples=["07.09.2026"])
class ComdirectCreditCard(ComdirectCommon):
# "Buchungstag";"Umsatztag";"Vorgang";"Referenz";"Buchungstext";"Umsatz in EUR"; # "Buchungstag";"Umsatztag";"Vorgang";"Referenz";"Buchungstext";"Umsatz in EUR";
date_book: str = Field(alias="Buchungstag", examples=["07.09.2026"]) date_book: date | None = Field(alias="Buchungstag", examples=["07.09.2026"])
date_val: str = Field(alias="Umsatztag", default="") date_val: date | None = Field(alias="Umsatztag", default=None)
type: str = Field( type: str = Field(
alias="Vorgang", alias="Vorgang",
examples=["Lastschrift / Belastung", "Kartenumsatz"], examples=["Lastschrift / Belastung", "Kartenumsatz"],
@@ -131,14 +154,18 @@ class ComdirectCreditCard(ComdirectCommon):
) )
reference: str = Field(alias="Referenz", default="") reference: str = Field(alias="Referenz", default="")
text: str = Field(alias="Buchungstext", default="") text: str = Field(alias="Buchungstext", default="")
amount_eur: str = Field(alias="Umsatz in EUR", examples=["32,43"], default="") amount_eur: Decimal | None = Field(alias="Umsatz in EUR", examples=["32,43"], default=None)
class ComdirectDepot(BaseModel): class ComdirectDepot(ComdirectBase):
# "Buchungstag";"Geschäftstag";"Stück / Nom.";"Bezeichnung";"WKN";"Währung";"Ausführungskurs";"Umsatz in EUR"; date_val: date | None = Field(alias="Geschäftstag")
date_val: str = Field(alias="Geschäftstag") num: Decimal = Field(alias="Stück / Nom.")
num: str = Field(alias="Stück / Nom.")
name: str = Field(alias="Bezeichnung") name: str = Field(alias="Bezeichnung")
wkn: str = Field(alias="WKN") wkn: str = Field(alias="WKN")
currency: str = Field(alias="Währung") currency: str = Field(alias="Währung")
spot_price: str = Field(alias="Ausführungskurs") spot_price: Decimal = Field(alias="Ausführungskurs")
@field_validator("num", "spot_price", mode="before")
@classmethod
def parse_decimals(cls, decimal_in: str) -> Decimal:
return _parse_decimal_german(decimal_in)
+11 -1
View File
@@ -1,10 +1,20 @@
from pathlib import Path from pathlib import Path
from src.models import ComdirectCreditCard, ComdirectDepot, ComdirectGiro
from src.reader import reader_comdirect from src.reader import reader_comdirect
from src.settings.account_ingest import ACCOUNT_INGESTION_FUNCTIONS
def test_reader(): def test_reader_comdirect():
bank_name = "comdirect"
assert bank_name in ACCOUNT_INGESTION_FUNCTIONS
test_path = Path(".local/data/comdirect/umsaetze_9773063005_20260906-2043.csv") test_path = Path(".local/data/comdirect/umsaetze_9773063005_20260906-2043.csv")
assert test_path.exists() assert test_path.exists()
data = reader_comdirect(test_path) data = reader_comdirect(test_path)
assert data assert data
assert len(data) == len(ACCOUNT_INGESTION_FUNCTIONS.get(bank_name))
assert set(data) == set(ACCOUNT_INGESTION_FUNCTIONS.get(bank_name))
assert all(isinstance(row, ComdirectGiro) for row in data["Girokonto"])
assert all(isinstance(row, ComdirectCreditCard) for row in data["Visa-Karte (Kreditkarte)"])
assert all(isinstance(row, ComdirectGiro) for row in data["Tagesgeld PLUS-Konto"])
assert all(isinstance(row, ComdirectDepot) for row in data["Depot"])