boekhouding-beancount/import
2025-06-27 12:50:25 +02:00

126 lines
3.5 KiB
Python
Executable file

#!/usr/bin/env python3
from os.path import basename
from beangulp import Ingest
from beangulp import mimetypes
from beangulp.importers import csvbase
from beangulp.testing import main
from beancount.core.data import Transaction, Posting
from deelnemers import deelnemers
participants = deelnemers()
class RaboAmount(csvbase.Amount):
def __init__(self, col):
super().__init__(col, subs={"\\+": "", ",": "."})
class Importer(csvbase.Importer):
encoding = "iso-8859-15"
date = csvbase.Date("Datum", "%Y-%m-%d")
payee = csvbase.Columns("Naam tegenpartij", "Tegenrekening IBAN/BBAN", sep="; ")
narration = csvbase.Columns(
"Omschrijving-1", "Omschrijving-2", "Omschrijving-3", sep=" "
)
amount = RaboAmount("Bedrag")
balance = RaboAmount("Saldo na trn")
def identify(self, filepath):
mimetype, encoding = mimetypes.guess_type(filepath)
if mimetype != "text/csv":
return False
with open(filepath, "rb") as fd:
head = fd.read(1024)
return head.startswith(
b'"IBAN/BBAN","Munt","BIC","Volgnr","Datum","Rentedatum","Bedrag","Saldo na trn","Tegenrekening IBAN/BBAN"'
)
def filename(self, filepath):
return "rabobank." + basename(filepath)
def guess_contra(entry):
posting = entry.postings[0]
if entry.payee == "Kosten":
return "Uitgaven:Bankkosten"
if entry.payee == "Stichting Bitlair; NL09RABO3159222187":
return "Activa:Spaarrekening"
if (
entry.payee == "Symbiose Beheer B.V.; NL84ABNA0630587221"
and posting.units.number < -3400
):
return "Uitgaven:Huur"
if (
entry.payee == "Stichting EventInfra; NL59RABO0312574800"
and entry.narration == "Verhuur opslagruimte "
):
return "Activa:Debiteuren:EventInfra"
if entry.narration.startswith("Decla") and posting.units.number < 0:
return "Passiva:Declaraties"
def have_keyword(*keywords):
l_description = entry.narration.lower()
return any(kw in l_description for kw in keywords)
if have_keyword(
"deposit",
"revbank",
" bar ",
"kassa vulling",
"desposit",
"(RB QR)",
"rev-bank",
" bank",
"elena transfer",
"barsaldo",
):
return "Passiva:RevBank"
for p in participants:
if (
hasattr(p, "maandbedrag")
and hasattr(p, "ibans")
and p.maandbedrag == posting.units.number
and any(iban in entry.payee for iban in p.ibans)
):
return f"Activa:Debiteuren:Deelnemers:{p.nickname.title().replace('_', '')}"
return None
def classify_hook(extracted_entries_list, ledger_entries):
def _classify(entry):
if not isinstance(entry, Transaction):
return entry
contra = guess_contra(entry)
posting = entry.postings[0]
entry.postings.append(
Posting(
contra or "Inkomsten:TODO",
-posting.units,
posting.cost,
posting.price,
None if contra else "!",
None,
)
)
return entry
return [
(filename, [_classify(entry) for entry in entries], account, importer)
for filename, entries, account, importer in extracted_entries_list
]
if __name__ == "__main__":
importers = [Importer("Activa:Betaalrekening", "EUR")]
hooks = [classify_hook]
main = Ingest(importers, hooks)
main()