/*
* Copyright (c) 2012 Denis Solonenko.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/
package ru.orangesoftware.financisto2.test.db;
import android.database.Cursor;
import java.util.Arrays;
import java.util.Map;
import ru.orangesoftware.financisto2.R;
import ru.orangesoftware.financisto2.blotter.BlotterFilter;
import ru.orangesoftware.financisto2.db.DatabaseUtils;
import ru.orangesoftware.financisto2.filter.WhereFilter;
import ru.orangesoftware.financisto2.model.Account;
import ru.orangesoftware.financisto2.model.Category;
import ru.orangesoftware.financisto2.model.Payee;
import ru.orangesoftware.financisto2.model.Transaction;
import ru.orangesoftware.financisto2.model.TransactionStatus;
import ru.orangesoftware.financisto2.test.builders.AccountBuilder;
import ru.orangesoftware.financisto2.test.builders.CategoryBuilder;
import ru.orangesoftware.financisto2.test.builders.DateTime;
import ru.orangesoftware.financisto2.test.builders.TransactionBuilder;
import ru.orangesoftware.financisto2.test.builders.TransferBuilder;
import static ru.orangesoftware.financisto2.test.builders.DateTime.date;
/**
* Created by IntelliJ IDEA.
* User: denis.solonenko
* Date: 5/25/12 11:26 PM
*/
public class AccountPurgeTest extends AbstractDbTest {
Account a1;
Account a2;
Map<String, Category> categoriesMap;
@Override
public void setUp() throws Exception {
super.setUp();
a1 = AccountBuilder.createDefault(db);
a2 = AccountBuilder.createDefault(db);
categoriesMap = CategoryBuilder.createDefaultHierarchy(categoryRepository);
/* A1 A2
* 29/05 A1 +10 | 40 |
* 28/05 A1 -20 | 30 |
* 27/05 A1->A2 -100 +20 | 50 | -40
* 26/05 A1 +100 | 150 |
* 25/05 A2->A1 -50 +10 | 50 | -60
* 24/05 A1 +200 | 40 |
* 24/05 A2 -20 | | -10
* 23/05 A1 -150 | -160 |
* -100 | |
* -> A2 -50 +10 | | 10
* 22/05 A1 -20 | -10 |
* 21/05 A1 +10 | 10 |
* */
TransactionBuilder.withDb(db).dateTime(date(2012, 5, 29)).account(a1).amount(10).create();
TransactionBuilder.withDb(db).dateTime(date(2012, 5, 28)).account(a1).amount(-20).create();
TransferBuilder.withDb(db).dateTime(date(2012, 5, 27))
.fromAccount(a1).fromAmount(-100)
.toAccount(a2).toAmount(20).create();
TransactionBuilder.withDb(db).dateTime(date(2012, 5, 26)).account(a1).amount(100).create();
TransferBuilder.withDb(db).dateTime(date(2012, 5, 25))
.fromAccount(a2).fromAmount(-50)
.toAccount(a1).toAmount(10).create();
TransactionBuilder.withDb(db).dateTime(date(2012, 5, 24)).account(a1).amount(200).create();
TransactionBuilder.withDb(db).dateTime(date(2012, 5, 24)).account(a2).amount(-20).create();
TransactionBuilder.withDb(db).dateTime(date(2012, 5, 23)).account(a1).amount(-150)
.withSplit(categoriesMap.get("A1"), -100)
.withTransferSplit(a2, -50, 10)
.create();
TransactionBuilder.withDb(db).dateTime(date(2012, 5, 22)).account(a1).amount(-20).create();
TransactionBuilder.withDb(db).dateTime(date(2012, 5, 21)).account(a1).amount(10).create();
}
public void test_should_delete_first_account_correctly() {
db.deleteAccount(a1.id);
assertAccount(a2, -40);
assertAccountBlotter(a2, 20, -50, -20, 10);
assertAccountRunningBalance(a2, -40, -60, -10, 10);
}
public void test_should_delete_second_account_correctly() {
db.deleteAccount(a2.id);
assertAccount(a1, 40);
assertAccountBlotter(a1, 10, -20, -100, 100, 10, 200, -150, -20, 10);
assertAccountRunningBalance(a1, 40, 30, 50, 150, 50, 40, -160, -10, 10);
}
public void test_should_purge_transactions_older_than_specified_date() {
//given
assertAccounts();
assertTransactionsCount(a1, 10);
assertTransactionsCount(a2, 2);
assertOldestTransaction(a1, date(2012, 5, 21), 10);
assertAccountBlotter(a1, 10, -20, -100, 100, 10, 200, -150, -20, 10);
assertAccountRunningBalance(a1, 40, 30, 50, 150, 50, 40, -160, -10, 10);
assertAccountBlotter(a2, 20, -50, -20, 10);
assertAccountRunningBalance(a2, -40, -60, -10, 10);
assertAccounts();
//when purged nothing, then nothing changes
db.purgeAccountAtDate(a1, date(2012, 5, 20).asLong());
assertOldestTransaction(a1, date(2012, 5, 21), 10);
assertAccountBlotter(a1, 10, -20, -100, 100, 10, 200, -150, -20, 10);
assertAccountRunningBalance(a1, 40, 30, 50, 150, 50, 40, -160, -10, 10);
assertAccountBlotter(a2, 20, -50, -20, 10);
assertAccountRunningBalance(a2, -40, -60, -10, 10);
assertAccounts();
//when purged last, then nothing changes
db.purgeAccountAtDate(a1, date(2012, 5, 21).asLong());
assertOldestTransaction(a1, date(2012, 5, 21).atDayEnd(), 10);
assertAccountBlotter(a1, 10, -20, -100, 100, 10, 200, -150, -20, 10);
assertAccountRunningBalance(a1, 40, 30, 50, 150, 50, 40, -160, -10, 10);
assertAccountBlotter(a2, 20, -50, -20, 10);
assertAccountRunningBalance(a2, -40, -60, -10, 10);
assertAccounts();
//when purged one, balance should stay
db.purgeAccountAtDate(a1, date(2012, 5, 22).asLong());
assertArchiveTransaction(a1, date(2012, 5, 22).atDayEnd(), -10);
assertAccountBlotter(a1, 10, -20, -100, 100, 10, 200, -150, -10);
assertAccountRunningBalance(a1, 40, 30, 50, 150, 50, 40, -160, -10);
assertAccountBlotter(a2, 20, -50, -20, 10);
assertAccountRunningBalance(a2, -40, -60, -10, 10);
assertAccounts();
//when purged split, then transfers should be restored
db.purgeAccountAtDate(a1, date(2012, 5, 23).asLong());
assertArchiveTransaction(a1, date(2012, 5, 23).atDayEnd(), -160);
assertAccountBlotter(a1, 10, -20, -100, 100, 10, 200, -160);
assertAccountRunningBalance(a1, 40, 30, 50, 150, 50, 40, -160);
assertAccountBlotter(a2, 20, -50, -20, 10);
assertAccountRunningBalance(a2, -40, -60, -10, 10);
assertAccounts();
//when purged 2 transactions together
db.purgeAccountAtDate(a1, date(2012, 5, 24).asLong());
assertArchiveTransaction(a1, date(2012, 5, 24).atDayEnd(), 40);
assertAccountBlotter(a1, 10, -20, -100, 100, 10, 40);
assertAccountRunningBalance(a1, 40, 30, 50, 150, 50, 40);
assertAccountBlotter(a2, 20, -50, -20, 10);
assertAccountRunningBalance(a2, -40, -60, -10, 10);
assertAccounts();
//when purged a transfer
db.purgeAccountAtDate(a1, date(2012, 5, 27).asLong());
assertArchiveTransaction(a1, date(2012, 5, 27).atDayEnd(), 50);
assertAccountBlotter(a1, 10, -20, 50);
assertAccountRunningBalance(a1, 40, 30, 50);
assertAccountBlotter(a2, 20, -50, -20, 10);
assertAccountRunningBalance(a2, -40, -60, -10, 10);
assertAccounts();
//when everything
db.purgeAccountAtDate(a1, date(2012, 5, 29).asLong());
assertArchiveTransaction(a1, date(2012, 5, 29).atDayEnd(), 40);
assertAccountBlotter(a1, 40);
assertAccountRunningBalance(a1, 40);
assertAccountBlotter(a2, 20, -50, -20, 10);
assertAccountRunningBalance(a2, -40, -60, -10, 10);
assertAccounts();
}
private void assertAccountBlotter(Account account, long...expectedAmounts) {
WhereFilter filter = WhereFilter.empty();
filter.eq(BlotterFilter.FROM_ACCOUNT_ID, String.valueOf(account.id));
Cursor c = db.getBlotterForAccount(filter);
long[] actualAmounts = new long[c.getCount()];
try {
int i = 0;
while (c.moveToNext()) {
Transaction t = Transaction.fromBlotterCursor(c);
actualAmounts[i++] = t.fromAmount;
}
} finally {
c.close();
}
assertAmountsForAccount(account, expectedAmounts, actualAmounts);
}
private void assertAccountRunningBalance(Account account, long...expectedBalance) {
Cursor c = db.db().rawQuery("select balance from running_balance where account_id=? order by datetime desc, transaction_id desc", new String[]{String.valueOf(account.id)});
long[] actualBalance = new long[c.getCount()];
try {
int i = 0;
while (c.moveToNext()) {
actualBalance[i++] = c.getLong(0);
}
} finally {
c.close();
}
assertAmountsForAccount(account, expectedBalance, actualBalance);
}
private void assertAmountsForAccount(Account account, long[] expectedAmounts, long[] amounts) {
String expectedVsActual = "Account "+account.id+" -> Expected:"+ Arrays.toString(expectedAmounts)+", Actual:"+Arrays.toString(amounts);
assertEquals("Too few or too many transactions. "+expectedVsActual, expectedAmounts.length, amounts.length);
assertTrue(expectedVsActual, Arrays.equals(expectedAmounts, amounts));
}
private Transaction assertOldestTransaction(Account account, DateTime date, long expectedAmount) {
Transaction t = getOldestTransaction(account);
assertEquals(date.asLong(), t.dateTime);
assertEquals(expectedAmount, t.fromAmount);
// this is the very first transaction, so running balance == amount
assertAccountBalanceForTransaction(t, account, expectedAmount);
return t;
}
private void assertArchiveTransaction(Account account, DateTime date, long expectedAmount) {
Transaction t = assertOldestTransaction(account, date, expectedAmount);
Payee payee = db.get(Payee.class, t.payeeId);
assertEquals(getContext().getString(R.string.purge_account_payee), payee.title);
assertEquals(TransactionStatus.CL, t.status);
}
private Transaction getOldestTransaction(Account account) {
long id = DatabaseUtils.rawFetchId(db,
"select _id from transactions where from_account_id=? and is_template=0 order by datetime limit 1",
new String[]{String.valueOf(account.id)});
return db.get(Transaction.class, id);
}
private void assertAccounts() {
assertAccount(a1, 40);
assertAccount(a2, -40);
}
private void assertAccount(Account account, long accountTotal) {
assertAccountTotal(account, accountTotal);
assertFinalBalanceForAccount(account, accountTotal);
db.rebuildRunningBalanceForAccount(account);
assertAccountTotal(account, accountTotal);
assertFinalBalanceForAccount(account, accountTotal);
}
}