2025-08-01 08:09:25 +00:00
|
|
|
from beancount.ingest import importer
|
|
|
|
|
from beancount.core import data
|
|
|
|
|
from beancount.core.number import D
|
|
|
|
|
|
|
|
|
|
import csv
|
2025-08-01 17:37:21 +00:00
|
|
|
import datetime
|
2025-08-01 08:09:25 +00:00
|
|
|
|
|
|
|
|
class MyCSVImporter(importer.ImporterProtocol):
|
|
|
|
|
"""A Beancount importer for CSV files"""
|
|
|
|
|
|
|
|
|
|
def identify(self, file):
|
|
|
|
|
return file.name.endswith('.csv')
|
|
|
|
|
|
|
|
|
|
def file_account(self, file):
|
2025-08-01 13:24:42 +00:00
|
|
|
return "Assets:Bank:PostbankGiro"
|
2025-08-01 08:09:25 +00:00
|
|
|
|
|
|
|
|
def file_date(self, file):
|
|
|
|
|
"""Returns the unique date of the file, if any."""
|
|
|
|
|
# Optioneel: als je bestandsnaam een datum bevat
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def file_name(self, file):
|
|
|
|
|
"""Returns the unique name of the file, if any."""
|
|
|
|
|
# Optioneel: als je een unieke naam wilt baseren op het bestand
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def extract(self, file, existing_entries=None):
|
2025-08-01 08:58:00 +00:00
|
|
|
"""Extracts entries from a files."""
|
2025-08-01 08:09:25 +00:00
|
|
|
entries = []
|
|
|
|
|
|
2025-08-01 17:37:21 +00:00
|
|
|
# file.contents
|
|
|
|
|
with open(file.name, encoding='utf-8') as f:
|
|
|
|
|
csv_reader = csv.reader(f, delimiter=';')
|
|
|
|
|
# Gebruik enumerate() om een teller (index) te krijgen
|
|
|
|
|
for index, row in enumerate(csv_reader):
|
|
|
|
|
if len(row) < 18 or row[17] != "EUR":
|
2025-08-01 08:58:00 +00:00
|
|
|
continue
|
|
|
|
|
|
2025-08-01 12:31:16 +00:00
|
|
|
# De index begint bij 0, dus de 12e kolom is index 11.
|
|
|
|
|
# Boekingsdatum = index 0
|
|
|
|
|
# Ontvanger = index 3
|
|
|
|
|
# Omschrijving = index 4
|
|
|
|
|
# Bedrag = index 11
|
|
|
|
|
# Valuta = index 17
|
2025-08-01 08:58:00 +00:00
|
|
|
datum_str = row[0]
|
2025-08-01 13:24:42 +00:00
|
|
|
ontvanger = row[3]
|
2025-08-01 12:31:16 +00:00
|
|
|
omschrijving_str = row[4]
|
|
|
|
|
bedrag_str = row[11]
|
2025-08-01 13:24:42 +00:00
|
|
|
valuta = row[17]
|
2025-08-01 08:58:00 +00:00
|
|
|
|
|
|
|
|
dag, maand, jaar = datum_str.split('.')
|
2025-08-01 17:37:21 +00:00
|
|
|
transactie_datum = datetime.date(int(jaar), int(maand), int(dag))
|
|
|
|
|
|
2025-08-01 13:24:42 +00:00
|
|
|
payee = ontvanger
|
2025-08-01 12:31:16 +00:00
|
|
|
narration = omschrijving_str
|
2025-08-01 17:37:21 +00:00
|
|
|
# Verwijder eerst de duizendtallen-scheidingstekens (de punten)
|
|
|
|
|
bedrag_str = bedrag_str.replace('.', '')
|
|
|
|
|
# Vervang daarna de komma door een punt
|
|
|
|
|
bedrag_str = bedrag_str.replace(',', '.')
|
|
|
|
|
bedrag = D(bedrag_str)
|
2025-08-01 13:24:42 +00:00
|
|
|
currency = valuta
|
2025-08-01 08:58:00 +00:00
|
|
|
|
|
|
|
|
meta = data.new_metadata(file.name, index)
|
|
|
|
|
|
2025-08-01 12:31:16 +00:00
|
|
|
# Bepaal of het een inkomst of uitgave is op basis van het voorteken van het bedrag.
|
|
|
|
|
if bedrag < D(0):
|
|
|
|
|
# Uitgave: bedrag is negatief.
|
|
|
|
|
# Geld gaat van de bankrekening naar een uitgavenrekening.
|
|
|
|
|
tegenrekening = self._map_payee_to_account(payee + " " + omschrijving_str)
|
2025-08-01 17:37:21 +00:00
|
|
|
|
|
|
|
|
#FORMAT: data.Posting(account, units, cost=None, price=None, flag=None, meta=None)
|
2025-08-01 12:31:16 +00:00
|
|
|
postings = [
|
2025-08-01 17:37:21 +00:00
|
|
|
#data.Posting(self.file_account(file), bedrag, currency, None, None, None),
|
|
|
|
|
data.Posting(self.file_account(file), data.Amount(bedrag, currency), None, None, None, None),
|
|
|
|
|
#data.Posting(tegenrekening, -bedrag, currency, None, None, None),
|
|
|
|
|
data.Posting(tegenrekening, data.Amount(bedrag, currency), None, None, None, None),
|
2025-08-01 12:31:16 +00:00
|
|
|
]
|
|
|
|
|
else:
|
|
|
|
|
# Inkomsten: bedrag is positief.
|
|
|
|
|
# Geld gaat van een inkomstenrekening naar de bankrekening.
|
|
|
|
|
tegenrekening = self._map_payee_to_account(payee + " " + omschrijving_str)
|
2025-08-01 17:37:21 +00:00
|
|
|
|
|
|
|
|
#FORMAT: data.Posting(account, units, cost=None, price=None, flag=None, meta=None)
|
2025-08-01 12:31:16 +00:00
|
|
|
postings = [
|
2025-08-01 17:37:21 +00:00
|
|
|
#data.Posting(tegenrekening, -bedrag, currency, None, None, None),
|
|
|
|
|
data.Posting(tegenrekening, data.Amount(-bedrag, currency), None, None, None, None),
|
|
|
|
|
#data.Posting(self.file_account(file), bedrag, currency, None, None, None),
|
|
|
|
|
data.Posting(self.file_account(file), data.Amount(bedrag, currency), None, None, None, None),
|
2025-08-01 12:31:16 +00:00
|
|
|
]
|
|
|
|
|
|
2025-08-01 08:58:00 +00:00
|
|
|
transaction = data.Transaction(
|
|
|
|
|
meta=meta,
|
|
|
|
|
date=transactie_datum,
|
|
|
|
|
flag='*',
|
2025-08-01 12:31:16 +00:00
|
|
|
payee=payee,
|
|
|
|
|
narration=narration,
|
2025-08-01 17:37:21 +00:00
|
|
|
tags=frozenset(),
|
|
|
|
|
links=frozenset(),
|
2025-08-01 12:31:16 +00:00
|
|
|
postings=postings
|
2025-08-01 08:58:00 +00:00
|
|
|
)
|
2025-08-01 12:31:16 +00:00
|
|
|
|
2025-08-01 08:58:00 +00:00
|
|
|
entries.append(transaction)
|
2025-08-01 12:31:16 +00:00
|
|
|
|
|
|
|
|
return entries
|
|
|
|
|
|
|
|
|
|
def _map_payee_to_account(self, payee):
|
|
|
|
|
mapping = {
|
2025-08-01 13:24:42 +00:00
|
|
|
"Lohn": "Income:Salaris",
|
2025-08-01 12:31:16 +00:00
|
|
|
"Miete": "Expenses:Rent",
|
|
|
|
|
"Sachsen":"Expenses:Electricity",
|
|
|
|
|
}
|
|
|
|
|
for sleutelwoord, rekening in mapping.items():
|
|
|
|
|
if sleutelwoord.lower() in payee.lower():
|
|
|
|
|
return rekening
|
|
|
|
|
|
|
|
|
|
return "Expenses:Uncategorized"
|