diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/__init__.py | 0 | ||||
-rw-r--r-- | tests/fixtures/account_listing_aggregation.ofx | 51 | ||||
-rw-r--r-- | tests/fixtures/bank_medium.ofx | 18 | ||||
-rw-r--r-- | tests/fixtures/bank_small.ofx | 11 | ||||
-rw-r--r-- | tests/fixtures/checking.ofx | 83 | ||||
-rw-r--r-- | tests/fixtures/fail_nice/date_missing.ofx | 65 | ||||
-rw-r--r-- | tests/fixtures/fail_nice/decimal_error.ofx | 51 | ||||
-rw-r--r-- | tests/fixtures/fail_nice/empty_balance.ofx | 48 | ||||
-rw-r--r-- | tests/fixtures/fidelity.ofx | 11 | ||||
-rw-r--r-- | tests/fixtures/investment_medium.ofx | 119 | ||||
-rw-r--r-- | tests/fixtures/multiple_accounts.ofx | 61 | ||||
-rw-r--r-- | tests/fixtures/multiple_accounts2.ofx | 61 | ||||
-rw-r--r-- | tests/fixtures/signon_fail.ofx | 11 | ||||
-rw-r--r-- | tests/fixtures/signon_success.ofx | 11 | ||||
-rw-r--r-- | tests/fixtures/signon_success_no_message.ofx | 11 | ||||
-rw-r--r-- | tests/fixtures/vanguard.ofx | 11 | ||||
-rw-r--r-- | tests/support.py | 9 | ||||
-rw-r--r-- | tests/test_parse.py | 623 | ||||
-rw-r--r-- | tests/test_write.py | 17 |
19 files changed, 1272 insertions, 0 deletions
diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/__init__.py diff --git a/tests/fixtures/account_listing_aggregation.ofx b/tests/fixtures/account_listing_aggregation.ofx new file mode 100644 index 0000000..5ecf241 --- /dev/null +++ b/tests/fixtures/account_listing_aggregation.ofx @@ -0,0 +1,51 @@ +OFXHEADER:100
+DATA:OFXSGML
+VERSION:102
+SECURITY:NONE
+ENCODING:USASCII
+CHARSET:1252
+COMPRESSION:NONE
+OLDFILEUID:NONE
+NEWFILEUID:85230611d6fc414fa391a8c2425f8e9e
+
+<OFX>
+<SIGNONMSGSRSV1>
+<SONRS>
+<STATUS><CODE>0<SEVERITY>INFO<MESSAGE>Success</STATUS>
+<DTSERVER>20120814060142<LANGUAGE>ENG
+<FI><ORG>USAA<FID>24591</FI>
+</SONRS>
+</SIGNONMSGSRSV1>
+<SIGNUPMSGSRSV1>
+<ACCTINFOTRNRS>
+<TRNUID>09ca62d0198049388252f0a547bae86a<STATUS>
+<CODE>0<SEVERITY>INFO<MESSAGE>Success</STATUS>
+<CLTCOOKIE>4<ACCTINFORS>
+<DTACCTUP>20120814120000<ACCTINFO>
+<DESC>USAA SAVINGS<BANKACCTINFO>
+<BANKACCTFROM>
+<BANKID>314074269<ACCTID>0000000001<ACCTTYPE>SAVINGS</BANKACCTFROM>
+<SUPTXDL>Y<XFERSRC>N<XFERDEST>N<SVCSTATUS>ACTIVE</BANKACCTINFO>
+</ACCTINFO>
+<ACCTINFO>
+<DESC>FOUR STAR CHECKING<BANKACCTINFO>
+<BANKACCTFROM>
+<BANKID>314074269<ACCTID>0000000002<ACCTTYPE>CHECKING</BANKACCTFROM>
+<SUPTXDL>Y<XFERSRC>N<XFERDEST>N<SVCSTATUS>ACTIVE</BANKACCTINFO>
+</ACCTINFO>
+<ACCTINFO>
+<DESC>LINE OF CREDIT<BANKACCTINFO>
+<BANKACCTFROM>
+<BANKID>314074269<ACCTID>00000000000003<ACCTTYPE>CREDITLINE</BANKACCTFROM>
+<SUPTXDL>Y<XFERSRC>N<XFERDEST>N<SVCSTATUS>ACTIVE</BANKACCTINFO>
+</ACCTINFO>
+<ACCTINFO>
+<DESC>MY CREDIT CARD<CCACCTINFO>
+<CCACCTFROM>
+<ACCTID>4111111111111111</CCACCTFROM>
+<SUPTXDL>Y<XFERSRC>N<XFERDEST>N<SVCSTATUS>ACTIVE</CCACCTINFO>
+</ACCTINFO>
+</ACCTINFORS>
+</ACCTINFOTRNRS>
+</SIGNUPMSGSRSV1>
+</OFX>
diff --git a/tests/fixtures/bank_medium.ofx b/tests/fixtures/bank_medium.ofx new file mode 100644 index 0000000..08c71c3 --- /dev/null +++ b/tests/fixtures/bank_medium.ofx @@ -0,0 +1,18 @@ +OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE + +<OFX><SIGNONMSGSRSV1><SONRS><STATUS><CODE>0<SEVERITY>INFO<MESSAGE>OK</STATUS><DTSERVER>20090523122017<LANGUAGE>ENG<DTPROFUP>20090523122017<DTACCTUP>20090523122017<INTU.BID>00024</SONRS></SIGNONMSGSRSV1> +<BANKMSGSRSV1><STMTTRNRS><TRNUID>20090523122017<STATUS><CODE>0<SEVERITY>INFO<MESSAGE>OK</STATUS> +<STMTRS><CURDEF>CAD<BANKACCTFROM><BANKID>160000100<BRANCHID>00<ACCTID>12300 000012345678<ACCTTYPE>CHECKING</BANKACCTFROM> +<BANKTRANLIST><DTSTART>20090401<DTEND>20090523122017 +<STMTTRN><TRNTYPE>POS<DTPOSTED>20090401122017.000[-5:EST]<TRNAMT>-6.60<FITID>0000123456782009040100001<NAME>MCDONALD'S #112<MEMO>POS MERCHANDISE;MCDONALD'S #112</STMTTRN> +<STMTTRN><TRNTYPE>CHECK<DTPOSTED>20090402122017.000[-5:EST]<TRNAMT>-316.67<FITID>0000123456782009040200004<CHECKNUM>0<NAME>Joe's Bald Hairstyles<MEMO>MISCELLANEOUS PAYMENTS;Joe's Bald Hairstyles</STMTTRN> +<STMTTRN><TRNTYPE>POS<DTPOSTED>20090403122017.000[-5:EST]<TRNAMT>-22.00<FITID>0000123456782009040300005<NAME>CONNIE'S HAIR D<MEMO>POS MERCHANDISE;CONNIE'S HAIR D</STMTTRN> +</BANKTRANLIST><LEDGERBAL><BALAMT>382.34<DTASOF>20090523122017</LEDGERBAL><AVAILBAL><BALAMT>682.34<DTASOF>20090523122017</AVAILBAL></STMTRS></STMTTRNRS></BANKMSGSRSV1></OFX> diff --git a/tests/fixtures/bank_small.ofx b/tests/fixtures/bank_small.ofx new file mode 100644 index 0000000..3040f8f --- /dev/null +++ b/tests/fixtures/bank_small.ofx @@ -0,0 +1,11 @@ +OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE + +<OFX></OFX> diff --git a/tests/fixtures/checking.ofx b/tests/fixtures/checking.ofx new file mode 100644 index 0000000..6c32904 --- /dev/null +++ b/tests/fixtures/checking.ofx @@ -0,0 +1,83 @@ +OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE + +<OFX> + <SIGNONMSGSRSV1> + <SONRS> + <STATUS> + <CODE>0 + <SEVERITY>INFO + </STATUS> + <DTSERVER>20130525225731.258 + <LANGUAGE>ENG + <DTPROFUP>20050531060000.000 + <FI> + <ORG>FAKE + <FID>1101 + </FI> + <INTU.BID>51123 + <INTU.USERID>9774652 + </SONRS> + </SIGNONMSGSRSV1> + <BANKMSGSRSV1> + <STMTTRNRS> + <TRNUID>0 + <STATUS> + <CODE>0 + <SEVERITY>INFO + </STATUS> + <STMTRS> + <CURDEF>USD + <BANKACCTFROM> + <BANKID>5472369148 + <ACCTID>1452687~7 + <ACCTTYPE>CHECKING + </BANKACCTFROM> + <BANKTRANLIST> + <DTSTART>20000101070000.000 + <DTEND>20130525060000.000 + <STMTTRN> + <TRNTYPE>CREDIT + <DTPOSTED>20110331120000.000 + <TRNAMT>0.01 + <FITID>0000486 + <NAME>DIVIDEND EARNED FOR PERIOD OF 03 + <MEMO>DIVIDEND EARNED FOR PERIOD OF 03/01/2011 THROUGH 03/31/2011 ANNUAL PERCENTAGE YIELD EARNED IS 0.05% + </STMTTRN> + <STMTTRN> + <TRNTYPE>DEBIT + <DTPOSTED>20110405120000.000 + <TRNAMT>-34.51 + <FITID>0000487 + <NAME>AUTOMATIC WITHDRAWAL, ELECTRIC BILL + <MEMO>AUTOMATIC WITHDRAWAL, ELECTRIC BILL WEB(S ) + </STMTTRN> + <STMTTRN> + <TRNTYPE>CHECK + <DTPOSTED>20110407120000.000 + <TRNAMT>-25.00 + <FITID>0000488 + <CHECKNUM>319 + <NAME>RETURNED CHECK FEE, CHECK # 319 + <MEMO>RETURNED CHECK FEE, CHECK # 319 FOR $45.33 ON 04/07/11 + </STMTTRN> + </BANKTRANLIST> + <LEDGERBAL> + <BALAMT>100.99 + <DTASOF>20130525225731.258 + </LEDGERBAL> + <AVAILBAL> + <BALAMT>75.99 + <DTASOF>20130525225731.258 + </AVAILBAL> + </STMTRS> + </STMTTRNRS> + </BANKMSGSRSV1> +</OFX>
\ No newline at end of file diff --git a/tests/fixtures/fail_nice/date_missing.ofx b/tests/fixtures/fail_nice/date_missing.ofx new file mode 100644 index 0000000..931f438 --- /dev/null +++ b/tests/fixtures/fail_nice/date_missing.ofx @@ -0,0 +1,65 @@ +
+
+<OFX>
+ <SIGNONMSGSRSV1>
+ <SONRS>
+ <STATUS>
+ <CODE>0</CODE>
+ <SEVERITY>INFO</SEVERITY>
+ </STATUS>
+ <DTSERVER>20110614</DTSERVER>
+ <LANGUAGE></LANGUAGE>
+ </SONRS>
+ </SIGNONMSGSRSV1>
+ <BANKMSGSRSV1>
+ <STMTTRNRS>
+ <TRNUID>1</TRNUID>
+ <STATUS>
+ <CODE>0</CODE>
+ <SEVERITY>INFO</SEVERITY>
+ </STATUS>
+ <STMTRS>
+ <CURDEF>USD</CURDEF>
+ <BANKACCTFROM>
+ <BANKID>123845030</BANKID>
+ <ACCTID>192639749</ACCTID>
+ <ACCTTYPE>CHECKING</ACCTTYPE>
+ </BANKACCTFROM>
+
+ <BANKTRANLIST>
+ <DTSTART>20110412</DTSTART>
+ <DTEND>20110614</DTEND>
+
+ <STMTTRN>
+ <TRNTYPE>OTHER</TRNTYPE>
+ <TRNAMT>-80.00</TRNAMT>
+ <FITID>184997056</FITID>
+ <NAME>TestFail1</NAME>
+ </STMTTRN>
+
+ <STMTTRN>
+ <TRNTYPE>OTHER</TRNTYPE>
+ <DTPOSTED></DTPOSTED>
+ <TRNAMT>200.00</TRNAMT>
+ <FITID>2000957249</FITID>
+ <NAME>TestFail2</NAME>
+ </STMTTRN>
+
+ <STMTTRN>
+ <TRNTYPE>OTHER</TRNTYPE>
+ <DTPOSTED>20120231</DTPOSTED>
+ <TRNAMT>200.00</TRNAMT>
+ <FITID>2000957249</FITID>
+ <NAME>TestFail2</NAME>
+ </STMTTRN>
+
+ </BANKTRANLIST>
+
+ <LEDGERBAL>
+ <BALAMT>0</BALAMT>
+ <DTASOF>20110614</DTASOF>
+ </LEDGERBAL>
+ </STMTRS>
+ </STMTTRNRS>
+ </BANKMSGSRSV1>
+</OFX>
diff --git a/tests/fixtures/fail_nice/decimal_error.ofx b/tests/fixtures/fail_nice/decimal_error.ofx new file mode 100644 index 0000000..4389f6c --- /dev/null +++ b/tests/fixtures/fail_nice/decimal_error.ofx @@ -0,0 +1,51 @@ +
+
+<OFX>
+ <SIGNONMSGSRSV1>
+ <SONRS>
+ <STATUS>
+ <CODE>0</CODE>
+ <SEVERITY>INFO</SEVERITY>
+ </STATUS>
+ <DTSERVER>20110614</DTSERVER>
+ <LANGUAGE></LANGUAGE>
+ </SONRS>
+ </SIGNONMSGSRSV1>
+ <BANKMSGSRSV1>
+ <STMTTRNRS>
+ <TRNUID>1</TRNUID>
+ <STATUS>
+ <CODE>0</CODE>
+ <SEVERITY>INFO</SEVERITY>
+ </STATUS>
+ <STMTRS>
+ <CURDEF>CAD
+ <BANKACCTFROM>
+ <BANKID>123845030</BANKID>
+ <ACCTID>192639749</ACCTID>
+ <ACCTTYPE>CHECKING</ACCTTYPE>
+ </BANKACCTFROM>
+
+ <BANKTRANLIST>
+ <DTSTART>20110412</DTSTART>
+ <DTEND>20110614</DTEND>
+
+
+ <STMTTRN>
+ <TRNTYPE>OTHER</TRNTYPE>
+ <DTPOSTED>201120000000</DTPOSTED>
+ <TRNAMT>$120</TRNAMT>
+ <FITID>2000957249</FITID>
+ <NAME>Fail1</NAME>
+ </STMTTRN>
+
+ </BANKTRANLIST>
+
+ <LEDGERBAL>
+ <BALAMT>0</BALAMT>
+ <DTASOF>20110614</DTASOF>
+ </LEDGERBAL>
+ </STMTRS>
+ </STMTTRNRS>
+ </BANKMSGSRSV1>
+</OFX>
diff --git a/tests/fixtures/fail_nice/empty_balance.ofx b/tests/fixtures/fail_nice/empty_balance.ofx new file mode 100644 index 0000000..bf76b48 --- /dev/null +++ b/tests/fixtures/fail_nice/empty_balance.ofx @@ -0,0 +1,48 @@ +<OFX> + <SIGNONMSGSRSV1> + <SONRS> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + </STATUS> + <DTSERVER>20110614</DTSERVER> + <LANGUAGE></LANGUAGE> + </SONRS> + </SIGNONMSGSRSV1> + <BANKMSGSRSV1> + <STMTTRNRS> + <TRNUID>1</TRNUID> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + </STATUS> + <STMTRS> + <CURDEF>CAD + <BANKACCTFROM> + <BANKID>123845030</BANKID> + <ACCTID>192639749</ACCTID> + <ACCTTYPE>CHECKING</ACCTTYPE> + </BANKACCTFROM> + <BANKTRANLIST> + <DTSTART>20110412</DTSTART> + <DTEND>20110614</DTEND> + <STMTTRN> + <TRNTYPE>OTHER</TRNTYPE> + <DTPOSTED>20110308020000</DTPOSTED> + <TRNAMT>120</TRNAMT> + <FITID>2000957249</FITID> + <NAME>Foobar</NAME> + </STMTTRN> + </BANKTRANLIST> + <LEDGERBAL> + <BALAMT> </BALAMT> + <DTASOF>20110614</DTASOF> + </LEDGERBAL> + <AVAILBAL> + <BALAMT></BALAMT> + <DTASOF>20110614</DTASOF> + </AVAILBAL> + </STMTRS> + </STMTTRNRS> + </BANKMSGSRSV1> +</OFX> diff --git a/tests/fixtures/fidelity.ofx b/tests/fixtures/fidelity.ofx new file mode 100644 index 0000000..de8e580 --- /dev/null +++ b/tests/fixtures/fidelity.ofx @@ -0,0 +1,11 @@ +OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0 + +<OFX><SIGNONMSGSRSV1><SONRS><STATUS><CODE>0<SEVERITY>INFO<MESSAGE>SUCCESS</STATUS><DTSERVER>20120908190849.317[-4:EDT]<LANGUAGE>ENG<FI><ORG>fidelity.com<FID>7776</FI></SONRS></SIGNONMSGSRSV1><INVSTMTMSGSRSV1><INVSTMTTRNRS><TRNUID>a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0<STATUS><CODE>0<SEVERITY>INFO<MESSAGE>SUCCESS</STATUS><INVSTMTRS><DTASOF>20120908033034.000[-4:EDT]<CURDEF>USD<INVACCTFROM><BROKERID>fidelity.com<ACCTID>01234567890</INVACCTFROM><INVTRANLIST><DTSTART>20120710000000.000[-4:EDT]<DTEND>20120908190849.555[-4:EDT]<BUYSTOCK><INVBUY><INVTRAN><FITID>0123456789020201120120720<DTTRADE>20120720000000.000[-4:EDT]<MEMO>YOU BOUGHT</INVTRAN><SECID><UNIQUEID>458140100<UNIQUEIDTYPE>CUSIP</SECID><UNITS>+0000000000100.00000<UNITPRICE>000000025.635000000<COMMISSION>+00000000000007.9500<FEES>+00000000000000.0000<TOTAL>-00000000002571.4500<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY><SUBACCTSEC>CASH<SUBACCTFUND>CASH</INVBUY><BUYTYPE>BUY </BUYSTOCK><BUYSTOCK><INVBUY><INVTRAN><FITID>0123456789020901120120727<DTTRADE>20120727000000.000[-4:EDT]<MEMO>YOU BOUGHT</INVTRAN><SECID><UNIQUEID>G7945E105<UNIQUEIDTYPE>CUSIP</SECID><UNITS>+0000000000128.00000<UNITPRICE>000000039.390900000<COMMISSION>+00000000000007.9500<FEES>+00000000000000.0000<TOTAL>-00000000005049.9900<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY><SUBACCTSEC>CASH<SUBACCTFUND>CASH</INVBUY><BUYTYPE>BUY </BUYSTOCK><BUYSTOCK><INVBUY><INVTRAN><FITID>0123456789020901220120727<DTTRADE>20120727000000.000[-4:EDT]<MEMO>YOU BOUGHT</INVTRAN><SECID><UNIQUEID>431571108<UNIQUEIDTYPE>CUSIP</SECID><UNITS>+0000000000115.00000<UNITPRICE>000000017.250000000<COMMISSION>+00000000000007.9500<FEES>+00000000000000.0000<TOTAL>-00000000001991.7000<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY><SUBACCTSEC>CASH<SUBACCTFUND>CASH</INVBUY><BUYTYPE>BUY </BUYSTOCK><BUYSTOCK><INVBUY><INVTRAN><FITID>0123456789021301120120731<DTTRADE>20120731000000.000[-4:EDT]<MEMO>YOU BOUGHT</INVTRAN><SECID><UNIQUEID>19421R200<UNIQUEIDTYPE>CUSIP</SECID><UNITS>+0000000000069.00000<UNITPRICE>000000014.469900000<COMMISSION>+00000000000007.9500<FEES>+00000000000000.0000<TOTAL>-00000000001006.3700<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY><SUBACCTSEC>CASH<SUBACCTFUND>CASH</INVBUY><BUYTYPE>BUY </BUYSTOCK><BUYSTOCK><INVBUY><INVTRAN><FITID>0123456789021301620120731<DTTRADE>20120731000000.000[-4:EDT]<MEMO>YOU BOUGHT</INVTRAN><SECID><UNIQUEID>98417P105<UNIQUEIDTYPE>CUSIP</SECID><UNITS>+0000000000386.00000<UNITPRICE>000000002.588700000<COMMISSION>+00000000000007.9500<FEES>+00000000000000.0000<TOTAL>-00000000001007.1900<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY><SUBACCTSEC>CASH<SUBACCTFUND>CASH</INVBUY><BUYTYPE>BUY </BUYSTOCK><BUYSTOCK><INVBUY><INVTRAN><FITID>0123456789023501220120820<DTTRADE>20120820000000.000[-4:EDT]<MEMO>REINVESTMENT</INVTRAN><SECID><UNIQUEID>98417P105<UNIQUEIDTYPE>CUSIP</SECID><UNITS>+0000000000004.90900<UNITPRICE>000000002.947400000<COMMISSION>+00000000000000.0000<FEES>+00000000000000.0000<TOTAL>-00000000000014.4700<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY><SUBACCTSEC>CASH<SUBACCTFUND>CASH</INVBUY><BUYTYPE>BUY </BUYSTOCK><BUYSTOCK><INVBUY><INVTRAN><FITID>0123456789024401120120831<DTTRADE>20120831000000.000[-4:EDT]<MEMO>REINVESTMENT</INVTRAN><SECID><UNIQUEID>19421R200<UNIQUEIDTYPE>CUSIP</SECID><UNITS>+0000000000001.57300<UNITPRICE>000000014.257000000<COMMISSION>+00000000000000.0000<FEES>+00000000000000.0000<TOTAL>-00000000000022.4300<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY><SUBACCTSEC>CASH<SUBACCTFUND>CASH</INVBUY><BUYTYPE>BUY </BUYSTOCK><BUYSTOCK><INVBUY><INVTRAN><FITID>0123456789024801120120901<DTTRADE>20120901000000.000[-4:EDT]<MEMO>REINVESTMENT</INVTRAN><SECID><UNIQUEID>458140100<UNIQUEIDTYPE>CUSIP</SECID><UNITS>+0000000000000.91100<UNITPRICE>000000024.705500000<COMMISSION>+00000000000000.0000<FEES>+00000000000000.0000<TOTAL>-00000000000022.5000<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY><SUBACCTSEC>CASH<SUBACCTFUND>CASH</INVBUY><BUYTYPE>BUY </BUYSTOCK><INCOME> <INVTRAN><FITID>0123456789021301520120731<DTTRADE>20120731000000.000[-4:EDT]<MEMO>DIVIDEND RECEIVED</INVTRAN><SECID><UNIQUEID>78462F103<UNIQUEIDTYPE>CUSIP</SECID><INCOMETYPE>DIV<TOTAL>+00000000000005.5300<SUBACCTSEC>CASH<SUBACCTFUND>CASH<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY> </INCOME><INCOME> <INVTRAN><FITID>0123456789023501320120820<DTTRADE>20120820000000.000[-4:EDT]<MEMO>DIVIDEND RECEIVED</INVTRAN><SECID><UNIQUEID>98417P105<UNIQUEIDTYPE>CUSIP</SECID><INCOMETYPE>DIV<TOTAL>+00000000000015.4400<SUBACCTSEC>CASH<SUBACCTFUND>CASH<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY> </INCOME><INCOME> <INVTRAN><FITID>0123456789024401220120831<DTTRADE>20120831000000.000[-4:EDT]<MEMO>DIVIDEND RECEIVED</INVTRAN><SECID><UNIQUEID>19421R200<UNIQUEIDTYPE>CUSIP</SECID><INCOMETYPE>DIV<TOTAL>+00000000000022.4300<SUBACCTSEC>CASH<SUBACCTFUND>CASH<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY> </INCOME><INCOME> <INVTRAN><FITID>0123456789024801220120901<DTTRADE>20120901000000.000[-4:EDT]<MEMO>DIVIDEND RECEIVED</INVTRAN><SECID><UNIQUEID>458140100<UNIQUEIDTYPE>CUSIP</SECID><INCOMETYPE>DIV<TOTAL>+00000000000022.5000<SUBACCTSEC>CASH<SUBACCTFUND>CASH<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY> </INCOME><SELLSTOCK><INVSELL><INVTRAN><FITID>0123456789020901320120727<DTTRADE>20120727000000.000[-4:EDT]<MEMO>YOU SOLD</INVTRAN><SECID><UNIQUEID>78462F103<UNIQUEIDTYPE>CUSIP</SECID><UNITS>-0000000000008.00000<UNITPRICE>000000137.160000000<COMMISSION>+00000000000007.9500<FEES>+00000000000000.0000<TOTAL>+00000000001089.3000<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY><SUBACCTSEC>CASH<SUBACCTFUND>CASH</INVSELL><SELLTYPE>SELL </SELLSTOCK><SELLSTOCK><INVSELL><INVTRAN><FITID>0123456789021401420120801<DTTRADE>20120801000000.000[-4:EDT]<MEMO>IN LIEU OF FRX SHARE</INVTRAN><SECID><UNIQUEID>78462F103<UNIQUEIDTYPE>CUSIP</SECID><UNITS>-0000000000000.03500<UNITPRICE>000000137.142857143<COMMISSION>+00000000000000.0000<FEES>+00000000000000.0000<TOTAL>+00000000000004.8000<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY><SUBACCTSEC>CASH<SUBACCTFUND>CASH</INVSELL><SELLTYPE>SELL </SELLSTOCK><INVBANKTRAN> <STMTTRN><TRNTYPE>DEP<DTPOSTED>20120731000000.000[-4:EDT]<TRNAMT>+00000000000000.2400<FITID>0123456789021301320120731<NAME>INTEREST EARNED<MEMO>INTEREST EARNED<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY> </STMTTRN><SUBACCTFUND>CASH </INVBANKTRAN><INVBANKTRAN> <STMTTRN><TRNTYPE>OTHER<DTPOSTED>20120820000000.000[-4:EDT]<TRNAMT>-00000000000000.9700<FITID>0123456789023501120120820<NAME>LATE SETTLEMENT FEE<MEMO>LATE SETTLEMENT FEE<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY> </STMTTRN><SUBACCTFUND>CASH </INVBANKTRAN><INVBANKTRAN> <STMTTRN><TRNTYPE>DEP<DTPOSTED>20120831000000.000[-4:EDT]<TRNAMT>+00000000000000.1600<FITID>0123456789024401420120831<NAME>INTEREST EARNED<MEMO>INTEREST EARNED<CURRENCY><CURRATE>1.00<CURSYM>USD</CURRENCY> </STMTTRN><SUBACCTFUND>CASH </INVBANKTRAN></INVTRANLIST><INVPOSLIST><POSSTOCK><INVPOS><SECID><UNIQUEID>G7945E105<UNIQUEIDTYPE>CUSIP</SECID><HELDINACCT>CASH<POSTYPE>LONG<UNITS>128.00000<UNITPRICE>40.8700000<MKTVAL>+00000005231.36<DTPRICEASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.0<CURSYM>USD</CURRENCY></INVPOS></POSSTOCK><POSSTOCK><INVPOS><SECID><UNIQUEID>19421R200<UNIQUEIDTYPE>CUSIP</SECID><HELDINACCT>CASH<POSTYPE>LONG<UNITS>70.57300<UNITPRICE>14.3200000<MKTVAL>+00000001010.60<DTPRICEASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.0<CURSYM>USD</CURRENCY></INVPOS></POSSTOCK><POSSTOCK><INVPOS><SECID><UNIQUEID>431571108<UNIQUEIDTYPE>CUSIP</SECID><HELDINACCT>CASH<POSTYPE>LONG<UNITS>115.00000<UNITPRICE>18.9300000<MKTVAL>+00000002176.95<DTPRICEASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.0<CURSYM>USD</CURRENCY></INVPOS></POSSTOCK><POSSTOCK><INVPOS><SECID><UNIQUEID>458140100<UNIQUEIDTYPE>CUSIP</SECID><HELDINACCT>CASH<POSTYPE>LONG<UNITS>100.91100<UNITPRICE>24.1900000<MKTVAL>+00000002441.03<DTPRICEASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.0<CURSYM>USD</CURRENCY></INVPOS></POSSTOCK><POSSTOCK><INVPOS><SECID><UNIQUEID>756577102<UNIQUEIDTYPE>CUSIP</SECID><HELDINACCT>CASH<POSTYPE>LONG<UNITS>50.00000<UNITPRICE>59.1500000<MKTVAL>+00000002957.50<DTPRICEASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.0<CURSYM>USD</CURRENCY></INVPOS></POSSTOCK><POSSTOCK><INVPOS><SECID><UNIQUEID>98417P105<UNIQUEIDTYPE>CUSIP</SECID><HELDINACCT>CASH<POSTYPE>LONG<UNITS>390.90900<UNITPRICE>2.8200000<MKTVAL>+00000001102.36<DTPRICEASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.0<CURSYM>USD</CURRENCY></INVPOS></POSSTOCK></INVPOSLIST><INVBAL><AVAILCASH>18073.98<MARGINBALANCE>+00000000000.00<SHORTBALANCE>+00000000000.00<BUYPOWER>+00000000000.00<BALLIST><BAL><NAME>Networth<DESC>The net market value of all long and short positions in the account<BALTYPE>DOLLAR<VALUE>32993.79<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Margin Equity<DESC>The margin market value less any margin debit balance<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Margin Equity Percentage<DESC>Margin equity / market value of long and short positions<BALTYPE>PERCENT<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Cash Debit Balance<DESC>Cash Debit Balance<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Total Money Markets<DESC>The total value of all money market positions in the cash account<BALTYPE>DOLLAR<VALUE>18073.98<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>House Surplus<DESC>Equity amount above house requirements<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>NYSE Surplus<DESC>Equity amount above exchange requirements<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Federal Surplus<DESC>Amount above federal requirements<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Buying Power - Equities<DESC>Amount of equities you can buy on margin without generating a margin call<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Buying Power - Municipal Bonds<DESC>Amount of municipal bonds you can buy on margin without generating a margin call<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Buying Power - Government Bonds<DESC>Amount of government bonds you can buy on margin without generating a margin cal<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Buying Power - Corporate Bonds<DESC>Amount you can buy of corporate bonds on margin with no margin call<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Option Market Value<DESC>The market value of all options in the account<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Option In The Money Amount<DESC>The in-the-money amount on covered options<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Cash Market value<DESC>Total value of all cash account positions<BALTYPE>DOLLAR<VALUE>14919.8<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Margin Market Value<DESC>Total value of positions in margin less in-the-money amount of covered options<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Short Market Value<DESC>Total value of short positions less in-the-money amount of covered put options<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL><BAL><NAME>Available to Borrow<DESC>Cash amount that can be borrowed without generating a margin call<BALTYPE>DOLLAR<VALUE>0.0<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD</CURRENCY></BAL></BALLIST></INVBAL></INVSTMTRS></INVSTMTTRNRS></INVSTMTMSGSRSV1><SECLISTMSGSRSV1><SECLIST><STOCKINFO><SECINFO><SECID><UNIQUEID>G7945E105<UNIQUEIDTYPE>CUSIP</SECID><SECNAME>SEADRILL LTD USD2<TICKER>SDRL<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD </CURRENCY></SECINFO><STOCKTYPE>COMMON<DTYIELDASOF>20120908033034.000[-4:EDT]</STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>19421R200<UNIQUEIDTYPE>CUSIP</SECID><SECNAME>COLLECTORS UNIVERSE INC<TICKER>CLCT<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD </CURRENCY></SECINFO><STOCKTYPE>COMMON<DTYIELDASOF>20120908033034.000[-4:EDT]</STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>431571108<UNIQUEIDTYPE>CUSIP</SECID><SECNAME>HILLENBRAND INC COM<TICKER>HI<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD </CURRENCY></SECINFO><STOCKTYPE>COMMON<DTYIELDASOF>20120908033034.000[-4:EDT]</STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>458140100<UNIQUEIDTYPE>CUSIP</SECID><SECNAME>INTEL CORP<TICKER>INTC<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD </CURRENCY></SECINFO><STOCKTYPE>COMMON<DTYIELDASOF>20120908033034.000[-4:EDT]</STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>756577102<UNIQUEIDTYPE>CUSIP</SECID><SECNAME>RED HAT INC<TICKER>RHT<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD </CURRENCY></SECINFO><STOCKTYPE>COMMON<DTYIELDASOF>20120908033034.000[-4:EDT]</STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>98417P105<UNIQUEIDTYPE>CUSIP</SECID><SECNAME>XINYUAN REAL ESTATE ADR EACH REPR 2 ORD SHS<TICKER>XIN<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD </CURRENCY></SECINFO><STOCKTYPE>COMMON<DTYIELDASOF>20120908033034.000[-4:EDT]</STOCKINFO><STOCKINFO><SECINFO><SECID><UNIQUEID>78462F103<UNIQUEIDTYPE>CUSIP</SECID><SECNAME>SPDR S&P 500 ETF TRUST UNIT SER 1 S&P<TICKER>SPY<DTASOF>20120908033034.000[-4:EDT]<CURRENCY><CURRATE>1.000<CURSYM>USD </CURRENCY></SECINFO><STOCKTYPE>COMMON<DTYIELDASOF>20120908033034.000[-4:EDT]</STOCKINFO></SECLIST></SECLISTMSGSRSV1></OFX> diff --git a/tests/fixtures/investment_medium.ofx b/tests/fixtures/investment_medium.ofx new file mode 100644 index 0000000..87f6eba --- /dev/null +++ b/tests/fixtures/investment_medium.ofx @@ -0,0 +1,119 @@ +OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE +<OFX> + <SIGNONMSGSRSV1> + <SONRS> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + </STATUS> + <DTSERVER>20091217162416.000[-:EST]</DTSERVER> + <LANGUAGE>ENG</LANGUAGE> + <FI> + <ORG>REDACTEDINC-US</ORG> + <FID>1234</FID> + </FI> + </SONRS> + </SIGNONMSGSRSV1> + <INVSTMTMSGSRSV1> + <INVSTMTTRNRS> + <TRNUID>0</TRNUID> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + </STATUS> + <INVSTMTRS> + <DTASOF>20091215202000.000[-4:EST]</DTASOF> + <CURDEF>CAD</CURDEF> + <INVACCTFROM> + <BROKERID>404</BROKERID> + <ACCTID>ABC123</ACCTID> + </INVACCTFROM> + <INVTRANLIST> + <DTSTART>20091214202000.000[-5:EST]</DTSTART> + <DTEND>20091215202000.000[-5:EST]</DTEND> + <INVBANKTRAN> + <STMTTRN> + <TRNTYPE>DEBIT</TRNTYPE> + <DTPOSTED>20091215202000.000[-4:EST]</DTPOSTED> + <TRNAMT>-3.65</TRNAMT> + <FITID>20091215.U489357.e.USD.1510480481</FITID> + <MEMO>CASH TRADE: AUD.USD</MEMO> + <CURRENCY> + <CURRATE>1.06</CURRATE> + <CURSYM>USD</CURSYM> + </CURRENCY> + </STMTTRN> + <SUBACCTFUND>CASH</SUBACCTFUND> + </INVBANKTRAN> + <INVBANKTRAN> + <STMTTRN> + <TRNTYPE>CREDIT</TRNTYPE> + <DTPOSTED>20091215202000.000[-4:EST]</DTPOSTED> + <TRNAMT>3.35</TRNAMT> + <FITID>20091215.U489357.e.USD.1510982018</FITID> + <MEMO>CASH TRADE: AUD.USD</MEMO> + <CURRENCY> + <CURRATE>1.06</CURRATE> + <CURSYM>USD</CURSYM> + </CURRENCY> + </STMTTRN> + <SUBACCTFUND>CASH</SUBACCTFUND> + </INVBANKTRAN> + <INVBANKTRAN> + <STMTTRN> + <TRNTYPE>DEBIT</TRNTYPE> + <DTPOSTED>20091215202000.000[-4:EST]</DTPOSTED> + <TRNAMT>-3.65</TRNAMT> + <FITID>20091215.U489357.e.USD.1511863617</FITID> + <MEMO>CASH TRADE: AUD.USD</MEMO> + <CURRENCY> + <CURRATE>1.06</CURRATE> + <CURSYM>USD</CURSYM> + </CURRENCY> + </STMTTRN> + <SUBACCTFUND>CASH</SUBACCTFUND> + </INVBANKTRAN> + </INVTRANLIST> + <INVBAL> + <AVAILCASH>1.00</AVAILCASH> + <MARGINBALANCE>0</MARGINBALANCE> + <SHORTBALANCE>0</SHORTBALANCE> + <BALLIST> + <BAL> + <NAME>ENDING CASH</NAME> + <DESC>ENDING CASH BALANCE</DESC> + <BALTYPE>NUMBER</BALTYPE> + <VALUE>2.00</VALUE> + </BAL> + <BAL> + <NAME>STOCK VALUE</NAME> + <DESC>TOTAL EQUITY IN STOCKS</DESC> + <BALTYPE>NUMBER</BALTYPE> + <VALUE>0.00</VALUE> + </BAL> + <BAL> + <NAME>OPTION VALUE</NAME> + <DESC>TOTAL EQUITY IN OPTIONS</DESC> + <BALTYPE>NUMBER</BALTYPE> + <VALUE>0.00</VALUE> + </BAL> + <BAL> + <NAME>IBGROUPNOTES VALUE</NAME> + <DESC>TOTAL EQUITY IN IBGROUPNOTES</DESC> + <BALTYPE>NUMBER</BALTYPE> + <VALUE>0.00</VALUE> + </BAL> + </BALLIST> + </INVBAL> + </INVSTMTRS> + </INVSTMTTRNRS> + </INVSTMTMSGSRSV1> +</OFX> diff --git a/tests/fixtures/multiple_accounts.ofx b/tests/fixtures/multiple_accounts.ofx new file mode 100644 index 0000000..853c788 --- /dev/null +++ b/tests/fixtures/multiple_accounts.ofx @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?OFX OFXHEADER="200" VERSION="211" SECURITY="NONE" OLDFILEUID="NONE" NEWFILEUID="NONE"?> +<OFX> + <SIGNONMSGSRSV1> + <SONRS> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + <MESSAGE>The operation succeeded.</MESSAGE> + </STATUS> + <DTSERVER>20120603203135.547[-7:PDT]</DTSERVER> + <LANGUAGE>ENG</LANGUAGE> + <FI> + <ORG>blah</ORG> + <FID>1000</FID> + </FI> + </SONRS> + </SIGNONMSGSRSV1> + <BANKMSGSRSV1> + <STMTTRNRS> + <TRNUID>1001</TRNUID> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + </STATUS> + <STMTRS> + <CURDEF>USD</CURDEF> + <BANKACCTFROM> + <BANKID>123</BANKID> + <BRANCHID>00</BRANCHID> + <ACCTID>9100</ACCTID> + <ACCTTYPE>CHECKING</ACCTTYPE> + </BANKACCTFROM> + <LEDGERBAL> + <BALAMT>111</BALAMT> + <DTASOF>20120603133220.000[-7:PDT]</DTASOF> + </LEDGERBAL> + </STMTRS> + </STMTTRNRS> + <STMTTRNRS> + <TRNUID>1002</TRNUID> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + </STATUS> + <STMTRS> + <CURDEF>USD</CURDEF> + <BANKACCTFROM> + <BANKID>123</BANKID> + <BRANCHID>00</BRANCHID> + <ACCTID>9200</ACCTID> + <ACCTTYPE>SAVINGS</ACCTTYPE> + </BANKACCTFROM> + <LEDGERBAL> + <BALAMT>222</BALAMT> + <DTASOF>20120603133220.000[-7:PDT]</DTASOF> + </LEDGERBAL> + </STMTRS> + </STMTTRNRS> + </BANKMSGSRSV1> +</OFX> diff --git a/tests/fixtures/multiple_accounts2.ofx b/tests/fixtures/multiple_accounts2.ofx new file mode 100644 index 0000000..853c788 --- /dev/null +++ b/tests/fixtures/multiple_accounts2.ofx @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?OFX OFXHEADER="200" VERSION="211" SECURITY="NONE" OLDFILEUID="NONE" NEWFILEUID="NONE"?> +<OFX> + <SIGNONMSGSRSV1> + <SONRS> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + <MESSAGE>The operation succeeded.</MESSAGE> + </STATUS> + <DTSERVER>20120603203135.547[-7:PDT]</DTSERVER> + <LANGUAGE>ENG</LANGUAGE> + <FI> + <ORG>blah</ORG> + <FID>1000</FID> + </FI> + </SONRS> + </SIGNONMSGSRSV1> + <BANKMSGSRSV1> + <STMTTRNRS> + <TRNUID>1001</TRNUID> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + </STATUS> + <STMTRS> + <CURDEF>USD</CURDEF> + <BANKACCTFROM> + <BANKID>123</BANKID> + <BRANCHID>00</BRANCHID> + <ACCTID>9100</ACCTID> + <ACCTTYPE>CHECKING</ACCTTYPE> + </BANKACCTFROM> + <LEDGERBAL> + <BALAMT>111</BALAMT> + <DTASOF>20120603133220.000[-7:PDT]</DTASOF> + </LEDGERBAL> + </STMTRS> + </STMTTRNRS> + <STMTTRNRS> + <TRNUID>1002</TRNUID> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + </STATUS> + <STMTRS> + <CURDEF>USD</CURDEF> + <BANKACCTFROM> + <BANKID>123</BANKID> + <BRANCHID>00</BRANCHID> + <ACCTID>9200</ACCTID> + <ACCTTYPE>SAVINGS</ACCTTYPE> + </BANKACCTFROM> + <LEDGERBAL> + <BALAMT>222</BALAMT> + <DTASOF>20120603133220.000[-7:PDT]</DTASOF> + </LEDGERBAL> + </STMTRS> + </STMTTRNRS> + </BANKMSGSRSV1> +</OFX> diff --git a/tests/fixtures/signon_fail.ofx b/tests/fixtures/signon_fail.ofx new file mode 100644 index 0000000..d55ef86 --- /dev/null +++ b/tests/fixtures/signon_fail.ofx @@ -0,0 +1,11 @@ +OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:0e1a88e56fc548a1ba2e83bce6323bb6 + +<OFX><SIGNONMSGSRSV1><SONRS><STATUS><CODE>15500<SEVERITY>ERROR<MESSAGE>Your request could not be processed because you supplied an invalid identification code or your password was incorrect</STATUS><DTSERVER>20130325211209.350[-7:MST]<LANGUAGE>ENG<FI><ORG>AMEX<FID>3101</FI><START.TIME>20130325211209<ERROR.CODE>15500</SONRS></SIGNONMSGSRSV1></OFX> diff --git a/tests/fixtures/signon_success.ofx b/tests/fixtures/signon_success.ofx new file mode 100644 index 0000000..82f34a3 --- /dev/null +++ b/tests/fixtures/signon_success.ofx @@ -0,0 +1,11 @@ +OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:3bb6707632b64da196722ef312e6376d + +<OFX><SIGNONMSGSRSV1><SONRS><STATUS><CODE>0<SEVERITY>INFO<MESSAGE>Login successful</STATUS><DTSERVER>20130325211405.187[-7:MST]<LANGUAGE>ENG<FI><ORG>AMEX<FID>3101</FI><START.TIME>20130325211405<ORIGIN.ID>FMPWeb</SONRS></SIGNONMSGSRSV1></OFX> diff --git a/tests/fixtures/signon_success_no_message.ofx b/tests/fixtures/signon_success_no_message.ofx new file mode 100644 index 0000000..636bffa --- /dev/null +++ b/tests/fixtures/signon_success_no_message.ofx @@ -0,0 +1,11 @@ +OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:3bb6707632b64da196722ef312e6376d + +<OFX><SIGNONMSGSRSV1><SONRS><STATUS><CODE>0<SEVERITY>INFO</STATUS><DTSERVER>20130325211405.187[-7:MST]<LANGUAGE>ENG<FI><ORG>AMEX<FID>3101</FI><START.TIME>20130325211405<ORIGIN.ID>FMPWeb</SONRS></SIGNONMSGSRSV1></OFX> diff --git a/tests/fixtures/vanguard.ofx b/tests/fixtures/vanguard.ofx new file mode 100644 index 0000000..47f42a0 --- /dev/null +++ b/tests/fixtures/vanguard.ofx @@ -0,0 +1,11 @@ +OFXHEADER:100
+DATA:OFXSGML
+VERSION:102
+SECURITY:NONE
+ENCODING:USASCII
+CHARSET:1252
+COMPRESSION:NONE
+OLDFILEUID:NONE
+NEWFILEUID:a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0
+
+<OFX><SIGNONMSGSRSV1><SONRS><STATUS><CODE>0<SEVERITY>INFO<MESSAGE>Successful Sign On</STATUS><DTSERVER>20110727001702[-5:EST]<LANGUAGE>ENG<DTPROFUP>20010918083000<FI><ORG>The Vanguard Group</FI></SONRS></SIGNONMSGSRSV1><INVSTMTMSGSRSV1><INVSTMTTRNRS><TRNUID>a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0<STATUS><CODE>0<SEVERITY>INFO</STATUS><CLTCOOKIE>4<INVSTMTRS><DTASOF>20110727<CURDEF>USD<INVACCTFROM><BROKERID>vanguard.com<ACCTID>01234567890</INVACCTFROM><INVTRANLIST><DTSTART>20110625160000.000[-5:EST]<DTEND>20110727160000.000[-5:EST]<SELLMF><INVSELL><INVTRAN><FITID>01234567890.0123.07152011.0<DTTRADE>20110715160000.000[-5:EST]<DTSETTLE>20110715160000.000[-5:EST]<MEMO>THIS IS A MEMO</INVTRAN><SECID><UNIQUEID>012345678<UNIQUEIDTYPE>CUSIP</SECID><UNITS>-42.123<UNITPRICE>100.00<TOTAL>4212.3<SUBACCTSEC>CASH<SUBACCTFUND>OTHER</INVSELL><SELLTYPE>SELL</SELLMF></INVTRANLIST><INVPOSLIST><POSMF><INVPOS><SECID><UNIQUEID>012345678<UNIQUEIDTYPE>CUSIP</SECID><HELDINACCT>OTHER<POSTYPE>LONG<UNITS>102.0<UNITPRICE>100.00<MKTVAL>10200.0<DTPRICEASOF>20110726160000.000[-5:EST]<MEMO>Price as of date based on closing price</INVPOS><REINVDIV>Y<REINVCG>Y</POSMF><POSMF><INVPOS><SECID><UNIQUEID>012345678<UNIQUEIDTYPE>CUSIP</SECID><HELDINACCT>OTHER<POSTYPE>LONG<UNITS>142.2<UNITPRICE>100.42<MKTVAL>14279.72<DTPRICEASOF>20110726160000.000[-5:EST]<MEMO>Price as of date based on closing price</INVPOS><REINVDIV>Y<REINVCG>Y</POSMF></INVPOSLIST></INVSTMTRS></INVSTMTTRNRS></INVSTMTMSGSRSV1><SECLISTMSGSRSV1><SECLIST><MFINFO><SECINFO><SECID><UNIQUEID>012345678<UNIQUEIDTYPE>CUSIP</SECID><SECNAME>Name of the security<TICKER>VFINX<FIID>0122<UNITPRICE>54.0<MEMO>Price as of date based on closing price</SECINFO><MFTYPE>OPENEND</MFINFO><MFINFO><SECINFO><SECID><UNIQUEID>012345678<UNIQUEIDTYPE>CUSIP</SECID><SECNAME>Name of share<TICKER>VFIAX<FIID>0123<UNITPRICE>123.45<MEMO>Price as of date based on closing price</SECINFO><MFTYPE>OPENEND</MFINFO></SECLIST></SECLISTMSGSRSV1></OFX>
\ No newline at end of file diff --git a/tests/support.py b/tests/support.py new file mode 100644 index 0000000..fe381f5 --- /dev/null +++ b/tests/support.py @@ -0,0 +1,9 @@ +import os + + +def open_file(filename): + ''' Load a file from the fixtures directory. ''' + path = 'fixtures/' + filename + if ('tests' in os.listdir('.')): + path = 'tests/' + path + return open(path, mode='rb') diff --git a/tests/test_parse.py b/tests/test_parse.py new file mode 100644 index 0000000..35260ce --- /dev/null +++ b/tests/test_parse.py @@ -0,0 +1,623 @@ +from __future__ import absolute_import + +from ofxparse.ofxparse import soup_maker +from datetime import datetime, timedelta +from decimal import Decimal +from unittest import TestCase +import sys +sys.path.append('..') + +import six + +from .support import open_file +from ofxparse import OfxParser, AccountType, Account, Statement, Transaction +from ofxparse.ofxparse import OfxFile, OfxPreprocessedFile, OfxParserException + +class TestOfxPreprocessedFile(TestCase): + + def testPreprocess(self): + fh = six.BytesIO(six.b("""OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE + +<OFX><DTASOF><![CDATA[></tricky]]><LEAVE ALONE><VAL.UE>a<VAL_UE>b<TE_ST></TE_ST><TE.ST></TE.ST><INVBAL><BALLIST><BAL><NAME>Net<DTASOF>2222</BAL><BAL><NAME>Gross<DTASOF>3333</BAL></BALLIST></INVBAL></OFX> +""")) + expect = """OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE + +<OFX><DTASOF><![CDATA[></tricky]]><LEAVE ALONE></DTASOF><VAL.UE>a</VAL.UE><VAL_UE>b</VAL_UE><TE_ST></TE_ST><TE.ST></TE.ST><INVBAL><BALLIST><BAL><NAME>Net</NAME><DTASOF>2222</DTASOF></BAL><BAL><NAME>Gross</NAME><DTASOF>3333</DTASOF></BAL></BALLIST></INVBAL></OFX> +""" + ofx_file = OfxPreprocessedFile(fh) + data = ofx_file.fh.read() + self.assertEqual(data,expect) + + def testHeaders(self): + expect = {"OFXHEADER": six.u("100"), + "DATA": six.u("OFXSGML"), + "VERSION": six.u("102"), + "SECURITY": None, + "ENCODING": six.u("USASCII"), + "CHARSET": six.u("1252"), + "COMPRESSION": None, + "OLDFILEUID": None, + "NEWFILEUID": None, + } + ofx = OfxParser.parse(open_file('bank_medium.ofx')) + self.assertEquals(expect, ofx.headers) + + def testUTF8(self): + fh = six.BytesIO(six.b("""OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:UNICODE +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE + +""")) + ofx_file = OfxPreprocessedFile(fh) + headers = ofx_file.headers + data = ofx_file.fh.read() + + self.assertTrue(type(data) is six.text_type) + for key, value in six.iteritems(headers): + self.assertTrue(type(key) is six.text_type) + self.assertTrue(type(value) is not six.binary_type) + + def testCP1252(self): + fh = six.BytesIO(six.b("""OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET: 1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE +""")) + ofx_file = OfxPreprocessedFile(fh) + headers = ofx_file.headers + result = ofx_file.fh.read() + + self.assertTrue(type(result) is six.text_type) + for key, value in six.iteritems(headers): + self.assertTrue(type(key) is six.text_type) + self.assertTrue(type(value) is not six.binary_type) + + def testUTF8Japanese(self): + fh = six.BytesIO(six.b("""OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:UTF-8 +CHARSET:CSUNICODE +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE +""")) + ofx_file = OfxPreprocessedFile(fh) + headers = ofx_file.headers + result = ofx_file.fh.read() + + self.assertTrue(type(result) is six.text_type) + for key, value in six.iteritems(headers): + self.assertTrue(type(key) is six.text_type) + self.assertTrue(type(value) is not six.binary_type) + + def testBrokenLineEndings(self): + fh = six.BytesIO(six.b("OFXHEADER:100\rDATA:OFXSGML\r")) + ofx_file = OfxPreprocessedFile(fh) + self.assertEquals(len(ofx_file.headers.keys()), 2) + + + +class TestOfxFile(TestCase): + def testHeaders(self): + expect = {"OFXHEADER": six.u("100"), + "DATA": six.u("OFXSGML"), + "VERSION": six.u("102"), + "SECURITY": None, + "ENCODING": six.u("USASCII"), + "CHARSET": six.u("1252"), + "COMPRESSION": None, + "OLDFILEUID": None, + "NEWFILEUID": None, + } + ofx = OfxParser.parse(open_file('bank_medium.ofx')) + self.assertEquals(expect, ofx.headers) + + def testUTF8(self): + fh = six.BytesIO(six.b("""OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:UNICODE +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE + +""")) + ofx_file = OfxFile(fh) + headers = ofx_file.headers + data = ofx_file.fh.read() + + self.assertTrue(type(data) is six.text_type) + for key, value in six.iteritems(headers): + self.assertTrue(type(key) is six.text_type) + self.assertTrue(type(value) is not six.binary_type) + + def testCP1252(self): + fh = six.BytesIO(six.b("""OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET: 1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE +""")) + ofx_file = OfxFile(fh) + headers = ofx_file.headers + result = ofx_file.fh.read() + + self.assertTrue(type(result) is six.text_type) + for key, value in six.iteritems(headers): + self.assertTrue(type(key) is six.text_type) + self.assertTrue(type(value) is not six.binary_type) + + def testUTF8Japanese(self): + fh = six.BytesIO(six.b("""OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:UTF-8 +CHARSET:CSUNICODE +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE +""")) + ofx_file = OfxFile(fh) + headers = ofx_file.headers + result = ofx_file.fh.read() + + self.assertTrue(type(result) is six.text_type) + for key, value in six.iteritems(headers): + self.assertTrue(type(key) is six.text_type) + self.assertTrue(type(value) is not six.binary_type) + + def testBrokenLineEndings(self): + fh = six.BytesIO(six.b("OFXHEADER:100\rDATA:OFXSGML\r")) + ofx_file = OfxFile(fh) + self.assertEquals(len(ofx_file.headers.keys()), 2) + + +class TestParse(TestCase): + def testEmptyFile(self): + fh = six.BytesIO(six.b("")) + self.assertRaises(OfxParserException, OfxParser.parse, fh) + + def testThatParseWorksWithoutErrors(self): + OfxParser.parse(open_file('bank_medium.ofx')) + + def testThatParseFailsIfNothingToParse(self): + self.assertRaises(TypeError, OfxParser.parse, None) + + def testThatParseFailsIfAPathIsPassedIn(self): + # A file handle should be passed in, not the path. + self.assertRaises(RuntimeError, OfxParser.parse, '/foo/bar') + + def testThatParseReturnsAResultWithABankAccount(self): + ofx = OfxParser.parse(open_file('bank_medium.ofx')) + self.assertTrue(ofx.account is not None) + + def testEverything(self): + ofx = OfxParser.parse(open_file('bank_medium.ofx')) + self.assertEquals('12300 000012345678', ofx.account.number) + self.assertEquals('160000100', ofx.account.routing_number) + self.assertEquals('00', ofx.account.branch_id) + self.assertEquals('CHECKING', ofx.account.account_type) + self.assertEquals(Decimal('382.34'), ofx.account.statement.balance) + # Todo: support values in decimal or int form. + # self.assertEquals('15', + # ofx.bank_account.statement.balance_in_pennies) + self.assertEquals( + Decimal('682.34'), ofx.account.statement.available_balance) + self.assertEquals( + datetime(2009, 4, 1), ofx.account.statement.start_date) + self.assertEquals( + datetime(2009, 5, 23, 12, 20, 17), ofx.account.statement.end_date) + + self.assertEquals(3, len(ofx.account.statement.transactions)) + + transaction = ofx.account.statement.transactions[0] + self.assertEquals("MCDONALD'S #112", transaction.payee) + self.assertEquals('pos', transaction.type) + self.assertEquals(Decimal('-6.60'), transaction.amount) + # Todo: support values in decimal or int form. + # self.assertEquals('15', transaction.amount_in_pennies) + + def testMultipleAccounts(self): + ofx = OfxParser.parse(open_file('multiple_accounts2.ofx')) + self.assertEquals(2, len(ofx.accounts)) + self.assertEquals('9100', ofx.accounts[0].number) + self.assertEquals('9200', ofx.accounts[1].number) + self.assertEquals('123', ofx.accounts[0].routing_number) + self.assertEquals('123', ofx.accounts[1].routing_number) + self.assertEquals('CHECKING', ofx.accounts[0].account_type) + self.assertEquals('SAVINGS', ofx.accounts[1].account_type) + + +class TestStringToDate(TestCase): + ''' Test the string to date parser ''' + def test_bad_format(self): + ''' A poorly formatted string should throw a ValueError ''' + + bad_string = 'abcdLOL!' + self.assertRaises(ValueError, OfxParser.parseOfxDateTime, bad_string) + + bad_but_close_string = '881103' + self.assertRaises(ValueError, OfxParser.parseOfxDateTime, bad_string) + + no_month_string = '19881301' + self.assertRaises(ValueError, OfxParser.parseOfxDateTime, bad_string) + + def test_parses_correct_time(self): + '''Test whether it can parse correct time for some valid time fields''' + self.assertEquals(OfxParser.parseOfxDateTime('19881201'), + datetime(1988, 12, 1, 0, 0)) + self.assertEquals(OfxParser.parseOfxDateTime('19881201230100'), + datetime(1988, 12, 1, 23, 1)) + self.assertEquals(OfxParser.parseOfxDateTime('20120229230100'), + datetime(2012, 2, 29, 23, 1)) + + def test_parses_time_offset(self): + ''' Test that we handle GMT offset ''' + self.assertEquals(OfxParser.parseOfxDateTime('20001201120000 [0:GMT]'), + datetime(2000, 12, 1, 12, 0)) + self.assertEquals(OfxParser.parseOfxDateTime('19991201120000 [1:ITT]'), + datetime(1999, 12, 1, 11, 0)) + self.assertEquals( + OfxParser.parseOfxDateTime('19881201230100 [-5:EST]'), + datetime(1988, 12, 2, 4, 1)) + self.assertEquals( + OfxParser.parseOfxDateTime('20120229230100 [-6:CAT]'), + datetime(2012, 3, 1, 5, 1)) + self.assertEquals( + OfxParser.parseOfxDateTime('20120412120000 [-5.5:XXX]'), + datetime(2012, 4, 12, 17, 30)) + self.assertEquals( + OfxParser.parseOfxDateTime('20120412120000 [-5:XXX]'), + datetime(2012, 4, 12, 17)) + self.assertEquals( + OfxParser.parseOfxDateTime('20120922230000 [+9:JST]'), + datetime(2012, 9, 22, 14, 0)) + + +class TestParseStmtrs(TestCase): + input = ''' +<STMTRS><CURDEF>CAD<BANKACCTFROM><BANKID>160000100<ACCTID>12300 000012345678<ACCTTYPE>CHECKING</BANKACCTFROM> +<BANKTRANLIST><DTSTART>20090401<DTEND>20090523122017 +<STMTTRN><TRNTYPE>POS<DTPOSTED>20090401122017.000[-5:EST]<TRNAMT>-6.60<FITID>0000123456782009040100001<NAME>MCDONALD'S #112<MEMO>POS MERCHANDISE;MCDONALD'S #112</STMTTRN> +</BANKTRANLIST><LEDGERBAL><BALAMT>382.34<DTASOF>20090523122017</LEDGERBAL><AVAILBAL><BALAMT>682.34<DTASOF>20090523122017</AVAILBAL></STMTRS> + ''' + + def testThatParseStmtrsReturnsAnAccount(self): + stmtrs = soup_maker(self.input) + account = OfxParser.parseStmtrs( + stmtrs.find('stmtrs'), AccountType.Bank)[0] + self.assertEquals('12300 000012345678', account.number) + self.assertEquals('160000100', account.routing_number) + self.assertEquals('CHECKING', account.account_type) + + def testThatReturnedAccountAlsoHasAStatement(self): + stmtrs = soup_maker(self.input) + account = OfxParser.parseStmtrs( + stmtrs.find('stmtrs'), AccountType.Bank)[0] + self.assertTrue(hasattr(account, 'statement')) + + +class TestAccount(TestCase): + def testThatANewAccountIsValid(self): + account = Account() + self.assertEquals('', account.number) + self.assertEquals('', account.routing_number) + self.assertEquals('', account.account_type) + self.assertEquals(None, account.statement) + + +class TestParseStatement(TestCase): + def testThatParseStatementReturnsAStatement(self): + input = ''' +<STMTTRNRS> + <TRNUID>20090523122017 + <STATUS> + <CODE>0 + <SEVERITY>INFO + <MESSAGE>OK + </STATUS> + <STMTRS> + <CURDEF>CAD + <BANKACCTFROM> + <BANKID>160000100 + <ACCTID>12300 000012345678 + <ACCTTYPE>CHECKING + </BANKACCTFROM> + <BANKTRANLIST> + <DTSTART>20090401 + <DTEND>20090523122017 + <STMTTRN> + <TRNTYPE>POS + <DTPOSTED>20090401122017.000[-5:EST] + <TRNAMT>-6.60 + <FITID>0000123456782009040100001 + <NAME>MCDONALD'S #112 + <MEMO>POS MERCHANDISE;MCDONALD'S #112 + </STMTTRN> + </BANKTRANLIST> + <LEDGERBAL> + <BALAMT>382.34 + <DTASOF>20090523122017 + </LEDGERBAL> + <AVAILBAL> + <BALAMT>682.34 + <DTASOF>20090523122017 + </AVAILBAL> + </STMTRS> +</STMTTRNRS> + ''' + txn = soup_maker(input) + statement = OfxParser.parseStatement(txn.find('stmttrnrs')) + self.assertEquals(datetime(2009, 4, 1), statement.start_date) + self.assertEquals( + datetime(2009, 5, 23, 12, 20, 17), statement.end_date) + self.assertEquals(1, len(statement.transactions)) + self.assertEquals(Decimal('382.34'), statement.balance) + self.assertEquals(Decimal('682.34'), statement.available_balance) + + +class TestStatement(TestCase): + def testThatANewStatementIsValid(self): + statement = Statement() + self.assertEquals('', statement.start_date) + self.assertEquals('', statement.end_date) + self.assertEquals(0, len(statement.transactions)) + + +class TestParseTransaction(TestCase): + def testThatParseTransactionReturnsATransaction(self): + input = ''' +<STMTTRN> + <TRNTYPE>POS + <DTPOSTED>20090401122017.000[-5:EST] + <TRNAMT>-6.60 + <FITID>0000123456782009040100001 + <NAME>MCDONALD'S #112 + <MEMO>POS MERCHANDISE;MCDONALD'S #112 +</STMTTRN> +''' + txn = soup_maker(input) + transaction = OfxParser.parseTransaction(txn.find('stmttrn')) + self.assertEquals('pos', transaction.type) + self.assertEquals(datetime( + 2009, 4, 1, 12, 20, 17) - timedelta(hours=-5), transaction.date) + self.assertEquals(Decimal('-6.60'), transaction.amount) + self.assertEquals('0000123456782009040100001', transaction.id) + self.assertEquals("MCDONALD'S #112", transaction.payee) + self.assertEquals("POS MERCHANDISE;MCDONALD'S #112", transaction.memo) + + + def testThatParseTransactionWithFieldCheckNum(self): + input = ''' +<STMTTRN> + <TRNTYPE>DEP + <DTPOSTED>20130306 + <TRNAMT>1000.00 + <FITID>2013030601009100 + <CHECKNUM>700 + <MEMO>DEPOSITO ONLINE +</STMTTRN> +''' + txn = soup_maker(input) + transaction = OfxParser.parseTransaction(txn.find('stmttrn')) + self.assertEquals('700', transaction.checknum) + +class TestTransaction(TestCase): + def testThatAnEmptyTransactionIsValid(self): + t = Transaction() + self.assertEquals('', t.payee) + self.assertEquals('', t.type) + self.assertEquals(None, t.date) + self.assertEquals(None, t.amount) + self.assertEquals('', t.id) + self.assertEquals('', t.memo) + self.assertEquals('', t.checknum) + + +class TestInvestmentAccount(TestCase): + sample = ''' +<?xml version="1.0" encoding="UTF-8" ?> +<?OFX OFXHEADER="200" VERSION="200" SECURITY="NONE" + OLDFILEUID="NONE" NEWFILEUID="NONE" ?> +<OFX> + <INVSTMTMSGSRSV1> + <INVSTMTTRNRS> + <TRNUID>38737714201101012011062420110624</TRNUID> + <STATUS> + <CODE>0</CODE> + <SEVERITY>INFO</SEVERITY> + </STATUS> + <INVSTMTRS> + </INVSTMTRS> + </INVSTMTTRNRS> + </INVSTMTMSGSRSV1> +</OFX> +''' + + def testThatParseCanCreateAnInvestmentAccount(self): + OfxParser.parse(six.BytesIO(six.b(self.sample))) + # Success! + + + +class TestVanguardInvestmentStatement(TestCase): + def testForUnclosedTags(self): + ofx = OfxParser.parse(open_file('vanguard.ofx')) + self.assertTrue(hasattr(ofx, 'account')) + self.assertTrue(hasattr(ofx.account, 'statement')) + self.assertTrue(hasattr(ofx.account.statement, 'transactions')) + self.assertEquals(len(ofx.account.statement.transactions), 1) + self.assertEquals(ofx.account.statement.transactions[0].id, + '01234567890.0123.07152011.0') + self.assertEquals(ofx.account.statement.transactions[0] + .tradeDate, datetime(2011, 7, 15, 21)) + self.assertEquals(ofx.account.statement.transactions[0] + .settleDate, datetime(2011, 7, 15, 21)) + self.assertTrue(hasattr(ofx.account.statement, 'positions')) + self.assertEquals(len(ofx.account.statement.positions), 2) + self.assertEquals( + ofx.account.statement.positions[0].units, Decimal('102.0')) + + def testSecurityListSuccess(self): + ofx = OfxParser.parse(open_file('vanguard.ofx')) + self.assertEquals(len(ofx.security_list), 2) + + +class TestFidelityInvestmentStatement(TestCase): + def testForUnclosedTags(self): + ofx = OfxParser.parse(open_file('fidelity.ofx')) + self.assertTrue(hasattr(ofx.account.statement, 'positions')) + self.assertEquals(len(ofx.account.statement.positions), 6) + self.assertEquals( + ofx.account.statement.positions[0].units, Decimal('128.0')) + + def testSecurityListSuccess(self): + ofx = OfxParser.parse(open_file('fidelity.ofx')) + self.assertEquals(len(ofx.security_list), 7) + + +class TestAccountInfoAggregation(TestCase): + def testForFourAccounts(self): + ofx = OfxParser.parse(open_file('account_listing_aggregation.ofx')) + self.assertTrue(hasattr(ofx, 'accounts')) + self.assertEquals(len(ofx.accounts), 4) + + # first account + account = ofx.accounts[0] + self.assertEquals(account.account_type, 'SAVINGS') + self.assertEquals(account.desc, 'USAA SAVINGS') + self.assertEquals(account.institution.organization, 'USAA') + self.assertEquals(account.number, '0000000001') + self.assertEquals(account.routing_number, '314074269') + + # second + account = ofx.accounts[1] + self.assertEquals(account.account_type, 'CHECKING') + self.assertEquals(account.desc, 'FOUR STAR CHECKING') + self.assertEquals(account.institution.organization, 'USAA') + self.assertEquals(account.number, '0000000002') + self.assertEquals(account.routing_number, '314074269') + + # third + account = ofx.accounts[2] + self.assertEquals(account.account_type, 'CREDITLINE') + self.assertEquals(account.desc, 'LINE OF CREDIT') + self.assertEquals(account.institution.organization, 'USAA') + self.assertEquals(account.number, '00000000000003') + self.assertEquals(account.routing_number, '314074269') + + # fourth + account = ofx.accounts[3] + self.assertEquals(account.account_type, '') + self.assertEquals(account.desc, 'MY CREDIT CARD') + self.assertEquals(account.institution.organization, 'USAA') + self.assertEquals(account.number, '4111111111111111') + + +class TestGracefulFailures(TestCase): + ''' Test that when fail_fast is False, failures are returned to the + caller as warnings and discarded entries in the Statement class. + ''' + def testDateFieldMissing(self): + ''' The test file contains three transactions in a single + statement. + + They fail due to: + 1) No date + 2) Empty date + 3) Invalid date + ''' + ofx = OfxParser.parse(open_file('fail_nice/date_missing.ofx'), False) + self.assertEquals(len(ofx.account.statement.transactions), 0) + self.assertEquals(len(ofx.account.statement.discarded_entries), 3) + self.assertEquals(len(ofx.account.statement.warnings), 0) + + # Test that it raises an error otherwise. + self.assertRaises(OfxParserException, OfxParser.parse, + open_file('fail_nice/date_missing.ofx')) + + def testDecimalConversionError(self): + ''' The test file contains a transaction that has a poorly formatted + decimal number ($20). Test that we catch this. + ''' + ofx = OfxParser.parse(open_file('fail_nice/decimal_error.ofx'), False) + self.assertEquals(len(ofx.account.statement.transactions), 0) + self.assertEquals(len(ofx.account.statement.discarded_entries), 1) + + # Test that it raises an error otherwise. + self.assertRaises(OfxParserException, OfxParser.parse, + open_file('fail_nice/decimal_error.ofx')) + + def testEmptyBalance(self): + ''' The test file contains empty or blank strings in the balance + fields. Fail nicely on those. + ''' + ofx = OfxParser.parse(open_file('fail_nice/empty_balance.ofx'), False) + self.assertEquals(len(ofx.account.statement.transactions), 1) + self.assertEquals(len(ofx.account.statement.discarded_entries), 0) + self.assertFalse(hasattr(ofx.account.statement, 'balance')) + self.assertFalse(hasattr(ofx.account.statement, 'available_balance')) + + # Test that it raises an error otherwise. + self.assertRaises(OfxParserException, OfxParser.parse, + open_file('fail_nice/empty_balance.ofx')) + +class TestParseSonrs(TestCase): + + def testSuccess(self): + ofx = OfxParser.parse(open_file('signon_success.ofx'), True) + self.assertTrue(ofx.signon.success) + self.assertEquals(ofx.signon.code, 0) + self.assertEquals(ofx.signon.severity, 'INFO') + self.assertEquals(ofx.signon.message, 'Login successful') + + ofx = OfxParser.parse(open_file('signon_success_no_message.ofx'), True) + self.assertTrue(ofx.signon.success) + self.assertEquals(ofx.signon.code, 0) + self.assertEquals(ofx.signon.severity, 'INFO') + self.assertEquals(ofx.signon.message, '') + + def testFailure(self): + ofx = OfxParser.parse(open_file('signon_fail.ofx'), True) + self.assertFalse(ofx.signon.success) + self.assertEquals(ofx.signon.code, 15500) + self.assertEquals(ofx.signon.severity, 'ERROR') + self.assertEquals(ofx.signon.message, 'Your request could not be processed because you supplied an invalid identification code or your password was incorrect') + +if __name__ == "__main__": + import unittest + unittest.main() diff --git a/tests/test_write.py b/tests/test_write.py new file mode 100644 index 0000000..3366217 --- /dev/null +++ b/tests/test_write.py @@ -0,0 +1,17 @@ +from __future__ import absolute_import + +from ofxparse import OfxParser as op +from unittest import TestCase +import sys +sys.path.append('..') +from .support import open_file + +class TestOfxWrite(TestCase): + def test_write(self): + test_file = open_file('fidelity.ofx') + ofx_doc = op.parse(test_file) + self.assertEqual(str(ofx_doc), "") + +if __name__ == "__main__": + import unittest + unittest.main() |