A booklet cannot load bank-to-ynab.js
dynamically due to https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP.
Instead, booklet-ify bank-to-ynab.js
with https://chriszarate.github.io/bookmarkleter/.
A booklet cannot load bank-to-ynab.js
dynamically due to https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP.
Instead, booklet-ify bank-to-ynab.js
with https://chriszarate.github.io/bookmarkleter/.
var months = { | |
"jan.": "01", | |
"feb.": "02", | |
"mar.": "03", | |
"apr.": "04", | |
"maj": "05", | |
"juni": "06", | |
"juli": "07", | |
"aug.": "08", | |
"sept.": "09", | |
"okt.": "10", | |
"nov.": "11", | |
"dec.": "12" | |
}; | |
var save = (transactions) => { | |
const filename = "ynab.csv"; | |
const blob = new Blob([transactions], {type: 'text/csv'}); | |
if (window.navigator.msSaveOrOpenBlob) { | |
window.navigator.msSaveBlob(blob, filename); | |
} else { | |
const elem = document.createElement('a'); | |
elem.href = window.URL.createObjectURL(blob); | |
elem.download = filename; | |
document.body.appendChild(elem); | |
elem.click(); | |
document.body.removeChild(elem); | |
window.URL.revokeObjectURL(elem); | |
} | |
} | |
var stot = (e, selector) => e.querySelector(selector).innerText; | |
var perItem = (prev, cur) => { | |
// YNAB wants date format dd/mm/yyyy. | |
// Date field format is "dd. mmm. yyyy" ... | |
var date = stot(cur, '.transaction-field--date'); | |
// ... unless it is "I dag" or "I går", then construct the value dynamically. | |
if (date.startsWith("I ")) { | |
const d = new Date(Date.now()); | |
if (date === "I går") { | |
d.setDate(d.getDate() - 1); | |
} | |
const opts = { year: "numeric", month: "numeric", day: "numeric" }; | |
// dd/mm/yyyy | |
date = d.toLocaleDateString('en-DK', opts); | |
} else { | |
for (const k in months) { | |
date = date.replace(`. ${k} `, `-${months[k]}-`) | |
} | |
date = date.replace(/-/g, '/'); | |
} | |
const statement = stot(cur, '.transaction-field--statementText'); | |
const memoRegex = /\s+((?:Nota (?:nr. )?[A-Z0-9]+)|(?:Aftalenr. \d+))/; | |
var components = statement.split(memoRegex) | |
const payee = '"' + components[0] + '"'; | |
const memo = components.length > 1 ? '"' + components[1] + '"' : ''; | |
// Naively US-ify transaction amount. | |
const value = stot(cur, '.transaction-field--amount').replace('.','').replace(',', '.'); | |
const isOutflow = value.startsWith('-'); | |
const inflow = isOutflow ? '' : value; | |
const outflow = isOutflow ? value.substr(1) : ''; | |
return prev + date + ',' + payee + ',,' + memo + ',' + outflow + ',' + inflow + '\n'; | |
} | |
var csv = Array.prototype.reduce.call(document.querySelectorAll('.transaction-item.transaction-item--hasClick'), perItem, 'Date,Payee,Category,Memo,Outflow,Inflow\n'); | |
save(csv); |
#!/usr/bin/env python3 | |
import csv | |
import codecs | |
import sys | |
import re | |
regex = re.compile( | |
r""" | |
\s+( | |
(?:Nota (?:nr. )?[a-zA-Z0-9]+) | |
| | |
(?:Aftalenr. \d+) | |
| | |
(?:\)*\s*\d+) | |
) | |
""", | |
re.X) | |
def payee_and_memo_of(statement): | |
comps = regex.split(statement) | |
memo = "" | |
payee = comps[0] | |
if len(comps) > 1: | |
memo = comps[1] | |
memo = memo.replace("))))", "") | |
payee = payee.replace("Nettoløn", "Danske Bank") | |
payee = payee.replace("EDWIN RAHRS VEJ", "Danske Bank PER") | |
payee = payee.replace("MobilePay:", "") | |
payee = payee.replace("MobilePay køb", "") | |
payee = payee.replace("MobilePay", "") | |
payee = payee.replace("kontaktløs Dankort", "") | |
payee = payee.replace("Visa/Dankort", "") | |
payee = payee.replace("Dankort-køb", "") | |
payee = payee.replace("Dankort", "") | |
payee = payee.replace("Betalingsservice", "") | |
if "gebyr" in payee.lower(): | |
memo = f"{memo} {payee}" | |
payee = "Danske Bank" | |
payee = payee.strip() | |
memo = memo.strip() | |
return (payee, memo) | |
if len(sys.argv) < 2: | |
print("usage: %s <csv>" % __file__, file=sys.stderr) | |
exit(1) | |
ynab_header = ["Date", "Payee", "Category", "Memo", "Outflow", "Inflow"] | |
def read(f): | |
return codecs.open(f, encoding="latin1") | |
with read(sys.argv[1]) as f: | |
reader = csv.reader(f, delimiter=";") | |
writer = csv.writer(sys.stdout, delimiter=",") | |
writer.writerow(ynab_header) | |
# payee startswith "MobilePay:" | |
for row in reader: | |
date = payee = category = memo = inflow = outflow = "" | |
date = row[0] | |
# Skip header if present | |
if date == "Dato": | |
continue | |
date = date.replace(".", "/") | |
flow = row[2] | |
flow = flow.replace(".", "") | |
flow = flow.replace(",", ".") | |
payee, memo = payee_and_memo_of(row[1]) | |
if flow.startswith("-"): | |
outflow = flow[1:] | |
else: | |
inflow = flow | |
writer.writerow([date, payee, category, memo, outflow, inflow]) |
"Dato";"Tekst";"Belřb";"Saldo";"Status";"Afstemt" | |
"08.06.2023";"VISA/DANKORT gebyr";"-269,00";"29.873,31";"Udfřrt";"Nej" | |
"19.06.2023";"Fřtex City Vest 28255";"-374,20";"29.499,11";"Udfřrt";"Nej" | |
"20.06.2023";"Fřtex City Vest )))) 02573";"-220,85";"29.278,26";"Udfřrt";"Nej" | |
"23.06.2023";"Fřtex City Vest )))) 80928";"-121,10";"29.157,16";"Udfřrt";"Nej" | |
"23.06.2023";"Fřtex City Vest )))) 04871";"-153,80";"29.003,36";"Udfřrt";"Nej" | |
"26.06.2023";"Fřtex City Vest )))) 93578";"-131,15";"28.872,21";"Udfřrt";"Nej" | |
"27.06.2023";"Nettolřn";"35.246,35";"64.118,56";"Udfřrt";"Nej" | |
"28.06.2023";"EDWIN RAHRS VEJ";"-20,00";"64.098,56";"Udfřrt";"Nej" | |
"28.06.2023";"Report ID 157297";"153,80";"64.252,36";"Udfřrt";"Nej" | |
"29.06.2023";"Fřtex City Vest )))) 50350";"-333,50";"63.918,86";"Udfřrt";"Nej" | |
"30.06.2023";"Mikkel budget";"-7.000,00";"56.918,86";"Udfřrt";"Nej" | |
"30.06.2023";"Mĺnedsopsparing";"-3.000,00";"53.918,86";"Udfřrt";"Nej" | |
"30.06.2023";"Gebyrer i alt";"-5,00";"53.913,86";"Udfřrt";"Nej" |
Date | Payee | Category | Memo | Outflow | Inflow | |
---|---|---|---|---|---|---|
04/01/2022 | out 19 | 590.00 | ||||
04/01/2022 | out 18 | 138.90 | ||||
03/01/2022 | out 17 | 1494.00 | ||||
03/01/2022 | out 16 | 1420.00 | ||||
03/01/2022 | out 15 | 494.23 | ||||
03/01/2022 | out 14 | 250.00 | ||||
03/01/2022 | out 13 | Nota c6d1df766c2 | 228.00 | |||
03/01/2022 | out 12 | 110.95 | ||||
03/01/2022 | out 10 | 79.00 | ||||
03/01/2022 | out 9 | 330.75 | ||||
03/01/2022 | out 8 | 6.26 | ||||
03/01/2022 | out 7 | 166.00 | ||||
31/12/2021 | out 6 | 653.24 | ||||
30/12/2021 | out 5 | 510.00 | ||||
30/12/2021 | out 4 | Nota nr. 64393 | 6000.00 | |||
30/12/2021 | in 1 | 26418.35 | ||||
23/12/2021 | out 3/ | Nota Z24000617998 | 703.00 | |||
23/12/2021 | out 2 | Aftalenr. 901711476 | 132.00 | |||
22/12/2021 | out 1 æøåÆØÅ | 113.20 |
#!/usr/bin/env python3 | |
import csv | |
import codecs | |
import sys | |
import re | |
regex = re.compile("\\s+((?:Nota (?:nr. )?[a-zA-Z0-9]+)|(?:Aftalenr. \\d+))") | |
def payee_and_memo_of(statement): | |
comps = regex.split(statement) | |
memo = "" | |
payee = comps[0] | |
if len(comps) > 1: | |
memo = comps[1] | |
payee = payee.replace("MobilePay:", "") | |
payee = payee.replace("MobilePay køb", "") | |
payee = payee.replace("MobilePay", "") | |
payee = payee.replace("kontaktløs Dankort", "") | |
payee = payee.replace("Visa/Dankort", "") | |
payee = payee.replace("Dankort-køb", "") | |
payee = payee.replace("Dankort", "") | |
payee = payee.replace("Betalingsservice", "") | |
payee = payee.strip() | |
return (payee, memo) | |
if len(sys.argv) < 2: | |
print("usage: %s <csv>" % __file__, file=sys.stderr) | |
exit(1) | |
ynab_header = ["Date", "Payee", "Category", "Memo", "Outflow", "Inflow"] | |
def read(f): | |
return codecs.open(f, encoding="utf-8-sig") | |
with read(sys.argv[1]) as f: | |
reader = csv.reader(f, delimiter=";") | |
writer = csv.writer(sys.stdout, delimiter=",") | |
writer.writerow(ynab_header) | |
# payee startswith "MobilePay:" | |
for row in reader: | |
date = payee = category = memo = inflow = outflow = "" | |
date = row[0] | |
date = date.replace("-", "/") | |
flow = row[2] | |
flow = flow.replace(".", "") | |
flow = flow.replace(",", ".") | |
payee, memo = payee_and_memo_of(row[1]) | |
if flow.startswith("-"): | |
outflow = flow[1:] | |
else: | |
inflow = flow | |
writer.writerow([date, payee, category, memo, outflow, inflow]) |
04-01-2022;out 19;-590 | 00;477.986 | 44;DKK | |
---|---|---|---|
04-01-2022;out 18;-138 | 90;478.576 | 44;DKK | |
03-01-2022;out 17;-1.494 | 00;478.715 | 34;DKK | |
03-01-2022;out 16;-1.420 | 00;480.209 | 34;DKK | |
03-01-2022;out 15;-494 | 23;481.629 | 34;DKK | |
03-01-2022;out 14;-250 | 00;482.123 | 57;DKK | |
03-01-2022;MobilePay out 13 Nota c6d1df766c2;-228 | 00;482.373 | 57;DKK | |
03-01-2022;Betalingsservice out 12;-110 | 95;482.601 | 57;DKK | |
03-01-2022;Dankort out 10;-79 | 00;482.712 | 52;DKK | |
03-01-2022;Dankort-køb out 9;-330 | 75;482.791 | 52;DKK | |
03-01-2022;Visa/Dankort out 8;-6 | 26;483.122 | 27;DKK | |
03-01-2022;kontaktløs Dankort out 7;-166 | 00;483.128 | 53;DKK | |
31-12-2021;MobilePay køb out 6;-653 | 24;483.294 | 53;DKK | |
30-12-2021;MobilePay: out 5;-510 | 00;483.947 | 77;DKK | |
30-12-2021;out 4 Nota nr. 64393;-6.000 | 00;484.457 | 77;DKK | |
30-12-2021;in 1;26.418 | 35;490.457 | 77;DKK | |
23-12-2021;out 3/ Nota Z24000617998;-703 | 00;464.039 | 42;DKK | |
23-12-2021;out 2 Aftalenr. 901711476;-132 | 00;464.742 | 42;DKK | |
22-12-2021;out 1 æøåÆØÅ;-113 | 20;464.874 | 42;DKK |
Date | Payee | Category | Memo | Outflow | Inflow | |
---|---|---|---|---|---|---|
25/07/2010 | Sample Payee | Sample Category | Sample Memo for an outflow | 100.00 | ||
26/07/2010 | Sample Payee 2 | Sample Category | Sample memo for an inflow | 500.00 |