You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmymoney/kmymoney2/mymoney/mymoneyforecasttest.cpp

978 lines
36 KiB

/***************************************************************************
mymoneyforecasttest.cpp
-------------------
copyright : (C) 2007 by Alvaro Soliverez
email : asoliverez@gmail.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "mymoneyforecasttest.h"
#include <iostream>
#include <tqvaluelist.h>
#include "mymoneybudget.h"
#include <kmymoney/mymoneyexception.h>
#include "../kmymoneyglobalsettings.h"
#include "../mymoney/storage/mymoneystoragedump.h"
#include "../mymoney/storage/mymoneystoragexml.h"
#include "../reports/reportstestcommon.h"
using namespace test;
MyMoneyForecastTest::MyMoneyForecastTest()
{
this->moT1 = MyMoneyMoney(57,1);
this->moT2 = MyMoneyMoney(63,1);
this->moT3 = MyMoneyMoney(84,1);
this->moT4 = MyMoneyMoney(62,1);
this->moT5 = MyMoneyMoney(104,1);
}
void MyMoneyForecastTest::setUp () {
//all this has been taken from pivottabletest.cpp, by Thomas Baumgart and Ace Jones
storage = new MyMoneySeqAccessMgr;
file = MyMoneyFile::instance();
file->attachStorage(storage);
MyMoneyFileTransaction ft;
file->addCurrency(MyMoneySecurity("CAD", "Canadian Dollar", "C$"));
file->addCurrency(MyMoneySecurity("USD", "US Dollar", "$"));
file->addCurrency(MyMoneySecurity("JPY", "Japanese Yen", TQChar(0x00A5), 100, 1));
file->addCurrency(MyMoneySecurity("GBP", "British Pound", "#"));
file->setBaseCurrency(file->currency("USD"));
MyMoneyPayee payeeTest("Test Payee");
file->addPayee(payeeTest);
MyMoneyPayee payeeTest2("Alvaro Soliverez");
file->addPayee(payeeTest2);
acAsset = (MyMoneyFile::instance()->asset().id());
acLiability = (MyMoneyFile::instance()->liability().id());
acExpense = (MyMoneyFile::instance()->expense().id());
acIncome = (MyMoneyFile::instance()->income().id());
acChecking = makeAccount(TQString("Checking Account"),MyMoneyAccount::Checkings,moCheckingOpen,TQDate(2004,5,15),acAsset, "USD");
acCredit = makeAccount(TQString("Credit Card"),MyMoneyAccount::CreditCard,moCreditOpen,TQDate(2004,7,15),acLiability, "USD");
acSolo = makeAccount(TQString("Solo"),MyMoneyAccount::Expense,0,TQDate(2004,1,11),acExpense, "USD");
acParent = makeAccount(TQString("Parent"),MyMoneyAccount::Expense,0,TQDate(2004,1,11),acExpense, "USD");
acChild = makeAccount(TQString("Child"),MyMoneyAccount::Expense,0,TQDate(2004,2,11),acParent, "USD");
acForeign = makeAccount(TQString("Foreign"),MyMoneyAccount::Expense,0,TQDate(2004,1,11),acExpense, "USD");
acInvestment = makeAccount("Investment",MyMoneyAccount::Investment,moZero,TQDate(2004,1,1),acAsset, "USD");
acSecondChild = makeAccount(TQString("Second Child"),MyMoneyAccount::Expense,0,TQDate(2004,2,11),acParent, "USD");
acGrandChild1 = makeAccount(TQString("Grand Child 1"),MyMoneyAccount::Expense,0,TQDate(2004,2,11),acChild, "USD");
acGrandChild2 = makeAccount(TQString("Grand Child 2"),MyMoneyAccount::Expense,0,TQDate(2004,2,11),acChild, "USD");
//this account added to have an account to test opening date calculations
acCash = makeAccount(TQString("Cash"),MyMoneyAccount::Cash,moCreditOpen,TQDate::tqcurrentDate().addDays(-2),acAsset, "USD");
MyMoneyInstitution i("Bank of the World","","","","","","");
file->addInstitution(i);
inBank = i.id();
ft.commit();
}
void MyMoneyForecastTest::tearDown () {
file->detachStorage(storage);
delete storage;
}
void MyMoneyForecastTest::testEmptyConstructor() {
MyMoneyForecast a;
MyMoneyAccount b;
int f = a.forecastBalance(b, TQDate::tqcurrentDate());
CPPUNIT_ASSERT(f == 0);
CPPUNIT_ASSERT(!a.isForecastAccount(b));
CPPUNIT_ASSERT(a.forecastBalance(b, TQDate::tqcurrentDate()) == MyMoneyMoney(0,1));
CPPUNIT_ASSERT(a.daysToMinimumBalance(b) == -1);
CPPUNIT_ASSERT(a.daysToZeroBalance(b) == -2);
CPPUNIT_ASSERT(a.forecastDays() == KMyMoneyGlobalSettings::forecastDays());
CPPUNIT_ASSERT(a.accountsCycle() == KMyMoneyGlobalSettings::forecastAccountCycle());
CPPUNIT_ASSERT(a.forecastCycles() == KMyMoneyGlobalSettings::forecastCycles());
CPPUNIT_ASSERT(a.historyStartDate() == TQDate::tqcurrentDate().addDays(-KMyMoneyGlobalSettings::forecastCycles()*KMyMoneyGlobalSettings::forecastAccountCycle()));
CPPUNIT_ASSERT(a.historyEndDate() == TQDate::tqcurrentDate().addDays(-1));
CPPUNIT_ASSERT(a.historyDays() == KMyMoneyGlobalSettings::forecastAccountCycle() * KMyMoneyGlobalSettings::forecastCycles());
}
void MyMoneyForecastTest::testDoForecastInit() {
MyMoneyForecast a;
a.doForecast();
/*
//check the illegal argument validation
try {
KMyMoneyGlobalSettings::setForecastDays(-10);
a.doForecast();
}
catch (MyMoneyException *e)
{
delete e;
CPPUNIT_FAIL("Unexpected exception");
}
try {
KMyMoneyGlobalSettings::setForecastAccountCycle(-20);
a.doForecast();
}
catch (MyMoneyException *e) {
delete e;
CPPUNIT_FAIL("Unexpected exception");
}
try {
KMyMoneyGlobalSettings::setForecastCycles(-10);
a.doForecast();
}
catch (MyMoneyException *e) {
delete e;
CPPUNIT_FAIL("Unexpected exception");
}
try {
KMyMoneyGlobalSettings::setForecastAccountCycle(0);
a.doForecast();
}
catch (MyMoneyException *e) {
delete e;
CPPUNIT_FAIL("Unexpected exception");
}
try {
KMyMoneyGlobalSettings::setForecastDays(0);
KMyMoneyGlobalSettings::setForecastCycles(0);
KMyMoneyGlobalSettings::setForecastAccountCycle(0);
a.doForecast();
}
catch (MyMoneyException *e) {
delete e;
CPPUNIT_ASSERT("Unexpected exception");
}*/
}
//test that it forecasts correctly with transactions in the period of forecast
void MyMoneyForecastTest::testDoForecast() {
//set up environment
MyMoneyForecast a;
MyMoneyAccount a_checking = file->account(acChecking);
MyMoneyAccount a_credit = file->account(acCredit);
//test empty forecast
a.doForecast(); //this is just to check nothing goes wrong if forecast is run agains an empty template
//setup some transactions
TransactionHelper t1( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionWithdrawal, this->moT1, acChecking, acSolo);
TransactionHelper t2( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionDeposit, -(this->moT2), acCredit, acParent);
TransactionHelper t3( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionTransfer, this->moT1, acCredit, acChecking);
a.setForecastMethod(1);
a.setForecastDays(3);
a.setAccountsCycle(1);
a.setForecastCycles(1);
a.setBeginForecastDay(0);
a.setHistoryMethod(0); //moving average
a.doForecast();
//checking didn't have balance variations, so the forecast should be equal to the current balance
MyMoneyMoney b_checking = file->balance(a_checking.id(), TQDate::tqcurrentDate());
CPPUNIT_ASSERT(a.forecastBalance(a_checking, TQDate::tqcurrentDate().addDays(1))==b_checking);
CPPUNIT_ASSERT(a.forecastBalance(a_checking, TQDate::tqcurrentDate().addDays(2))==b_checking);
CPPUNIT_ASSERT(a.forecastBalance(a_checking, TQDate::tqcurrentDate().addDays(3))==b_checking);
CPPUNIT_ASSERT(a.forecastBalance(a_checking, TQDate::tqcurrentDate())==b_checking);
//credit had a variation so the forecast should be different for each day
MyMoneyMoney b_credit = file->balance(a_credit.id(), TQDate::tqcurrentDate());
CPPUNIT_ASSERT(a.forecastBalance(a_credit, 0) == b_credit);
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(1)) == (b_credit+(moT2-moT1)));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(2)) == (b_credit+((moT2-moT1)*2)));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(3)) == b_credit+((moT2-moT1)*3));
a.setHistoryMethod(1); //weighted moving average
a.doForecast();
CPPUNIT_ASSERT(a.forecastBalance(a_credit, 0) == b_credit);
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(1)) == (b_credit+(moT2-moT1)));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(2)) == (b_credit+((moT2-moT1)*2)));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(3)) == b_credit+((moT2-moT1)*3));
//insert transactions outside the forecast period. The calculation should be the same.
TransactionHelper t4( TQDate::tqcurrentDate().addDays(-2), MyMoneySplit::ActionDeposit, -moT2, acCredit, acParent );
TransactionHelper t5( TQDate::tqcurrentDate().addDays(-10), MyMoneySplit::ActionDeposit, -moT2, acCredit, acParent );
TransactionHelper t6( TQDate::tqcurrentDate().addDays(-3), MyMoneySplit::ActionDeposit, -moT2, acCredit, acParent );
a.setForecastMethod(1);
a.setForecastDays(3);
a.setAccountsCycle(1);
a.setForecastCycles(1);
a.setBeginForecastDay(0);
a.setHistoryMethod(0); //moving average
a.doForecast();
//check forecast
b_credit = file->balance(a_credit.id(), TQDate::tqcurrentDate());
MyMoneyMoney b_credit_1_exp = (b_credit+((moT2-moT1)));
MyMoneyMoney b_credit_2 = a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(2));
MyMoneyMoney b_credit_2_exp = (b_credit+((moT2-moT1)*2));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate())==file->balance(a_credit.id(), TQDate::tqcurrentDate()));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(1))==b_credit+(moT2-moT1));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(2))==b_credit+((moT2-moT1)*2));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(3))==b_credit+((moT2-moT1)*3));
//test weighted moving average
a.setForecastMethod(1);
a.setForecastDays(3);
a.setAccountsCycle(1);
a.setForecastCycles(3);
a.setBeginForecastDay(0);
a.setHistoryMethod(1);
a.doForecast();
CPPUNIT_ASSERT(a.forecastBalance(a_credit, 0) == b_credit);
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(1)) == (b_credit+(((moT2-moT1)*3+moT2*2+moT2)/MyMoneyMoney(6,1))));
}
void MyMoneyForecastTest::testGetForecastAccountList()
{
MyMoneyForecast a;
MyMoneyAccount a_checking = file->account(acChecking);
MyMoneyAccount a_parent = file->account(acParent);
TQValueList<MyMoneyAccount> b;
b = a.forecastAccountList();
//check that it contains asset account, but not expense accounts
CPPUNIT_ASSERT(b.contains(a_checking));
CPPUNIT_ASSERT(!b.contains(a_parent));
}
void MyMoneyForecastTest::testCalculateAccountTrend()
{
//set up environment
TransactionHelper t1( TQDate::tqcurrentDate().addDays(-3), MyMoneySplit::ActionDeposit, -moT2, acChecking, acSolo );
MyMoneyAccount a_checking = file->account(acChecking);
//test invalid arguments
try {
MyMoneyForecast::calculateAccountTrend(a_checking, 0);
}
catch (MyMoneyException *e) {
CPPUNIT_ASSERT(e->what().compare("Illegal arguments when calling calculateAccountTrend. trendDays must be higher than 0") == 0);
delete e;
}
try {
MyMoneyForecast::calculateAccountTrend(a_checking, -10);
}
catch (MyMoneyException *e) {
CPPUNIT_ASSERT(e->what().compare("Illegal arguments when calling calculateAccountTrend. trendDays must be higher than 0") == 0);
delete e;
}
//test that it calculates correctly
CPPUNIT_ASSERT(MyMoneyForecast::calculateAccountTrend(a_checking ,3) == moT2/MyMoneyMoney(3,1));
//test that it works for all kind of accounts
MyMoneyAccount a_solo = file->account(acSolo);
MyMoneyMoney soloTrend = MyMoneyForecast::calculateAccountTrend(a_solo,3);
MyMoneyMoney soloTrendExp = -moT2/MyMoneyMoney(3,1);
CPPUNIT_ASSERT(MyMoneyForecast::calculateAccountTrend(a_solo,3) == -moT2/MyMoneyMoney(3,1));
//test that it does not take into account the transactions of the opening date of the account
MyMoneyAccount a_cash = file->account(acCash);
TransactionHelper t2( TQDate::tqcurrentDate().addDays(-2), MyMoneySplit::ActionDeposit, moT2, acCash, acParent );
TransactionHelper t3( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionDeposit, moT1, acCash, acParent );
CPPUNIT_ASSERT(MyMoneyForecast::calculateAccountTrend(a_cash,3) == -moT1);
}
void MyMoneyForecastTest::testGetForecastBalance()
{
//set up environment
MyMoneyForecast a;
TransactionHelper t1( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionWithdrawal, this->moT1, acChecking, acSolo);
TransactionHelper t2( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionDeposit, -(this->moT2), acCredit, acParent);
TransactionHelper t3( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionTransfer, this->moT1, acCredit, acChecking);
a.setForecastMethod(1);
a.setForecastDays(3);
a.setAccountsCycle(1);
a.setForecastCycles(1);
a.setHistoryMethod(0);
a.doForecast();
MyMoneyAccount a_checking = file->account(acChecking);
MyMoneyAccount a_credit = file->account(acCredit);
//test invalid arguments
CPPUNIT_ASSERT(a.forecastBalance(a_checking, TQDate::tqcurrentDate().addDays(-1))==MyMoneyMoney(0,1));
CPPUNIT_ASSERT(a.forecastBalance(a_checking, TQDate::tqcurrentDate().addDays(-10))==MyMoneyMoney(0,1));
CPPUNIT_ASSERT(a.forecastBalance(a_checking, -1)==MyMoneyMoney(0,1));
CPPUNIT_ASSERT(a.forecastBalance(a_checking, -100)==MyMoneyMoney(0,1));
//test a date outside the forecast days
CPPUNIT_ASSERT(a.forecastBalance(a_checking, TQDate::tqcurrentDate().addDays(4))==MyMoneyMoney(0,1));
CPPUNIT_ASSERT(a.forecastBalance(a_checking, 4)==MyMoneyMoney(0,1));
CPPUNIT_ASSERT(a.forecastBalance(a_checking, TQDate::tqcurrentDate().addDays(10))==MyMoneyMoney(0,1));
CPPUNIT_ASSERT(a.forecastBalance(a_checking, 10)==MyMoneyMoney(0,1));
//test it returns valid results
MyMoneyMoney b_credit = file->balance(a_credit.id(), TQDate::tqcurrentDate());
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate())==file->balance(a_credit.id(), TQDate::tqcurrentDate()));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(1))==b_credit+(moT2-moT1));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(2))==b_credit+((moT2-moT1)*2));
CPPUNIT_ASSERT(a.forecastBalance(a_credit, TQDate::tqcurrentDate().addDays(3))==b_credit+((moT2-moT1)*3));
}
void MyMoneyForecastTest::testIsForecastAccount()
{
MyMoneyForecast a;
MyMoneyAccount a_checking = file->account(acChecking);
MyMoneyAccount a_solo = file->account(acSolo);
MyMoneyAccount a_investment = file->account(acInvestment);
//test an invalid account
CPPUNIT_ASSERT(a.isForecastAccount(a_solo)==false);
CPPUNIT_ASSERT(a.isForecastAccount(a_investment)==true);
//test a valid account
CPPUNIT_ASSERT(a.isForecastAccount(a_checking)==true);
}
void MyMoneyForecastTest::testDoFutureScheduledForecast()
{
//set up future transactions
MyMoneyForecast a;
MyMoneyAccount a_cash = file->account(acCash);
TransactionHelper t1( TQDate::tqcurrentDate().addDays(1), MyMoneySplit::ActionDeposit, -moT1, acCash, acParent );
TransactionHelper t2( TQDate::tqcurrentDate().addDays(2), MyMoneySplit::ActionDeposit, -moT2, acCash, acParent );
TransactionHelper t3( TQDate::tqcurrentDate().addDays(3), MyMoneySplit::ActionDeposit, -moT3, acCash, acParent );
TransactionHelper t4( TQDate::tqcurrentDate().addDays(10), MyMoneySplit::ActionDeposit, -moT4, acCash, acParent );
a.setForecastMethod(0);
a.setForecastDays(3);
a.setAccountsCycle(1);
a.setForecastCycles(1);
a.doForecast();
MyMoneyMoney b_cash = file->balance(a_cash.id(), TQDate::tqcurrentDate());
//test valid results
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate())==b_cash);
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(1))==b_cash+moT1);
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(2))==b_cash+moT1+moT2);
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(3))==b_cash+moT1+moT2+moT3);
}
void MyMoneyForecastTest::testScheduleForecast()
{
//set up schedule environment for testing
MyMoneyAccount a_cash = file->account(acCash);
MyMoneyAccount a_parent = file->account(acParent);
MyMoneyFileTransaction ft;
MyMoneySchedule sch( "A Name",
MyMoneySchedule::TYPE_BILL,
MyMoneySchedule::OCCUR_WEEKLY, 1,
MyMoneySchedule::STYPE_DIRECTDEBIT,
TQDate::tqcurrentDate().addDays(1),
TQDate(),
true,
true);
MyMoneyTransaction t;
t.setPostDate(TQDate::tqcurrentDate().addDays(1));
t.setEntryDate(TQDate::tqcurrentDate().addDays(1));
//t.setId("T000000000000000001");
t.setBankID("BID");
t.setMemo("Wohnung:Miete");
t.setCommodity("USD");
t.setValue("key", "value");
MyMoneySplit s;
s.setPayeeId("P000001");
s.setShares(moT2);
s.setValue(moT2);
s.setAccountId(a_parent.id());
s.setBankID("SPID1");
s.setReconcileFlag(MyMoneySplit::Reconciled);
t.addSplit(s);
s.setPayeeId("P000001");
s.setShares(-moT2);
s.setValue(-moT2);
s.setAccountId(a_cash.id());
s.setBankID("SPID2");
s.setReconcileFlag(MyMoneySplit::Cleared);
s.clearId();
t.addSplit(s);
sch.setTransaction(t);
file->addSchedule(sch);
ft.commit();
MyMoneyFileTransaction ft3;
MyMoneySchedule sch3( "A Name1",
MyMoneySchedule::TYPE_BILL,
MyMoneySchedule::OCCUR_WEEKLY, 1,
MyMoneySchedule::STYPE_DIRECTDEBIT,
TQDate::tqcurrentDate().addDays(5),
TQDate(),
true,
true);
//sch.setLastPayment(TQDate::tqcurrentDate());
//sch.recordPayment(TQDate::tqcurrentDate().addDays(1));
//sch.setId("SCH0001");
MyMoneyTransaction t3;
t3.setPostDate(TQDate::tqcurrentDate().addDays(5));
t3.setEntryDate(TQDate::tqcurrentDate().addDays(5));
//t.setId("T000000000000000001");
t3.setBankID("BID");
t3.setMemo("Wohnung:Miete");
t3.setCommodity("USD");
t3.setValue("key", "value");
MyMoneySplit s3;
s3.setPayeeId("P000001");
s3.setShares(moT2);
s3.setValue(moT2);
s3.setAccountId(a_parent.id());
s3.setBankID("SPID1");
s3.setReconcileFlag(MyMoneySplit::Reconciled);
t3.addSplit(s3);
s3.setPayeeId("P000001");
s3.setShares(-moT2);
s3.setValue(-moT2);
s3.setAccountId(a_cash.id());
s3.setBankID("SPID2");
s3.setReconcileFlag(MyMoneySplit::Cleared);
s3.clearId();
t3.addSplit(s3);
sch3.setTransaction(t3);
file->addSchedule(sch3);
ft3.commit();
MyMoneyFileTransaction ft2;
MyMoneySchedule sch2( "A Name2",
MyMoneySchedule::TYPE_BILL,
MyMoneySchedule::OCCUR_WEEKLY, 1,
MyMoneySchedule::STYPE_DIRECTDEBIT,
TQDate::tqcurrentDate().addDays(2),
TQDate(),
true,
true);
//sch.setLastPayment(TQDate::tqcurrentDate());
//sch.recordPayment(TQDate::tqcurrentDate().addDays(1));
//sch.setId("SCH0001");
MyMoneyTransaction t2;
t2.setPostDate(TQDate::tqcurrentDate().addDays(2));
t2.setEntryDate(TQDate::tqcurrentDate().addDays(2));
//t.setId("T000000000000000001");
t2.setBankID("BID");
t2.setMemo("Wohnung:Miete");
t2.setCommodity("USD");
t2.setValue("key", "value");
MyMoneySplit s2;
s2.setPayeeId("P000001");
s2.setShares(moT1);
s2.setValue(moT1);
s2.setAccountId(a_parent.id());
s2.setBankID("SPID1");
s2.setReconcileFlag(MyMoneySplit::Reconciled);
t2.addSplit(s2);
s2.setPayeeId("P000001");
s2.setShares(-moT1);
s2.setValue(-moT1);
s2.setAccountId(a_cash.id());
s2.setBankID("SPID2");
s2.setReconcileFlag(MyMoneySplit::Cleared);
s2.clearId();
t2.addSplit(s2);
sch2.setTransaction(t2);
file->addSchedule(sch2);
ft2.commit();
//run forecast
MyMoneyForecast a;
a.setForecastMethod(0);
a.setForecastDays(3);
a.setAccountsCycle(1);
a.setForecastCycles(1);
a.doForecast();
//check result for single schedule
MyMoneyMoney b_cash = file->balance(a_cash.id(), TQDate::tqcurrentDate());
MyMoneyMoney b_cash1 = a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(1));
//test valid results
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate())==b_cash);
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(1))==b_cash-moT2);
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(2))==b_cash-moT2-moT1);
}
void MyMoneyForecastTest::testDaysToMinimumBalance()
{
//setup environment
MyMoneyForecast a;
MyMoneyAccount a_cash = file->account(acCash);
MyMoneyAccount a_credit = file->account(acCredit);
MyMoneyAccount a_parent = file->account(acParent);
a_cash.setValue("minBalanceAbsolute", "50");
a_credit.setValue("minBalanceAbsolute", "50");
TransactionHelper t1( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionDeposit, -moT1, acCash, acParent );
TransactionHelper t2( TQDate::tqcurrentDate().addDays(2), MyMoneySplit::ActionDeposit, moT2, acCash, acParent );
TransactionHelper t3( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionWithdrawal, -moT1, acCredit, acParent );
TransactionHelper t4( TQDate::tqcurrentDate().addDays(4), MyMoneySplit::ActionWithdrawal, moT5, acCredit, acParent );
a.setForecastMethod(0);
a.setForecastDays(3);
a.setAccountsCycle(1);
a.setForecastCycles(1);
a.setBeginForecastDay(0);
a.doForecast();
//test invalid arguments
MyMoneyAccount nullAcc;
CPPUNIT_ASSERT(a.daysToMinimumBalance(nullAcc) == -1);
//test when not a forecast account
CPPUNIT_ASSERT(a.daysToMinimumBalance(a_parent) == -1);
//test it warns when inside the forecast period
CPPUNIT_ASSERT(a.daysToMinimumBalance(a_cash) == 2);
//test it does not warn when it will be outside of the forecast period
CPPUNIT_ASSERT(a.daysToMinimumBalance(a_credit) == -1);
}
void MyMoneyForecastTest::testDaysToZeroBalance()
{
//set up environment
MyMoneyAccount a_Solo = file->account(acSolo);
MyMoneyAccount a_Cash = file->account(acCash);
MyMoneyAccount a_Credit = file->account(acCredit);
//MyMoneyFileTransaction ft;
TransactionHelper t1( TQDate::tqcurrentDate().addDays(2), MyMoneySplit::ActionWithdrawal, -moT1, acChecking, acSolo );
TransactionHelper t2( TQDate::tqcurrentDate().addDays(2), MyMoneySplit::ActionTransfer, (moT5), acCash, acCredit );
TransactionHelper t3( TQDate::tqcurrentDate().addDays(2), MyMoneySplit::ActionWithdrawal, (moT5*100), acCredit, acParent );
//ft.commit();
MyMoneyForecast a;
a.setForecastMethod(0);
a.setForecastDays(30);
a.setAccountsCycle(1);
a.setForecastCycles(3);
a.doForecast();
//test invalid arguments
MyMoneyAccount nullAcc;
try {
a.daysToZeroBalance(nullAcc);
}
catch (MyMoneyException *e) {
delete e;
CPPUNIT_FAIL("Unexpected exception");
}
//test when not a forecast account
MyMoneyAccount a_solo = file->account(acSolo);
int iSolo = a.daysToZeroBalance(a_Solo);
CPPUNIT_ASSERT(iSolo == -2);
//test it warns when inside the forecast period
MyMoneyMoney fCash = a.forecastBalance(a_Cash, TQDate::tqcurrentDate().addDays(2));
CPPUNIT_ASSERT(a.daysToZeroBalance(a_Cash) == 2);
//test it does not warn when it will be outside of the forecast period
}
void MyMoneyForecastTest::testSkipOpeningDate()
{
//set up environment
MyMoneyForecast a;
TransactionHelper t1( TQDate::tqcurrentDate().addDays(-2), MyMoneySplit::ActionWithdrawal, this->moT1, acCash, acSolo);
TransactionHelper t2( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionWithdrawal, this->moT2, acCash, acSolo);
a.setForecastMethod(1);
a.setForecastDays(3);
a.setAccountsCycle(2);
a.setForecastCycles(1);
a.setHistoryMethod(0);
a.doForecast();
MyMoneyAccount a_cash = file->account(acCash);
//test it has no variation because it skipped the variation of the opening date
MyMoneyMoney b_cash = file->balance(a_cash.id(), TQDate::tqcurrentDate());
CPPUNIT_ASSERT(a.skipOpeningDate() == true);
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate())==b_cash);
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(1))==b_cash);
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(2))==b_cash-moT2);
CPPUNIT_ASSERT(a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(3))==b_cash-moT2);
}
void MyMoneyForecastTest::testAccountMinimumBalanceDateList() {
//set up environment
MyMoneyForecast a;
TransactionHelper t1( TQDate::tqcurrentDate().addDays(-2), MyMoneySplit::ActionWithdrawal, this->moT1, acCash, acSolo);
TransactionHelper t2( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionWithdrawal, this->moT2, acCash, acSolo);
a.setForecastMethod(1);
a.setForecastDays(6);
a.setAccountsCycle(2);
a.setForecastCycles(3);
a.setHistoryMethod(0);
a.setBeginForecastDay(TQDate::tqcurrentDate().addDays(1).day());
a.doForecast();
MyMoneyAccount a_cash = file->account(acCash);
//test
TQValueList<TQDate> dateList;
dateList = a.accountMinimumBalanceDateList(a_cash);
TQValueList<TQDate>::iterator it = dateList.begin();
TQDate minDate = *it;
CPPUNIT_ASSERT(minDate==TQDate::tqcurrentDate().addDays(2));
it++;
minDate = *it;
CPPUNIT_ASSERT(minDate==TQDate::tqcurrentDate().addDays(4));
it++;
minDate = *it;
CPPUNIT_ASSERT(minDate==TQDate::tqcurrentDate().addDays(6));
}
void MyMoneyForecastTest::testAccountMaximumBalanceDateList() {
//set up environment
MyMoneyForecast a;
TransactionHelper t1( TQDate::tqcurrentDate().addDays(-2), MyMoneySplit::ActionWithdrawal, this->moT1, acCash, acSolo);
TransactionHelper t2( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionWithdrawal, this->moT2, acCash, acSolo);
a.setForecastMethod(1);
a.setForecastDays(6);
a.setAccountsCycle(2);
a.setForecastCycles(3);
a.setHistoryMethod(0);
a.setBeginForecastDay(TQDate::tqcurrentDate().addDays(1).day());
a.doForecast();
MyMoneyAccount a_cash = file->account(acCash);
//test
TQValueList<TQDate> dateList;
dateList = a.accountMaximumBalanceDateList(a_cash);
TQValueList<TQDate>::iterator it = dateList.begin();
TQDate maxDate = *it;
CPPUNIT_ASSERT(maxDate==TQDate::tqcurrentDate().addDays(1));
it++;
maxDate = *it;
CPPUNIT_ASSERT(maxDate==TQDate::tqcurrentDate().addDays(3));
it++;
maxDate = *it;
CPPUNIT_ASSERT(maxDate==TQDate::tqcurrentDate().addDays(5));
}
void MyMoneyForecastTest::testAccountAverageBalance() {
//set up environment
MyMoneyForecast a;
TransactionHelper t1( TQDate::tqcurrentDate().addDays(-2), MyMoneySplit::ActionWithdrawal, this->moT1, acCash, acSolo);
TransactionHelper t2( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionWithdrawal, this->moT2, acCash, acSolo);
a.setForecastMethod(1);
a.setForecastDays(3);
a.setAccountsCycle(2);
a.setForecastCycles(1);
a.setBeginForecastDay(0);
a.doForecast();
MyMoneyAccount a_cash = file->account(acCash);
//test
MyMoneyMoney b_cash1 = a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(1));
MyMoneyMoney b_cash2 = a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(2));
MyMoneyMoney b_cash3 = a.forecastBalance(a_cash, TQDate::tqcurrentDate().addDays(3));
MyMoneyMoney average = (b_cash1 + b_cash2 +b_cash3)/MyMoneyMoney(3,1);
CPPUNIT_ASSERT(a.accountAverageBalance(a_cash)==average);
}
void MyMoneyForecastTest::testBeginForecastDate() {
//set up environment
MyMoneyForecast a;
TQDate beginDate;
int beginDay;
a.setForecastMethod(1);
a.setForecastDays(90);
a.setAccountsCycle(14);
a.setForecastCycles(3);
a.setBeginForecastDay(0);
a.doForecast();
//test when using old method without begin day
CPPUNIT_ASSERT(TQDate::tqcurrentDate() == a.beginForecastDate());
//setup begin to last day of month
a.setBeginForecastDay(31);
beginDay = a.beginForecastDay();
a.doForecast();
//test
if(TQDate::tqcurrentDate().day() < beginDay)
{
if(TQDate::tqcurrentDate().daysInMonth() < beginDay)
beginDay = TQDate::tqcurrentDate().daysInMonth();
beginDate = TQDate(TQDate::tqcurrentDate().year(), TQDate::tqcurrentDate().month(), beginDay);
CPPUNIT_ASSERT(beginDate == a.beginForecastDate());
}
//setup begin day to same date
a.setBeginForecastDay(TQDate::tqcurrentDate().day());
beginDay = a.beginForecastDay();
a.doForecast();
CPPUNIT_ASSERT(TQDate::tqcurrentDate() == a.beginForecastDate());
//setup to first day of month with small interval
a.setBeginForecastDay(1);
a.setAccountsCycle(1);
beginDay = a.beginForecastDay();
a.doForecast();
//test
if(TQDate::tqcurrentDate() == a.beginForecastDate()) {
CPPUNIT_ASSERT(TQDate::tqcurrentDate() == a.beginForecastDate());
} else {
beginDay = ((((TQDate::tqcurrentDate().day() - beginDay)/a.accountsCycle()) + 1) * a.accountsCycle()) + beginDay;
if(beginDay > TQDate::tqcurrentDate().daysInMonth())
beginDay = TQDate::tqcurrentDate().daysInMonth();
beginDate = TQDate(TQDate::tqcurrentDate().year(), TQDate::tqcurrentDate().month(), beginDay);
if(TQDate::tqcurrentDate().day() == TQDate::tqcurrentDate().daysInMonth() ) {
std::cout << std::endl << "testBeginForecastDate(): test of first day of month with small interval skipped because it is the last day of month" << std::endl;
} else {
CPPUNIT_ASSERT(beginDate == a.beginForecastDate());
}
}
//setup to test when current date plus cycle equals begin day
a.setAccountsCycle(14);
beginDay = TQDate::tqcurrentDate().addDays(14).day();
a.setBeginForecastDay(beginDay);
beginDate = TQDate::tqcurrentDate().addDays(14);
a.doForecast();
//test
CPPUNIT_ASSERT(beginDate == a.beginForecastDate());
//setup to test when the begin day will be next month
a.setBeginForecastDay(1);
a.setAccountsCycle(40);
a.doForecast();
beginDate = TQDate(TQDate::tqcurrentDate().addMonths(1).year(), TQDate::tqcurrentDate().addMonths(1).month(), 1);
//test
if(TQDate::tqcurrentDate().day() > 1) {
CPPUNIT_ASSERT(beginDate == a.beginForecastDate());
} else {
//test is not valid if today is 1st of month
std::cout << std::endl << "testBeginForecastDate(): test of first day of month skipped because current day is 1st of month" << std::endl;
}
}
void MyMoneyForecastTest::testHistoryDays(void)
{
MyMoneyForecast a;
CPPUNIT_ASSERT(a.historyStartDate() == TQDate::tqcurrentDate().addDays(-a.forecastCycles()*a.accountsCycle()) );
CPPUNIT_ASSERT(a.historyEndDate() == TQDate::tqcurrentDate().addDays(-1) );
CPPUNIT_ASSERT(a.historyDays() == a.forecastCycles()*a.accountsCycle());
a.setForecastMethod(1);
a.setForecastDays(90);
a.setAccountsCycle(14);
a.setForecastCycles(3);
a.setBeginForecastDay(0);
a.doForecast();
CPPUNIT_ASSERT(a.historyStartDate() == TQDate::tqcurrentDate().addDays(-14*3) );
CPPUNIT_ASSERT(a.historyDays() == (14*3));
CPPUNIT_ASSERT(a.historyEndDate() == (TQDate::tqcurrentDate().addDays(-1)) );
}
void MyMoneyForecastTest::testCreateBudget()
{
//set up environment
MyMoneyForecast a;
MyMoneyForecast b;
MyMoneyBudget budget;
TransactionHelper t1( TQDate(2005, 1, 3), MyMoneySplit::ActionWithdrawal, this->moT1, acCash, acSolo);
TransactionHelper t2( TQDate(2005, 1, 15), MyMoneySplit::ActionWithdrawal, this->moT2, acCash, acParent);
TransactionHelper t3( TQDate(2005, 1, 30), MyMoneySplit::ActionWithdrawal, this->moT3, acCash, acSolo);
TransactionHelper t4( TQDate(2006, 1, 25), MyMoneySplit::ActionWithdrawal, this->moT4, acCash, acParent);
TransactionHelper t5( TQDate(2005, 4, 3), MyMoneySplit::ActionWithdrawal, this->moT1, acCash, acSolo);
TransactionHelper t6( TQDate(2006, 5, 15), MyMoneySplit::ActionWithdrawal, this->moT2, acCash, acParent);
TransactionHelper t7( TQDate(2005, 8, 3), MyMoneySplit::ActionWithdrawal, this->moT3, acCash, acSolo);
TransactionHelper t8( TQDate(2006, 9, 15), MyMoneySplit::ActionWithdrawal, this->moT4, acCash, acParent);
a.setHistoryMethod(0);
a.setForecastMethod(1);
a.createBudget(budget, TQDate(2005, 1, 1), TQDate(2006, 12, 31), TQDate(2007, 1, 1), TQDate(2007, 12, 31), true);
//test
MyMoneyAccount a_solo = file->account(acSolo);
MyMoneyAccount a_parent = file->account(acParent);
//test it has no variation because it skipped the variation of the opening date
CPPUNIT_ASSERT(a.forecastBalance(a_solo, TQDate(2007, 1, 1)) == ((moT1+moT3)/MyMoneyMoney(2, 1)));
CPPUNIT_ASSERT(a.forecastBalance(a_parent, TQDate(2007, 1, 1)) == ((moT2+moT4)/MyMoneyMoney(2, 1)));
CPPUNIT_ASSERT(a.forecastBalance(a_solo, TQDate(2007, 4, 1)) == ((moT1)/MyMoneyMoney(2, 1)));
CPPUNIT_ASSERT(a.forecastBalance(a_parent, TQDate(2007, 5, 1)) == ((moT2)/MyMoneyMoney(2, 1)));
CPPUNIT_ASSERT(a.forecastBalance(a_solo, TQDate(2007, 8, 1)) == ((moT3)/MyMoneyMoney(2, 1)));
CPPUNIT_ASSERT(a.forecastBalance(a_parent, TQDate(2007, 9, 1)) == ((moT4)/MyMoneyMoney(2, 1)));
//test the budget object returned by the method
CPPUNIT_ASSERT(budget.account(a_parent.id()).period(TQDate(2007, 9, 1)).amount() == ((moT4)/MyMoneyMoney(2, 1)));
//setup test for a length lower than a year
b.setForecastMethod(1);
b.setHistoryMethod(0);
b.createBudget(budget, TQDate(2005, 1, 1), TQDate(2005, 6, 30), TQDate(2007, 1, 1), TQDate(2007, 6, 30), true);
//test
CPPUNIT_ASSERT(b.forecastBalance(a_solo, TQDate(2007, 1, 1)) == (moT1+moT3));
CPPUNIT_ASSERT(b.forecastBalance(a_parent, TQDate(2007, 1, 1)) == (moT2));
CPPUNIT_ASSERT(b.forecastBalance(a_solo, TQDate(2007, 4, 1)) == (moT1));
CPPUNIT_ASSERT(b.forecastBalance(a_parent, TQDate(2007, 5, 1)) == (MyMoneyMoney(0, 1)));
//set up schedule environment for testing
MyMoneyAccount a_cash = file->account(acCash);
MyMoneyFileTransaction ft;
MyMoneySchedule sch( "A Name",
MyMoneySchedule::TYPE_BILL,
MyMoneySchedule::OCCUR_MONTHLY, 1,
MyMoneySchedule::STYPE_DIRECTDEBIT,
TQDate::tqcurrentDate(),
TQDate(),
true,
true);
MyMoneyTransaction t10;
t10.setPostDate(TQDate::tqcurrentDate().addMonths(1));
t10.setEntryDate(TQDate::tqcurrentDate().addMonths(1));
//t.setId("T000000000000000001");
t10.setBankID("BID");
t10.setMemo("Wohnung:Miete");
t10.setCommodity("USD");
t10.setValue("key", "value");
MyMoneySplit s;
s.setPayeeId("P000001");
s.setShares(moT2);
s.setValue(moT2);
s.setAccountId(a_parent.id());
s.setBankID("SPID1");
s.setReconcileFlag(MyMoneySplit::Reconciled);
t10.addSplit(s);
s.setPayeeId("P000001");
s.setShares(-moT2);
s.setValue(-moT2);
s.setAccountId(a_cash.id());
s.setBankID("SPID2");
s.setReconcileFlag(MyMoneySplit::Cleared);
s.clearId();
t10.addSplit(s);
sch.setTransaction(t10);
file->addSchedule(sch);
ft.commit();
//run forecast
MyMoneyForecast c;
c.setForecastMethod(0);
c.setForecastCycles(1);
c.createBudget(budget, TQDate::tqcurrentDate().addYears(-2), TQDate::tqcurrentDate().addYears(-1), TQDate::tqcurrentDate().addMonths(-2), TQDate::tqcurrentDate().addMonths(6), true);
MyMoneyMoney c_parent = c.forecastBalance(a_parent, TQDate(TQDate::tqcurrentDate().addMonths(1).year(), TQDate::tqcurrentDate().addMonths(1).month(), 1) );
//test valid results
CPPUNIT_ASSERT(c.forecastBalance(a_parent, TQDate(TQDate::tqcurrentDate().addMonths(1).year(), TQDate::tqcurrentDate().addMonths(1).month(), 1) ) == (moT2));
}
void MyMoneyForecastTest::testLinearRegression() {
//set up environment
MyMoneyForecast a;
MyMoneyAccount a_checking = file->account(acChecking);
MyMoneyAccount a_credit = file->account(acCredit);
//setup some transactions
TransactionHelper t1( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionWithdrawal, this->moT1, acChecking, acSolo);
TransactionHelper t2( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionDeposit, -(this->moT2), acCredit, acParent);
TransactionHelper t3( TQDate::tqcurrentDate().addDays(-1), MyMoneySplit::ActionTransfer, this->moT1, acCredit, acChecking);
//TODO Add tests specific for linear regression
}