/***************************************************************************
                          pivottabletest.cpp
                          -------------------
    copyright            : (C) 2002-2005 by Thomas Baumgart
    email                : ipwizard@users.sourceforge.net
                           Ace Jones <ace.j@hotpop.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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <tqvaluelist.h>
#include <tqvaluevector.h>
#include <tqdom.h>
#include <tqfile.h>

#include <kdebug.h>
#include <tdeglobal.h>
#include <tdeglobalsettings.h>
#include <tdelocale.h>
#include <kstandarddirs.h>

// DOH, mmreport.h uses this without including it!!
#include "../mymoney/mymoneyaccount.h"

#include "../mymoney/mymoneysecurity.h"
#include "../mymoney/mymoneyprice.h"
#include "../mymoney/mymoneyreport.h"
#include "../mymoney/mymoneystatement.h"
#include "../mymoney/storage/mymoneystoragedump.h"
#include "../mymoney/storage/mymoneystoragexml.h"

#define private public
#include "../reports/pivottable.h"
#undef private

#include "reportstestcommon.h"
#include "pivottabletest.h"

using namespace reports;
using namespace test;

PivotTableTest::PivotTableTest()
{
}

void PivotTableTest::setUp ()
{
  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("Thomas Baumgart");
  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);
  acCredit = makeAccount(TQString("Credit Card"),MyMoneyAccount::CreditCard,moCreditOpen,TQDate(2004,7,15),acLiability);
  acSolo = makeAccount(TQString("Solo"),MyMoneyAccount::Expense,0,TQDate(2004,1,11),acExpense);
  acParent = makeAccount(TQString("Parent"),MyMoneyAccount::Expense,0,TQDate(2004,1,11),acExpense);
  acChild = makeAccount(TQString("Child"),MyMoneyAccount::Expense,0,TQDate(2004,2,11),acParent);
  acForeign = makeAccount(TQString("Foreign"),MyMoneyAccount::Expense,0,TQDate(2004,1,11),acExpense);

  acSecondChild = makeAccount(TQString("Second Child"),MyMoneyAccount::Expense,0,TQDate(2004,2,11),acParent);
  acGrandChild1 = makeAccount(TQString("Grand Child 1"),MyMoneyAccount::Expense,0,TQDate(2004,2,11),acChild);
  acGrandChild2 = makeAccount(TQString("Grand Child 2"),MyMoneyAccount::Expense,0,TQDate(2004,2,11),acChild);

  MyMoneyInstitution i("Bank of the World","","","","","","");
  file->addInstitution(i);
  inBank = i.id();
  ft.commit();
}

void PivotTableTest::tearDown ()
{
  file->detachStorage(storage);
  delete storage;
}

void PivotTableTest::testNetWorthSingle()
{
  try
  {
    MyMoneyReport filter;
    filter.setRowType( MyMoneyReport::eAssetLiability );
    filter.setDateFilter(TQDate(2004,1,1),TQDate(2004,7,1).addDays(-1));
    XMLandback(filter);
    PivotTable networth_f(filter);
    writeTabletoCSV(networth_f);

    CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Checking Account"][acChecking][eActual][5]==moCheckingOpen);
    CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Checking Account"][acChecking][eActual][6]==moCheckingOpen);
    CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Checking Account"].m_total[eActual][5]==moCheckingOpen);
    CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][0]==moZero);
    CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][4]==moZero);
    CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][5]==moCheckingOpen);
    CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][6]==moCheckingOpen);
  }
  catch(MyMoneyException *e)
  {
    CPPUNIT_FAIL(e->what().local8Bit().data());
    delete e;
  }
}

void PivotTableTest::testNetWorthOfsetting()
{
  // Test the net worth report to make sure it picks up the opening balance for two
  // accounts opened during the period of the report, one asset & one liability.  Test
  // that it calculates the totals correctly.

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eAssetLiability );
  filter.setDateFilter(TQDate(2004,1,1),TQDate(2005,1,1).addDays(-1));
  XMLandback(filter);
  PivotTable networth_f( filter );
  CPPUNIT_ASSERT(networth_f.m_grid["Liability"]["Credit Card"][acCredit][eActual][7]==-moCreditOpen);
  CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][0]==moZero);
  CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][12]==moCheckingOpen+moCreditOpen);

}

void PivotTableTest::testNetWorthOpeningPrior()
{
  // Test the net worth report to make sure it's picking up opening balances PRIOR to
  // the period of the report.

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eAssetLiability );
  filter.setDateFilter(TQDate(2005,8,1),TQDate(2005,12,31));
  filter.setName("Net Worth Opening Prior 1");
  XMLandback(filter);
  PivotTable networth_f( filter );
  writeTabletoCSV(networth_f);

  CPPUNIT_ASSERT(networth_f.m_grid["Liability"]["Credit Card"].m_total[eActual][0]==-moCreditOpen);
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Checking Account"].m_total[eActual][0]==moCheckingOpen);
  CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][0]==moCheckingOpen+moCreditOpen);
  CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][1]==moCheckingOpen+moCreditOpen);

  // Test the net worth report to make sure that transactions prior to the report
  // period are included in the opening balance

  TransactionHelper t1( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );
  TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acChecking, acChild );

  filter.setName("Net Worth Opening Prior 2");
  PivotTable networth_f2( filter );
  writeTabletoCSV(networth_f2);
  CPPUNIT_ASSERT(networth_f2.m_grid["Liability"]["Credit Card"].m_total[eActual][1]==-moCreditOpen+moParent);
  CPPUNIT_ASSERT(networth_f2.m_grid["Asset"]["Checking Account"].m_total[eActual][1]==moCheckingOpen-moChild);
  CPPUNIT_ASSERT(networth_f2.m_grid.m_total[eActual][1]==moCheckingOpen+moCreditOpen-moChild-moParent);
}

void PivotTableTest::testNetWorthDateFilter()
{
  // Test a net worth report whose period is prior to the time any accounts are open,
  // so the report should be zero.

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eAssetLiability );
  filter.setDateFilter(TQDate(2004,1,1),TQDate(2004,2,1).addDays(-1));
  XMLandback(filter);
  PivotTable networth_f( filter );
  CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][1]==moZero);

}

void PivotTableTest::testSpendingEmpty()
{
  // test a spending report with no entries

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  XMLandback(filter);
  PivotTable spending_f1( filter );
  CPPUNIT_ASSERT(spending_f1.m_grid.m_total[eActual].m_total==moZero);

  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  PivotTable spending_f2( filter );
  CPPUNIT_ASSERT(spending_f2.m_grid.m_total[eActual].m_total==moZero);
}

void PivotTableTest::testSingleTransaction()
{
  // Test a single transaction
  TransactionHelper t( TQDate(2004,10,31), MyMoneySplit::ActionWithdrawal,moSolo, acChecking, acSolo );

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  filter.setName("Spending with Single Transaction.html");
  XMLandback(filter);
  PivotTable spending_f( filter );
  writeTabletoHTML(spending_f,"Spending with Single Transaction.html");

  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Solo"][acSolo][eActual][2]==moSolo);
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Solo"].m_total[eActual][2]==moSolo);
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Solo"].m_total[eActual][1]==moZero);
  CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual][2]==(-moSolo));
  CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==(-moSolo));

  filter.clear();
  filter.setRowType(MyMoneyReport::eAssetLiability);
  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  XMLandback(filter);
  PivotTable networth_f( filter );
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Checking Account"].m_total[eActual][2]==(moCheckingOpen-moSolo) );
}

void PivotTableTest::testSubAccount()
{
  // Test a sub-account with a value, under an account with a value

  TransactionHelper t1( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );
  TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  filter.setDetailLevel(MyMoneyReport::eDetailAll);
  filter.setName("Spending with Sub-Account");
  XMLandback(filter);
  PivotTable spending_f( filter );
  writeTabletoHTML(spending_f,"Spending with Sub-Account.html");

  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Parent"][acParent][eActual][3]==moParent);
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Parent"][acChild][eActual][3]==moChild);
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Parent"].m_total[eActual][3]==moParent+moChild);
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Parent"].m_total[eActual][2]==moZero);
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Parent"].m_total[eActual].m_total==moParent+moChild);
  CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual][3]==(-moParent-moChild));
  CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==(-moParent-moChild));

  filter.clear();
  filter.setRowType(MyMoneyReport::eAssetLiability);
  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  filter.setName("Net Worth with Sub-Account");
  XMLandback(filter);
  PivotTable networth_f( filter );
  writeTabletoHTML(networth_f,"Net Worth with Sub-Account.html");
  CPPUNIT_ASSERT(networth_f.m_grid["Liability"]["Credit Card"].m_total[eActual][3]==moParent+moChild-moCreditOpen );
  CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][4] == -moParent-moChild+moCreditOpen+moCheckingOpen );

}

void PivotTableTest::testFilterIEvsIE()
{
  // Test that removing an income/spending account will remove the entry from an income/spending report
  TransactionHelper t1( TQDate(2004,10,31), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  filter.addCategory(acChild);
  filter.addCategory(acSolo);
  XMLandback(filter);
  PivotTable spending_f( filter );

  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Parent"].m_total[eActual][3]==moChild);
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"].m_total[eActual][2]==moSolo);
  CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==-moSolo-moChild);

}

void PivotTableTest::testFilterALvsAL()
{
  // Test that removing an asset/liability account will remove the entry from an asset/liability report
  TransactionHelper t1( TQDate(2004,10,31), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eAssetLiability );
  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  filter.addAccount(acChecking);
  filter.addCategory(acChild);
  filter.addCategory(acSolo);
  XMLandback(filter);
  PivotTable networth_f( filter );
  CPPUNIT_ASSERT(networth_f.m_grid.m_total[eActual][3] == -moSolo+moCheckingOpen );
}

void PivotTableTest::testFilterALvsIE()
{
  // Test that removing an asset/liability account will remove the entry from an income/spending report
  TransactionHelper t1( TQDate(2004,10,31), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  filter.addAccount(acChecking);
  CPPUNIT_ASSERT(file->transactionList(filter).count() == 1);

  XMLandback(filter);
  PivotTable spending_f( filter );

  CPPUNIT_ASSERT(spending_f.m_grid["Expense"].m_total[eActual][3]==moZero);
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"].m_total[eActual][2]==moSolo);
  CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==-moSolo);
}

void PivotTableTest::testFilterAllvsIE()
{
  // Test that removing an asset/liability account AND an income/expense
  // category will remove the entry from an income/spending report
  TransactionHelper t1( TQDate(2004,10,31), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  filter.addAccount(acCredit);
  filter.addCategory(acChild);
  PivotTable spending_f( filter );

  CPPUNIT_ASSERT(spending_f.m_grid["Expense"].m_total[eActual][2]==moZero);
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"].m_total[eActual][3]==moChild);
  CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==-moChild);
}

void PivotTableTest::testFilterBasics()
{
  // Test that the filters are operating the way that the reports expect them to
  TransactionHelper t1( TQDate(2004,10,31), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );
  TransactionHelper t4( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );

  MyMoneyTransactionFilter filter;
  filter.clear();
  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  filter.addCategory(acSolo);
  filter.setReportAllSplits(false);
  filter.setConsiderCategory(true);

  CPPUNIT_ASSERT(file->transactionList(filter).count() == 1);

  filter.addCategory(acParent);

  CPPUNIT_ASSERT(file->transactionList(filter).count() == 3);

  filter.addAccount(acChecking);

  CPPUNIT_ASSERT(file->transactionList(filter).count() == 1);

  filter.clear();
  filter.setDateFilter(TQDate(2004,9,1),TQDate(2005,1,1).addDays(-1));
  filter.addCategory(acParent);
  filter.addAccount(acCredit);
  filter.setReportAllSplits(false);
  filter.setConsiderCategory(true);

  CPPUNIT_ASSERT(file->transactionList(filter).count() == 2);
}

void PivotTableTest::testMultipleCurrencies()
{
  MyMoneyMoney moCanOpening( 0.0 );
  MyMoneyMoney moJpyOpening( 0.0 );
  MyMoneyMoney moCanPrice( 0.75 );
  MyMoneyMoney moJpyPrice( 0.010 );
  MyMoneyMoney moJpyPrice2( 0.011 );
  MyMoneyMoney moJpyPrice3( 0.014 );
  MyMoneyMoney moJpyPrice4( 0.0395 );
  MyMoneyMoney moCanTransaction( 100.0 );
  MyMoneyMoney moJpyTransaction( 100.0 );

  TQString acCanChecking = makeAccount(TQString("Canadian Checking"),MyMoneyAccount::Checkings,moCanOpening,TQDate(2003,11,15),acAsset,"CAD");
  TQString acJpyChecking = makeAccount(TQString("Japanese Checking"),MyMoneyAccount::Checkings,moJpyOpening,TQDate(2003,11,15),acAsset,"JPY");
  TQString acCanCash = makeAccount(TQString("Canadian"),MyMoneyAccount::Expense,0,TQDate(2004,2,11),acForeign,"CAD");
  TQString acJpyCash = makeAccount(TQString("Japanese"),MyMoneyAccount::Expense,0,TQDate(2004,2,11),acForeign,"JPY");

  makePrice("CAD",TQDate(2004,1,1),MyMoneyMoney(moCanPrice));
  makePrice("JPY",TQDate(2004,1,1),MyMoneyMoney(moJpyPrice));
  makePrice("JPY",TQDate(2004,5,1),MyMoneyMoney(moJpyPrice2));
  makePrice("JPY",TQDate(2004,6,30),MyMoneyMoney(moJpyPrice3));
  makePrice("JPY",TQDate(2004,7,15),MyMoneyMoney(moJpyPrice4));

  TransactionHelper t1( TQDate(2004,2,20), MyMoneySplit::ActionWithdrawal,MyMoneyMoney(moJpyTransaction), acJpyChecking, acJpyCash, "JPY" );
  TransactionHelper t2( TQDate(2004,3,20), MyMoneySplit::ActionWithdrawal,MyMoneyMoney(moJpyTransaction), acJpyChecking, acJpyCash, "JPY" );
  TransactionHelper t3( TQDate(2004,4,20), MyMoneySplit::ActionWithdrawal,MyMoneyMoney(moJpyTransaction), acJpyChecking, acJpyCash, "JPY" );
  TransactionHelper t4( TQDate(2004,2,20), MyMoneySplit::ActionWithdrawal,MyMoneyMoney(moCanTransaction), acCanChecking, acCanCash, "CAD" );
  TransactionHelper t5( TQDate(2004,3,20), MyMoneySplit::ActionWithdrawal,MyMoneyMoney(moCanTransaction), acCanChecking, acCanCash, "CAD" );
  TransactionHelper t6( TQDate(2004,4,20), MyMoneySplit::ActionWithdrawal,MyMoneyMoney(moCanTransaction), acCanChecking, acCanCash, "CAD" );

#if 0
  TQFile g( "multicurrencykmy.xml" );
  g.open( IO_WriteOnly );
  MyMoneyStorageXML xml;
  IMyMoneyStorageFormat& interface = xml;
  interface.writeFile(&g, dynamic_cast<IMyMoneySerialize*> (MyMoneyFile::instance()->storage()));
  g.close();
#endif

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setDateFilter(TQDate(2004,1,1),TQDate(2005,1,1).addDays(-1));
  filter.setDetailLevel(MyMoneyReport::eDetailAll);
  filter.setConvertCurrency(true);
  filter.setName("Multiple Currency Spending Rerport (with currency conversion)");
  XMLandback(filter);

  PivotTable spending_f( filter );

  writeTabletoCSV(spending_f);

  // test single foreign currency
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Foreign"][acCanCash][eActual][2]==(moCanTransaction*moCanPrice));
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Foreign"][acCanCash][eActual][3]==(moCanTransaction*moCanPrice));
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Foreign"][acCanCash][eActual][4]==(moCanTransaction*moCanPrice));

  // test multiple foreign currencies under a common parent
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Foreign"][acJpyCash][eActual][2]==(moJpyTransaction*moJpyPrice));
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Foreign"][acJpyCash][eActual][3]==(moJpyTransaction*moJpyPrice));
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Foreign"][acJpyCash][eActual][4]==(moJpyTransaction*moJpyPrice));
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Foreign"].m_total[eActual][2]==(moJpyTransaction*moJpyPrice + moCanTransaction*moCanPrice));
  CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Foreign"].m_total[eActual].m_total==(moJpyTransaction*moJpyPrice + moCanTransaction*moCanPrice + moJpyTransaction*moJpyPrice + moCanTransaction*moCanPrice + moJpyTransaction*moJpyPrice + moCanTransaction*moCanPrice));

  // Test the report type where we DO NOT convert the currency
  filter.setConvertCurrency(false);
  filter.setDetailLevel(MyMoneyReport::eDetailAll);
  filter.setName("Multiple Currency Spending Report (WITHOUT currency conversion)");
  XMLandback(filter);
  PivotTable spending_fnc( filter );
  writeTabletoCSV(spending_fnc);

  CPPUNIT_ASSERT(spending_fnc.m_grid["Expense"]["Foreign"][acCanCash][eActual][2]==(moCanTransaction));
  CPPUNIT_ASSERT(spending_fnc.m_grid["Expense"]["Foreign"][acCanCash][eActual][3]==(moCanTransaction));
  CPPUNIT_ASSERT(spending_fnc.m_grid["Expense"]["Foreign"][acCanCash][eActual][4]==(moCanTransaction));
  CPPUNIT_ASSERT(spending_fnc.m_grid["Expense"]["Foreign"][acJpyCash][eActual][2]==(moJpyTransaction));
  CPPUNIT_ASSERT(spending_fnc.m_grid["Expense"]["Foreign"][acJpyCash][eActual][3]==(moJpyTransaction));
  CPPUNIT_ASSERT(spending_fnc.m_grid["Expense"]["Foreign"][acJpyCash][eActual][4]==(moJpyTransaction));

  filter.setConvertCurrency(true);
  filter.clear();
  filter.setName("Multiple currency net worth");
  filter.setRowType(MyMoneyReport::eAssetLiability);
  filter.setDateFilter(TQDate(2004,1,1),TQDate(2005,1,1).addDays(-1));
  XMLandback(filter);
  PivotTable networth_f( filter );
  writeTabletoCSV(networth_f);

  // test single foreign currency
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Canadian Checking"][acCanChecking][eActual][1]==(moCanOpening*moCanPrice));
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Canadian Checking"][acCanChecking][eActual][2]==((moCanOpening-moCanTransaction)*moCanPrice));
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Canadian Checking"][acCanChecking][eActual][3]==((moCanOpening-moCanTransaction-moCanTransaction)*moCanPrice));
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Canadian Checking"][acCanChecking][eActual][4]==((moCanOpening-moCanTransaction-moCanTransaction-moCanTransaction)*moCanPrice));
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Canadian Checking"][acCanChecking][eActual][12]==((moCanOpening-moCanTransaction-moCanTransaction-moCanTransaction)*moCanPrice));

  // test Stable currency price, fluctuating account balance
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Japanese Checking"][acJpyChecking][eActual][1]==(moJpyOpening*moJpyPrice));
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Japanese Checking"][acJpyChecking][eActual][2]==((moJpyOpening-moJpyTransaction)*moJpyPrice));
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Japanese Checking"][acJpyChecking][eActual][3]==((moJpyOpening-moJpyTransaction-moJpyTransaction)*moJpyPrice));
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Japanese Checking"][acJpyChecking][eActual][4]==((moJpyOpening-moJpyTransaction-moJpyTransaction-moJpyTransaction)*moJpyPrice));

  // test Fluctuating currency price, stable account balance
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Japanese Checking"][acJpyChecking][eActual][5]==((moJpyOpening-moJpyTransaction-moJpyTransaction-moJpyTransaction)*moJpyPrice2));
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Japanese Checking"][acJpyChecking][eActual][6]==((moJpyOpening-moJpyTransaction-moJpyTransaction-moJpyTransaction)*moJpyPrice3));
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"]["Japanese Checking"][acJpyChecking][eActual][7]==((moJpyOpening-moJpyTransaction-moJpyTransaction-moJpyTransaction)*moJpyPrice4));

  // test multiple currencies totalled up
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"].m_total[eActual][4]==((moCanOpening-moCanTransaction-moCanTransaction-moCanTransaction)*moCanPrice)+((moJpyOpening-moJpyTransaction-moJpyTransaction-moJpyTransaction)*moJpyPrice));
  CPPUNIT_ASSERT(networth_f.m_grid["Asset"].m_total[eActual][5]==((moCanOpening-moCanTransaction-moCanTransaction-moCanTransaction)*moCanPrice)+((moJpyOpening-moJpyTransaction-moJpyTransaction-moJpyTransaction)*moJpyPrice2)+moCheckingOpen);

}

void PivotTableTest::testAdvancedFilter()
{
  // test more advanced filtering capabilities

  // amount
  {
    TransactionHelper t1( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
    TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );

    MyMoneyReport filter;
    filter.setRowType( MyMoneyReport::eExpenseIncome );
    filter.setDateFilter(TQDate(2004,1,1),TQDate(2005,1,1).addDays(-1));
    filter.setAmountFilter(moChild,moChild);
    XMLandback(filter);
    PivotTable spending_f( filter );
    CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==-moChild);
  }

  // payee (specific)
  {
    TransactionHelper t1( TQDate(2004,10,31), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
    TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
    TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );
    TransactionHelper t4( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moThomas, acCredit, acParent, TQString(), "Thomas Baumgart" );

    MyMoneyReport filter;
    filter.setRowType( MyMoneyReport::eExpenseIncome );
    filter.setDateFilter(TQDate(2004,1,1),TQDate(2005,1,1).addDays(-1));
    filter.addPayee(MyMoneyFile::instance()->payeeByName("Thomas Baumgart").id());
    filter.setName("Spending with Payee Filter");
    XMLandback(filter);
    PivotTable spending_f( filter );
    writeTabletoHTML(spending_f,"Spending with Payee Filter.html");

    CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Parent"][acParent][eActual][11]==moThomas);
    CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==-moThomas);
  }
  // payee (no payee)
  {
    TransactionHelper t1( TQDate(2004,10,31), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
    TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
    TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );
    TransactionHelper t4( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moNoPayee, acCredit, acParent, TQString(), TQString() );

    MyMoneyReport filter;
    filter.setRowType( MyMoneyReport::eExpenseIncome );
    filter.setDateFilter(TQDate(2004,1,1),TQDate(2005,1,1).addDays(-1));
    filter.addPayee(TQString());
    XMLandback(filter);
    PivotTable spending_f( filter );
    CPPUNIT_ASSERT(spending_f.m_grid["Expense"]["Parent"][acParent][eActual][11]==moNoPayee);
    CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==-moNoPayee);
  }

  // text
  {
    TransactionHelper t1( TQDate(2004,10,31), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
    TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
    TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );
    TransactionHelper t4( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moThomas, acCredit, acParent, TQString(), "Thomas Baumgart" );

    MyMoneyReport filter;
    filter.setRowType( MyMoneyReport::eExpenseIncome );
    filter.setDateFilter(TQDate(2004,1,1),TQDate(2005,1,1).addDays(-1));
    filter.setTextFilter(TQRegExp("Thomas"));
    XMLandback(filter);
    PivotTable spending_f( filter );
  }

  // type (payment, deposit, transfer)
  {
    TransactionHelper t1( TQDate(2004,1,1), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
    TransactionHelper t2( TQDate(2004,2,1), MyMoneySplit::ActionDeposit, -moParent1, acCredit, acParent );
    TransactionHelper t3( TQDate(2004,11,1), MyMoneySplit::ActionTransfer, moChild, acCredit, acChecking );

    MyMoneyReport filter;
    filter.setRowType( MyMoneyReport::eExpenseIncome );
    filter.addType(MyMoneyTransactionFilter::payments);
    XMLandback(filter);
    PivotTable spending_f( filter );

    CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total == -moSolo);

    filter.clear();
    filter.addType(MyMoneyTransactionFilter::deposits);
    XMLandback(filter);
    PivotTable spending_f2( filter );

    CPPUNIT_ASSERT(spending_f2.m_grid.m_total[eActual].m_total == moParent1);

    filter.clear();
    filter.addType(MyMoneyTransactionFilter::transfers);
    XMLandback(filter);
    PivotTable spending_f3( filter );

    CPPUNIT_ASSERT(spending_f3.m_grid.m_total[eActual].m_total == moZero);

    filter.setRowType(MyMoneyReport::eAssetLiability);
    filter.setDateFilter( TQDate(2004,1,1), TQDate(2004,12,31) );
    XMLandback(filter);
    PivotTable networth_f4( filter );

    CPPUNIT_ASSERT(networth_f4.m_grid["Asset"].m_total[eActual][11] == moCheckingOpen + moChild);
    CPPUNIT_ASSERT(networth_f4.m_grid["Liability"].m_total[eActual][11] == - moCreditOpen + moChild);
    CPPUNIT_ASSERT(networth_f4.m_grid.m_total[eActual][10] == moCheckingOpen + moCreditOpen);
    CPPUNIT_ASSERT(networth_f4.m_grid.m_total[eActual][11] == moCheckingOpen + moCreditOpen);
  }

  // state (reconciled, cleared, not)
  {
    TransactionHelper t1( TQDate(2004,1,1), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
    TransactionHelper t2( TQDate(2004,2,1), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
    TransactionHelper t3( TQDate(2004,3,1), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );
    TransactionHelper t4( TQDate(2004,4,1), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );

    TQValueList<MyMoneySplit> splits = t1.splits();
    splits[0].setReconcileFlag(MyMoneySplit::Cleared);
    splits[1].setReconcileFlag(MyMoneySplit::Cleared);
    t1.modifySplit(splits[0]);
    t1.modifySplit(splits[1]);
    t1.update();

    splits.clear();
    splits = t2.splits();
    splits[0].setReconcileFlag(MyMoneySplit::Reconciled);
    splits[1].setReconcileFlag(MyMoneySplit::Reconciled);
    t2.modifySplit(splits[0]);
    t2.modifySplit(splits[1]);
    t2.update();

    MyMoneyReport filter;
    filter.setRowType( MyMoneyReport::eExpenseIncome );
    filter.setDateFilter(TQDate(2004,1,1),TQDate(2005,1,1).addDays(-1));
    filter.addState(MyMoneyTransactionFilter::cleared);
    XMLandback(filter);
    PivotTable spending_f( filter );

    CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==-moSolo);

    filter.addState(MyMoneyTransactionFilter::reconciled);
    XMLandback(filter);
    PivotTable spending_f2( filter );

    CPPUNIT_ASSERT(spending_f2.m_grid.m_total[eActual].m_total==-moSolo-moParent1);

    filter.clear();
    filter.addState(MyMoneyTransactionFilter::notReconciled);
    XMLandback(filter);
    PivotTable spending_f3( filter );

    CPPUNIT_ASSERT(spending_f3.m_grid.m_total[eActual].m_total==-moChild-moParent2);
  }

  // number
  {
    TransactionHelper t1( TQDate(2004,10,31), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
    TransactionHelper t2( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
    TransactionHelper t3( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );
    TransactionHelper t4( TQDate(2004,11,7), MyMoneySplit::ActionWithdrawal, moChild, acCredit, acChild );

    TQValueList<MyMoneySplit> splits = t1.splits();
    splits[0].setNumber("1");
    splits[1].setNumber("1");
    t1.modifySplit(splits[0]);
    t1.modifySplit(splits[1]);
    t1.update();

    splits.clear();
    splits = t2.splits();
    splits[0].setNumber("2");
    splits[1].setNumber("2");
    t2.modifySplit(splits[0]);
    t2.modifySplit(splits[1]);
    t2.update();

    splits.clear();
    splits = t3.splits();
    splits[0].setNumber("3");
    splits[1].setNumber("3");
    t3.modifySplit(splits[0]);
    t3.modifySplit(splits[1]);
    t3.update();

    splits.clear();
    splits = t2.splits();
    splits[0].setNumber("4");
    splits[1].setNumber("4");
    t4.modifySplit(splits[0]);
    t4.modifySplit(splits[1]);
    t4.update();

    MyMoneyReport filter;
    filter.setRowType( MyMoneyReport::eExpenseIncome );
    filter.setDateFilter(TQDate(2004,1,1),TQDate(2005,1,1).addDays(-1));
    filter.setNumberFilter("1","3");
    XMLandback(filter);
    PivotTable spending_f( filter );
    CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==-moSolo-moParent1-moParent2);
  }

  // blank dates
  {
    TransactionHelper t1y1( TQDate(2003,10,1), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
    TransactionHelper t2y1( TQDate(2003,11,1), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
    TransactionHelper t3y1( TQDate(2003,12,1), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );

    TransactionHelper t1y2( TQDate(2004,4,1), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
    TransactionHelper t2y2( TQDate(2004,5,1), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
    TransactionHelper t3y2( TQDate(2004,6,1), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );

    TransactionHelper t1y3( TQDate(2005,1,1), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
    TransactionHelper t2y3( TQDate(2005,5,1), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
    TransactionHelper t3y3( TQDate(2005,9,1), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );

    MyMoneyReport filter;
    filter.setRowType( MyMoneyReport::eExpenseIncome );
    filter.setDateFilter(TQDate(),TQDate(2004,7,1));
    XMLandback(filter);
    PivotTable spending_f( filter );
    CPPUNIT_ASSERT(spending_f.m_grid.m_total[eActual].m_total==-moSolo-moParent1-moParent2-moSolo-moParent1-moParent2);

    filter.clear();
    XMLandback(filter);
    PivotTable spending_f2( filter );
    CPPUNIT_ASSERT(spending_f2.m_grid.m_total[eActual].m_total==-moSolo-moParent1-moParent2-moSolo-moParent1-moParent2-moSolo-moParent1-moParent2);

  }

}

void PivotTableTest::testColumnType()
{
  // test column type values of other than 'month'

  TransactionHelper t1q1( TQDate(2004,1,1), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2q1( TQDate(2004,2,1), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3q1( TQDate(2004,3,1), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );

  TransactionHelper t1q2( TQDate(2004,4,1), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2q2( TQDate(2004,5,1), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3q2( TQDate(2004,6,1), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );

  TransactionHelper t1y2( TQDate(2005,1,1), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2y2( TQDate(2005,5,1), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3y2( TQDate(2005,9,1), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );

  MyMoneyReport filter;
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setDateFilter(TQDate(2003,12,31),TQDate(2005,12,31));
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setColumnType(MyMoneyReport::eBiMonths);
  XMLandback(filter);
  PivotTable spending_b( filter );

  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][1] == moZero);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][2] == -moParent1-moSolo);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][3] == -moParent2-moSolo);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][4] == -moParent);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][5] == moZero);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][6] == moZero);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][7] == moZero);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][8] == -moSolo);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][9] == moZero);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][10] == -moParent1);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][11] == moZero);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][12] == -moParent2);
  CPPUNIT_ASSERT(spending_b.m_grid.m_total[eActual][13] == moZero);

  filter.setColumnType(MyMoneyReport::eQuarters);
  XMLandback(filter);
  PivotTable spending_q( filter );

  CPPUNIT_ASSERT(spending_q.m_grid.m_total[eActual][1] == moZero);
  CPPUNIT_ASSERT(spending_q.m_grid.m_total[eActual][2] == -moSolo-moParent);
  CPPUNIT_ASSERT(spending_q.m_grid.m_total[eActual][3] == -moSolo-moParent);
  CPPUNIT_ASSERT(spending_q.m_grid.m_total[eActual][4] == moZero);
  CPPUNIT_ASSERT(spending_q.m_grid.m_total[eActual][5] == moZero);
  CPPUNIT_ASSERT(spending_q.m_grid.m_total[eActual][6] == -moSolo);
  CPPUNIT_ASSERT(spending_q.m_grid.m_total[eActual][7] == -moParent1);
  CPPUNIT_ASSERT(spending_q.m_grid.m_total[eActual][8] == -moParent2);
  CPPUNIT_ASSERT(spending_q.m_grid.m_total[eActual][9] == moZero);

  filter.setRowType( MyMoneyReport::eAssetLiability );
  filter.setName( "Net Worth by Quarter" );
  XMLandback(filter);
  PivotTable networth_q( filter );
  writeTabletoHTML( networth_q, "Net Worth by Quarter.html" );

  CPPUNIT_ASSERT(networth_q.m_grid.m_total[eActual][1] == moZero);
  CPPUNIT_ASSERT(networth_q.m_grid.m_total[eActual][2] == -moSolo-moParent);
  CPPUNIT_ASSERT(networth_q.m_grid.m_total[eActual][3] == -moSolo-moParent-moSolo-moParent+moCheckingOpen);
  CPPUNIT_ASSERT(networth_q.m_grid.m_total[eActual][4] == -moSolo-moParent-moSolo-moParent+moCheckingOpen+moCreditOpen);
  CPPUNIT_ASSERT(networth_q.m_grid.m_total[eActual][5] == -moSolo-moParent-moSolo-moParent+moCheckingOpen+moCreditOpen);
  CPPUNIT_ASSERT(networth_q.m_grid.m_total[eActual][6] == -moSolo-moSolo-moParent-moSolo-moParent+moCheckingOpen+moCreditOpen);
  CPPUNIT_ASSERT(networth_q.m_grid.m_total[eActual][7] == -moParent1-moSolo-moSolo-moParent-moSolo-moParent+moCheckingOpen+moCreditOpen);
  CPPUNIT_ASSERT(networth_q.m_grid.m_total[eActual][8] == -moParent2-moParent1-moSolo-moSolo-moParent-moSolo-moParent+moCheckingOpen+moCreditOpen);
  CPPUNIT_ASSERT(networth_q.m_grid.m_total[eActual][9] == -moParent2-moParent1-moSolo-moSolo-moParent-moSolo-moParent+moCheckingOpen+moCreditOpen);

  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setColumnType(MyMoneyReport::eYears);
  XMLandback(filter);
  PivotTable spending_y( filter );

  CPPUNIT_ASSERT(spending_y.m_grid.m_total[eActual][1] == moZero);
  CPPUNIT_ASSERT(spending_y.m_grid.m_total[eActual][2] == -moSolo-moParent-moSolo-moParent);
  CPPUNIT_ASSERT(spending_y.m_grid.m_total[eActual][3] == -moSolo-moParent);
  CPPUNIT_ASSERT(spending_y.m_grid.m_total[eActual].m_total == -moSolo-moParent-moSolo-moParent-moSolo-moParent);

  filter.setRowType( MyMoneyReport::eAssetLiability );
  XMLandback(filter);
  PivotTable networth_y( filter );

  CPPUNIT_ASSERT(networth_y.m_grid.m_total[eActual][1] == moZero);
  CPPUNIT_ASSERT(networth_y.m_grid.m_total[eActual][2] == -moSolo-moParent-moSolo-moParent+moCheckingOpen+moCreditOpen);
  CPPUNIT_ASSERT(networth_y.m_grid.m_total[eActual][3] == -moSolo-moParent-moSolo-moParent-moSolo-moParent+moCheckingOpen+moCreditOpen);

  // Test days-based reports

  TransactionHelper t1d1( TQDate(2004,7,1), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2d1( TQDate(2004,7,1), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3d1( TQDate(2004,7,5), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );

  TransactionHelper t1d2( TQDate(2004,7,14), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2d2( TQDate(2004,7,15), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3d2( TQDate(2004,7,20), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );

  TransactionHelper t1d3( TQDate(2004,8,2), MyMoneySplit::ActionWithdrawal, moSolo, acChecking, acSolo );
  TransactionHelper t2d3( TQDate(2004,8,3), MyMoneySplit::ActionWithdrawal, moParent1, acCredit, acParent );
  TransactionHelper t3d3( TQDate(2004,8,4), MyMoneySplit::ActionWithdrawal, moParent2, acCredit, acParent );

  filter.setDateFilter(TQDate(2004,7,2),TQDate(2004,7,14));
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setColumnType(MyMoneyReport::eMonths);
  filter.setColumnsAreDays(true);

  XMLandback(filter);
  PivotTable spending_days( filter );
  writeTabletoHTML(spending_days,"Spending by Days.html");

  CPPUNIT_ASSERT(spending_days.m_grid.m_total[eActual][4] == -moParent2);
  CPPUNIT_ASSERT(spending_days.m_grid.m_total[eActual][13] == -moSolo);
  CPPUNIT_ASSERT(spending_days.m_grid.m_total[eActual].m_total == -moSolo-moParent2);

  unsigned save_dayweekstart = TDEGlobal::locale()->weekStartDay();
  TDEGlobal::locale()->setWeekStartDay(2);

  filter.setDateFilter(TQDate(2004,7,2),TQDate(2004,8,1));
  filter.setRowType( MyMoneyReport::eExpenseIncome );
  filter.setColumnType(static_cast<MyMoneyReport::EColumnType>(7));
  filter.setColumnsAreDays(true);

  XMLandback(filter);
  PivotTable spending_weeks( filter );
  writeTabletoHTML(spending_weeks,"Spending by Weeks.html");

  TDEGlobal::locale()->setWeekStartDay(save_dayweekstart);

  CPPUNIT_ASSERT(spending_weeks.m_grid.m_total[eActual][0] == moZero);
  CPPUNIT_ASSERT(spending_weeks.m_grid.m_total[eActual][1] == -moParent2);
  CPPUNIT_ASSERT(spending_weeks.m_grid.m_total[eActual][2] == moZero);
  CPPUNIT_ASSERT(spending_weeks.m_grid.m_total[eActual][3] == -moSolo-moParent1);
  CPPUNIT_ASSERT(spending_weeks.m_grid.m_total[eActual][4] == -moParent2);
  CPPUNIT_ASSERT(spending_weeks.m_grid.m_total[eActual][5] == moZero);
  CPPUNIT_ASSERT(spending_weeks.m_grid.m_total[eActual].m_total == -moSolo-moParent-moParent2);


}

void PivotTableTest::testInvestment(void)
{
  try
  {
  // Equities
  eqStock1 = makeEquity("Stock1","STK1");
  eqStock2 = makeEquity("Stock2","STK2");

  // Accounts
  acInvestment = makeAccount("Investment",MyMoneyAccount::Investment,moZero,TQDate(2004,1,1),acAsset);
  acStock1 = makeAccount("Stock 1",MyMoneyAccount::Stock,moZero,TQDate(2004,1,1),acInvestment,eqStock1);
  acStock2 = makeAccount("Stock 2",MyMoneyAccount::Stock,moZero,TQDate(2004,1,1),acInvestment,eqStock2);
  acDividends = makeAccount("Dividends",MyMoneyAccount::Income,moZero,TQDate(2004,1,1),acIncome);

  // Transactions
  //                         Date             Action                              Shares  Price   Stock     Asset       Income
  InvTransactionHelper s1b1( TQDate(2004,2,1), MyMoneySplit::ActionBuyShares,      1000.00, 100.00, acStock1, acChecking, TQString() );
  InvTransactionHelper s1b2( TQDate(2004,3,1), MyMoneySplit::ActionBuyShares,      1000.00, 110.00, acStock1, acChecking, TQString() );
  InvTransactionHelper s1s1( TQDate(2004,4,1), MyMoneySplit::ActionBuyShares,      -200.00, 120.00, acStock1, acChecking, TQString() );
  InvTransactionHelper s1s2( TQDate(2004,5,1), MyMoneySplit::ActionBuyShares,      -200.00, 100.00, acStock1, acChecking, TQString() );
  InvTransactionHelper s1r1( TQDate(2004,6,1), MyMoneySplit::ActionReinvestDividend, 50.00, 100.00, acStock1, TQString(), acDividends );
  InvTransactionHelper s1r2( TQDate(2004,7,1), MyMoneySplit::ActionReinvestDividend, 50.00,  80.00, acStock1, TQString(), acDividends );
  InvTransactionHelper s1c1( TQDate(2004,8,1), MyMoneySplit::ActionDividend,         10.00, 100.00, acStock1, acChecking, acDividends );
  InvTransactionHelper s1c2( TQDate(2004,9,1), MyMoneySplit::ActionDividend,         10.00, 120.00, acStock1, acChecking, acDividends );

  makeEquityPrice( eqStock1, TQDate(2004,10,1), 100.00 );

  //
  // Net Worth Report (with investments)
  //

  MyMoneyReport networth_r;
  networth_r.setRowType( MyMoneyReport::eAssetLiability );
  networth_r.setDateFilter(TQDate(2004,1,1),TQDate(2004,12,31).addDays(-1));
  XMLandback(networth_r);
  PivotTable networth(networth_r);

  networth.dump("networth_i.html");

  CPPUNIT_ASSERT(networth.m_grid["Asset"]["Investment"].m_total[eActual][1]==moZero);
  // 1000 shares @ $100.00
  CPPUNIT_ASSERT(networth.m_grid["Asset"]["Investment"].m_total[eActual][2]==MyMoneyMoney(100000.0));
  // 2000 shares @ $110.00
  CPPUNIT_ASSERT(networth.m_grid["Asset"]["Investment"].m_total[eActual][3]==MyMoneyMoney(220000.0));
  // 1800 shares @ $120.00
  CPPUNIT_ASSERT(networth.m_grid["Asset"]["Investment"].m_total[eActual][4]==MyMoneyMoney(216000.0));
  // 1600 shares @ $100.00
  CPPUNIT_ASSERT(networth.m_grid["Asset"]["Investment"].m_total[eActual][5]==MyMoneyMoney(160000.0));
  // 1650 shares @ $100.00
  CPPUNIT_ASSERT(networth.m_grid["Asset"]["Investment"].m_total[eActual][6]==MyMoneyMoney(165000.0));
  // 1700 shares @ $ 80.00
  CPPUNIT_ASSERT(networth.m_grid["Asset"]["Investment"].m_total[eActual][7]==MyMoneyMoney(136000.0));
  // 1700 shares @ $100.00
  CPPUNIT_ASSERT(networth.m_grid["Asset"]["Investment"].m_total[eActual][8]==MyMoneyMoney(170000.0));
  // 1700 shares @ $120.00
  CPPUNIT_ASSERT(networth.m_grid["Asset"]["Investment"].m_total[eActual][9]==MyMoneyMoney(204000.0));
  // 1700 shares @ $100.00
  CPPUNIT_ASSERT(networth.m_grid["Asset"]["Investment"].m_total[eActual][10]==MyMoneyMoney(170000.0));

#if 0
  // Dump file & reports
  TQFile g( "investmentkmy.xml" );
  g.open( IO_WriteOnly );
  MyMoneyStorageXML xml;
  IMyMoneyStorageFormat& interface = xml;
  interface.writeFile(&g, dynamic_cast<IMyMoneySerialize*> (MyMoneyFile::instance()->storage()));
  g.close();

  invtran.dump("invtran.html","<html><head></head><body>%1</body></html>");
  invhold.dump("invhold.html","<html><head></head><body>%1</body></html>");
#endif

  }
  catch(MyMoneyException *e)
  {
    CPPUNIT_FAIL(e->what().local8Bit().data());
    delete e;
  }
}

void PivotTableTest::testBudget(void)
{

  // 1. Budget on A, transations on A
  {
    BudgetHelper budget;
    budget += BudgetEntryHelper( TQDate(2006,1,1), acSolo, false, 100.0 );

    MyMoneyReport report(MyMoneyReport::eBudgetActual,
                         MyMoneyReport::eMonths,
                         MyMoneyTransactionFilter::yearToDate,
                         MyMoneyReport::eDetailTop,
                         "Yearly Budgeted vs. Actual","Default Report");
    PivotTable table(report);
  }

  // 2. Budget on B, not applying to sub accounts, transactions on B and B:1
  {
    BudgetHelper budget;
    budget += BudgetEntryHelper( TQDate(2006,1,1), acParent, false, 100.0 );
    MyMoneyReport report(MyMoneyReport::eBudgetActual,
                         MyMoneyReport::eMonths,
                         MyMoneyTransactionFilter::yearToDate,
                         MyMoneyReport::eDetailTop,
                         "Yearly Budgeted vs. Actual","Default Report");
    PivotTable table(report);
  }

  //  - Both B and B:1 totals should show up
  //  - B actuals compare against B budget
  //  - B:1 actuals compare against 0

  // 3. Budget on C, applying to sub accounts, transactions on C and C:1 and C:1:a
  {
    BudgetHelper budget;
    budget += BudgetEntryHelper( TQDate(2006,1,1), acParent, true, 100.0 );
    MyMoneyReport report(MyMoneyReport::eBudgetActual,
                         MyMoneyReport::eMonths,
                         MyMoneyTransactionFilter::yearToDate,
                         MyMoneyReport::eDetailTop ,
                         "Yearly Budgeted vs. Actual","Default Report");
    PivotTable table(report);
  }

  //  - Only C totals show up, not C:1 or C:1:a totals
  //  - C + C:1 totals compare against C budget

  // 4. Budget on D, not applying to sub accounts, budget on D:1 not applying, budget on D:2 applying.  Transactions on D, D:1, D:2, D:2:a, D:2:b
  {
    BudgetHelper budget;
    budget += BudgetEntryHelper( TQDate(2006,1,1), acParent, false, 100.0 );
    budget += BudgetEntryHelper( TQDate(2006,1,1), acChild, false, 100.0 );
    budget += BudgetEntryHelper( TQDate(2006,1,1), acSecondChild, true, 100.0 );
    MyMoneyReport report(MyMoneyReport::eBudgetActual,
                         MyMoneyReport::eMonths,
                         MyMoneyTransactionFilter::yearToDate,
                         MyMoneyReport::eDetailTop,
                         "Yearly Budgeted vs. Actual","Default Report");
    PivotTable table(report);
  }

  //  - Totals for D, D:1, D:2 show up.  D:2:a and D:2:b do not
  //  - D actuals (only) compare against D budget
  //  - Ditto for D:1
  //  - D:2 acutals and children compare against D:2 budget

  // 5. Budget on E, no transactions on E
  {
    BudgetHelper budget;
    budget += BudgetEntryHelper( TQDate(2006,1,1), acSolo, false, 100.0 );
    MyMoneyReport report(MyMoneyReport::eBudgetActual,
                         MyMoneyReport::eMonths,
                         MyMoneyTransactionFilter::yearToDate,
                         MyMoneyReport::eDetailTop,
                         "Yearly Budgeted vs. Actual","Default Report");
    PivotTable table(report);
  }
}
