Skip to content

Instantly share code, notes, and snippets.

@thraizz
Created June 24, 2024 17:52
Show Gist options
  • Save thraizz/fbfd91cc71e62ff463bd32f5586aa0d8 to your computer and use it in GitHub Desktop.
Save thraizz/fbfd91cc71e62ff463bd32f5586aa0d8 to your computer and use it in GitHub Desktop.
ING DiBa DE to YNAB CSV

This script will add a "Download past transactions CSV" and a "Download prebooked transactions CSV" button to your Umsätze page. Use these to either download past or upcoming transactions that you can import into YNAB.

// ==UserScript==
// @name ING DiBa Transactions to CSV
// @namespace http://tampermonkey.net/
// @version 1.2
// @description Extract ING transactions and convert to CSV format
// @author Aron Schüler
// @match https://banking.ing.de/*
// @grant none
// ==/UserScript==
(function () {
"use strict";
function extractTransactions(extractPrebookedTransactions = false) {
let transactions = [];
// Function to parse amount to separate outflow and inflow
function parseAmount(amountStr) {
let amount = amountStr
.replace("€", "")
.trim()
.replace(".", "")
.replace(",", ".");
let outflow = "";
let inflow = "";
if (parseFloat(amount) < 0) {
outflow = Math.abs(parseFloat(amount));
} else {
inflow = Math.abs(parseFloat(amount));
}
return { outflow, inflow };
}
// Function to parse dates into en-US format
function parseDate(dateStr) {
let [day, month, year] = dateStr.split(".");
return `${month}/${day}/${year}`;
}
// Function to clean up the extracted text
function cleanText(text) {
if (text) {
return text
.replace("/&nbsp;", " ")
.replace(/\n/g, " ")
.replace(/\s\s+/g, " ")
.trim();
}
return text;
}
// Function to remove prepended noise
function cleanMemo(memo) {
if (memo) {
return memo.replace("Lastschrift / ", " ");
}
return memo;
}
// Function to parse memo into payee
function getPayeeFromMemo(memo) {
if (memo) {
// Match for "Überweisung" transactions
let payee = memo.match(/(.*?)(?:\s+IBAN|,)/);
if (payee) {
return payee[1];
}
// Match for transactions to or from other money sources
let transfer =
memo.match(/Kleingeld Plus - (.*)/) || memo.includes("Bareinzahlung");
if (transfer) {
return "YOUR_NAME";
}
// Match for "Entgelt" transactions
let entgelt = memo.includes("Entgelt");
if (entgelt) {
return "ING DiBa";
}
}
return "";
}
if (extractPrebookedTransactions) {
// Extract prebooked transactions
document
.querySelectorAll(".g2p-transaction-summary--prebooked")
.forEach((summary) => {
let button = summary.querySelector(".g2p-transaction-details-btn");
let payee = cleanText(
summary.querySelector(".g2p-transaction-summary__recipient")
?.textContent || "",
);
let memo = "Vorgebuchte Transaktion";
let amountText = cleanText(
summary.querySelector(
".g2p-transaction-summary__amount .g2p-amount",
)?.textContent || "",
);
let { outflow, inflow } = parseAmount(amountText);
let dateMatch = button
.getAttribute("aria-label")
.match(/Datum (\d{2}\.\d{2}\.\d{4})/);
let date = dateMatch
? parseDate(dateMatch[1])
: new Date().toLocaleDateString("en-US");
if (payee === "") {
payee = getPayeeFromMemo(memo);
}
transactions.push({ date, payee, memo, outflow, inflow });
});
} else {
// Extract past transactions
document.querySelectorAll(".g2p-transaction-group").forEach((group) => {
let dateElement = group.querySelector(".g2p-transaction-group__title");
let date = dateElement
? parseDate(dateElement.textContent.trim())
: new Date().toLocaleDateString("en-US");
group
.querySelectorAll(".g2p-transaction-summary--notags")
.forEach((summary) => {
let payee = cleanText(
summary.querySelector(".g2p-transaction-summary__recipient")
?.textContent || "",
);
let memo = cleanText(
summary.querySelector(".g2p-transaction-summary__type")
?.textContent || "",
);
memo = cleanMemo(memo);
let amountText = cleanText(
summary.querySelector(
".g2p-transaction-summary__amount .g2p-amount",
)?.textContent || "",
);
if (payee === "") {
payee = getPayeeFromMemo(memo);
}
let { outflow, inflow } = parseAmount(amountText);
transactions.push({ date, payee, memo, outflow, inflow });
});
});
}
return transactions;
}
function convertToCSV(transactions) {
let csvContent = "Date\tPayee\tMemo\tOutflow\tInflow\n";
transactions.forEach((tr) => {
csvContent += `${tr.date}\t${tr.payee}\t${tr.memo}\t${tr.outflow}\t${tr.inflow}\n`;
});
return csvContent;
}
function downloadCSV(csvContent, prefix = "") {
let blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
let link = document.createElement("a");
let url = URL.createObjectURL(blob);
link.setAttribute("href", url);
let name = `${prefix}transactions-${new Date()
.toLocaleDateString("de-DE")
.replace(/\./g, "-")}.csv`;
link.setAttribute("download", name);
link.style.visibility = "hidden";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// Add a button to trigger the CSV download of prebooked
let btn = document.createElement("button");
btn.innerHTML = "Download prebooked transactions CSV";
btn.style.position = "fixed";
btn.style.top = "10px";
btn.style.right = "10px";
btn.style.zIndex = 1000;
btn.onclick = function () {
let transactions = extractTransactions(true);
let csvContent = convertToCSV(transactions);
downloadCSV(csvContent, "prebooked-");
};
document.body.appendChild(btn);
// Add a button to trigger the CSV download of past transactions
let btn2 = document.createElement("button");
btn2.innerHTML = "Download past transactions CSV";
btn2.style.position = "fixed";
btn2.style.top = "50px";
btn2.style.right = "10px";
btn2.style.zIndex = 1000;
btn2.onclick = function () {
let transactions = extractTransactions(false);
let csvContent = convertToCSV(transactions);
downloadCSV(csvContent);
};
document.body.appendChild(btn2);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment