1- #!/usr/local/bin/python3.11.9
1+ #!/usr/local/bin/python3.11.14
22
33# ------------ import functions ---------------------------------
44import csv
88import pdfplumber
99from jproperties import Properties
1010import shutil
11- # import re
11+ from colorama import Fore
12+ from openpyxl import load_workbook
1213
1314# ------------ define global variables ---------------------------
1415fields = [] # create list with original headers
1920visa_file = ""
2021year_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
2425with 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 --------------------
426463def 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 ---------------------------
470511print ("" )
471512print ("NL-Bankstatement-converter for GnuCash - written by JensTec" )
513+ print ("(c) 2026 JensTec" )
472514menu ()
0 commit comments