diff --git a/ofx_processor/processors/bpvf.py b/ofx_processor/processors/bpvf.py
index 1372e3e..48867b9 100644
--- a/ofx_processor/processors/bpvf.py
+++ b/ofx_processor/processors/bpvf.py
@@ -3,6 +3,7 @@ import sys
import click
from ofxtools import OFXTree
+from ofxtools.header import OFXHeaderError
from ofx_processor.utils.processor import Processor, Line
@@ -48,14 +49,14 @@ class BpvfProcessor(Processor):
parser = OFXTree()
try:
parser.parse(self.filename)
- except FileNotFoundError:
- click.secho("Couldn't open ofx file", fg="red")
+ except (FileNotFoundError, OFXHeaderError):
+ click.secho("Couldn't open or parse ofx file", fg="red")
sys.exit(1)
ofx = parser.convert()
if ofx is None:
- click.secho("Couldn't parse ofx file", fg="red")
+ click.secho("Couldn't convert ofx file", fg="red")
sys.exit(1)
return ofx.statements[0].transactions
diff --git a/ofx_processor/processors/revolut.py b/ofx_processor/processors/revolut.py
index bed341f..505d3f4 100644
--- a/ofx_processor/processors/revolut.py
+++ b/ofx_processor/processors/revolut.py
@@ -1,4 +1,5 @@
import csv
+import sys
import click
import dateparser
@@ -52,9 +53,13 @@ class RevolutProcessor(Processor):
account_name = "revolut"
def parse_file(self):
- with open(self.filename) as f:
- reader = csv.DictReader(f, delimiter=";")
- return [line for line in reader]
+ try:
+ with open(self.filename) as f:
+ reader = csv.DictReader(f, delimiter=";")
+ return [line for line in reader]
+ except FileNotFoundError:
+ click.secho("File not found", fg="red")
+ sys.exit(1)
@staticmethod
@click.command("revolut", help="Process Revolut bank statement (CSV)")
diff --git a/ofx_processor/utils/processor.py b/ofx_processor/utils/processor.py
index dd0caf7..c54f7f4 100644
--- a/ofx_processor/utils/processor.py
+++ b/ofx_processor/utils/processor.py
@@ -52,7 +52,7 @@ class Processor:
self.iterable = self.parse_file()
def parse_file(self):
- return []
+ return [] # pragma: nocover
def push_to_ynab(self):
transactions = self.get_transactions()
diff --git a/tests/samples/bpvf.ofx b/tests/samples/bpvf.ofx
new file mode 100644
index 0000000..76ae3e7
--- /dev/null
+++ b/tests/samples/bpvf.ofx
@@ -0,0 +1,206 @@
+OFXHEADER:100
+DATA:OFXSGML
+VERSION:102
+SECURITY:NONE
+ENCODING:USASCII
+CHARSET:1252
+COMPRESSION:NONE
+OLDFILEUID:NONE
+NEWFILEUID:NONE
+
+
+
+
+0
+INFO
+
+20200226060348
+FRA
+20200226060348
+20200226060348
+
+
+
+
+12345678912
+
+0
+INFO
+
+
+EUR
+
+12345
+65432
+12345678912
+CHECKING
+
+
+20200217
+20200226
+
+DEBIT
+20200226
+-9.66
+25364 HC4X
+0719ATG
+PRLV SEPA Company 3
+123456789 PAYPAL 542UHBON
+
+
+DEBIT
+20200225
+-2.40
+727278312
+2L9Z4OP
+240220 CB****5555
+H.I.K 69VILLEURBANNE
+
+
+DEBIT
+20200225
+-39.20
+7834583486
+2LFVF8U
+230220 CB****5555
+DELIVEROO FR WWW 39,20EUR 1 EURO = 1,000000
+
+
+DEBIT
+20200225
+-9.99
+883783453
+0711ZB6
+PRLV SEPA Company 1
+Votre abonnement mobile: 06XXXXX 6498165189060897
+
+
+DEBIT
+20200224
+-7.50
+651876109
+2JSCN09
+210220 CB****5555
+COMPANY FR LYON 6EME 7,50EUR 1 EURO = 1,000000
+
+
+DEBIT
+20200224
+-34.99
+3483483
+0710J21
+PRLV SEPA Company 2
+24-02-2020 / 22-03-2020 56418710
+
+
+DEBIT
+20200224
+-2.39
+79782112104
+5964661
+VIR Person 1
+481840871 Splitwise
+
+
+CREDIT
+20200220
++235.00
+5408506055
+2D08DAF
+VIREMENT Person 2
+Cadeau
+
+
+CREDIT
+20200220
++55.00
+548910308
+2D088L5
+VIREMENT Company 3
+48716508719
+
+
+DEBIT
+20200219
+-55.00
+54874910320
+2BCS20C
+170220 CB****5555
+BDE INSA LYON 69VILLEURBANNE
+
+
+DEBIT
+20200219
+-0.90
+5478058
+2BINNA4
+180220 CB****5555
+GUY AND SONS FR LYON 0,90EUR 1 EURO = 1,000000
+
+
+DEBIT
+20200219
+-1.40
+56098760894
+2BINNA3
+170220 CB****5555
+GUY AND SONS FR LYON 1,40EUR 1 EURO = 1,000000
+
+
+DEBIT
+20200219
+-473.50
+5484916740
+5543368
+VIR Person 1
+65187460 Acompte cuisine 2
+
+
+DEBIT
+20200218
+-96.96
+6518749560840
+070M39Z
+PRLV SEPA Company 4
+487105874 Amazon.fr 3X QC.(OJBIYN:ZOFEUBZF51871
+
+
+DEBIT
+20200217
+-232.00
+51981560870
+281U2GL
+150220 CB****5555
+GRAND PARC PUY 85LES EPESSES
+
+
+DEBIT
+20200217
+-1.00
+6508941561987
+287L9X0
+140220 CB****5555
+UBER BV NL HELP.UBER.CO 1,00EUR 1 EURO = 1,000000
+
+
+CREDIT
+20200217
++8.60
+654897503260
+281M0PK
+VIREMENT Person 5
+VIREMENT DE PERSON 6
+
+
+
+1234.56
+20200226
+
+
+1234.56
+20200226
+
+
+
+
+
diff --git a/tests/samples/bpvf_expected.json b/tests/samples/bpvf_expected.json
new file mode 100644
index 0000000..eb09353
--- /dev/null
+++ b/tests/samples/bpvf_expected.json
@@ -0,0 +1 @@
+[{"date": "2020-02-26", "amount": -9660, "payee_name": "PRLV SEPA Company 3", "memo": "123456789 PAYPAL 542UHBON", "import_id": "YNAB:-9660:2020-02-26:1"}, {"date": "2020-02-25", "amount": -2400, "payee_name": "H.I.K 69VILLEURBANNE", "memo": "240220 CB****5555", "import_id": "YNAB:-2400:2020-02-25:1"}, {"date": "2020-02-25", "amount": -39200, "payee_name": "DELIVEROO FR WWW", "memo": "230220 CB****5555 39,20EUR 1 EURO = 1,000000", "import_id": "YNAB:-39200:2020-02-25:1"}, {"date": "2020-02-25", "amount": -9990, "payee_name": "PRLV SEPA Company 1", "memo": "Votre abonnement mobile: 06XXXXX 6498165189060897", "import_id": "YNAB:-9990:2020-02-25:1"}, {"date": "2020-02-24", "amount": -7500, "payee_name": "COMPANY FR LYON 6EME", "memo": "210220 CB****5555 7,50EUR 1 EURO = 1,000000", "import_id": "YNAB:-7500:2020-02-24:1"}, {"date": "2020-02-24", "amount": -34990, "payee_name": "PRLV SEPA Company 2", "memo": "24-02-2020 / 22-03-2020 56418710", "import_id": "YNAB:-34990:2020-02-24:1"}, {"date": "2020-02-24", "amount": -2390, "payee_name": "VIR Person 1", "memo": "481840871 Splitwise", "import_id": "YNAB:-2390:2020-02-24:1"}, {"date": "2020-02-20", "amount": 235000, "payee_name": "VIREMENT Person 2", "memo": "Cadeau", "import_id": "YNAB:235000:2020-02-20:1"}, {"date": "2020-02-20", "amount": 55000, "payee_name": "VIREMENT Company 3", "memo": "48716508719", "import_id": "YNAB:55000:2020-02-20:1"}, {"date": "2020-02-19", "amount": -55000, "payee_name": "BDE INSA LYON 69VILLEURBANNE", "memo": "170220 CB****5555", "import_id": "YNAB:-55000:2020-02-19:1"}, {"date": "2020-02-19", "amount": -900, "payee_name": "GUY AND SONS FR LYON", "memo": "180220 CB****5555 0,90EUR 1 EURO = 1,000000", "import_id": "YNAB:-900:2020-02-19:1"}, {"date": "2020-02-19", "amount": -1400, "payee_name": "GUY AND SONS FR LYON", "memo": "170220 CB****5555 1,40EUR 1 EURO = 1,000000", "import_id": "YNAB:-1400:2020-02-19:1"}, {"date": "2020-02-19", "amount": -473500, "payee_name": "VIR Person 1", "memo": "65187460 Acompte cuisine 2", "import_id": "YNAB:-473500:2020-02-19:1"}, {"date": "2020-02-18", "amount": -96960, "payee_name": "PRLV SEPA Company 4", "memo": "487105874 Amazon.fr 3X QC.(OJBIYN:ZOFEUBZF51871", "import_id": "YNAB:-96960:2020-02-18:1"}, {"date": "2020-02-17", "amount": -232000, "payee_name": "GRAND PARC PUY 85LES EPESSES", "memo": "150220 CB****5555", "import_id": "YNAB:-232000:2020-02-17:1"}, {"date": "2020-02-17", "amount": -1000, "payee_name": "UBER BV NL HELP.UBER.CO", "memo": "140220 CB****5555 1,00EUR 1 EURO = 1,000000", "import_id": "YNAB:-1000:2020-02-17:1"}, {"date": "2020-02-17", "amount": 8600, "payee_name": "VIREMENT Person 5", "memo": "VIREMENT DE PERSON 6", "import_id": "YNAB:8600:2020-02-17:1"}]
\ No newline at end of file
diff --git a/tests/samples/bpvf_malformed.ofx b/tests/samples/bpvf_malformed.ofx
new file mode 100644
index 0000000..e137782
--- /dev/null
+++ b/tests/samples/bpvf_malformed.ofx
@@ -0,0 +1,197 @@
+OFXHEADER:100
+DATA:OFXSGML
+VERSION:102
+SECURITY:NONE
+ENCODING:USASCII
+CHARSET:1252
+COMPRESSION:NONE
+OLDFILEUID:NONE
+FRA
+20200226060348
+20200226060348
+
+
+
+
+12345678912
+
+0
+INFO
+
+
+EUR
+
+12345
+65432
+12345678912
+CHECKING
+
+
+20200217
+20200226
+
+DEBIT
+20200226
+-9.66
+25364 HC4X
+0719ATG
+PRLV SEPA Company 3
+123456789 PAYPAL 542UHBON
+
+
+DEBIT
+20200225
+-2.40
+727278312
+2L9Z4OP
+240220 CB****5555
+H.I.K 69VILLEURBANNE
+
+
+DEBIT
+20200225
+-39.20
+7834583486
+2LFVF8U
+230220 CB****5555
+DELIVEROO FR WWW 39,20EUR 1 EURO = 1,000000
+
+
+DEBIT
+20200225
+-9.99
+883783453
+0711ZB6
+PRLV SEPA Company 1
+Votre abonnement mobile: 06XXXXX 6498165189060897
+
+
+DEBIT
+20200224
+-7.50
+651876109
+2JSCN09
+210220 CB****5555
+COMPANY FR LYON 6EME 7,50EUR 1 EURO = 1,000000
+
+
+DEBIT
+20200224
+-34.99
+3483483
+0710J21
+PRLV SEPA Company 2
+24-02-2020 / 22-03-2020 56418710
+
+
+DEBIT
+20200224
+-2.39
+79782112104
+5964661
+VIR Person 1
+481840871 Splitwise
+
+
+CREDIT
+20200220
++235.00
+5408506055
+2D08DAF
+VIREMENT Person 2
+Cadeau
+
+
+CREDIT
+20200220
++55.00
+548910308
+2D088L5
+VIREMENT Company 3
+48716508719
+
+
+DEBIT
+20200219
+-55.00
+54874910320
+2BCS20C
+170220 CB****5555
+BDE INSA LYON 69VILLEURBANNE
+
+
+DEBIT
+20200219
+-0.90
+5478058
+2BINNA4
+180220 CB****5555
+GUY AND SONS FR LYON 0,90EUR 1 EURO = 1,000000
+
+
+DEBIT
+20200219
+-1.40
+56098760894
+2BINNA3
+170220 CB****5555
+GUY AND SONS FR LYON 1,40EUR 1 EURO = 1,000000
+
+
+DEBIT
+20200219
+-473.50
+5484916740
+5543368
+VIR Person 1
+65187460 Acompte cuisine 2
+
+
+DEBIT
+20200218
+-96.96
+6518749560840
+070M39Z
+PRLV SEPA Company 4
+487105874 Amazon.fr 3X QC.(OJBIYN:ZOFEUBZF51871
+
+
+DEBIT
+20200217
+-232.00
+51981560870
+281U2GL
+150220 CB****5555
+GRAND PARC PUY 85LES EPESSES
+
+
+DEBIT
+20200217
+-1.00
+6508941561987
+287L9X0
+140220 CB****5555
+UBER BV NL HELP.UBER.CO 1,00EUR 1 EURO = 1,000000
+
+
+CREDIT
+20200217
++8.60
+654897503260
+281M0PK
+VIREMENT Person 5
+VIREMENT DE PERSON 6
+
+
+
+1234.56
+20200226
+
+
+1234.56
+20200226
+
+
+
+
+
diff --git a/tests/samples/ce.ofx b/tests/samples/ce.ofx
new file mode 100644
index 0000000..d5d41c4
--- /dev/null
+++ b/tests/samples/ce.ofx
@@ -0,0 +1,97 @@
+OFXHEADER:100
+DATA:OFXSGML
+VERSION:102
+SECURITY:NONE
+ENCODING:USASCII
+CHARSET:1252
+COMPRESSION:NONE
+OLDFILEUID:NONE
+NEWFILEUID:NONE
+
+
+
+
+0
+INFO
+
+20200226
+FRA
+20200226
+20200226
+
+
+
+
+00
+
+0
+INFO
+
+
+EUR
+
+12345
+54321
+12345678932
+SAVINGS
+
+
+20200223
+20200225
+
+DEBIT
+20200225
+-21,00
+54875116484966-41.57.21.325698
+CB DECATHLON FACT 240220
+CB DECATHLON FACT 240220
+
+
+DEBIT
+20200225
+-7,00
+54875116484966-41.57.21.325698
+PRLV COMPANY
+Company Ref Prlvt SEPA 99-1KIBHEF-01 45871984
+
+
+DEBIT
+20200224
+-48,13
+54875116484966-41.57.21.325698
+CB 3403 MONOP FACT 210220
+CB 3403 MONOP FACT 210220
+
+
+DEBIT
+20200224
+-1,20
+54875116484966-41.57.21.325698
+CB MALATIER FACT 210220
+CB MALATIER FACT 210220
+
+
+CREDIT
+20200224
++2,39
+54875116484966-41.57.21.325698
+VIR SEPA PERSON 1
+_
+
+
+CREDIT
+20200224
++14,49
+2402202020200224-05.41.39.551984
+VIR SEPA PERSON 2
+_
+
+
+
+1234,56
+20200226
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/samples/ce_expected.json b/tests/samples/ce_expected.json
new file mode 100644
index 0000000..4e76833
--- /dev/null
+++ b/tests/samples/ce_expected.json
@@ -0,0 +1 @@
+[{"date": "2020-02-25", "amount": -21000, "payee_name": "CB DECATHLON", "memo": "FACT 240220", "import_id": "YNAB:-21000:2020-02-25:1"}, {"date": "2020-02-25", "amount": -7000, "payee_name": "PRLV COMPANY", "memo": "Company Ref Prlvt SEPA 99-1KIBHEF-01 45871984", "import_id": "YNAB:-7000:2020-02-25:1"}, {"date": "2020-02-24", "amount": -48130, "payee_name": "CB 3403 MONOP", "memo": "FACT 210220", "import_id": "YNAB:-48130:2020-02-24:1"}, {"date": "2020-02-24", "amount": -1200, "payee_name": "CB MALATIER", "memo": "FACT 210220", "import_id": "YNAB:-1200:2020-02-24:1"}, {"date": "2020-02-24", "amount": 2390, "payee_name": "VIR SEPA PERSON 1", "memo": "_", "import_id": "YNAB:2390:2020-02-24:1"}, {"date": "2020-02-24", "amount": 14490, "payee_name": "VIR SEPA PERSON 2", "memo": "_", "import_id": "YNAB:14490:2020-02-24:1"}]
\ No newline at end of file
diff --git a/tests/samples/revolut.csv b/tests/samples/revolut.csv
new file mode 100644
index 0000000..9207757
--- /dev/null
+++ b/tests/samples/revolut.csv
@@ -0,0 +1,10 @@
+Completed Date;Reference;Paid Out (EUR);Paid In (EUR);Exchange Out;Exchange In; Balance (EUR);Exchange Rate;Category
+29 Jan;To Person 1;53,63;;;;21,66; ;Transfers
+29 Jan;To Person 2;0,90;;;;75,29; ;Transfers
+29 Jan;Refund from Company 2;;53,63;;;76,19; ;Shopping
+24 Jan;To Person 3;8,50;;;;22,56; ;Transfers
+16 Jan;To Person 4;1,40;;;;31,06; ;Transfers
+10 Jan;To Person 5;2,01;;;;32,46; ;Transfers
+10 Jan;To Person 6;1,21;;;;34,47; ;Transfers
+5 Jan;Company 1;123,68;;USD 138,00;;35,68;FX-rate €1 = US$1,1158;Shopping
+4 Jan;Top-up via Apple Pay;;100,00;;;159,36; ;General
diff --git a/tests/samples/revolut_expected.json b/tests/samples/revolut_expected.json
new file mode 100644
index 0000000..adc661d
--- /dev/null
+++ b/tests/samples/revolut_expected.json
@@ -0,0 +1 @@
+[{"date": "2020-01-29", "amount": -53630, "payee_name": "To Person 1", "memo": "Transfers", "import_id": "YNAB:-53630:2020-01-29:1"}, {"date": "2020-01-29", "amount": -900, "payee_name": "To Person 2", "memo": "Transfers", "import_id": "YNAB:-900:2020-01-29:1"}, {"date": "2020-01-29", "amount": 53630, "payee_name": "Refund from Company 2", "memo": "Shopping", "import_id": "YNAB:53630:2020-01-29:1"}, {"date": "2020-01-24", "amount": -8500, "payee_name": "To Person 3", "memo": "Transfers", "import_id": "YNAB:-8500:2020-01-24:1"}, {"date": "2020-01-16", "amount": -1400, "payee_name": "To Person 4", "memo": "Transfers", "import_id": "YNAB:-1400:2020-01-16:1"}, {"date": "2020-01-10", "amount": -2009, "payee_name": "To Person 5", "memo": "Transfers", "import_id": "YNAB:-2009:2020-01-10:1"}, {"date": "2020-01-10", "amount": -1210, "payee_name": "To Person 6", "memo": "Transfers", "import_id": "YNAB:-1210:2020-01-10:1"}, {"date": "2020-01-05", "amount": -123680, "payee_name": "Company 1", "memo": "Shopping - FX-rate \u20ac1\u2008=\u2008US$1,1158", "import_id": "YNAB:-123680:2020-01-05:1"}, {"date": "2020-01-04", "amount": 100000, "payee_name": "Top-up via Apple Pay", "memo": "General", "import_id": "YNAB:100000:2020-01-04:1"}]
\ No newline at end of file
diff --git a/tests/test_bpvf_processor.py b/tests/test_bpvf_processor.py
index 3ea5561..888aecc 100644
--- a/tests/test_bpvf_processor.py
+++ b/tests/test_bpvf_processor.py
@@ -1,7 +1,8 @@
import datetime
+import json
import unittest
-from ofx_processor.processors.bpvf import BpvfLine
+from ofx_processor.processors.bpvf import BpvfLine, BpvfProcessor
class BpvfTransaction:
@@ -22,7 +23,7 @@ class BpvfTransaction:
self.name = name
-class BpvfProcessorTestCase(unittest.TestCase):
+class BpvfLineTestCase(unittest.TestCase):
def test_process_name_and_memo_no_change(self):
name = "business"
memo = "2020-01-17"
@@ -84,5 +85,22 @@ class BpvfProcessorTestCase(unittest.TestCase):
self.assertEqual(result_amount, expected_amount)
+class BpvfProcessorTestCase(unittest.TestCase):
+ def test_file_not_found(self):
+ with self.assertRaises(SystemExit):
+ BpvfProcessor("filenotfound.ofx").get_transactions()
+
+ def test_file(self):
+ transactions = BpvfProcessor("tests/samples/bpvf.ofx").get_transactions()
+ with open("tests/samples/bpvf_expected.json") as f:
+ expected_transactions = json.load(f)
+
+ self.assertListEqual(transactions, expected_transactions)
+
+ def test_file_malformed(self):
+ with self.assertRaises(SystemExit):
+ BpvfProcessor("tests/samples/bpvf_malformed.ofx")
+
+
if __name__ == "__main__":
unittest.main() # pragma: nocover
diff --git a/tests/test_ce_processor.py b/tests/test_ce_processor.py
new file mode 100644
index 0000000..f2d6ba3
--- /dev/null
+++ b/tests/test_ce_processor.py
@@ -0,0 +1,21 @@
+import json
+import unittest
+
+from ofx_processor.processors.ce import CeProcessor
+
+
+class CeProcessorTestCase(unittest.TestCase):
+ def test_file_not_found(self):
+ with self.assertRaises(SystemExit):
+ CeProcessor("filenotfound.ofx").get_transactions()
+
+ def test_file(self):
+ transactions = CeProcessor("tests/samples/ce.ofx").get_transactions()
+ with open("tests/samples/ce_expected.json") as f:
+ expected_transactions = json.load(f)
+
+ self.assertListEqual(transactions, expected_transactions)
+
+
+if __name__ == "__main__":
+ unittest.main() # pragma: nocover
diff --git a/tests/test_revolut_processor.py b/tests/test_revolut_processor.py
index 2e00110..b684664 100644
--- a/tests/test_revolut_processor.py
+++ b/tests/test_revolut_processor.py
@@ -1,9 +1,11 @@
import datetime
+import json
import unittest
from ofx_processor.processors.revolut import (
_amount_str_to_float,
RevolutLine,
+ RevolutProcessor,
)
@@ -70,5 +72,18 @@ class RevolutProcessorTestCase(unittest.TestCase):
self.assertEqual(RevolutLine(line).get_payee(), expected)
+class BpvfProcessorTestCase(unittest.TestCase):
+ def test_file_not_found(self):
+ with self.assertRaises(SystemExit):
+ RevolutProcessor("notfound.csv").get_transactions()
+
+ def test_file(self):
+ transactions = RevolutProcessor("tests/samples/revolut.csv").get_transactions()
+ with open("tests/samples/revolut_expected.json") as f:
+ expected_transactions = json.load(f)
+
+ self.assertListEqual(transactions, expected_transactions)
+
+
if __name__ == "__main__":
unittest.main() # pragma: nocover