Skip to content

Commit

Permalink
Check model for missing cross entries
Browse files Browse the repository at this point in the history
Issue: #22
  • Loading branch information
buchen committed Mar 16, 2013
1 parent 0243fd2 commit 1266716
Show file tree
Hide file tree
Showing 19 changed files with 1,271 additions and 9 deletions.
@@ -0,0 +1,253 @@
package name.abuchen.portfolio.checks.impl;

import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;

import java.util.ArrayList;
import java.util.List;

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.util.Dates;

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(Dates.today(), security, //
PortfolioTransaction.Type.BUY, 1, 1, 1));

List<Issue> issues = new CrossEntryCheck().execute(client);

assertThat(issues.size(), is(1));
assertThat(issues.get(0), is(MissingBuySellAccountIssue.class));
assertThat(issues.get(0).getEntity(), is((Object) portfolio));

applyFixes(client, issues);
}

@Test
public void testMissingBuyInAccountIssue()
{
portfolio.addTransaction(new PortfolioTransaction(Dates.today(), security, //
PortfolioTransaction.Type.SELL, 1, 1, 1));

List<Issue> issues = new CrossEntryCheck().execute(client);

assertThat(issues.size(), is(1));
assertThat(issues.get(0), is(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(Dates.today());
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(Dates.today(), security, //
PortfolioTransaction.Type.SELL, 1, 1, 1));

account.addTransaction(new AccountTransaction(Dates.today(), security, //
AccountTransaction.Type.SELL, 1));

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(Dates.today(), security, //
PortfolioTransaction.Type.SELL, 1, 1, 1));

account.addTransaction(new AccountTransaction(Dates.today(), security, //
AccountTransaction.Type.SELL, 2));

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(Dates.today(), security, //
AccountTransaction.Type.TRANSFER_IN, 1));

List<Issue> issues = new CrossEntryCheck().execute(client);

assertThat(issues.size(), is(1));
assertThat(issues.get(0), is(MissingAccountTransferIssue.class));
assertThat(issues.get(0).getEntity(), is((Object) account));

applyFixes(client, issues);
}

@Test
public void testMissingAccountTransferInIssue()
{
account.addTransaction(new AccountTransaction(Dates.today(), security, //
AccountTransaction.Type.TRANSFER_OUT, 1));

List<Issue> issues = new CrossEntryCheck().execute(client);

assertThat(issues.size(), is(1));
assertThat(issues.get(0), is(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(Dates.today(), security, //
AccountTransaction.Type.TRANSFER_IN, 2));

AccountTransaction umatched = new AccountTransaction(Dates.today(), security, //
AccountTransaction.Type.TRANSFER_OUT, 2);
account.addTransaction(umatched);

second.addTransaction(new AccountTransaction(Dates.today(), security, //
AccountTransaction.Type.TRANSFER_OUT, 2));

List<Issue> issues = new CrossEntryCheck().execute(client);

assertThat(issues.size(), is(1));
assertThat(issues.get(0), is(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(Dates.today(), security, //
PortfolioTransaction.Type.TRANSFER_IN, 1, 1, 1));

List<Issue> issues = new CrossEntryCheck().execute(client);

assertThat(issues.size(), is(1));
assertThat(issues.get(0), is(MissingPortfolioTransferIssue.class));
assertThat(issues.get(0).getEntity(), is((Object) portfolio));

applyFixes(client, issues);
}

@Test
public void testMissingPortfolioTransferInIssue()
{
portfolio.addTransaction(new PortfolioTransaction(Dates.today(), security, //
PortfolioTransaction.Type.TRANSFER_OUT, 1, 1, 1));

List<Issue> issues = new CrossEntryCheck().execute(client);

assertThat(issues.size(), is(1));
assertThat(issues.get(0), is(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(Dates.today(), security, //
PortfolioTransaction.Type.TRANSFER_IN, 1, 3, 1));

PortfolioTransaction umatched = new PortfolioTransaction(Dates.today(), security, //
PortfolioTransaction.Type.TRANSFER_OUT, 1, 3, 1);
portfolio.addTransaction(umatched);

second.addTransaction(new PortfolioTransaction(Dates.today(), security, //
PortfolioTransaction.Type.TRANSFER_OUT, 1, 3, 1));

List<Issue> issues = new CrossEntryCheck().execute(client);

assertThat(issues.size(), is(1));
assertThat(issues.get(0), is(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);
}

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));
}
}
3 changes: 2 additions & 1 deletion name.abuchen.portfolio/META-INF/MANIFEST.MF
Expand Up @@ -4,7 +4,8 @@ Bundle-Name: %Bundle-Name
Bundle-SymbolicName: name.abuchen.portfolio
Bundle-Version: 0.7.7.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: name.abuchen.portfolio.model,
Export-Package: name.abuchen.portfolio.checks,
name.abuchen.portfolio.model,
name.abuchen.portfolio.online,
name.abuchen.portfolio.snapshot,
name.abuchen.portfolio.util
Expand Down
@@ -0,0 +1 @@
name.abuchen.portfolio.checks.impl.CrossEntryCheck
13 changes: 13 additions & 0 deletions name.abuchen.portfolio/src/name/abuchen/portfolio/Messages.java
Expand Up @@ -41,6 +41,19 @@ public class Messages extends NLS
public static String CSVImportSecurityExists;
public static String CSVImportMissingField;
public static String CSVImportMissingOneOfManyFields;
public static String FixConvertToDelivery;
public static String FixConvertToDeliveryDone;
public static String FixCreateCrossEntryAccount;
public static String FixCreateCrossEntryDone;
public static String FixCreateCrossEntryPortfolio;
public static String FixCreateTransfer;
public static String FixCreateTransferDone;
public static String FixDeleteTransaction;
public static String FixDeleteTransactionDone;
public static String IssueMissingAccountTransfer;
public static String IssueMissingBuySellInAccount;
public static String IssueMissingBuySellInPortfolio;
public static String IssueMissingPortfolioTransfer;
public static String LabelDeposits;
public static String LabelIndustryClassification;
public static String LabelInterest;
Expand Down
@@ -0,0 +1,10 @@
package name.abuchen.portfolio.checks;

import java.util.List;

import name.abuchen.portfolio.model.Client;

public interface Check
{
List<Issue> execute(Client client);
}
@@ -0,0 +1,31 @@
package name.abuchen.portfolio.checks;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;

import name.abuchen.portfolio.model.Client;

public class Checker
{
private static List<Check> CHECKS;

static
{
CHECKS = new ArrayList<Check>();
Iterator<Check> iter = ServiceLoader.load(Check.class).iterator();
while (iter.hasNext())
CHECKS.add(iter.next());
}

public static final List<Issue> runAll(Client client)
{
List<Issue> answer = new ArrayList<Issue>();

for (Check check : CHECKS)
answer.addAll(check.execute(client));

return answer;
}
}
@@ -0,0 +1,17 @@
package name.abuchen.portfolio.checks;

import java.util.Date;
import java.util.List;

public interface Issue
{
Date getDate();

Object getEntity();

Long getAmount();

String getLabel();

List<QuickFix> getAvailableFixes();
}
@@ -0,0 +1,10 @@
package name.abuchen.portfolio.checks;

public interface QuickFix
{
String getLabel();

String getDoneLabel();

void execute();
}
@@ -0,0 +1,40 @@
package name.abuchen.portfolio.checks.impl;

import java.util.Date;

import name.abuchen.portfolio.checks.Issue;
import name.abuchen.portfolio.model.Account;
import name.abuchen.portfolio.model.AccountTransaction;
import name.abuchen.portfolio.model.Client;

/* package */abstract class AbstractAccountIssue implements Issue
{
protected Client client;
protected Account account;
protected AccountTransaction transaction;

public AbstractAccountIssue(Client client, Account account, AccountTransaction transaction)
{
this.client = client;
this.account = account;
this.transaction = transaction;
}

@Override
public Date getDate()
{
return transaction.getDate();
}

@Override
public Account getEntity()
{
return account;
}

@Override
public Long getAmount()
{
return transaction.getAmount();
}
}

0 comments on commit 1266716

Please sign in to comment.