Created
April 12, 2024 23:24
-
-
Save tlhunter/a702cf859b34df60aaf99c9e7158b6eb to your computer and use it in GitHub Desktop.
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
#!/usr/bin/env node | |
// this script looks through chase credit card transactions looking for repeat description/amount combinations | |
// login to Chase, click credit card, set a time range, click download account activity (down arrow), choose CSV | |
// usage: node ./chase-subscription-finder.js Chase*Activity*.CSV | |
const fs = require('fs'); | |
const BLACKLIST = [ | |
'PEETZ', // coffee | |
'PHILZ', // coffee | |
'CLIPPER', // public trans | |
]; | |
const filename = process.argv[2]; | |
const lines = fs.readFileSync(filename).toString().trim().split('\n'); | |
lines.shift(); // discard header row | |
const cases = new Map(); | |
iterator: for (let line of lines) { | |
let [date, _post, vendor, _cat, _type, amount, _memo] = line.split(','); | |
let [month, day, year] = date.split('/'); | |
amount = Number(amount); | |
const cents = Math.round(amount * 100) * -1; | |
if (cents < 0) continue; // ignore credit card payments and returns | |
vendor = vendor.replaceAll('&', ' ').toUpperCase(); | |
date = `${year}-${month}-${day}`; | |
const normalized = vendor.replaceAll(/[^A-Z]+/g, '_'); // replace non letters with underscores | |
let skip = false; | |
for (const blacklist of BLACKLIST) { // inefficiency is OK | |
if (vendor.includes(blacklist)) { | |
skip = true; | |
break; | |
} | |
} | |
console.log(`${skip ? '\x1b[2m' : '\x1b[1m'}${date} ${normalized.padStart(30, ' ')} ${String(cents).padStart(8)}¢\x1b[0m`); | |
if (skip) continue; | |
const name = normalized + ':' + cents; | |
const c = cases.get(name); | |
if (!c) { | |
const obj = { | |
dates: [date], | |
vendor, | |
amount | |
} | |
cases.set(name, obj); | |
} else { | |
c.dates.push(date); | |
} | |
} | |
console.log(); | |
console.log('DISCOVERED THE FOLLOWING RECURRING PAYMENTS'); | |
console.log(); | |
let sum = 0; | |
for (const c of cases.values()) { | |
if (c.dates.length > 1) { | |
const amount = '$' + (c.amount * -1).toFixed(2); | |
console.log(`${c.vendor.padEnd(30, ' ')} ${String(amount).padStart(8)} ${c.dates.join(', ')}`); | |
sum += c.amount * -1; | |
} | |
} | |
console.log(); | |
console.log(`TOTAL MONTHLY: \$${sum.toFixed(2)}`); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
output: