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.
Created
June 24, 2024 17:52
-
-
Save thraizz/fbfd91cc71e62ff463bd32f5586aa0d8 to your computer and use it in GitHub Desktop.
ING DiBa DE to YNAB CSV
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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("/ ", " ") | |
.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