Skip to content

Instantly share code, notes, and snippets.

@houshuang
Forked from weiserr/README.md
Last active September 29, 2019 21:27
Show Gist options
  • Save houshuang/44b8590a0cee0d5e17f7169f56dc12bf to your computer and use it in GitHub Desktop.
Save houshuang/44b8590a0cee0d5e17f7169f56dc12bf 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 regular CSV Export. (You can cancel the download)
  2. Now click on the YNAB Export button.
  3. 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/fp/html/e-finance/assets
// @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 8
// @grant none
// @require https://code.jquery.com/jquery-3.3.1.slim.js
// ==/UserScript==
// Function definitions
function createGenerateButton() {
let input = document.createElement("input");
input.type = "button";
input.value = "YNAB Export";
input.className = "btn";
input.style.setProperty('margin-left', '8px');
input.onclick = createExportButton;
$("input[name='data']")[0].parentNode.append(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
let result = JSON.parse($("input[name='data']")[0].value)
// process the content
result = result.filter(row => row.length === 6).map((row, index) => index > 0 ? sanitizeRow(row) : createHeader());
// join the results into a single string
return result.join("\n");
}
function createHeader() {
return 'Date,Payee,Memo,Outflow,Inflow';
}
function sanitizeRow(row) {
const date = row[0].replace(/(.*)-(.*)-(.*)/, '$3.$2.$1');
const payee = row[1].replace(/\"/gi, '').replace(/\r/gi, '').replace(/,/gi, ';');
const category = '';
const memo = '';
const outflow = row[3];
const inflow = row[2];
let line = [];
line.push(date);
line.push(payee);
line.push(memo);
line.push(outflow);
line.push(inflow);
return line.join(',');
}
/* waitForKeyElements(): A handy, utility function that
* does what it says.
*/
function waitForKeyElements(
selectorTxt, /* Required: The jQuery selector string that
specifies the desired element(s).
*/
actionFunction, /* Required: The code to run when elements are
found. It is passed a jNode to the matched
element.
*/
bWaitOnce, /* Optional: If false, will continue to scan for
new elements even after the first match is
found.
*/
iframeSelector /* Optional: If set, identifies the iframe to
search.
*/
) {
var targetNodes, btargetsFound;
if (typeof iframeSelector == "undefined")
targetNodes = $(selectorTxt);
else
targetNodes = $(iframeSelector).contents()
.find(selectorTxt);
if (targetNodes && targetNodes.length > 0) {
/*--- Found target node(s). Go through each and act if they
are new.
*/
targetNodes.each(function () {
var jThis = $(this);
var alreadyFound = jThis.data('alreadyFound') || false;
if (!alreadyFound) {
//--- Call the payload function.
actionFunction(jThis);
jThis.data('alreadyFound', true);
}
});
btargetsFound = true;
}
else {
btargetsFound = false;
}
//--- Get the timer-control variable for this selector.
var controlObj = waitForKeyElements.controlObj || {};
var controlKey = selectorTxt.replace(/[^\w]/g, "_");
var timeControl = controlObj[controlKey];
//--- Now set or clear the timer as appropriate.
if (btargetsFound && bWaitOnce && timeControl) {
//--- The only condition where we need to clear the timer.
clearInterval(timeControl);
delete controlObj[controlKey]
}
else {
//--- Set a timer, if needed.
if (!timeControl) {
timeControl = setInterval(function () {
waitForKeyElements(selectorTxt,
actionFunction,
bWaitOnce,
iframeSelector
);
},
500
);
controlObj[controlKey] = timeControl;
}
}
waitForKeyElements.controlObj = controlObj;
}
// Main entry point
waitForKeyElements("input[name='data']", createGenerateButton);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment