Skip to content

Instantly share code, notes, and snippets.

@weiserr
Last active February 22, 2025 20:37
Show Gist options
  • Save weiserr/bc0745d3145b29f02b1d to your computer and use it in GitHub Desktop.
Save weiserr/bc0745d3145b29f02b1d to your computer and use it in GitHub Desktop.
Postfinance YNAB Export

PostFinance YNAB 4 Export

This scripts allows the export of PostFinance account flow data for YNAB 4 using Tampermonkey. The export is not based on the CSV generated by the Post as it does not adhere to the standard but makes use of the data loaded on the client side for doing said CSV export.

Installation

If Tampermonkey is installed you can click on the RAW view of the postfinance-ynab.user.js file of this Gist. Tampermonkey should ask you whether or not you would like to install this script.

Usage

Tip: if you have a lot of transactions make sure to increase the shown amount from 25 to 100 somewhere in the settings.

Navigate to the account flow data in PostFinance (Konto -> Bewegungen) and:

  1. Click on the YNAB Export button.
  2. Hit the import button in the YNAB account view.
// ==UserScript==
// @name Postfinance CSV Export
// @namespace http://death-knight.com
// @include https://www.postfinance.ch/ap/ba/ob/html/finance/*
// @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
// @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
// @version 13
// @grant none
// ==/UserScript==
// Function definitions
function createGenerateButton() {
let input = document.createElement("button");
input.type = "button";
input.className = "ynab-button fpui-button text-sm fpui-button--primary fpui-button--normal";
input.textContent = "YNAP Export";
input.onclick = createExportButton;
document.querySelectorAll('fpui-actions').forEach(elem => {if(elem.querySelectorAll(".ynab-button").length < 1) {elem.appendChild(input)}});
}
function createExportButton() {
const csv = createCSV();
const date = new Date();
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const filename = "ynab-" + year + "-" + month + "-" + day + ".csv";
// create a temporary download link
let input = document.createElement("a");
input.setAttribute('href', 'data:text/csv;charset=UTF-8,' + encodeURIComponent(csv));
input.setAttribute('download', filename);
input.style.display = 'none';
// ...and click it to trigger the download
document.body.appendChild(input);
input.click();
document.body.removeChild(input);
}
function createCSV() {
// acquire the data computed by the Post
const rows = Array.from(document.querySelector('[data-cy="movements-table"] > tbody').querySelectorAll("tr"));
// process the content
let result = rows.map((row, index) => sanitizeRow(row));
// add the header
result.unshift(createHeader());
// join the results into a single string
return result.join("\n");
}
function createHeader() {
return 'Date,Payee,Category,Memo,Outflow,Inflow';
}
function sanitizeRow(row) {
const date = row.querySelector('[data-cy="date"] > span:nth-child(2)').textContent;
const payee = row.querySelector('[data-cy="shortText"] > span:nth-child(2)').textContent.replace(/\"/gi, '').replace(/\n/gi, ' ').replace(/,/gi, '; ').trim();
const category = '';
const memo = '';
const outflow = row.querySelector('[data-cy="debit"] > span:nth-child(2) > fpui-amount > span:nth-child(1)')?.textContent?.replace(/[^\d\.]/g, '')?.trim() ?? '';
const inflow = row.querySelector('[data-cy="credit"] > span:nth-child(2) > fpui-amount > span:nth-child(1)')?.textContent?.replace(/[^\d\.]/g, '')?.trim() ?? '';
let line = [];
line.push(date);
line.push(payee);
line.push(category);
line.push(memo);
line.push(outflow);
line.push(inflow);
return line.join(',');
}
function waitForElem(selector) {
if (document.URL == "https://www.postfinance.ch/ap/ba/ob/html/finance/assets/movements-overview" && document.querySelector(selector)) {
createGenerateButton();
}
const observer = new MutationObserver((mutations) => {
if (document.URL == "https://www.postfinance.ch/ap/ba/ob/html/finance/assets/movements-overview" && document.querySelector(selector)) {
createGenerateButton();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
waitForElem('fpui-actions');
@weiserr
Copy link
Author

weiserr commented Feb 22, 2025

Minor update due to the following:

  • Previously we had to skip over the first entry of the transaction table body due the header information being encoded in the first row (not apparent in the UI but in the HTML). As the table now follows HTML semantics - the header information is not part of the body anymore - we are not skipping over the first entry anymore but correctly exporting all transactions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment