package name.abuchen.portfolio.checks.impl; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import name.abuchen.portfolio.TestCurrencyConverter; import name.abuchen.portfolio.checks.Issue; import name.abuchen.portfolio.checks.QuickFix; 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.Portfolio; import name.abuchen.portfolio.model.PortfolioTransaction; import name.abuchen.portfolio.model.Security; import name.abuchen.portfolio.money.CurrencyUnit; import name.abuchen.portfolio.snapshot.ClientSnapshot; import org.junit.Before; import org.junit.Test; public class CrossEntryCheckTest { private Client client; private Account account; private Portfolio portfolio; private Security security; @Before public void setupClient() { client = new Client(); account = new Account(); client.addAccount(account); portfolio = new Portfolio(); client.addPortfolio(portfolio); security = new Security(); client.addSecurity(security); } @Test public void testEmptyClient() { assertThat(new CrossEntryCheck().execute(client).size(), is(0)); } @Test public void testMissingSellInAccountIssue() { portfolio.addTransaction(new PortfolioTransaction(LocalDate.now(), CurrencyUnit.EUR, 1, security, 1, PortfolioTransaction.Type.BUY, 1, 0)); List<Issue> issues = new CrossEntryCheck().execute(client); assertThat(issues.size(), is(1)); assertThat(issues.get(0), is(instanceOf(MissingBuySellAccountIssue.class))); assertThat(issues.get(0).getEntity(), is((Object) portfolio)); applyFixes(client, issues); } @Test public void testMissingBuyInAccountIssue() { portfolio.addTransaction(new PortfolioTransaction(LocalDate.now(), CurrencyUnit.EUR, 1, security, 1, PortfolioTransaction.Type.SELL, 1, 0)); List<Issue> issues = new CrossEntryCheck().execute(client); assertThat(issues.size(), is(1)); assertThat(issues.get(0), is(instanceOf(MissingBuySellAccountIssue.class))); assertThat(issues.get(0).getEntity(), is((Object) portfolio)); applyFixes(client, issues); } @Test public void testThatCorrectBuySellEntriesAreNotReported() { BuySellEntry entry = new BuySellEntry(portfolio, account); entry.setType(PortfolioTransaction.Type.BUY); entry.setDate(LocalDate.now()); entry.setSecurity(security); entry.setShares(1); entry.setAmount(100); entry.insert(); assertThat(new CrossEntryCheck().execute(client).size(), is(0)); } @Test public void testThatMatchingBuySellEntriesAreFixed() { portfolio.addTransaction(new PortfolioTransaction(LocalDate.now(), CurrencyUnit.EUR, 1, security, 1, PortfolioTransaction.Type.SELL, 1, 0)); account.addTransaction(new AccountTransaction(LocalDate.now(), CurrencyUnit.EUR, 1, security, // AccountTransaction.Type.SELL)); assertThat(new CrossEntryCheck().execute(client).size(), is(0)); assertThat(portfolio.getTransactions().get(0).getCrossEntry(), notNullValue()); assertThat(account.getTransactions().get(0).getCrossEntry(), notNullValue()); } @Test public void testThatAlmostMatchingBuySellEntriesAreNotMatched() { portfolio.addTransaction(new PortfolioTransaction(LocalDate.now(), CurrencyUnit.EUR, 1, security, 1, PortfolioTransaction.Type.SELL, 1, 0)); account.addTransaction(new AccountTransaction(LocalDate.now(), CurrencyUnit.EUR, 2, security, AccountTransaction.Type.SELL)); List<Issue> issues = new CrossEntryCheck().execute(client); assertThat(issues.size(), is(2)); List<Object> objects = new ArrayList<Object>(issues); assertThat(objects, hasItem(instanceOf(MissingBuySellAccountIssue.class))); assertThat(objects, hasItem(instanceOf(MissingBuySellPortfolioIssue.class))); applyFixes(client, issues); } @Test public void testMissingAccountTransferOutIssue() { account.addTransaction(new AccountTransaction(LocalDate.now(), CurrencyUnit.EUR, 1, security, AccountTransaction.Type.TRANSFER_IN)); List<Issue> issues = new CrossEntryCheck().execute(client); assertThat(issues.size(), is(1)); assertThat(issues.get(0), is(instanceOf(MissingAccountTransferIssue.class))); assertThat(issues.get(0).getEntity(), is((Object) account)); applyFixes(client, issues); } @Test public void testMissingAccountTransferInIssue() { account.addTransaction(new AccountTransaction(LocalDate.now(), CurrencyUnit.EUR, 1, security, AccountTransaction.Type.TRANSFER_OUT)); List<Issue> issues = new CrossEntryCheck().execute(client); assertThat(issues.size(), is(1)); assertThat(issues.get(0), is(instanceOf(MissingAccountTransferIssue.class))); assertThat(issues.get(0).getEntity(), is((Object) account)); applyFixes(client, issues); } @Test public void testThatNotTheSameAccountIsMatched() { Account second = new Account(); client.addAccount(second); account.addTransaction(new AccountTransaction(LocalDate.now(), CurrencyUnit.EUR, 2, security, AccountTransaction.Type.TRANSFER_IN)); AccountTransaction umatched = new AccountTransaction(LocalDate.now(), CurrencyUnit.EUR, 2, security, AccountTransaction.Type.TRANSFER_OUT); account.addTransaction(umatched); second.addTransaction(new AccountTransaction(LocalDate.now(), CurrencyUnit.EUR, 2, security, AccountTransaction.Type.TRANSFER_OUT)); List<Issue> issues = new CrossEntryCheck().execute(client); assertThat(issues.size(), is(1)); assertThat(issues.get(0), is(instanceOf(MissingAccountTransferIssue.class))); assertThat(account.getTransactions(), hasItem(umatched)); assertThat(second.getTransactions().get(0).getCrossEntry(), notNullValue()); assertThat(second.getTransactions().get(0).getType(), is(AccountTransaction.Type.TRANSFER_OUT)); applyFixes(client, issues); } @Test public void testMissingPortfolioTransferOutIssue() { portfolio.addTransaction(new PortfolioTransaction(LocalDate.now(), CurrencyUnit.EUR, 1, security, 1, PortfolioTransaction.Type.TRANSFER_IN, 1, 0)); List<Issue> issues = new CrossEntryCheck().execute(client); assertThat(issues.size(), is(1)); assertThat(issues.get(0), is(instanceOf(MissingPortfolioTransferIssue.class))); assertThat(issues.get(0).getEntity(), is((Object) portfolio)); applyFixes(client, issues); } @Test public void testMissingPortfolioTransferInIssue() { portfolio.addTransaction(new PortfolioTransaction(LocalDate.now(), CurrencyUnit.EUR, 1, security, 1, PortfolioTransaction.Type.TRANSFER_OUT, 1, 0)); List<Issue> issues = new CrossEntryCheck().execute(client); assertThat(issues.size(), is(1)); assertThat(issues.get(0), is(instanceOf(MissingPortfolioTransferIssue.class))); assertThat(issues.get(0).getEntity(), is((Object) portfolio)); applyFixes(client, issues); } @Test public void testThatNotTheSamePortfolioIsMatched() { Portfolio second = new Portfolio(); client.addPortfolio(second); portfolio.addTransaction(new PortfolioTransaction(LocalDate.now(), CurrencyUnit.EUR, 3, security, 1, PortfolioTransaction.Type.TRANSFER_IN, 1, 0)); PortfolioTransaction umatched = new PortfolioTransaction(LocalDate.now(), CurrencyUnit.EUR, 3, security, 1, PortfolioTransaction.Type.TRANSFER_OUT, 1, 0); portfolio.addTransaction(umatched); second.addTransaction(new PortfolioTransaction(LocalDate.now(), CurrencyUnit.EUR, 3, security, 1, PortfolioTransaction.Type.TRANSFER_OUT, 1, 0)); List<Issue> issues = new CrossEntryCheck().execute(client); assertThat(issues.size(), is(1)); assertThat(issues.get(0), is(instanceOf(MissingPortfolioTransferIssue.class))); assertThat(portfolio.getTransactions(), hasItem(umatched)); assertThat(second.getTransactions().get(0).getCrossEntry(), notNullValue()); assertThat(second.getTransactions().get(0).getType(), is(PortfolioTransaction.Type.TRANSFER_OUT)); applyFixes(client, issues); } @Test public void testThatAccountTransactionsWithoutSecurity() { Portfolio second = new Portfolio(); client.addPortfolio(second); account.addTransaction(new AccountTransaction(LocalDate.now(), CurrencyUnit.EUR, 1, null, AccountTransaction.Type.BUY)); List<Issue> issues = new CrossEntryCheck().execute(client); assertThat(issues.size(), is(1)); assertThat(issues.get(0).getAvailableFixes().get(0), is(instanceOf(DeleteTransactionFix.class))); applyFixes(client, issues); ClientSnapshot.create(client, new TestCurrencyConverter(), LocalDate.now()); } private void applyFixes(Client client, List<Issue> issues) { for (Issue issue : issues) { List<QuickFix> fixes = issue.getAvailableFixes(); assertThat(fixes.isEmpty(), is(false)); fixes.get(0).execute(); } assertThat(new CrossEntryCheck().execute(client).size(), is(0)); } }