From 0aac24e659a730e3e6d2b9243c7cf9f8d011aebb Mon Sep 17 00:00:00 2001 From: Nirus2000 Date: Sun, 8 Jan 2023 09:48:54 +0100 Subject: [PATCH] Add new WealthSimple PDF-Importer https://forum.portfolio-performance.info/t/import-pdf-from-wealthsimple-invest-canada/19247 https://forum.portfolio-performance.info/t/import-pdf-from-wealthsimple-invest-canada/19247/7 --- .../DepotStatement01.txt | 272 ++ .../DepotStatement02.txt | 168 + ...hsimpleInvestmentsIncPDFExtractorTest.java | 3940 +++++++++++++++++ .../datatransfer/pdf/PDFExtractorUtils.java | 22 +- .../datatransfer/pdf/PDFImportAssistant.java | 1 + ...ealthsimpleInvestmentsIncPDFExtractor.java | 626 +++ 6 files changed, 5022 insertions(+), 7 deletions(-) create mode 100644 name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/DepotStatement01.txt create mode 100644 name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/DepotStatement02.txt create mode 100644 name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/WealthsimpleInvestmentsIncPDFExtractorTest.java create mode 100644 name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/WealthsimpleInvestmentsIncPDFExtractor.java diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/DepotStatement01.txt b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/DepotStatement01.txt new file mode 100644 index 0000000000..0ab8dc4ed8 --- /dev/null +++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/DepotStatement01.txt @@ -0,0 +1,272 @@ +PDFBox Version: 1.8.16 +----------------------------------------- +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 1 of 13 +2020 Performance Report NotesThis report details how the investments in your account have +$10,543.92 performed from January 01, 2020 to December 31, 2020.By comparing the actual performance of your investments, it +can help you to evaluate your progress toward meeting your +investment goals. +Portfolio Start date Change in End datebalance 1 Deposits Withdrawals market value 2 balance 3 +TFSA – $10,200.00 -$1,000.00 $1,343.92 $10,543.92 +Account Number +Totals – $10,200.00 -$1,000.00 $1,343.92 $10,543.92 +Summary of Earnings and Fees YTD Total +Interest – +CAD Dividends $92.99 +Foreign Dividends $71.51 +Non-registered account fees and taxes 9 – +Unrealized Capital Gains/Losses $1,010.94 +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 2 of 132020 Performance Report +TFSA - Account Number +Portfolio Performance Report — TFSA Contributions this year Withdrawals +$10,543.92 $10,200.00 $1,000.00 +Account performance Return rates +$12.5k Time-weighted returns 12 +1 month 1.0% +$10k +1 year* – +$7.5k Year to date* 13.8% +Since inception* 13.8% +$5k +$2.5k Change in market value 2 +1 year – +Since inception +01/01/20 07/01/20 12/31/20 $1,343.92 + Account Value Net Contributions +Summary of Earnings YTD Total +Interest – +CAD Dividends $92.99 +Foreign Dividends $71.51 +Unrealized Capital Gains/Losses $1,010.94 +The securities in your account are held in the name of Canadian ShareOwner Investments Inc., the custodian. Because the custodian is a member of the Canadian Investor Protection Fund, +the securities in your account are covered by that fund. The money-weighted returns table shows the total percentage return of your account for the period(s) ending December 31, 2020. Your +total percentage return means the cumulative realized and unrealized capital gains and losses of an investment, plus income from the investment, over a specified period of time, expressed as a +percentage. Returns are calculated after costs have been deducted, including transaction charges and account-related fees, but not income tax. +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada *Calculated returns are annualized. Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 3 of 132020 Performance Report +TFSA - Account Number +TFSA holdings +$10,543.92 +Qty Market value 4 Book cost 5 Unrealized gain or loss 6 +Equity $5,526.05 $4,474.06 $1,051.99 23.5% +ACWV iShares Edge MSCI Min Vol Global ETF 8.361 $1,031.46 ($123.37/share) $944.40 ($112.95/share) $87.06 9.2% +EEMV iShares MSCI Emerg Min Vol ETF 20.8583 $1,621.83 ($77.75/share) $1,377.66 ($66.05/share) $244.17 17.7% +VTI Vanguard Total Stock Market ETF 4.7706 $1,182.04 ($247.78/share) $849.86 ($178.15/share) $332.18 39.1% +XEF iShares Core MSCI EAFE IMI Index ETF 34.2631 $1,123.49 ($32.79/share) $874.72 ($25.53/share) $248.77 28.4% +XIC iShares Core S&P/TSX Capped Composite Index ETF 20.4999 $567.23 ($27.67/share) $427.42 ($20.85/share) $139.81 32.7% +Fixed Income $4,612.04 $4,640.14 -$28.10 -0.6% +XSH iShares Core Canadian ST Corp 79.9364 $1,592.73 ($19.92/share) $1,582.74 ($19.80/share) $9.99 0.6% +ZFL BMO Long Federal Bond ETF 150.0652 $3,019.31 ($20.12/share) $3,057.40 ($20.37/share) -$38.09 -1.2% +Other $405.83 $418.78 -$12.95 -3.1% +CAD CAD $41.94 $41.94 – – +GLDM SPDR Gold MiniShares Trust 15.0926 $363.89 ($24.11/share) $376.84 ($24.97/share) -$12.95 -3.4% +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 4 of 132020 Performance Report + +TFSA - Account Number +TFSA activity +Description Qty Price persecurity Total +Contributions and Deposits +Jul 02 Electronic Funds Transfer Out: 1,000.00 CAD – – -$1,000.00 +Jun 01 Electronic Funds Transfer In: $100.00 CAD – – $100.00 +May 02 settled May 04 Electronic Funds Transfer In: $100.00 CAD – – $100.00 +Apr 02 Electronic Funds Transfer In: $5,000.00 CAD – – $5,000.00 +Apr 02 Electronic Funds Transfer In: $5,000.00 CAD – – $5,000.00 +Total - Contributions and Deposits $9,200.00 + +Trades +Dec 29 settled Dec 31 Bought 1.7582 of ZFL - BMO Long Federal Bond ETF for 35.04 CAD 1.7582 $19.93 $35.04 +Oct 05 settled Oct 07 Bought 0.6794 of ZFL - BMO Long Federal Bond ETF for 13.82 CAD 0.6794 $20.34 $13.82 +Sep 25 settled Sep 29 Sold 14.0853 of QTIP - Mackenzie Financial Corp for 1521.49 CAD -14.0853 $108.02 -$1,521.49 +Sep 25 settled Sep 29 Bought 50.9493 of ZFL - BMO Long Federal Bond ETF for 1055.16 CAD 50.9493 $20.71 $1,055.16 +Sep 25 settled Sep 29 Sold 88.9806 of ZAG - BMO AGGREGATE BOND INDEX ETF for 1493.09 CAD -88.9806 $16.78 -$1,493.09 +Sep 25 settled Sep 29 Bought 79.9364 of XSH - iShares Core Canadian ST Corp for 1582.74 CAD 79.9364 $19.80 $1,582.74 +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 5 of 132020 Performance Report +TFSA - Account Number +TFSA activity +Description Qty Price persecurity Total +Sep 25 settled Sep 29 Bought 15.0926 of GLDM - World Gold Trust for 376.84 CAD 15.0926 $24.97 $376.84 +Aug 07 settled Aug 11 Bought 0.5764 of ZFL - BMO Long Federal Bond ETF for 12.19 CAD 0.5764 $21.15 $12.19 +Jul 03 settled Jul 07 Bought 1.0702 of ZFL - BMO Long Federal Bond ETF for 22.32 CAD 1.0702 $20.86 $22.32 +Jun 29 settled Jul 01 Sold 1.7993 of EEMV - iShares MSCI Emerg Min Vol ETF for 128.87 CAD -1.7993 $71.62 -$128.87 +Jun 29 settled Jul 01 Sold 0.4814 of ACWV - iShares Edge MSCI Min Vol Global ETF for 57.32 CAD -0.4814 $119.07 -$57.32 +Jun 29 settled Jul 01 Sold 0.8204 of VTI - Vanguard Total Stock Market ETF for 172.91 CAD -0.8204 $210.76 -$172.91 +Jun 29 settled Jul 02 Sold 3.3852 of XIC - iShares Core S&P/TSX Capped Composite Index ETF for 82.43 CAD -3.3852 $24.35 -$82.43 +Jun 29 settled Jul 02 Sold 4.7504 of XEF - iShares Core MSCI EAFE IMI Index ETF for 138.26 CAD -4.7504 $29.10 -$138.26 +Jun 29 settled Jul 02 Sold 9.2542 of ZAG - BMO AGGREGATE BOND INDEX ETF for 154.73 CAD -9.2542 $16.72 -$154.73 +Jun 29 settled Jul 02 Sold 0.3119 of QTIP - Mackenzie Financial Corp for 33.0 CAD -0.3119 $105.80 -$33.00 +Jun 29 settled Jul 02 Sold 10.9669 of ZFL - BMO Long Federal Bond ETF for 228.77 CAD -10.9669 $20.86 -$228.77 +Jun 26 settled Jun 30 Bought 1.72 of ZAG - BMO AGGREGATE BOND INDEX ETF for 28.75 CAD 1.72 $16.72 $28.75 +Jun 01 settled Jun 03 Bought 0.9932 of ZAG - BMO AGGREGATE BOND INDEX ETF for 16.39 CAD 0.9932 $16.50 $16.39 +Jun 01 settled Jun 03 Bought 4.1164 of ZFL - BMO Long Federal Bond ETF for 84.16 CAD 4.1164 $20.44 $84.16 +May 04 settled May 06 Bought 0.0232 of ACWV - iShares Edge MSCI Min Vol Global ETF for 2.78 CAD 0.0232 $119.83 $2.78 +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 6 of 132020 Performance Report +TFSA - Account Number +TFSA activity +Description Qty Price persecurity Total +May 04 settled May 06 Bought 0.0357 of EEMV - iShares MSCI Emerg Min Vol ETF for 2.5 CAD 0.0357 $70.03 $2.50 +May 04 settled May 06 Bought 1.6774 of ZAG - BMO AGGREGATE BOND INDEX ETF for 27.71 CAD 1.6774 $16.52 $27.71 +May 04 settled May 06 Bought 3.1217 of ZFL - BMO Long Federal Bond ETF for 64.15 CAD 3.1217 $20.55 $64.15 +Apr 02 settled Apr 06 Bought 22.6219 of EEMV - iShares MSCI Emerg Min Vol ETF for 1494.0 CAD 22.6219 $66.04 $1,494.00 +Apr 02 settled Apr 06 Bought 8.8192 of ACWV - iShares Edge MSCI Min Vol Global ETF for 996.0 CAD 8.8192 $112.94 $996.00 +Apr 02 settled Apr 06 Bought 5.591 of VTI - Vanguard Total Stock Market ETF for 996.01 CAD 5.591 $178.15 $996.01 +Apr 02 settled Apr 06 Bought 14.3972 of QTIP - Mackenzie Financial Corp for 1494.0 CAD 14.3972 $103.77 $1,494.00 +Apr 02 settled Apr 06 Bought 39.0135 of XEF - iShares Core MSCI EAFE IMI Index ETF for 996.0 CAD 39.0135 $25.53 $996.00 +Apr 02 settled Apr 06 Bought 23.8851 of XIC - iShares Core S&P/TSX Capped Composite Index ETF for 498.0 CAD 23.8851 $20.85 $498.00 +Apr 02 settled Apr 06 Bought 93.8442 of ZAG - BMO AGGREGATE BOND INDEX ETF for 1494.0 CAD 93.8442 $15.92 $1,494.00 +Apr 02 settled Apr 06 Bought 98.7605 of ZFL - BMO Long Federal Bond ETF for 1992.0 CAD 98.7605 $20.17 $1,992.00 + +Canadian and Foreign Dividends +Dec 22 ACWV-iShares Edge MSCI Min Vol Global ETF: 15-DEC-20 (record date) 8.3610 shares, gross 6.61 USD, – – $8.55 +convert to CAD @ 1.2927 +Dec 22 EEMV-iShares MSCI Emerg Min Vol ETF: 15-DEC-20 (record date) 20.8583 shares, gross 19.65 USD, – – $25.40 +convert to CAD @ 1.2927 +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 7 of 132020 Performance Report + +TFSA - Account Number +TFSA activity +Description Qty Price persecurity Total +Dec 03 ZFL-BMO Long Federal Bond ETF: 30-NOV-20 (record date) 148.3070 shares – – $6.38 +Nov 30 XSH - IShares Core CDN Short ETF Un: 25-NOV-20 (record date) 79.9364 shares – – $3.44 +Nov 03 ZFL-BMO Long Federal Bond ETF: 29-OCT-20 (record date) 148.3070 shares – – $6.38 +Oct 30 XSH - IShares Core CDN Short ETF Un: 27-OCT-20 (record date) 79.9364 shares – – $3.44 +Oct 02 VTI - Vanguard Index STK MKT ETF: 28-SEP-20 (record date) 4.7706 shares, gross 3.22 USD, convert to CAD @ 1.3319 – – $4.29 +Oct 02 ZFL-BMO Long Federal Bond ETF: 29-SEP-20 (record date) 147.6276 shares – – $6.35 +Sep 30 XIC-S&P/TSX Capped Composite Index ETF: 25-SEP-20 (record date) 20.4999 shares – – $4.30 +Sep 02 ZAG-BMO AGG Bond Index ETF: 28-AUG-20 (record date) 88.9806 shares – – $3.56 +Sep 02 ZFL-BMO Long Federal Bond ETF: 28-AUG-20 (record date) 96.6783 shares – – $4.16 +Aug 05 ZAG-BMO AGG Bond Index ETF: 30-JUL-20 (record date) 88.9806 shares – – $3.56 +Aug 05 ZFL-BMO Long Federal Bond ETF: 30-JUL-20 (record date) 96.1019 shares – – $4.13 +Jul 10 Mackenzie US TIPS Index ETF (CAD-Hedged): 03-JUL-20 (record date) 14.0853 shares – – $1.36 +Jul 06 ZAG-BMO AGG Bond Index ETF: 29-JUN-20 (record date) 96.5148 shares – – $3.86 +Jul 06 ZFL-BMO Long Federal Bond ETF: 29-JUN-20 (record date) 105.9986 shares – – $4.56 +Jul 02 VTI - Vanguard Index STK MKT ETF: 26-JUN-20 (record date) 5.5910 shares, gross 3.91 USD, convert to CAD @ 1.3602 – – $5.32 +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 8 of 132020 Performance Report +TFSA - Account Number +TFSA activity +Price +Description Qty per Total +security +Jun 30 XEF - IShares Core MSCI EAFE ETF Un: 25-JUN-20 (record date) 39.0135 shares – – $13.93 +Jun 30 XIC-S&P/TSX Capped Composite Index ETF: 25-JUN-20 (record date) 23.8851 shares – – $5.23 +Jun 23 ACWV-iShares Edge MSCI Min Vol Global ETF: 16-JUN-20 (record date) 8.8424 shares, gross 8.14 USD, convert to CAD – – $11.02 +@ 1.3535 +Jun 23 EEMV-iShares MSCI Emerg Min Vol ETF: 16-JUN-20 (record date) 22.6576 shares, gross 12.51 USD, convert to CAD @ – – $16.93 +1.3535 +Jun 09 Mackenzie US TIPS Index ETF (CAD-Hedged): 02-JUN-20 (record date) 14.3972 shares – – $0.88 +Jun 02 ZFL-BMO Long Federal Bond ETF: 28-MAY-20 (record date) 101.8822 shares – – $4.38 +Jun 02 ZAG-BMO AGG Bond Index ETF: 28-MAY-20 (record date) 95.5216 shares – – $3.82 +May 11 Mackenzie US TIPS Index ETF (CAD-Hedged): 04-MAY-20 (record date) 14.3972 shares – – $1.27 +May 04 ZFL-BMO Long Federal Bond ETF: 29-APR-20 (record date) 98.7605 shares – – $4.25 +May 04 ZAG-BMO AGG Bond Index ETF: 29-APR-20 (record date) 93.8442 shares – – $3.75 +Total - Canadian and Foreign Dividends $164.50 + +Tax +Dec 22 ACWV-iShares Edge MSCI Min Vol Global ETF: Non-resident tax withheld at source (0.99 USD, convert to CAD @ 1.2927) – $1.00 -$1.28 +Dec 22 EEMV-iShares MSCI Emerg Min Vol ETF: Non-resident tax withheld at source (2.95 USD, convert to CAD @ 1.2927) – $1.00 -$3.81 +Oct 02 VTI - Vanguard Index STK MKT ETF: Non-resident tax withheld at source (0.48 USD, convert to CAD @ 1.3319) – $1.00 -$0.64 +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 9 of 132020 Performance Report +TFSA - Account Number +TFSA activity +Description Qty Price persecurity Total +Jul 02 VTI - Vanguard Index STK MKT ETF: Non-resident tax withheld at source (0.59 USD, convert to CAD @ 1.3602) – $1.00 -$0.80 +Jun 23 ACWV-iShares Edge MSCI Min Vol Global ETF: Non-resident tax withheld at source (1.22 USD, convert to CAD @ 1.3535) – $1.00 -$1.65 +Jun 23 EEMV-iShares MSCI Emerg Min Vol ETF: Non-resident tax withheld at source (1.88 USD, convert to CAD @ 1.3535) – $1.00 -$2.54 +Total - Tax -$10.72 + +Management Fees 7 +Dec 31 Gross management fee to Wealthsimple – – -$4.42 +Dec 31 Promotions and discounts applied to Wealthsimple fee – – $0.26 +Dec 31 Sales tax on management fee to Wealthsimple – – -$0.21 +Nov 30 Gross management fee to Wealthsimple – – -$4.25 +Nov 30 Promotions and discounts applied to Wealthsimple fee – – $0.09 +Nov 30 Sales tax on management fee to Wealthsimple – – -$0.21 +Oct 31 Gross management fee to Wealthsimple – – -$4.33 +Oct 31 Promotions and discounts applied to Wealthsimple fee – – $0.09 +Oct 31 Sales tax on management fee to Wealthsimple – – -$0.21 +Sep 30 Gross management fee to Wealthsimple – – -$4.15 +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 10 of 132020 Performance Report + +TFSA - Account Number +TFSA activity +Description Qty Price persecurity Total +Sep 30 Promotions and discounts applied to Wealthsimple fee – – $0.08 +Sep 30 Sales tax on management fee to Wealthsimple – – -$0.20 +Aug 31 Gross management fee to Wealthsimple – – -$4.34 +Aug 31 Promotions and discounts applied to Wealthsimple fee – – $0.09 +Aug 31 Sales tax on management fee to Wealthsimple – – -$0.21 +Jul 31 Gross management fee to Wealthsimple – – -$4.30 +Jul 31 Promotions and discounts applied to Wealthsimple fee – – $0.51 +Jul 31 Sales tax on management fee to Wealthsimple – – -$0.19 +Jun 30 Gross management fee to Wealthsimple – – -$4.46 +Jun 30 Promotions and discounts applied to Wealthsimple fee – – $0.49 +Jun 30 Sales tax on management fee to Wealthsimple – – -$0.20 +May 31 Gross management fee to Wealthsimple – – -$4.51 +May 31 Promotions and discounts applied to Wealthsimple fee – – $0.47 +May 31 Sales tax on management fee to Wealthsimple – – -$0.20 +Apr 30 Gross management fee to Wealthsimple – – -$4.08 +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 11 of 132020 Performance Report + +TFSA - Account Number +TFSA activity +Description Qty Price persecurity Total +Apr 30 Promotions and discounts applied to Wealthsimple fee – – $0.42 +Apr 30 Sales tax on management fee to Wealthsimple – – -$0.18 +Total - Management Fees 7 -$38.15 +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 12 of 13 + +Glossary +Start date balance 1 Net proceeds represent the amount you receive after deducting fees and expenses from selling +asset(s) at their market value. +Your account's balance at January 01, 2020. If your account was funded after January 01, +2020, the balance is -. Non-registered account fees and taxes 9 +Change in market value2 Management fees charged on non-registered investment accounts (Personal, Joint, and +Corporate) can be claimed as a deduction on your income taxes. +The Change in Market Value is calculated as follows: The market value of all cash and +investments in the account as at the end of the period of time covered by the performance DSC/LL/LSC 10 +report, minus the market value of all cash and investments in the account at the beginning of +that period, minus the market value of all deposits and transfers of cash and investments into This investment may be subject to a deferred sales charge upon redemption. For mutual funds, +the account in that period, plus the market value of all withdrawals and transfers of cash and the sales charge is usually based on the net asset value of your units/shares at the time that +investments out of the account in that period. The difference calculated is the Change in you redeem, and is deducted from the amount you receive for the units/shares. Deferred sales +Market Value. charges decline to zero after a specific number of years. +End date balance 3 Money-weighted returns 11 +Your account's balance at December 31, 2020. Money-weighted returns is a net of fees calculation for your portfolio performance including +the timing of deposits and withdrawals. +Market value 4 As an example, if you contribute $100 into your portfolio, this contribution is included in the +Market value is the current value of an asset. It is displayed in Canadian Dollars (CAD) unless calculation for your money-weighted return. Read more how returns are calculated here. +otherwise labelled. Sample calculation +Book cost 5 You decide to fund your Wealthsimple account with $1,000 on January 1st. The first 364 +days of the year are great and your portfolio earns $100. You decide to add another +Book cost reflects the total of what you paid to purchase an asset including all transaction $100,000 on December 31st and over that one day your portfolio loses a total of $101. +charges and reinvested distributions. It is displayed in CAD unless otherwise labelled. Book What are your returns? +values marked by † indicate that a part of this information comes from your prior dealer or +adviser and may not be completely accurate. To calculate your MWR +Unrealized gain and loss6 $100,999 = ($1,000 * (1+(MWR))) + ($100,000 * (1+(MWR))^(1/365)) +Calculated profit or loss based on the differential between an asset’s market value and its MWR = -0.07865% +book cost. Gains or losses are unrealized because the asset has not been sold. It is displayed 12 +in CAD unless otherwise labelled. Time-weighted returns +Management Fees7 Time-weighted returns is the performance of an account over a period of time excludingdeposits and withdrawals. +We charge a percentage of your portfolio value as investment management fees. For more 13 +details on your fees, refer to your Investor Policy Statement in My Documents. Adjusted cost base +Trailer fees are charged for your Smart Savings account(s) outside of the interest rate you An adjusted calculation of book cost used for determining the cost of an investment +receive. Here's an example: Your account shows you're receiving 1.50% interest and the specifically for tax purposes. Adjusted cost base is used by the Canada Revenue Agency to +trailer fee is 0.50%, you'll still be receiving 1.50% in interest with this trailer fee deducted. determine capital gains or losses for income tax purposes. This calculation is based on the +securities you hold with us. If you hold the same securities with another adviser or dealer, you +Net proceeds8 will need to recalculate your adjusted cost base for that security. +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 13 of 13 +Glossary +Realized gain and loss14 +Actual profit or loss of an asset that has been sold. This number doesn't include fees and +expenses deducted during the sale of asset(s). It is displayed in CAD unless otherwise +labelled. Realized returns marked by ‡ indicate incomplete history required to calculate your +adjusted cost base. Please contact support@wealthsimple. +Wealthsimple Inc. 860 Richmond Street West, 3rd Floor Toronto Ontario M6J 1C9 Canada Conversion rate $1.00 USD = $1.27300 CAD \ No newline at end of file diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/DepotStatement02.txt b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/DepotStatement02.txt new file mode 100644 index 0000000000..69506d5b99 --- /dev/null +++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/DepotStatement02.txt @@ -0,0 +1,168 @@ +PDFBox Version: 1.8.16 +----------------------------------------- +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 1 of 8 +June 2020 Performance Report NotesThis report details how the investments in your account have +$10,995.55 performed from June 01, 2020 to June 30, 2020. Bycomparing the actual performance of your investments, it can +help you to evaluate your progress toward meeting your +investment goals. +Portfolio Start datebalance 1 Deposits Withdrawals +Change in End date +market value 2 balance 3 +TFSA $10,777.52 $100.00 – $118.03 $10,995.55 +Account Number +Totals $10,777.52 $100.00 – $118.03 $10,995.55 +Summary of Earnings and Fees MTD Total YTD Total +Interest – – +CAD Dividends $28.24 $37.51 +Foreign Dividends $27.95 $27.95 +Non-registered account fees and taxes 9 +Wealthsimple Inc. 400 - 80 Spadina Avenue, Toronto Ontario M5V 2J4 Canada Conversion rate $1.00 USD = $1.35750 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 2 of 8June 2020 Performance Report +TFSA - Account Number +Portfolio Performance Report — TFSA Contributions this year Withdrawals +$10,995.55 $10,200.00 – +Account performance Return rates +$12.5k Time-weighted returns 12 +1 month 1.1% +$10k +1 year* – +$7.5k Year to date* 8.1% +Since inception* 8.1% +$5k +Money-weighted returns 11 +$2.5k 1 year* – +Year to date* 8.1% +06/01/19 12/15/19 06/30/20 Since inception* 8.1% + + Account Value Net Contributions +Change in market value 2 +Summary of Earnings MTD Total YTD Total 1 month $118.03 +Interest – – Since inception $795.55 +CAD Dividends $28.24 $37.51 +Foreign Dividends $27.95 $27.95 +Unrealized Capital Gains/Losses – $662.76 +The securities in your account are held in the name of Canadian ShareOwner Investments Inc., the custodian. Because the custodian is a member of the Canadian Investor Protection Fund, +the securities in your account are covered by that fund. The money-weighted returns table shows the total percentage return of your account for the period(s) ending June 30, 2020. Your total +percentage return means the cumulative realized and unrealized capital gains and losses of an investment, plus income from the investment, over a specified period of time, expressed as a +percentage. Returns are calculated after costs have been deducted, including transaction charges and account-related fees, but not income tax. +Wealthsimple Inc. 400 - 80 Spadina Avenue, Toronto Ontario M5V 2J4 Canada *Calculated returns are annualized. Conversion rate $1.00 USD = $1.35750 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 3 of 8June 2020 Performance Report +TFSA - Account Number +TFSA holdings +$10,995.55 +Qty Market value 4 Book cost 5 Unrealized gain orloss 6 +Equity $4,974.06 $4,474.06 $500.00 11.2% +ACWV BTC iShares Edge MSCI Min Vol Global ETF 8.361 $994.03 ($118.89/share) $944.40 ($112.95/share) $49.63 5.3% +EEMV BTC iShares Edge MSCI Min Vol Emerging Markets ETF 20.8583 $1,472.38 ($70.59/share) $1,377.66 ($66.05/share) $94.72 6.9% +VTI Vanguard Group, Inc. - Vanguard Total Stock Market ETF 4.7706 $1,013.70 ($212.49/share) $849.86 ($178.15/share) $163.84 19.3% +XEF BlackRock Canada iShares Core MSCI EAFE IMI Index ETF 34.2631 $988.83 ($28.86/share) $874.72 ($25.53/share) $114.11 13.0% +XIC BlackRock iShares Core S&P/TSX Capped Composite Index 20.4999 $505.12 ($24.64/share) $427.42 ($20.85/share) $77.70 18.2% +ETF +Fixed Income $4,962.63 $4,799.87 $162.76 3.4% +QTIP Mackenzie US TIPS Index ETF CAD-Hedged Ser E 14.0853 $1,488.96 ($105.71/share) $1,461.63 ($103.77/share) $27.33 1.9% +ZAG BMO Aggregate Bond Index ETF 88.9806 $1,491.31 ($16.76/share) $1,419.37 ($15.95/share) $71.94 5.1% +ZFL BMO Long Federal Bond Index ETF 95.0317 $1,982.36 ($20.86/share) $1,918.87 ($20.19/share) $63.49 3.3% +Other $1,058.86 $1,058.86 – – +CAD CAD $1,058.86 $1,058.86 – – +Wealthsimple Inc. 400 - 80 Spadina Avenue, Toronto Ontario M5V 2J4 Canada Conversion rate $1.00 USD = $1.35750 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 4 of 8June 2020 Performance Report +TFSA - Account Number +TFSA activity +Description Qty Price persecurity Total +Contributions and Deposits +Jun 01 Electronic Funds Transfer In: $100.00 CAD – – $100.00 +Total - Contributions and Deposits $100.00 +Trades +Jun 29 settled Jul 01 Sold 1.7993 of EEMV - iShares MSCI Emerg Min Vol ETF for 128.87 CAD -1.7993 $71.62 -$128.87 +Jun 29 settled Jul 01 Sold 0.4814 of ACWV - iShares Edge MSCI Min Vol Global ETF for 57.32 CAD -0.4814 $119.07 -$57.32 +Jun 29 settled Jul 01 Sold 0.8204 of VTI - Vanguard Total Stock Market ETF for 172.91 CAD -0.8204 $210.76 -$172.91 +Jun 29 settled Jul 02 Sold 3.3852 of XIC - iShares Core S&P/TSX Capped Composite Index ETF for 82.43 CAD -3.3852 $24.35 -$82.43 +Jun 29 settled Jul 02 Sold 4.7504 of XEF - iShares Core MSCI EAFE IMI Index ETF for 138.26 CAD -4.7504 $29.10 -$138.26 +Jun 29 settled Jul 02 Sold 9.2542 of ZAG - BMO AGGREGATE BOND INDEX ETF for 154.73 CAD -9.2542 $16.72 -$154.73 +Jun 29 settled Jul 02 Sold 0.3119 of QTIP - Mackenzie Financial Corp for 33.0 CAD -0.3119 $105.80 -$33.00 +Jun 29 settled Jul 02 Sold 10.9669 of ZFL - BMO Long Federal Bond ETF for 228.77 CAD -10.9669 $20.86 -$228.77 +Jun 26 settled Jun 30 Bought 1.72 of ZAG - BMO AGGREGATE BOND INDEX ETF for 28.75 CAD 1.72 $16.72 $28.75 +Jun 01 settled Jun 03 Bought 0.9932 of ZAG - BMO AGGREGATE BOND INDEX ETF for 16.39 CAD 0.9932 $16.50 $16.39 +Wealthsimple Inc. 400 - 80 Spadina Avenue, Toronto Ontario M5V 2J4 Canada Conversion rate $1.00 USD = $1.35750 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 5 of 8June 2020 Performance Report +TFSA - Account Number +TFSA activity +Price +Description Qty per Total +security +Jun 01 settled Jun 03 Bought 4.1164 of ZFL - BMO Long Federal Bond ETF for 84.16 CAD 4.1164 $20.44 $84.16 +Canadian and Foreign Dividends +Jun 30 XEF - IShares Core MSCI EAFE ETF Un: 25-JUN-20 (record date) 39.0135 shares – – $13.93 +Jun 30 XIC-S&P/TSX Capped Composite Index ETF: 25-JUN-20 (record date) 23.8851 shares – – $5.23 +Jun 23 ACWV-iShares Edge MSCI Min Vol Global ETF: 16-JUN-20 (record date) 8.8424 shares, gross 8.14 USD, convert to – – $11.02 +CAD @ 1.3535 +Jun 23 EEMV-iShares MSCI Emerg Min Vol ETF: 16-JUN-20 (record date) 22.6576 shares, gross 12.51 USD, convert to CAD – – $16.93 +@ 1.3535 +Jun 09 Mackenzie US TIPS Index ETF (CAD-Hedged): 02-JUN-20 (record date) 14.3972 shares – – $0.88 +Jun 02 ZFL-BMO Long Federal Bond ETF: 28-MAY-20 (record date) 101.8822 shares – – $4.38 +Jun 02 ZAG-BMO AGG Bond Index ETF: 28-MAY-20 (record date) 95.5216 shares – – $3.82 +Total - Canadian and Foreign Dividends $56.19 +Tax +Jun 23 ACWV-iShares Edge MSCI Min Vol Global ETF: Non-resident tax withheld at source (1.22 USD, convert to CAD @ – $1.00 -$1.65 +1.3535) +Jun 23 EEMV-iShares MSCI Emerg Min Vol ETF: Non-resident tax withheld at source (1.88 USD, convert to CAD @ 1.3535) – $1.00 -$2.54 +Total - Tax -$4.19 +Wealthsimple Inc. 400 - 80 Spadina Avenue, Toronto Ontario M5V 2J4 Canada Conversion rate $1.00 USD = $1.35750 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 6 of 8June 2020 Performance Report +TFSA - Account Number +TFSA activity +Description Qty Price persecurity Total +Management Fees 7 +Jun 30 Gross management fee to Wealthsimple – – -$4.46 +Jun 30 Promotions and discounts applied to Wealthsimple fee – – $0.49 +Jun 30 Sales tax on management fee to Wealthsimple – – -$0.20 +Total - Management Fees 7 -$4.17 +Wealthsimple Inc. 400 - 80 Spadina Avenue, Toronto Ontario M5V 2J4 Canada Conversion rate $1.00 USD = $1.35750 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 7 of 8 +Glossary +Start date balance 1 Net proceeds represent the amount you receive after deducting fees and expenses from selling +asset(s) at their market value. +Your account's balance at June 01, 2020. If your account was funded after June 01, 2020, +the balance is -. Non-registered account fees and taxes 9 +Change in market value2 Management fees charged on non-registered investment accounts (Personal, Joint, and +Corporate) can be claimed as a deduction on your income taxes. +The Change in Market Value is calculated as follows: The market value of all cash and +investments in the account as at the end of the period of time covered by the performance DSC/LL/LSC 10 +report, minus the market value of all cash and investments in the account at the beginning of +that period, minus the market value of all deposits and transfers of cash and investments into This investment may be subject to a deferred sales charge upon redemption. For mutual funds, +the account in that period, plus the market value of all withdrawals and transfers of cash and the sales charge is usually based on the net asset value of your units/shares at the time that +investments out of the account in that period. The difference calculated is the Change in you redeem, and is deducted from the amount you receive for the units/shares. Deferred sales +Market Value. charges decline to zero after a specific number of years. +End date balance 3 Money-weighted returns 11 +Your account's balance at June 30, 2020. Money-weighted returns is a net of fees calculation for your portfolio performance including +the timing of deposits and withdrawals. +Market value 4 As an example, if you contribute $100 into your portfolio, this contribution is included in the +Market value is the current value of an asset. It is displayed in Canadian Dollars (CAD) unless calculation for your money-weighted return. Read more how returns are calculated here. +otherwise labelled. Sample calculation +Book cost 5 You decide to fund your Wealthsimple account with $1,000 on January 1st. The first 364 +days of the year are great and your portfolio earns $100. You decide to add another +Book cost reflects the total of what you paid to purchase an asset including all transaction $100,000 on December 31st and over that one day your portfolio loses a total of $101. +charges and reinvested distributions. It is displayed in CAD unless otherwise labelled. Book What are your returns? +values marked by † indicate that a part of this information comes from your prior dealer or +adviser and may not be completely accurate. To calculate your MWR +Unrealized gain and loss6 $100,999 = ($1,000 * (1+(MWR))) + ($100,000 * (1+(MWR))^(1/365)) +Calculated profit or loss based on the differential between an asset’s market value and its MWR = -0.07865% +book cost. Gains or losses are unrealized because the asset has not been sold. It is displayed Time-weighted returns 12in CAD unless otherwise labelled. +Management Fees7 Time-weighted returns is the performance of an account over a period of time excludingdeposits and withdrawals. +We charge a percentage of your portfolio value as investment management fees. For more +details on your fees, refer to your Investor Policy Statement in My Documents. Adjusted cost base +13 +Trailer fees are charged for your Smart Savings account(s) outside of the interest rate you An adjusted calculation of book cost used for determining the cost of an investment +receive. Here's an example: Your account shows you're receiving 1.50% interest and the specifically for tax purposes. Adjusted cost base is used by the Canada Revenue Agency to +trailer fee is 0.50%, you'll still be receiving 1.50% in interest with this trailer fee deducted. determine capital gains or losses for income tax purposes. This calculation is based on the +securities you hold with us. If you hold the same securities with another adviser or dealer, you +Net proceeds8 will need to recalculate your adjusted cost base for that security. +Wealthsimple Inc. 400 - 80 Spadina Avenue, Toronto Ontario M5V 2J4 Canada Conversion rate $1.00 USD = $1.35750 CAD +Wealthsimple Prepared for: Jon Doe, 123 Main Street, Toronto ON M4W2G4 CA Page 8 of 8 +Glossary +Realized gain and loss14 +Actual profit or loss of an asset that has been sold. This number doesn't include fees and +expenses deducted during the sale of asset(s). It is displayed in CAD unless otherwise +labelled. Realized returns marked by ‡ indicate incomplete history required to calculate your +adjusted cost base. Please contact support@wealthsimple. +Wealthsimple Inc. 400 - 80 Spadina Avenue, Toronto Ontario M5V 2J4 Canada Conversion rate $1.00 USD = $1.35750 CAD \ No newline at end of file diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/WealthsimpleInvestmentsIncPDFExtractorTest.java b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/WealthsimpleInvestmentsIncPDFExtractorTest.java new file mode 100644 index 0000000000..5683dbc59d --- /dev/null +++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/wealthsimpleInvestmentsInc/WealthsimpleInvestmentsIncPDFExtractorTest.java @@ -0,0 +1,3940 @@ +package name.abuchen.portfolio.datatransfer.pdf.wealthsimpleInvestmentsInc; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsEmptyCollection.empty; +import static org.junit.Assert.assertNull; + +import java.text.MessageFormat; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.junit.Test; + +import name.abuchen.portfolio.Messages; +import name.abuchen.portfolio.datatransfer.Extractor; +import name.abuchen.portfolio.datatransfer.Extractor.BuySellEntryItem; +import name.abuchen.portfolio.datatransfer.Extractor.Item; +import name.abuchen.portfolio.datatransfer.Extractor.NonImportableItem; +import name.abuchen.portfolio.datatransfer.Extractor.SecurityItem; +import name.abuchen.portfolio.datatransfer.Extractor.TransactionItem; +import name.abuchen.portfolio.datatransfer.ImportAction.Status; +import name.abuchen.portfolio.datatransfer.actions.AssertImportActions; +import name.abuchen.portfolio.datatransfer.actions.CheckCurrenciesAction; +import name.abuchen.portfolio.datatransfer.pdf.PDFInputFile; +import name.abuchen.portfolio.datatransfer.pdf.WealthsimpleInvestmentsIncPDFExtractor; +import name.abuchen.portfolio.model.Account; +import name.abuchen.portfolio.model.AccountTransaction; +import name.abuchen.portfolio.model.BuySellEntry; +import name.abuchen.portfolio.model.Client; +import name.abuchen.portfolio.model.PortfolioTransaction; +import name.abuchen.portfolio.model.Security; +import name.abuchen.portfolio.model.Transaction.Unit; +import name.abuchen.portfolio.money.CurrencyUnit; +import name.abuchen.portfolio.money.Money; +import name.abuchen.portfolio.money.Values; + +public class WealthsimpleInvestmentsIncPDFExtractorTest +{ + @Test + public void testDepotStatement01() + { + Client client = new Client(); + + WealthsimpleInvestmentsIncPDFExtractor extractor = new WealthsimpleInvestmentsIncPDFExtractor(client); + + List errors = new ArrayList<>(); + + List results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "DepotStatement01.txt"), errors); + + assertThat(errors, empty()); + assertThat(results.size(), is(83)); + new AssertImportActions().check(results, "CAD"); + + // check security + Security security1 = results.stream().filter(SecurityItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security1.getTickerSymbol(), is("ZFL")); + assertThat(security1.getName(), is("BMO Long Federal Bond ETF")); + assertThat(security1.getCurrencyCode(), is("CAD")); + + Security security2 = results.stream().filter(SecurityItem.class::isInstance).skip(1).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security2.getTickerSymbol(), is("QTIP")); + assertThat(security2.getName(), is("Mackenzie Financial Corp")); + assertThat(security2.getCurrencyCode(), is("CAD")); + + Security security3 = results.stream().filter(SecurityItem.class::isInstance).skip(2).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security3.getTickerSymbol(), is("ZAG")); + assertThat(security3.getName(), is("BMO AGGREGATE BOND INDEX ETF")); + assertThat(security3.getCurrencyCode(), is("CAD")); + + Security security4 = results.stream().filter(SecurityItem.class::isInstance).skip(3).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security4.getTickerSymbol(), is("XSH")); + assertThat(security4.getName(), is("iShares Core Canadian ST Corp")); + assertThat(security4.getCurrencyCode(), is("CAD")); + + Security security5 = results.stream().filter(SecurityItem.class::isInstance).skip(4).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security5.getTickerSymbol(), is("GLDM")); + assertThat(security5.getName(), is("World Gold Trust")); + assertThat(security5.getCurrencyCode(), is("CAD")); + + Security security6 = results.stream().filter(SecurityItem.class::isInstance).skip(5).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security6.getTickerSymbol(), is("EEMV")); + assertThat(security6.getName(), is("iShares MSCI Emerg Min Vol ETF")); + assertThat(security6.getCurrencyCode(), is("CAD")); + + Security security7 = results.stream().filter(SecurityItem.class::isInstance).skip(6).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security7.getTickerSymbol(), is("ACWV")); + assertThat(security7.getName(), is("iShares Edge MSCI Min Vol Global ETF")); + assertThat(security7.getCurrencyCode(), is("CAD")); + + Security security8 = results.stream().filter(SecurityItem.class::isInstance).skip(7).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security8.getTickerSymbol(), is("VTI")); + assertThat(security8.getName(), is("Vanguard Total Stock Market ETF")); + assertThat(security8.getCurrencyCode(), is("CAD")); + + Security security9 = results.stream().filter(SecurityItem.class::isInstance).skip(8).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security9.getTickerSymbol(), is("XIC")); + assertThat(security9.getName(), is("iShares Core S&P/TSX Capped Composite Index ETF")); + assertThat(security9.getCurrencyCode(), is("CAD")); + + Security security10 = results.stream().filter(SecurityItem.class::isInstance).skip(9).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security10.getTickerSymbol(), is("XEF")); + assertThat(security10.getName(), is("iShares Core MSCI EAFE IMI Index ETF")); + assertThat(security10.getCurrencyCode(), is("CAD")); + + // check 1st cancellation without ticker symbol transaction + NonImportableItem Cancelations = (NonImportableItem) results.stream() + .filter(NonImportableItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(Cancelations.getTypeInformation(), is(MessageFormat.format(Messages.MsgMissingTickerSymbol, "Mackenzie US TIPS Index ETF (CAD-Hedged) -> 2020-07-10T00:00"))); + assertNull(Cancelations.getSecurity()); + assertNull(Cancelations.getDate()); + assertThat(Cancelations.getNote(), is("DepotStatement01.txt")); + + // check 2nd cancellation without ticker symbol transaction + Cancelations = (NonImportableItem) results.stream().filter(NonImportableItem.class::isInstance).skip(1) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(Cancelations.getTypeInformation(), is(MessageFormat.format(Messages.MsgMissingTickerSymbol, "Mackenzie US TIPS Index ETF (CAD-Hedged) -> 2020-06-09T00:00"))); + assertNull(Cancelations.getSecurity()); + assertNull(Cancelations.getDate()); + assertThat(Cancelations.getNote(), is("DepotStatement01.txt")); + + // check 3rd cancellation without ticker symbol transaction + Cancelations = (NonImportableItem) results.stream().filter(NonImportableItem.class::isInstance).skip(2) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(Cancelations.getTypeInformation(), is(MessageFormat.format(Messages.MsgMissingTickerSymbol, "Mackenzie US TIPS Index ETF (CAD-Hedged) -> 2020-05-11T00:00"))); + assertNull(Cancelations.getSecurity()); + assertNull(Cancelations.getDate()); + assertThat(Cancelations.getNote(), is("DepotStatement01.txt")); + + // check transaction + Iterator iter = results.stream().filter(TransactionItem.class::isInstance).iterator(); + assertThat(results.stream().filter(TransactionItem.class::isInstance).count(), is(38L)); + + Item item = iter.next(); + + // assert transaction + AccountTransaction transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.REMOVAL)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-07-02T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(1000.00)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.DEPOSIT)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(100.00)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.DEPOSIT)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-05-02T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(100.00)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.DEPOSIT)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(5000.00)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.DEPOSIT)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(5000.00)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + // check 1st buy sell transaction + BuySellEntry entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-12-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.7582))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(35.04)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(35.04)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 2nd buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(1).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-10-05T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.6794))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(13.82)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(13.82)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 3rd buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(2).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-09-25T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(14.0853))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1521.49)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1521.49)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 4th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(3).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-09-25T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(50.9493))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1055.16)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1055.16)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 5th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(4).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-09-25T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(88.9806))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1493.09)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1493.09)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 6th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(5).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-09-25T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(79.9364))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1582.74)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1582.74)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 7th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(6).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-09-25T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(15.0926))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(376.84)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(376.84)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 8th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(7).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-08-07T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.5764))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(12.19)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(12.19)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 9th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(8).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-07-03T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.0702))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(22.32)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(22.32)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 10th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(9).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.7993))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(128.87)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(128.87)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 11th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(10).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.4814))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(57.32)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(57.32)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 12th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(11).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.8204))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(172.91)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(172.91)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 13th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(12).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(3.3852))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(82.43)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(82.43)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 14th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(13).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(4.7504))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(138.26)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(138.26)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 15th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(14).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(9.2542))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(154.73)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(154.73)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 16th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(15).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.3119))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(33.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(33.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 17th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(16).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(10.9669))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(228.77)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(228.77)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 18th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(17).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-26T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.72))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(28.75)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(28.75)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 19th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(18).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.9932))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(16.39)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(16.39)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 20th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(19).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(4.1164))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(84.16)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(84.16)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 21th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(20).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.0232))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(2.78)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(2.78)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 22th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(21).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.0357))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(2.50)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(2.50)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 23th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(22).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.6774))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(27.71)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(27.71)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 24th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(23).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(3.1217))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(64.15)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(64.15)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 25th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(24).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(22.6219))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 26th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(25).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(8.8192))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(996.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(996.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 27th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(26).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(5.591))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(996.01)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(996.01)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 27th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(26).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(5.591))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(996.01)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(996.01)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 28th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(27).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(14.3972))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 29th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(28).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(39.0135))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(996.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(996.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 30th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(29).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(23.8851))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(498.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(498.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 31th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(30).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(93.8442))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 32th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(31).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(98.7605))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1992.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1992.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 1st dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(5) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-12-22T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(8.3610))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(8.55)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(9.83)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(1.28)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 2nd dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(6) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-12-22T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(20.8583))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(25.40)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(29.21)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(3.81)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 3rd dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(7) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-12-03T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(148.3070))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(6.38)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(6.38)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 4th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(8) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-11-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(79.9364))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.44)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.44)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 5th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(9) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-11-03T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(148.3070))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(6.38)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(6.38)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 6th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(10) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-10-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(79.9364))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.44)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.44)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 7th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(11) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-10-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(4.7706))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.29)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.93)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.64)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 8th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(12) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-10-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(147.6276))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(6.35)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(6.35)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 9th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(13) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-09-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(20.4999))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.30)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.30)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 10th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(14) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-09-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(88.9806))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.56)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.56)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 11th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(15) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-09-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(96.6783))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.16)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.16)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 12th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(16) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-08-05T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(88.9806))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.56)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.56)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 13th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(17) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-08-05T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(96.1019))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.13)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.13)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 14th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(18) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-07-06T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(96.5148))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.86)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.86)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 15th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(19) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-07-06T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(105.9986))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.56)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.56)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 16th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(20) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-07-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(5.5910))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(5.32)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(6.12)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.80)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 17th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(21) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(39.0135))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(13.93)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(13.93)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 18th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(22) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(23.8851))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(5.23)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(5.23)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 19th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(23) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-23T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(8.8424))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(11.02)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(12.67)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(1.65)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 20th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(24) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-23T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(22.6576))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(16.93)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(19.47)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(2.54)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 21th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(25) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(101.8822))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.38)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.38)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 22th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(26) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(95.5216))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.82)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.82)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 23th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(27) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(98.7605))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.25)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.25)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 24th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(28) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(93.8442))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.75)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.75)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check fee transaction + iter = results.stream().filter(TransactionItem.class::isInstance).skip(29).iterator(); + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-12-31T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.42 - 0.26 + 0.21)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-11-30T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.25 - 0.09 + 0.21)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-10-31T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.33 - 0.09 + 0.21)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-09-30T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.15 - 0.08 + 0.20)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-08-31T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.34 - 0.09 + 0.21)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-07-31T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.30 - 0.51 + 0.19)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.46 - 0.49 + 0.20)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-05-31T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.51 - 0.47 + 0.20)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-04-30T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.08 - 0.42 + 0.18)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + } + + @Test + public void testtestDepotStatement01WithAllSecuritiesInUSD() + { + Security security6 = new Security("iShares MSCI Emerg Min Vol ETF", CurrencyUnit.USD); + security6.setTickerSymbol("EEMV"); + + Security security7 = new Security("iShares Edge MSCI Min Vol Global ETF", CurrencyUnit.USD); + security7.setTickerSymbol("ACWV"); + + Security security8 = new Security("Vanguard Total Stock Market ETF", CurrencyUnit.USD); + security8.setTickerSymbol("VTI"); + + Client client = new Client(); + client.addSecurity(security6); + client.addSecurity(security7); + client.addSecurity(security8); + + WealthsimpleInvestmentsIncPDFExtractor extractor = new WealthsimpleInvestmentsIncPDFExtractor(client); + + CheckCurrenciesAction c = new CheckCurrenciesAction(); + Account account = new Account(); + account.setCurrencyCode("CAD"); + + List errors = new ArrayList<>(); + + List results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "DepotStatement01.txt"), errors); + + // check security + Security security1 = results.stream().filter(SecurityItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security1.getTickerSymbol(), is("ZFL")); + assertThat(security1.getName(), is("BMO Long Federal Bond ETF")); + assertThat(security1.getCurrencyCode(), is("CAD")); + + Security security2 = results.stream().filter(SecurityItem.class::isInstance).skip(1).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security2.getTickerSymbol(), is("QTIP")); + assertThat(security2.getName(), is("Mackenzie Financial Corp")); + assertThat(security2.getCurrencyCode(), is("CAD")); + + Security security3 = results.stream().filter(SecurityItem.class::isInstance).skip(2).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security3.getTickerSymbol(), is("ZAG")); + assertThat(security3.getName(), is("BMO AGGREGATE BOND INDEX ETF")); + assertThat(security3.getCurrencyCode(), is("CAD")); + + Security security4 = results.stream().filter(SecurityItem.class::isInstance).skip(3).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security4.getTickerSymbol(), is("XSH")); + assertThat(security4.getName(), is("iShares Core Canadian ST Corp")); + assertThat(security4.getCurrencyCode(), is("CAD")); + + Security security5 = results.stream().filter(SecurityItem.class::isInstance).skip(4).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security5.getTickerSymbol(), is("GLDM")); + assertThat(security5.getName(), is("World Gold Trust")); + assertThat(security5.getCurrencyCode(), is("CAD")); + + Security security9 = results.stream().filter(SecurityItem.class::isInstance).skip(5).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security9.getTickerSymbol(), is("XIC")); + assertThat(security9.getName(), is("iShares Core S&P/TSX Capped Composite Index ETF")); + assertThat(security9.getCurrencyCode(), is("CAD")); + + Security security10 = results.stream().filter(SecurityItem.class::isInstance).skip(6).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security10.getTickerSymbol(), is("XEF")); + assertThat(security10.getName(), is("iShares Core MSCI EAFE IMI Index ETF")); + assertThat(security10.getCurrencyCode(), is("CAD")); + + assertThat(errors, empty()); + assertThat(results.size(), is(80)); + + // check 1st cancellation without ticker symbol transaction + NonImportableItem Cancelations = (NonImportableItem) results.stream() + .filter(NonImportableItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(Cancelations.getTypeInformation(), is(MessageFormat.format(Messages.MsgMissingTickerSymbol, "Mackenzie US TIPS Index ETF (CAD-Hedged) -> 2020-07-10T00:00"))); + assertNull(Cancelations.getSecurity()); + assertNull(Cancelations.getDate()); + assertThat(Cancelations.getNote(), is("DepotStatement01.txt")); + + // check 2nd cancellation without ticker symbol transaction + Cancelations = (NonImportableItem) results.stream().filter(NonImportableItem.class::isInstance).skip(1) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(Cancelations.getTypeInformation(), is(MessageFormat.format(Messages.MsgMissingTickerSymbol, "Mackenzie US TIPS Index ETF (CAD-Hedged) -> 2020-06-09T00:00"))); + assertNull(Cancelations.getSecurity()); + assertNull(Cancelations.getDate()); + assertThat(Cancelations.getNote(), is("DepotStatement01.txt")); + + // check 3rd cancellation without ticker symbol transaction + Cancelations = (NonImportableItem) results.stream().filter(NonImportableItem.class::isInstance).skip(2) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(Cancelations.getTypeInformation(), is(MessageFormat.format(Messages.MsgMissingTickerSymbol, "Mackenzie US TIPS Index ETF (CAD-Hedged) -> 2020-05-11T00:00"))); + assertNull(Cancelations.getSecurity()); + assertNull(Cancelations.getDate()); + assertThat(Cancelations.getNote(), is("DepotStatement01.txt")); + + // check transaction + Iterator iter = results.stream().filter(TransactionItem.class::isInstance).iterator(); + assertThat(results.stream().filter(TransactionItem.class::isInstance).count(), is(38L)); + + Item item = iter.next(); + + // assert transaction + AccountTransaction transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.REMOVAL)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-07-02T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(1000.00)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.DEPOSIT)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(100.00)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.DEPOSIT)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-05-02T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(100.00)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.DEPOSIT)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(5000.00)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.DEPOSIT)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(5000.00)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + // check 1st buy sell transaction + BuySellEntry entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-12-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.7582))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(35.04)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(35.04)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 2nd buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(1).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-10-05T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.6794))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(13.82)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(13.82)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 3rd buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(2).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-09-25T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(14.0853))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1521.49)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1521.49)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 4th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(3).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-09-25T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(50.9493))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1055.16)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1055.16)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 5th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(4).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-09-25T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(88.9806))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1493.09)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1493.09)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 6th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(5).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-09-25T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(79.9364))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1582.74)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1582.74)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 7th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(6).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-09-25T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(15.0926))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(376.84)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(376.84)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 8th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(7).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-08-07T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.5764))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(12.19)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(12.19)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 9th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(8).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-07-03T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.0702))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(22.32)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(22.32)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 10th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(9).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.7993))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(128.87)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(128.87)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 11th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(10).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.4814))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(57.32)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(57.32)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 12th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(11).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.8204))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(172.91)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(172.91)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 13th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(12).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(3.3852))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(82.43)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(82.43)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 14th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(13).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(4.7504))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(138.26)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(138.26)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 15th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(14).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(9.2542))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(154.73)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(154.73)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 16th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(15).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.3119))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(33.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(33.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 17th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(16).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(10.9669))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(228.77)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(228.77)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 18th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(17).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-26T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.72))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(28.75)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(28.75)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 19th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(18).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.9932))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(16.39)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(16.39)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 20th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(19).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(4.1164))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(84.16)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(84.16)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 21th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(20).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.0232))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(2.78)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(2.78)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 22th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(21).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.0357))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(2.50)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(2.50)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 23th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(22).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.6774))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(27.71)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(27.71)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 24th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(23).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(3.1217))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(64.15)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(64.15)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 25th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(24).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(22.6219))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 26th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(25).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(8.8192))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(996.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(996.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 27th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(26).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(5.591))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(996.01)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(996.01)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 27th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(26).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(5.591))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(996.01)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(996.01)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 28th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(27).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(14.3972))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 29th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(28).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(39.0135))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(996.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(996.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 30th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(29).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(23.8851))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(498.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(498.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 31th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(30).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(93.8442))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1494.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 32th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(31).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-04-02T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(98.7605))); + assertThat(entry.getSource(), is("DepotStatement01.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(1992.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(1992.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 1st dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(5) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-12-22T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(8.3610))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(8.55)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(9.83)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(1.28)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + Unit grossValueUnit = transaction.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(IllegalArgumentException::new); + assertThat(grossValueUnit.getForex(), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(7.60)))); + + Status s = c.process(transaction, account); + assertThat(s, is(Status.OK_STATUS)); + + // check 2nd dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(6) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-12-22T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(20.8583))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(25.40)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(29.21)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(3.81)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + grossValueUnit = transaction.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(IllegalArgumentException::new); + assertThat(grossValueUnit.getForex(), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(22.60)))); + + s = c.process(transaction, account); + assertThat(s, is(Status.OK_STATUS)); + + // check 3rd dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(7) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-12-03T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(148.3070))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(6.38)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(6.38)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 4th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(8) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-11-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(79.9364))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.44)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.44)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 5th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(9) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-11-03T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(148.3070))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(6.38)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(6.38)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 6th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(10) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-10-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(79.9364))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.44)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.44)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 7th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(11) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-10-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(4.7706))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.29)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.93)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.64)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + grossValueUnit = transaction.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(IllegalArgumentException::new); + assertThat(grossValueUnit.getForex(), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(3.70)))); + + s = c.process(transaction, account); + assertThat(s, is(Status.OK_STATUS)); + + // check 8th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(12) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-10-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(147.6276))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(6.35)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(6.35)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 9th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(13) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-09-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(20.4999))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.30)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.30)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 10th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(14) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-09-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(88.9806))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.56)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.56)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 11th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(15) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-09-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(96.6783))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.16)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.16)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 12th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(16) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-08-05T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(88.9806))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.56)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.56)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 13th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(17) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-08-05T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(96.1019))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.13)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.13)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 14th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(18) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-07-06T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(96.5148))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.86)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.86)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 15th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(19) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-07-06T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(105.9986))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.56)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.56)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 16th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(20) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-07-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(5.5910))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(5.32)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(6.12)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.80)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + grossValueUnit = transaction.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(IllegalArgumentException::new); + assertThat(grossValueUnit.getForex(), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(4.50)))); + + s = c.process(transaction, account); + assertThat(s, is(Status.OK_STATUS)); + + // check 17th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(21) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(39.0135))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(13.93)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(13.93)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 18th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(22) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(23.8851))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(5.23)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(5.23)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 19th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(23) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-23T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(8.8424))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(11.02)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(12.67)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(1.65)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + grossValueUnit = transaction.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(IllegalArgumentException::new); + assertThat(grossValueUnit.getForex(), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(9.36)))); + + s = c.process(transaction, account); + assertThat(s, is(Status.OK_STATUS)); + + // check 20th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(24) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-23T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(22.6576))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(16.93)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(19.47)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(2.54)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + grossValueUnit = transaction.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(IllegalArgumentException::new); + assertThat(grossValueUnit.getForex(), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(14.38)))); + + s = c.process(transaction, account); + assertThat(s, is(Status.OK_STATUS)); + + // check 21th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(25) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(101.8822))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.38)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.38)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 22th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(26) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(95.5216))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.82)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.82)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 23th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(27) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(98.7605))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.25)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.25)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 24th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(28) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-05-04T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(93.8442))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.75)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.75)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check fee transaction + iter = results.stream().filter(TransactionItem.class::isInstance).skip(29).iterator(); + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-12-31T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.42 - 0.26 + 0.21)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-11-30T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.25 - 0.09 + 0.21)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-10-31T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.33 - 0.09 + 0.21)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-09-30T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.15 - 0.08 + 0.20)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-08-31T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.34 - 0.09 + 0.21)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-07-31T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.30 - 0.51 + 0.19)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.46 - 0.49 + 0.20)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-05-31T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.51 - 0.47 + 0.20)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-04-30T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.08 - 0.42 + 0.18)))); + assertThat(transaction.getSource(), is("DepotStatement01.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + } + + @Test + public void testDepotStatement02() + { + Client client = new Client(); + + WealthsimpleInvestmentsIncPDFExtractor extractor = new WealthsimpleInvestmentsIncPDFExtractor(client); + + List errors = new ArrayList<>(); + + List results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "DepotStatement02.txt"), errors); + + assertThat(errors, empty()); + assertThat(results.size(), is(28)); + new AssertImportActions().check(results, "CAD"); + + // check security + Security security1 = results.stream().filter(SecurityItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security1.getTickerSymbol(), is("EEMV")); + assertThat(security1.getName(), is("iShares MSCI Emerg Min Vol ETF")); + assertThat(security1.getCurrencyCode(), is("CAD")); + + Security security2 = results.stream().filter(SecurityItem.class::isInstance).skip(1).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security2.getTickerSymbol(), is("ACWV")); + assertThat(security2.getName(), is("iShares Edge MSCI Min Vol Global ETF")); + assertThat(security2.getCurrencyCode(), is("CAD")); + + Security security3 = results.stream().filter(SecurityItem.class::isInstance).skip(2).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security3.getTickerSymbol(), is("VTI")); + assertThat(security3.getName(), is("Vanguard Total Stock Market ETF")); + assertThat(security3.getCurrencyCode(), is("CAD")); + + Security security4 = results.stream().filter(SecurityItem.class::isInstance).skip(3).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security4.getTickerSymbol(), is("XIC")); + assertThat(security4.getName(), is("iShares Core S&P/TSX Capped Composite Index ETF")); + assertThat(security4.getCurrencyCode(), is("CAD")); + + Security security5 = results.stream().filter(SecurityItem.class::isInstance).skip(4).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security5.getTickerSymbol(), is("XEF")); + assertThat(security5.getName(), is("iShares Core MSCI EAFE IMI Index ETF")); + assertThat(security5.getCurrencyCode(), is("CAD")); + + Security security6 = results.stream().filter(SecurityItem.class::isInstance).skip(5).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security6.getTickerSymbol(), is("ZAG")); + assertThat(security6.getName(), is("BMO AGGREGATE BOND INDEX ETF")); + assertThat(security6.getCurrencyCode(), is("CAD")); + + Security security7 = results.stream().filter(SecurityItem.class::isInstance).skip(6).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security7.getTickerSymbol(), is("QTIP")); + assertThat(security7.getName(), is("Mackenzie Financial Corp")); + assertThat(security7.getCurrencyCode(), is("CAD")); + + Security security8 = results.stream().filter(SecurityItem.class::isInstance).skip(7).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security8.getTickerSymbol(), is("ZFL")); + assertThat(security8.getName(), is("BMO Long Federal Bond ETF")); + assertThat(security8.getCurrencyCode(), is("CAD")); + + // check 1st cancellation without ticker symbol transaction + NonImportableItem Cancelations = (NonImportableItem) results.stream() + .filter(NonImportableItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(Cancelations.getTypeInformation(), is(MessageFormat.format(Messages.MsgMissingTickerSymbol, "Mackenzie US TIPS Index ETF (CAD-Hedged) -> 2020-06-09T00:00"))); + assertNull(Cancelations.getSecurity()); + assertNull(Cancelations.getDate()); + assertThat(Cancelations.getNote(), is("DepotStatement02.txt")); + + // check transaction + Iterator iter = results.stream().filter(TransactionItem.class::isInstance).iterator(); + assertThat(results.stream().filter(TransactionItem.class::isInstance).count(), is(8L)); + + Item item = iter.next(); + + // assert transaction + AccountTransaction transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.DEPOSIT)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(100.00)))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + // check 1st buy sell transaction + BuySellEntry entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.7993))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(128.87)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(128.87)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 2th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(1).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.4814))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(57.32)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(57.32)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 3th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(2).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.8204))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(172.91)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(172.91)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 4th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(3).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(3.3852))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(82.43)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(82.43)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 5th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(4).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(4.7504))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(138.26)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(138.26)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 6th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(5).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(9.2542))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(154.73)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(154.73)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 7th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(6).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.3119))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(33.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(33.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 8th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(7).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(10.9669))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(228.77)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(228.77)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 9th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(8).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-26T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.72))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(28.75)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(28.75)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 10th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(9).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.9932))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(16.39)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(16.39)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 11th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(10).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(4.1164))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(84.16)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(84.16)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 1th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(1) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(39.0135))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(13.93)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(13.93)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 2nd dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(2) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(23.8851))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(5.23)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(5.23)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 3rd dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(3) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-23T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(8.8424))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(11.02)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(12.67)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(1.65)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 4th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(4) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-23T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(22.6576))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(16.93)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(19.47)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(2.54)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 5th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(5) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(101.8822))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.38)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.38)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 6th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(6) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(95.5216))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.82)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.82)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check fee transaction + iter = results.stream().filter(TransactionItem.class::isInstance).skip(7).iterator(); + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.46 - 0.49 + 0.20)))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + } + + @Test + public void testtestDepotStatement02WithAllSecuritiesInUSD() + { + Security security1 = new Security("iShares MSCI Emerg Min Vol ETF", CurrencyUnit.USD); + security1.setTickerSymbol("EEMV"); + + Security security2 = new Security("iShares Edge MSCI Min Vol Global ETF", CurrencyUnit.USD); + security2.setTickerSymbol("ACWV"); + + Client client = new Client(); + client.addSecurity(security1); + client.addSecurity(security2); + + WealthsimpleInvestmentsIncPDFExtractor extractor = new WealthsimpleInvestmentsIncPDFExtractor(client); + + CheckCurrenciesAction c = new CheckCurrenciesAction(); + Account account = new Account(); + account.setCurrencyCode("CAD"); + + List errors = new ArrayList<>(); + + List results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "DepotStatement02.txt"), errors); + + // check security + Security security3 = results.stream().filter(SecurityItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security3.getTickerSymbol(), is("VTI")); + assertThat(security3.getName(), is("Vanguard Total Stock Market ETF")); + assertThat(security3.getCurrencyCode(), is("CAD")); + + Security security4 = results.stream().filter(SecurityItem.class::isInstance).skip(1).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security4.getTickerSymbol(), is("XIC")); + assertThat(security4.getName(), is("iShares Core S&P/TSX Capped Composite Index ETF")); + assertThat(security4.getCurrencyCode(), is("CAD")); + + Security security5 = results.stream().filter(SecurityItem.class::isInstance).skip(2).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security5.getTickerSymbol(), is("XEF")); + assertThat(security5.getName(), is("iShares Core MSCI EAFE IMI Index ETF")); + assertThat(security5.getCurrencyCode(), is("CAD")); + + Security security6 = results.stream().filter(SecurityItem.class::isInstance).skip(3).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security6.getTickerSymbol(), is("ZAG")); + assertThat(security6.getName(), is("BMO AGGREGATE BOND INDEX ETF")); + assertThat(security6.getCurrencyCode(), is("CAD")); + + Security security7 = results.stream().filter(SecurityItem.class::isInstance).skip(4).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security7.getTickerSymbol(), is("QTIP")); + assertThat(security7.getName(), is("Mackenzie Financial Corp")); + assertThat(security7.getCurrencyCode(), is("CAD")); + + Security security8 = results.stream().filter(SecurityItem.class::isInstance).skip(5).findFirst() + .orElseThrow(IllegalArgumentException::new).getSecurity(); + assertThat(security8.getTickerSymbol(), is("ZFL")); + assertThat(security8.getName(), is("BMO Long Federal Bond ETF")); + assertThat(security8.getCurrencyCode(), is("CAD")); + + assertThat(errors, empty()); + assertThat(results.size(), is(26)); + + // check 1st cancellation without ticker symbol transaction + NonImportableItem Cancelations = (NonImportableItem) results.stream() + .filter(NonImportableItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(Cancelations.getTypeInformation(), is(MessageFormat.format(Messages.MsgMissingTickerSymbol, "Mackenzie US TIPS Index ETF (CAD-Hedged) -> 2020-06-09T00:00"))); + assertNull(Cancelations.getSecurity()); + assertNull(Cancelations.getDate()); + assertThat(Cancelations.getNote(), is("DepotStatement02.txt")); + + // check transaction + Iterator iter = results.stream().filter(TransactionItem.class::isInstance).iterator(); + assertThat(results.stream().filter(TransactionItem.class::isInstance).count(), is(8L)); + + Item item = iter.next(); + + // assert transaction + AccountTransaction transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.DEPOSIT)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(100.00)))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + // check 1st buy sell transaction + BuySellEntry entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.7993))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(128.87)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(128.87)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 2th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(1).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.4814))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(57.32)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(57.32)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 3th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(2).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.8204))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(172.91)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(172.91)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 4th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(3).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(3.3852))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(82.43)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(82.43)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 5th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(4).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(4.7504))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(138.26)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(138.26)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 6th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(5).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(9.2542))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(154.73)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(154.73)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 7th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(6).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.3119))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(33.00)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(33.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 8th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(7).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.SELL)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.SELL)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-29T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(10.9669))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(228.77)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(228.77)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 9th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(8).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-26T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1.72))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(28.75)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(28.75)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 10th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(9).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(0.9932))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(16.39)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(16.39)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 11th buy sell transaction + entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).skip(10).findFirst() + .orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); + assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); + + assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2020-06-01T00:00"))); + assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(4.1164))); + assertThat(entry.getSource(), is("DepotStatement02.txt")); + assertNull(entry.getNote()); + + assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(84.16)))); + assertThat(entry.getPortfolioTransaction().getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(84.16)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + // check 1th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(1) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(39.0135))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(13.93)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(13.93)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 2nd dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(2) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(23.8851))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(5.23)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(5.23)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 3rd dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(3) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-23T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(8.8424))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(11.02)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(12.67)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(1.65)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + Unit grossValueUnit = transaction.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(IllegalArgumentException::new); + assertThat(grossValueUnit.getForex(), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(9.36)))); + + Status s = c.process(transaction, account); + assertThat(s, is(Status.OK_STATUS)); + + // check 4th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(4) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-23T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(22.6576))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(16.93)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(19.47)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(2.54)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + grossValueUnit = transaction.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(IllegalArgumentException::new); + assertThat(grossValueUnit.getForex(), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(14.38)))); + + s = c.process(transaction, account); + assertThat(s, is(Status.OK_STATUS)); + + // check 5th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(5) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(101.8822))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(4.38)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(4.38)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check 6th dividends transaction + transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance).skip(6) + .findFirst().orElseThrow(IllegalArgumentException::new).getSubject(); + + assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); + + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-02T00:00"))); + assertThat(transaction.getShares(), is(Values.Share.factorize(95.5216))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertNull(transaction.getNote()); + + assertThat(transaction.getMonetaryAmount(), + is(Money.of("CAD", Values.Amount.factorize(3.82)))); + assertThat(transaction.getGrossValue(), + is(Money.of("CAD", Values.Amount.factorize(3.82)))); + assertThat(transaction.getUnitSum(Unit.Type.TAX), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + assertThat(transaction.getUnitSum(Unit.Type.FEE), + is(Money.of("CAD", Values.Amount.factorize(0.00)))); + + assertThat(transaction.getUnit(Unit.Type.GROSS_VALUE).isPresent(), is(false)); + + // check fee transaction + iter = results.stream().filter(TransactionItem.class::isInstance).skip(7).iterator(); + item = iter.next(); + + // assert transaction + transaction = (AccountTransaction) item.getSubject(); + assertThat(transaction.getType(), is(AccountTransaction.Type.FEES)); + assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2020-06-30T00:00"))); + assertThat(transaction.getMonetaryAmount(), is(Money.of("CAD", Values.Amount.factorize(4.46 - 0.49 + 0.20)))); + assertThat(transaction.getSource(), is("DepotStatement02.txt")); + assertThat(transaction.getNote(), is("Management fee to Wealthsimple")); + } +} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFExtractorUtils.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFExtractorUtils.java index eb92e6ddf6..5ff2f2aae4 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFExtractorUtils.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFExtractorUtils.java @@ -40,15 +40,25 @@ public class PDFExtractorUtils private static final DateTimeFormatter[] DATE_FORMATTER_US = { // DateTimeFormatter.ofPattern("dd LLL yyyy", Locale.US), //$NON-NLS-1$ - DateTimeFormatter.ofPattern("d MMM yyyy", Locale.US) }; //$NON-NLS-1$ + DateTimeFormatter.ofPattern("d LLL yyyy", Locale.US) }; //$NON-NLS-1$ + + private static final DateTimeFormatter[] DATE_FORMATTER_CANADA = { // + DateTimeFormatter.ofPattern("dd LLL yyyy", Locale.CANADA) }; //$NON-NLS-1$ + + private static final DateTimeFormatter[] DATE_FORMATTER_CANADA_FRENCH = { // + DateTimeFormatter.ofPattern("dd LLL yyyy", Locale.CANADA_FRENCH) }; //$NON-NLS-1$ private static final DateTimeFormatter[] DATE_FORMATTER_UK = { // DateTimeFormatter.ofPattern("dd LLL yyyy", Locale.UK), //$NON-NLS-1$ DateTimeFormatter.ofPattern("LL/dd/yyyy", Locale.UK), //$NON-NLS-1$ - DateTimeFormatter.ofPattern("dd.MM.yyyy", Locale.UK) }; //$NON-NLS-1$ + DateTimeFormatter.ofPattern("dd.LL.yyyy", Locale.UK) }; //$NON-NLS-1$ - private static final Map LOCALE2DATE = Map.of(Locale.GERMANY, DATE_FORMATTER_GERMANY, - Locale.US, DATE_FORMATTER_US, Locale.UK, DATE_FORMATTER_UK); + private static final Map LOCALE2DATE = Map.of( // + Locale.GERMANY, DATE_FORMATTER_GERMANY, // + Locale.US, DATE_FORMATTER_US, // + Locale.CANADA, DATE_FORMATTER_CANADA, // + Locale.CANADA_FRENCH, DATE_FORMATTER_CANADA_FRENCH, // + Locale.UK, DATE_FORMATTER_UK); private static final DateTimeFormatter[] DATE_TIME_FORMATTER = { // DateTimeFormatter.ofPattern("d.M.yyyy HH:mm", Locale.GERMANY), //$NON-NLS-1$ @@ -87,9 +97,7 @@ public static void checkAndSetGrossUnit(Money gross, Money fxGross, name.abuchen Optional rate = type.getCurrentContext().getType(PDFExchangeRate.class); if (rate.isPresent()) - { t.addUnit(new Unit(Unit.Type.GROSS_VALUE, gross, fxGross, rate.get().getRate(gross.getCurrencyCode()))); - } } public static void checkAndSetTax(Money tax, Object transaction, DocumentType type) @@ -223,7 +231,7 @@ public static long asShares(String value, String language, String country) public static LocalDateTime asDate(String value, Locale... hints) { - Locale[] locales = hints.length > 0 ? hints : new Locale[] { Locale.GERMANY, Locale.US, Locale.UK }; + Locale[] locales = hints.length > 0 ? hints : new Locale[] { Locale.GERMANY, Locale.US, Locale.CANADA, Locale.CANADA_FRENCH, Locale.UK }; for (Locale l : locales) { diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFImportAssistant.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFImportAssistant.java index c76cba4208..07664a0731 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFImportAssistant.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFImportAssistant.java @@ -88,6 +88,7 @@ public PDFImportAssistant(Client client, List files) extractors.add(new UBSAGBankingAGPDFExtractor(client)); extractors.add(new UnicreditPDFExtractor(client)); extractors.add(new VBankAGPDFExtractor(client)); + extractors.add(new WealthsimpleInvestmentsIncPDFExtractor(client)); extractors.add(new WirBankPDFExtractor(client)); extractors.add(new WeberbankPDFExtractor(client)); extractors.add(new YuhPDFExtractor(client)); diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/WealthsimpleInvestmentsIncPDFExtractor.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/WealthsimpleInvestmentsIncPDFExtractor.java new file mode 100644 index 0000000000..9386e8a6bf --- /dev/null +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/WealthsimpleInvestmentsIncPDFExtractor.java @@ -0,0 +1,626 @@ +package name.abuchen.portfolio.datatransfer.pdf; + +import static name.abuchen.portfolio.datatransfer.pdf.PDFExtractorUtils.checkAndSetGrossUnit; +import static name.abuchen.portfolio.datatransfer.pdf.PDFExtractorUtils.checkAndSetTax; + +import java.math.BigDecimal; +import java.text.MessageFormat; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import name.abuchen.portfolio.Messages; +import name.abuchen.portfolio.datatransfer.pdf.PDFParser.Block; +import name.abuchen.portfolio.datatransfer.pdf.PDFParser.DocumentContext; +import name.abuchen.portfolio.datatransfer.pdf.PDFParser.DocumentType; +import name.abuchen.portfolio.datatransfer.pdf.PDFParser.Transaction; +import name.abuchen.portfolio.model.AccountTransaction; +import name.abuchen.portfolio.model.BuySellEntry; +import name.abuchen.portfolio.model.Client; +import name.abuchen.portfolio.model.PortfolioTransaction; +import name.abuchen.portfolio.money.Money; +import name.abuchen.portfolio.money.Values; + +@SuppressWarnings("nls") +public class WealthsimpleInvestmentsIncPDFExtractor extends AbstractPDFExtractor +{ + /** + * Information: + * Wealthsimple Investments Inc. is a CAD-based financial + * services company. The currency is $CAD. + * + * All securities are specified in $CAD. + * However, there is an exchange rate in $USD. + */ + + public WealthsimpleInvestmentsIncPDFExtractor(Client client) + { + super(client); + + addBankIdentifier("Wealthsimple Inc."); //$NON-NLS-1$ + + addDepotStatementTransaction(); + } + + @Override + public String getLabel() + { + return "Wealthsimple Investments Inc."; //$NON-NLS-1$ + } + + private void addDepotStatementTransaction() + { + final DocumentType type = new DocumentType("Portfolio Performance Report", (context, lines) -> { + Pattern pYear = Pattern.compile("^.*(?[\\d]{4}) Performance Report .*$"); + Pattern pCurrency = Pattern.compile("^.* Canada Conversion rate \\p{Sc}[\\.,\\d]+ [\\w]{3} = \\p{Sc}[\\.,\\d]+ (?[\\w]{3})$"); + Pattern pDividendTaxTransactions = Pattern.compile("^(?[\\w]{3,4}) " + + "(?[\\d]{2}) " + + "(?[A-Z]{3,4})" + + "[\\W]{1,3}.*: .* tax .* \\([\\.,\\d]+ [\\w]{3}, .* " + + "(?[\\w]{3})" + + " .([\\.,\\d]+\\))? .* " + + "\\-\\p{Sc}(?[\\.,\\d]+)$"); + Pattern pFeeRefundTransactions = Pattern.compile("^(?[\\w]{3,4}) " + + "(?[\\d]{2}) " + + "Promotions and discounts applied to Wealthsimple fee [\\W]{1,3} " + + "\\p{Sc}(?[\\.,\\d]+)$"); + Pattern pFeeTaxTransactions = Pattern.compile("^(?[\\w]{3,4}) " + + "(?[\\d]{2}) " + + "Sales tax on management fee to Wealthsimple [\\W]{1,3} " + + "\\-\\p{Sc}(?[\\.,\\d]+)$"); + + DividendTaxTransactionHelper dividendTaxTransactionHelper = new DividendTaxTransactionHelper(); + context.putType(dividendTaxTransactionHelper); + + FeeRefundTransactionHelper feeRefundTransactionHelper = new FeeRefundTransactionHelper(); + context.putType(feeRefundTransactionHelper); + + FeeTaxTransactionHelper feeTaxTransactionHelper = new FeeTaxTransactionHelper(); + context.putType(feeTaxTransactionHelper); + + for (String line : lines) + { + Matcher m = pYear.matcher(line); + if (m.matches()) + context.put("year", m.group("year")); + + m = pCurrency.matcher(line); + if (m.matches()) + context.put("currency", m.group("currency")); + + m = pDividendTaxTransactions.matcher(line); + if (m.matches()) + { + DividendTaxTransactionsItem item = new DividendTaxTransactionsItem(); + item.dateTime = asDate(m.group("day") + " " + m.group("month") + " " + context.get("year")); + item.tickerSymbol = m.group("tickerSymbol"); + item.currency = m.group("currency"); + item.tax = asAmount(m.group("tax")); + + dividendTaxTransactionHelper.items.add(item); + } + + m = pFeeRefundTransactions.matcher(line); + if (m.matches()) + { + FeeRefundTransactionsItem item = new FeeRefundTransactionsItem(); + item.dateTime = asDate(m.group("day") + " " + m.group("month") + " " + context.get("year")); + item.feeRefund = asAmount(m.group("feeRefund")); + + feeRefundTransactionHelper.items.add(item); + } + + m = pFeeTaxTransactions.matcher(line); + if (m.matches()) + { + FeeTaxTransactionsItem item = new FeeTaxTransactionsItem(); + item.dateTime = asDate(m.group("day") + " " + m.group("month") + " " + context.get("year")); + item.tax = asAmount(m.group("tax")); + + feeTaxTransactionHelper.items.add(item); + } + } + }); + this.addDocumentTyp(type); + + // @formatter:off + // Jul 02 Electronic Funds Transfer Out: 1,000.00 CAD – – -$1,000.00 + // Jun 01 Electronic Funds Transfer In: $100.00 CAD – – $100.00 + // May 02 settled May 04 Electronic Funds Transfer In: $100.00 CAD – – $100.00 + // @formatter:on + Block depositRemovalBlock = new Block("^[\\w]{3,4} [\\d]{2} .* Transfer (In|Out): (\\p{Sc})?[\\.,\\d]+ [\\w]{3} .* (\\-)?\\p{Sc}[\\.,\\d]+$"); + type.addBlock(depositRemovalBlock); + depositRemovalBlock.set(new Transaction() + + .subject(() -> { + AccountTransaction entry = new AccountTransaction(); + entry.setType(AccountTransaction.Type.DEPOSIT); + return entry; + }) + + .section("month", "day", "type", "amount", "currency") + .match("^(?[\\w]{3,4}) " + + "(?[\\d]{2}) " + + ".* Transfer " + + "(?(In|Out)): " + + "(\\p{Sc})?(?[\\.,\\d]+) " + + "(?[\\w]{3}) .* " + + "(\\-)?\\p{Sc}[\\.,\\d]+$") + .assign((t, v) -> { + Map context = type.getCurrentContext(); + + // Is type --> "Out" change from DEPOSIT to REMOVAL + if (v.get("type").equals("Out")) + t.setType(AccountTransaction.Type.REMOVAL); + + t.setDateTime(asDate(v.get("day") + " " + v.get("month") + " " + context.get("year"))); + t.setAmount(asAmount(v.get("amount"))); + t.setCurrencyCode(asCurrencyCode(v.get("currency"))); + }) + + .wrap(t -> { + if (t.getCurrencyCode() != null && t.getAmount() != 0) + return new TransactionItem(t); + return null; + })); + + // @formatter:off + // Jul 03 settled Jul 07 Bought 1.0702 of ZFL - BMO Long Federal Bond ETF for 22.32 CAD 1.0702 $20.86 $22.32 + // Jun 29 settled Jul 01 Sold 1.7993 of EEMV - iShares MSCI Emerg Min Vol ETF for 128.87 CAD -1.7993 $71.62 -$128.87 + // @formatter:om + Block buySellBlock = new Block("^[\\w]{3} [\\d]{2} .* (Bought|Sold) [\\.,\\d]+ of [A-Z]{3,4} [\\W]{1,3} .* for [\\.,\\d]+ [\\w]{3} (\\-)?[\\.,\\d]+ \\p{Sc}[\\.,\\d]+ (\\-)?\\p{Sc}[\\.,\\d]+$"); + type.addBlock(buySellBlock); + buySellBlock.set(new Transaction() + + .subject(() -> { + BuySellEntry entry = new BuySellEntry(); + entry.setType(PortfolioTransaction.Type.BUY); + return entry; + }) + + .section("month", "day", "type", "tickerSymbol", "name", "amount", "currency", "shares") + .match("^(?[\\w]{3,4}) " + + "(?[\\d]{2}) " + + ".* " + + "(?(Bought|Sold)) " + + "[\\.,\\d]+ of " + + "(?[A-Z]{3,4}) " + + "[\\W]{1,3} " + + "(?.*) " + + "for " + + "(?[\\.,\\d]+) " + + "(?[\\w]{3}) " + + "(\\-)?(?[\\.,\\d]+) " + + "\\p{Sc}[\\.,\\d]+ (\\-)?\\p{Sc}[\\.,\\d]+$") + .assign((t, v) -> { + DocumentContext context = type.getCurrentContext(); + + // Is type --> "Out" change from BUY to SELL + if (v.get("type").equals("Sold")) + t.setType(PortfolioTransaction.Type.SELL); + + t.setSecurity(getOrCreateSecurity(v)); + t.setDate(asDate(v.get("day") + " " + v.get("month") + " " + context.get("year"))); + t.setShares(asShares(v.get("shares"))); + + t.setAmount(asAmount(v.get("amount"))); + t.setCurrencyCode(asCurrencyCode(v.get("currency"))); + }) + + .wrap(BuySellEntryItem::new)); + + // @formatter:off + // Dec 22 ACWV-iShares Edge MSCI Min Vol Global ETF: 15-DEC-20 (record date) 8.3610 shares, gross 6.61 USD, – – $8.55 + // convert to CAD @ 1.2927 + // Dec 22 EEMV-iShares MSCI Emerg Min Vol ETF: 15-DEC-20 (record date) 20.8583 shares, gross 19.65 USD, – – $25.40 + // convert to CAD @ 1.2927 + // + // Dec 03 ZFL-BMO Long Federal Bond ETF: 30-NOV-20 (record date) 148.3070 shares – – $6.38 + // + // Oct 02 VTI - Vanguard Index STK MKT ETF: 28-SEP-20 (record date) 4.7706 shares, gross 3.22 USD, convert to CAD @ 1.3319 – – $4.29 + // + // Jun 23 ACWV-iShares Edge MSCI Min Vol Global ETF: 16-JUN-20 (record date) 8.8424 shares, gross 8.14 USD, convert to CAD – – $11.02 + // @ 1.3535 + // + // Jun 23 EEMV-iShares MSCI Emerg Min Vol ETF: 16-JUN-20 (record date) 22.6576 shares, gross 12.51 USD, convert to CAD @ – – $16.93 + // 1.3535 + // + // Jun 23 ACWV-iShares Edge MSCI Min Vol Global ETF: 16-JUN-20 (record date) 8.8424 shares, gross 8.14 USD, convert to – – $11.02 + // CAD @ 1.3535 + // @formatter:on + Block dividendBlock = new Block("^[\\w]{3,4} [\\d]{2} [A-Z]{3,4}[\\W]{1,3}.*: .* \\(record date\\) .*$"); + type.addBlock(dividendBlock); + dividendBlock.set(new Transaction() + + .subject(() -> { + AccountTransaction entry = new AccountTransaction(); + entry.setType(AccountTransaction.Type.DIVIDENDS); + return entry; + }) + + .oneOf( + section -> section + .attributes("month", "day", "tickerSymbol", "name", "shares", "fxCurrency", "amount", "currency", "exchangeRate") + .match("^(?[\\w]{3,4}) " + + "(?[\\d]{2}) " + + "(?[A-Z]{3,4})" + + "[\\W]{1,3}(?.*): .* " + + "(?[\\.,\\d]+) " + + "shares, .* [\\.,\\d]+ " + + "(?[\\w]{3}), .*[\\s\\–]{2,3} " + + "\\p{Sc}(?[\\.,\\d]+)$") + .match("^(?![\\w]{3} [\\d]{2})([\\D]+)?(?[\\w]{3})(.* )?(?[\\.,\\d]+)$") + .assign((t, v) -> { + DocumentContext context = type.getCurrentContext(); + + v.put("termCurrency", asCurrencyCode(v.get("currency"))); + v.put("baseCurrency", asCurrencyCode(v.get("fxCurrency"))); + + t.setSecurity(getOrCreateSecurity(v)); + t.setDateTime(asDate(v.get("day") + " " + v.get("month") + " " + context.get("year"))); + t.setShares(asShares(v.get("shares"))); + + t.setAmount(asAmount(v.get("amount"))); + t.setCurrencyCode(asCurrencyCode(v.get("currency"))); + + DividendTaxTransactionHelper dividendTaxTransactionHelper = context.getType(DividendTaxTransactionHelper.class).orElseGet(DividendTaxTransactionHelper::new); + Optional dividendTaxTransaction = dividendTaxTransactionHelper.findItem(t.getDateTime(), t.getSecurity().getTickerSymbol()); + if (dividendTaxTransaction.isPresent() && v.get("tickerSymbol").equalsIgnoreCase(t.getSecurity().getTickerSymbol())) + { + Money tax = Money.of(asCurrencyCode(dividendTaxTransaction.get().currency), dividendTaxTransaction.get().tax); + checkAndSetTax(tax, t, type); + } + + PDFExchangeRate rate = asExchangeRate(v); + type.getCurrentContext().putType(rate); + + Money gross = Money.of(asCurrencyCode(v.get("currency")), t.getGrossValueAmount()); + Money fxGross = rate.convert(asCurrencyCode(v.get("fxCurrency")), gross); + + checkAndSetGrossUnit(gross, fxGross, t, type); + }) + , + section -> section + .attributes("month", "day", "tickerSymbol", "name", "shares", "fxCurrency", "currency", "amount", "exchangeRate") + .match("^(?[\\w]{3,4}) " + + "(?[\\d]{2}) " + + "(?[A-Z]{3,4})" + + "[\\W]{1,3}" + + "(?.*)" + + ": .* " + + "(?[\\.,\\d]+) " + + "shares, .* [\\.,\\d]+ " + + "(?[\\w]{3})" + + ", convert to " + + "(?[\\w]{3}) " + + ".* " + + "\\p{Sc}(?[\\.,\\d]+)$") + .match("^(?![\\w]{3} [\\d]{2})([\\D]+)?(?[\\.,\\d]+)$") + .assign((t, v) -> { + DocumentContext context = type.getCurrentContext(); + + v.put("termCurrency", asCurrencyCode(v.get("currency"))); + v.put("baseCurrency", asCurrencyCode(v.get("fxCurrency"))); + + t.setSecurity(getOrCreateSecurity(v)); + t.setDateTime(asDate(v.get("day") + " " + v.get("month") + " " + context.get("year"))); + t.setShares(asShares(v.get("shares"))); + + t.setAmount(asAmount(v.get("amount"))); + t.setCurrencyCode(asCurrencyCode(v.get("currency"))); + + PDFExchangeRate rate = asExchangeRate(v); + type.getCurrentContext().putType(rate); + + DividendTaxTransactionHelper dividendTaxTransactionHelper = context.getType(DividendTaxTransactionHelper.class).orElseGet(DividendTaxTransactionHelper::new); + Optional dividendTaxTransaction = dividendTaxTransactionHelper.findItem(t.getDateTime(), t.getSecurity().getTickerSymbol()); + if (dividendTaxTransaction.isPresent() && v.get("tickerSymbol").equalsIgnoreCase(t.getSecurity().getTickerSymbol())) + { + Money tax = Money.of(asCurrencyCode(dividendTaxTransaction.get().currency), dividendTaxTransaction.get().tax); + checkAndSetTax(tax, t, type); + } + + Money gross = Money.of(asCurrencyCode(v.get("currency")), t.getGrossValueAmount()); + Money fxGross = rate.convert(asCurrencyCode(v.get("fxCurrency")), gross); + + checkAndSetGrossUnit(gross, fxGross, t, type); + }) + , + section -> section + .attributes("month", "day", "tickerSymbol", "name", "shares", "fxCurrency", "currency", "exchangeRate", "amount") + .match("^(?[\\w]{3,4}) " + + "(?[\\d]{2}) " + + "(?[A-Z]{3,4})" + + "[\\W]{1,3}" + + "(?.*)" + + ": .* " + + "(?[\\.,\\d]+) " + + "shares, .* [\\.,\\d]+ " + + "(?[\\w]{3})" + + ", convert to " + + "(?[\\w]{3})" + + " . " + + "(?[\\.,\\d]+) " + + "[\\s\\–]{2,3} " + + "\\p{Sc}(?[\\.,\\d]+)$") + .assign((t, v) -> { + DocumentContext context = type.getCurrentContext(); + + v.put("termCurrency", asCurrencyCode(v.get("currency"))); + v.put("baseCurrency", asCurrencyCode(v.get("fxCurrency"))); + + t.setSecurity(getOrCreateSecurity(v)); + t.setDateTime(asDate(v.get("day") + " " + v.get("month") + " " + context.get("year"))); + t.setShares(asShares(v.get("shares"))); + + t.setAmount(asAmount(v.get("amount"))); + t.setCurrencyCode(asCurrencyCode(v.get("currency"))); + + PDFExchangeRate rate = asExchangeRate(v); + type.getCurrentContext().putType(rate); + + DividendTaxTransactionHelper dividendTaxTransactionHelper = context.getType(DividendTaxTransactionHelper.class).orElseGet(DividendTaxTransactionHelper::new); + Optional dividendTaxTransaction = dividendTaxTransactionHelper.findItem(t.getDateTime(), t.getSecurity().getTickerSymbol()); + if (dividendTaxTransaction.isPresent() && v.get("tickerSymbol").equalsIgnoreCase(t.getSecurity().getTickerSymbol())) + { + Money tax = Money.of(asCurrencyCode(dividendTaxTransaction.get().currency), dividendTaxTransaction.get().tax); + checkAndSetTax(tax, t, type); + } + + Money gross = Money.of(asCurrencyCode(v.get("currency")), t.getGrossValueAmount()); + Money fxGross = rate.convert(asCurrencyCode(v.get("fxCurrency")), gross); + + checkAndSetGrossUnit(gross, fxGross, t, type); + }) + , + section -> section + .attributes("month", "day", "tickerSymbol", "name", "shares", "amount") + .match("^(?[\\w]{3,4}) " + + "(?[\\d]{2}) " + + "(?[A-Z]{3,4})" + + "[\\W]{1,3}(?.*)" + + ": .* " + + "(?[\\.,\\d]+) " + + "shares [\\s\\–]{2,3} " + + "\\p{Sc}(?[\\.,\\d]+)$") + .assign((t, v) -> { + DocumentContext context = type.getCurrentContext(); + + v.put("currency", asCurrencyCode(context.get("currency"))); + + t.setSecurity(getOrCreateSecurity(v)); + t.setDateTime(asDate(v.get("day") + " " + v.get("month") + " " + context.get("year"))); + t.setShares(asShares(v.get("shares"))); + + t.setAmount(asAmount(v.get("amount"))); + t.setCurrencyCode(asCurrencyCode(v.get("currency"))); + + DividendTaxTransactionHelper dividendTaxTransactionHelper = context.getType(DividendTaxTransactionHelper.class).orElseGet(DividendTaxTransactionHelper::new); + Optional dividendTaxTransaction = dividendTaxTransactionHelper.findItem(t.getDateTime(), t.getSecurity().getTickerSymbol()); + if (dividendTaxTransaction.isPresent() && v.get("tickerSymbol").equalsIgnoreCase(t.getSecurity().getTickerSymbol())) + { + Money tax = Money.of(asCurrencyCode(dividendTaxTransaction.get().currency), dividendTaxTransaction.get().tax); + checkAndSetTax(tax, t, type); + } + }) + ) + + .wrap(t -> { + type.getCurrentContext().removeType(DividendTaxTransactionsItem.class); + + if (t.getCurrencyCode() != null && t.getAmount() != 0) + return new TransactionItem(t); + return null; + })); + + // @formatter:off + // It may happen that no ticker symbol is available for + // dividend transaction and the security name is different. + // In this case, we are unable to assign it and + // therefore deny the transaction. + // + // Detected Security: + // ----------------- + // Sep 25 settled Sep 29 Sold 14.0853 of QTIP - Mackenzie Financial Corp for 1521.49 CAD -14.0853 $108.02 -$1,521.49 + // + // Transaction: + // ------------ + // Jul 10 Mackenzie US TIPS Index ETF (CAD-Hedged): 03-JUL-20 (record date) 14.0853 shares – – $1.36 + // Jun 09 Mackenzie US TIPS Index ETF (CAD-Hedged): 02-JUN-20 (record date) 14.3972 shares – – $0.88 + // May 11 Mackenzie US TIPS Index ETF (CAD-Hedged): 04-MAY-20 (record date) 14.3972 shares – – $1.27 + // @formatter:on + Block missingTickerSymbolforDividendeBlock = new Block("^[\\w]{3,4} [\\d]{2} [\\w]{5,}[\\W]{1,3}.*: .* \\(record date\\) .*$"); + type.addBlock(missingTickerSymbolforDividendeBlock); + missingTickerSymbolforDividendeBlock.set(new Transaction() + + .subject(() -> { + AccountTransaction entry = new AccountTransaction(); + entry.setType(AccountTransaction.Type.DIVIDENDS); + return entry; + }) + + .section("month", "day", "name") + .match("^(?[\\w]{3,4}) (?[\\d]{2}) (?[\\w]{5,}[\\W]{1,3}.*): .* \\(record date\\) .*$") + .assign((t, v) -> { + DocumentContext context = type.getCurrentContext(); + + t.setDateTime(asDate(v.get("day") + " " + v.get("month") + " " + context.get("year"))); + t.setNote(v.get("name") + " -> " + t.getDateTime()); + }) + + .wrap(t -> { + return new NonImportableItem(MessageFormat.format(Messages.MsgMissingTickerSymbol, t.getNote())); + })); + + // Dec 31 Gross management fee to Wealthsimple – – -$4.42 + // Dec 31 Promotions and discounts applied to Wealthsimple fee – – $0.26 + // Dec 31 Sales tax on management fee to Wealthsimple – – -$0.21 + Block feeBlock = new Block("^[\\w]{3,4} [\\d]{2} Gross management fee to Wealthsimple [\\W]{1,3} \\-\\p{Sc}[\\.,\\d]+$"); + type.addBlock(feeBlock); + feeBlock.set(new Transaction() + + .subject(() -> { + AccountTransaction entry = new AccountTransaction(); + entry.setType(AccountTransaction.Type.FEES); + return entry; + }) + + .section("month", "day", "amount") + .match("^(?[\\w]{3,4}) (?[\\d]{2}) Gross management fee to Wealthsimple [\\W]{1,3} \\-\\p{Sc}(?[\\.,\\d]+)$") + .assign((t, v) -> { + DocumentContext context = type.getCurrentContext(); + + t.setDateTime(asDate(v.get("day") + " " + v.get("month") + " " + context.get("year"))); + t.setAmount(asAmount(v.get("amount"))); + t.setCurrencyCode(asCurrencyCode(context.get("currency"))); + t.setNote("Management fee to Wealthsimple"); + + // Calculation of total fees: + // Fee = gross + taxes - fee refund + FeeRefundTransactionHelper feeRefundTransactionHelper = context.getType(FeeRefundTransactionHelper.class).orElseGet(FeeRefundTransactionHelper::new); + Optional feeRefundTransaction = feeRefundTransactionHelper.findItem(t.getDateTime()); + if (feeRefundTransaction.isPresent()) + { + Money feeRefund = Money.of(t.getCurrencyCode(), feeRefundTransaction.get().feeRefund); + t.setMonetaryAmount(t.getMonetaryAmount().subtract(feeRefund)); + } + + FeeTaxTransactionHelper feeTaxTransactionHelper = context.getType(FeeTaxTransactionHelper.class).orElseGet(FeeTaxTransactionHelper::new); + Optional feeTaxTransaction = feeTaxTransactionHelper.findItem(t.getDateTime()); + if (feeTaxTransaction.isPresent()) + { + Money tax = Money.of(t.getCurrencyCode(), feeTaxTransaction.get().tax); + t.setMonetaryAmount(t.getMonetaryAmount().add(tax)); + } + }) + + .wrap(t -> { + type.getCurrentContext().removeType(FeeRefundTransactionsItem.class); + type.getCurrentContext().removeType(FeeTaxTransactionsItem.class); + + return new TransactionItem(t); + })); + } + + private static class DividendTaxTransactionHelper + { + private List items = new ArrayList<>(); + + public Optional findItem(LocalDateTime dateTime, String tickerSymbol) + { + // Search date of dividend transaction using date and tickerSymbol. + + for (int i = 0; i < items.size(); i++) // NOSONAR + { + DividendTaxTransactionsItem item = items.get(i); + + if (item.dateTime.equals(dateTime) && item.tickerSymbol.equals(tickerSymbol)) + return Optional.of(item); + } + + return Optional.empty(); + } + } + + private static class DividendTaxTransactionsItem + { + LocalDateTime dateTime; + String tickerSymbol; + + String currency; + long tax; + + @Override + public String toString() + { + return "DividendTaxTransactionsItem [dateTime=" + dateTime + ", tickerSymbol=" + tickerSymbol + ", currency=" + + currency + ", tax=" + tax + "]"; + } + } + + private static class FeeTaxTransactionHelper + { + private List items = new ArrayList<>(); + + public Optional findItem(LocalDateTime dateTime) + { + // Search for date of fee tax by date. + + for (int i = 0; i < items.size(); i++) // NOSONAR + { + FeeTaxTransactionsItem item = items.get(i); + + if (item.dateTime.equals(dateTime)) + return Optional.of(item); + } + + return Optional.empty(); + } + } + + private static class FeeTaxTransactionsItem + { + LocalDateTime dateTime; + long tax; + + @Override + public String toString() + { + return "FeeTaxTransactionsItem [dateTime=" + dateTime + ", tax=" + tax + "]"; + } + } + + private static class FeeRefundTransactionHelper + { + private List items = new ArrayList<>(); + + public Optional findItem(LocalDateTime dateTime) + { + // Search by date of fee refund by date. + + for (int i = 0; i < items.size(); i++) // NOSONAR + { + FeeRefundTransactionsItem item = items.get(i); + + if (item.dateTime.equals(dateTime)) + return Optional.of(item); + } + + return Optional.empty(); + } + } + + private static class FeeRefundTransactionsItem + { + LocalDateTime dateTime; + long feeRefund; + + @Override + public String toString() + { + return "FeeRefundTransactionsItem [dateTime=" + dateTime + ", feeRefund=" + feeRefund + "]"; + } + } + + @Override + protected long asAmount(String value) + { + return PDFExtractorUtils.convertToNumberLong(value, Values.Amount, "en", "CA"); + } + + @Override + protected long asShares(String value) + { + return PDFExtractorUtils.convertToNumberLong(value, Values.Share, "en", "CA"); + } + + @Override + protected BigDecimal asExchangeRate(String value) + { + return PDFExtractorUtils.convertToNumberBigDecimal(value, Values.Share, "en", "CA"); + } +}