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).map(s => s.replace(/[\n\r]/, ' ')); | |
| const payee = '"' + components[0] + '"'; | |
| const memo = components.length > 1 ? '"' + components[1] + '"' : ''; | |
| var category = ''; | |
| if (/gjensidige/i.test(payee)) { | |
| if (/013778004/.test(memo)) { | |
| category = '"Rainy Day Funds: Insurance, ulykke"'; | |
| } | |
| if (/013778005/.test(memo)) { | |
| category = '"Rainy Day Funds: Insurance, hus"'; | |
| } | |
| if (/013778008/.test(memo)) { | |
| category = '"Rainy Day Funds: Insurance, rejse"'; | |
| } | |
| } | |
| if (/kredsl.b/i.test(payee)) { | |
| if (/024540837/.test(memo)) { | |
| category = '"Monthly Bills: Heating"'; | |
| } | |
| if (/021739653/.test(memo)) { | |
| category = '"Monthly Bills: Affald"'; | |
| } | |
| } | |
| // 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 + ',' + category + ',' + 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* | |
| (?: | |
| (?:[)]{4}) | |
| | | |
| (?:[ ]{3}) | |
| | | |
| (?:\\\s\\) | |
| )\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", "") | |
| payee = payee.replace("PAYPAL *", "") | |
| if "gebyr" in payee.lower(): | |
| memo = f"{memo} {payee}" | |
| payee = "Danske Bank" | |
| if "udl. kortbetaling" 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" | |
| "01.09.2025";"REMA 1000 Stavtrup )))) 06875";"-186,90";"50.175,61";"Udført";"Nej" | |
| "16.09.2025";"Udl. kortbetaling, 1.50% af...";"-1,56";"45.739,36";"Udført";"Nej" | |
| "26.09.2025";"PAYPAL *STEAM GAMES\ \35314...";"-45,92";"82.722,25";"Udført";"Nej" | |
| "16.09.2025";"PAYPAL *WIKIPEDIA\ \4029357...";"-104,00";"45.740,92";"Udført";"Nej" | |
| "05.09.2025";"NETFLIX.COM\ \80251252\";"-169,00";"47.583,01";"Udført";"Nej" | |
| "09.09.2025";"MobilePay R 919 - DB Aarhus";"-32,00";"46.892,77";"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 | |
| 23-12-2021;KREDSLøB A/S Aftalenr. 024540837;-3892,00;464.742,42;DKK | |
| 23-12-2021;GJENSIDIGE FORSIKRING, DANSK FI Aftalenr. 013778004;-4954,87;464.742,42;DKK | |
| 23-12-2021;GJENSIDIGE FORSIKRING, DANSK FI Aftalenr. 013778005;-3554,92;464.742,42;DKK | |
| 23-12-2021;GJENSIDIGE FORSIKRING, DANSK FI Aftalenr. 013778008;-1093,75;464.742,42;DKK | |
| 23-12-2021;KREDSLøB GENBRUG A/S Aftalenr. 021739653;-1931,49;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 |