/*************************************************************************** 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 #include #include "mymoneybudget.h" #include #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 b; b = a.forecastAccountList(); //check that it contains asset account, but not expense accounts CPPUNIT_ASSERT(b.tqcontains(a_checking)); CPPUNIT_ASSERT(!b.tqcontains(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 dateList; dateList = a.accountMinimumBalanceDateList(a_cash); TQValueList::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 dateList; dateList = a.accountMaximumBalanceDateList(a_cash); TQValueList::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 }