Skip to content

Commit 0b6ab8e

Browse files
committed
Add a basic rough script for converting Revolut extracts to Holded CSV
1 parent 4217612 commit 0b6ab8e

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import * as fs from 'fs';
2+
import csv from 'csv-parser';
3+
import moment from 'moment';
4+
import { parse } from 'path';
5+
import { createObjectCsvWriter as createCsvWriter } from 'csv-writer';
6+
7+
interface RawTransaction {
8+
date: string;
9+
description: string;
10+
amount: number;
11+
balance: number;
12+
}
13+
14+
interface ProcessedTransaction {
15+
date: string;
16+
description: string;
17+
amount: string;
18+
balance: string;
19+
}
20+
21+
let rawTransactions: RawTransaction[] = [];
22+
let balance = 0;
23+
24+
// Get the filename from the command line arguments
25+
const filename = process.argv[2];
26+
if (!filename) {
27+
console.error('Please provide a filename as a command line argument.');
28+
process.exit(1);
29+
}
30+
31+
fs.createReadStream(filename)
32+
.pipe(csv())
33+
.on('data', (row) => {
34+
let amount = parseFloat(row['Value'].replace('€', ''));
35+
balance += amount;
36+
37+
rawTransactions.push({
38+
date: moment(row['Date (UTC)'], 'MMM D, YYYY').format('DD/MM/YYYY'),
39+
description: row['Description'],
40+
amount: amount,
41+
balance: balance
42+
});
43+
})
44+
.on('end', () => {
45+
let combinedTransactions: ProcessedTransaction[] = [];
46+
47+
let combinedAmount = 0;
48+
let previousTransaction: RawTransaction | undefined;
49+
50+
for (let transaction of rawTransactions) {
51+
console.log('Processing transaction:', transaction);
52+
let { description, amount, balance, date } = transaction;
53+
54+
// Don't record interest/fee transactions until the last one:
55+
if (description.startsWith('Interest PAID') || description.startsWith('Service Fee Charged')) {
56+
console.log('Keeping transaction pending...');
57+
combinedAmount += amount;
58+
previousTransaction = transaction;
59+
continue;
60+
}
61+
62+
if (combinedAmount) {
63+
// We've reached the last interest/fee transaction, so record the combined transaction:
64+
combinedTransactions.push({
65+
date: previousTransaction!.date,
66+
description: 'Interest after fees',
67+
amount: combinedAmount.toFixed(2),
68+
balance: previousTransaction!.balance.toFixed(2)
69+
});
70+
71+
combinedAmount = 0;
72+
}
73+
74+
// Record the current (non-interest/fee) transaction:
75+
combinedTransactions.push({
76+
date: date,
77+
description: description,
78+
amount: amount.toFixed(2),
79+
balance: balance.toFixed(2)
80+
});
81+
}
82+
83+
if (combinedAmount) {
84+
// We've reached the last interest/fee transaction, so record the combined transaction:
85+
combinedTransactions.push({
86+
date: previousTransaction!.date,
87+
description: 'Interest after fees',
88+
amount: combinedAmount.toFixed(2),
89+
balance: previousTransaction!.balance.toFixed(2)
90+
});
91+
}
92+
93+
// Sort from most recent to oldest
94+
combinedTransactions
95+
.reverse() // Ensure same-date order is flipped
96+
.sort((a, b) => moment(b.date, 'DD/MM/YYYY').valueOf() - moment(a.date, 'DD/MM/YYYY').valueOf());
97+
98+
// Write the output to a file
99+
const { dir, name } = parse(filename);
100+
const outputFilename = `${dir}/${name}-processed.csv`;
101+
const csvWriter = createCsvWriter({
102+
path: outputFilename,
103+
header: [
104+
{id: 'date', title: 'DATE'},
105+
{id: 'description', title: 'DESCRIPTION'},
106+
{id: 'amount', title: 'AMOUNT'},
107+
{id: 'balance', title: 'BALANCE'},
108+
]
109+
});
110+
csvWriter.writeRecords(combinedTransactions);
111+
});

scripts/package-lock.json

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
"license": "AGPL-3.0-or-later",
77
"dependencies": {
88
"@types/prompts": "^2.4.9",
9+
"csv-parser": "^3.0.0",
10+
"csv-writer": "^1.6.0",
911
"moment": "^2.29.4",
1012
"prompts": "^2.4.2",
1113
"ts-node": "^10.9.1"

0 commit comments

Comments
 (0)