Skip to content

Commit 0745729

Browse files
author
Jens Tec
committed
Significant update, which now also supports xlsx files from BBVA in Spain. Also added a VISA account number variable to recognize new filename format ICS / ANWB.
Signed-off-by: Jens Tec <1vai6hkn@duck.com>
1 parent 9aba957 commit 0745729

2 files changed

Lines changed: 101 additions & 56 deletions

File tree

SC_user_data.properties

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22
FILE_PATH=/Users/John/Downloads
33

44
# Auto delete downloaded bank file (.csv or .pdf). When set to 'NO', file will be archived in /Archive
5-
AUTO_DELETE=YES
5+
AUTO_DELETE=NO
66

77
# Account information
88
VISA_CARD1=J SMID
99
VISA_CARD2=A.B. BONTEKOE
1010
VISA_CARD1_INIT=JS
1111
VISA_CARD2_INIT=AB
12+
VISA_ACCOUNT_NR=12345678962252
13+
1214
# for Bunq account -> put four digits of your IBAN number to identify different accounts
1315
BUNQ_ACCOUNT1=1234
1416
BUNQ_ACCOUNT2=5678
1517
BUNQ_ACCOUNT3=9012
16-
BUNQ_ACCOUNT4=5874
18+
BUNQ_ACCOUNT4=5874
19+
BUNQ_ACCOUNT5=3498

Statement_Converter.py

Lines changed: 96 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/local/bin/python3.11.9
1+
#!/usr/local/bin/python3.11.14
22

33
# ------------ import functions ---------------------------------
44
import csv
@@ -8,7 +8,8 @@
88
import pdfplumber
99
from jproperties import Properties
1010
import shutil
11-
# import re
11+
from colorama import Fore
12+
from openpyxl import load_workbook
1213

1314
# ------------ define global variables ---------------------------
1415
fields = [] # create list with original headers
@@ -19,18 +20,20 @@
1920
visa_file = ""
2021
year_month = str(datetime.now().year) + "-" + str(datetime.now().month)
2122

22-
# reading the properties from the properties file
23-
configs = Properties() # instantiate the Properties object
23+
# read properties from properties file
24+
configs = Properties() # instantiate the Properties object
2425
with open('SC_user_data.properties', 'rb') as config_file: # load the properties file into the Properties object
2526
configs.load(config_file)
2627

27-
path = configs.get("FILE_PATH").data # path where the csv and pdf files are saved
28-
archivepath = str(path + "/archive") # creates name and directory for archiving files
29-
bunq_acc = [ # creating list with unique identifiers to distinguish between bunq accounts
28+
visa_account_nr = configs.get("VISA_ACCOUNT_NR").data # visa account number to identify filename
29+
path = configs.get("FILE_PATH").data # path where the csv and pdf files are saved
30+
archivepath = str(path + "/archive") # create name and directory for archiving files
31+
bunq_acc = [ # create list with unique identifiers to distinguish between bunq accounts
3032
configs.get("BUNQ_ACCOUNT1").data,
3133
configs.get("BUNQ_ACCOUNT2").data,
3234
configs.get("BUNQ_ACCOUNT3").data,
33-
configs.get("BUNQ_ACCOUNT4").data
35+
configs.get("BUNQ_ACCOUNT4").data,
36+
configs.get("BUNQ_ACCOUNT5").data
3437
]
3538

3639

@@ -42,57 +45,59 @@ def menu():
4245
global csvfile
4346
global filename
4447
global bunq_acc
45-
files = [] # create list with all the csv file names
48+
files = [] # create list with all the csv file names
4649
filenumber = 1
4750

48-
print("")
49-
print("Available .csv & .pdf files in %s: " % path)
51+
print(Fore.BLUE + "***********************************************************************************************")
52+
print("** " + Fore.YELLOW + "Available .csv, .xlsx & .pdf files in %s: " % path)
53+
print(Fore.BLUE + "***********************************************************************************************")
5054
for x in os.listdir(path): # show available csv and pdf files in path, and ability to select the correct one
51-
if x.endswith(".csv") or x.endswith(".pdf"):
55+
if x.endswith(".csv") or x.endswith(".pdf") or x.endswith(".xlsx"):
5256
files.append(x)
5357
files.sort()
5458

5559
for f in files:
56-
print(str(filenumber) + " - " + files[filenumber - 1])
60+
print(Fore.BLUE + "** " + Fore.YELLOW + str(filenumber) +") " + Fore.RESET + files[filenumber - 1])
5761
filenumber += 1
58-
exit_number = filenumber
59-
print(str(exit_number) + " - Exit")
60-
print('\n')
61-
62-
file_input = int(input("Choose the file to convert : ")) - 1
62+
print(Fore.BLUE + "***********************************************************************************************")
63+
print(Fore.GREEN + "Select a file to convert or")
64+
file_input = input(Fore.RED + "Q " + Fore.RESET + "to quit: ")
6365

64-
if (file_input + 1) == exit_number: # create option to exit the software gracefully
66+
if file_input == ("q" or "Q"): # create option to exit the software
6567
print("")
6668
raise SystemExit
6769
else:
68-
filename = str(path + "/" + files[file_input]) # creating the variable which will be used in reading the file
70+
filename = str(path + "/" + files[int(file_input) -1]) # create variable which will be used in read file
6971

70-
if year_month in filename: # before November 2025 ICS ANWB used the word 'Overzicht' and 'download' but this changed in a recent update.
72+
if visa_account_nr in filename: # use account number which is in standard filename
7173
visa()
74+
elif 'Latest transactions' in filename:
75+
bbva()
7276
elif 'Rekeningtransacties' in filename:
7377
ob()
7478
elif 'Brubank' in filename:
7579
brubank()
76-
else: # reading csv selected file
77-
with open(filename, 'r', errors='ignore') as csvfile: # errors='ignore' due to some characters in Rabobank
78-
csvreader = csv.reader(csvfile) # creating a csv reader object
79-
if 'RABO' in filename:
80-
fields = next(csvreader) # extracting field names through first row
81-
for row in csvreader: # extracting each data row one by one
82-
rows.append(row)
83-
rabo()
84-
elif 'ASN' in filename:
85-
for row in csvreader: # extracting each data row one by one
80+
else: # read csv selected file
81+
with open(filename, 'r', errors='ignore') as csvfile: # errors='ignore' due to some characters in Rabobank
82+
csvreader = csv.reader(csvfile) # create a csv reader object
83+
84+
if 'ASN' in filename:
85+
for row in csvreader: # extract each data row one by one
8686
rows.append(row)
8787
asn()
88-
elif bunq_acc[0] or bunq_acc[1] or bunq_acc[2] or bunq_acc[3] in filename: #
89-
fields = next(csvreader) # extracting field names through first row
90-
for row in csvreader: # extracting each data row one by one
88+
elif 'RABO' in filename:
89+
next(csvreader, None) # Returns None if the file is empty
90+
for row in csvreader:
91+
rows.append(row)
92+
rabo()
93+
elif any(sub in filename for sub in bunq_acc):
94+
next(csvreader, None)
95+
for row in csvreader:
9196
rows.append(row)
9297
bunq()
9398
elif 'INGB' in filename:
94-
fields = next(csvreader) # extracting field names through first row
95-
for row in csvreader: # extracting each data row one by one
99+
next(csvreader, None)
100+
for row in csvreader:
96101
rows.append(row)
97102
ing()
98103
else:
@@ -210,13 +215,15 @@ def bunq():
210215
global bunq_acc
211216

212217
if bunq_acc[0] in rows[0][3]:
213-
bank = "Bunq Joint Account" # you can insert any name here
218+
bank = "Bunq Joint" # you can insert any name here
214219
elif bunq_acc[1] in rows[0][3]:
215-
bank = "Bunq ES Deduction Account"
220+
bank = "Bunq ES"
216221
elif bunq_acc[2] in rows[0][3]:
217-
bank = "Bunq Savings Account"
222+
bank = "Bunq Savings"
218223
elif bunq_acc[3] in rows[0][3]:
219-
bank = "Bunq Bizum Account"
224+
bank = "Bunq Bizum"
225+
elif bunq_acc[4] in rows[0][3]:
226+
bank = "Bunq Supermarket"
220227
else:
221228
bank = "Bunq unknown"
222229

@@ -299,6 +306,7 @@ def visa():
299306
global rows
300307
global filename
301308
global visa_file
309+
global visa_account_nr
302310
bank = "ANWB Visa Card"
303311
card1 = configs.get("VISA_CARD1").data
304312
card2 = configs.get("VISA_CARD2").data
@@ -421,6 +429,35 @@ def visa():
421429
rows.append(rowsplit)
422430
filecreation()
423431

432+
# ------------- convert data into the required output for BBVA xlsx transactions --------
433+
def bbva():
434+
global rows
435+
global bank
436+
global filename
437+
bank = "BBVA" # set variable to BBVA
438+
439+
wb = load_workbook(filename) # Read xlsx file
440+
441+
for row in wb.active.iter_rows(min_row=6, values_only=True): # Iterate over the remaining rows
442+
rows.append(list(row))
443+
444+
wb.close() # Close the workbook
445+
446+
for row in rows:
447+
amount = float(row[5])
448+
if amount < 0: # check if it should be in the column withdrawal or deposit
449+
row.insert(0, -1 * amount) # multiply by -1 to have the amount with 'minus' sign
450+
row.insert(1, "")
451+
else:
452+
row.insert(0, "")
453+
row.insert(1, amount)
454+
row.insert(2, str(row[5] + " - " + row[6] + " - " + row[11])) # insert description
455+
row.insert(3, "")
456+
del row[4:6] # delete column not needed
457+
del row[5:13] # delete columns not needed
458+
459+
filecreation()
460+
424461

425462
# --------------- create unique name, write the file and show results --------------------
426463
def filecreation():
@@ -439,34 +476,39 @@ def filecreation():
439476
fields_output = ["Withdrawal", "Deposit", "Description", "Number", "Date"]
440477

441478
with open(filename_write, 'w') as csvfile:
442-
csvwriter = csv.writer(csvfile) # creating a csv writer object
443-
csvwriter.writerow(fields_output) # writing the fields
444-
csvwriter.writerows(rows) # writing the data rows
479+
csvwriter = csv.writer(csvfile) # create csv writer object
480+
csvwriter.writerow(fields_output) # write fields
481+
csvwriter.writerows(rows) # write data rows
445482

446-
print("You have converted the file: '" + filename[len(path) + 1:] + "' from %s." % bank)
447483
print('\n')
448-
print(tabulate(rows, headers=fields_output)) # printing the table on screen with the data
484+
print(Fore.BLUE + "***********************************************************************************************")
485+
print("** " + Fore.YELLOW + "Converted the file: '" + Fore.RESET + filename[len(path) + 1:] + Fore.YELLOW + "' from %s." % bank + Fore.RESET)
486+
print('\n')
487+
print(tabulate(rows, headers=fields_output)) # print table on screen with data
488+
print()
489+
print(Fore.BLUE + "***********************************************************************************************")
449490

450491
if auto_delete == "YES":
451492
os.remove(filename)
452-
print("")
493+
print(Fore.GREEN + "")
453494
print("File %s has been removed successfully." % filename)
454-
else: # if auto_delete is set to "NO", the input file will be archived in a separate folder
455-
if not os.path.exists(archivepath): # check if the archivepath folder already exists
495+
else: # if auto_delete is set to "NO", the input file will be archived in a separate folder
496+
if not os.path.exists(archivepath): # check if the archivepath folder already exists
456497
os.mkdir(archivepath)
457498
print('\n')
458-
print("Folder '%s' has been created." % archivepath)
459-
shutil.move(filename, archivepath) # move inputfile to 'archivepath'
460-
print("The input file has been moved to %s." % archivepath)
461-
print("----------------------------------------------------------------------")
499+
print(Fore.GREEN + "Folder '%s' has been created." % archivepath)
500+
shutil.move(filename, archivepath) # move inputfile to 'archivepath'
501+
print(Fore.GREEN + "The input file has been moved to %s." % archivepath + Fore.RESET)
502+
print()
462503

463504
# reset values and return to menu()
464-
rows = [] # reset rows list
465-
visa_file = "" # reset visa file addition
505+
rows = [] # reset rows list
506+
visa_file = "" # reset visa file addition
466507
menu()
467508

468509

469510
# ------------ start menu ---------------------------
470511
print("")
471512
print("NL-Bankstatement-converter for GnuCash - written by JensTec")
513+
print("(c) 2026 JensTec")
472514
menu()

0 commit comments

Comments
 (0)