/*
 * Copyright 2002-2018  Thomas Baumgart <tbaumgart@kde.org>
 * Copyright 2004       Ace Jones <acejones@users.sourceforge.net>
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mymoneyfile-test.h"
#include <iostream>

#include <memory>
#include <QFile>
#include <QDataStream>
#include <QList>
#include <QtTest>

#include "mymoneystoragemgr_p.h"
#include "mymoneytestutils.h"
#include "mymoneymoney.h"
#include "mymoneyinstitution.h"
#include "mymoneyaccount.h"
#include "mymoneysecurity.h"
#include "mymoneytransaction.h"
#include "mymoneytransactionfilter.h"
#include "mymoneysplit.h"
#include "mymoneyprice.h"
#include "mymoneypayee.h"
#include "mymoneyenums.h"
#include "onlinejob.h"

#include "payeeidentifier/ibanbic/ibanbic.h"
#include "payeeidentifiertyped.h"

QTEST_GUILESS_MAIN(MyMoneyFileTest)

MyMoneyFileTest::MyMoneyFileTest() :
  m(nullptr),
  storage(nullptr)
{
}

void MyMoneyFileTest::objectAdded(eMyMoney::File::Object type, const QString& id)
{
  Q_UNUSED(type);
  m_objectsAdded += id;
}

void MyMoneyFileTest::objectRemoved(eMyMoney::File::Object type, const QString& id)
{
  Q_UNUSED(type);
  m_objectsRemoved += id;
}

void MyMoneyFileTest::objectModified(eMyMoney::File::Object type, const QString& id)
{
  Q_UNUSED(type);
  m_objectsModified += id;
}

void MyMoneyFileTest::clearObjectLists()
{
  m_objectsAdded.clear();
  m_objectsModified.clear();
  m_objectsRemoved.clear();
  m_balanceChanged.clear();
  m_valueChanged.clear();
}

void MyMoneyFileTest::balanceChanged(const MyMoneyAccount& account)
{
  m_balanceChanged += account.id();
}

void MyMoneyFileTest::valueChanged(const MyMoneyAccount& account)
{
  m_valueChanged += account.id();
}

// this method will be called once at the beginning of the test
void MyMoneyFileTest::initTestCase()
{
  m = MyMoneyFile::instance();

  connect(m, &MyMoneyFile::objectAdded, this, &MyMoneyFileTest::objectAdded);
  connect(m, &MyMoneyFile::objectRemoved, this, &MyMoneyFileTest::objectRemoved);
  connect(m, &MyMoneyFile::objectModified, this, &MyMoneyFileTest::objectModified);
  connect(m, &MyMoneyFile::balanceChanged, this, &MyMoneyFileTest::balanceChanged);
  connect(m, &MyMoneyFile::valueChanged, this, &MyMoneyFileTest::valueChanged);
}

// this method will be called before each testfunction
void MyMoneyFileTest::init()
{
  storage = new MyMoneyStorageMgr;
  m->attachStorage(storage);
  clearObjectLists();
}

// this method will be called after each testfunction
void MyMoneyFileTest::cleanup()
{
  m->detachStorage(storage);
  delete storage;
}

void MyMoneyFileTest::testEmptyConstructor()
{
  MyMoneyPayee user = m->user();

  QCOMPARE(user.name(), QString());
  QCOMPARE(user.address(), QString());
  QCOMPARE(user.city(), QString());
  QCOMPARE(user.state(), QString());
  QCOMPARE(user.postcode(), QString());
  QCOMPARE(user.telephone(), QString());
  QCOMPARE(user.email(), QString());

  QCOMPARE(m->institutionCount(), static_cast<unsigned>(0));
  QCOMPARE(m->dirty(), false);
  QCOMPARE(m->accountCount(), static_cast<unsigned>(5));
}

void MyMoneyFileTest::testAddOneInstitution()
{
  MyMoneyInstitution institution;

  institution.setName("institution1");
  institution.setTown("town");
  institution.setStreet("street");
  institution.setPostcode("postcode");
  institution.setTelephone("telephone");
  institution.setManager("manager");
  institution.setSortcode("sortcode");

  // MyMoneyInstitution institution_file("", institution);
  MyMoneyInstitution institution_id("I000002", institution);
  MyMoneyInstitution institution_noname(institution);
  institution_noname.setName(QString());

  QCOMPARE(m->institutionCount(), static_cast<unsigned>(0));
  storage->d_func()->m_dirty = false;

  clearObjectLists();
  MyMoneyFileTransaction ft;
  try {
    m->addInstitution(institution);
    ft.commit();
    QCOMPARE(institution.id(), QLatin1String("I000001"));
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(1));
    QCOMPARE(m->dirty(), true);

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 1);
    QCOMPARE(m_objectsModified.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QCOMPARE(m_objectsAdded[0], QLatin1String("I000001"));
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }

  clearObjectLists();
  ft.restart();
  try {
    m->addInstitution(institution_id);
    QFAIL("Missing expected exception");
  } catch (const MyMoneyException &) {
    ft.commit();
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(1));
  }

  ft.restart();
  try {
    m->addInstitution(institution_noname);
    QFAIL("Missing expected exception");
  } catch (const MyMoneyException &) {
    ft.commit();
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(1));
  }
  QCOMPARE(m_objectsRemoved.count(), 0);
  QCOMPARE(m_objectsAdded.count(), 0);
  QCOMPARE(m_objectsModified.count(), 0);
  QCOMPARE(m_balanceChanged.count(), 0);
  QCOMPARE(m_valueChanged.count(), 0);
}

void MyMoneyFileTest::testAddTwoInstitutions()
{
  testAddOneInstitution();
  MyMoneyInstitution institution;
  institution.setName("institution2");
  institution.setTown("town");
  institution.setStreet("street");
  institution.setPostcode("postcode");
  institution.setTelephone("telephone");
  institution.setManager("manager");
  institution.setSortcode("sortcode");

  storage->d_func()->m_dirty = false;
  MyMoneyFileTransaction ft;
  try {
    m->addInstitution(institution);
    ft.commit();

    QCOMPARE(institution.id(), QLatin1String("I000002"));
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(2));
    QCOMPARE(m->dirty(), true);
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }

  storage->d_func()->m_dirty = false;

  try {
    institution = m->institution("I000001");
    QCOMPARE(institution.id(), QLatin1String("I000001"));
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(2));
    QCOMPARE(m->dirty(), false);

    institution = m->institution("I000002");
    QCOMPARE(institution.id(), QLatin1String("I000002"));
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(2));
    QCOMPARE(m->dirty(), false);
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }
}

void MyMoneyFileTest::testRemoveInstitution()
{
  testAddTwoInstitutions();

  MyMoneyInstitution i;

  QCOMPARE(m->institutionCount(), static_cast<unsigned>(2));

  i = m->institution("I000001");
  QCOMPARE(i.id(), QLatin1String("I000001"));
  QCOMPARE(i.accountCount(), static_cast<unsigned>(0));

  clearObjectLists();

  storage->d_func()->m_dirty = false;
  MyMoneyFileTransaction ft;
  try {
    m->removeInstitution(i);
    QCOMPARE(m_objectsRemoved.count(), 0);
    ft.commit();
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(1));
    QCOMPARE(m->dirty(), true);
    QCOMPARE(m_objectsRemoved.count(), 1);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_objectsModified.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QCOMPARE(m_objectsRemoved[0], QLatin1String("I000001"));
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }

  storage->d_func()->m_dirty = false;

  try {
    m->institution("I000001");
    QFAIL("Missing expected exception");
  } catch (const MyMoneyException &) {
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(1));
    QCOMPARE(m->dirty(), false);
  }

  clearObjectLists();
  ft.restart();
  try {
    m->removeInstitution(i);
    QFAIL("Missing expected exception");
  } catch (const MyMoneyException &) {
    ft.commit();
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(1));
    QCOMPARE(m->dirty(), false);
    QCOMPARE(m_objectsRemoved.count(), 0);
  }
}

void MyMoneyFileTest::testInstitutionRetrieval()
{

  testAddOneInstitution();

  storage->d_func()->m_dirty = false;

  MyMoneyInstitution institution;

  QCOMPARE(m->institutionCount(), static_cast<unsigned>(1));

  try {
    institution = m->institution("I000001");
    QCOMPARE(institution.id(), QLatin1String("I000001"));
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(1));
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }

  try {
    institution = m->institution("I000002");
    QFAIL("Missing expected exception");
  } catch (const MyMoneyException &) {
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(1));
  }

  QCOMPARE(m->dirty(), false);
}

void MyMoneyFileTest::testInstitutionListRetrieval()
{
  QList<MyMoneyInstitution> list;

  storage->d_func()->m_dirty = false;
  list = m->institutionList();
  QCOMPARE(m->dirty(), false);
  QCOMPARE(list.count(), 0);

  testAddTwoInstitutions();

  storage->d_func()->m_dirty = false;
  list = m->institutionList();
  QCOMPARE(m->dirty(), false);
  QCOMPARE(list.count(), 2);

  QList<MyMoneyInstitution>::ConstIterator it;
  it = list.constBegin();

  QVERIFY((*it).name() == "institution1" || (*it).name() == "institution2");
  ++it;
  QVERIFY((*it).name() == "institution2"  || (*it).name() == "institution1");
  ++it;
  QVERIFY(it == list.constEnd());
}

void MyMoneyFileTest::testInstitutionModify()
{
  testAddTwoInstitutions();
  MyMoneyInstitution institution;

  institution = m->institution("I000001");
  institution.setStreet("new street");
  institution.setTown("new town");
  institution.setPostcode("new postcode");
  institution.setTelephone("new telephone");
  institution.setManager("new manager");
  institution.setName("new name");
  institution.setSortcode("new sortcode");

  storage->d_func()->m_dirty = false;

  clearObjectLists();
  MyMoneyFileTransaction ft;
  try {
    m->modifyInstitution(institution);
    ft.commit();
    QCOMPARE(institution.id(), QLatin1String("I000001"));
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(2));
    QCOMPARE(m->dirty(), true);

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QCOMPARE(m_objectsModified[0], QLatin1String("I000001"));
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }

  MyMoneyInstitution newInstitution;
  newInstitution = m->institution("I000001");

  QCOMPARE(newInstitution.id(), QLatin1String("I000001"));
  QCOMPARE(newInstitution.street(), QLatin1String("new street"));
  QCOMPARE(newInstitution.town(), QLatin1String("new town"));
  QCOMPARE(newInstitution.postcode(), QLatin1String("new postcode"));
  QCOMPARE(newInstitution.telephone(), QLatin1String("new telephone"));
  QCOMPARE(newInstitution.manager(), QLatin1String("new manager"));
  QCOMPARE(newInstitution.name(), QLatin1String("new name"));
  QCOMPARE(newInstitution.sortcode(), QLatin1String("new sortcode"));

  storage->d_func()->m_dirty = false;

  ft.restart();
  MyMoneyInstitution failInstitution2("I000003", newInstitution);
  try {
    m->modifyInstitution(failInstitution2);
    QFAIL("Exception expected");
  } catch (const MyMoneyException &) {
    ft.commit();
    QCOMPARE(failInstitution2.id(), QLatin1String("I000003"));
    QCOMPARE(m->institutionCount(), static_cast<unsigned>(2));
    QCOMPARE(m->dirty(), false);
  }
}

void MyMoneyFileTest::testSetFunctions()
{
  MyMoneyPayee user = m->user();

  QCOMPARE(user.name(), QString());
  QCOMPARE(user.address(), QString());
  QCOMPARE(user.city(), QString());
  QCOMPARE(user.state(), QString());
  QCOMPARE(user.postcode(), QString());
  QCOMPARE(user.telephone(), QString());
  QCOMPARE(user.email(), QString());

  MyMoneyFileTransaction ft;
  storage->d_func()->m_dirty = false;
  user.setName("Name");
  m->setUser(user);
  QCOMPARE(m->dirty(), true);
  storage->d_func()->m_dirty = false;
  user.setAddress("Street");
  m->setUser(user);
  QCOMPARE(m->dirty(), true);
  storage->d_func()->m_dirty = false;
  user.setCity("Town");
  m->setUser(user);
  QCOMPARE(m->dirty(), true);
  storage->d_func()->m_dirty = false;
  user.setState("County");
  m->setUser(user);
  QCOMPARE(m->dirty(), true);
  storage->d_func()->m_dirty = false;
  user.setPostcode("Postcode");
  m->setUser(user);
  QCOMPARE(m->dirty(), true);
  storage->d_func()->m_dirty = false;
  user.setTelephone("Telephone");
  m->setUser(user);
  QCOMPARE(m->dirty(), true);
  storage->d_func()->m_dirty = false;
  user.setEmail("Email");
  m->setUser(user);
  QCOMPARE(m->dirty(), true);
  storage->d_func()->m_dirty = false;

  ft.commit();
  user = m->user();
  QCOMPARE(user.name(), QLatin1String("Name"));
  QCOMPARE(user.address(), QLatin1String("Street"));
  QCOMPARE(user.city(), QLatin1String("Town"));
  QCOMPARE(user.state(), QLatin1String("County"));
  QCOMPARE(user.postcode(), QLatin1String("Postcode"));
  QCOMPARE(user.telephone(), QLatin1String("Telephone"));
  QCOMPARE(user.email(), QLatin1String("Email"));
}

void MyMoneyFileTest::testAddAccounts()
{
  testAddTwoInstitutions();
  MyMoneyAccount  a, b, c;
  a.setAccountType(eMyMoney::Account::Type::Checkings);
  b.setAccountType(eMyMoney::Account::Type::Checkings);

  MyMoneyInstitution institution;

  storage->d_func()->m_dirty = false;

  QCOMPARE(m->accountCount(), static_cast<unsigned>(5));

  institution = m->institution("I000001");
  QCOMPARE(institution.id(), QLatin1String("I000001"));

  a.setName("Account1");
  a.setInstitutionId(institution.id());
  a.setCurrencyId("EUR");

  clearObjectLists();
  MyMoneyFileTransaction ft;
  try {
    MyMoneyAccount parent = m->asset();
    m->addAccount(a, parent);
    ft.commit();
    QCOMPARE(m->accountCount(), static_cast<unsigned>(6));
    QCOMPARE(a.parentAccountId(), QLatin1String("AStd::Asset"));
    QCOMPARE(a.id(), QLatin1String("A000001"));
    QCOMPARE(a.institutionId(), QLatin1String("I000001"));
    QCOMPARE(a.currencyId(), QLatin1String("EUR"));
    QCOMPARE(m->dirty(), true);
    QCOMPARE(m->asset().accountList().count(), 1);
    QCOMPARE(m->asset().accountList()[0], QLatin1String("A000001"));

    institution = m->institution("I000001");
    QCOMPARE(institution.accountCount(), static_cast<unsigned>(1));
    QCOMPARE(institution.accountList()[0], QLatin1String("A000001"));

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 1);
    QCOMPARE(m_objectsModified.count(), 2);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsAdded.contains(QLatin1String("A000001")));
    QVERIFY(m_objectsModified.contains(QLatin1String("I000001")));
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Asset")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  // try to add this account again, should not work
  ft.restart();
  try {
    MyMoneyAccount parent = m->asset();
    m->addAccount(a, parent);
    QFAIL("Expecting exception!");
  } catch (const MyMoneyException &) {
    ft.commit();
  }

  // check that we can modify the local object and
  // reload it from the file
  a.setName("AccountX");
  a = m->account("A000001");
  QCOMPARE(a.name(), QLatin1String("Account1"));

  storage->d_func()->m_dirty = false;

  // check if we can get the same info to a different object
  c = m->account("A000001");
  QCOMPARE(c.accountType(), eMyMoney::Account::Type::Checkings);
  QCOMPARE(c.id(), QLatin1String("A000001"));
  QCOMPARE(c.name(), QLatin1String("Account1"));
  QCOMPARE(c.institutionId(), QLatin1String("I000001"));

  QCOMPARE(m->dirty(), false);

  // add a second account
  institution = m->institution("I000002");
  b.setName("Account2");
  b.setInstitutionId(institution.id());
  b.setCurrencyId("EUR");
  clearObjectLists();
  ft.restart();
  try {
    MyMoneyAccount parent = m->asset();
    m->addAccount(b, parent);
    ft.commit();
    QCOMPARE(m->dirty(), true);
    QCOMPARE(b.id(), QLatin1String("A000002"));
    QCOMPARE(b.currencyId(), QLatin1String("EUR"));
    QCOMPARE(b.parentAccountId(), QLatin1String("AStd::Asset"));
    QCOMPARE(m->accountCount(), static_cast<unsigned>(7));

    institution = m->institution("I000001");
    QCOMPARE(institution.accountCount(), static_cast<unsigned>(1));
    QCOMPARE(institution.accountList()[0], QLatin1String("A000001"));

    institution = m->institution("I000002");
    QCOMPARE(institution.accountCount(), static_cast<unsigned>(1));
    QCOMPARE(institution.accountList()[0], QLatin1String("A000002"));

    QCOMPARE(m->asset().accountList().count(), 2);
    QCOMPARE(m->asset().accountList()[0], QLatin1String("A000001"));
    QCOMPARE(m->asset().accountList()[1], QLatin1String("A000002"));

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 1);
    QCOMPARE(m_objectsModified.count(), 2);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsAdded.contains(QLatin1String("A000002")));
    QVERIFY(m_objectsModified.contains(QLatin1String("I000002")));
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Asset")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  MyMoneyAccount p;

  p = m->account("A000002");
  QCOMPARE(p.accountType(), eMyMoney::Account::Type::Checkings);
  QCOMPARE(p.id(), QLatin1String("A000002"));
  QCOMPARE(p.name(), QLatin1String("Account2"));
  QCOMPARE(p.institutionId(), QLatin1String("I000002"));
  QCOMPARE(p.currencyId(), QLatin1String("EUR"));
}

void MyMoneyFileTest::testAddCategories()
{
  MyMoneyAccount  a, b, c;
  a.setAccountType(eMyMoney::Account::Type::Income);
  a.setOpeningDate(QDate::currentDate());
  b.setAccountType(eMyMoney::Account::Type::Expense);

  storage->d_func()->m_dirty = false;

  QCOMPARE(m->accountCount(), static_cast<unsigned>(5));
  QCOMPARE(a.openingDate(), QDate::currentDate());
  QVERIFY(!b.openingDate().isValid());

  a.setName("Account1");
  a.setCurrencyId("EUR");

  clearObjectLists();
  MyMoneyFileTransaction ft;
  try {
    MyMoneyAccount parent = m->income();
    m->addAccount(a, parent);
    ft.commit();
    QCOMPARE(m->accountCount(), static_cast<unsigned>(6));
    QCOMPARE(a.parentAccountId(), QLatin1String("AStd::Income"));
    QCOMPARE(a.id(), QLatin1String("A000001"));
    QCOMPARE(a.institutionId(), QString());
    QCOMPARE(a.currencyId(), QLatin1String("EUR"));
    QCOMPARE(a.openingDate(), QDate(1900, 1, 1));
    QCOMPARE(m->dirty(), true);
    QCOMPARE(m->income().accountList().count(), 1);
    QCOMPARE(m->income().accountList()[0], QLatin1String("A000001"));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  // add a second category, expense this time
  b.setName("Account2");
  b.setCurrencyId("EUR");
  clearObjectLists();
  ft.restart();
  try {
    MyMoneyAccount parent = m->expense();
    m->addAccount(b, parent);
    ft.commit();
    QCOMPARE(m->dirty(), true);
    QCOMPARE(b.id(), QLatin1String("A000002"));
    QCOMPARE(a.institutionId(), QString());
    QCOMPARE(b.currencyId(), QLatin1String("EUR"));
    QCOMPARE(b.openingDate(), QDate(1900, 1, 1));
    QCOMPARE(b.parentAccountId(), QLatin1String("AStd::Expense"));
    QCOMPARE(m->accountCount(), static_cast<unsigned>(7));

    QCOMPARE(m->income().accountList().count(), 1);
    QCOMPARE(m->expense().accountList().count(), 1);
    QCOMPARE(m->income().accountList()[0], QLatin1String("A000001"));
    QCOMPARE(m->expense().accountList()[0], QLatin1String("A000002"));

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 1);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsAdded.contains(QLatin1String("A000002")));
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Expense")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

void MyMoneyFileTest::testModifyAccount()
{
  testAddAccounts();
  storage->d_func()->m_dirty = false;

  MyMoneyAccount p = m->account("A000001");
  MyMoneyInstitution institution;

  QCOMPARE(p.accountType(), eMyMoney::Account::Type::Checkings);
  QCOMPARE(p.name(), QLatin1String("Account1"));

  p.setName("New account name");
  MyMoneyFileTransaction ft;
  clearObjectLists();
  try {
    m->modifyAccount(p);
    ft.commit();

    QCOMPARE(m->dirty(), true);
    QCOMPARE(m->accountCount(), static_cast<unsigned>(7));
    QCOMPARE(p.accountType(), eMyMoney::Account::Type::Checkings);
    QCOMPARE(p.name(), QLatin1String("New account name"));

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsModified.contains(QLatin1String("A000001")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
  storage->d_func()->m_dirty = false;

  // try to move account to new institution
  p.setInstitutionId("I000002");
  ft.restart();
  clearObjectLists();
  try {
    m->modifyAccount(p);
    ft.commit();

    QCOMPARE(m->dirty(), true);
    QCOMPARE(m->accountCount(), static_cast<unsigned>(7));
    QCOMPARE(p.accountType(), eMyMoney::Account::Type::Checkings);
    QCOMPARE(p.name(), QLatin1String("New account name"));
    QCOMPARE(p.institutionId(), QLatin1String("I000002"));

    institution = m->institution("I000001");
    QCOMPARE(institution.accountCount(), static_cast<unsigned>(0));

    institution = m->institution("I000002");
    QCOMPARE(institution.accountCount(), static_cast<unsigned>(2));
    QCOMPARE(institution.accountList()[0], QLatin1String("A000002"));
    QCOMPARE(institution.accountList()[1], QLatin1String("A000001"));

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_objectsModified.count(), 3);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsModified.contains(QLatin1String("A000001")));
    QVERIFY(m_objectsModified.contains(QLatin1String("I000001")));
    QVERIFY(m_objectsModified.contains(QLatin1String("I000002")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
  storage->d_func()->m_dirty = false;

  // try to change to an account type that is allowed
  p.setAccountType(eMyMoney::Account::Type::Savings);
  ft.restart();
  try {
    m->modifyAccount(p);
    ft.commit();

    QCOMPARE(m->dirty(), true);
    QCOMPARE(m->accountCount(), static_cast<unsigned>(7));
    QCOMPARE(p.accountType(), eMyMoney::Account::Type::Savings);
    QCOMPARE(p.name(), QLatin1String("New account name"));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
  storage->d_func()->m_dirty = false;

  // try to change to an account type that is not allowed
  p.setAccountType(eMyMoney::Account::Type::CreditCard);
  ft.restart();
  try {
    m->modifyAccount(p);
    QFAIL("Expecting exception!");
  } catch (const MyMoneyException &) {
    ft.commit();
  }
  storage->d_func()->m_dirty = false;

  // try to fool engine a bit
  p.setParentAccountId("A000001");
  ft.restart();
  try {
    m->modifyAccount(p);
    QFAIL("Expecting exception!");
  } catch (const MyMoneyException &) {
    ft.commit();
  }
}

void MyMoneyFileTest::testReparentAccount()
{
  testAddAccounts();
  storage->d_func()->m_dirty = false;

  MyMoneyAccount p = m->account("A000001");
  MyMoneyAccount q = m->account("A000002");
  MyMoneyAccount o = m->account(p.parentAccountId());

  // make A000001 a child of A000002
  clearObjectLists();
  MyMoneyFileTransaction ft;
  try {
    QVERIFY(p.parentAccountId() != q.id());
    QCOMPARE(o.accountCount(), 2);
    QCOMPARE(q.accountCount(), 0);
    m->reparentAccount(p, q);
    ft.commit();
    QCOMPARE(m->dirty(), true);
    QCOMPARE(p.parentAccountId(), q.id());
    QCOMPARE(q.accountCount(), 1);
    QCOMPARE(q.id(), QLatin1String("A000002"));
    QCOMPARE(p.id(), QLatin1String("A000001"));
    QCOMPARE(q.accountList()[0], p.id());

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_objectsModified.count(), 3);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsModified.contains(QLatin1String("A000001")));
    QVERIFY(m_objectsModified.contains(QLatin1String("A000002")));
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Asset")));

    o = m->account(o.id());
    QCOMPARE(o.accountCount(), 1);
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

void MyMoneyFileTest::testRemoveStdAccount(const MyMoneyAccount& acc)
{
  QString txt("Exception expected while removing account ");
  txt += acc.id();
  MyMoneyFileTransaction ft;
  try {
    m->removeAccount(acc);
    QFAIL(qPrintable(txt));
  } catch (const MyMoneyException &) {
    ft.commit();
  }
}

void MyMoneyFileTest::testRemoveAccount()
{
  MyMoneyInstitution institution;

  testAddAccounts();
  QCOMPARE(m->accountCount(), static_cast<unsigned>(7));
  storage->d_func()->m_dirty = false;

  MyMoneyAccount p = m->account("A000001");

  clearObjectLists();

  MyMoneyFileTransaction ft;
  try {
    MyMoneyAccount q("Ainvalid", p);
    m->removeAccount(q);
    QFAIL("Exception expected!");
  } catch (const MyMoneyException &) {
    ft.commit();
  }

  ft.restart();
  try {
    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_objectsModified.count(), 0);

    m->removeAccount(p);
    ft.commit();
    QCOMPARE(m->dirty(), true);
    QCOMPARE(m->accountCount(), static_cast<unsigned>(6));
    institution = m->institution("I000001");
    QCOMPARE(institution.accountCount(), static_cast<unsigned>(0));
    QCOMPARE(m->asset().accountList().count(), 1);

    QCOMPARE(m_objectsRemoved.count(), 1);
    QCOMPARE(m_objectsModified.count(), 2);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);

    QVERIFY(m_objectsRemoved.contains(QLatin1String("A000001")));
    QVERIFY(m_objectsModified.contains(QLatin1String("I000001")));
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Asset")));

    institution = m->institution("I000002");
    QCOMPARE(institution.accountCount(), static_cast<unsigned>(1));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  // Check that the standard account-groups cannot be removed
  testRemoveStdAccount(m->liability());
  testRemoveStdAccount(m->asset());
  testRemoveStdAccount(m->expense());
  testRemoveStdAccount(m->income());
}

void MyMoneyFileTest::testRemoveAccountTree()
{
  testReparentAccount();
  MyMoneyAccount a = m->account("A000002");

  clearObjectLists();
  MyMoneyFileTransaction ft;
  // remove the account
  try {
    m->removeAccount(a);
    ft.commit();

    QCOMPARE(m_objectsRemoved.count(), 1);
    QCOMPARE(m_objectsModified.count(), 3);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);

    QVERIFY(m_objectsRemoved.contains(QLatin1String("A000002")));
    QVERIFY(m_objectsModified.contains(QLatin1String("A000001")));
    QVERIFY(m_objectsModified.contains(QLatin1String("I000002")));
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Asset")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
  QCOMPARE(m->accountCount(), static_cast<unsigned>(6));

  // make sure it's gone
  try {
    m->account("A000002");
    QFAIL("Exception expected!");
  } catch (const MyMoneyException &) {
  }

  // make sure that children are re-parented to parent account
  try {
    a = m->account("A000001");
    QCOMPARE(a.parentAccountId(), m->asset().id());
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

void MyMoneyFileTest::testAccountListRetrieval()
{
  QList<MyMoneyAccount> list;

  storage->d_func()->m_dirty = false;
  m->accountList(list);
  QCOMPARE(m->dirty(), false);
  QCOMPARE(list.count(), 0);

  testAddAccounts();

  storage->d_func()->m_dirty = false;
  list.clear();
  m->accountList(list);
  QCOMPARE(m->dirty(), false);
  QCOMPARE(list.count(), 2);

  QCOMPARE(list[0].accountType(), eMyMoney::Account::Type::Checkings);
  QCOMPARE(list[1].accountType(), eMyMoney::Account::Type::Checkings);
}

void MyMoneyFileTest::testAddTransaction()
{
  testAddAccounts();
  MyMoneyTransaction t, p;

  MyMoneyAccount exp1;
  exp1.setAccountType(eMyMoney::Account::Type::Expense);
  exp1.setName("Expense1");
  MyMoneyAccount exp2;
  exp2.setAccountType(eMyMoney::Account::Type::Expense);
  exp2.setName("Expense2");

  MyMoneyFileTransaction ft;
  try {
    MyMoneyAccount parent = m->expense();
    m->addAccount(exp1, parent);
    m->addAccount(exp2, parent);
    ft.commit();
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  // fake the last modified flag to check that the
  // date is updated when we add the transaction
  MyMoneyAccount a = m->account("A000001");
  a.setLastModified(QDate(1, 2, 3));
  ft.restart();
  try {
    m->modifyAccount(a);
    ft.commit();
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
  ft.restart();

  QCOMPARE(m->accountCount(), static_cast<unsigned>(9));
  a = m->account("A000001");
  QCOMPARE(a.lastModified(), QDate(1, 2, 3));

  // construct a transaction and add it to the pool
  t.setPostDate(QDate(2002, 2, 1));
  t.setMemo("Memotext");

  MyMoneySplit split1;
  MyMoneySplit split2;

  split1.setAccountId("A000001");
  split1.setShares(MyMoneyMoney(-1000, 100));
  split1.setValue(MyMoneyMoney(-1000, 100));
  split2.setAccountId("A000003");
  split2.setValue(MyMoneyMoney(1000, 100));
  split2.setShares(MyMoneyMoney(1000, 100));
  try {
    t.addSplit(split1);
    t.addSplit(split2);
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  /*
          // FIXME: we don't have a payee and a number field right now
          // guess we should have a number field per split, don't know
          // about the payee
          t.setMethod(MyMoneyCheckingTransaction::Withdrawal);
          t.setPayee("Thomas Baumgart");
          t.setNumber("1234");
          t.setState(MyMoneyCheckingTransaction::Cleared);
  */
  storage->d_func()->m_dirty = false;

  ft.restart();
  clearObjectLists();
  try {
    m->addTransaction(t);
    ft.commit();
    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsModified.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 1);
    QCOMPARE(m_balanceChanged.count(), 2);
    QCOMPARE(m_balanceChanged.count("A000001"), 1);
    QCOMPARE(m_balanceChanged.count("A000003"), 1);
    QCOMPARE(m_valueChanged.count(), 0);
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
  ft.restart();
  clearObjectLists();

  QCOMPARE(t.id(), QLatin1String("T000000000000000001"));
  QCOMPARE(t.postDate(), QDate(2002, 2, 1));
  QCOMPARE(t.entryDate(), QDate::currentDate());
  QCOMPARE(m->dirty(), true);

  // check the balance of the accounts
  a = m->account("A000001");
  QCOMPARE(a.lastModified(), QDate::currentDate());
  QCOMPARE(a.balance(), MyMoneyMoney(-1000, 100));

  MyMoneyAccount b = m->account("A000003");
  QCOMPARE(b.lastModified(), QDate::currentDate());
  QCOMPARE(b.balance(), MyMoneyMoney(1000, 100));

  storage->d_func()->m_dirty = false;

  // locate transaction in MyMoneyFile via id

  try {
    p = m->transaction("T000000000000000001");
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QCOMPARE(p.splitCount(), static_cast<unsigned>(2));
    QCOMPARE(p.memo(), QLatin1String("Memotext"));
    QCOMPARE(p.splits()[0].accountId(), QLatin1String("A000001"));
    QCOMPARE(p.splits()[1].accountId(), QLatin1String("A000003"));
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  // check if it's in the account(s) as well

  try {
    p = m->transaction("A000001", 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QCOMPARE(p.id(), QLatin1String("T000000000000000001"));
    QCOMPARE(p.splitCount(), static_cast<unsigned>(2));
    QCOMPARE(p.memo(), QLatin1String("Memotext"));
    QCOMPARE(p.splits()[0].accountId(), QLatin1String("A000001"));
    QCOMPARE(p.splits()[1].accountId(), QLatin1String("A000003"));
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  try {
    p = m->transaction("A000003", 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QCOMPARE(p.id(), QLatin1String("T000000000000000001"));
    QCOMPARE(p.splitCount(), static_cast<unsigned>(2));
    QCOMPARE(p.memo(), QLatin1String("Memotext"));
    QCOMPARE(p.splits()[0].accountId(), QLatin1String("A000001"));
    QCOMPARE(p.splits()[1].accountId(), QLatin1String("A000003"));
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

void MyMoneyFileTest::testIsStandardAccount()
{
  QCOMPARE(m->isStandardAccount(m->liability().id()), true);
  QCOMPARE(m->isStandardAccount(m->asset().id()), true);
  QCOMPARE(m->isStandardAccount(m->expense().id()), true);
  QCOMPARE(m->isStandardAccount(m->income().id()), true);
  QCOMPARE(m->isStandardAccount("A00001"), false);
}

void MyMoneyFileTest::testHasActiveSplits()
{
  testAddTransaction();

  QCOMPARE(m->hasActiveSplits("A000001"), true);
  QCOMPARE(m->hasActiveSplits("A000002"), false);
}

void MyMoneyFileTest::testModifyTransactionSimple()
{
  // this will test that we can modify the basic attributes
  // of a transaction
  testAddTransaction();

  MyMoneyTransaction t = m->transaction("T000000000000000001");
  t.setMemo("New Memotext");
  storage->d_func()->m_dirty = false;

  MyMoneyFileTransaction ft;
  clearObjectLists();
  try {
    m->modifyTransaction(t);
    ft.commit();
    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 2);
    QCOMPARE(m_balanceChanged.count(QLatin1String("A000001")), 1);
    QCOMPARE(m_balanceChanged.count(QLatin1String("A000003")), 1);
    QCOMPARE(m_valueChanged.count(), 0);
    t = m->transaction("T000000000000000001");
    QCOMPARE(t.memo(), QLatin1String("New Memotext"));
    QCOMPARE(m->dirty(), true);

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

void MyMoneyFileTest::testModifyTransactionNewPostDate()
{
  // this will test that we can modify the basic attributes
  // of a transaction
  testAddTransaction();

  MyMoneyTransaction t = m->transaction("T000000000000000001");
  t.setPostDate(QDate(2004, 2, 1));
  storage->d_func()->m_dirty = false;

  MyMoneyFileTransaction ft;
  clearObjectLists();
  try {
    m->modifyTransaction(t);
    ft.commit();
    t = m->transaction("T000000000000000001");
    QCOMPARE(t.postDate(), QDate(2004, 2, 1));
    t = m->transaction("A000001", 0);
    QCOMPARE(t.id(), QLatin1String("T000000000000000001"));
    QCOMPARE(m->dirty(), true);

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 2);
    QCOMPARE(m_balanceChanged.count(QLatin1String("A000001")), 1);
    QCOMPARE(m_balanceChanged.count(QLatin1String("A000003")), 1);
    QCOMPARE(m_valueChanged.count(), 0);
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

void MyMoneyFileTest::testModifyTransactionNewAccount()
{
  // this will test that we can modify the basic attributes
  // of a transaction
  testAddTransaction();

  MyMoneyTransaction t = m->transaction("T000000000000000001");
  MyMoneySplit s;
  s = t.splits()[0];
  s.setAccountId("A000002");
  t.modifySplit(s);

  storage->d_func()->m_dirty = false;
  MyMoneyFileTransaction ft;
  clearObjectLists();
  try {
    MyMoneyTransactionFilter f1("A000001");
    MyMoneyTransactionFilter f2("A000002");
    MyMoneyTransactionFilter f3("A000003");
    QCOMPARE(m->transactionList(f1).count(), 1);
    QCOMPARE(m->transactionList(f2).count(), 0);
    QCOMPARE(m->transactionList(f3).count(), 1);

    m->modifyTransaction(t);
    ft.commit();
    t = m->transaction("T000000000000000001");
    QCOMPARE(t.postDate(), QDate(2002, 2, 1));
    t = m->transaction("A000002", 0);
    QCOMPARE(m->dirty(), true);
    QCOMPARE(m->transactionList(f1).count(), 0);
    QCOMPARE(m->transactionList(f2).count(), 1);
    QCOMPARE(m->transactionList(f3).count(), 1);

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 3);
    QCOMPARE(m_balanceChanged.count(QLatin1String("A000001")), 1);
    QCOMPARE(m_balanceChanged.count(QLatin1String("A000002")), 1);
    QCOMPARE(m_balanceChanged.count(QLatin1String("A000003")), 1);
    QCOMPARE(m_valueChanged.count(), 0);


  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

void MyMoneyFileTest::testRemoveTransaction()
{
  testModifyTransactionNewPostDate();

  MyMoneyTransaction t;
  t = m->transaction("T000000000000000001");

  storage->d_func()->m_dirty = false;
  MyMoneyFileTransaction ft;
  clearObjectLists();
  try {
    m->removeTransaction(t);
    ft.commit();
    QCOMPARE(m->dirty(), true);
    QCOMPARE(m->transactionCount(), static_cast<unsigned>(0));
    MyMoneyTransactionFilter f1("A000001");
    MyMoneyTransactionFilter f2("A000002");
    MyMoneyTransactionFilter f3("A000003");
    QCOMPARE(m->transactionList(f1).count(), 0);
    QCOMPARE(m->transactionList(f2).count(), 0);
    QCOMPARE(m->transactionList(f3).count(), 0);

    QCOMPARE(m_objectsRemoved.count(), 1);
    QCOMPARE(m_objectsModified.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 2);
    QCOMPARE(m_balanceChanged.count(QLatin1String("A000001")), 1);
    QCOMPARE(m_balanceChanged.count(QLatin1String("A000003")), 1);
    QCOMPARE(m_valueChanged.count(), 0);

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

/*
 * This function is currently not implemented. It's kind of tricky
 * because it modifies a lot of objects in a single call. This might
 * be a problem for the undo/redo stuff. That's why I left it out in
 * the first run. We migh add it, if we need it.
 * /
void testMoveSplits() {
        testModifyTransactionNewPostDate();

        QCOMPARE(m->account("A000001").transactionCount(), 1);
        QCOMPARE(m->account("A000002").transactionCount(), 0);
        QCOMPARE(m->account("A000003").transactionCount(), 1);

        try {
                m->moveSplits("A000001", "A000002");
                QCOMPARE(m->account("A000001").transactionCount(), 0);
                QCOMPARE(m->account("A000002").transactionCount(), 1);
                QCOMPARE(m->account("A000003").transactionCount(), 1);
        } catch (const MyMoneyException &e) {
                QFAIL("Unexpected exception!");
        }
}
*/

void MyMoneyFileTest::testBalanceTotal()
{
  testAddTransaction();
  MyMoneyTransaction t;

  // construct a transaction and add it to the pool
  t.setPostDate(QDate(2002, 2, 1));
  t.setMemo("Memotext");

  MyMoneySplit split1;
  MyMoneySplit split2;

  MyMoneyFileTransaction ft;
  try {
    split1.setAccountId("A000002");
    split1.setShares(MyMoneyMoney(-1000, 100));
    split1.setValue(MyMoneyMoney(-1000, 100));
    split2.setAccountId("A000004");
    split2.setValue(MyMoneyMoney(1000, 100));
    split2.setShares(MyMoneyMoney(1000, 100));
    t.addSplit(split1);
    t.addSplit(split2);
    m->addTransaction(t);
    ft.commit();
    ft.restart();
    QCOMPARE(t.id(), QLatin1String("T000000000000000002"));
    QCOMPARE(m->totalBalance("A000001"), MyMoneyMoney(-1000, 100));
    QCOMPARE(m->totalBalance("A000002"), MyMoneyMoney(-1000, 100));

    MyMoneyAccount p = m->account("A000001");
    MyMoneyAccount q = m->account("A000002");
    m->reparentAccount(p, q);
    ft.commit();
    // check totalBalance() and balance() with combinations of parameters
    QCOMPARE(m->totalBalance("A000001"), MyMoneyMoney(-1000, 100));
    QCOMPARE(m->totalBalance("A000002"), MyMoneyMoney(-2000, 100));
    QVERIFY(m->totalBalance("A000002", QDate(2002, 1, 15)).isZero());

    QCOMPARE(m->balance("A000001"), MyMoneyMoney(-1000, 100));
    QCOMPARE(m->balance("A000002"), MyMoneyMoney(-1000, 100));
    // Date of a transaction
    QCOMPARE(m->balance("A000001", QDate(2002, 2, 1)), MyMoneyMoney(-1000, 100));
    QCOMPARE(m->balance("A000002", QDate(2002, 2, 1)), MyMoneyMoney(-1000, 100));
    // Date after last transaction
    QCOMPARE(m->balance("A000001", QDate(2002, 2, 1)), MyMoneyMoney(-1000, 100));
    QCOMPARE(m->balance("A000002", QDate(2002, 2, 1)), MyMoneyMoney(-1000, 100));
    // Date before first transaction
    QVERIFY(m->balance("A000001", QDate(2002, 1, 15)).isZero());
    QVERIFY(m->balance("A000002", QDate(2002, 1, 15)).isZero());

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  // Now check for exceptions
  try {
    // Account not found for balance()
    QVERIFY(m->balance("A000005").isZero());
    QFAIL("Exception expected");
  } catch (const MyMoneyException &) {
  }

  try {
    // Account not found for totalBalance()
    QVERIFY(m->totalBalance("A000005").isZero());
    QFAIL("Exception expected");
  } catch (const MyMoneyException &) {
  }

}

void MyMoneyFileTest::testSetAccountName()
{
  MyMoneyFileTransaction ft;
  clearObjectLists();
  try {
    m->setAccountName(MyMoneyAccount::stdAccName(eMyMoney::Account::Standard::Liability), "Verbindlichkeiten");
    ft.commit();
    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Liability")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }
  ft.restart();
  clearObjectLists();
  try {
    m->setAccountName(MyMoneyAccount::stdAccName(eMyMoney::Account::Standard::Asset), QString::fromUtf8("Vermögen"));
    ft.commit();
    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Asset")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }
  ft.restart();
  clearObjectLists();
  try {
    m->setAccountName(MyMoneyAccount::stdAccName(eMyMoney::Account::Standard::Expense), "Ausgaben");
    ft.commit();
    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Expense")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }
  ft.restart();
  clearObjectLists();
  try {
    m->setAccountName(MyMoneyAccount::stdAccName(eMyMoney::Account::Standard::Income), "Einnahmen");
    ft.commit();
    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Income")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }
  ft.restart();

  QCOMPARE(m->liability().name(), QLatin1String("Verbindlichkeiten"));
  QCOMPARE(m->asset().name(), QString::fromUtf8("Vermögen"));
  QCOMPARE(m->expense().name(), QLatin1String("Ausgaben"));
  QCOMPARE(m->income().name(), QLatin1String("Einnahmen"));

  try {
    m->setAccountName("A000001", "New account name");
    ft.commit();
    QFAIL("Exception expected");
  } catch (const MyMoneyException &) {
  }
}

void MyMoneyFileTest::testAddPayee()
{
  MyMoneyPayee p;

  p.setName("THB");
  QCOMPARE(m->dirty(), false);
  MyMoneyFileTransaction ft;
  try {
    m->addPayee(p);
    ft.commit();
    QCOMPARE(m->dirty(), true);
    QCOMPARE(p.id(), QLatin1String("P000001"));

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsModified.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 1);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);

    QVERIFY(m_objectsAdded.contains(QLatin1String("P000001")));
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }
}

void MyMoneyFileTest::testModifyPayee()
{
  MyMoneyPayee p;

  testAddPayee();
  clearObjectLists();

  p = m->payee("P000001");
  p.setName("New name");
  MyMoneyFileTransaction ft;
  try {
    m->modifyPayee(p);
    ft.commit();
    p = m->payee("P000001");
    QCOMPARE(p.name(), QLatin1String("New name"));

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);

    QVERIFY(m_objectsModified.contains(QLatin1String("P000001")));
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }
}

void MyMoneyFileTest::testRemovePayee()
{
  MyMoneyPayee p;

  testAddPayee();
  clearObjectLists();
  QCOMPARE(m->payeeList().count(), 1);

  p = m->payee("P000001");
  MyMoneyFileTransaction ft;
  try {
    m->removePayee(p);
    ft.commit();
    QCOMPARE(m->payeeList().count(), 0);

    QCOMPARE(m_objectsRemoved.count(), 1);
    QCOMPARE(m_objectsModified.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 0);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);

    QVERIFY(m_objectsRemoved.contains(QLatin1String("P000001")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception");
  }
}

void MyMoneyFileTest::testPayeeWithIdentifier()
{
  MyMoneyPayee p;
  try {
    MyMoneyFileTransaction ft;
    m->addPayee(p);
    ft.commit();

    p = m->payee(p.id());

    payeeIdentifier ident = payeeIdentifier(new payeeIdentifiers::ibanBic());
    payeeIdentifierTyped<payeeIdentifiers::ibanBic> iban(ident);
    iban->setIban(QLatin1String("DE82 2007 0024 0066 6446 00"));

    ft.restart();
    p.addPayeeIdentifier(iban);
    m->modifyPayee(p);
    ft.commit();

    p = m->payee(p.id());
    QCOMPARE(p.payeeIdentifiers().count(), 1);

    ident = p.payeeIdentifiers().first();
    try {
      iban = payeeIdentifierTyped<payeeIdentifiers::ibanBic>(ident);
    } catch (...) {
      QFAIL("Unexpected exception");
    }
    QCOMPARE(iban->electronicIban(), QLatin1String("DE82200700240066644600"));
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
}

void MyMoneyFileTest::testAddTransactionStd()
{
  testAddAccounts();
  MyMoneyTransaction t, p;
  MyMoneyAccount a;

  a = m->account("A000001");

  // construct a transaction and add it to the pool
  t.setPostDate(QDate(2002, 2, 1));
  t.setMemo("Memotext");

  MyMoneySplit split1;
  MyMoneySplit split2;

  split1.setAccountId("A000001");
  split1.setShares(MyMoneyMoney(-1000, 100));
  split1.setValue(MyMoneyMoney(-1000, 100));
  split2.setAccountId(MyMoneyAccount::stdAccName(eMyMoney::Account::Standard::Expense));
  split2.setValue(MyMoneyMoney(1000, 100));
  split2.setShares(MyMoneyMoney(1000, 100));
  try {
    t.addSplit(split1);
    t.addSplit(split2);
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  /*
          // FIXME: we don't have a payee and a number field right now
          // guess we should have a number field per split, don't know
          // about the payee
          t.setMethod(MyMoneyCheckingTransaction::Withdrawal);
          t.setPayee("Thomas Baumgart");
          t.setNumber("1234");
          t.setState(MyMoneyCheckingTransaction::Cleared);
  */
  storage->d_func()->m_dirty = false;

  MyMoneyFileTransaction ft;
  try {
    m->addTransaction(t);
    ft.commit();
    QFAIL("Missing expected exception!");
  } catch (const MyMoneyException &) {
  }

  QCOMPARE(m->dirty(), false);
}

void MyMoneyFileTest::testAttachStorage()
{
  MyMoneyStorageMgr *store = new MyMoneyStorageMgr;
  MyMoneyFile *file = new MyMoneyFile;

  QCOMPARE(file->storageAttached(), false);
  try {
    file->attachStorage(store);
    QCOMPARE(file->storageAttached(), true);
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  try {
    file->attachStorage(store);
    QFAIL("Exception expected!");
  } catch (const MyMoneyException &) {
  }

  try {
    file->attachStorage(0);
    QFAIL("Exception expected!");
  } catch (const MyMoneyException &) {
  }

  try {
    file->detachStorage(store);
    QCOMPARE(file->storageAttached(), false);
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  delete store;
  delete file;
}


void MyMoneyFileTest::testAccount2Category()
{
  testReparentAccount();
  QCOMPARE(m->accountToCategory("A000001"), QLatin1String("Account2:Account1"));
  QCOMPARE(m->accountToCategory("A000002"), QLatin1String("Account2"));
}

void MyMoneyFileTest::testCategory2Account()
{
  testAddTransaction();
  MyMoneyAccount a = m->account("A000003");
  MyMoneyAccount b = m->account("A000004");

  MyMoneyFileTransaction ft;
  try {
    m->reparentAccount(b, a);
    ft.commit();
    QCOMPARE(m->categoryToAccount("Expense1"), QLatin1String("A000003"));
    QCOMPARE(m->categoryToAccount("Expense1:Expense2"), QLatin1String("A000004"));
    QVERIFY(m->categoryToAccount("Acc2").isEmpty());
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
}

void MyMoneyFileTest::testAttachedStorage()
{
  QCOMPARE(m->storageAttached(), true);
  QVERIFY(m->storage() != 0);
  MyMoneyStorageMgr *p = m->storage();
  m->detachStorage(p);
  QCOMPARE(m->storageAttached(), false);
  QCOMPARE(m->storage(), static_cast<MyMoneyStorageMgr*>(0));
  m->attachStorage(p);
  QCOMPARE(m->storageAttached(), true);
  QVERIFY(m->storage() != 0);
}

void MyMoneyFileTest::testHasAccount()
{
  testAddAccounts();

  MyMoneyAccount a, b;
  a.setAccountType(eMyMoney::Account::Type::Checkings);
  a.setName("Account3");
  b = m->account("A000001");
  MyMoneyFileTransaction ft;
  try {
    m->addAccount(a, b);
    ft.commit();
    QCOMPARE(m->accountCount(), static_cast<unsigned>(8));
    QCOMPARE(a.parentAccountId(), QLatin1String("A000001"));
    QCOMPARE(m->hasAccount("A000001", "Account3"), true);
    QCOMPARE(m->hasAccount("A000001", "Account2"), false);
    QCOMPARE(m->hasAccount("A000002", "Account3"), false);
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
}

void MyMoneyFileTest::testAddEquityAccount()
{
  MyMoneyAccount i;
  i.setName("Investment");
  i.setAccountType(eMyMoney::Account::Type::Investment);

  MyMoneyFileTransaction ft;
  try {
    MyMoneyAccount parent = m->asset();
    m->addAccount(i, parent);
    ft.commit();
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
  // keep a copy for later use
  m_inv = i;

  // make sure, that only equity accounts can be children to it
  MyMoneyAccount a;
  a.setName("Testaccount");
  QList<eMyMoney::Account::Type> list;
  list << eMyMoney::Account::Type::Checkings;
  list << eMyMoney::Account::Type::Savings;
  list << eMyMoney::Account::Type::Cash;
  list << eMyMoney::Account::Type::CreditCard;
  list << eMyMoney::Account::Type::Loan;
  list << eMyMoney::Account::Type::CertificateDep;
  list << eMyMoney::Account::Type::Investment;
  list << eMyMoney::Account::Type::MoneyMarket;
  list << eMyMoney::Account::Type::Asset;
  list << eMyMoney::Account::Type::Liability;
  list << eMyMoney::Account::Type::Currency;
  list << eMyMoney::Account::Type::Income;
  list << eMyMoney::Account::Type::Expense;
  list << eMyMoney::Account::Type::AssetLoan;

  QList<eMyMoney::Account::Type>::Iterator it;
  for (it = list.begin(); it != list.end(); ++it) {
    a.setAccountType(*it);
    ft.restart();
    try {
      char    msg[100];
      m->addAccount(a, i);
      sprintf(msg, "Can add non-equity type %d to investment", (int)*it);
      QFAIL(msg);
    } catch (const MyMoneyException &) {
      ft.commit();
    }
  }
  ft.restart();
  try {
    a.setName("Teststock");
    a.setAccountType(eMyMoney::Account::Type::Stock);
    m->addAccount(a, i);
    ft.commit();
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
}

void MyMoneyFileTest::testReparentEquity()
{
  testAddEquityAccount();
  testAddEquityAccount();
  MyMoneyAccount parent;

  // check the bad cases
  QList<eMyMoney::Account::Type> list;
  list << eMyMoney::Account::Type::Checkings;
  list << eMyMoney::Account::Type::Savings;
  list << eMyMoney::Account::Type::Cash;
  list << eMyMoney::Account::Type::CertificateDep;
  list << eMyMoney::Account::Type::MoneyMarket;
  list << eMyMoney::Account::Type::Asset;
  list << eMyMoney::Account::Type::AssetLoan;
  list << eMyMoney::Account::Type::Currency;
  parent = m->asset();
  testReparentEquity(list, parent);

  list.clear();
  list << eMyMoney::Account::Type::CreditCard;
  list << eMyMoney::Account::Type::Loan;
  list << eMyMoney::Account::Type::Liability;
  parent = m->liability();
  testReparentEquity(list, parent);

  list.clear();
  list << eMyMoney::Account::Type::Income;
  parent = m->income();
  testReparentEquity(list, parent);

  list.clear();
  list << eMyMoney::Account::Type::Expense;
  parent = m->expense();
  testReparentEquity(list, parent);

  // now check the good case
  MyMoneyAccount stock = m->account("A000002");
  MyMoneyAccount inv = m->account(m_inv.id());
  MyMoneyFileTransaction ft;
  try {
    m->reparentAccount(stock, inv);
    ft.commit();
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
}

void MyMoneyFileTest::testReparentEquity(QList<eMyMoney::Account::Type>& list, MyMoneyAccount& parent)
{
  MyMoneyAccount a;
  MyMoneyAccount stock = m->account("A000002");

  QList<eMyMoney::Account::Type>::Iterator it;
  MyMoneyFileTransaction ft;
  for (it = list.begin(); it != list.end(); ++it) {
    a.setName(QString("Testaccount %1").arg((int)*it));
    a.setAccountType(*it);
    try {
      m->addAccount(a, parent);
      char    msg[100];
      m->reparentAccount(stock, a);
      sprintf(msg, "Can reparent stock to non-investment type %d account", (int)*it);
      QFAIL(msg);
    } catch (const MyMoneyException &) {
      ft.commit();
    }
    ft.restart();
  }
}

void MyMoneyFileTest::testBaseCurrency()
{
  MyMoneySecurity base("EUR", "Euro", QChar(0x20ac));
  MyMoneySecurity ref;

  // make sure, no base currency is set
  try {
    ref = m->baseCurrency();
    QVERIFY(ref.id().isEmpty());
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  // make sure, we cannot assign an unknown currency
  try {
    m->setBaseCurrency(base);
    QFAIL("Missing expected exception");
  } catch (const MyMoneyException &) {
  }

  MyMoneyFileTransaction ft;
  // add the currency and try again
  try {
    m->addCurrency(base);
    m->setBaseCurrency(base);
    ft.commit();
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
  ft.restart();

  // make sure, the base currency is set
  try {
    ref = m->baseCurrency();
    QCOMPARE(ref.id(), QLatin1String("EUR"));
    QCOMPARE(ref.name(), QLatin1String("Euro"));
    QVERIFY(ref.tradingSymbol() == QChar(0x20ac));
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  // check if it gets reset when attaching a new storage
  m->detachStorage(storage);

  MyMoneyStorageMgr* newStorage = new MyMoneyStorageMgr;
  m->attachStorage(newStorage);

  ref = m->baseCurrency();
  QVERIFY(ref.id().isEmpty());

  m->detachStorage(newStorage);
  delete newStorage;

  m->attachStorage(storage);
  ref = m->baseCurrency();
  QCOMPARE(ref.id(), QLatin1String("EUR"));
  QCOMPARE(ref.name(), QLatin1String("Euro"));
  QVERIFY(ref.tradingSymbol() == QChar(0x20ac));
}

void MyMoneyFileTest::testOpeningBalanceNoBase()
{
  MyMoneyAccount openingAcc;
  MyMoneySecurity base;

  try {
    base = m->baseCurrency();
    openingAcc = m->openingBalanceAccount(base);
    QFAIL("Missing expected exception");
  } catch (const MyMoneyException &) {
  }
}

void MyMoneyFileTest::testOpeningBalance()
{
  MyMoneyAccount openingAcc;
  MyMoneySecurity second("USD", "US Dollar", "$");
  testBaseCurrency();

  try {
    openingAcc = m->openingBalanceAccount(m->baseCurrency());
    QCOMPARE(openingAcc.parentAccountId(), m->equity().id());
    QCOMPARE(openingAcc.name(), MyMoneyFile::openingBalancesPrefix());
    QCOMPARE(openingAcc.openingDate(), QDate::currentDate());
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  // add a second currency
  MyMoneyFileTransaction ft;
  try {
    m->addCurrency(second);
    ft.commit();
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  QString refName = QString("%1 (%2)").arg(MyMoneyFile::openingBalancesPrefix()).arg("USD");
  try {
    openingAcc = m->openingBalanceAccount(second);
    QCOMPARE(openingAcc.parentAccountId(), m->equity().id());
    QCOMPARE(openingAcc.name(), refName);
    QCOMPARE(openingAcc.openingDate(), QDate::currentDate());
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
}

void MyMoneyFileTest::testModifyStdAccount()
{
  QVERIFY(m->asset().currencyId().isEmpty());
  QCOMPARE(m->asset().name(), QLatin1String("Asset"));
  testBaseCurrency();
  QVERIFY(m->asset().currencyId().isEmpty());
  QVERIFY(!m->baseCurrency().id().isEmpty());

  MyMoneyFileTransaction ft;
  try {
    MyMoneyAccount acc = m->asset();
    acc.setName("Anlagen");
    acc.setCurrencyId(m->baseCurrency().id());
    m->modifyAccount(acc);
    ft.commit();

    QCOMPARE(m->asset().name(), QLatin1String("Anlagen"));
    QCOMPARE(m->asset().currencyId(), m->baseCurrency().id());
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  ft.restart();
  try {
    MyMoneyAccount acc = m->asset();
    acc.setNumber("Test");
    m->modifyAccount(acc);
    QFAIL("Missing expected exception");
  } catch (const MyMoneyException &) {
    ft.rollback();
  }

}

void MyMoneyFileTest::testAddPrice()
{
  testAddAccounts();
  testBaseCurrency();
  MyMoneyAccount p;

  MyMoneyFileTransaction ft;
  try {
    p = m->account("A000002");
    p.setCurrencyId("RON");
    m->modifyAccount(p);
    ft.commit();

    QCOMPARE(m->account("A000002").currencyId(), QLatin1String("RON"));
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  clearObjectLists();
  ft.restart();
  MyMoneyPrice price("EUR", "RON", QDate::currentDate(), MyMoneyMoney(4.1), "Test source");
  m->addPrice(price);
  ft.commit();
  QCOMPARE(m_balanceChanged.count(), 0);
  QCOMPARE(m_valueChanged.count(), 1);
  QCOMPARE(m_valueChanged.count("A000002"), 1);

  clearObjectLists();
  ft.restart();
  MyMoneyPrice priceReciprocal("RON", "EUR", QDate::currentDate(), MyMoneyMoney(1 / 4.1), "Test source reciprocal price");
  m->addPrice(priceReciprocal);
  ft.commit();
  QCOMPARE(m_balanceChanged.count(), 0);
  QCOMPARE(m_valueChanged.count(), 1);
  QCOMPARE(m_valueChanged.count("A000002"), 1);
}

void MyMoneyFileTest::testRemovePrice()
{
  testAddPrice();
  clearObjectLists();
  MyMoneyFileTransaction ft;
  MyMoneyPrice price("EUR", "RON", QDate::currentDate(), MyMoneyMoney(4.1), "Test source");
  m->removePrice(price);
  ft.commit();
  QCOMPARE(m_balanceChanged.count(), 0);
  QCOMPARE(m_valueChanged.count(), 1);
  QCOMPARE(m_valueChanged.count("A000002"), 1);
}

void MyMoneyFileTest::testGetPrice()
{
  testAddPrice();
  // the price for the current date is found
  QVERIFY(m->price("EUR", "RON", QDate::currentDate()).isValid());
  // the price for the current date is returned when asking for the next day with exact date set to false
  {
    const MyMoneyPrice &price = m->price("EUR", "RON", QDate::currentDate().addDays(1), false);
    QVERIFY(price.isValid() && price.date() == QDate::currentDate());
  }
  // no price is returned while asking for the next day with exact date set to true
  QVERIFY(!m->price("EUR", "RON", QDate::currentDate().addDays(1), true).isValid());

  // no price is returned while asking for the previous day with exact date set to true/false because all prices are newer
  QVERIFY(!m->price("EUR", "RON", QDate::currentDate().addDays(-1), false).isValid());
  QVERIFY(!m->price("EUR", "RON", QDate::currentDate().addDays(-1), true).isValid());

  // add two more prices
  MyMoneyFileTransaction ft;
  m->addPrice(MyMoneyPrice("EUR", "RON", QDate::currentDate().addDays(3), MyMoneyMoney(4.1), "Test source"));
  m->addPrice(MyMoneyPrice("EUR", "RON", QDate::currentDate().addDays(5), MyMoneyMoney(4.1), "Test source"));
  ft.commit();
  clearObjectLists();

  // extra tests for the exactDate=false behavior
  {
    const MyMoneyPrice &price = m->price("EUR", "RON", QDate::currentDate().addDays(2), false);
    QVERIFY(price.isValid() && price.date() == QDate::currentDate());
  }
  {
    const MyMoneyPrice &price = m->price("EUR", "RON", QDate::currentDate().addDays(3), false);
    QVERIFY(price.isValid() && price.date() == QDate::currentDate().addDays(3));
  }
  {
    const MyMoneyPrice &price = m->price("EUR", "RON", QDate::currentDate().addDays(4), false);
    QVERIFY(price.isValid() && price.date() == QDate::currentDate().addDays(3));
  }
  {
    const MyMoneyPrice &price = m->price("EUR", "RON", QDate::currentDate().addDays(5), false);
    QVERIFY(price.isValid() && price.date() == QDate::currentDate().addDays(5));
  }
  {
    const MyMoneyPrice &price = m->price("EUR", "RON", QDate::currentDate().addDays(6), false);
    QVERIFY(price.isValid() && price.date() == QDate::currentDate().addDays(5));
  }
}

void MyMoneyFileTest::testAddAccountMissingCurrency()
{
  testAddTwoInstitutions();
  MyMoneySecurity base("EUR", "Euro", QChar(0x20ac));
  MyMoneyAccount  a;
  a.setAccountType(eMyMoney::Account::Type::Checkings);

  MyMoneyInstitution institution;

  storage->d_func()->m_dirty = false;

  QCOMPARE(m->accountCount(), static_cast<unsigned>(5));

  institution = m->institution("I000001");
  QCOMPARE(institution.id(), QLatin1String("I000001"));

  a.setName("Account1");
  a.setInstitutionId(institution.id());

  clearObjectLists();
  MyMoneyFileTransaction ft;
  try {
    m->addCurrency(base);
    m->setBaseCurrency(base);
    MyMoneyAccount parent = m->asset();
    m->addAccount(a, parent);
    ft.commit();
    QCOMPARE(m->account("A000001").currencyId(), QLatin1String("EUR"));
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
}

void MyMoneyFileTest::testAddTransactionToClosedAccount()
{
  QSKIP("Test not implemented yet", SkipAll);
}

void MyMoneyFileTest::testRemoveTransactionFromClosedAccount()
{
  QSKIP("Test not implemented yet", SkipAll);
}

void MyMoneyFileTest::testModifyTransactionInClosedAccount()
{
  QSKIP("Test not implemented yet", SkipAll);
}

void MyMoneyFileTest::testStorageId()
{
  QString id;

  // make sure id will be setup if it does not exist
  MyMoneyFileTransaction ft;
  try {
    m->setValue("kmm-id", "");
    ft.commit();
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  try {
    // check for a new id
    id = m->storageId();
    QVERIFY(!id.isEmpty());
    // check that it is the same if we ask again
    QCOMPARE(id, m->storageId());
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
}

void MyMoneyFileTest::testHasMatchingOnlineBalance_emptyAccountWithoutImportedBalance()
{
  AddOneAccount();

  MyMoneyAccount a = m->account("A000001");

  QCOMPARE(m->hasMatchingOnlineBalance(a), false);
}

void MyMoneyFileTest::testHasMatchingOnlineBalance_emptyAccountWithEqualImportedBalance()
{
  AddOneAccount();

  MyMoneyAccount a = m->account("A000001");

  a.setValue("lastImportedTransactionDate", QDate(2011, 12, 1).toString(Qt::ISODate));
  a.setValue("lastStatementBalance", MyMoneyMoney().toString());

  MyMoneyFileTransaction ft;
  m->modifyAccount(a);
  ft.commit();

  QCOMPARE(m->hasMatchingOnlineBalance(a), true);
}

void MyMoneyFileTest::testHasMatchingOnlineBalance_emptyAccountWithUnequalImportedBalance()
{
  AddOneAccount();

  MyMoneyAccount a = m->account("A000001");

  a.setValue("lastImportedTransactionDate", QDate(2011, 12, 1).toString(Qt::ISODate));
  a.setValue("lastStatementBalance", MyMoneyMoney::ONE.toString());

  MyMoneyFileTransaction ft;
  m->modifyAccount(a);
  ft.commit();

  QCOMPARE(m->hasMatchingOnlineBalance(a), false);
}

void MyMoneyFileTest::testHasNewerTransaction_withoutAnyTransaction_afterLastImportedTransaction()
{
  AddOneAccount();

  MyMoneyAccount a = m->account("A000001");

  QDate dateOfLastTransactionImport(2011, 12, 1);

  // There are no transactions at all:
  QCOMPARE(m->hasNewerTransaction(a.id(), dateOfLastTransactionImport), false);
}

void MyMoneyFileTest::testHasNewerTransaction_withoutNewerTransaction_afterLastImportedTransaction()
{

  AddOneAccount();

  QString accId("A000001");
  QDate dateOfLastTransactionImport(2011, 12, 1);

  MyMoneyFileTransaction ft;
  MyMoneyTransaction t;

  // construct a transaction at the day of the last transaction import and add it to the pool
  t.setPostDate(dateOfLastTransactionImport);

  MyMoneySplit split1;

  split1.setAccountId(accId);
  split1.setShares(MyMoneyMoney(-1000, 100));
  split1.setValue(MyMoneyMoney(-1000, 100));
  t.addSplit(split1);

  ft.restart();
  m->addTransaction(t);
  ft.commit();

  QCOMPARE(m->hasNewerTransaction(accId, dateOfLastTransactionImport), false);
}

void MyMoneyFileTest::testHasNewerTransaction_withNewerTransaction_afterLastImportedTransaction()
{

  AddOneAccount();

  QString accId("A000001");
  QDate dateOfLastTransactionImport(2011, 12, 1);
  QDate dateOfDayAfterLastTransactionImport(dateOfLastTransactionImport.addDays(1));

  MyMoneyFileTransaction ft;
  MyMoneyTransaction t;

  // construct a transaction a day after the last transaction import and add it to the pool
  t.setPostDate(dateOfDayAfterLastTransactionImport);

  MyMoneySplit split1;

  split1.setAccountId(accId);
  split1.setShares(MyMoneyMoney(-1000, 100));
  split1.setValue(MyMoneyMoney(-1000, 100));
  t.addSplit(split1);

  ft.restart();
  m->addTransaction(t);
  ft.commit();

  QCOMPARE(m->hasNewerTransaction(accId, dateOfLastTransactionImport), true);
}

void MyMoneyFileTest::AddOneAccount()
{
  QString accountId = "A000001";
  MyMoneyAccount  a;
  a.setAccountType(eMyMoney::Account::Type::Checkings);

  storage->d_func()->m_dirty = false;

  QCOMPARE(m->accountCount(), static_cast<unsigned>(5));

  a.setName("Account1");
  a.setCurrencyId("EUR");

  clearObjectLists();
  MyMoneyFileTransaction ft;
  try {
    MyMoneyAccount parent = m->asset();
    m->addAccount(a, parent);
    ft.commit();
    QCOMPARE(m->accountCount(), static_cast<unsigned>(6));
    QCOMPARE(a.parentAccountId(), QLatin1String("AStd::Asset"));
    QCOMPARE(a.id(), accountId);
    QCOMPARE(a.currencyId(), QLatin1String("EUR"));
    QCOMPARE(m->dirty(), true);
    QCOMPARE(m->asset().accountList().count(), 1);
    QCOMPARE(m->asset().accountList()[0], accountId);

    QCOMPARE(m_objectsRemoved.count(), 0);
    QCOMPARE(m_objectsAdded.count(), 1);
    QCOMPARE(m_objectsModified.count(), 1);
    QCOMPARE(m_balanceChanged.count(), 0);
    QCOMPARE(m_valueChanged.count(), 0);
    QVERIFY(m_objectsAdded.contains(accountId.toLatin1()));
    QVERIFY(m_objectsModified.contains(QLatin1String("AStd::Asset")));

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

void MyMoneyFileTest::testCountTransactionsWithSpecificReconciliationState_noTransactions()
{
  AddOneAccount();
  QString accountId = "A000001";

  QCOMPARE(m->countTransactionsWithSpecificReconciliationState(accountId, eMyMoney::TransactionFilter::State::NotReconciled), 0);
}

void MyMoneyFileTest::testCountTransactionsWithSpecificReconciliationState_transactionWithWantedReconcileState()
{
  AddOneAccount();
  QString accountId = "A000001";

  // construct split & transaction
  MyMoneySplit split;
  split.setAccountId(accountId);
  split.setShares(MyMoneyMoney(-1000, 100));
  split.setValue(MyMoneyMoney(-1000, 100));

  MyMoneyTransaction transaction;
  transaction.setPostDate(QDate(2013, 1, 1));
  transaction.addSplit(split);

  // add transaction
  MyMoneyFileTransaction ft;
  m->addTransaction(transaction);
  ft.commit();

  QCOMPARE(m->countTransactionsWithSpecificReconciliationState(accountId, eMyMoney::TransactionFilter::State::NotReconciled), 1);
}

void MyMoneyFileTest::testCountTransactionsWithSpecificReconciliationState_transactionWithUnwantedReconcileState()
{
  AddOneAccount();
  QString accountId = "A000001";

  // construct split & transaction
  MyMoneySplit split;
  split.setAccountId(accountId);
  split.setShares(MyMoneyMoney(-1000, 100));
  split.setValue(MyMoneyMoney(-1000, 100));
  split.setReconcileFlag(eMyMoney::Split::State::Reconciled);

  MyMoneyTransaction transaction;
  transaction.setPostDate(QDate(2013, 1, 1));
  transaction.addSplit(split);

  // add transaction
  MyMoneyFileTransaction ft;
  m->addTransaction(transaction);
  ft.commit();

  QCOMPARE(m->countTransactionsWithSpecificReconciliationState(accountId, eMyMoney::TransactionFilter::State::NotReconciled), 0);
}

void MyMoneyFileTest::testAddOnlineJob()
{
  QSKIP("Need dummy task for this test", SkipAll);
#if 0
  // Add a onlineJob
  onlineJob job(new germanOnlineTransfer());

  MyMoneyFileTransaction ft;
  m->addOnlineJob(job);
  QCOMPARE(job.id(), QString("O000001"));
  ft.commit();

  QCOMPARE(m_objectsRemoved.count(), 0);
  QCOMPARE(m_objectsAdded.count(), 1);
  QCOMPARE(m_objectsModified.count(), 0);
  QCOMPARE(m_balanceChanged.count(), 0);
  QCOMPARE(m_valueChanged.count(), 0);
#endif
}

void MyMoneyFileTest::testGetOnlineJob()
{
  QSKIP("Need dummy task for this test", SkipAll);
  #if 0
  testAddOnlineJob();

  const onlineJob requestedJob = m->getOnlineJob("O000001");
  QVERIFY(!requestedJob.isNull());
  QCOMPARE(requestedJob.id(), QString("O000001"));
  #endif
}

void MyMoneyFileTest::testRemoveOnlineJob()
{
  QSKIP("Need dummy task for this test", SkipAll);
#if 0
  // Add a onlineJob
  onlineJob job(new germanOnlineTransfer());
  onlineJob job2(new germanOnlineTransfer());
  onlineJob job3(new germanOnlineTransfer());


  MyMoneyFileTransaction ft;
  m->addOnlineJob(job);
  m->addOnlineJob(job2);
  m->addOnlineJob(job3);
  ft.commit();

  clearObjectLists();

  ft.restart();
  m->removeOnlineJob(job);
  m->removeOnlineJob(job2);
  ft.commit();

  QCOMPARE(m_objectsRemoved.count(), 2);
  QCOMPARE(m_objectsAdded.count(), 0);
  QCOMPARE(m_objectsModified.count(), 0);
  QCOMPARE(m_balanceChanged.count(), 0);
  QCOMPARE(m_valueChanged.count(), 0);
#endif
}

void MyMoneyFileTest::testOnlineJobRollback()
{
  QSKIP("Need dummy task for this test", SkipAll);
#if 0
  // Add a onlineJob
  onlineJob job(new germanOnlineTransfer());
  onlineJob job2(new germanOnlineTransfer());
  onlineJob job3(new germanOnlineTransfer());

  MyMoneyFileTransaction ft;
  m->addOnlineJob(job);
  m->addOnlineJob(job2);
  m->addOnlineJob(job3);
  ft.rollback();

  QCOMPARE(m_objectsRemoved.count(), 0);
  QCOMPARE(m_objectsAdded.count(), 0);
  QCOMPARE(m_objectsModified.count(), 0);
  QCOMPARE(m_balanceChanged.count(), 0);
  QCOMPARE(m_valueChanged.count(), 0);
#endif
}

void MyMoneyFileTest::testRemoveLockedOnlineJob()
{
  QSKIP("Need dummy task for this test", SkipAll);
#if 0
  // Add a onlineJob
  onlineJob job(new germanOnlineTransfer());
  job.setLock(true);
  QVERIFY(job.isLocked());

  MyMoneyFileTransaction ft;
  m->addOnlineJob(job);
  ft.commit();

  clearObjectLists();

  // Try removing locked transfer
  ft.restart();
  m->removeOnlineJob(job);
  ft.commit();
  QVERIFY2(m_objectsRemoved.count() == 0, "Online Job was locked, removing is not allowed");
  QVERIFY(m_objectsAdded.count() == 0);
  QVERIFY(m_objectsModified.count() == 0);
  QVERIFY(m_balanceChanged.count() == 0);
  QVERIFY(m_valueChanged.count() == 0);

#endif
}

/** @todo */
void MyMoneyFileTest::testModifyOnlineJob()
{
  QSKIP("Need dummy task for this test", SkipAll);
#if 0
  // Add a onlineJob
  onlineJob job(new germanOnlineTransfer());
  MyMoneyFileTransaction ft;
  m->addOnlineJob(job);
  ft.commit();

  clearObjectLists();

  // Modify online job
  job.setJobSend();
  ft.restart();
  m->modifyOnlineJob(job);
  ft.commit();

  QCOMPARE(m_objectsRemoved.count(), 0);
  QCOMPARE(m_objectsAdded.count(), 0);
  QCOMPARE(m_objectsModified.count(), 1);
  QCOMPARE(m_balanceChanged.count(), 0);
  QCOMPARE(m_valueChanged.count(), 0);

  //onlineJob modifiedJob = m->getOnlineJob( job.id() );
  //QCOMPARE(modifiedJob.responsibleAccount(), QString("Std::Assert"));
#endif
}

void MyMoneyFileTest::testClearedBalance()
{
  testAddTransaction();
  MyMoneyTransaction t1;
  MyMoneyTransaction t2;

  // construct a transaction and add it to the pool
  t1.setPostDate(QDate(2002, 2, 1));
  t1.setMemo("Memotext");

  t2.setPostDate(QDate(2002, 2, 4));
  t2.setMemo("Memotext");

  MyMoneySplit split1;
  MyMoneySplit split2;
  MyMoneySplit split3;
  MyMoneySplit split4;

  MyMoneyFileTransaction ft;
  try {
    split1.setAccountId("A000002");
    split1.setShares(MyMoneyMoney(-1000, 100));
    split1.setValue(MyMoneyMoney(-1000, 100));
    split1.setReconcileFlag(eMyMoney::Split::State::Cleared);
    split2.setAccountId("A000004");
    split2.setValue(MyMoneyMoney(1000, 100));
    split2.setShares(MyMoneyMoney(1000, 100));
    split2.setReconcileFlag(eMyMoney::Split::State::Cleared);
    t1.addSplit(split1);
    t1.addSplit(split2);
    m->addTransaction(t1);
    ft.commit();
    ft.restart();
    QCOMPARE(t1.id(), QLatin1String("T000000000000000002"));
    split3.setAccountId("A000002");
    split3.setShares(MyMoneyMoney(-2000, 100));
    split3.setValue(MyMoneyMoney(-2000, 100));
    split3.setReconcileFlag(eMyMoney::Split::State::Cleared);
    split4.setAccountId("A000004");
    split4.setValue(MyMoneyMoney(2000, 100));
    split4.setShares(MyMoneyMoney(2000, 100));
    split4.setReconcileFlag(eMyMoney::Split::State::Cleared);
    t2.addSplit(split3);
    t2.addSplit(split4);
    m->addTransaction(t2);
    ft.commit();
    ft.restart();

    QCOMPARE(m->balance("A000001", QDate(2002, 2, 4)), MyMoneyMoney(-1000, 100));
    QCOMPARE(m->balance("A000002", QDate(2002, 2, 4)), MyMoneyMoney(-3000, 100));
    // Date of last cleared transaction
    QCOMPARE(m->clearedBalance("A000002", QDate(2002, 2, 1)), MyMoneyMoney(-1000, 100));

    // Date of last transaction
    QCOMPARE(m->balance("A000002", QDate(2002, 2, 4)), MyMoneyMoney(-3000, 100));

    // Date before first transaction
    QVERIFY(m->clearedBalance("A000002", QDate(2002, 1, 15)).isZero());


  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

void MyMoneyFileTest::testAdjustedValues()
{
  // create a checking account, an expeense, an investment account and a stock
  AddOneAccount();

  MyMoneyAccount exp1;
  exp1.setAccountType(eMyMoney::Account::Type::Expense);
  exp1.setName("Expense1");
  exp1.setCurrencyId("EUR");

  MyMoneyFileTransaction ft;
  try {
    MyMoneyAccount parent = m->expense();
    m->addAccount(exp1, parent);
    ft.commit();
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  testAddEquityAccount();

  testBaseCurrency();
  MyMoneySecurity stockSecurity(QLatin1String("Blubber"), QLatin1String("TestsockSecurity"), QLatin1String("BLUB"), 1000, 1000, 1000);
  stockSecurity.setTradingCurrency(QLatin1String("BLUB"));
  // add the security
  ft.restart();
  try {
    m->addSecurity(stockSecurity);
    ft.commit();
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  MyMoneyAccount i = m->accountByName("Investment");
  MyMoneyAccount stock;
  ft.restart();
  try {
    stock.setName("Teststock");
    stock.setCurrencyId(stockSecurity.id());
    stock.setAccountType(eMyMoney::Account::Type::Stock);
    m->addAccount(stock, i);
    ft.commit();
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  // values taken from real example on https://bugs.kde.org/show_bug.cgi?id=345655
  MyMoneySplit s1, s2, s3;
  s1.setAccountId(QLatin1String("A000001"));
  s1.setShares(MyMoneyMoney(QLatin1String("-99901/1000")));
  s1.setValue(MyMoneyMoney(QLatin1String("-999/10")));

  s2.setAccountId(exp1.id());
  s2.setShares(MyMoneyMoney(QLatin1String("-611/250")));
  s2.setValue(MyMoneyMoney(QLatin1String("-61/25")));

  s3.setAccountId(stock.id());
  s3.setAction(eMyMoney::Split::InvestmentTransactionType::BuyShares);
  s3.setShares(MyMoneyMoney(QLatin1String("64901/100000")));
  s3.setPrice(MyMoneyMoney(QLatin1String("157689/1000")));
  s3.setValue(MyMoneyMoney(QLatin1String("102340161/1000000")));

  MyMoneyTransaction t;
  t.setCommodity(QLatin1String("EUR"));
  t.setPostDate(QDate::currentDate());
  t.addSplit(s1);
  t.addSplit(s2);
  t.addSplit(s3);

  // make sure the split sum is not zero
  QVERIFY(!t.splitSum().isZero());

  ft.restart();
  try {
    m->addTransaction(t);
    ft.commit();
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  QCOMPARE(t.splitById(s1.id()).shares(), MyMoneyMoney(QLatin1String("-999/10")));
  QCOMPARE(t.splitById(s1.id()).value(), MyMoneyMoney(QLatin1String("-999/10")));

  QCOMPARE(t.splitById(s2.id()).shares(), MyMoneyMoney(QLatin1String("-61/25")));
  QCOMPARE(t.splitById(s2.id()).value(), MyMoneyMoney(QLatin1String("-61/25")));

  QCOMPARE(t.splitById(s3.id()).shares(), MyMoneyMoney(QLatin1String("649/1000")));
  QCOMPARE(t.splitById(s3.id()).value(), MyMoneyMoney(QLatin1String("10234/100")));
  QCOMPARE(t.splitById(s3.id()).price(), MyMoneyMoney(QLatin1String("157689/1000")));
  QCOMPARE(t.splitSum(), MyMoneyMoney());

  // now reset and check if modify also works
  s1.setShares(MyMoneyMoney(QLatin1String("-999/10")));
  s1.setValue(MyMoneyMoney(QLatin1String("-999/10")));

  s2.setShares(MyMoneyMoney(QLatin1String("-61/25")));
  s2.setValue(MyMoneyMoney(QLatin1String("-61/25")));

  s3.setShares(MyMoneyMoney(QLatin1String("649/1000")));
  s3.setPrice(MyMoneyMoney(QLatin1String("157689/1000")));
  s3.setValue(MyMoneyMoney(QLatin1String("102340161/1000000")));

  t.modifySplit(s1);
  t.modifySplit(s2);
  t.modifySplit(s3);

  // make sure the split sum is not zero
  QVERIFY(!t.splitSum().isZero());

  ft.restart();
  try {
    m->modifyTransaction(t);
    ft.commit();
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  // we need to get the transaction from the engine, as modifyTransaction does
  // not return the modified values
  MyMoneyTransaction t2 = m->transaction(t.id());

  QCOMPARE(t2.splitById(s3.id()).shares(), MyMoneyMoney(QLatin1String("649/1000")));
  QCOMPARE(t2.splitById(s3.id()).value(), MyMoneyMoney(QLatin1String("10234/100")));
  QCOMPARE(t2.splitById(s3.id()).price(), MyMoneyMoney(QLatin1String("157689/1000")));
  QCOMPARE(t2.splitSum(), MyMoneyMoney());
}

void MyMoneyFileTest::testVatAssignment()
{
  MyMoneyAccount acc;
  MyMoneyAccount vat;
  MyMoneyAccount expense;

  testAddTransaction();

  vat.setName("VAT");
  vat.setCurrencyId("EUR");
  vat.setAccountType(eMyMoney::Account::Type::Expense);
  // make it a VAT account
  vat.setValue(QLatin1String("VatRate"), QLatin1String("20/100"));

  MyMoneyFileTransaction ft;
  try {
    MyMoneyAccount parent = m->expense();
    m->addAccount(vat, parent);
    QVERIFY(!vat.id().isEmpty());
    acc = m->account(QLatin1String("A000001"));
    expense = m->account(QLatin1String("A000003"));
    QCOMPARE(acc.name(), QLatin1String("Account1"));
    QCOMPARE(expense.name(), QLatin1String("Expense1"));
    expense.setValue(QLatin1String("VatAccount"), vat.id());
    m->modifyAccount(expense);
    ft.commit();
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  // the categories are setup now for gross value entry
  MyMoneyTransaction tr;
  MyMoneySplit sp;
  MyMoneyMoney amount(1707, 100);

  // setup the transaction
  sp.setShares(amount);
  sp.setValue(amount);
  sp.setAccountId(acc.id());
  tr.addSplit(sp);
  sp.clearId();
  sp.setShares(-amount);
  sp.setValue(-amount);
  sp.setAccountId(expense.id());
  tr.addSplit(sp);

  QCOMPARE(m->addVATSplit(tr, acc, expense, amount), true);
  QCOMPARE(tr.splits().count(), 3);
  QCOMPARE(tr.splitByAccount(acc.id()).shares().toString(), MyMoneyMoney(1707, 100).toString());
  QCOMPARE(tr.splitByAccount(expense.id()).shares().toString(), MyMoneyMoney(-1422, 100).toString());
  QCOMPARE(tr.splitByAccount(vat.id()).shares().toString(), MyMoneyMoney(-285, 100).toString());
  QCOMPARE(tr.splitSum().toString(), MyMoneyMoney().toString());

  tr.removeSplits();
  ft.restart();
  try {
    expense.setValue(QLatin1String("VatAmount"), QLatin1String("net"));
    m->modifyAccount(expense);
    ft.commit();
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  // the categories are setup now for net value entry
  amount = MyMoneyMoney(1422, 100);
  sp.clearId();
  sp.setShares(amount);
  sp.setValue(amount);
  sp.setAccountId(acc.id());
  tr.addSplit(sp);
  sp.clearId();
  sp.setShares(-amount);
  sp.setValue(-amount);
  sp.setAccountId(expense.id());
  tr.addSplit(sp);

  QCOMPARE(m->addVATSplit(tr, acc, expense, amount), true);
  QCOMPARE(tr.splits().count(), 3);
  QCOMPARE(tr.splitByAccount(acc.id()).shares().toString(), MyMoneyMoney(1706, 100).toString());
  QCOMPARE(tr.splitByAccount(expense.id()).shares().toString(), MyMoneyMoney(-1422, 100).toString());
  QCOMPARE(tr.splitByAccount(vat.id()).shares().toString(), MyMoneyMoney(-284, 100).toString());
  QCOMPARE(tr.splitSum().toString(), MyMoneyMoney().toString());
}

void MyMoneyFileTest::testEmptyFilter()
{
  testAddTransaction();

  try {
    QList<QPair<MyMoneyTransaction, MyMoneySplit> > tList;
    MyMoneyTransactionFilter filter;
    MyMoneyFile::instance()->transactionList(tList, filter);
    QCOMPARE(tList.count(), 2);

  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }
}

void MyMoneyFileTest::testAddSecurity()
{
  // create a checking account, an expeense, an investment account and a stock
  AddOneAccount();

  MyMoneyAccount exp1;
  exp1.setAccountType(eMyMoney::Account::Type::Expense);
  exp1.setName("Expense1");
  exp1.setCurrencyId("EUR");

  MyMoneyFileTransaction ft;
  try {
    MyMoneyAccount parent = m->expense();
    m->addAccount(exp1, parent);
    ft.commit();
  } catch (const MyMoneyException &) {
    QFAIL("Unexpected exception!");
  }

  testAddEquityAccount();

  testBaseCurrency();
  MyMoneySecurity stockSecurity(QLatin1String("Blubber"), QLatin1String("TestsockSecurity"), QLatin1String("BLUB"), 1000, 1000, 1000);
  stockSecurity.setTradingCurrency(QLatin1String("BLUB"));
  // add the security
  ft.restart();
  try {
    m->addSecurity(stockSecurity);
    ft.commit();
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  // check that we can get it via the security method
  try {
    MyMoneySecurity sec = m->security(stockSecurity.id());
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }

  // and also via the currency method
  try {
    MyMoneySecurity sec = m->currency(stockSecurity.id());
  } catch (const MyMoneyException &e) {
    unexpectedException(e);
  }
}
