summaryrefslogtreecommitdiffstats
path: root/kmymoney2/mymoney/storage
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2011-07-04 22:38:03 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2011-07-04 22:38:03 +0000
commitdadc34655c3ab961b0b0b94a10eaaba710f0b5e8 (patch)
tree99e72842fe687baea16376a147619b6048d7e441 /kmymoney2/mymoney/storage
downloadkmymoney-dadc34655c3ab961b0b0b94a10eaaba710f0b5e8.tar.gz
kmymoney-dadc34655c3ab961b0b0b94a10eaaba710f0b5e8.zip
Added kmymoney
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kmymoney@1239792 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kmymoney2/mymoney/storage')
-rw-r--r--kmymoney2/mymoney/storage/Makefile.am20
-rw-r--r--kmymoney2/mymoney/storage/imymoneyserialize.cpp31
-rw-r--r--kmymoney2/mymoney/storage/imymoneyserialize.h374
-rw-r--r--kmymoney2/mymoney/storage/imymoneystorage.cpp38
-rw-r--r--kmymoney2/mymoney/storage/imymoneystorage.h886
-rw-r--r--kmymoney2/mymoney/storage/imymoneystorageformat.cpp31
-rw-r--r--kmymoney2/mymoney/storage/imymoneystorageformat.h75
-rw-r--r--kmymoney2/mymoney/storage/mymoneydatabasemgr.cpp1880
-rw-r--r--kmymoney2/mymoney/storage/mymoneydatabasemgr.h1038
-rw-r--r--kmymoney2/mymoney/storage/mymoneydatabasemgrtest.cpp1996
-rw-r--r--kmymoney2/mymoney/storage/mymoneydatabasemgrtest.h143
-rw-r--r--kmymoney2/mymoney/storage/mymoneymap.h328
-rw-r--r--kmymoney2/mymoney/storage/mymoneymaptest.cpp38
-rw-r--r--kmymoney2/mymoney/storage/mymoneymaptest.h47
-rw-r--r--kmymoney2/mymoney/storage/mymoneyseqaccessmgr.cpp1944
-rw-r--r--kmymoney2/mymoney/storage/mymoneyseqaccessmgr.h1232
-rw-r--r--kmymoney2/mymoney/storage/mymoneyseqaccessmgrtest.cpp1705
-rw-r--r--kmymoney2/mymoney/storage/mymoneyseqaccessmgrtest.h131
-rw-r--r--kmymoney2/mymoney/storage/mymoneystorageanon.cpp294
-rw-r--r--kmymoney2/mymoney/storage/mymoneystorageanon.h113
-rw-r--r--kmymoney2/mymoney/storage/mymoneystoragebin.h49
-rw-r--r--kmymoney2/mymoney/storage/mymoneystoragedump.cpp446
-rw-r--r--kmymoney2/mymoney/storage/mymoneystoragedump.h56
-rw-r--r--kmymoney2/mymoney/storage/mymoneystoragesql.cpp4511
-rw-r--r--kmymoney2/mymoney/storage/mymoneystoragesql.h807
-rw-r--r--kmymoney2/mymoney/storage/mymoneystoragexml.cpp908
-rw-r--r--kmymoney2/mymoney/storage/mymoneystoragexml.h156
27 files changed, 19277 insertions, 0 deletions
diff --git a/kmymoney2/mymoney/storage/Makefile.am b/kmymoney2/mymoney/storage/Makefile.am
new file mode 100644
index 0000000..0055800
--- /dev/null
+++ b/kmymoney2/mymoney/storage/Makefile.am
@@ -0,0 +1,20 @@
+KDE_OPTIONS = noautodist
+
+INCLUDES = $(all_includes) -I$(top_srcdir) -I. -I$(top_builddir)/kmymoney2/dialogs
+
+noinst_LIBRARIES = libstorage.a
+libstorage_a_METASOURCES = AUTO
+
+libstorage_a_SOURCES = imymoneystorageformat.cpp mymoneystoragexml.cpp mymoneystoragedump.cpp mymoneyseqaccessmgr.cpp mymoneydatabasemgr.cpp imymoneystorage.cpp imymoneyserialize.cpp mymoneystorageanon.cpp mymoneystoragesql.cpp
+
+instdir=$(includedir)/kmymoney
+inst_HEADERS = imymoneystorage.h imymoneyserialize.h imymoneystorageformat.h
+
+noinst_HEADERS = mymoneyseqaccessmgr.h mymoneydatabasemgr.h mymoneystorageanon.h mymoneystoragedump.h mymoneystoragexml.h mymoneyseqaccessmgrtest.h mymoneydatabasemgrtest.h mymoneystoragesql.h mymoneystoragebin.h mymoneymap.h mymoneymaptest.h
+
+if CPPUNIT
+check_LIBRARIES = libstoragetest.a
+
+libstoragetest_a_SOURCES = mymoneyseqaccessmgrtest.cpp mymoneymaptest.cpp mymoneydatabasemgrtest.cpp
+endif
+
diff --git a/kmymoney2/mymoney/storage/imymoneyserialize.cpp b/kmymoney2/mymoney/storage/imymoneyserialize.cpp
new file mode 100644
index 0000000..14b76dd
--- /dev/null
+++ b/kmymoney2/mymoney/storage/imymoneyserialize.cpp
@@ -0,0 +1,31 @@
+/***************************************************************************
+ imymoneyserialize.cpp - description
+ -------------------
+ begin : Fri May 10 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#include "imymoneyserialize.h"
+
+IMyMoneySerialize::IMyMoneySerialize()
+{
+}
+IMyMoneySerialize::~IMyMoneySerialize()
+{
+}
+
diff --git a/kmymoney2/mymoney/storage/imymoneyserialize.h b/kmymoney2/mymoney/storage/imymoneyserialize.h
new file mode 100644
index 0000000..a0c12ca
--- /dev/null
+++ b/kmymoney2/mymoney/storage/imymoneyserialize.h
@@ -0,0 +1,374 @@
+/***************************************************************************
+ imymoneyserialize.h - description
+ -------------------
+ begin : Fri May 10 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#ifndef IMYMONEYSERIALIZE_H
+#define IMYMONEYSERIALIZE_H
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+#include <qstring.h>
+#include <qvaluelist.h>
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include <kmymoney/mymoneyutils.h>
+#include <kmymoney/mymoneyinstitution.h>
+#include <kmymoney/mymoneyaccount.h>
+#include <kmymoney/mymoneytransaction.h>
+#include <kmymoney/mymoneypayee.h>
+#include <kmymoney/mymoneyscheduled.h>
+#include <kmymoney/mymoneytransactionfilter.h>
+#include <kmymoney/mymoneysecurity.h>
+#include <kmymoney/mymoneyprice.h>
+#include <kmymoney/mymoneyreport.h>
+#include <kmymoney/mymoneybudget.h>
+#include "mymoneystoragesql.h"
+
+/**
+ * @author Thomas Baumgart
+ */
+
+/**
+ * This class represents the interface to serialize a MyMoneyStorage object
+ */
+class IMyMoneySerialize {
+public:
+ IMyMoneySerialize();
+ virtual ~IMyMoneySerialize();
+
+ // general get functions
+ virtual const MyMoneyPayee user(void) const = 0;
+ virtual const QDate creationDate(void) const = 0;
+ virtual const QDate lastModificationDate(void) const = 0;
+ virtual unsigned int currentFixVersion(void) const = 0;
+ virtual unsigned int fileFixVersion(void) const = 0;
+
+ // general set functions
+ virtual void setUser(const MyMoneyPayee& val) = 0;
+ virtual void setCreationDate(const QDate& val) = 0;
+ virtual void setFileFixVersion(const unsigned int v) = 0;
+ /**
+ * This method is used to get a SQL reader for subsequent database access
+ */
+ virtual KSharedPtr <MyMoneyStorageSql> connectToDatabase
+ (const KURL& url) = 0;
+ /**
+ * This method is used when a database file is open, and the data is to
+ * be saved in a different file or format. It will ensure that all data
+ * from the database is available in memory to enable it to be written.
+ */
+ virtual void fillStorage() = 0;
+
+ /**
+ * This method is used to set the last modification date of
+ * the storage object. It also clears the dirty flag and should
+ * therefor be called as last operation when loading from a
+ * file.
+ *
+ * @param val QDate of last modification
+ */
+ virtual void setLastModificationDate(const QDate& val) = 0;
+
+ /**
+ * This method returns a list of accounts inside the storage object.
+ *
+ * @param list reference to QValueList receiving the account objects
+ *
+ * @note The standard accounts will not be returned
+ */
+ virtual void accountList(QValueList<MyMoneyAccount>& list) const = 0;
+
+ /**
+ * This method returns a list of the institutions
+ * inside a MyMoneyStorage object
+ *
+ * @return QMap containing the institution information
+ */
+ virtual const QValueList<MyMoneyInstitution> institutionList(void) const = 0;
+
+ /**
+ * This method is used to pull a list of transactions from the file
+ * global transaction pool. It returns all those transactions
+ * that match the filter passed as argument. If the filter is empty,
+ * the whole journal will be returned.
+ *
+ * @param list reference to QValueList<MyMoneyTransaction> receiving
+ * the set of transactions
+ * @param filter MyMoneyTransactionFilter object with the match criteria
+ */
+ virtual void transactionList(QValueList<MyMoneyTransaction>& list, MyMoneyTransactionFilter& filter) const = 0;
+
+
+ /**
+ * This method returns whether a given transaction is already in memory, to avoid
+ * reloading it from the database
+ */
+ virtual bool isDuplicateTransaction(const QString&) const = 0;
+ /**
+ * This method returns a list of the payees
+ * inside a MyMoneyStorage object
+ *
+ * @return QValueList<MyMoneyPayee> containing the payee information
+ */
+ virtual const QValueList<MyMoneyPayee> payeeList(void) const = 0;
+
+ /**
+ * This method returns a list of the scheduled transactions
+ * inside a MyMoneyStorage object. In order to retrieve a complete
+ * list of the transactions, all arguments should be used with their
+ * default arguments.
+ */
+ virtual const QValueList<MyMoneySchedule> scheduleList(const QString& = QString(),
+ const MyMoneySchedule::typeE = MyMoneySchedule::TYPE_ANY,
+ const MyMoneySchedule::occurenceE = MyMoneySchedule::OCCUR_ANY,
+ const MyMoneySchedule::paymentTypeE = MyMoneySchedule::STYPE_ANY,
+ const QDate& = QDate(),
+ const QDate& = QDate(),
+ const bool = false) const = 0;
+
+ /**
+ * This method returns a list of security objects that the engine has
+ * knowledge of.
+ */
+ virtual const QValueList<MyMoneySecurity> securityList(void) const = 0;
+
+ /**
+ * This method is used to return the standard liability account
+ * @return MyMoneyAccount liability account(group)
+ */
+ virtual const MyMoneyAccount liability(void) const = 0;
+
+ /**
+ * This method is used to return the standard asset account
+ * @return MyMoneyAccount asset account(group)
+ */
+ virtual const MyMoneyAccount asset(void) const = 0;
+
+ /**
+ * This method is used to return the standard expense account
+ * @return MyMoneyAccount expense account(group)
+ */
+ virtual const MyMoneyAccount expense(void) const = 0;
+
+ /**
+ * This method is used to return the standard income account
+ * @return MyMoneyAccount income account(group)
+ */
+ virtual const MyMoneyAccount income(void) const = 0;
+
+ /**
+ * This method is used to return the standard equity account
+ * @return MyMoneyAccount equity account(group)
+ */
+ virtual const MyMoneyAccount equity(void) const = 0;
+
+ /**
+ * This method is used to create a new account
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account MyMoneyAccount filled with data
+ */
+ virtual void addAccount(MyMoneyAccount& account) = 0;
+
+ /**
+ * This method is used to add one account as sub-ordinate to another
+ * (parent) account. The objects that are passed will be modified
+ * accordingly.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param parent parent account the account should be added to
+ * @param account the account to be added
+ *
+ * @deprecated This method is only provided as long as we provide
+ * the version 0.4 binary reader. As soon as we deprecate
+ * this compatability mode this method will disappear from
+ * this interface!
+ */
+ virtual void addAccount(MyMoneyAccount& parent, MyMoneyAccount& account) = 0;
+
+ /**
+ * This method is used to create a new payee
+ *
+ * An exception will be thrown upon error conditions
+ *
+ * @param payee MyMoneyPayee reference to payee information
+ *
+ * @deprecated This method is only provided as long as we provide
+ * the version 0.4 binary reader. As soon as we deprecate
+ * this compatability mode this method will disappear from
+ * this interface!
+ *
+ */
+ virtual void addPayee(MyMoneyPayee& payee) = 0;
+
+ /**
+ * Adds an institution to the storage. A
+ * respective institution-ID will be generated within this record.
+ * The ID is stored as QString in the object passed as argument.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param institution The complete institution information in a
+ * MyMoneyInstitution object
+ *
+ * @deprecated This method is only provided as long as we provide
+ * the version 0.4 binary reader. As soon as we deprecate
+ * this compatability mode this method will disappear from
+ * this interface!
+ */
+ virtual void addInstitution(MyMoneyInstitution& institution) = 0;
+
+ /**
+ * Adds a transaction to the file-global transaction pool. A respective
+ * transaction-ID will be generated within this record. The ID is stored
+ * as QString with the object.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param transaction reference to the transaction
+ * @param skipAccountUpdate if set, the transaction lists of the accounts
+ * referenced in the splits are not updated. This is used for
+ * bulk loading a lot of transactions but not during normal operation.
+ * Refreshing the account's transaction list can be done using
+ * refreshAllAccountTransactionList().
+ *
+ * @deprecated This method is only provided as long as we provide
+ * the version 0.4 binary reader. As soon as we deprecate
+ * this compatability mode this method will disappear from
+ * this interface!
+ */
+ virtual void addTransaction(MyMoneyTransaction& transaction, const bool skipAccountUpdate = false) = 0;
+
+ virtual void loadAccounts(const QMap<QString, MyMoneyAccount>& map) = 0;
+ virtual void loadTransactions(const QMap<QString, MyMoneyTransaction>& map) = 0;
+ virtual void loadInstitutions(const QMap<QString, MyMoneyInstitution>& map) = 0;
+ virtual void loadPayees(const QMap<QString, MyMoneyPayee>& map) = 0;
+ virtual void loadSchedules(const QMap<QString, MyMoneySchedule>& map) = 0;
+ virtual void loadSecurities(const QMap<QString, MyMoneySecurity>& map) = 0;
+ virtual void loadCurrencies(const QMap<QString, MyMoneySecurity>& map) = 0;
+ virtual void loadReports( const QMap<QString, MyMoneyReport>& reports ) = 0;
+ virtual void loadBudgets( const QMap<QString, MyMoneyBudget>& budgets ) = 0;
+ virtual void loadPrices(const MyMoneyPriceList& list) = 0;
+
+ virtual unsigned long accountId(void) const = 0;
+ virtual unsigned long transactionId(void) const = 0;
+ virtual unsigned long payeeId(void) const = 0;
+ virtual unsigned long institutionId(void) const = 0;
+ virtual unsigned long scheduleId(void) const = 0;
+ virtual unsigned long securityId(void) const = 0;
+ virtual unsigned long reportId(void) const = 0;
+ virtual unsigned long budgetId(void) const = 0;
+
+ virtual void loadAccountId(const unsigned long id) = 0;
+ virtual void loadTransactionId(const unsigned long id) = 0;
+ virtual void loadPayeeId(const unsigned long id) = 0;
+ virtual void loadInstitutionId(const unsigned long id) = 0;
+ virtual void loadScheduleId(const unsigned long id) = 0;
+ virtual void loadSecurityId(const unsigned long id) = 0;
+ virtual void loadReportId(const unsigned long id) = 0;
+ virtual void loadBudgetId(const unsigned long id) = 0;
+
+ /**
+ * This method is used to retrieve the whole set of key/value pairs
+ * from the container. It is meant to be used for permanent storage
+ * functionality. See MyMoneyKeyValueContainer::pairs() for details.
+ *
+ * @return QMap<QString, QString> containing all key/value pairs of
+ * this container.
+ */
+ virtual const QMap<QString, QString> pairs(void) const = 0;
+
+ /**
+ * This method is used to initially store a set of key/value pairs
+ * in the container. It is meant to be used for loading functionality
+ * from permanent storage. See MyMoneyKeyValueContainer::setPairs()
+ * for details
+ *
+ * @param list const QMap<QString, QString> containing the set of
+ * key/value pairs to be loaded into the container.
+ *
+ * @note All existing key/value pairs in the container will be deleted.
+ */
+ virtual void setPairs(const QMap<QString, QString>& list) = 0;
+
+ virtual const QValueList<MyMoneySchedule> scheduleListEx( int scheduleTypes,
+ int scheduleOcurrences,
+ int schedulePaymentTypes,
+ QDate startDate,
+ const QStringList& accounts=QStringList()) const = 0;
+
+ /**
+ * This method is used to retrieve the list of all currencies
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneySecurity objects representing a currency.
+ */
+ virtual const QValueList<MyMoneySecurity> currencyList(void) const = 0;
+
+ /**
+ * This method is used to retrieve the list of all reports
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneyReport objects.
+ */
+ virtual const QValueList<MyMoneyReport> reportList( void ) const = 0;
+
+ /**
+ * This method is used to retrieve the list of all budgets
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneyBudget objects.
+ */
+ virtual const QValueList<MyMoneyBudget> budgetList( void ) const = 0;
+
+
+ /**
+ * This method adds a price entry to the price list.
+ */
+ virtual void addPrice(const MyMoneyPrice& price) = 0;
+
+ /**
+ * This method returns a list of all prices.
+ *
+ * @return MyMoneyPriceList of all MyMoneyPrice objects.
+ */
+ virtual const MyMoneyPriceList priceList(void) const = 0;
+
+ /**
+ * This method recalculates the balances of all accounts
+ * based on the transactions stored in the engine.
+ */
+ virtual void rebuildAccountBalances(void) = 0;
+
+};
+
+#endif
diff --git a/kmymoney2/mymoney/storage/imymoneystorage.cpp b/kmymoney2/mymoney/storage/imymoneystorage.cpp
new file mode 100644
index 0000000..dc67726
--- /dev/null
+++ b/kmymoney2/mymoney/storage/imymoneystorage.cpp
@@ -0,0 +1,38 @@
+/***************************************************************************
+ imymoneystorage.cpp - description
+ -------------------
+ begin : Sun May 5 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#include "imymoneystorage.h"
+
+bool MyMoneyFileBitArray::testBit(uint index) const
+{
+ if(index < size())
+ return QBitArray::testBit(index);
+ return false;
+}
+
+
+IMyMoneyStorage::IMyMoneyStorage()
+{
+}
+IMyMoneyStorage::~IMyMoneyStorage()
+{
+}
diff --git a/kmymoney2/mymoney/storage/imymoneystorage.h b/kmymoney2/mymoney/storage/imymoneystorage.h
new file mode 100644
index 0000000..a4e55dd
--- /dev/null
+++ b/kmymoney2/mymoney/storage/imymoneystorage.h
@@ -0,0 +1,886 @@
+/***************************************************************************
+ imymoneystorage.h - description
+ -------------------
+ begin : Sun May 5 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#ifndef IMYMONEYSTORAGE_H
+#define IMYMONEYSTORAGE_H
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+#include <qstring.h>
+#include <qbitarray.h>
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include <kmymoney/mymoneyutils.h>
+#include <kmymoney/mymoneyinstitution.h>
+#include <kmymoney/mymoneyaccount.h>
+#include <kmymoney/mymoneytransaction.h>
+#include <kmymoney/mymoneypayee.h>
+#include <kmymoney/mymoneyscheduled.h>
+#include <kmymoney/mymoneyobserver.h>
+#include <kmymoney/mymoneytransactionfilter.h>
+#include <kmymoney/mymoneysecurity.h>
+#include <kmymoney/mymoneyprice.h>
+#include <kmymoney/mymoneyreport.h>
+#include <kmymoney/mymoneybudget.h>
+
+/**
+ * @author Thomas Baumgart
+ *
+ * A simple replacement for QBitArray that does not bark if testBit()
+ * is called with an index out of bounds. It silently returns false
+ * in that case, otherwise calls the base classes implementation.
+ */
+class MyMoneyFileBitArray : public QBitArray
+{
+public:
+ MyMoneyFileBitArray() : QBitArray() {}
+ MyMoneyFileBitArray(int size) : QBitArray(size) {}
+ bool testBit(uint index) const;
+ bool operator[](int index) const { return testBit(index); }
+ bool at(uint index) const { return testBit(index); }
+};
+
+
+/**
+ * @author Thomas Baumgart
+ */
+
+/**
+ * The IMyMoneyStorage class describes the interface between the MyMoneyFile class
+ * and the real storage manager.
+ *
+ * @see MyMoneySeqAccessMgr
+ */
+class IMyMoneyStorage {
+public:
+
+ typedef enum {
+ RefCheckAccount = 0,
+ RefCheckInstitution,
+ RefCheckPayee,
+ RefCheckTransaction,
+ RefCheckReport,
+ RefCheckBudget,
+ RefCheckSchedule,
+ RefCheckSecurity,
+ RefCheckCurrency,
+ RefCheckPrice,
+ // insert new entries above this line
+ MaxRefCheckBits
+ } ReferenceCheckBits;
+
+ // definitions for the ID's of the standard accounts
+#define STD_ACC_LIABILITY "AStd::Liability"
+#define STD_ACC_ASSET "AStd::Asset"
+#define STD_ACC_EXPENSE "AStd::Expense"
+#define STD_ACC_INCOME "AStd::Income"
+#define STD_ACC_EQUITY "AStd::Equity"
+
+ IMyMoneyStorage();
+ virtual ~IMyMoneyStorage();
+
+ // general get functions
+ virtual const MyMoneyPayee user(void) const = 0;
+ virtual const QDate creationDate(void) const = 0;
+ virtual const QDate lastModificationDate(void) const = 0;
+ virtual unsigned int currentFixVersion(void) const = 0;
+ virtual unsigned int fileFixVersion(void) const = 0;
+
+ // general set functions
+ virtual void setUser(const MyMoneyPayee& user) = 0;
+ virtual void setFileFixVersion(const unsigned int v) = 0;
+
+ // methods provided by MyMoneyKeyValueContainer
+ virtual void setValue(const QString& key, const QString& value) = 0;
+ virtual const QString value(const QString& key) const = 0;
+ virtual void deletePair(const QString& key) = 0;
+
+ /**
+ * This method is used to duplicate an IMyMoneyStorage object and return
+ * a pointer to the newly created copy. The caller of this method is the
+ * new owner of the object and must destroy it.
+ */
+ virtual IMyMoneyStorage const * duplicate(void) = 0;
+
+ /**
+ * This method is used to create a new account
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account MyMoneyAccount filled with data
+ */
+ virtual void addAccount(MyMoneyAccount& account) = 0;
+
+ /**
+ * This method is used to add one account as sub-ordinate to another
+ * (parent) account. The objects that are passed will be modified
+ * accordingly.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param parent parent account the account should be added to
+ * @param account the account to be added
+ */
+ virtual void addAccount(MyMoneyAccount& parent, MyMoneyAccount& account) = 0;
+
+ /**
+ * This method is used to create a new payee
+ *
+ * An exception will be thrown upon error conditions
+ *
+ * @param payee MyMoneyPayee reference to payee information
+ */
+ virtual void addPayee(MyMoneyPayee& payee) = 0;
+
+ /**
+ * This method is used to retrieve information about a payee
+ * An exception will be thrown upon error conditions.
+ *
+ * @param id QString reference to id of payee
+ *
+ * @return MyMoneyPayee object of payee
+ */
+ virtual const MyMoneyPayee payee(const QString& id) const = 0;
+
+ /**
+ * This method is used to retrieve the id to a corresponding
+ * name of a payee/receiver.
+ * An exception will be thrown upon error conditions.
+ *
+ * @param payee QString reference to name of payee
+ *
+ * @return MyMoneyPayee object of payee
+ */
+ virtual const MyMoneyPayee payeeByName(const QString& payee) const = 0;
+
+ /**
+ * This method is used to modify an existing payee
+ *
+ * An exception will be thrown upon error conditions
+ *
+ * @param payee MyMoneyPayee reference to payee information
+ */
+ virtual void modifyPayee(const MyMoneyPayee& payee) = 0;
+
+ /**
+ * This method is used to remove an existing payee
+ *
+ * An exception will be thrown upon error conditions
+ *
+ * @param payee MyMoneyPayee reference to payee information
+ */
+ virtual void removePayee(const MyMoneyPayee& payee) = 0;
+
+ /**
+ * This method returns a list of the payees
+ * inside a MyMoneyStorage object
+ *
+ * @return QValueList<MyMoneyPayee> containing the payee information
+ */
+ virtual const QValueList<MyMoneyPayee> payeeList(void) const = 0;
+
+ /**
+ * Returns the account addressed by it's id.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param id id of the account to locate.
+ * @return reference to MyMoneyAccount object. An exception is thrown
+ * if the id is unknown
+ */
+ virtual const MyMoneyAccount account(const QString& id) const = 0;
+
+ /**
+ * This method is used to check whether a given
+ * account id references one of the standard accounts or not.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param id account id
+ * @return true if account-id is one of the standards, false otherwise
+ */
+ virtual bool isStandardAccount(const QString& id) const = 0;
+
+ /**
+ * This method is used to set the name for the specified standard account
+ * within the storage area. An exception will be thrown, if an error
+ * occurs
+ *
+ * @param id QString reference to one of the standard accounts.
+ * @param name QString reference to the name to be set
+ *
+ */
+ virtual void setAccountName(const QString& id, const QString& name) = 0;
+
+ /**
+ * Adds an institution to the storage. A
+ * respective institution-ID will be generated within this record.
+ * The ID is stored as QString in the object passed as argument.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param institution The complete institution information in a
+ * MyMoneyInstitution object
+ */
+ virtual void addInstitution(MyMoneyInstitution& institution) = 0;
+
+ /**
+ * Adds a transaction to the file-global transaction pool. A respective
+ * transaction-ID will be generated within this record. The ID is stored
+ * QString with the object.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param transaction reference to the transaction
+ * @param skipAccountUpdate if set, the transaction lists of the accounts
+ * referenced in the splits are not updated. This is used for
+ * bulk loading a lot of transactions but not during normal operation
+ */
+ virtual void addTransaction(MyMoneyTransaction& transaction, const bool skipAccountUpdate = false) = 0;
+
+ /**
+ * This method is used to determince, if the account with the
+ * given ID is referenced by any split in m_transactionList.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param id id of the account to be checked for
+ * @return true if account is referenced, false otherwise
+ */
+ virtual bool hasActiveSplits(const QString& id) const = 0;
+
+ /**
+ * This method is used to return the actual balance of an account
+ * without it's sub-ordinate accounts. If a @p date is presented,
+ * the balance at the beginning of this date (not including any
+ * transaction on this date) is returned. Otherwise all recorded
+ * transactions are included in the balance.
+ *
+ * @param id id of the account in question
+ * @param date return balance for specific date
+ * @return balance of the account as MyMoneyMoney object
+ */
+ virtual const MyMoneyMoney balance(const QString& id, const QDate& date) const= 0;
+
+ /**
+ * This method is used to return the actual balance of an account
+ * including it's sub-ordinate accounts. If a @p date is presented,
+ * the balance at the beginning of this date (not including any
+ * transaction on this date) is returned. Otherwise all recorded
+ * transactions are included in the balance.
+ *
+ * @param id id of the account in question
+ * @param date return balance for specific date
+ * @return balance of the account as MyMoneyMoney object
+ */
+ virtual const MyMoneyMoney totalBalance(const QString& id, const QDate& date) const = 0;
+
+ /**
+ * Returns the institution of a given ID
+ *
+ * @param id id of the institution to locate
+ * @return MyMoneyInstitution object filled with data. If the institution
+ * could not be found, an exception will be thrown
+ */
+ virtual const MyMoneyInstitution institution(const QString& id) const = 0;
+
+ /**
+ * This method returns an indicator if the storage object has been
+ * changed after it has last been saved to permanent storage.
+ *
+ * @return true if changed, false if not
+ */
+ virtual bool dirty(void) const = 0;
+
+ /**
+ * This method can be used by an external object to force the
+ * storage object to be dirty. This is used e.g. when an upload
+ * to an external destination failed but the previous storage
+ * to a local disk was ok.
+ */
+ virtual void setDirty(void) = 0;
+
+ /**
+ * This method returns the number of accounts currently known to this storage
+ * in the range 0..MAXUINT
+ *
+ * @return number of accounts currently known inside a MyMoneyFile object
+ */
+ virtual unsigned int accountCount(void) const = 0;
+
+ /**
+ * This method returns a list of the institutions
+ * inside a MyMoneyStorage object
+ *
+ * @return QValueList<MyMoneyInstitution> containing the
+ * institution information
+ */
+ virtual const QValueList<MyMoneyInstitution> institutionList(void) const = 0;
+
+ /**
+ * Modifies an already existing account in the file global account pool.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account reference to the new account information
+ * @param skipCheck allows to skip the builtin consistency checks
+ */
+ virtual void modifyAccount(const MyMoneyAccount& account, const bool skipCheck = false) = 0;
+
+ /**
+ * Modifies an already existing institution in the file global
+ * institution pool.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param institution The complete new institution information
+ */
+ virtual void modifyInstitution(const MyMoneyInstitution& institution) = 0;
+
+ /**
+ * This method is used to update a specific transaction in the
+ * transaction pool of the MyMoneyFile object
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param transaction reference to transaction to be changed
+ */
+ virtual void modifyTransaction(const MyMoneyTransaction& transaction) = 0;
+
+ /**
+ * This method re-parents an existing account
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account MyMoneyAccount reference to account to be re-parented
+ * @param parent MyMoneyAccount reference to new parent account
+ */
+ virtual void reparentAccount(MyMoneyAccount &account, MyMoneyAccount& parent) = 0;
+
+ /**
+ * This method is used to remove a transaction from the transaction
+ * pool (journal).
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param transaction const reference to transaction to be deleted
+ */
+ virtual void removeTransaction(const MyMoneyTransaction& transaction) = 0;
+
+ /**
+ * This method returns the number of transactions currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @param account QString reference to account id. If account is empty
+ + all transactions (the journal) will be counted. If account
+ * is not empty it returns the number of transactions
+ * that have splits in this account.
+ *
+ * @return number of transactions in journal/account
+ */
+ virtual unsigned int transactionCount(const QString& account = QString()) const = 0;
+
+ /**
+ * This method returns a QMap filled with the number of transactions
+ * per account. The account id serves as index into the map. If one
+ * needs to have all transactionCounts() for many accounts, this method
+ * is faster than calling transactionCount(const QString& account) many
+ * times.
+ *
+ * @return QMap with numbers of transactions per account
+ */
+ virtual const QMap<QString, unsigned long> transactionCountMap(void) const = 0;
+
+ /**
+ * This method is used to pull a list of transactions from the file
+ * global transaction pool. It returns all those transactions
+ * that match the filter passed as argument. If the filter is empty,
+ * the whole journal will be returned.
+ * The list returned is sorted according to the transactions posting date.
+ * If more than one transaction exists for the same date, the order among
+ * them is undefined.
+ *
+ * @param filter MyMoneyTransactionFilter object with the match criteria
+ *
+ * @return set of transactions in form of a QValueList<MyMoneyTransaction>
+ */
+ virtual const QValueList<MyMoneyTransaction> transactionList(MyMoneyTransactionFilter& filter) const = 0;
+
+ virtual void transactionList(QValueList<MyMoneyTransaction>& list, MyMoneyTransactionFilter& filter) const = 0;
+
+ virtual void transactionList(QValueList<QPair<MyMoneyTransaction, MyMoneySplit> >& list, MyMoneyTransactionFilter& filter) const = 0;
+
+ /**
+ * Deletes an existing account from the file global account pool
+ * This method only allows to remove accounts that are not
+ * referenced by any split. Use moveSplits() to move splits
+ * to another account. An exception is thrown in case of a
+ * problem.
+ *
+ * @param account reference to the account to be deleted.
+ */
+ virtual void removeAccount(const MyMoneyAccount& account) = 0;
+
+ /**
+ * Deletes an existing institution from the file global institution pool
+ * Also modifies the accounts that reference this institution as
+ * their institution.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param institution institution to be deleted.
+ */
+ virtual void removeInstitution(const MyMoneyInstitution& institution) = 0;
+
+ /**
+ * This method is used to extract a transaction from the file global
+ * transaction pool through an id. In case of an invalid id, an
+ * exception will be thrown.
+ *
+ * @param id id of transaction as QString.
+ * @return reference to the requested transaction
+ */
+ virtual const MyMoneyTransaction transaction(const QString& id) const = 0;
+
+ /**
+ * This method is used to extract a transaction from the file global
+ * transaction pool through an index into an account.
+ *
+ * @param account id of the account as QString
+ * @param idx number of transaction in this account
+ * @return reference to MyMoneyTransaction object
+ */
+ virtual const MyMoneyTransaction transaction(const QString& account, const int idx) const = 0;
+
+ /**
+ * This method returns the number of institutions currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @return number of institutions known to file
+ */
+ virtual unsigned int institutionCount(void) const = 0;
+
+ /**
+ * This method returns a list of accounts inside the storage object.
+ *
+ * @param list reference to QValueList receiving the account objects
+ *
+ * @note The standard accounts will not be returned
+ */
+ virtual void accountList(QValueList<MyMoneyAccount>& list) const = 0;
+
+ /**
+ * This method is used to return the standard liability account
+ * @return MyMoneyAccount liability account(group)
+ */
+ virtual const MyMoneyAccount liability(void) const = 0;
+
+ /**
+ * This method is used to return the standard asset account
+ * @return MyMoneyAccount asset account(group)
+ */
+ virtual const MyMoneyAccount asset(void) const = 0;
+
+ /**
+ * This method is used to return the standard expense account
+ * @return MyMoneyAccount expense account(group)
+ */
+ virtual const MyMoneyAccount expense(void) const = 0;
+
+ /**
+ * This method is used to return the standard income account
+ * @return MyMoneyAccount income account(group)
+ */
+ virtual const MyMoneyAccount income(void) const = 0;
+
+ /**
+ * This method is used to return the standard equity account
+ * @return MyMoneyAccount equity account(group)
+ */
+ virtual const MyMoneyAccount equity(void) const = 0;
+
+ /**
+ * This method is used to create a new security object. The ID will be created
+ * automatically. The object passed with the parameter @p security is modified
+ * to contain the assigned id.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param security MyMoneySecurity filled with data
+ */
+ virtual void addSecurity(MyMoneySecurity& security) = 0;
+
+ /**
+ * This method is used to modify an existing MyMoneySecurity
+ * object.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param security reference to the MyMoneySecurity object to be updated
+ */
+ virtual void modifySecurity(const MyMoneySecurity& security) = 0;
+
+ /**
+ * This method is used to remove an existing MyMoneySecurity object
+ * from the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param security reference to the MyMoneySecurity object to be removed
+ */
+ virtual void removeSecurity(const MyMoneySecurity& security) = 0;
+
+ /**
+ * This method is used to retrieve a single MyMoneySecurity object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneySecurity object
+ * @return MyMoneySecurity object
+ */
+ virtual const MyMoneySecurity security(const QString& id) const = 0;
+
+ /**
+ * This method returns a list of the security objects
+ * inside a MyMoneyStorage object
+ *
+ * @return QValueList<MyMoneySecurity> containing objects
+ */
+ virtual const QValueList<MyMoneySecurity> securityList(void) const = 0;
+
+ virtual void addPrice(const MyMoneyPrice& price) = 0;
+ virtual void removePrice(const MyMoneyPrice& price) = 0;
+ virtual const MyMoneyPrice price(const QString& fromId, const QString& toId, const QDate& date, const bool exactDate) const = 0;
+
+ /**
+ * This method returns a list of all prices.
+ *
+ * @return MyMoneyPriceList of all MyMoneyPrice objects.
+ */
+ virtual const MyMoneyPriceList priceList(void) const = 0;
+
+ /**
+ * This method is used to add a scheduled transaction to the engine.
+ * It must be sure, that the id of the object is not filled. When the
+ * method returns to the caller, the id will be filled with the
+ * newly created object id value.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param sched reference to the MyMoneySchedule object
+ */
+ virtual void addSchedule(MyMoneySchedule& sched) = 0;
+
+ /**
+ * This method is used to modify an existing MyMoneySchedule
+ * object. Therefor, the id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param sched const reference to the MyMoneySchedule object to be updated
+ */
+ virtual void modifySchedule(const MyMoneySchedule& sched) = 0;
+
+ /**
+ * This method is used to remove an existing MyMoneySchedule object
+ * from the engine. The id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param sched const reference to the MyMoneySchedule object to be updated
+ */
+ virtual void removeSchedule(const MyMoneySchedule& sched) = 0;
+
+ /**
+ * This method is used to retrieve a single MyMoneySchedule object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneySchedule object
+ * @return MyMoneySchedule object
+ */
+ virtual const MyMoneySchedule schedule(const QString& id) const = 0;
+
+ /**
+ * This method is used to extract a list of scheduled transactions
+ * according to the filter criteria passed as arguments.
+ *
+ * @param accountId only search for scheduled transactions that reference
+ * accound @p accountId. If accountId is the empty string,
+ * this filter is off. Default is @p QString().
+ * @param type only schedules of type @p type are searched for.
+ * See MyMoneySchedule::typeE for details.
+ * Default is MyMoneySchedule::TYPE_ANY
+ * @param occurence only schedules of occurence type @p occurance are searched for.
+ * See MyMoneySchedule::occurenceE for details.
+ * Default is MyMoneySchedule::OCCUR_ANY
+ * @param paymentType only schedules of payment method @p paymentType
+ * are searched for.
+ * See MyMoneySchedule::paymentTypeE for details.
+ * Default is MyMoneySchedule::STYPE_ANY
+ * @param startDate only schedules with payment dates after @p startDate
+ * are searched for. Default is all dates (QDate()).
+ * @param endDate only schedules with payment dates ending prior to @p endDate
+ * are searched for. Default is all dates (QDate()).
+ * @param overdue if true, only those schedules that are overdue are
+ * searched for. Default is false (all schedules will be returned).
+ *
+ * @return const QValueList<MyMoneySchedule> list of schedule objects.
+ */
+ virtual const QValueList<MyMoneySchedule> scheduleList(const QString& accountId = QString(),
+ const MyMoneySchedule::typeE type = MyMoneySchedule::TYPE_ANY,
+ const MyMoneySchedule::occurenceE occurence = MyMoneySchedule::OCCUR_ANY,
+ const MyMoneySchedule::paymentTypeE paymentType = MyMoneySchedule::STYPE_ANY,
+ const QDate& startDate = QDate(),
+ const QDate& endDate = QDate(),
+ const bool overdue = false) const = 0;
+
+ virtual const QValueList<MyMoneySchedule> scheduleListEx( int scheduleTypes,
+ int scheduleOcurrences,
+ int schedulePaymentTypes,
+ QDate startDate,
+ const QStringList& accounts=QStringList()) const = 0;
+
+ /**
+ * This method is used to add a new currency object to the engine.
+ * The ID of the object is the trading symbol, so there is no need for an additional
+ * ID since the symbol is guaranteed to be unique.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param currency reference to the MyMoneySecurity object
+ */
+ virtual void addCurrency(const MyMoneySecurity& currency) = 0;
+
+ /**
+ * This method is used to modify an existing MyMoneySecurity
+ * object.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param currency reference to the MyMoneyCurrency object
+ */
+ virtual void modifyCurrency(const MyMoneySecurity& currency) = 0;
+
+ /**
+ * This method is used to remove an existing MyMoneySecurity object
+ * from the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param currency reference to the MyMoneySecurity object
+ */
+ virtual void removeCurrency(const MyMoneySecurity& currency) = 0;
+
+ /**
+ * This method is used to retrieve a single MyMoneySecurity object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneySecurity object
+ * @return MyMoneyCurrency object
+ */
+ virtual const MyMoneySecurity currency(const QString& id) const = 0;
+
+ /**
+ * This method is used to retrieve the list of all currencies
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneySecurity objects representing a currency.
+ */
+ virtual const QValueList<MyMoneySecurity> currencyList(void) const = 0;
+
+ /**
+ * This method is used to retrieve the list of all reports
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneyReport objects.
+ */
+ virtual const QValueList<MyMoneyReport> reportList( void ) const = 0;
+
+ /**
+ * This method is used to add a new report to the engine.
+ * It must be sure, that the id of the object is not filled. When the
+ * method returns to the caller, the id will be filled with the
+ * newly created object id value.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param report reference to the MyMoneyReport object
+ */
+ virtual void addReport( MyMoneyReport& report ) = 0;
+
+ /**
+ * This method is used to modify an existing MyMoneyReport
+ * object. Therefor, the id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param report const reference to the MyMoneyReport object to be updated
+ */
+ virtual void modifyReport( const MyMoneyReport& report ) = 0;
+
+ /**
+ * This method returns the number of reports currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @return number of reports known to file
+ */
+ virtual unsigned countReports( void ) const = 0;
+
+ /**
+ * This method is used to retrieve a single MyMoneyReport object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneyReport object
+ * @return MyMoneyReport object
+ */
+ virtual const MyMoneyReport report( const QString& id ) const = 0;
+
+ /**
+ * This method is used to remove an existing MyMoneyReport object
+ * from the engine. The id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param report const reference to the MyMoneyReport object to be updated
+ */
+ virtual void removeReport(const MyMoneyReport& report) = 0;
+
+ /**
+ * This method is used to retrieve the list of all budgets
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneyBudget objects.
+ */
+ virtual const QValueList<MyMoneyBudget> budgetList( void ) const = 0;
+
+ /**
+ * This method is used to add a new budget to the engine.
+ * It must be sure, that the id of the object is not filled. When the
+ * method returns to the caller, the id will be filled with the
+ * newly created object id value.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param budget reference to the MyMoneyBudget object
+ */
+ virtual void addBudget( MyMoneyBudget& budget ) = 0;
+
+ /**
+ * This method is used to retrieve the id to a corresponding
+ * name of a budget
+ * An exception will be thrown upon error conditions.
+ *
+ * @param budget QString reference to name of budget
+ *
+ * @return MyMoneyBudget object of budget
+ */
+ virtual const MyMoneyBudget budgetByName(const QString& budget) const = 0;
+
+ /**
+ * This method is used to modify an existing MyMoneyBudget
+ * object. Therefor, the id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param budget const reference to the MyMoneyBudget object to be updated
+ */
+ virtual void modifyBudget( const MyMoneyBudget& budget ) = 0;
+
+ /**
+ * This method returns the number of budgets currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @return number of budgets known to file
+ */
+ virtual unsigned countBudgets( void ) const = 0;
+
+ /**
+ * This method is used to retrieve a single MyMoneyBudget object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneyBudget object
+ * @return MyMoneyBudget object
+ */
+ virtual MyMoneyBudget budget( const QString& id ) const = 0;
+
+ /**
+ * This method is used to remove an existing MyMoneyBudget object
+ * from the engine. The id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param budget const reference to the MyMoneyBudget object to be updated
+ */
+ virtual void removeBudget(const MyMoneyBudget& budget) = 0;
+
+
+
+ /**
+ * Clear all internal caches (used internally for performance measurements)
+ */
+ virtual void clearCache(void) = 0;
+
+ /**
+ * This method checks, if the given @p object is referenced
+ * by another engine object.
+ *
+ * @param obj const reference to object to be checked
+ * @param skipCheck MyMoneyFileBitArray with ReferenceCheckBits set for which
+ * the check should be skipped
+ *
+ * @retval false @p object is not referenced
+ * @retval true @p institution is referenced
+ */
+ virtual bool isReferenced(const MyMoneyObject& obj, const MyMoneyFileBitArray& skipCheck = MyMoneyFileBitArray()) const = 0;
+
+ /**
+ * This method is provided to allow closing of the database before logoff
+ */
+ virtual void close(void) = 0;
+
+ /**
+ * These methods have to be provided to allow transaction safe data handling.
+ */
+ virtual void startTransaction(void) = 0;
+ virtual bool commitTransaction(void) = 0;
+ virtual void rollbackTransaction(void) = 0;
+};
+
+#endif
diff --git a/kmymoney2/mymoney/storage/imymoneystorageformat.cpp b/kmymoney2/mymoney/storage/imymoneystorageformat.cpp
new file mode 100644
index 0000000..bc3bbb2
--- /dev/null
+++ b/kmymoney2/mymoney/storage/imymoneystorageformat.cpp
@@ -0,0 +1,31 @@
+/***************************************************************************
+ imymoneystorageformat.cpp - description
+ -------------------
+ begin : Sun Oct 27 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#include "imymoneystorageformat.h"
+
+IMyMoneyStorageFormat::IMyMoneyStorageFormat()
+{
+}
+
+IMyMoneyStorageFormat::~IMyMoneyStorageFormat()
+{
+}
diff --git a/kmymoney2/mymoney/storage/imymoneystorageformat.h b/kmymoney2/mymoney/storage/imymoneystorageformat.h
new file mode 100644
index 0000000..b045898
--- /dev/null
+++ b/kmymoney2/mymoney/storage/imymoneystorageformat.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ imymoneystorageformat.h - description
+ -------------------
+ begin : Sun Oct 27 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#ifndef IMYMONEYSTORAGEFORMAT_H
+#define IMYMONEYSTORAGEFORMAT_H
+
+
+/**
+ * @author Kevin Tambascio (ktambascio@yahoo.com)
+ */
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+class QString;
+class QIODevice;
+class QProgressDialog;
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+class IMyMoneySerialize;
+
+
+class IMyMoneyStorageFormat
+{
+public:
+ IMyMoneyStorageFormat();
+ virtual ~IMyMoneyStorageFormat();
+
+ enum fileVersionDirectionType {
+ Reading = 0, /**< version of file to be read */
+ Writing = 1 /**< version to be used when writing a file */
+ };
+
+ virtual void readFile(QIODevice* qf, IMyMoneySerialize* storage) = 0;
+ // virtual void readStream(QDataStream& s, IMyMoneySerialize* storage) = 0;
+
+ virtual void writeFile(QIODevice* qf, IMyMoneySerialize* storage) = 0;
+ //virtual void writeStream(QDataStream& s, IMyMoneySerialize* storage) = 0;
+
+ virtual void setProgressCallback(void(*callback)(int, int, const QString&)) = 0;
+ /**
+ * This member is used to store the file version information
+ * obtained while reading a file.
+ */
+ static unsigned int fileVersionRead;
+
+ /**
+ * This member is used to store the file version information
+ * to be used when writing a file.
+ */
+ static unsigned int fileVersionWrite;
+};
+
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneydatabasemgr.cpp b/kmymoney2/mymoney/storage/mymoneydatabasemgr.cpp
new file mode 100644
index 0000000..e845094
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneydatabasemgr.cpp
@@ -0,0 +1,1880 @@
+/***************************************************************************
+ mymoneydatabasemgr.cpp
+ -------------------
+ begin : June 5 2007
+ copyright : (C) 2007 by Fernando Vilas
+ email : Fernando Vilas <fvilas@iname.com>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#include <typeinfo>
+#include <algorithm>
+
+#include "mymoneydatabasemgr.h"
+#include "../mymoneytransactionfilter.h"
+#include "../mymoneycategory.h"
+
+#define TRY try {
+#define CATCH } catch (MyMoneyException *e) {
+#define PASS } catch (MyMoneyException *e) { throw; }
+
+MyMoneyDatabaseMgr::MyMoneyDatabaseMgr() :
+m_creationDate (QDate::currentDate ()),
+m_lastModificationDate (QDate::currentDate ()),
+m_sql (0)
+{ }
+
+MyMoneyDatabaseMgr::~MyMoneyDatabaseMgr()
+{ }
+
+ // general get functions
+const MyMoneyPayee MyMoneyDatabaseMgr::user(void) const
+{ return m_user; }
+
+const QDate MyMoneyDatabaseMgr::creationDate(void) const
+{ return m_creationDate; }
+
+const QDate MyMoneyDatabaseMgr::lastModificationDate(void) const
+{ return m_lastModificationDate; }
+
+unsigned int MyMoneyDatabaseMgr::currentFixVersion(void) const
+{ return CURRENT_FIX_VERSION; }
+
+unsigned int MyMoneyDatabaseMgr::fileFixVersion(void) const
+{ return m_fileFixVersion; }
+
+ // general set functions
+void MyMoneyDatabaseMgr::setUser(const MyMoneyPayee& user)
+{
+ m_user = user;
+ if (m_sql != 0) m_sql->modifyUserInfo(user);
+}
+
+void MyMoneyDatabaseMgr::setFileFixVersion(const unsigned int v)
+{ m_fileFixVersion = v; }
+
+ // methods provided by MyMoneyKeyValueContainer
+const QString MyMoneyDatabaseMgr::value(const QString& key) const
+{
+ return MyMoneyKeyValueContainer::value(key);
+}
+
+void MyMoneyDatabaseMgr::setValue(const QString& key, const QString& val)
+{
+ MyMoneyKeyValueContainer::setValue(key, val);
+}
+
+void MyMoneyDatabaseMgr::deletePair(const QString& key)
+{
+ MyMoneyKeyValueContainer::deletePair(key);
+}
+
+const QMap<QString, QString> MyMoneyDatabaseMgr::pairs(void) const
+{
+ return MyMoneyKeyValueContainer::pairs();
+}
+
+void MyMoneyDatabaseMgr::setPairs(const QMap<QString, QString>& list)
+{
+ MyMoneyKeyValueContainer::setPairs(list);
+}
+
+MyMoneyDatabaseMgr const * MyMoneyDatabaseMgr::duplicate(void)
+{
+ MyMoneyDatabaseMgr* that = new MyMoneyDatabaseMgr();
+ *that = *this;
+ return that;
+}
+
+void MyMoneyDatabaseMgr::addAccount(MyMoneyAccount& account)
+{
+ if (m_sql) {
+ // create the account.
+ MyMoneyAccount newAccount(nextAccountID(), account);
+
+ m_sql->addAccount(newAccount);
+ account = newAccount;
+ }
+}
+
+void MyMoneyDatabaseMgr::addAccount(MyMoneyAccount& parent, MyMoneyAccount& account)
+{
+ QMap<QString, MyMoneyAccount> accountList;
+ QStringList accountIdList;
+ QMap<QString, MyMoneyAccount>::ConstIterator theParent;
+ QMap<QString, MyMoneyAccount>::ConstIterator theChild;
+
+ accountIdList << parent.id() << account.id();
+ startTransaction();
+ accountList = m_sql->fetchAccounts(accountIdList, true);
+
+ theParent = accountList.find(parent.id());
+ if(theParent == accountList.end()) {
+ QString msg = "Unknown parent account '";
+ msg += parent.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ theChild = accountList.find(account.id());
+ if(theChild == accountList.end()) {
+ QString msg = "Unknown child account '";
+ msg += account.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ MyMoneyAccount acc = *theParent;
+ acc.addAccountId(account.id());
+ parent = acc;
+
+ acc = *theChild;
+ acc.setParentAccountId(parent.id());
+ account = acc;
+
+//FIXME: MyMoneyBalanceCacheItem balance;
+//FIXME: m_balanceCache[account.id()] = balance;
+
+ m_sql->modifyAccount(parent);
+ m_sql->modifyAccount(account);
+ commitTransaction();
+}
+
+void MyMoneyDatabaseMgr::addPayee(MyMoneyPayee& payee)
+{
+ if (m_sql) {
+ // create the payee
+ MyMoneyPayee newPayee(nextPayeeID(), payee);
+
+ m_sql->addPayee(newPayee);
+ payee = newPayee;
+ }
+}
+
+const MyMoneyPayee MyMoneyDatabaseMgr::payee(const QString& id) const
+{
+ QMap<QString, MyMoneyPayee>::ConstIterator it;
+ QMap<QString, MyMoneyPayee> payeeList = m_sql->fetchPayees(QString(id));
+ it = payeeList.find(id);
+ if(it == payeeList.end())
+ throw new MYMONEYEXCEPTION("Unknown payee '" + id + "'");
+
+ return *it;
+}
+
+const MyMoneyPayee MyMoneyDatabaseMgr::payeeByName(const QString& payee) const
+{
+ if(payee.isEmpty())
+ return MyMoneyPayee::null;
+
+ QMap<QString, MyMoneyPayee> payeeList;
+
+ TRY
+ payeeList = m_sql->fetchPayees();
+ PASS
+
+ QMap<QString, MyMoneyPayee>::ConstIterator it_p;
+
+ for(it_p = payeeList.begin(); it_p != payeeList.end(); ++it_p) {
+ if((*it_p).name() == payee) {
+ return *it_p;
+ }
+ }
+
+ throw new MYMONEYEXCEPTION("Unknown payee '" + payee + "'");
+}
+
+void MyMoneyDatabaseMgr::modifyPayee(const MyMoneyPayee& payee)
+{
+ QMap<QString, MyMoneyPayee> payeeList = m_sql->fetchPayees(QString(payee.id()), true);
+ QMap<QString, MyMoneyPayee>::ConstIterator it;
+
+ it = payeeList.find(payee.id());
+ if(it == payeeList.end()) {
+ QString msg = "Unknown payee '" + payee.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_sql->modifyPayee(payee);
+}
+
+void MyMoneyDatabaseMgr::removePayee(const MyMoneyPayee& payee)
+{
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+ QMap<QString, MyMoneySchedule>::ConstIterator it_s;
+ QMap<QString, MyMoneyPayee> payeeList = m_sql->fetchPayees(QString(payee.id()));
+ QMap<QString, MyMoneyPayee>::ConstIterator it_p;
+
+ it_p = payeeList.find(payee.id());
+ if(it_p == payeeList.end()) {
+ QString msg = "Unknown payee '" + payee.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ // scan all transactions to check if the payee is still referenced
+ QMap<QString, MyMoneyTransaction> transactionList = m_sql->fetchTransactions(); // make sure they're all here
+ for(it_t = transactionList.begin(); it_t != transactionList.end(); ++it_t) {
+ if((*it_t).hasReferenceTo(payee.id())) {
+ throw new MYMONEYEXCEPTION(QString("Cannot remove payee that is still referenced to a %1").arg("transaction"));
+ }
+ }
+
+ // check referential integrity in schedules
+ QMap<QString, MyMoneySchedule> scheduleList = m_sql->fetchSchedules(); // make sure they're all here
+ for(it_s = scheduleList.begin(); it_s != scheduleList.end(); ++it_s) {
+ if((*it_s).hasReferenceTo(payee.id())) {
+ throw new MYMONEYEXCEPTION(QString("Cannot remove payee that is still referenced to a %1").arg("schedule"));
+ }
+ }
+ // remove any reference to report and/or budget
+ removeReferences(payee.id());
+
+ m_sql->removePayee(payee);
+}
+
+const QValueList<MyMoneyPayee> MyMoneyDatabaseMgr::payeeList(void) const
+{
+ if (m_sql)
+ return m_sql->fetchPayees().values();
+ else
+ return QValueList<MyMoneyPayee> ();
+}
+
+const MyMoneyAccount MyMoneyDatabaseMgr::account(const QString& id) const
+{
+ if (m_sql)
+ {
+ QMap <QString, MyMoneyAccount> accountList = m_sql->fetchAccounts(QString(id));
+ QMap <QString, MyMoneyAccount>::ConstIterator pos = accountList.find(id);
+
+ // locate the account and if present, return it's data
+ if(pos != accountList.end())
+ return *pos;
+ }
+
+ // throw an exception, if it does not exist
+ QString msg = "Unknown account id '" + id + "'";
+ throw new MYMONEYEXCEPTION(msg);
+}
+
+bool MyMoneyDatabaseMgr::isStandardAccount(const QString& id) const
+{
+ return id == STD_ACC_LIABILITY
+ || id == STD_ACC_ASSET
+ || id == STD_ACC_EXPENSE
+ || id == STD_ACC_INCOME
+ || id == STD_ACC_EQUITY;
+}
+
+void MyMoneyDatabaseMgr::setAccountName(const QString& id, const QString& name)
+{
+ if(!isStandardAccount(id))
+ throw new MYMONEYEXCEPTION("Only standard accounts can be modified using setAccountName()");
+
+ if (m_sql) {
+ startTransaction();
+ MyMoneyAccount acc = m_sql->fetchAccounts(QString(id), true) [id];
+ acc.setName(name);
+ m_sql->modifyAccount(acc);
+ commitTransaction();
+ }
+}
+
+void MyMoneyDatabaseMgr::addInstitution(MyMoneyInstitution& institution)
+{
+ if (m_sql) {
+ MyMoneyInstitution newInstitution(nextInstitutionID(), institution);
+
+ // mark file as changed
+ m_sql->addInstitution (newInstitution);
+
+ // return new data
+ institution = newInstitution;
+ }
+}
+
+const QString MyMoneyDatabaseMgr::nextPayeeID(void)
+{
+ QString id;
+ if (m_sql) {
+ id.setNum(ulong(m_sql->incrementPayeeId()));
+ id = "P" + id.rightJustify(PAYEE_ID_SIZE, '0');
+ }
+ return id;
+}
+
+const QString MyMoneyDatabaseMgr::nextInstitutionID(void)
+{
+ QString id;
+ if (m_sql) {
+ id.setNum(ulong(m_sql->incrementInstitutionId()));
+ id = "I" + id.rightJustify(INSTITUTION_ID_SIZE, '0');
+ }
+ return id;
+}
+
+const QString MyMoneyDatabaseMgr::nextAccountID(void)
+{
+ QString id;
+ if (m_sql) {
+ id.setNum(ulong(m_sql->incrementAccountId()));
+ id = "A" + id.rightJustify(ACCOUNT_ID_SIZE, '0');
+ }
+ return id;
+}
+
+const QString MyMoneyDatabaseMgr::nextBudgetID(void)
+{
+ QString id;
+ if (m_sql) {
+ id.setNum(ulong(m_sql->incrementBudgetId()));
+ id = "B" + id.rightJustify(BUDGET_ID_SIZE, '0');
+ }
+ return id;
+}
+
+const QString MyMoneyDatabaseMgr::nextReportID(void)
+{
+ QString id;
+ if (m_sql) {
+ id.setNum(ulong(m_sql->incrementReportId()));
+ id = "R" + id.rightJustify(REPORT_ID_SIZE, '0');
+ }
+ return id;
+}
+
+const QString MyMoneyDatabaseMgr::nextTransactionID(void)
+{
+ QString id;
+ if (m_sql) {
+ id.setNum(ulong(m_sql->incrementTransactionId()));
+ id = "T" + id.rightJustify(TRANSACTION_ID_SIZE, '0');
+ }
+ return id;
+}
+
+const QString MyMoneyDatabaseMgr::nextScheduleID(void)
+{
+ QString id;
+ if (m_sql) {
+ id.setNum(ulong(m_sql->incrementScheduleId()));
+ id = "SCH" + id.rightJustify(SCHEDULE_ID_SIZE, '0');
+ }
+ return id;
+}
+
+const QString MyMoneyDatabaseMgr::nextSecurityID(void)
+{
+ QString id;
+ if (m_sql) {
+ id.setNum(ulong(m_sql->incrementSecurityId()));
+ id = "E" + id.rightJustify(SECURITY_ID_SIZE, '0');
+ }
+ return id;
+}
+
+void MyMoneyDatabaseMgr::addTransaction(MyMoneyTransaction& transaction, const bool skipAccountUpdate)
+{
+ // perform some checks to see that the transaction stuff is OK. For
+ // now we assume that
+ // * no ids are assigned
+ // * the date valid (must not be empty)
+ // * the referenced accounts in the splits exist
+
+ // first perform all the checks
+ if(!transaction.id().isEmpty())
+ throw new MYMONEYEXCEPTION("transaction already contains an id");
+ if(!transaction.postDate().isValid())
+ throw new MYMONEYEXCEPTION("invalid post date");
+
+ // now check the splits
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s) {
+ // the following lines will throw an exception if the
+ // account or payee do not exist
+ account((*it_s).accountId());
+ if(!(*it_s).payeeId().isEmpty())
+ payee((*it_s).payeeId());
+ }
+
+ MyMoneyTransaction newTransaction(nextTransactionID(), transaction);
+ QString key = newTransaction.uniqueSortKey();
+
+ m_sql->addTransaction(newTransaction);
+
+ transaction = newTransaction;
+
+ // adjust the balance of all affected accounts
+ for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s) {
+ MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId());
+ acc.adjustBalance((*it_s));
+ if(!skipAccountUpdate) {
+ acc.touch();
+//FIXME: invalidateBalanceCache(acc.id());
+ }
+ m_sql->modifyAccount(acc);
+ }
+}
+
+bool MyMoneyDatabaseMgr::hasActiveSplits(const QString& id) const
+{
+ QMap<QString, MyMoneyTransaction>::ConstIterator it;
+
+ MyMoneyTransactionFilter f(id);
+ QMap<QString, MyMoneyTransaction> transactionList = m_sql->fetchTransactions(f);
+
+ for(it = transactionList.begin(); it != transactionList.end(); ++it) {
+ if((*it).accountReferenced(id)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+ /**
+ * This method is used to return the actual balance of an account
+ * without it's sub-ordinate accounts. If a @p date is presented,
+ * the balance at the beginning of this date (not including any
+ * transaction on this date) is returned. Otherwise all recorded
+ * transactions are included in the balance.
+ *
+ * @param id id of the account in question
+ * @param date return balance for specific date
+ * @return balance of the account as MyMoneyMoney object
+ */
+//const MyMoneyMoney MyMoneyDatabaseMgr::balance(const QString& id, const QDate& date);
+
+const MyMoneyMoney MyMoneyDatabaseMgr::totalBalance(const QString& id, const QDate& date) const
+{
+ QStringList accounts;
+ QStringList::ConstIterator it_a;
+
+ MyMoneyMoney result; //(balance(id, date));
+
+ accounts = MyMoneyFile::instance()->account(id).accountList();
+ for (it_a = accounts.begin(); it_a != accounts.end(); ++it_a) {
+ accounts += MyMoneyFile::instance()->account(*it_a).accountList();
+ }
+ std::list <QString> tempList (accounts.begin(), accounts.end());
+ tempList.sort();;
+ tempList.unique();
+
+ accounts = QStringList(tempList);
+
+ QMap<QString, MyMoneyMoney> balanceMap = m_sql->fetchBalance(accounts, date);
+ for (QMap<QString, MyMoneyMoney>::ConstIterator it_b = balanceMap.begin(); it_b != balanceMap.end(); ++it_b) {
+ result += it_b.data();
+ }
+
+ return result;
+}
+
+const MyMoneyInstitution MyMoneyDatabaseMgr::institution(const QString& id) const
+{
+ QMap<QString, MyMoneyInstitution>::ConstIterator pos;
+ QMap<QString, MyMoneyInstitution> institutionList = m_sql->fetchInstitutions(QString(id));
+
+ pos = institutionList.find(id);
+ if(pos != institutionList.end())
+ return *pos;
+ throw new MYMONEYEXCEPTION("unknown institution");
+}
+
+bool MyMoneyDatabaseMgr::dirty(void) const
+{ return false; }
+
+void MyMoneyDatabaseMgr::setDirty(void)
+{}
+
+unsigned int MyMoneyDatabaseMgr::accountCount(void) const
+{
+ return m_sql->getRecCount("kmmAccounts");
+}
+
+const QValueList<MyMoneyInstitution> MyMoneyDatabaseMgr::institutionList(void) const
+{
+ if (m_sql) {
+ return m_sql->fetchInstitutions().values();
+ } else {
+ return QValueList<MyMoneyInstitution> ();
+ }
+}
+
+void MyMoneyDatabaseMgr::modifyAccount(const MyMoneyAccount& account, const bool skipCheck)
+{
+ QMap<QString, MyMoneyAccount>::ConstIterator pos;
+
+ // locate the account in the file global pool
+ startTransaction();
+ QMap<QString, MyMoneyAccount> accountList = m_sql->fetchAccounts (QString(account.id()), true);
+ pos = accountList.find(account.id());
+ if(pos != accountList.end()) {
+ // check if the new info is based on the old one.
+ // this is the case, when the file and the id
+ // as well as the type are equal.
+ if(((*pos).parentAccountId() == account.parentAccountId()
+ && (*pos).accountType() == account.accountType())
+ || skipCheck == true) {
+ // make sure that all the referenced objects exist
+ if(!account.institutionId().isEmpty())
+ institution(account.institutionId());
+
+ QValueList<QString>::ConstIterator it_a;
+ for(it_a = account.accountList().begin(); it_a != account.accountList().end(); ++it_a) {
+ this->account(*it_a);
+ }
+
+ // update information in account list
+ //m_accountList.modify(account.id(), account);
+
+ // invalidate cached balance
+//FIXME: invalidateBalanceCache(account.id());
+
+ // mark file as changed
+ m_sql->modifyAccount(account);
+ commitTransaction();
+ } else {
+ rollbackTransaction();
+ throw new MYMONEYEXCEPTION("Invalid information for update");
+ }
+
+ } else {
+ rollbackTransaction();
+ throw new MYMONEYEXCEPTION("Unknown account id");
+ }
+}
+
+void MyMoneyDatabaseMgr::modifyInstitution(const MyMoneyInstitution& institution)
+{
+ QMap<QString, MyMoneyInstitution> institutionList = m_sql->fetchInstitutions(QString(institution.id()));
+ QMap<QString, MyMoneyInstitution>::ConstIterator pos;
+
+ // locate the institution in the file global pool
+ pos = institutionList.find(institution.id());
+ if(pos != institutionList.end()) {
+ m_sql->modifyInstitution(institution);
+ } else
+ throw new MYMONEYEXCEPTION("unknown institution");
+}
+
+ /**
+ * This method is used to update a specific transaction in the
+ * transaction pool of the MyMoneyFile object
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param transaction reference to transaction to be changed
+ */
+void MyMoneyDatabaseMgr::modifyTransaction(const MyMoneyTransaction& transaction)
+{
+ QMap<QString, bool> modifiedAccounts;
+
+ // perform some checks to see that the transaction stuff is OK. For
+ // now we assume that
+ // * ids are assigned
+ // * the pointer to the MyMoneyFile object is not 0
+ // * the date valid (must not be empty)
+ // * the splits must have valid account ids
+
+ // first perform all the checks
+ if(transaction.id().isEmpty()
+// || transaction.file() != this
+ || !transaction.postDate().isValid())
+ throw new MYMONEYEXCEPTION("invalid transaction to be modified");
+
+ // now check the splits
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s) {
+ // the following lines will throw an exception if the
+ // account or payee do not exist
+ MyMoneyFile::instance()->account((*it_s).accountId());
+ if(!(*it_s).payeeId().isEmpty())
+ MyMoneyFile::instance()->payee((*it_s).payeeId());
+ }
+
+ // new data seems to be ok. find old version of transaction
+ // in our pool. Throw exception if unknown.
+// if(!m_transactionKeys.contains(transaction.id()))
+// throw new MYMONEYEXCEPTION("invalid transaction id");
+
+// QString oldKey = m_transactionKeys[transaction.id()];
+ QMap <QString, MyMoneyTransaction> transactionList = m_sql->fetchTransactions("('" + QString(transaction.id()) + "')");
+// if(transactionList.size() != 1)
+// throw new MYMONEYEXCEPTION("invalid transaction key");
+
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+
+// it_t = transactionList.find(oldKey);
+ it_t = transactionList.begin();
+ if(it_t == transactionList.end())
+ throw new MYMONEYEXCEPTION("invalid transaction key");
+
+ // mark all accounts referenced in old and new transaction data
+ // as modified
+ QMap<QString, MyMoneyAccount> accountList = m_sql->fetchAccounts();
+ for(it_s = (*it_t).splits().begin(); it_s != (*it_t).splits().end(); ++it_s) {
+ MyMoneyAccount acc = accountList[(*it_s).accountId()];
+ acc.adjustBalance((*it_s), true);
+ acc.touch();
+//FIXME: invalidateBalanceCache(acc.id());
+ //m_accountList.modify(acc.id(), acc);
+ m_sql->modifyAccount(acc);
+ //modifiedAccounts[(*it_s).accountId()] = true;
+ }
+ for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s) {
+ MyMoneyAccount acc = accountList[(*it_s).accountId()];
+ acc.adjustBalance((*it_s));
+ acc.touch();
+//FIXME: invalidateBalanceCache(acc.id());
+ //m_accountList.modify(acc.id(), acc);
+ m_sql->modifyAccount(acc);
+ //modifiedAccounts[(*it_s).accountId()] = true;
+ }
+
+ // remove old transaction from lists
+// m_sql->removeTransaction(oldKey);
+
+ // add new transaction to lists
+ // QString newKey = transaction.uniqueSortKey();
+// m_sql->insertTransaction(newKey, transaction);
+ //m_transactionKeys.modify(transaction.id(), newKey);
+
+ // mark file as changed
+ m_sql->modifyTransaction(transaction);
+}
+
+void MyMoneyDatabaseMgr::reparentAccount(MyMoneyAccount &account, MyMoneyAccount& parent)
+{
+ if(account.accountType() == MyMoneyAccount::Stock && parent.accountType() != MyMoneyAccount::Investment)
+ throw new MYMONEYEXCEPTION("Cannot move a stock acocunt into a non-investment account");
+
+ QStringList accountIdList;
+ QMap<QString, MyMoneyAccount>::ConstIterator oldParent;
+ QMap<QString, MyMoneyAccount>::ConstIterator newParent;
+ QMap<QString, MyMoneyAccount>::ConstIterator childAccount;
+
+ // verify that accounts exist. If one does not,
+ // an exception is thrown
+ accountIdList << account.id() << parent.id();
+ MyMoneyDatabaseMgr::account(account.id());
+ MyMoneyDatabaseMgr::account(parent.id());
+
+ if(!account.parentAccountId().isEmpty()) {
+ accountIdList << account.parentAccountId();
+ }
+
+ startTransaction();
+ QMap<QString, MyMoneyAccount> accountList = m_sql->fetchAccounts(accountIdList, true);
+
+ if(!account.parentAccountId().isEmpty()) {
+ MyMoneyDatabaseMgr::account(account.parentAccountId());
+ oldParent = accountList.find(account.parentAccountId());
+ }
+
+ newParent = accountList.find(parent.id());
+ childAccount = accountList.find(account.id());
+
+ MyMoneyAccount acc;
+ if(!account.parentAccountId().isEmpty()) {
+ acc = (*oldParent);
+ acc.removeAccountId(account.id());
+ m_sql->modifyAccount(acc);
+ }
+
+ parent = (*newParent);
+ parent.addAccountId(account.id());
+
+ account = (*childAccount);
+ account.setParentAccountId(parent.id());
+
+ m_sql->modifyAccount(parent);
+ m_sql->modifyAccount(account);
+ commitTransaction();
+}
+
+void MyMoneyDatabaseMgr::removeTransaction(const MyMoneyTransaction& transaction)
+{
+ QMap<QString, bool> modifiedAccounts;
+
+ // first perform all the checks
+ if(transaction.id().isEmpty())
+ throw new MYMONEYEXCEPTION("invalid transaction to be deleted");
+
+ QMap<QString, QString>::ConstIterator it_k;
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+
+// it_k = m_transactionKeys.find(transaction.id());
+// if(it_k == m_transactionKeys.end())
+// throw new MYMONEYEXCEPTION("invalid transaction to be deleted");
+
+ QMap <QString, MyMoneyTransaction> transactionList = m_sql->fetchTransactions("('" + QString(transaction.id()) + "')");
+// it_t = transactionList.find(*it_k);
+ it_t = transactionList.begin();
+ if(it_t == transactionList.end())
+ throw new MYMONEYEXCEPTION("invalid transaction key");
+
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+
+ // scan the splits and collect all accounts that need
+ // to be updated after the removal of this transaction
+ QMap<QString, MyMoneyAccount> accountList = m_sql->fetchAccounts();
+ for(it_s = (*it_t).splits().begin(); it_s != (*it_t).splits().end(); ++it_s) {
+ MyMoneyAccount acc = accountList[(*it_s).accountId()];
+// modifiedAccounts[(*it_s).accountId()] = true;
+ acc.adjustBalance((*it_s), true);
+ acc.touch();
+ m_sql->modifyAccount(acc);
+//FIXME: invalidateBalanceCache(acc.id());
+ }
+
+ // FIXME: check if any split is frozen and throw exception
+
+ // remove the transaction from the two lists
+ //m_transactionList.remove(*it_k);
+// m_transactionKeys.remove(transaction.id());
+
+ // mark file as changed
+ m_sql->removeTransaction(transaction);
+}
+
+unsigned int MyMoneyDatabaseMgr::transactionCount(const QString& account) const
+{ return (m_sql->transactionCount(account)); }
+
+const QMap<QString, unsigned long> MyMoneyDatabaseMgr::transactionCountMap(void) const
+{ return (m_sql->transactionCountMap()); }
+
+const QValueList<MyMoneyTransaction> MyMoneyDatabaseMgr::transactionList(MyMoneyTransactionFilter& filter) const
+{
+ QValueList<MyMoneyTransaction> list;
+ transactionList(list, filter);
+ return list;
+}
+
+void MyMoneyDatabaseMgr::transactionList(QValueList<MyMoneyTransaction>& list, MyMoneyTransactionFilter& filter) const
+{
+ list.clear();
+
+ TRY
+ if (m_sql) list = m_sql->fetchTransactions(filter).values();
+ PASS
+}
+
+void MyMoneyDatabaseMgr::transactionList(QValueList<QPair<MyMoneyTransaction, MyMoneySplit> >& list, MyMoneyTransactionFilter& filter) const
+{
+ list.clear();
+ MyMoneyMap<QString, MyMoneyTransaction> transactionList;
+ TRY
+ if (m_sql) transactionList = m_sql->fetchTransactions(filter);
+ PASS
+
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+ QMap<QString, MyMoneyTransaction>::ConstIterator txEnd = transactionList.end();
+
+ for(it_t = transactionList.begin(); it_t != txEnd; ++it_t) {
+ if(filter.match(*it_t)) {
+ QValueList<MyMoneySplit>::const_iterator it_s;
+ for(it_s = filter.matchingSplits().begin(); it_s != filter.matchingSplits().end(); ++it_s) {
+ list.append(qMakePair(*it_t, *it_s));
+ }
+ }
+ }
+}
+
+void MyMoneyDatabaseMgr::removeAccount(const MyMoneyAccount& account)
+{
+ MyMoneyAccount parent;
+
+ // check that the account and it's parent exist
+ // this will throw an exception if the id is unknown
+ MyMoneyDatabaseMgr::account(account.id());
+ parent = MyMoneyDatabaseMgr::account(account.parentAccountId());
+
+ // check that it's not one of the standard account groups
+ if(isStandardAccount(account.id()))
+ throw new MYMONEYEXCEPTION("Unable to remove the standard account groups");
+
+ if(hasActiveSplits(account.id())) {
+ throw new MYMONEYEXCEPTION("Unable to remove account with active splits");
+ }
+
+ // re-parent all sub-ordinate accounts to the parent of the account
+ // to be deleted. First round check that all accounts exist, second
+ // round do the re-parenting.
+ QStringList::ConstIterator it;
+ for(it = account.accountList().begin(); it != account.accountList().end(); ++it) {
+ MyMoneyDatabaseMgr::account(*it);
+ }
+
+ // if one of the accounts did not exist, an exception had been
+ // thrown and we would not make it until here.
+
+ QStringList accountIdList;
+ accountIdList << parent.id() << account.id();
+ startTransaction();
+ QMap<QString, MyMoneyAccount> accountList = m_sql->fetchAccounts(accountIdList, true);
+
+ QMap<QString, MyMoneyAccount>::ConstIterator it_a;
+ QMap<QString, MyMoneyAccount>::ConstIterator it_p;
+
+ // locate the account in the file global pool
+
+ it_a = accountList.find(account.id());
+ if(it_a == accountList.end())
+ throw new MYMONEYEXCEPTION("Internal error: account not found in list");
+
+ it_p = accountList.find(parent.id());
+ if(it_p == accountList.end())
+ throw new MYMONEYEXCEPTION("Internal error: parent account not found in list");
+
+ if(!account.institutionId().isEmpty())
+ throw new MYMONEYEXCEPTION("Cannot remove account still attached to an institution");
+
+ // FIXME: check referential integrity for the account to be removed
+
+ // check if the new info is based on the old one.
+ // this is the case, when the file and the id
+ // as well as the type are equal.
+ if((*it_a).id() == account.id()
+ && (*it_a).accountType() == account.accountType()) {
+
+ // second round over sub-ordinate accounts: do re-parenting
+ // but only if the list contains at least one entry
+ // FIXME: move this logic to MyMoneyFile
+ if((*it_a).accountList().count() > 0) {
+ for(it = (*it_a).accountList().begin(); it != (*it_a).accountList().end(); ++it) {
+ MyMoneyAccount acc(MyMoneyDatabaseMgr::account(*it));
+ reparentAccount(acc, parent);//, false);
+ }
+ }
+ // remove account from parent's list
+ parent.removeAccountId(account.id());
+ m_sql->modifyAccount(parent);
+
+ // remove account from the global account pool
+ //m_accountList.remove(account.id());
+
+ // remove from balance list
+//FIXME: m_balanceCache.remove(account.id());
+//FIXME: invalidateBalanceCache(parent.id());
+
+ m_sql->removeAccount(account);
+ }
+ commitTransaction();
+}
+
+void MyMoneyDatabaseMgr::removeInstitution(const MyMoneyInstitution& institution)
+{
+ QMap<QString, MyMoneyInstitution> institutionList = m_sql->fetchInstitutions(QString(institution.id()));
+ QMap<QString, MyMoneyInstitution>::ConstIterator it_i;
+
+ it_i = institutionList.find(institution.id());
+ if(it_i != institutionList.end()) {
+ // mark file as changed
+ m_sql->removeInstitution(institution);
+ } else
+ throw new MYMONEYEXCEPTION("invalid institution");
+}
+
+const MyMoneyTransaction MyMoneyDatabaseMgr::transaction(const QString& id) const
+{
+ // get the full key of this transaction, throw exception
+ // if it's invalid (unknown)
+ //if(!m_transactionKeys.contains(id))
+ // throw new MYMONEYEXCEPTION("invalid transaction id");
+
+ // check if this key is in the list, throw exception if not
+ //QString key = m_transactionKeys[id];
+ QMap <QString, MyMoneyTransaction> transactionList = m_sql->fetchTransactions("('" + QString(id) + "')");
+
+ //there should only be one transaction in the map, if it was found, so check the size of the map
+ //return the first element.
+ //if(!transactionList.contains(key))
+ if(!transactionList.size())
+ throw new MYMONEYEXCEPTION("invalid transaction key");
+
+ return transactionList.begin().data();
+}
+
+const MyMoneyMoney MyMoneyDatabaseMgr::balance(const QString& id, const QDate& date) const
+{
+ QStringList idList;
+ idList.append(id);
+ QMap<QString,MyMoneyMoney> tempMap = m_sql->fetchBalance(idList, date);
+
+ MyMoneyMoney returnValue = tempMap[id];
+ if (returnValue != MyMoneyMoney()) {
+ return returnValue;
+ }
+
+//DEBUG
+ QDate date_ (date);
+ //if (date_ == QDate()) date_ = QDate::currentDate();
+// END DEBUG
+
+ MyMoneyMoney result(0);
+ MyMoneyAccount acc;
+ QMap<QString, MyMoneyAccount> accountList = m_sql->fetchAccounts(/*QString(id)*/);
+ //QMap<QString, MyMoneyAccount>::const_iterator accpos = accountList.find(id);
+ if (date_ != QDate()) qDebug ("request balance for %s at %s", id.data(), date_.toString(Qt::ISODate).latin1());
+// if(!date_.isValid() && MyMoneyFile::instance()->account(id).accountType() != MyMoneyAccount::Stock) {
+// if(accountList.find(id) != accountList.end())
+// return accountList[id].balance();
+// return MyMoneyMoney(0);
+// }
+ if(/*m_balanceCache[id].valid == false || date != m_balanceCacheDate) || */ m_sql != 0) {
+ QMap<QString, MyMoneyMoney> balances;
+ QMap<QString, MyMoneyMoney>::ConstIterator it_b;
+//FIXME: if (date != m_balanceCacheDate) {
+//FIXME: m_balanceCache.clear();
+//FIXME: m_balanceCacheDate = date;
+//FIXME: }
+
+ QValueList<MyMoneyTransaction>::ConstIterator it_t;
+ QValueList<MyMoneyTransaction>::ConstIterator txEnd;
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+
+ MyMoneyTransactionFilter filter;
+ filter.addAccount(id);
+ filter.setDateFilter(QDate(), date_);
+ filter.setReportAllSplits(false);
+ QValueList<MyMoneyTransaction> list = transactionList(filter);
+
+ txEnd = list.end();
+ for(it_t = list.begin(); it_t != txEnd; ++it_t) {
+ for(it_s = (*it_t).splits().begin(); it_s != (*it_t).splits().end(); ++it_s){
+ const QString aid = (*it_s).accountId();
+ if((*it_s).action() == MyMoneySplit::ActionSplitShares) {
+ balances[aid] = balances[aid] * (*it_s).shares();
+ } else {
+ balances[aid] += (*it_s).value((*it_t).commodity(), accountList[aid].currencyId());
+ }
+ }
+ }
+
+ // fill the found balances into the cache
+//FIXME: for(it_b = balances.begin(); it_b != balances.end(); ++it_b) {
+//FIXME: MyMoneyBalanceCacheItem balance(*it_b);
+//FIXME: m_balanceCache[it_b.key()] = balance;
+//FIXME: }
+
+ // fill all accounts w/o transactions to zero
+// if (m_sql != 0) {
+// QMap<QString, MyMoneyAccount>::ConstIterator it_a;
+// for(it_a = m_accountList.begin(); it_a != m_accountList.end(); ++it_a) {
+//FIXME: if(m_balanceCache[(*it_a).id()].valid == false) {
+//FIXME: MyMoneyBalanceCacheItem balance(MyMoneyMoney(0,1));
+//FIXME: m_balanceCache[(*it_a).id()] = balance;
+//FIXME: }
+// }
+// }
+
+ result = balances[id];
+
+ }
+
+//FIXME: if(m_balanceCache[id].valid == true)
+//FIXME: result = m_balanceCache[id].balance;
+//FIXME: else
+//FIXME: qDebug("Cache mishit should never happen at this point");
+
+ return result;
+}
+
+const MyMoneyTransaction MyMoneyDatabaseMgr::transaction(const QString& account, const int idx) const
+{
+/* removed with MyMoneyAccount::Transaction
+ QMap<QString, MyMoneyAccount>::ConstIterator acc;
+
+ // find account object in list, throw exception if unknown
+ acc = m_accountList.find(account);
+ if(acc == m_accountList.end())
+ throw new MYMONEYEXCEPTION("unknown account id");
+
+ // get the transaction info from the account
+ MyMoneyAccount::Transaction t = (*acc).transaction(idx);
+
+ // return the transaction, throw exception if not found
+ return transaction(t.transactionID());
+*/
+
+ // new implementation if the above code does not work anymore
+ QValueList<MyMoneyTransaction> list;
+ //MyMoneyAccount acc = m_accountList[account];
+ MyMoneyAccount acc = m_sql->fetchAccounts(QString(account)) [account];
+ MyMoneyTransactionFilter filter;
+
+ if(acc.accountGroup() == MyMoneyAccount::Income
+ || acc.accountGroup() == MyMoneyAccount::Expense)
+ filter.addCategory(account);
+ else
+ filter.addAccount(account);
+
+ transactionList(list, filter);
+ if(idx < 0 || idx >= static_cast<int> (list.count()))
+ throw new MYMONEYEXCEPTION("Unknown idx for transaction");
+
+ return transaction(list[idx].id());
+}
+
+unsigned int MyMoneyDatabaseMgr::institutionCount(void) const
+{
+ return m_sql->getRecCount("kmmInstitutions");
+}
+
+void MyMoneyDatabaseMgr::accountList(QValueList<MyMoneyAccount>& list) const
+{
+ QMap <QString, MyMoneyAccount> accountList;
+ if (m_sql) accountList = m_sql->fetchAccounts();
+ QMap<QString, MyMoneyAccount>::ConstIterator it;
+ QMap<QString, MyMoneyAccount>::ConstIterator accEnd = accountList.end();
+ for(it = accountList.begin(); it != accEnd; ++it) {
+ if(!isStandardAccount((*it).id())) {
+ list.append(*it);
+ }
+ }
+}
+
+const MyMoneyAccount MyMoneyDatabaseMgr::liability(void) const
+{ return MyMoneyFile::instance()->account(STD_ACC_LIABILITY); }
+
+const MyMoneyAccount MyMoneyDatabaseMgr::asset(void) const
+{ return MyMoneyFile::instance()->account(STD_ACC_ASSET); }
+
+const MyMoneyAccount MyMoneyDatabaseMgr::expense(void) const
+{ return MyMoneyFile::instance()->account(STD_ACC_EXPENSE); }
+
+const MyMoneyAccount MyMoneyDatabaseMgr::income(void) const
+{ return MyMoneyFile::instance()->account(STD_ACC_INCOME); }
+
+const MyMoneyAccount MyMoneyDatabaseMgr::equity(void) const
+{ return MyMoneyFile::instance()->account(STD_ACC_EQUITY); }
+
+void MyMoneyDatabaseMgr::addSecurity(MyMoneySecurity& security)
+{
+ // create the account
+ MyMoneySecurity newSecurity(nextSecurityID(), security);
+
+ m_sql->addSecurity(newSecurity);
+ security = newSecurity;
+}
+
+void MyMoneyDatabaseMgr::modifySecurity(const MyMoneySecurity& security)
+{
+ QMap<QString, MyMoneySecurity> securitiesList = m_sql->fetchSecurities(QString(security.id()), true);
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ it = securitiesList.find(security.id());
+ if(it == securitiesList.end())
+ {
+ QString msg = "Unknown security '";
+ msg += security.id() + "' during modifySecurity()";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_sql->modifySecurity(security);
+}
+
+void MyMoneyDatabaseMgr::removeSecurity(const MyMoneySecurity& security)
+{
+ QMap<QString, MyMoneySecurity> securitiesList = m_sql->fetchSecurities(QString(security.id()));
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ // FIXME: check referential integrity
+
+ it = securitiesList.find(security.id());
+ if(it == securitiesList.end())
+ {
+ QString msg = "Unknown security '";
+ msg += security.id() + "' during removeSecurity()";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_sql->removeSecurity(security);
+}
+
+const MyMoneySecurity MyMoneyDatabaseMgr::security(const QString& id) const
+{
+ QMap<QString, MyMoneySecurity> securitiesList = m_sql->fetchSecurities(QString(id));
+ QMap<QString, MyMoneySecurity>::ConstIterator it = securitiesList.find(id);
+ if(it != securitiesList.end())
+ {
+ return it.data();
+ }
+
+ return MyMoneySecurity();
+}
+
+const QValueList<MyMoneySecurity> MyMoneyDatabaseMgr::securityList(void) const
+{ return m_sql->fetchSecurities().values(); }
+
+void MyMoneyDatabaseMgr::addPrice(const MyMoneyPrice& price)
+{
+ MyMoneyPriceEntries::ConstIterator it;
+ MyMoneyPriceList priceList = m_sql->fetchPrices();
+ it = priceList[MyMoneySecurityPair(price.from(), price.to())].find(price.date());
+ // do not replace, if the information did not change.
+ if(it != priceList[MyMoneySecurityPair(price.from(), price.to())].end()) {
+ if((*it).rate((*it).to()) == price.rate(price.to())
+ && (*it).source() == price.source())
+ return;
+ }
+
+ m_sql->addPrice(price);
+}
+
+void MyMoneyDatabaseMgr::removePrice(const MyMoneyPrice& price)
+{
+ m_sql->removePrice(price);
+}
+
+const MyMoneyPrice MyMoneyDatabaseMgr::price(const QString& fromId, const QString& toId, const QDate& _date, const bool exactDate) const
+{
+ return m_sql->fetchSinglePrice(fromId, toId, _date, exactDate);
+}
+
+const MyMoneyPriceList MyMoneyDatabaseMgr::priceList(void) const
+{ return m_sql->fetchPrices(); }
+
+void MyMoneyDatabaseMgr::addSchedule(MyMoneySchedule& sched)
+{
+ // first perform all the checks
+ if(!sched.id().isEmpty())
+ throw new MYMONEYEXCEPTION("schedule already contains an id");
+
+ // The following will throw an exception when it fails
+ sched.validate(false);
+
+ if (m_sql) {
+ startTransaction();
+ sched = MyMoneySchedule (nextScheduleID(), sched);
+
+ m_sql->addSchedule(sched);
+ commitTransaction();
+ }
+}
+
+void MyMoneyDatabaseMgr::modifySchedule(const MyMoneySchedule& sched)
+{
+ QMap<QString, MyMoneySchedule> scheduleList = m_sql->fetchSchedules(QString(sched.id()));
+ QMap<QString, MyMoneySchedule>::ConstIterator it;
+
+ it = scheduleList.find(sched.id());
+ if(it == scheduleList.end()) {
+ QString msg = "Unknown schedule '" + sched.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_sql->modifySchedule(sched);
+}
+
+void MyMoneyDatabaseMgr::removeSchedule(const MyMoneySchedule& sched)
+{
+ QMap<QString, MyMoneySchedule> scheduleList = m_sql->fetchSchedules(QString(sched.id()));
+ QMap<QString, MyMoneySchedule>::ConstIterator it;
+
+ it = scheduleList.find(sched.id());
+ if(it == scheduleList.end()) {
+ QString msg = "Unknown schedule '" + sched.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ // FIXME: check referential integrity for loan accounts
+
+ m_sql->removeSchedule(sched);
+}
+
+const MyMoneySchedule MyMoneyDatabaseMgr::schedule(const QString& id) const
+{
+ QMap<QString, MyMoneySchedule> scheduleList = m_sql->fetchSchedules(QString(id));
+ QMap<QString, MyMoneySchedule>::ConstIterator pos;
+
+ // locate the schedule and if present, return it's data
+ pos = scheduleList.find(id);
+ if(pos != scheduleList.end())
+ return (*pos);
+
+ // throw an exception, if it does not exist
+ QString msg = "Unknown schedule id '" + id + "'";
+ throw new MYMONEYEXCEPTION(msg);
+}
+
+const QValueList<MyMoneySchedule> MyMoneyDatabaseMgr::scheduleList(const QString& accountId,
+ const MyMoneySchedule::typeE type,
+ const MyMoneySchedule::occurenceE occurence,
+ const MyMoneySchedule::paymentTypeE paymentType,
+ const QDate& startDate,
+ const QDate& endDate,
+ const bool overdue) const
+{
+ QMap<QString, MyMoneySchedule> scheduleList;
+ if (m_sql) scheduleList = m_sql->fetchSchedules();
+ QMap<QString, MyMoneySchedule>::ConstIterator pos;
+ QValueList<MyMoneySchedule> list;
+
+ // qDebug("scheduleList()");
+
+ for(pos = scheduleList.begin(); pos != scheduleList.end(); ++pos) {
+ // qDebug(" '%s'", (*pos).id().data());
+
+ if(type != MyMoneySchedule::TYPE_ANY) {
+ if(type != (*pos).type()) {
+ continue;
+ }
+ }
+
+ if(occurence != MyMoneySchedule::OCCUR_ANY) {
+ if(occurence != (*pos).occurence()) {
+ continue;
+ }
+ }
+
+ if(paymentType != MyMoneySchedule::STYPE_ANY) {
+ if(paymentType != (*pos).paymentType()) {
+ continue;
+ }
+ }
+
+ if(!accountId.isEmpty()) {
+ MyMoneyTransaction t = (*pos).transaction();
+ QValueList<MyMoneySplit>::ConstIterator it;
+ QValueList<MyMoneySplit> splits;
+ splits = t.splits();
+ for(it = splits.begin(); it != splits.end(); ++it) {
+ if((*it).accountId() == accountId)
+ break;
+ }
+ if(it == splits.end()) {
+ continue;
+ }
+ }
+
+ if(startDate.isValid() && endDate.isValid()) {
+ if((*pos).paymentDates(startDate, endDate).count() == 0) {
+ continue;
+ }
+ }
+
+ if(startDate.isValid() && !endDate.isValid()) {
+ if(!(*pos).nextPayment(startDate.addDays(-1)).isValid()) {
+ continue;
+ }
+ }
+
+ if(!startDate.isValid() && endDate.isValid()) {
+ if((*pos).startDate() > endDate) {
+ continue;
+ }
+ }
+
+ if(overdue) {
+ if (!(*pos).isOverdue())
+ continue;
+/*
+ QDate nextPayment = (*pos).nextPayment((*pos).lastPayment());
+ if(!nextPayment.isValid())
+ continue;
+ if(nextPayment >= QDate::currentDate())
+ continue;
+*/
+ }
+
+ // qDebug("Adding '%s'", (*pos).name().latin1());
+ list << *pos;
+ }
+ return list;
+}
+
+const QValueList<MyMoneySchedule> MyMoneyDatabaseMgr::scheduleListEx( int scheduleTypes,
+ int scheduleOcurrences,
+ int schedulePaymentTypes,
+ QDate startDate,
+ const QStringList& accounts) const
+{
+// qDebug("scheduleListEx");
+ QMap<QString, MyMoneySchedule> scheduleList = m_sql->fetchSchedules();
+ QMap<QString, MyMoneySchedule>::ConstIterator pos;
+ QValueList<MyMoneySchedule> list;
+
+ if (!startDate.isValid())
+ return list;
+
+ for(pos = scheduleList.begin(); pos != scheduleList.end(); ++pos)
+ {
+ if (scheduleTypes && !(scheduleTypes & (*pos).type()))
+ continue;
+
+ if (scheduleOcurrences && !(scheduleOcurrences & (*pos).occurence()))
+ continue;
+
+ if (schedulePaymentTypes && !(schedulePaymentTypes & (*pos).paymentType()))
+ continue;
+
+ if((*pos).paymentDates(startDate, startDate).count() == 0)
+ continue;
+
+ if ((*pos).isFinished())
+ continue;
+
+ if ((*pos).hasRecordedPayment(startDate))
+ continue;
+
+ if (accounts.count() > 0)
+ {
+ if (accounts.contains((*pos).account().id()))
+ continue;
+ }
+
+// qDebug("\tAdding '%s'", (*pos).name().latin1());
+ list << *pos;
+ }
+
+ return list;
+}
+
+void MyMoneyDatabaseMgr::addCurrency(const MyMoneySecurity& currency)
+{
+ if (m_sql) {
+ QMap<QString, MyMoneySecurity> currencyList = m_sql->fetchCurrencies(QString(currency.id()));
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ it = currencyList.find(currency.id());
+ if(it != currencyList.end()) {
+ throw new MYMONEYEXCEPTION(QString("Cannot add currency with existing id %1").arg(currency.id().data()));
+ }
+
+ m_sql->addCurrency(currency);
+ }
+}
+
+void MyMoneyDatabaseMgr::modifyCurrency(const MyMoneySecurity& currency)
+{
+ QMap<QString, MyMoneySecurity> currencyList = m_sql->fetchCurrencies(QString(currency.id()));
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ it = currencyList.find(currency.id());
+ if(it == currencyList.end()) {
+ throw new MYMONEYEXCEPTION(QString("Cannot modify currency with unknown id %1").arg(currency.id().data()));
+ }
+
+ m_sql->modifyCurrency(currency);
+}
+
+void MyMoneyDatabaseMgr::removeCurrency(const MyMoneySecurity& currency)
+{
+ QMap<QString, MyMoneySecurity> currencyList = m_sql->fetchCurrencies(QString(currency.id()));
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ // FIXME: check referential integrity
+
+ it = currencyList.find(currency.id());
+ if(it == currencyList.end()) {
+ throw new MYMONEYEXCEPTION(QString("Cannot remove currency with unknown id %1").arg(currency.id().data()));
+ }
+
+ m_sql->removeCurrency(currency);
+}
+
+const MyMoneySecurity MyMoneyDatabaseMgr::currency(const QString& id) const
+{
+ if(id.isEmpty()) {
+
+ }
+ QMap<QString, MyMoneySecurity> currencyList = m_sql->fetchCurrencies(QString(id));
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ it = currencyList.find(id);
+ if(it == currencyList.end()) {
+ throw new MYMONEYEXCEPTION(QString("Cannot retrieve currency with unknown id '%1'").arg(id.data()));
+ }
+
+ return *it;
+}
+
+const QValueList<MyMoneySecurity> MyMoneyDatabaseMgr::currencyList(void) const
+{
+ if (m_sql) {
+ return m_sql->fetchCurrencies().values();
+ } else {
+ return QValueList<MyMoneySecurity> ();
+ }
+}
+
+const QValueList<MyMoneyReport> MyMoneyDatabaseMgr::reportList( void ) const
+{
+ if (m_sql) {
+ return m_sql->fetchReports().values();
+ } else {
+ return QValueList<MyMoneyReport> ();
+ }
+}
+
+void MyMoneyDatabaseMgr::addReport( MyMoneyReport& report )
+{
+ if(!report.id().isEmpty())
+ throw new MYMONEYEXCEPTION("transaction already contains an id");
+
+ MyMoneyReport newReport(nextReportID(), report);
+ report = newReport;
+ m_sql->addReport(newReport);
+ //m_sql->addReport(MyMoneyReport (nextReportID(), report));
+}
+
+void MyMoneyDatabaseMgr::modifyReport( const MyMoneyReport& report )
+{
+ QMap<QString, MyMoneyReport> reportList = m_sql->fetchReports(QString(report.id()));
+ QMap<QString, MyMoneyReport>::ConstIterator it;
+
+ it = reportList.find(report.id());
+ if(it == reportList.end()) {
+ QString msg = "Unknown report '" + report.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_sql->modifyReport(report);
+}
+
+unsigned MyMoneyDatabaseMgr::countReports( void ) const
+{
+ return m_sql->getRecCount("kmmReports");
+}
+
+const MyMoneyReport MyMoneyDatabaseMgr::report( const QString& id ) const
+{
+ return m_sql->fetchReports(QString(id))[id];
+}
+
+void MyMoneyDatabaseMgr::removeReport(const MyMoneyReport& report)
+{
+ QMap<QString, MyMoneyReport> reportList = m_sql->fetchReports(QString(report.id()));
+ QMap<QString, MyMoneyReport>::ConstIterator it;
+
+ it = reportList.find(report.id());
+ if(it == reportList.end()) {
+ QString msg = "Unknown report '" + report.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_sql->removeReport(report);
+}
+
+const QValueList<MyMoneyBudget> MyMoneyDatabaseMgr::budgetList( void ) const
+{
+ return m_sql->fetchBudgets().values();
+}
+
+void MyMoneyDatabaseMgr::addBudget( MyMoneyBudget& budget )
+{
+ MyMoneyBudget newBudget(nextBudgetID(), budget);
+ m_sql->addBudget(newBudget);
+}
+
+const MyMoneyBudget MyMoneyDatabaseMgr::budgetByName(const QString& budget) const
+{
+ QMap<QString, MyMoneyBudget> budgets = m_sql->fetchBudgets();
+ QMap<QString, MyMoneyBudget>::ConstIterator it_p;
+
+ for(it_p = budgets.begin(); it_p != budgets.end(); ++it_p) {
+ if((*it_p).name() == budget) {
+ return *it_p;
+ }
+ }
+
+ throw new MYMONEYEXCEPTION("Unknown budget '" + budget + "'");
+}
+
+void MyMoneyDatabaseMgr::modifyBudget( const MyMoneyBudget& budget )
+{
+ //QMap<QString, MyMoneyBudget>::ConstIterator it;
+
+ //it = m_budgetList.find(budget.id());
+ //if(it == m_budgetList.end()) {
+ // QString msg = "Unknown budget '" + budget.id() + "'";
+ // throw new MYMONEYEXCEPTION(msg);
+ //}
+ //m_budgetList.modify(budget.id(), budget);
+
+ startTransaction();
+ if (m_sql->fetchBudgets(QString(budget.id()), true).empty()) {
+ QString msg = "Unknown budget '" + budget.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+ m_sql->modifyBudget(budget);
+ commitTransaction();
+}
+
+unsigned MyMoneyDatabaseMgr::countBudgets( void ) const
+{
+ return m_sql->getRecCount("kmmBudgetConfig");
+}
+
+MyMoneyBudget MyMoneyDatabaseMgr::budget( const QString& id ) const
+{
+ return m_sql->fetchBudgets(QString(id)) [id];
+}
+
+void MyMoneyDatabaseMgr::removeBudget(const MyMoneyBudget& budget)
+{
+// QMap<QString, MyMoneyBudget>::ConstIterator it;
+//
+// it = m_budgetList.find(budget.id());
+// if(it == m_budgetList.end()) {
+// QString msg = "Unknown budget '" + budget.id() + "'";
+// throw new MYMONEYEXCEPTION(msg);
+// }
+//
+ m_sql->removeBudget(budget);
+}
+
+void MyMoneyDatabaseMgr::clearCache(void)
+{
+ //m_balanceCache.clear();
+}
+
+class isReferencedHelper {
+ public:
+ isReferencedHelper(const QString& id)
+ : m_id (id)
+ {}
+
+ inline bool operator() (const MyMoneyObject& obj) const
+ { return obj.hasReferenceTo(m_id); }
+
+ private:
+ QString m_id;
+};
+
+bool MyMoneyDatabaseMgr::isReferenced(const MyMoneyObject& obj, const MyMoneyFileBitArray& skipCheck) const
+{
+ bool rc = false;
+ const QString& id = obj.id();
+
+ MyMoneyPriceList::const_iterator it_pr;
+
+ MyMoneyPriceList::const_iterator priceEnd;
+
+ // FIXME optimize the list of objects we have to checks
+ // with a bit of knowledge of the internal structure, we
+ // could optimize the number of objects we check for references
+
+ // Scan all engine objects for a reference
+ if(!skipCheck[RefCheckTransaction]) {
+ bool skipTransactions = false;
+ MyMoneyTransactionFilter f;
+ if (typeid(obj) == typeid(MyMoneyAccount)) {
+ f.addAccount(obj.id());
+ } else if (typeid(obj) == typeid(MyMoneyCategory)) {
+ f.addCategory(obj.id());
+ } else if (typeid(obj) == typeid(MyMoneyPayee)) {
+ f.addPayee(obj.id());
+ } // if it's anything else, I guess we just read everything
+ //FIXME: correction, transactions can only have a reference to an account or payee,
+ // so, read nothing.
+ else {
+ skipTransactions = true;
+ }
+ if (! skipTransactions) {
+ //QMap <QString, MyMoneyTransaction> transactionList = m_sql->fetchTransactions(f);
+ //rc = (transactionList.end() != std::find_if(transactionList.begin(), transactionList.end(), isReferencedHelper(id)));
+ //if (rc != m_sql->isReferencedByTransaction(obj.id()))
+ // qDebug ("Transaction match inconsistency.");
+ rc = m_sql->isReferencedByTransaction(obj.id());
+ }
+ }
+
+ if(!skipCheck[RefCheckAccount] && !rc) {
+ QValueList<MyMoneyAccount> accountList;
+ MyMoneyFile::instance()->accountList(accountList);
+ rc = (accountList.end() != std::find_if(accountList.begin(), accountList.end(), isReferencedHelper(id)));
+ }
+ if(!skipCheck[RefCheckInstitution] && !rc) {
+ QValueList<MyMoneyInstitution> institutionList;
+ MyMoneyFile::instance()->institutionList(institutionList);
+ rc = (institutionList.end() != std::find_if(institutionList.begin(), institutionList.end(), isReferencedHelper(id)));
+ }
+ if(!skipCheck[RefCheckPayee] && !rc) {
+ QValueList<MyMoneyPayee> payeeList = MyMoneyFile::instance()->payeeList();
+ rc = (payeeList.end() != std::find_if(payeeList.begin(), payeeList.end(), isReferencedHelper(id)));
+ }
+ if(!skipCheck[RefCheckReport] && !rc) {
+ QMap<QString, MyMoneyReport> reportList = m_sql->fetchReports();
+ rc = (reportList.end() != std::find_if(reportList.begin(), reportList.end(), isReferencedHelper(id)));
+ }
+ if(!skipCheck[RefCheckBudget] && !rc) {
+ QMap<QString, MyMoneyBudget> budgets = m_sql->fetchBudgets();
+ rc = (budgets.end() != std::find_if(budgets.begin(), budgets.end(), isReferencedHelper(id)));
+ }
+ if(!skipCheck[RefCheckSchedule] && !rc) {
+ QMap<QString, MyMoneySchedule> scheduleList = m_sql->fetchSchedules();
+ rc = (scheduleList.end() != std::find_if(scheduleList.begin(), scheduleList.end(), isReferencedHelper(id)));
+ }
+ if(!skipCheck[RefCheckSecurity] && !rc) {
+ QValueList<MyMoneySecurity> securitiesList = MyMoneyFile::instance()->securityList();
+ rc = (securitiesList.end() != std::find_if(securitiesList.begin(), securitiesList.end(), isReferencedHelper(id)));
+ }
+ if(!skipCheck[RefCheckCurrency] && !rc) {
+ QValueList<MyMoneySecurity> currencyList = m_sql->fetchCurrencies().values();
+ rc = (currencyList.end() != std::find_if(currencyList.begin(), currencyList.end(), isReferencedHelper(id)));
+ }
+ // within the pricelist we don't have to scan each entry. Checking the QPair
+ // members of the MyMoneySecurityPair is enough as they are identical to the
+ // two security ids
+ if(!skipCheck[RefCheckPrice] && !rc) {
+ MyMoneyPriceList priceList = m_sql->fetchPrices();
+ priceEnd = priceList.end();
+ for(it_pr = priceList.begin(); !rc && it_pr != priceEnd; ++it_pr) {
+ rc = (it_pr.key().first == id) || (it_pr.key().second == id);
+ }
+ }
+ return rc;
+}
+
+void MyMoneyDatabaseMgr::close(void) {
+ if (m_sql != 0) {
+ m_sql->close(true);
+ m_sql = 0;
+ }
+}
+
+void MyMoneyDatabaseMgr::startTransaction(void)
+{ if (m_sql) m_sql->startCommitUnit ("databasetransaction"); }
+
+bool MyMoneyDatabaseMgr::commitTransaction(void)
+{
+ if (m_sql)
+ return m_sql->endCommitUnit ("databasetransaction");
+ return false;
+}
+
+void MyMoneyDatabaseMgr::rollbackTransaction(void)
+{ if (m_sql) m_sql->cancelCommitUnit ("databasetransaction"); }
+
+void MyMoneyDatabaseMgr::setCreationDate(const QDate& val)
+{ m_creationDate = val; }
+
+KSharedPtr <MyMoneyStorageSql> MyMoneyDatabaseMgr::connectToDatabase(const KURL& url) {
+ m_sql = new MyMoneyStorageSql (this, url);
+ return m_sql;
+}
+
+ void MyMoneyDatabaseMgr::fillStorage()
+{ m_sql->fillStorage(); }
+
+void MyMoneyDatabaseMgr::setLastModificationDate(const QDate& val)
+{ m_lastModificationDate = val; }
+
+bool MyMoneyDatabaseMgr::isDuplicateTransaction(const QString& /*id*/) const
+{
+ //FIXME: figure out the real id from the key and check the DB.
+//return m_transactionKeys.contains(id);
+ return false;
+}
+
+void MyMoneyDatabaseMgr::loadAccounts(const QMap<QString, MyMoneyAccount>& /*map*/)
+{
+// m_accountList = map;
+//FIXME: update the database.
+// startTransaction
+// DELETE FROM kmmAccounts
+// for each account in the map
+// m_sql->addAccount(...)
+// commitTransaction
+// on error, rollbackTransaction
+}
+
+void MyMoneyDatabaseMgr::loadTransactions(const QMap<QString, MyMoneyTransaction>& /*map*/)
+{
+// m_transactionList = map;
+//FIXME: update the database.
+
+// // now fill the key map
+// QMap<QString, QString> keys;
+// QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+// for(it_t = map.begin(); it_t != map.end(); ++it_t) {
+// keys[(*it_t).id()] = it_t.key();
+// }
+// m_transactionKeys = keys;
+}
+
+void MyMoneyDatabaseMgr::loadInstitutions(const QMap<QString, MyMoneyInstitution>& /*map*/)
+{
+// m_institutionList = map;
+//FIXME: update the database.
+
+// // now fill the key map
+// QMap<QString, QString> keys;
+// QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+// for(it_t = map.begin(); it_t != map.end(); ++it_t) {
+// keys[(*it_t).id()] = it_t.key();
+// }
+// m_transactionKeys = keys;
+}
+
+void MyMoneyDatabaseMgr::loadPayees(const QMap<QString, MyMoneyPayee>& /*map*/)
+{
+// m_payeeList = map;
+}
+
+void MyMoneyDatabaseMgr::loadSchedules(const QMap<QString, MyMoneySchedule>& /*map*/)
+{
+// m_scheduleList = map;
+}
+
+void MyMoneyDatabaseMgr::loadSecurities(const QMap<QString, MyMoneySecurity>& /*map*/)
+{
+// m_securitiesList = map;
+}
+
+void MyMoneyDatabaseMgr::loadCurrencies(const QMap<QString, MyMoneySecurity>& /*map*/)
+{
+// m_currencyList = map;
+//FIXME: update the database.
+// startTransaction
+// DELETE FROM kmmBudgetConfig
+// for each budget in the map
+// m_sql->addBudget(...)
+// commitTransaction
+// on error, rollbackTransaction
+}
+
+void MyMoneyDatabaseMgr::loadReports( const QMap<QString, MyMoneyReport>& /*reports*/ )
+{
+// m_reportList = reports;
+//FIXME: update the database.
+// startTransaction
+// DELETE FROM kmmBudgetConfig
+// for each budget in the map
+// m_sql->addBudget(...)
+// commitTransaction
+// on error, rollbackTransaction
+}
+
+void MyMoneyDatabaseMgr::loadBudgets( const QMap<QString, MyMoneyBudget>& /*budgets*/ )
+{
+// m_budgetList = budgets;
+//FIXME: update the database.
+// startTransaction
+// DELETE FROM kmmBudgetConfig
+// for each budget in the map
+// m_sql->addBudget(...)
+// commitTransaction
+// on error, rollbackTransaction
+}
+
+void MyMoneyDatabaseMgr::loadPrices(const MyMoneyPriceList& list)
+{
+ Q_UNUSED(list);
+}
+
+unsigned long MyMoneyDatabaseMgr::accountId(void) const
+{ return m_sql->getNextAccountId(); }
+
+unsigned long MyMoneyDatabaseMgr::transactionId(void) const
+{ return m_sql->getNextTransactionId(); }
+
+unsigned long MyMoneyDatabaseMgr::payeeId(void) const
+{ return m_sql->getNextPayeeId(); }
+
+unsigned long MyMoneyDatabaseMgr::institutionId(void) const
+{ return m_sql->getNextInstitutionId(); }
+
+unsigned long MyMoneyDatabaseMgr::scheduleId(void) const
+{ return m_sql->getNextScheduleId(); }
+
+unsigned long MyMoneyDatabaseMgr::securityId(void) const
+{ return m_sql->getNextSecurityId(); }
+
+unsigned long MyMoneyDatabaseMgr::reportId(void) const
+{ return m_sql->getNextReportId(); }
+
+unsigned long MyMoneyDatabaseMgr::budgetId(void) const
+{ return m_sql->getNextBudgetId(); }
+
+void MyMoneyDatabaseMgr::loadAccountId(const unsigned long id)
+{
+ m_sql->loadAccountId(id);
+}
+
+void MyMoneyDatabaseMgr::loadTransactionId(const unsigned long id)
+{
+ m_sql->loadTransactionId(id);
+}
+
+void MyMoneyDatabaseMgr::loadPayeeId(const unsigned long id)
+{
+ m_sql->loadPayeeId(id);
+}
+
+void MyMoneyDatabaseMgr::loadInstitutionId(const unsigned long id)
+{
+ m_sql->loadInstitutionId(id);
+}
+
+void MyMoneyDatabaseMgr::loadScheduleId(const unsigned long id)
+{
+ m_sql->loadScheduleId(id);
+}
+
+void MyMoneyDatabaseMgr::loadSecurityId(const unsigned long id)
+{
+ m_sql->loadSecurityId(id);
+}
+
+void MyMoneyDatabaseMgr::loadReportId(const unsigned long id)
+{
+ m_sql->loadReportId(id);
+}
+
+void MyMoneyDatabaseMgr::loadBudgetId(const unsigned long id)
+{
+ m_sql->loadBudgetId(id);
+}
+
+void MyMoneyDatabaseMgr::rebuildAccountBalances(void)
+{
+ startTransaction();
+ QMap<QString, MyMoneyAccount> accountMap = m_sql->fetchAccounts(QStringList(), true);
+
+ QMap<QString, MyMoneyMoney> balanceMap = m_sql->fetchBalance(accountMap.keys(), QDate());
+
+ for (QMap<QString, MyMoneyMoney>::const_iterator it_b = balanceMap.begin();
+ it_b != balanceMap.end(); ++it_b) {
+ accountMap[it_b.key()].setBalance(it_b.data());
+ }
+
+ for (QMap<QString, MyMoneyAccount>::const_iterator it_a = accountMap.begin();
+ it_a != accountMap.end(); ++it_a) {
+ m_sql->modifyAccount(it_a.data());
+ }
+ commitTransaction();
+}
+
+void MyMoneyDatabaseMgr::removeReferences(const QString& id)
+{
+ QMap<QString, MyMoneyReport>::const_iterator it_r;
+ QMap<QString, MyMoneyBudget>::const_iterator it_b;
+
+ // remove from reports
+ QMap<QString, MyMoneyReport> reportList = m_sql->fetchReports();
+ for(it_r = reportList.begin(); it_r != reportList.end(); ++it_r) {
+ MyMoneyReport r = *it_r;
+ r.removeReference(id);
+// reportList.modify(r.id(), r);
+ }
+
+ // remove from budgets
+ QMap<QString, MyMoneyBudget> budgetList = m_sql->fetchBudgets();
+ for(it_b = budgetList.begin(); it_b != budgetList.end(); ++it_b) {
+ MyMoneyBudget b = *it_b;
+ b.removeReference(id);
+// budgetList.modify(b.id(), b);
+ }
+}
+
+#undef TRY
+#undef CATCH
+#undef PASS
diff --git a/kmymoney2/mymoney/storage/mymoneydatabasemgr.h b/kmymoney2/mymoney/storage/mymoneydatabasemgr.h
new file mode 100644
index 0000000..21bf8d6
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneydatabasemgr.h
@@ -0,0 +1,1038 @@
+/***************************************************************************
+ mymoneydatabasemgr.h - description
+ -------------------
+ begin : June 5 2007
+ copyright : (C) 2007 by Fernando Vilas
+ email : Fernando Vilas <fvilas@iname.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. *
+ * *
+ ***************************************************************************/
+
+#ifndef MYMONEYDATABASEMGR_H
+#define MYMONEYDATABASEMGR_H
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include "imymoneyserialize.h"
+#include "imymoneystorage.h"
+#include "mymoneymap.h"
+#include "mymoneystoragesql.h"
+
+/**
+ * The MyMoneyDatabaseMgr class represents the storage engine for databases.
+ * The actual connection and internal storage is handled through the
+ * MyMoneyStorageSql interface.
+ *
+ * The MyMoneyDatabaseMgr must have a MyMoneyStorageSql connected to a
+ * database to be useful. Once connected, data will be loaded from/sent to the
+ * database synchronously. The method dirty() will always return false. Making
+ * this many trips to the database is not very fast, so when possible, the
+ * data cache in MyMoneyFile is used.
+ *
+ */
+class MyMoneyDatabaseMgr : public IMyMoneyStorage, public IMyMoneySerialize,
+ public MyMoneyKeyValueContainer
+{
+public:
+ MyMoneyDatabaseMgr();
+ ~MyMoneyDatabaseMgr();
+
+ // general get functions
+ virtual const MyMoneyPayee user(void) const;
+ virtual const QDate creationDate(void) const;
+ virtual const QDate lastModificationDate(void) const;
+ virtual unsigned int currentFixVersion(void) const;
+ virtual unsigned int fileFixVersion(void) const;
+
+ // general set functions
+ virtual void setUser(const MyMoneyPayee& user);
+ virtual void setFileFixVersion(const unsigned int v);
+
+ // methods provided by MyMoneyKeyValueContainer
+ virtual void setValue(const QString& key, const QString& value);
+ virtual const QString value(const QString& key) const;
+ virtual void deletePair(const QString& key);
+
+ /**
+ * This method is used to duplicate an IMyMoneyStorage object and return
+ * a pointer to the newly created copy. The caller of this method is the
+ * new owner of the object and must destroy it.
+ */
+ virtual MyMoneyDatabaseMgr const * duplicate(void);
+
+ /**
+ * This method is used to create a new account
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account MyMoneyAccount filled with data
+ */
+ virtual void addAccount(MyMoneyAccount& account);
+
+ /**
+ * This method is used to add one account as sub-ordinate to another
+ * (parent) account. The objects that are passed will be modified
+ * accordingly.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param parent parent account the account should be added to
+ * @param account the account to be added
+ */
+ virtual void addAccount(MyMoneyAccount& parent, MyMoneyAccount& account);
+
+ /**
+ * This method is used to create a new payee
+ *
+ * An exception will be thrown upon error conditions
+ *
+ * @param payee MyMoneyPayee reference to payee information
+ */
+ virtual void addPayee(MyMoneyPayee& payee);
+
+ /**
+ * This method is used to retrieve information about a payee
+ * An exception will be thrown upon error conditions.
+ *
+ * @param id QString reference to id of payee
+ *
+ * @return MyMoneyPayee object of payee
+ */
+ virtual const MyMoneyPayee payee(const QString& id) const;
+
+ /**
+ * This method is used to retrieve the id to a corresponding
+ * name of a payee/receiver.
+ * An exception will be thrown upon error conditions.
+ *
+ * @param payee QString reference to name of payee
+ *
+ * @return MyMoneyPayee object of payee
+ */
+ virtual const MyMoneyPayee payeeByName(const QString& payee) const;
+
+ /**
+ * This method is used to modify an existing payee
+ *
+ * An exception will be thrown upon error conditions
+ *
+ * @param payee MyMoneyPayee reference to payee information
+ */
+ virtual void modifyPayee(const MyMoneyPayee& payee);
+
+ /**
+ * This method is used to remove an existing payee
+ *
+ * An exception will be thrown upon error conditions
+ *
+ * @param payee MyMoneyPayee reference to payee information
+ */
+ virtual void removePayee(const MyMoneyPayee& payee);
+
+ /**
+ * This method returns a list of the payees
+ * inside a MyMoneyStorage object
+ *
+ * @return QValueList<MyMoneyPayee> containing the payee information
+ */
+ virtual const QValueList<MyMoneyPayee> payeeList(void) const;
+
+ /**
+ * Returns the account addressed by it's id.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param id id of the account to locate.
+ * @return reference to MyMoneyAccount object. An exception is thrown
+ * if the id is unknown
+ */
+ virtual const MyMoneyAccount account(const QString& id) const;
+
+ /**
+ * This method is used to check whether a given
+ * account id references one of the standard accounts or not.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param id account id
+ * @return true if account-id is one of the standards, false otherwise
+ */
+ virtual bool isStandardAccount(const QString& id) const;
+
+ /**
+ * This method is used to set the name for the specified standard account
+ * within the storage area. An exception will be thrown, if an error
+ * occurs
+ *
+ * @param id QString reference to one of the standard accounts.
+ * @param name QString reference to the name to be set
+ *
+ */
+ virtual void setAccountName(const QString& id, const QString& name);
+
+ /**
+ * Adds an institution to the storage. A
+ * respective institution-ID will be generated within this record.
+ * The ID is stored as QString in the object passed as argument.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param institution The complete institution information in a
+ * MyMoneyInstitution object
+ */
+ virtual void addInstitution(MyMoneyInstitution& institution);
+
+ /**
+ * Adds a transaction to the file-global transaction pool. A respective
+ * transaction-ID will be generated within this record. The ID is stored
+ * QString with the object.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param transaction reference to the transaction
+ * @param skipAccountUpdate if set, the transaction lists of the accounts
+ * referenced in the splits are not updated. This is used for
+ * bulk loading a lot of transactions but not during normal operation
+ */
+ virtual void addTransaction(MyMoneyTransaction& transaction, const bool skipAccountUpdate = false);
+
+ /**
+ * This method is used to determince, if the account with the
+ * given ID is referenced by any split in m_transactionList.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param id id of the account to be checked for
+ * @return true if account is referenced, false otherwise
+ */
+ virtual bool hasActiveSplits(const QString& id) const;
+
+ /**
+ * This method is used to return the actual balance of an account
+ * without it's sub-ordinate accounts. If a @p date is presented,
+ * the balance at the beginning of this date (not including any
+ * transaction on this date) is returned. Otherwise all recorded
+ * transactions are included in the balance.
+ *
+ * @param id id of the account in question
+ * @param date return balance for specific date
+ * @return balance of the account as MyMoneyMoney object
+ */
+ virtual const MyMoneyMoney balance(const QString& id, const QDate& date) const;
+
+ /**
+ * This method is used to return the actual balance of an account
+ * including it's sub-ordinate accounts. If a @p date is presented,
+ * the balance at the beginning of this date (not including any
+ * transaction on this date) is returned. Otherwise all recorded
+ * transactions are included in the balance.
+ *
+ * @param id id of the account in question
+ * @param date return balance for specific date
+ * @return balance of the account as MyMoneyMoney object
+ */
+ virtual const MyMoneyMoney totalBalance(const QString& id, const QDate& date) const;
+
+ /**
+ * Returns the institution of a given ID
+ *
+ * @param id id of the institution to locate
+ * @return MyMoneyInstitution object filled with data. If the institution
+ * could not be found, an exception will be thrown
+ */
+ virtual const MyMoneyInstitution institution(const QString& id) const;
+
+ /**
+ * This method returns an indicator if the storage object has been
+ * changed after it has last been saved to permanent storage.
+ *
+ * @return true if changed, false if not (for a database, always false).
+ */
+ virtual bool dirty(void) const;
+
+ /**
+ * This method can be used by an external object to force the
+ * storage object to be dirty. This is used e.g. when an upload
+ * to an external destination failed but the previous storage
+ * to a local disk was ok.
+ *
+ * Since the database is synchronized with the application, this method
+ * is a no-op.
+ */
+ virtual void setDirty(void);
+
+ /**
+ * This method returns the number of accounts currently known to this storage
+ * in the range 0..MAXUINT
+ *
+ * @return number of accounts currently known inside a MyMoneyFile object
+ */
+ virtual unsigned int accountCount(void) const;
+
+ /**
+ * This method returns a list of the institutions
+ * inside a MyMoneyStorage object
+ *
+ * @return QValueList<MyMoneyInstitution> containing the
+ * institution information
+ */
+ virtual const QValueList<MyMoneyInstitution> institutionList(void) const;
+
+ /**
+ * Modifies an already existing account in the file global account pool.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account reference to the new account information
+ * @param skipCheck allows to skip the builtin consistency checks
+ */
+ virtual void modifyAccount(const MyMoneyAccount& account, const bool skipCheck = false);
+
+ /**
+ * Modifies an already existing institution in the file global
+ * institution pool.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param institution The complete new institution information
+ */
+ virtual void modifyInstitution(const MyMoneyInstitution& institution);
+
+ /**
+ * This method is used to update a specific transaction in the
+ * transaction pool of the MyMoneyFile object
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param transaction reference to transaction to be changed
+ */
+ virtual void modifyTransaction(const MyMoneyTransaction& transaction);
+
+ /**
+ * This method re-parents an existing account
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account MyMoneyAccount reference to account to be re-parented
+ * @param parent MyMoneyAccount reference to new parent account
+ */
+ virtual void reparentAccount(MyMoneyAccount &account, MyMoneyAccount& parent);
+
+ /**
+ * This method is used to remove a transaction from the transaction
+ * pool (journal).
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param transaction const reference to transaction to be deleted
+ */
+ virtual void removeTransaction(const MyMoneyTransaction& transaction);
+
+ /**
+ * This method returns the number of transactions currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @param account QString reference to account id. If account is empty
+ + all transactions (the journal) will be counted. If account
+ * is not empty it returns the number of transactions
+ * that have splits in this account.
+ *
+ * @return number of transactions in journal/account
+ */
+ virtual unsigned int transactionCount(const QString& account = QString()) const;
+
+ /**
+ * This method returns a QMap filled with the number of transactions
+ * per account. The account id serves as index into the map. If one
+ * needs to have all transactionCounts() for many accounts, this method
+ * is faster than calling transactionCount(const QString& account) many
+ * times.
+ *
+ * @return QMap with numbers of transactions per account
+ */
+ virtual const QMap<QString, unsigned long> transactionCountMap(void) const;
+
+ /**
+ * This method is used to pull a list of transactions from the file
+ * global transaction pool. It returns all those transactions
+ * that match the filter passed as argument. If the filter is empty,
+ * the whole journal will be returned.
+ * The list returned is sorted according to the transactions posting date.
+ * If more than one transaction exists for the same date, the order among
+ * them is undefined.
+ *
+ * @param filter MyMoneyTransactionFilter object with the match criteria
+ *
+ * @return set of transactions in form of a QValueList<MyMoneyTransaction>
+ */
+ virtual const QValueList<MyMoneyTransaction> transactionList(MyMoneyTransactionFilter& filter) const;
+
+ /**
+ * This method is the same as above, but instead of a return value, a
+ * parameter is used.
+ *
+ * @param list The set of transactions returned. The list passed in will
+ * be cleared before filling with results.
+ * @param filter MyMoneyTransactionFilter object with the match criteria
+ */
+ virtual void transactionList(QValueList<MyMoneyTransaction>& list, MyMoneyTransactionFilter& filter) const;
+
+ /**
+ * This method is the same as above, but the list contains pairs of
+ * transactions and splits.
+ *
+ * @param list The set of transactions returned. The list passed in will
+ * be cleared before filling with results.
+ * @param filter MyMoneyTransactionFilter object with the match criteria
+ */
+ virtual void transactionList(QValueList<QPair<MyMoneyTransaction, MyMoneySplit> >& list, MyMoneyTransactionFilter& filter) const;
+
+ /**
+ * Deletes an existing account from the file global account pool
+ * This method only allows to remove accounts that are not
+ * referenced by any split. Use moveSplits() to move splits
+ * to another account. An exception is thrown in case of a
+ * problem.
+ *
+ * @param account reference to the account to be deleted.
+ */
+ virtual void removeAccount(const MyMoneyAccount& account);
+
+ /**
+ * Deletes an existing institution from the file global institution pool
+ * Also modifies the accounts that reference this institution as
+ * their institution.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param institution institution to be deleted.
+ */
+ virtual void removeInstitution(const MyMoneyInstitution& institution);
+
+ /**
+ * This method is used to extract a transaction from the file global
+ * transaction pool through an id. In case of an invalid id, an
+ * exception will be thrown.
+ *
+ * @param id id of transaction as QString.
+ * @return the requested transaction
+ */
+ virtual const MyMoneyTransaction transaction(const QString& id) const;
+
+ /**
+ * This method is used to extract a transaction from the file global
+ * transaction pool through an index into an account.
+ *
+ * @param account id of the account as QString
+ * @param idx number of transaction in this account
+ * @return MyMoneyTransaction object
+ */
+ virtual const MyMoneyTransaction transaction(const QString& account, const int idx) const;
+
+ /**
+ * This method returns the number of institutions currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @return number of institutions known to file
+ */
+ virtual unsigned int institutionCount(void) const;
+
+ /**
+ * This method returns a list of accounts inside the storage object.
+ *
+ * @param list reference to QValueList receiving the account objects
+ *
+ * @note The standard accounts will not be returned
+ */
+ virtual void accountList(QValueList<MyMoneyAccount>& list) const;
+
+ /**
+ * This method is used to return the standard liability account
+ * @return MyMoneyAccount liability account(group)
+ */
+ virtual const MyMoneyAccount liability(void) const;
+
+ /**
+ * This method is used to return the standard asset account
+ * @return MyMoneyAccount asset account(group)
+ */
+ virtual const MyMoneyAccount asset(void) const;
+
+ /**
+ * This method is used to return the standard expense account
+ * @return MyMoneyAccount expense account(group)
+ */
+ virtual const MyMoneyAccount expense(void) const;
+
+ /**
+ * This method is used to return the standard income account
+ * @return MyMoneyAccount income account(group)
+ */
+ virtual const MyMoneyAccount income(void) const;
+
+ /**
+ * This method is used to return the standard equity account
+ * @return MyMoneyAccount equity account(group)
+ */
+ virtual const MyMoneyAccount equity(void) const;
+
+ /**
+ * This method is used to create a new security object. The ID will be
+ * created automatically. The object passed with the parameter @p security
+ * is modified to contain the assigned id.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param security MyMoneySecurity filled with data
+ */
+ virtual void addSecurity(MyMoneySecurity& security);
+
+ /**
+ * This method is used to modify an existing MyMoneySecurity
+ * object.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param security reference to the MyMoneySecurity object to be updated
+ */
+ virtual void modifySecurity(const MyMoneySecurity& security);
+
+ /**
+ * This method is used to remove an existing MyMoneySecurity object
+ * from the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param security reference to the MyMoneySecurity object to be removed
+ */
+ virtual void removeSecurity(const MyMoneySecurity& security);
+
+ /**
+ * This method is used to retrieve a single MyMoneySecurity object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneySecurity object
+ * @return MyMoneySecurity object
+ */
+ virtual const MyMoneySecurity security(const QString& id) const;
+
+ /**
+ * This method returns a list of the security objects
+ * inside a MyMoneyStorage object
+ *
+ * @return QValueList<MyMoneySecurity> containing objects
+ */
+ virtual const QValueList<MyMoneySecurity> securityList(void) const;
+
+ virtual void addPrice(const MyMoneyPrice& price);
+ virtual void removePrice(const MyMoneyPrice& price);
+ virtual const MyMoneyPrice price(const QString& fromId, const QString& toId, const QDate& date, const bool exactDate) const;
+
+ /**
+ * This method returns a list of all prices.
+ *
+ * @return MyMoneyPriceList of all MyMoneyPrice objects.
+ */
+ virtual const MyMoneyPriceList priceList(void) const;
+
+ /**
+ * This method is used to add a scheduled transaction to the engine.
+ * It must be sure, that the id of the object is not filled. When the
+ * method returns to the caller, the id will be filled with the
+ * newly created object id value.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param sched reference to the MyMoneySchedule object
+ */
+ virtual void addSchedule(MyMoneySchedule& sched);
+
+ /**
+ * This method is used to modify an existing MyMoneySchedule
+ * object. Therefor, the id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param sched const reference to the MyMoneySchedule object to be updated
+ */
+ virtual void modifySchedule(const MyMoneySchedule& sched);
+
+ /**
+ * This method is used to remove an existing MyMoneySchedule object
+ * from the engine. The id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param sched const reference to the MyMoneySchedule object to be updated
+ */
+ virtual void removeSchedule(const MyMoneySchedule& sched);
+
+ /**
+ * This method is used to retrieve a single MyMoneySchedule object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneySchedule object
+ * @return MyMoneySchedule object
+ */
+ virtual const MyMoneySchedule schedule(const QString& id) const;
+
+ /**
+ * This method is used to extract a list of scheduled transactions
+ * according to the filter criteria passed as arguments.
+ *
+ * @param accountId only search for scheduled transactions that reference
+ * accound @p accountId. If accountId is the empty string,
+ * this filter is off. Default is @p QString().
+ * @param type only schedules of type @p type are searched for.
+ * See MyMoneySchedule::typeE for details.
+ * Default is MyMoneySchedule::TYPE_ANY
+ * @param occurence only schedules of occurence type @p occurance are searched for.
+ * See MyMoneySchedule::occurenceE for details.
+ * Default is MyMoneySchedule::OCCUR_ANY
+ * @param paymentType only schedules of payment method @p paymentType
+ * are searched for.
+ * See MyMoneySchedule::paymentTypeE for details.
+ * Default is MyMoneySchedule::STYPE_ANY
+ * @param startDate only schedules with payment dates after @p startDate
+ * are searched for. Default is all dates (QDate()).
+ * @param endDate only schedules with payment dates ending prior to @p endDate
+ * are searched for. Default is all dates (QDate()).
+ * @param overdue if true, only those schedules that are overdue are
+ * searched for. Default is false (all schedules will be returned).
+ *
+ * @return const QValueList<MyMoneySchedule> list of schedule objects.
+ */
+ virtual const QValueList<MyMoneySchedule> scheduleList(const QString& accountId = QString(),
+ const MyMoneySchedule::typeE type = MyMoneySchedule::TYPE_ANY,
+ const MyMoneySchedule::occurenceE occurence = MyMoneySchedule::OCCUR_ANY,
+ const MyMoneySchedule::paymentTypeE paymentType = MyMoneySchedule::STYPE_ANY,
+ const QDate& startDate = QDate(),
+ const QDate& endDate = QDate(),
+ const bool overdue = false) const;
+
+ virtual const QValueList<MyMoneySchedule> scheduleListEx( int scheduleTypes,
+ int scheduleOcurrences,
+ int schedulePaymentTypes,
+ QDate startDate,
+ const QStringList& accounts=QStringList()) const;
+
+ /**
+ * This method is used to add a new currency object to the engine.
+ * The ID of the object is the trading symbol, so there is no need for an additional
+ * ID since the symbol is guaranteed to be unique.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param currency reference to the MyMoneySecurity object
+ */
+ virtual void addCurrency(const MyMoneySecurity& currency);
+
+ /**
+ * This method is used to modify an existing MyMoneySecurity
+ * object.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param currency reference to the MyMoneyCurrency object
+ */
+ virtual void modifyCurrency(const MyMoneySecurity& currency);
+
+ /**
+ * This method is used to remove an existing MyMoneySecurity object
+ * from the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param currency reference to the MyMoneySecurity object
+ */
+ virtual void removeCurrency(const MyMoneySecurity& currency);
+
+ /**
+ * This method is used to retrieve a single MyMoneySecurity object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneySecurity object
+ * @return MyMoneyCurrency object
+ */
+ virtual const MyMoneySecurity currency(const QString& id) const;
+
+ /**
+ * This method is used to retrieve the list of all currencies
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneySecurity objects representing a currency.
+ */
+ virtual const QValueList<MyMoneySecurity> currencyList(void) const;
+
+ /**
+ * This method is used to retrieve the list of all reports
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneyReport objects.
+ */
+ virtual const QValueList<MyMoneyReport> reportList( void ) const;
+
+ /**
+ * This method is used to add a new report to the engine.
+ * It must be sure, that the id of the object is not filled. When the
+ * method returns to the caller, the id will be filled with the
+ * newly created object id value.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param report reference to the MyMoneyReport object
+ */
+ virtual void addReport( MyMoneyReport& report );
+
+ /**
+ * This method is used to modify an existing MyMoneyReport
+ * object. Therefor, the id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param report const reference to the MyMoneyReport object to be updated
+ */
+ virtual void modifyReport( const MyMoneyReport& report );
+
+ /**
+ * This method returns the number of reports currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @return number of reports known to file
+ */
+ virtual unsigned countReports( void ) const;
+
+ /**
+ * This method is used to retrieve a single MyMoneyReport object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneyReport object
+ * @return MyMoneyReport object
+ */
+ virtual const MyMoneyReport report( const QString& id ) const;
+
+ /**
+ * This method is used to remove an existing MyMoneyReport object
+ * from the engine. The id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param report const reference to the MyMoneyReport object to be updated
+ */
+ virtual void removeReport(const MyMoneyReport& report);
+
+ /**
+ * This method is used to retrieve the list of all budgets
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneyBudget objects.
+ */
+ virtual const QValueList<MyMoneyBudget> budgetList( void ) const;
+
+ /**
+ * This method is used to add a new budget to the engine.
+ * It must be sure, that the id of the object is not filled. When the
+ * method returns to the caller, the id will be filled with the
+ * newly created object id value.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param budget reference to the MyMoneyBudget object
+ */
+ virtual void addBudget( MyMoneyBudget& budget );
+
+ /**
+ * This method is used to retrieve the id to a corresponding
+ * name of a budget
+ * An exception will be thrown upon error conditions.
+ *
+ * @param budget QString reference to name of budget
+ *
+ * @return MyMoneyBudget object of budget
+ */
+ virtual const MyMoneyBudget budgetByName(const QString& budget) const;
+
+ /**
+ * This method is used to modify an existing MyMoneyBudget
+ * object. Therefor, the id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param budget const reference to the MyMoneyBudget object to be updated
+ */
+ virtual void modifyBudget( const MyMoneyBudget& budget );
+
+ /**
+ * This method returns the number of budgets currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @return number of budgets known to file
+ */
+ virtual unsigned countBudgets( void ) const;
+
+ /**
+ * This method is used to retrieve a single MyMoneyBudget object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneyBudget object
+ * @return MyMoneyBudget object
+ */
+ virtual MyMoneyBudget budget( const QString& id ) const;
+
+ /**
+ * This method is used to remove an existing MyMoneyBudget object
+ * from the engine. The id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param budget const reference to the MyMoneyBudget object to be updated
+ */
+ virtual void removeBudget(const MyMoneyBudget& budget);
+
+
+
+ /**
+ * Clear all internal caches (used internally for performance measurements)
+ */
+ virtual void clearCache(void);
+
+ /**
+ * This method checks, if the given @p object is referenced
+ * by another engine object.
+ *
+ * @param obj const reference to object to be checked
+ * @param skipCheck MyMoneyFileBitArray with ReferenceCheckBits set for which
+ * the check should be skipped
+ *
+ * @retval false @p object is not referenced
+ * @retval true @p institution is referenced
+ */
+ virtual bool isReferenced(const MyMoneyObject& obj, const MyMoneyFileBitArray& skipCheck = MyMoneyFileBitArray()) const;
+
+ /**
+ * This method is provided to allow closing of the database before logoff
+ */
+ virtual void close(void);
+
+ /**
+ * These methods have to be provided to allow transaction safe data handling.
+ */
+ virtual void startTransaction(void);
+ virtual bool commitTransaction(void);
+ virtual void rollbackTransaction(void);
+
+ // general set functions
+ virtual void setCreationDate(const QDate& val);
+
+ /**
+ * This method is used to get a SQL reader for subsequent database access
+ */
+ virtual KSharedPtr <MyMoneyStorageSql> connectToDatabase
+ (const KURL& url);
+ /**
+ * This method is used when a database file is open, and the data is to
+ * be saved in a different file or format. It will ensure that all data
+ * from the database is available in memory to enable it to be written.
+ */
+ virtual void fillStorage();
+
+ /**
+ * This method is used to set the last modification date of
+ * the storage object. It also clears the dirty flag and should
+ * therefor be called as last operation when loading from a
+ * file.
+ *
+ * @param val QDate of last modification
+ */
+ virtual void setLastModificationDate(const QDate& val);
+
+ /**
+ * This method returns whether a given transaction is already in memory, to avoid
+ * reloading it from the database
+ */
+ virtual bool isDuplicateTransaction(const QString&) const;
+
+ virtual void loadAccounts(const QMap<QString, MyMoneyAccount>& map);
+ virtual void loadTransactions(const QMap<QString, MyMoneyTransaction>& map);
+ virtual void loadInstitutions(const QMap<QString, MyMoneyInstitution>& map);
+ virtual void loadPayees(const QMap<QString, MyMoneyPayee>& map);
+ virtual void loadSchedules(const QMap<QString, MyMoneySchedule>& map);
+ virtual void loadSecurities(const QMap<QString, MyMoneySecurity>& map);
+ virtual void loadCurrencies(const QMap<QString, MyMoneySecurity>& map);
+ virtual void loadReports( const QMap<QString, MyMoneyReport>& reports );
+ virtual void loadBudgets( const QMap<QString, MyMoneyBudget>& budgets );
+ virtual void loadPrices(const MyMoneyPriceList& list);
+
+ virtual unsigned long accountId(void) const;
+ virtual unsigned long transactionId(void) const;
+ virtual unsigned long payeeId(void) const;
+ virtual unsigned long institutionId(void) const;
+ virtual unsigned long scheduleId(void) const;
+ virtual unsigned long securityId(void) const;
+ virtual unsigned long reportId(void) const;
+ virtual unsigned long budgetId(void) const;
+
+ virtual void loadAccountId(const unsigned long id);
+ virtual void loadTransactionId(const unsigned long id);
+ virtual void loadPayeeId(const unsigned long id);
+ virtual void loadInstitutionId(const unsigned long id);
+ virtual void loadScheduleId(const unsigned long id);
+ virtual void loadSecurityId(const unsigned long id);
+ virtual void loadReportId(const unsigned long id);
+ virtual void loadBudgetId(const unsigned long id);
+
+ /**
+ * This method is used to retrieve the whole set of key/value pairs
+ * from the container. It is meant to be used for permanent storage
+ * functionality. See MyMoneyKeyValueContainer::pairs() for details.
+ *
+ * @return QMap<QString, QString> containing all key/value pairs of
+ * this container.
+ */
+ virtual const QMap<QString, QString> pairs(void) const;
+
+ /**
+ * This method is used to initially store a set of key/value pairs
+ * in the container. It is meant to be used for loading functionality
+ * from permanent storage. See MyMoneyKeyValueContainer::setPairs()
+ * for details
+ *
+ * @param list const QMap<QString, QString> containing the set of
+ * key/value pairs to be loaded into the container.
+ *
+ * @note All existing key/value pairs in the container will be deleted.
+ */
+ virtual void setPairs(const QMap<QString, QString>& list);
+
+ /**
+ * This method recalculates the balances of all accounts
+ * based on the transactions stored in the engine.
+ */
+ virtual void rebuildAccountBalances(void);
+
+private:
+ /**
+ * This member variable keeps the creation date of this MyMoneySeqAccessMgr
+ * object. It is set during the constructor and can only be modified using
+ * the stream read operator.
+ */
+ QDate m_creationDate;
+
+ /**
+ * This member variable contains the current fix level of application
+ * data files. (see kmymoneyview.cpp)
+ */
+ unsigned int m_currentFixVersion;
+
+ /**
+ * This member variable contains the current fix level of the
+ * presently open data file. (see kmymoneyview.cpp)
+ */
+ unsigned int m_fileFixVersion;
+
+ /**
+ * This member variable keeps the date of the last modification of
+ * the MyMoneySeqAccessMgr object.
+ */
+ QDate m_lastModificationDate;
+
+ /**
+ * This contains the interface with SQL reader for database access
+ */
+ KSharedPtr <MyMoneyStorageSql> m_sql;
+
+ /**
+ * This member variable keeps the User information.
+ * @see setUser()
+ */
+ MyMoneyPayee m_user;
+
+ /**
+ * This method is used to get the next valid ID for a institution
+ * @return id for a institution
+ */
+ const QString nextInstitutionID(void);
+
+ /**
+ * This method is used to get the next valid ID for an account
+ * @return id for an account
+ */
+ const QString nextAccountID(void);
+
+ /**
+ * This method is used to get the next valid ID for a transaction
+ * @return id for a transaction
+ */
+ const QString nextTransactionID(void);
+
+ /**
+ * This method is used to get the next valid ID for a payee
+ * @return id for a payee
+ */
+ const QString nextPayeeID(void);
+
+ /**
+ * This method is used to get the next valid ID for a scheduled transaction
+ * @return id for a scheduled transaction
+ */
+ const QString nextScheduleID(void);
+
+ /**
+ * This method is used to get the next valid ID for an security object.
+ * @return id for an security object
+ */
+ const QString nextSecurityID(void);
+
+ const QString nextReportID(void);
+
+ /**
+ * This method is used to get the next valid ID for a budget object.
+ * @return id for an budget object
+ */
+ const QString nextBudgetID(void);
+
+ void removeReferences(const QString& id);
+
+ static const int INSTITUTION_ID_SIZE = 6;
+ static const int ACCOUNT_ID_SIZE = 6;
+ static const int TRANSACTION_ID_SIZE = 18;
+ static const int PAYEE_ID_SIZE = 6;
+ static const int SCHEDULE_ID_SIZE = 6;
+ static const int SECURITY_ID_SIZE = 6;
+ static const int REPORT_ID_SIZE = 6;
+ static const int BUDGET_ID_SIZE = 6;
+
+ // Increment this to force an update in KMMView.
+ // This is different from the db schema version stored in
+ // MMStorageSql::m_majorVersion
+ static const int CURRENT_FIX_VERSION = 3;
+
+};
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneydatabasemgrtest.cpp b/kmymoney2/mymoney/storage/mymoneydatabasemgrtest.cpp
new file mode 100644
index 0000000..f6a2bba
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneydatabasemgrtest.cpp
@@ -0,0 +1,1996 @@
+/***************************************************************************
+ mymoneydatabasemgrtest.cpp
+ -------------------
+ copyright : (C) 2008 by Fernando Vilas
+ email : fvilas@iname.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "mymoneydatabasemgrtest.h"
+#include <pwd.h>
+#include <iostream>
+
+MyMoneyDatabaseMgrTest::MyMoneyDatabaseMgrTest()
+ : m_dbAttached (false),
+ m_canOpen (true)
+{}
+
+void MyMoneyDatabaseMgrTest::setUp()
+{
+ m = new MyMoneyDatabaseMgr;
+
+ m->startTransaction();
+}
+
+void MyMoneyDatabaseMgrTest::tearDown()
+{
+ if (m_canOpen) {
+ m->commitTransaction();
+ }
+ if (MyMoneyFile::instance()->storageAttached()) {
+ MyMoneyFile::instance()->detachStorage(m);
+ }
+ delete m;
+}
+
+void MyMoneyDatabaseMgrTest::testEmptyConstructor()
+{
+ MyMoneyPayee user = m->user();
+
+ CPPUNIT_ASSERT(user.name().isEmpty());
+ CPPUNIT_ASSERT(user.address().isEmpty());
+ CPPUNIT_ASSERT(user.city().isEmpty());
+ CPPUNIT_ASSERT(user.state().isEmpty());
+ CPPUNIT_ASSERT(user.postcode().isEmpty());
+ CPPUNIT_ASSERT(user.telephone().isEmpty());
+ CPPUNIT_ASSERT(user.email().isEmpty());
+ CPPUNIT_ASSERT(m->nextInstitutionID() == 0);
+ CPPUNIT_ASSERT(m->nextAccountID() == 0);
+ CPPUNIT_ASSERT(m->nextTransactionID() == 0);
+ CPPUNIT_ASSERT(m->nextPayeeID() == 0);
+ CPPUNIT_ASSERT(m->nextScheduleID() == 0);
+ CPPUNIT_ASSERT(m->nextReportID() == 0);
+ CPPUNIT_ASSERT(m->institutionList().count() == 0);
+
+ QValueList<MyMoneyAccount> accList;
+ m->accountList(accList);
+ CPPUNIT_ASSERT(accList.count() == 0);
+
+ MyMoneyTransactionFilter f;
+ CPPUNIT_ASSERT(m->transactionList(f).count() == 0);
+
+ CPPUNIT_ASSERT(m->payeeList().count() == 0);
+ CPPUNIT_ASSERT(m->scheduleList().count() == 0);
+
+ CPPUNIT_ASSERT(m->m_creationDate == QDate::currentDate());
+}
+
+void MyMoneyDatabaseMgrTest::testCreateDb() {
+ m->commitTransaction();
+
+ // Fetch the list of available drivers
+ QStringList list = QSqlDatabase::drivers();
+ QStringList::Iterator it = list.begin();
+
+ if (it == list.end()) {
+ m_canOpen = false;
+ } else {
+ struct passwd * pwd = getpwuid(geteuid());
+ QString userName;
+ if (pwd != 0) {
+ userName = QString(pwd->pw_name);
+ }
+ //"QMYSQL3"
+ //"QPSQL7"
+ //"QSQLITE3"
+ m_url = "sql://" + userName + "@localhost/kmm_test_driver?driver="
+ //"QPSQL7&mode=single";
+ //"QSQLITE3&mode=single";
+ //"QMYSQL3&mode=single";
+ + *it + "&mode=single";
+ KSharedPtr <MyMoneyStorageSql> sql = m->connectToDatabase(m_url);
+ CPPUNIT_ASSERT(0 != sql);
+ //qDebug("Database driver is %s", sql->driverName().ascii());
+ // Clear the database, so there is a fresh start on each run.
+ if (0 == sql->open(m_url, IO_WriteOnly, true)) {
+ MyMoneyFile::instance()->attachStorage(m);
+ CPPUNIT_ASSERT(sql->writeFile());
+ m->startTransaction();
+ CPPUNIT_ASSERT(0 == sql->upgradeDb());
+ } else {
+ m_canOpen = false;
+ }
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testAttachDb() {
+ if (!m_dbAttached) {
+ testCreateDb();
+ if (m_canOpen) {
+ MyMoneyFile::instance()->detachStorage();
+ KSharedPtr <MyMoneyStorageSql> sql = m->connectToDatabase(m_url);
+ CPPUNIT_ASSERT(sql);
+ int openStatus = sql->open(m_url, IO_ReadWrite);
+ CPPUNIT_ASSERT(0 == openStatus);
+ MyMoneyFile::instance()->attachStorage(m);
+ m->startTransaction();
+ m_dbAttached = true;
+ }
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testSetFunctions() {
+ testAttachDb();
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyPayee user = m->user();
+
+ user.setName("Name");
+ m->setUser(user);
+ user.setAddress("Street");
+ m->setUser(user);
+ user.setCity("Town");
+ m->setUser(user);
+ user.setState("County");
+ m->setUser(user);
+ user.setPostcode("Postcode");
+ m->setUser(user);
+ user.setTelephone("Telephone");
+ m->setUser(user);
+ user.setEmail("Email");
+ m->setUser(user);
+ m->setValue("key", "value");
+
+ user = m->user();
+ CPPUNIT_ASSERT(user.name() == "Name");
+ CPPUNIT_ASSERT(user.address() == "Street");
+ CPPUNIT_ASSERT(user.city() == "Town");
+ CPPUNIT_ASSERT(user.state() == "County");
+ CPPUNIT_ASSERT(user.postcode() == "Postcode");
+ CPPUNIT_ASSERT(user.telephone() == "Telephone");
+ CPPUNIT_ASSERT(user.email() == "Email");
+ CPPUNIT_ASSERT(m->value("key") == "value");
+
+ m->setDirty();
+ m->deletePair("key");
+ CPPUNIT_ASSERT(m->dirty() == false);
+}
+
+void MyMoneyDatabaseMgrTest::testSupportFunctions()
+{
+ testAttachDb();
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ CPPUNIT_ASSERT(m->nextInstitutionID() == "I000001");
+ CPPUNIT_ASSERT(m->nextAccountID() == "A000001");
+ CPPUNIT_ASSERT(m->nextTransactionID() == "T000000000000000001");
+ CPPUNIT_ASSERT(m->nextPayeeID() == "P000001");
+ CPPUNIT_ASSERT(m->nextScheduleID() == "SCH000001");
+ CPPUNIT_ASSERT(m->nextReportID() == "R000001");
+
+ CPPUNIT_ASSERT(m->liability().name() == "Liability");
+ CPPUNIT_ASSERT(m->asset().name() == "Asset");
+ CPPUNIT_ASSERT(m->expense().name() == "Expense");
+ CPPUNIT_ASSERT(m->income().name() == "Income");
+ CPPUNIT_ASSERT(m->equity().name() == "Equity");
+ CPPUNIT_ASSERT(m->dirty() == false);
+}
+
+void MyMoneyDatabaseMgrTest::testIsStandardAccount()
+{
+ testAttachDb();
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ CPPUNIT_ASSERT(m->isStandardAccount(STD_ACC_LIABILITY) == true);
+ CPPUNIT_ASSERT(m->isStandardAccount(STD_ACC_ASSET) == true);
+ CPPUNIT_ASSERT(m->isStandardAccount(STD_ACC_EXPENSE) == true);
+ CPPUNIT_ASSERT(m->isStandardAccount(STD_ACC_INCOME) == true);
+ CPPUNIT_ASSERT(m->isStandardAccount(STD_ACC_EQUITY) == true);
+ CPPUNIT_ASSERT(m->isStandardAccount("A0002") == false);
+}
+
+void MyMoneyDatabaseMgrTest::testNewAccount() {
+ testAttachDb();
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyAccount a;
+
+ a.setName("AccountName");
+ a.setNumber("AccountNumber");
+
+ m->addAccount(a);
+
+ CPPUNIT_ASSERT(m->accountId() == 1);
+ QValueList<MyMoneyAccount> accList;
+ m->accountList(accList);
+ CPPUNIT_ASSERT(accList.count() == 1);
+ CPPUNIT_ASSERT((*(accList.begin())).name() == "AccountName");
+ CPPUNIT_ASSERT((*(accList.begin())).id() == "A000001");
+}
+
+void MyMoneyDatabaseMgrTest::testAccount() {
+ testNewAccount();
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ m->setDirty();
+
+ MyMoneyAccount a;
+
+ // make sure that an invalid ID causes an exception
+ try {
+ a = m->account("Unknown ID");
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+ CPPUNIT_ASSERT(m->dirty() == false);
+
+ // now make sure, that a real ID works
+ try {
+ a = m->account("A000001");
+ CPPUNIT_ASSERT(a.name() == "AccountName");
+ CPPUNIT_ASSERT(a.id() == "A000001");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testAddNewAccount() {
+ testNewAccount();
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyAccount a,b;
+ b.setName("Account2");
+ b.setNumber("Acc2");
+ m->addAccount(b);
+
+ m->setDirty();
+
+ CPPUNIT_ASSERT(m->accountId() == 2);
+ QValueList<MyMoneyAccount> accList;
+ m->accountList(accList);
+ CPPUNIT_ASSERT(accList.count() == 2);
+
+ // try to add account to undefined account
+ try {
+ MyMoneyAccount c("UnknownID", b);
+ m->addAccount(c, a);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ CPPUNIT_ASSERT(m->dirty() == false);
+ // now try to add account 1 as sub-account to account 2
+ a = m->account("A000001");
+ try {
+ CPPUNIT_ASSERT(m->asset().accountList().count() == 0);
+ m->addAccount(b, a);
+ MyMoneyAccount acc (m->account("A000002"));
+ CPPUNIT_ASSERT(acc.accountList()[0] == "A000001");
+ CPPUNIT_ASSERT(acc.accountList().count() == 1);
+ CPPUNIT_ASSERT(m->asset().accountList().count() == 0);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testAddInstitution() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyInstitution i;
+
+ i.setName("Inst Name");
+
+ m->addInstitution(i);
+ CPPUNIT_ASSERT(m->institutionList().count() == 1);
+ CPPUNIT_ASSERT(m->institutionId() == 1);
+ CPPUNIT_ASSERT((*(m->institutionList().begin())).name() == "Inst Name");
+ CPPUNIT_ASSERT((*(m->institutionList().begin())).id() == "I000001");
+}
+
+void MyMoneyDatabaseMgrTest::testInstitution() {
+ testAddInstitution();
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyInstitution i;
+
+ m->setDirty();
+
+ // try to find unknown institution
+ try {
+ i = m->institution("Unknown ID");
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ CPPUNIT_ASSERT(m->dirty() == false);
+
+ // now try to find real institution
+ try {
+ i = m->institution("I000001");
+ CPPUNIT_ASSERT(i.name() == "Inst Name");
+ CPPUNIT_ASSERT(m->dirty() == false);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testAccount2Institution() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddInstitution();
+ testAddNewAccount();
+
+ MyMoneyInstitution i;
+ MyMoneyAccount a, b;
+
+ try {
+ i = m->institution("I000001");
+ a = m->account("A000001");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->setDirty();
+
+ // try to add to a false institution
+ MyMoneyInstitution fake("Unknown ID", i);
+ a.setInstitutionId(fake.id());
+ try {
+ m->modifyAccount(a);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ CPPUNIT_ASSERT(m->dirty() == false);
+ // now try to do it with a real institution
+ try {
+ CPPUNIT_ASSERT(i.accountList().count() == 0);
+ a.setInstitutionId(i.id());
+ m->modifyAccount(a);
+ CPPUNIT_ASSERT(a.institutionId() == i.id());
+ b = m->account("A000001");
+ CPPUNIT_ASSERT(b.institutionId() == i.id());
+ CPPUNIT_ASSERT(i.accountList().count() == 0);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testModifyAccount() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAccount2Institution();
+
+ // test the OK case
+ MyMoneyAccount a = m->account("A000001");
+ a.setName("New account name");
+ m->setDirty();
+ try {
+ m->modifyAccount(a);
+ MyMoneyAccount b = m->account("A000001");
+ CPPUNIT_ASSERT(b.parentAccountId() == a.parentAccountId());
+ CPPUNIT_ASSERT(b.name() == "New account name");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ // modify institution to unknown id
+ MyMoneyAccount c("Unknown ID", a);
+ m->setDirty();
+ try {
+ m->modifyAccount(c);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ // use different account type
+ MyMoneyAccount d;
+ d.setAccountType(MyMoneyAccount::CreditCard);
+ MyMoneyAccount f("A000001", d);
+ try {
+ m->modifyAccount(f);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ // use different parent
+ a.setParentAccountId("A000002");
+ try {
+ m->modifyAccount(c);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testModifyInstitution() {
+ testAddInstitution();
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyInstitution i = m->institution("I000001");
+
+ m->setDirty();
+ i.setName("New inst name");
+ try {
+ m->modifyInstitution(i);
+ i = m->institution("I000001");
+ CPPUNIT_ASSERT(i.name() == "New inst name");
+
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ // try to modify an institution that does not exist
+ MyMoneyInstitution f("Unknown ID", i);
+ try {
+ m->modifyInstitution(f);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testReparentAccount() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ // this one adds some accounts to the database
+ MyMoneyAccount ex1;
+ ex1.setAccountType(MyMoneyAccount::Expense);
+ MyMoneyAccount ex2;
+ ex2.setAccountType(MyMoneyAccount::Expense);
+ MyMoneyAccount ex3;
+ ex3.setAccountType(MyMoneyAccount::Expense);
+ MyMoneyAccount ex4;
+ ex4.setAccountType(MyMoneyAccount::Expense);
+ MyMoneyAccount in;
+ in.setAccountType(MyMoneyAccount::Income);
+ MyMoneyAccount ch;
+ ch.setAccountType(MyMoneyAccount::Checkings);
+
+ ex1.setName("Sales Tax");
+ ex2.setName("Sales Tax 16%");
+ ex3.setName("Sales Tax 7%");
+ ex4.setName("Grosseries");
+
+ in.setName("Salary");
+ ch.setName("My checkings account");
+
+ try {
+ m->addAccount(ex1);
+ m->addAccount(ex2);
+ m->addAccount(ex3);
+ m->addAccount(ex4);
+ m->addAccount(in);
+ m->addAccount(ch);
+
+ CPPUNIT_ASSERT(ex1.id() == "A000001");
+ CPPUNIT_ASSERT(ex2.id() == "A000002");
+ CPPUNIT_ASSERT(ex3.id() == "A000003");
+ CPPUNIT_ASSERT(ex4.id() == "A000004");
+ CPPUNIT_ASSERT(in.id() == "A000005");
+ CPPUNIT_ASSERT(ch.id() == "A000006");
+
+ MyMoneyAccount parent = m->expense();
+
+ m->addAccount(parent, ex1);
+ m->addAccount(ex1, ex2);
+ m->addAccount(parent, ex3);
+ m->addAccount(parent, ex4);
+
+ parent = m->income();
+ m->addAccount(parent, in);
+
+ parent = m->asset();
+ m->addAccount(parent, ch);
+
+ MyMoneyFile::instance()->preloadCache();
+ CPPUNIT_ASSERT(m->expense().accountCount() == 3);
+ CPPUNIT_ASSERT(m->account(ex1.id()).accountCount() == 1);
+ CPPUNIT_ASSERT(ex3.parentAccountId() == STD_ACC_EXPENSE);
+
+ //for (int i = 0; i < 100; ++i) {
+ m->reparentAccount(ex3, ex1);
+ //}
+ MyMoneyFile::instance()->preloadCache();
+ CPPUNIT_ASSERT(m->expense().accountCount() == 2);
+ CPPUNIT_ASSERT(m->account(ex1.id()).accountCount() == 2);
+ CPPUNIT_ASSERT(ex3.parentAccountId() == ex1.id());
+ } catch (MyMoneyException *e) {
+ std::cout << std::endl << e->what() << std::endl;
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testAddTransactions() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testReparentAccount();
+
+ MyMoneyAccount ch;
+ MyMoneyTransaction t1, t2;
+ MyMoneySplit s;
+
+ try {
+ // I made some money, great
+ s.setAccountId("A000006"); // Checkings
+ s.setShares(100000);
+ s.setValue(100000);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t1.addSplit(s);
+
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000005"); // Salary
+ s.setShares(-100000);
+ s.setValue(-100000);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t1.addSplit(s);
+
+ t1.setPostDate(QDate(2002,5,10));
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+
+ m->setDirty();
+ try {
+ m->addTransaction(t1);
+ CPPUNIT_ASSERT(t1.id() == "T000000000000000001");
+ CPPUNIT_ASSERT(t1.splitCount() == 2);
+ CPPUNIT_ASSERT(m->transactionCount() == 1);
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+
+ try {
+ // I spent some money, not so great
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000004"); // Grosseries
+ s.setShares(10000);
+ s.setValue(10000);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t2.addSplit(s);
+
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000002"); // 16% sales tax
+ s.setShares(1200);
+ s.setValue(1200);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t2.addSplit(s);
+
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000003"); // 7% sales tax
+ s.setShares(400);
+ s.setValue(400);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t2.addSplit(s);
+
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000006"); // Checkings account
+ s.setShares(-11600);
+ s.setValue(-11600);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t2.addSplit(s);
+
+ t2.setPostDate(QDate(2002,5,9));
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+ m->setDirty();
+ try {
+ m->addTransaction(t2);
+ CPPUNIT_ASSERT(t2.id() == "T000000000000000002");
+ CPPUNIT_ASSERT(t2.splitCount() == 4);
+ CPPUNIT_ASSERT(m->transactionCount() == 2);
+
+ //QMap<QString, QString>::ConstIterator it_k;
+ MyMoneyTransactionFilter f;
+ QValueList<MyMoneyTransaction> transactionList (m->transactionList(f));
+ QValueList<MyMoneyTransaction>::ConstIterator it_t (transactionList.begin());
+
+ //CPPUNIT_ASSERT((*it_k) == "2002-05-10-T000000000000000001");
+ CPPUNIT_ASSERT((*it_t).id() == "T000000000000000002");
+ //++it_k;
+ ++it_t;
+ //CPPUNIT_ASSERT((*it_k) == "2002-05-09-T000000000000000002");
+ CPPUNIT_ASSERT((*it_t).id() == "T000000000000000001");
+ //++it_k;
+ ++it_t;
+ //CPPUNIT_ASSERT(it_k == m->m_transactionKeys.end());
+ CPPUNIT_ASSERT(it_t == transactionList.end());
+
+ ch = m->account("A000006");
+
+ // check that the account's transaction list is updated
+ QValueList<MyMoneyTransaction> list;
+ MyMoneyTransactionFilter filter("A000006");
+ list = m->transactionList(filter);
+ CPPUNIT_ASSERT(list.size() == 2);
+
+ QValueList<MyMoneyTransaction>::ConstIterator it;
+ it = list.begin();
+ CPPUNIT_ASSERT((*it).id() == "T000000000000000002");
+ ++it;
+ CPPUNIT_ASSERT((*it).id() == "T000000000000000001");
+ ++it;
+ CPPUNIT_ASSERT(it == list.end());
+
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testTransactionCount() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddTransactions();
+ CPPUNIT_ASSERT(m->transactionCount("A000001") == 0);
+ CPPUNIT_ASSERT(m->transactionCount("A000002") == 1);
+ CPPUNIT_ASSERT(m->transactionCount("A000003") == 1);
+ CPPUNIT_ASSERT(m->transactionCount("A000004") == 1);
+ CPPUNIT_ASSERT(m->transactionCount("A000005") == 1);
+ CPPUNIT_ASSERT(m->transactionCount("A000006") == 2);
+}
+
+void MyMoneyDatabaseMgrTest::testAddBudget() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyBudget budget;
+
+ budget.setName("TestBudget");
+ budget.setBudgetStart(QDate::currentDate(Qt::LocalTime));
+
+ m->addBudget(budget);
+
+ CPPUNIT_ASSERT(m->budgetList().count() == 1);
+ CPPUNIT_ASSERT(m->budgetId() == 1);
+ MyMoneyBudget newBudget = m->budgetByName("TestBudget");
+
+ CPPUNIT_ASSERT(budget.budgetStart() == newBudget.budgetStart());
+ CPPUNIT_ASSERT(budget.name() == newBudget.name());
+}
+
+void MyMoneyDatabaseMgrTest::testCopyBudget() {
+ testAddBudget();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ try {
+ MyMoneyBudget oldBudget = m->budgetByName("TestBudget");
+ MyMoneyBudget newBudget = oldBudget;
+
+ newBudget.clearId();
+ newBudget.setName(QString("Copy of %1").arg(oldBudget.name()));
+ m->addBudget(newBudget);
+
+ CPPUNIT_ASSERT(m->budgetList().count() == 2);
+ CPPUNIT_ASSERT(m->budgetId() == 2);
+
+ MyMoneyBudget testBudget = m->budgetByName("TestBudget");
+
+ CPPUNIT_ASSERT(oldBudget.budgetStart() == testBudget.budgetStart());
+ CPPUNIT_ASSERT(oldBudget.name() == testBudget.name());
+
+ testBudget = m->budgetByName("Copy of TestBudget");
+
+ CPPUNIT_ASSERT(testBudget.budgetStart() == newBudget.budgetStart());
+ CPPUNIT_ASSERT(testBudget.name() == newBudget.name());
+ } catch (QString& s) {
+ std::cout << "Error in testCopyBudget(): " << s << std::endl;
+ CPPUNIT_ASSERT(false);
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testModifyBudget() {
+ testAddBudget();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyBudget budget = m->budgetByName("TestBudget");
+
+ budget.setBudgetStart(QDate::currentDate(Qt::LocalTime).addDays(-1));
+
+ m->modifyBudget(budget);
+
+ MyMoneyBudget newBudget = m->budgetByName("TestBudget");
+
+ CPPUNIT_ASSERT(budget.id() == newBudget.id());
+ CPPUNIT_ASSERT(budget.budgetStart() == newBudget.budgetStart());
+ CPPUNIT_ASSERT(budget.name() == newBudget.name());
+}
+
+void MyMoneyDatabaseMgrTest::testRemoveBudget() {
+ testAddBudget();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyBudget budget = m->budgetByName("TestBudget");
+ m->removeBudget(budget);
+
+ try {
+ budget = m->budgetByName("TestBudget");
+ // exception should be thrown if budget not found.
+ CPPUNIT_ASSERT(false);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_ASSERT(true);
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testBalance() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddTransactions();
+
+ CPPUNIT_ASSERT(m->balance("A000001", QDate()).isZero());
+ CPPUNIT_ASSERT(m->balance("A000002", QDate()) == MyMoneyMoney(1200));
+ CPPUNIT_ASSERT(m->balance("A000003", QDate()) == MyMoneyMoney(400));
+ //Add a transaction to zero account A000003
+ MyMoneyTransaction t1;
+ MyMoneySplit s;
+
+ s.setAccountId("A000003");
+ s.setShares(-400);
+ s.setValue(-400);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t1.addSplit(s);
+
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000002");
+ s.setShares(400);
+ s.setValue(400);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t1.addSplit(s);
+
+ t1.setPostDate(QDate(2007,5,10));
+
+ m->addTransaction(t1);
+
+ //qDebug ("Balance of A000003 is 0 = %s", m->balance("A000003", QDate()).toString().ascii());
+ CPPUNIT_ASSERT(m->balance("A000003", QDate()).isZero());
+
+ //qDebug ("Balance of A000001 is 1600 = %s", m->balance("A000001", QDate()).toString().ascii());
+ CPPUNIT_ASSERT(m->totalBalance("A000001", QDate()) == MyMoneyMoney(1600));
+
+ //qDebug ("Balance of A000006 is -11600 = %s", m->balance("A000006", QDate(2002,5,9)).toString().ascii());
+ CPPUNIT_ASSERT(m->balance("A000006", QDate(2002,5,9)) == MyMoneyMoney(-11600));
+
+ //qDebug ("Balance of A000005 is -100000 = %s", m->balance("A000005", QDate(2002,5,10)).toString().ascii());
+ CPPUNIT_ASSERT(m->balance("A000005", QDate(2002,5,10)) == MyMoneyMoney(-100000));
+
+ //qDebug ("Balance of A000006 is 88400 = %s", m->balance("A000006", QDate(2002,5,10)).toString().ascii());
+ CPPUNIT_ASSERT(m->balance("A000006", QDate(2002,5,10)) == MyMoneyMoney(88400));
+}
+
+void MyMoneyDatabaseMgrTest::testModifyTransaction() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddTransactions();
+
+ MyMoneyTransaction t = m->transaction("T000000000000000002");
+ MyMoneySplit s;
+ MyMoneyAccount ch;
+
+ // just modify simple stuff (splits)
+ CPPUNIT_ASSERT(t.splitCount() == 4);
+
+ s = t.splits()[0];
+ s.setShares(11000);
+ s.setValue(11000);
+ t.modifySplit(s);
+
+ CPPUNIT_ASSERT(t.splitCount() == 4);
+ s = t.splits()[3];
+ s.setShares(-12600);
+ s.setValue(-12600);
+ t.modifySplit(s);
+
+ try {
+ CPPUNIT_ASSERT(m->balance("A000004", QDate()) == MyMoneyMoney(10000));
+ CPPUNIT_ASSERT(m->balance("A000006", QDate()) == MyMoneyMoney(100000-11600));
+ CPPUNIT_ASSERT(m->totalBalance("A000001", QDate()) == MyMoneyMoney(1600));
+ m->modifyTransaction(t);
+ CPPUNIT_ASSERT(m->balance("A000004", QDate()) == MyMoneyMoney(11000));
+ CPPUNIT_ASSERT(m->balance("A000006", QDate()) == MyMoneyMoney(100000-12600));
+ CPPUNIT_ASSERT(m->totalBalance("A000001", QDate()) == MyMoneyMoney(1600));
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ // now modify the date
+ t.setPostDate(QDate(2002,5,11));
+ try {
+ m->modifyTransaction(t);
+ CPPUNIT_ASSERT(m->balance("A000004", QDate()) == MyMoneyMoney(11000));
+ CPPUNIT_ASSERT(m->balance("A000006", QDate()) == MyMoneyMoney(100000-12600));
+ CPPUNIT_ASSERT(m->totalBalance("A000001", QDate()) == MyMoneyMoney(1600));
+
+ //QMap<QString, QString>::ConstIterator it_k;
+ MyMoneyTransactionFilter f;
+ QValueList<MyMoneyTransaction> transactionList (m->transactionList(f));
+ QValueList<MyMoneyTransaction>::ConstIterator it_t (transactionList.begin());
+ //it_k = m->m_transactionKeys.begin();
+ //CPPUNIT_ASSERT((*it_k) == "2002-05-10-T000000000000000001");
+ CPPUNIT_ASSERT((*it_t).id() == "T000000000000000001");
+ //++it_k;
+ ++it_t;
+ //CPPUNIT_ASSERT((*it_k) == "2002-05-11-T000000000000000002");
+ CPPUNIT_ASSERT((*it_t).id() == "T000000000000000002");
+ //++it_k;
+ ++it_t;
+ //CPPUNIT_ASSERT(it_k == m->m_transactionKeys.end());
+ CPPUNIT_ASSERT(it_t == transactionList.end());
+
+ ch = m->account("A000006");
+
+ // check that the account's transaction list is updated
+ QValueList<MyMoneyTransaction> list;
+ MyMoneyTransactionFilter filter("A000006");
+ list = m->transactionList(filter);
+ CPPUNIT_ASSERT(list.size() == 2);
+
+ QValueList<MyMoneyTransaction>::ConstIterator it;
+ it = list.begin();
+ CPPUNIT_ASSERT((*it).id() == "T000000000000000001");
+ ++it;
+ CPPUNIT_ASSERT((*it).id() == "T000000000000000002");
+ ++it;
+ CPPUNIT_ASSERT(it == list.end());
+
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+
+void MyMoneyDatabaseMgrTest::testRemoveUnusedAccount() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAccount2Institution();
+
+ MyMoneyAccount a = m->account("A000001");
+ MyMoneyInstitution i = m->institution("I000001");
+
+ m->setDirty();
+ // make sure, we cannot remove the standard account groups
+ try {
+ m->removeAccount(m->liability());
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ try {
+ m->removeAccount(m->asset());
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ try {
+ m->removeAccount(m->expense());
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ try {
+ m->removeAccount(m->income());
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ // try to remove the account still attached to the institution
+ try {
+ m->removeAccount(a);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ // now really remove an account
+
+ try {
+ MyMoneyFile::instance()->preloadCache();
+ i = m->institution("I000001");
+
+ //CPPUNIT_ASSERT(i.accountCount() == 0);
+ CPPUNIT_ASSERT(i.accountCount() == 1);
+ CPPUNIT_ASSERT(m->accountCount() == 7);
+
+ a.setInstitutionId(QString());
+ m->modifyAccount(a);
+ m->removeAccount(a);
+ CPPUNIT_ASSERT(m->accountCount() == 6);
+ i = m->institution("I000001");
+ CPPUNIT_ASSERT(i.accountCount() == 0);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testRemoveUsedAccount() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddTransactions();
+
+ MyMoneyAccount a = m->account("A000006");
+
+ try {
+ m->removeAccount(a);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testRemoveInstitution() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testModifyInstitution();
+ testReparentAccount();
+
+ MyMoneyInstitution i;
+ MyMoneyAccount a;
+
+ // assign the checkings account to the institution
+ try {
+ i = m->institution("I000001");
+ a = m->account("A000006");
+ a.setInstitutionId(i.id());
+ m->modifyAccount(a);
+ CPPUNIT_ASSERT(i.accountCount() == 0);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->setDirty();
+ // now remove the institution and see if the account survived ;-)
+ try {
+ m->removeInstitution(i);
+ a.setInstitutionId(QString());
+ m->modifyAccount(a);
+ a = m->account("A000006");
+ CPPUNIT_ASSERT(a.institutionId().isEmpty());
+ CPPUNIT_ASSERT(m->institutionCount() == 0);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testRemoveTransaction() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddTransactions();
+
+ MyMoneyTransaction t = m->transaction("T000000000000000002");
+
+ m->setDirty();
+ try {
+ m->removeTransaction(t);
+ CPPUNIT_ASSERT(m->transactionCount() == 1);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testTransactionList() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddTransactions();
+
+ QValueList<MyMoneyTransaction> list;
+ MyMoneyTransactionFilter filter("A000006");
+ list = m->transactionList(filter);
+ CPPUNIT_ASSERT(list.count() == 2);
+ CPPUNIT_ASSERT((*(list.at(0))).id() == "T000000000000000002");
+ CPPUNIT_ASSERT((*(list.at(1))).id() == "T000000000000000001");
+
+ filter.clear();
+ filter.addAccount(QString("A000003"));
+ list = m->transactionList(filter);
+ CPPUNIT_ASSERT(list.count() == 1);
+ CPPUNIT_ASSERT((*(list.at(0))).id() == "T000000000000000002");
+
+ filter.clear();
+ list = m->transactionList(filter);
+ CPPUNIT_ASSERT(list.count() == 2);
+ CPPUNIT_ASSERT((*(list.at(0))).id() == "T000000000000000002");
+ CPPUNIT_ASSERT((*(list.at(1))).id() == "T000000000000000001");
+}
+
+void MyMoneyDatabaseMgrTest::testAddPayee() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyPayee p;
+
+ p.setName("THB");
+ m->setDirty();
+ try {
+ CPPUNIT_ASSERT(m->payeeId() == 0);
+ m->addPayee(p);
+ CPPUNIT_ASSERT(m->payeeId() == 1);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+}
+
+void MyMoneyDatabaseMgrTest::testSetAccountName() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ try {
+ m->setAccountName(STD_ACC_LIABILITY, "Verbindlichkeiten");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+ try {
+ m->setAccountName(STD_ACC_ASSET, "Verm�gen");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+ try {
+ m->setAccountName(STD_ACC_EXPENSE, "Ausgaben");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+ try {
+ m->setAccountName(STD_ACC_INCOME, "Einnahmen");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ MyMoneyFile::instance()->preloadCache();
+
+ CPPUNIT_ASSERT(m->liability().name() == "Verbindlichkeiten");
+ CPPUNIT_ASSERT(m->asset().name() == "Verm�gen");
+ CPPUNIT_ASSERT(m->expense().name() == "Ausgaben");
+ CPPUNIT_ASSERT(m->income().name() == "Einnahmen");
+
+ try {
+ m->setAccountName("A000001", "New account name");
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testModifyPayee() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyPayee p;
+
+ testAddPayee();
+
+ p = m->payee("P000001");
+ p.setName("New name");
+ m->setDirty();
+ try {
+ m->modifyPayee(p);
+ p = m->payee("P000001");
+ CPPUNIT_ASSERT(p.name() == "New name");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testRemovePayee() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddPayee();
+ m->setDirty();
+
+ // check that we can remove an unreferenced payee
+ MyMoneyPayee p = m->payee("P000001");
+ try {
+ CPPUNIT_ASSERT(m->payeeList().count() == 1);
+ m->removePayee(p);
+ CPPUNIT_ASSERT(m->payeeList().count() == 0);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ // add transaction
+ testAddTransactions();
+
+ MyMoneyTransaction tr = m->transaction("T000000000000000001");
+ MyMoneySplit sp;
+ sp = tr.splits()[0];
+ sp.setPayeeId("P000001");
+ tr.modifySplit(sp);
+
+ // check that we cannot add a transaction referencing
+ // an unknown payee
+ try {
+ m->modifyTransaction(tr);
+ CPPUNIT_FAIL("Expected exception");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ // reset here, so that the
+ // testAddPayee will not fail
+ m->loadPayeeId(0);
+ testAddPayee();
+
+ // check that it works when the payee exists
+ try {
+ m->modifyTransaction(tr);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->setDirty();
+
+ // now check, that we cannot remove the payee
+ try {
+ m->removePayee(p);
+ CPPUNIT_FAIL("Expected exception");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+ CPPUNIT_ASSERT(m->payeeList().count() == 1);
+}
+
+
+void MyMoneyDatabaseMgrTest::testRemoveAccountFromTree() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneyAccount a, b, c;
+ a.setName("Acc A");
+ b.setName("Acc B");
+ c.setName("Acc C");
+
+ // build a tree A -> B -> C, remove B and see if A -> C
+ // remains in the storage manager
+
+ try {
+ m->addAccount(a);
+ m->addAccount(b);
+ m->addAccount(c);
+ m->reparentAccount(b, a);
+ m->reparentAccount(c, b);
+
+ CPPUNIT_ASSERT(a.accountList().count() == 1);
+ CPPUNIT_ASSERT(m->account(a.accountList()[0]).name() == "Acc B");
+
+ CPPUNIT_ASSERT(b.accountList().count() == 1);
+ CPPUNIT_ASSERT(m->account(b.accountList()[0]).name() == "Acc C");
+
+ CPPUNIT_ASSERT(c.accountList().count() == 0);
+
+ m->removeAccount(b);
+
+ // reload account info from titutionIDtorage
+ a = m->account(a.id());
+ c = m->account(c.id());
+
+ try {
+ b = m->account(b.id());
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+ CPPUNIT_ASSERT(a.accountList().count() == 1);
+ CPPUNIT_ASSERT(m->account(a.accountList()[0]).name() == "Acc C");
+
+ CPPUNIT_ASSERT(c.accountList().count() == 0);
+
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testPayeeName() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddPayee();
+
+ MyMoneyPayee p;
+ QString name("THB");
+
+ // OK case
+ try {
+ p = m->payeeByName(name);
+ CPPUNIT_ASSERT(p.name() == "THB");
+ CPPUNIT_ASSERT(p.id() == "P000001");
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+
+ // Not OK case
+ name = "Thb";
+ try {
+ p = m->payeeByName(name);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testAssignment() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddTransactions();
+
+ MyMoneyPayee user;
+ user.setName("Thomas");
+ m->setUser(user);
+
+ MyMoneyDatabaseMgr test = *m;
+ testEquality(&test);
+}
+
+void MyMoneyDatabaseMgrTest::testEquality(const MyMoneyDatabaseMgr *t)
+{
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ CPPUNIT_ASSERT(m->user().name() == t->user().name());
+ CPPUNIT_ASSERT(m->user().address() == t->user().address());
+ CPPUNIT_ASSERT(m->user().city() == t->user().city());
+ CPPUNIT_ASSERT(m->user().state() == t->user().state());
+ CPPUNIT_ASSERT(m->user().postcode() == t->user().postcode());
+ CPPUNIT_ASSERT(m->user().telephone() == t->user().telephone());
+ CPPUNIT_ASSERT(m->user().email() == t->user().email());
+ //CPPUNIT_ASSERT(m->nextInstitutionID() == t->nextInstitutionID());
+ //CPPUNIT_ASSERT(m->nextAccountID() == t->nextAccountID());
+ //CPPUNIT_ASSERT(m->m_nextTransactionID == t->m_nextTransactionID);
+ //CPPUNIT_ASSERT(m->nextPayeeID() == t->nextPayeeID());
+ //CPPUNIT_ASSERT(m->m_nextScheduleID == t->m_nextScheduleID);
+ CPPUNIT_ASSERT(m->dirty() == t->dirty());
+ CPPUNIT_ASSERT(m->m_creationDate == t->m_creationDate);
+ CPPUNIT_ASSERT(m->m_lastModificationDate == t->m_lastModificationDate);
+
+ /*
+ * make sure, that the keys and values are the same
+ * on the left and the right side
+ */
+ //CPPUNIT_ASSERT(m->payeeList().keys() == t->payeeList().keys());
+ //CPPUNIT_ASSERT(m->payeeList().values() == t->payeeList().values());
+ CPPUNIT_ASSERT(m->payeeList() == t->payeeList());
+ //CPPUNIT_ASSERT(m->m_transactionKeys.keys() == t->m_transactionKeys.keys());
+ //CPPUNIT_ASSERT(m->m_transactionKeys.values() == t->m_transactionKeys.values());
+ //CPPUNIT_ASSERT(m->institutionList().keys() == t->institutionList().keys());
+ //CPPUNIT_ASSERT(m->institutionList().values() == t->institutionList().values());
+ //CPPUNIT_ASSERT(m->m_accountList.keys() == t->m_accountList.keys());
+ //CPPUNIT_ASSERT(m->m_accountList.values() == t->m_accountList.values());
+ //CPPUNIT_ASSERT(m->m_transactionList.keys() == t->m_transactionList.keys());
+ //CPPUNIT_ASSERT(m->m_transactionList.values() == t->m_transactionList.values());
+ //CPPUNIT_ASSERT(m->m_balanceCache.keys() == t->m_balanceCache.keys());
+ //CPPUNIT_ASSERT(m->m_balanceCache.values() == t->m_balanceCache.values());
+
+// CPPUNIT_ASSERT(m->scheduleList().keys() == t->scheduleList().keys());
+// CPPUNIT_ASSERT(m->scheduleList().values() == t->scheduleList().values());
+}
+
+void MyMoneyDatabaseMgrTest::testDuplicate() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ const MyMoneyDatabaseMgr* t;
+
+ testModifyTransaction();
+
+ t = m->duplicate();
+ testEquality(t);
+ delete t;
+}
+
+void MyMoneyDatabaseMgrTest::testAddSchedule() {
+ /* Note addSchedule() now calls validate as it should
+ * so we need an account id. Later this will
+ * be checked to make sure its a valid account id. The
+ * tests currently fail because no splits are defined
+ * for the schedules transaction.
+ */
+
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ // put some accounts in the db, so the tests don't break
+ testReparentAccount();
+
+ try {
+ CPPUNIT_ASSERT(m->scheduleList().count() == 0);
+ MyMoneyTransaction t1;
+ MyMoneySplit s1, s2;
+ s1.setAccountId("A000001");
+ t1.addSplit(s1);
+ s2.setAccountId("A000002");
+ t1.addSplit(s2);
+ MyMoneySchedule schedule("Sched-Name",
+ MyMoneySchedule::TYPE_DEPOSIT,
+ MyMoneySchedule::OCCUR_DAILY, 1,
+ MyMoneySchedule::STYPE_MANUALDEPOSIT,
+ QDate(),
+ QDate(),
+ true,
+ false);
+ t1.setPostDate(QDate(2003,7,10));
+ schedule.setTransaction(t1);
+
+ m->addSchedule(schedule);
+
+ CPPUNIT_ASSERT(m->scheduleList().count() == 1);
+ CPPUNIT_ASSERT(schedule.id() == "SCH000001");
+ MyMoneyFile::instance()->clearCache();
+ CPPUNIT_ASSERT(m->schedule("SCH000001").id() == "SCH000001");
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ try {
+ MyMoneySchedule schedule("Sched-Name",
+ MyMoneySchedule::TYPE_DEPOSIT,
+ MyMoneySchedule::OCCUR_DAILY, 1,
+ MyMoneySchedule::STYPE_MANUALDEPOSIT,
+ QDate(),
+ QDate(),
+ true,
+ false);
+ m->addSchedule(schedule);
+ CPPUNIT_FAIL("Exception expected");
+ } catch(MyMoneyException *e) {
+ delete e;
+ }
+
+ // now try with a bad account, so this should cause an exception
+ // TODO: enable this check without corrupting other tests
+// try {
+// MyMoneyTransaction t1;
+// MyMoneySplit s1, s2;
+// s1.setAccountId("Abadaccount1");
+// t1.addSplit(s1);
+// s2.setAccountId("Abadaccount2");
+// t1.addSplit(s2);
+// MyMoneySchedule schedule("Sched-Name",
+// MyMoneySchedule::TYPE_DEPOSIT,
+// MyMoneySchedule::OCCUR_DAILY, 1,
+// MyMoneySchedule::STYPE_MANUALDEPOSIT,
+// QDate(),
+// QDate(),
+// true,
+// false);
+// t1.setPostDate(QDate(2003,7,10));
+// schedule.setTransaction(t1);
+
+// m->addSchedule(schedule);
+// CPPUNIT_FAIL("Exception expected, but not thrown");
+// } catch(MyMoneyException *e) {
+// delete e;
+// // Exception caught as expected.
+// }
+
+}
+
+void MyMoneyDatabaseMgrTest::testSchedule() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddSchedule();
+ MyMoneySchedule sched;
+
+ sched = m->schedule("SCH000001");
+ CPPUNIT_ASSERT(sched.name() == "Sched-Name");
+ CPPUNIT_ASSERT(sched.id() == "SCH000001");
+
+ try {
+ m->schedule("SCH000002");
+ CPPUNIT_FAIL("Exception expected");
+ } catch(MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testModifySchedule() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddSchedule();
+ MyMoneySchedule sched;
+
+ sched = m->schedule("SCH000001");
+ sched.setId("SCH000002");
+ try {
+ m->modifySchedule(sched);
+ CPPUNIT_FAIL("Exception expected");
+ } catch(MyMoneyException *e) {
+ delete e;
+ }
+
+ sched = m->schedule("SCH000001");
+ sched.setName("New Sched-Name");
+ try {
+ m->modifySchedule(sched);
+ CPPUNIT_ASSERT(m->scheduleList().count() == 1);
+ CPPUNIT_ASSERT((*(m->scheduleList().begin())).name() == "New Sched-Name");
+ CPPUNIT_ASSERT((*(m->scheduleList().begin())).id() == "SCH000001");
+
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+}
+
+void MyMoneyDatabaseMgrTest::testRemoveSchedule() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ testAddSchedule();
+ MyMoneySchedule sched;
+
+ sched = m->schedule("SCH000001");
+ sched.setId("SCH000002");
+ try {
+ m->removeSchedule(sched);
+ CPPUNIT_FAIL("Exception expected");
+ } catch(MyMoneyException *e) {
+ delete e;
+ }
+
+ sched = m->schedule("SCH000001");
+ try {
+ m->removeSchedule(sched);
+ CPPUNIT_ASSERT(m->scheduleList().count() == 0);
+
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testScheduleList() {
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ // put some accounts in the db, so the tests don't break
+ testReparentAccount();
+
+ QDate testDate = QDate::currentDate();
+ QDate notOverdue = testDate.addDays(2);
+ QDate overdue = testDate.addDays(-2);
+
+ MyMoneyTransaction t1;
+ MyMoneySplit s1, s2;
+ s1.setAccountId("A000001");
+ t1.addSplit(s1);
+ s2.setAccountId("A000002");
+ t1.addSplit(s2);
+ MyMoneySchedule schedule1("Schedule 1",
+ MyMoneySchedule::TYPE_BILL,
+ MyMoneySchedule::OCCUR_ONCE, 1,
+ MyMoneySchedule::STYPE_DIRECTDEBIT,
+ QDate(),
+ QDate(),
+ false,
+ false);
+ t1.setPostDate(notOverdue);
+ schedule1.setTransaction(t1);
+ schedule1.setLastPayment(notOverdue);
+
+ MyMoneyTransaction t2;
+ MyMoneySplit s3, s4;
+ s3.setAccountId("A000001");
+ t2.addSplit(s3);
+ s4.setAccountId("A000003");
+ t2.addSplit(s4);
+ MyMoneySchedule schedule2("Schedule 2",
+ MyMoneySchedule::TYPE_DEPOSIT,
+ MyMoneySchedule::OCCUR_DAILY, 1,
+ MyMoneySchedule::STYPE_DIRECTDEPOSIT,
+ QDate(),
+ QDate(),
+ false,
+ false);
+ t2.setPostDate(notOverdue.addDays(1));
+ schedule2.setTransaction(t2);
+ schedule2.setLastPayment(notOverdue.addDays(1));
+
+ MyMoneyTransaction t3;
+ MyMoneySplit s5, s6;
+ s5.setAccountId("A000005");
+ t3.addSplit(s5);
+ s6.setAccountId("A000006");
+ t3.addSplit(s6);
+ MyMoneySchedule schedule3("Schedule 3",
+ MyMoneySchedule::TYPE_TRANSFER,
+ MyMoneySchedule::OCCUR_WEEKLY, 1,
+ MyMoneySchedule::STYPE_OTHER,
+ QDate(),
+ QDate(),
+ false,
+ false);
+ t3.setPostDate(notOverdue.addDays(2));
+ schedule3.setTransaction(t3);
+ schedule3.setLastPayment(notOverdue.addDays(2));
+
+ MyMoneyTransaction t4;
+ MyMoneySplit s7, s8;
+ s7.setAccountId("A000005");
+ t4.addSplit(s7);
+ s8.setAccountId("A000006");
+ t4.addSplit(s8);
+ MyMoneySchedule schedule4("Schedule 4",
+ MyMoneySchedule::TYPE_BILL,
+ MyMoneySchedule::OCCUR_WEEKLY, 1,
+ MyMoneySchedule::STYPE_WRITECHEQUE,
+ QDate(),
+ notOverdue.addDays(31),
+ false,
+ false);
+ t4.setPostDate(overdue.addDays(-7));
+ schedule4.setTransaction(t4);
+
+ try {
+ m->addSchedule(schedule1);
+ m->addSchedule(schedule2);
+ m->addSchedule(schedule3);
+ m->addSchedule(schedule4);
+ } catch(MyMoneyException *e) {
+ qDebug("Error: %s", e->what().latin1());
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ QValueList<MyMoneySchedule> list;
+
+ // no filter
+ list = m->scheduleList();
+ CPPUNIT_ASSERT(list.count() == 4);
+
+ // filter by type
+ list = m->scheduleList("", MyMoneySchedule::TYPE_BILL);
+ CPPUNIT_ASSERT(list.count() == 2);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 1");
+ CPPUNIT_ASSERT(list[1].name() == "Schedule 4");
+
+ // filter by occurence
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_DAILY);
+ CPPUNIT_ASSERT(list.count() == 1);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 2");
+
+ // filter by payment type
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_ANY,
+ MyMoneySchedule::STYPE_DIRECTDEPOSIT);
+ CPPUNIT_ASSERT(list.count() == 1);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 2");
+
+ // filter by account
+ list = m->scheduleList("A01");
+ CPPUNIT_ASSERT(list.count() == 0);
+ list = m->scheduleList("A000001");
+ CPPUNIT_ASSERT(list.count() == 2);
+ list = m->scheduleList("A000002");
+ CPPUNIT_ASSERT(list.count() == 1);
+
+ // filter by start date
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_ANY,
+ MyMoneySchedule::STYPE_ANY,
+ notOverdue.addDays(31));
+ CPPUNIT_ASSERT(list.count() == 3);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 2");
+ CPPUNIT_ASSERT(list[1].name() == "Schedule 3");
+ CPPUNIT_ASSERT(list[2].name() == "Schedule 4");
+
+ // filter by end date
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_ANY,
+ MyMoneySchedule::STYPE_ANY,
+ QDate(),
+ notOverdue.addDays(1));
+ CPPUNIT_ASSERT(list.count() == 3);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 1");
+ CPPUNIT_ASSERT(list[1].name() == "Schedule 2");
+ CPPUNIT_ASSERT(list[2].name() == "Schedule 4");
+
+ // filter by start and end date
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_ANY,
+ MyMoneySchedule::STYPE_ANY,
+ notOverdue.addDays(-1),
+ notOverdue.addDays(1));
+ CPPUNIT_ASSERT(list.count() == 2);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 1");
+ CPPUNIT_ASSERT(list[1].name() == "Schedule 2");
+
+ // filter by overdue status
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_ANY,
+ MyMoneySchedule::STYPE_ANY,
+ QDate(),
+ QDate(),
+ true);
+ CPPUNIT_ASSERT(list.count() == 1);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 4");
+}
+
+void MyMoneyDatabaseMgrTest::testAddCurrency()
+{
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneySecurity curr("EUR", "Euro", "?", 100, 100);
+ CPPUNIT_ASSERT(m->currencyList().count() == 0);
+ m->setDirty();
+ try {
+ m->addCurrency(curr);
+ CPPUNIT_ASSERT(m->currencyList().count() == 1);
+ CPPUNIT_ASSERT((*(m->currencyList().begin())).name() == "Euro");
+ CPPUNIT_ASSERT((*(m->currencyList().begin())).id() == "EUR");
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->setDirty();
+ try {
+ m->addCurrency(curr);
+ CPPUNIT_FAIL("Expected exception missing");
+ } catch(MyMoneyException *e) {
+ CPPUNIT_ASSERT(m->dirty() == false);
+ delete e;
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testModifyCurrency()
+{
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneySecurity curr("EUR", "Euro", "?", 100, 100);
+ testAddCurrency();
+ m->setDirty();
+ curr.setName("EURO");
+ try {
+ m->modifyCurrency(curr);
+ CPPUNIT_ASSERT(m->currencyList().count() == 1);
+ CPPUNIT_ASSERT((*(m->currencyList().begin())).name() == "EURO");
+ CPPUNIT_ASSERT((*(m->currencyList().begin())).id() == "EUR");
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->setDirty();
+
+ MyMoneySecurity unknownCurr("DEM", "Deutsche Mark", "DM", 100, 100);
+ try {
+ m->modifyCurrency(unknownCurr);
+ CPPUNIT_FAIL("Expected exception missing");
+ } catch(MyMoneyException *e) {
+ CPPUNIT_ASSERT(m->dirty() == false);
+ delete e;
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testRemoveCurrency()
+{
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneySecurity curr("EUR", "Euro", "?", 100, 100);
+ testAddCurrency();
+ m->setDirty();
+ try {
+ m->removeCurrency(curr);
+ CPPUNIT_ASSERT(m->currencyList().count() == 0);
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->setDirty();
+
+ MyMoneySecurity unknownCurr("DEM", "Deutsche Mark", "DM", 100, 100);
+ try {
+ m->removeCurrency(unknownCurr);
+ CPPUNIT_FAIL("Expected exception missing");
+ } catch(MyMoneyException *e) {
+ CPPUNIT_ASSERT(m->dirty() == false);
+ delete e;
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testCurrency()
+{
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ MyMoneySecurity curr("EUR", "Euro", "?", 100, 100);
+ MyMoneySecurity newCurr;
+ testAddCurrency();
+ m->setDirty();
+ try {
+ newCurr = m->currency("EUR");
+ CPPUNIT_ASSERT(m->dirty() == false);
+ CPPUNIT_ASSERT(newCurr.id() == curr.id());
+ CPPUNIT_ASSERT(newCurr.name() == curr.name());
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ try {
+ m->currency("DEM");
+ CPPUNIT_FAIL("Expected exception missing");
+ } catch(MyMoneyException *e) {
+ CPPUNIT_ASSERT(m->dirty() == false);
+ delete e;
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testCurrencyList()
+{
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ CPPUNIT_ASSERT(m->currencyList().count() == 0);
+
+ testAddCurrency();
+ CPPUNIT_ASSERT(m->currencyList().count() == 1);
+
+ MyMoneySecurity unknownCurr("DEM", "Deutsche Mark", "DM", 100, 100);
+ try {
+ m->addCurrency(unknownCurr);
+ m->setDirty();
+ CPPUNIT_ASSERT(m->currencyList().count() == 2);
+ CPPUNIT_ASSERT(m->currencyList().count() == 2);
+ CPPUNIT_ASSERT(m->dirty() == false);
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneyDatabaseMgrTest::testAccountList()
+{
+ testAttachDb();
+
+ if (!m_canOpen) {
+ std::cout << "Database test skipped because no database could be opened." << std::endl;
+ return;
+ }
+
+ QValueList<MyMoneyAccount> accounts;
+ m->accountList(accounts);
+ CPPUNIT_ASSERT(accounts.count() == 0);
+ testAddNewAccount();
+ accounts.clear();
+ m->accountList(accounts);
+ CPPUNIT_ASSERT(accounts.count() == 2);
+
+ MyMoneyAccount a = m->account("A000001");
+ MyMoneyAccount b = m->account("A000002");
+ m->reparentAccount(b, a);
+ accounts.clear();
+ m->accountList(accounts);
+ CPPUNIT_ASSERT(accounts.count() == 2);
+}
+
diff --git a/kmymoney2/mymoney/storage/mymoneydatabasemgrtest.h b/kmymoney2/mymoney/storage/mymoneydatabasemgrtest.h
new file mode 100644
index 0000000..8ab4b64
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneydatabasemgrtest.h
@@ -0,0 +1,143 @@
+/***************************************************************************
+ mymoneydatabasemgrtest.h
+ -------------------
+ copyright : (C) 2008 by Fernando Vilas
+ email : fvilas@iname.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. *
+ * *
+ ***************************************************************************/
+
+#ifndef __MYMONEYDATABASEMGRTEST_H__
+#define __MYMONEYDATABASEMGRTEST_H__
+
+#include <cppunit/TestCaller.h>
+#include <cppunit/TestCase.h>
+#include <cppunit/TestSuite.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "../autotest.h"
+
+#define private public
+#define protected public
+#include "../mymoneyobject.h"
+#include "mymoneydatabasemgr.h"
+#undef private
+
+class MyMoneyDatabaseMgrTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(MyMoneyDatabaseMgrTest);
+ CPPUNIT_TEST(testEmptyConstructor);
+ CPPUNIT_TEST(testCreateDb);
+ CPPUNIT_TEST(testAttachDb);
+ CPPUNIT_TEST(testSetFunctions);
+ CPPUNIT_TEST(testSupportFunctions);
+ CPPUNIT_TEST(testIsStandardAccount);
+ CPPUNIT_TEST(testNewAccount);
+ CPPUNIT_TEST(testAddNewAccount);
+ CPPUNIT_TEST(testReparentAccount);
+ CPPUNIT_TEST(testAddInstitution);
+ CPPUNIT_TEST(testInstitution);
+ CPPUNIT_TEST(testAccount2Institution);
+ CPPUNIT_TEST(testModifyAccount);
+ CPPUNIT_TEST(testModifyInstitution);
+ CPPUNIT_TEST(testAddTransactions);
+ CPPUNIT_TEST(testTransactionCount);
+ CPPUNIT_TEST(testBalance);
+ CPPUNIT_TEST(testAddBudget);
+ CPPUNIT_TEST(testCopyBudget);
+ CPPUNIT_TEST(testModifyBudget);
+ CPPUNIT_TEST(testRemoveBudget);
+ CPPUNIT_TEST(testModifyTransaction);
+ CPPUNIT_TEST(testRemoveUnusedAccount);
+ CPPUNIT_TEST(testRemoveUsedAccount);
+ CPPUNIT_TEST(testRemoveInstitution);
+ CPPUNIT_TEST(testRemoveTransaction);
+ CPPUNIT_TEST(testTransactionList);
+ CPPUNIT_TEST(testAddPayee);
+ CPPUNIT_TEST(testSetAccountName);
+ CPPUNIT_TEST(testModifyPayee);
+ CPPUNIT_TEST(testPayeeName);
+ CPPUNIT_TEST(testRemovePayee);
+ CPPUNIT_TEST(testRemoveAccountFromTree);
+ CPPUNIT_TEST(testAssignment);
+ CPPUNIT_TEST(testDuplicate);
+ CPPUNIT_TEST(testAddSchedule);
+ CPPUNIT_TEST(testModifySchedule);
+ CPPUNIT_TEST(testRemoveSchedule);
+ CPPUNIT_TEST(testSchedule);
+ CPPUNIT_TEST(testScheduleList);
+ CPPUNIT_TEST(testAddCurrency);
+ CPPUNIT_TEST(testModifyCurrency);
+ CPPUNIT_TEST(testRemoveCurrency);
+ CPPUNIT_TEST(testCurrency);
+ CPPUNIT_TEST(testCurrencyList);
+ CPPUNIT_TEST(testAccountList);
+ CPPUNIT_TEST_SUITE_END();
+
+protected:
+ MyMoneyDatabaseMgr *m;
+ bool m_dbAttached;
+ bool m_canOpen;
+ KURL m_url;
+public:
+ MyMoneyDatabaseMgrTest();
+
+ void setUp();
+ void tearDown();
+ void testEmptyConstructor();
+ void testCreateDb();
+ void testAttachDb();
+ void testSetFunctions();
+ void testIsStandardAccount();
+ void testNewAccount();
+ void testAccount();
+ void testAddNewAccount();
+ void testAddInstitution();
+ void testInstitution();
+ void testAccount2Institution();
+ void testModifyAccount();
+ void testModifyInstitution();
+ void testReparentAccount();
+ void testAddTransactions();
+ void testTransactionCount();
+ void testAddBudget();
+ void testCopyBudget();
+ void testModifyBudget();
+ void testRemoveBudget();
+ void testBalance();
+ void testModifyTransaction();
+ void testRemoveUnusedAccount();
+ void testRemoveUsedAccount();
+ void testRemoveInstitution();
+ void testRemoveTransaction();
+ void testTransactionList();
+ void testAddPayee();
+ void testSetAccountName();
+ void testModifyPayee();
+ void testPayeeName();
+ void testRemovePayee();
+ void testRemoveAccountFromTree();
+ void testAssignment();
+ void testEquality(const MyMoneyDatabaseMgr* t);
+ void testDuplicate();
+ void testAddSchedule();
+ void testSchedule();
+ void testModifySchedule();
+ void testRemoveSchedule();
+ void testSupportFunctions();
+ void testScheduleList();
+ void testAddCurrency();
+ void testModifyCurrency();
+ void testRemoveCurrency();
+ void testCurrency();
+ void testCurrencyList();
+ void testAccountList();
+};
+
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneymap.h b/kmymoney2/mymoney/storage/mymoneymap.h
new file mode 100644
index 0000000..fa5cda8
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneymap.h
@@ -0,0 +1,328 @@
+#include <stdint.h>
+#include <qmap.h>
+#include <qptrstack.h>
+#include <kmymoney/mymoneyexception.h>
+
+#ifndef MYMONEYMAP_H
+#define MYMONEYMAP_H
+
+#define MY_OWN_DEBUG 0
+
+/**
+ * @author Thomas Baumgart
+ *
+ * This template class adds transaction security to the QMap<> class.
+ * The interface is very simple. Before you perform any changes,
+ * you have to call the startTransaction() method. Then you can use
+ * the insert(), modify() and remove() methods to modify the map.
+ * Changes are recorded and if you are finished, use the
+ * commitTransaction() to finish the transaction. If you want to go
+ * back before you have committed the transaction, use
+ * rollbackTransaction() to set the container to the state it was
+ * in before you called startTransaction().
+ *
+ * The implementation is based on the command pattern, in case
+ * someone is interested.
+ */
+template <class Key, class T>
+class MyMoneyMap : protected QMap<Key, T>
+{
+public:
+ // typedef QMapConstIterator<Key, T> const_iterator;
+
+ MyMoneyMap() : QMap<Key, T>() {}
+ virtual ~MyMoneyMap() {}
+
+ void startTransaction(unsigned long* id = 0)
+ {
+ m_stack.push(new MyMoneyMapStart(this, id));
+ }
+
+ void rollbackTransaction(void)
+ {
+ if(m_stack.count() == 0)
+ throw new MYMONEYEXCEPTION("No transaction started to rollback changes");
+
+ // undo all actions
+ MyMoneyMapAction* action;
+ while(m_stack.count()) {
+ action = m_stack.pop();
+ action->undo();
+ delete action;
+ }
+ }
+
+ bool commitTransaction(void)
+ {
+ if(m_stack.count() == 0)
+ throw new MYMONEYEXCEPTION("No transaction started to commit changes");
+
+ bool rc = m_stack.count() > 1;
+ m_stack.setAutoDelete(true);
+ m_stack.clear();
+ return rc;
+ }
+
+ void insert(const Key& key, const T& obj)
+ {
+ if(m_stack.count() == 0)
+ throw new MYMONEYEXCEPTION("No transaction started to insert new element into container");
+
+ // store object in
+ m_stack.push(new MyMoneyMapInsert(this, key, obj));
+ }
+
+ void modify(const Key& key, const T& obj)
+ {
+ if(m_stack.count() == 0)
+ throw new MYMONEYEXCEPTION("No transaction started to modify element in container");
+
+#if 0
+ // had to take this out, because we use QPair in one instance as key
+ if(key.isEmpty())
+ throw new MYMONEYEXCEPTION("No key to update object");
+#endif
+
+ m_stack.push(new MyMoneyMapModify(this, key, obj));
+ }
+
+ void remove(const Key& key)
+ {
+ if(m_stack.count() == 0)
+ throw new MYMONEYEXCEPTION("No transaction started to remove element from container");
+
+#if 0
+ // had to take this out, because we use QPair in one instance as key
+ if(key.isEmpty())
+ throw new MYMONEYEXCEPTION("No key to remove object");
+#endif
+
+ m_stack.push(new MyMoneyMapRemove(this, key));
+ }
+
+ MyMoneyMap<Key, T>& operator= (const QMap<Key, T>& m)
+ {
+ if(m_stack.count() != 0) {
+ throw new MYMONEYEXCEPTION("Cannot assign whole container during transaction");
+ }
+ QMap<Key, T>::operator=(m);
+ return *this;
+ }
+
+
+ inline QValueList<T> values(void) const
+ {
+ return QMap<Key,T>::values();
+ }
+
+ inline QValueList<Key> keys(void) const
+ {
+ return QMap<Key,T>::keys();
+ }
+
+ const T& operator[] ( const Key& k ) const
+ { QT_CHECK_INVALID_MAP_ELEMENT; return QMap<Key,T>::operator[](k); }
+
+ inline Q_TYPENAME QMap<Key, T>::const_iterator find(const Key& k) const
+ {
+ return QMap<Key,T>::find(k);
+ }
+
+ inline Q_TYPENAME QMap<Key, T>::const_iterator begin(void) const
+ {
+ return QMap<Key,T>::begin();
+ }
+
+ inline Q_TYPENAME QMap<Key, T>::const_iterator end(void) const
+ {
+ return QMap<Key,T>::end();
+ }
+
+ inline bool contains(const Key& k) const
+ {
+ return find(k) != end();
+ }
+
+ inline void map(QMap<Key, T>& that) const
+ {
+ //QMap<Key, T>* ptr = dynamic_cast<QMap<Key, T>* >(this);
+ //that = *ptr;
+ that = *(dynamic_cast<QMap<Key, T>* >(const_cast<MyMoneyMap<Key, T>* >(this)));
+ }
+
+ inline size_t count(void) const
+ {
+ return QMap<Key, T>::count();
+ }
+
+#if MY_OWN_DEBUG
+ void dump(void) const
+ {
+ printf("Container dump\n");
+ printf(" items in container = %d\n", count());
+ printf(" items on stack = %d\n", m_stack.count());
+
+ const_iterator it;
+ for(it = begin(); it != end(); ++it) {
+ printf(" %s \n", it.key().data());
+ }
+ }
+#endif
+
+private:
+ class MyMoneyMapAction
+ {
+ public:
+ MyMoneyMapAction(QMap<Key, T>* container) :
+ m_container(container) {}
+
+ MyMoneyMapAction(QMap<Key, T>* container, const Key& key, const T& obj) :
+ m_container(container),
+ m_obj(obj),
+ m_key(key) {}
+
+ virtual ~MyMoneyMapAction() {}
+ virtual void undo(void) = 0;
+
+ protected:
+ QMap<Key, T>* m_container;
+ T m_obj;
+ Key m_key;
+ };
+
+ class MyMoneyMapStart : public MyMoneyMapAction
+ {
+ public:
+ MyMoneyMapStart(QMap<Key, T>* container, unsigned long* id) :
+ MyMoneyMapAction(container),
+ m_idPtr(id)
+ {
+ if(id != 0)
+ m_id = *id;
+ }
+ virtual ~MyMoneyMapStart() {}
+ void undo(void)
+ {
+ if(m_idPtr != 0)
+ *m_idPtr = m_id;
+ }
+
+ private:
+ unsigned long* m_idPtr;
+ unsigned long m_id;
+ };
+
+ class MyMoneyMapInsert : public MyMoneyMapAction
+ {
+ public:
+ MyMoneyMapInsert(QMap<Key, T>* container, const Key& key, const T& obj) :
+ MyMoneyMapAction(container, key, obj)
+ {
+ (*container)[key] = obj;
+ }
+
+ virtual ~MyMoneyMapInsert() {}
+ void undo(void)
+ {
+ // m_container->remove(m_key) does not work on GCC 4.0.2
+ // using this-> to access those member does the trick
+ this->m_container->remove(this->m_key);
+ }
+ };
+
+ class MyMoneyMapRemove : public MyMoneyMapAction
+ {
+ public:
+ MyMoneyMapRemove(QMap<Key, T>* container, const Key& key) :
+ MyMoneyMapAction(container, key, (*container)[key])
+ {
+ container->remove(key);
+ }
+
+ virtual ~MyMoneyMapRemove() {}
+ void undo(void)
+ {
+ (*(this->m_container))[this->m_key] = this->m_obj;
+ }
+ };
+
+ class MyMoneyMapModify : public MyMoneyMapAction
+ {
+ public:
+ MyMoneyMapModify(QMap<Key, T>* container, const Key& key, const T& obj) :
+ MyMoneyMapAction(container, key, (*container)[key])
+ {
+ (*container)[key] = obj;
+ }
+
+ virtual ~MyMoneyMapModify() {}
+ void undo(void)
+ {
+ (*(this->m_container))[this->m_key] = this->m_obj;
+ }
+ };
+
+protected:
+ QPtrStack<MyMoneyMapAction> m_stack;
+};
+
+#if MY_OWN_DEBUG
+#include <kmymoney/mymoneyaccount.h>
+#include <kmymoney/mymoneytransaction.h>
+main()
+{
+ MyMoneyMap<QString, MyMoneyAccount> container;
+ MyMoneyMap<QString, MyMoneyTransaction> ct;
+
+ MyMoneyAccount acc;
+ acc.setName("Test");
+ // this should not be possible
+ // container["a"] = acc;
+
+ QValueList<MyMoneyAccount> list;
+ list = container.values();
+
+ MyMoneyAccount b;
+ b.setName("Thomas");
+
+ try {
+ container.startTransaction();
+ container.insert("001", acc);
+ container.dump();
+ container.commitTransaction();
+ acc.setName("123");
+ container.startTransaction();
+ container.modify("001", acc);
+ container.dump();
+ container.rollbackTransaction();
+ container.dump();
+
+ container.startTransaction();
+ container.remove(QString("001"));
+ container.dump();
+ container.rollbackTransaction();
+ container.dump();
+
+ b = container["001"];
+ printf("b.name() = %s\n", b.name().data());
+
+ QMap<QString, MyMoneyAccount>::ConstIterator it;
+ it = container.find("001");
+ it = container.begin();
+
+ } catch(MyMoneyException *e) {
+ printf("Caught exception: %s\n", e->what().data());
+ delete e;
+ }
+
+ QMap<QString, MyMoneyAccount> map;
+ map["005"] = b;
+ container = map;
+
+ printf("b.name() = %s\n", container["001"].name().data());
+ printf("b.name() = %s\n", container["005"].name().data());
+}
+
+#endif
+
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneymaptest.cpp b/kmymoney2/mymoney/storage/mymoneymaptest.cpp
new file mode 100644
index 0000000..5104fad
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneymaptest.cpp
@@ -0,0 +1,38 @@
+/***************************************************************************
+ mymoneymaptest.cpp
+ -------------------
+ copyright : (C) 2007 by Thomas Baumgart
+ email : ipwizard@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. *
+ * *
+ ***************************************************************************/
+
+#include "mymoneymaptest.h"
+#include <iostream>
+
+MyMoneyMapTest::MyMoneyMapTest()
+{
+}
+
+
+void MyMoneyMapTest::setUp()
+{
+ m = new MyMoneyMap<QString, QString>;
+}
+
+void MyMoneyMapTest::tearDown()
+{
+ delete m;
+}
+
+void MyMoneyMapTest::testArrayOperator()
+{
+}
+
diff --git a/kmymoney2/mymoney/storage/mymoneymaptest.h b/kmymoney2/mymoney/storage/mymoneymaptest.h
new file mode 100644
index 0000000..c089a3f
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneymaptest.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ mymoneymaptest.h
+ -------------------
+ copyright : (C) 2007 by Thomas Baumgart
+ email : ipwizard@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. *
+ * *
+ ***************************************************************************/
+
+#ifndef __MYMONEYMAPTEST_H__
+#define __MYMONEYMAPTEST_H__
+
+#include <cppunit/TestCaller.h>
+#include <cppunit/TestCase.h>
+#include <cppunit/TestSuite.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "../autotest.h"
+
+#define private public
+#include "mymoneyseqaccessmgr.h"
+#undef private
+
+class MyMoneyMapTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(MyMoneyMapTest);
+ CPPUNIT_TEST(testArrayOperator);
+ CPPUNIT_TEST_SUITE_END();
+
+protected:
+ MyMoneyMap<QString, QString> *m;
+public:
+ MyMoneyMapTest();
+
+
+ void setUp();
+ void tearDown();
+ void testArrayOperator(void);
+};
+
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneyseqaccessmgr.cpp b/kmymoney2/mymoney/storage/mymoneyseqaccessmgr.cpp
new file mode 100644
index 0000000..7341ec1
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneyseqaccessmgr.cpp
@@ -0,0 +1,1944 @@
+/***************************************************************************
+ mymoneyseqaccessmgr.cpp
+ -------------------
+ begin : Sun May 5 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ 2002 Thomas Baumgart
+ email : mte@users.sourceforge.net
+ Thomas Baumgart <ipwizard@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. *
+ * *
+ ***************************************************************************/
+
+#include <typeinfo>
+#include "mymoneyseqaccessmgr.h"
+#include "../mymoneytransactionfilter.h"
+#include "../mymoneycategory.h"
+
+#define TRY try {
+#define CATCH } catch (MyMoneyException *e) {
+#define PASS } catch (MyMoneyException *e) { throw; }
+
+bool MyMoneyBalanceCacheItem::operator ==(const MyMoneyBalanceCacheItem & right) const
+{
+ return ((balance == right.balance)
+ && (valid == right.valid));
+}
+
+MyMoneySeqAccessMgr::MyMoneySeqAccessMgr()
+{
+ m_nextAccountID = 0;
+ m_nextInstitutionID = 0;
+ m_nextTransactionID = 0;
+ m_nextPayeeID = 0;
+ m_nextScheduleID = 0;
+ m_nextSecurityID = 0;
+ m_nextReportID = 0;
+ m_nextBudgetID = 0;
+ m_user = MyMoneyPayee();
+ m_dirty = false;
+ m_creationDate = QDate::currentDate();
+
+ // setup standard accounts
+ MyMoneyAccount acc_l;
+ acc_l.setAccountType(MyMoneyAccount::Liability);
+ acc_l.setName("Liability");
+ MyMoneyAccount liability(STD_ACC_LIABILITY, acc_l);
+
+ MyMoneyAccount acc_a;
+ acc_a.setAccountType(MyMoneyAccount::Asset);
+ acc_a.setName("Asset");
+ MyMoneyAccount asset(STD_ACC_ASSET, acc_a);
+
+ MyMoneyAccount acc_e;
+ acc_e.setAccountType(MyMoneyAccount::Expense);
+ acc_e.setName("Expense");
+ MyMoneyAccount expense(STD_ACC_EXPENSE, acc_e);
+
+ MyMoneyAccount acc_i;
+ acc_i.setAccountType(MyMoneyAccount::Income);
+ acc_i.setName("Income");
+ MyMoneyAccount income(STD_ACC_INCOME, acc_i);
+
+ MyMoneyAccount acc_q;
+ acc_q.setAccountType(MyMoneyAccount::Equity);
+ acc_q.setName("Equity");
+ MyMoneyAccount equity(STD_ACC_EQUITY, acc_q);
+
+ QMap<QString, MyMoneyAccount> map;
+ map[STD_ACC_ASSET] = asset;
+ map[STD_ACC_LIABILITY] = liability;
+ map[STD_ACC_INCOME] = income;
+ map[STD_ACC_EXPENSE] = expense;
+ map[STD_ACC_EQUITY] = equity;
+
+ // load account list with inital accounts
+ m_accountList = map;
+
+ MyMoneyBalanceCacheItem balance;
+
+ m_balanceCache.clear();
+ m_balanceCache[STD_ACC_LIABILITY] = balance;
+ m_balanceCache[STD_ACC_ASSET] = balance;
+ m_balanceCache[STD_ACC_EXPENSE] = balance;
+ m_balanceCache[STD_ACC_INCOME] = balance;
+ m_balanceCache[STD_ACC_EQUITY] = balance;
+
+ // initialize for file fixes (see kmymoneyview.cpp)
+ m_currentFixVersion = 2;
+ m_fileFixVersion = 0; // default value if no fix-version in file
+ m_transactionListFull = false;
+}
+
+MyMoneySeqAccessMgr::~MyMoneySeqAccessMgr()
+{
+}
+
+MyMoneySeqAccessMgr const * MyMoneySeqAccessMgr::duplicate(void)
+{
+ MyMoneySeqAccessMgr* that = new MyMoneySeqAccessMgr();
+ *that = *this;
+ return that;
+}
+ /**
+ * This method is used to get a SQL reader for subsequent database access
+ */
+KSharedPtr <MyMoneyStorageSql> MyMoneySeqAccessMgr::connectToDatabase
+ (const KURL& /*url*/) {
+ return 0;
+}
+
+bool MyMoneySeqAccessMgr::isStandardAccount(const QString& id) const
+{
+ return id == STD_ACC_LIABILITY
+ || id == STD_ACC_ASSET
+ || id == STD_ACC_EXPENSE
+ || id == STD_ACC_INCOME
+ || id == STD_ACC_EQUITY;
+}
+
+void MyMoneySeqAccessMgr::setAccountName(const QString& id, const QString& name)
+{
+ if(!isStandardAccount(id))
+ throw new MYMONEYEXCEPTION("Only standard accounts can be modified using setAccountName()");
+
+ MyMoneyAccount acc = m_accountList[id];
+ acc.setName(name);
+ m_accountList.modify(acc.id(), acc);
+}
+
+const MyMoneyAccount MyMoneySeqAccessMgr::account(const QString& id) const
+{
+ // locate the account and if present, return it's data
+ if(m_accountList.find(id) != m_accountList.end())
+ return m_accountList[id];
+
+ // throw an exception, if it does not exist
+ QString msg = "Unknown account id '" + id + "'";
+ throw new MYMONEYEXCEPTION(msg);
+}
+
+void MyMoneySeqAccessMgr::accountList(QValueList<MyMoneyAccount>& list) const
+{
+ QMap<QString, MyMoneyAccount>::ConstIterator it;
+ for(it = m_accountList.begin(); it != m_accountList.end(); ++it) {
+ if(!isStandardAccount((*it).id())) {
+ list.append(*it);
+ }
+ }
+}
+
+void MyMoneySeqAccessMgr::addAccount(MyMoneyAccount& account)
+{
+ // create the account.
+ MyMoneyAccount newAccount(nextAccountID(), account);
+ m_accountList.insert(newAccount.id(), newAccount);
+
+ account = newAccount;
+}
+
+void MyMoneySeqAccessMgr::addPayee(MyMoneyPayee& payee)
+{
+ // create the payee
+ MyMoneyPayee newPayee(nextPayeeID(), payee);
+ m_payeeList.insert(newPayee.id(), newPayee);
+ payee = newPayee;
+}
+
+const MyMoneyPayee MyMoneySeqAccessMgr::payee(const QString& id) const
+{
+ QMap<QString, MyMoneyPayee>::ConstIterator it;
+ it = m_payeeList.find(id);
+ if(it == m_payeeList.end())
+ throw new MYMONEYEXCEPTION("Unknown payee '" + id + "'");
+
+ return *it;
+}
+
+const MyMoneyPayee MyMoneySeqAccessMgr::payeeByName(const QString& payee) const
+{
+ if(payee.isEmpty())
+ return MyMoneyPayee::null;
+
+ QMap<QString, MyMoneyPayee>::ConstIterator it_p;
+
+ for(it_p = m_payeeList.begin(); it_p != m_payeeList.end(); ++it_p) {
+ if((*it_p).name() == payee) {
+ return *it_p;
+ }
+ }
+
+ throw new MYMONEYEXCEPTION("Unknown payee '" + payee + "'");
+}
+
+void MyMoneySeqAccessMgr::modifyPayee(const MyMoneyPayee& payee)
+{
+ QMap<QString, MyMoneyPayee>::ConstIterator it;
+
+ it = m_payeeList.find(payee.id());
+ if(it == m_payeeList.end()) {
+ QString msg = "Unknown payee '" + payee.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+ m_payeeList.modify((*it).id(), payee);
+}
+
+void MyMoneySeqAccessMgr::removePayee(const MyMoneyPayee& payee)
+{
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+ QMap<QString, MyMoneySchedule>::ConstIterator it_s;
+ QMap<QString, MyMoneyPayee>::ConstIterator it_p;
+
+ it_p = m_payeeList.find(payee.id());
+ if(it_p == m_payeeList.end()) {
+ QString msg = "Unknown payee '" + payee.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ // scan all transactions to check if the payee is still referenced
+ for(it_t = m_transactionList.begin(); it_t != m_transactionList.end(); ++it_t) {
+ if((*it_t).hasReferenceTo(payee.id())) {
+ throw new MYMONEYEXCEPTION(QString("Cannot remove payee that is still referenced to a %1").arg("transaction"));
+ }
+ }
+
+ // check referential integrity in schedules
+ for(it_s = m_scheduleList.begin(); it_s != m_scheduleList.end(); ++it_s) {
+ if((*it_s).hasReferenceTo(payee.id())) {
+ throw new MYMONEYEXCEPTION(QString("Cannot remove payee that is still referenced to a %1").arg("schedule"));
+ }
+ }
+
+ // remove any reference to report and/or budget
+ removeReferences(payee.id());
+
+ m_payeeList.remove((*it_p).id());
+}
+
+const QValueList<MyMoneyPayee> MyMoneySeqAccessMgr::payeeList(void) const
+{
+ return m_payeeList.values();
+}
+
+
+void MyMoneySeqAccessMgr::addAccount(MyMoneyAccount& parent, MyMoneyAccount& account)
+{
+ QMap<QString, MyMoneyAccount>::ConstIterator theParent;
+ QMap<QString, MyMoneyAccount>::ConstIterator theChild;
+
+ theParent = m_accountList.find(parent.id());
+ if(theParent == m_accountList.end()) {
+ QString msg = "Unknown parent account '";
+ msg += parent.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ theChild = m_accountList.find(account.id());
+ if(theChild == m_accountList.end()) {
+ QString msg = "Unknown child account '";
+ msg += account.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ MyMoneyAccount acc = *theParent;
+ acc.addAccountId(account.id());
+ m_accountList.modify(acc.id(), acc);
+ parent = acc;
+
+ acc = *theChild;
+ acc.setParentAccountId(parent.id());
+ m_accountList.modify(acc.id(), acc);
+ account = acc;
+
+ MyMoneyBalanceCacheItem balance;
+ m_balanceCache[account.id()] = balance;
+}
+
+void MyMoneySeqAccessMgr::addInstitution(MyMoneyInstitution& institution)
+{
+ MyMoneyInstitution newInstitution(nextInstitutionID(), institution);
+
+ m_institutionList.insert(newInstitution.id(), newInstitution);
+
+ // return new data
+ institution = newInstitution;
+}
+
+unsigned int MyMoneySeqAccessMgr::transactionCount(const QString& account) const
+{
+ unsigned int cnt = 0;
+
+ if(account.length() == 0) {
+ cnt = m_transactionList.count();
+
+ } else {
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+
+ // scan all transactions
+ for(it_t = m_transactionList.begin(); it_t != m_transactionList.end(); ++it_t) {
+
+ // scan all splits of this transaction
+ for(it_s = (*it_t).splits().begin(); it_s != (*it_t).splits().end(); ++it_s) {
+ // is it a split in our account?
+ if((*it_s).accountId() == account) {
+ // since a transaction can only have one split referencing
+ // each account, we're done with the splits here!
+ break;
+ }
+ }
+ // if no split contains the account id, continue with the
+ // next transaction
+ if(it_s == (*it_t).splits().end())
+ continue;
+
+ // otherwise count it
+ ++cnt;
+ }
+ }
+ return cnt;
+}
+
+const QMap<QString, unsigned long> MyMoneySeqAccessMgr::transactionCountMap(void) const
+{
+ QMap<QString, unsigned long> map;
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+
+ // scan all transactions
+ for(it_t = m_transactionList.begin(); it_t != m_transactionList.end(); ++it_t) {
+ // scan all splits of this transaction
+ for(it_s = (*it_t).splits().begin(); it_s != (*it_t).splits().end(); ++it_s) {
+ map[(*it_s).accountId()]++;
+ }
+ }
+ return map;
+}
+
+unsigned int MyMoneySeqAccessMgr::institutionCount(void) const
+{
+ return m_institutionList.count();
+}
+
+unsigned int MyMoneySeqAccessMgr::accountCount(void) const
+{
+ return m_accountList.count();
+}
+
+QString MyMoneySeqAccessMgr::nextPayeeID(void)
+{
+ QString id;
+ id.setNum(++m_nextPayeeID);
+ id = "P" + id.rightJustify(PAYEE_ID_SIZE, '0');
+ return id;
+}
+
+QString MyMoneySeqAccessMgr::nextInstitutionID(void)
+{
+ QString id;
+ id.setNum(++m_nextInstitutionID);
+ id = "I" + id.rightJustify(INSTITUTION_ID_SIZE, '0');
+ return id;
+}
+
+QString MyMoneySeqAccessMgr::nextAccountID(void)
+{
+ QString id;
+ id.setNum(++m_nextAccountID);
+ id = "A" + id.rightJustify(ACCOUNT_ID_SIZE, '0');
+ return id;
+}
+
+QString MyMoneySeqAccessMgr::nextTransactionID(void)
+{
+ QString id;
+ id.setNum(++m_nextTransactionID);
+ id = "T" + id.rightJustify(TRANSACTION_ID_SIZE, '0');
+ return id;
+}
+
+QString MyMoneySeqAccessMgr::nextScheduleID(void)
+{
+ QString id;
+ id.setNum(++m_nextScheduleID);
+ id = "SCH" + id.rightJustify(SCHEDULE_ID_SIZE, '0');
+ return id;
+}
+
+QString MyMoneySeqAccessMgr::nextSecurityID(void)
+{
+ QString id;
+ id.setNum(++m_nextSecurityID);
+ id = "E" + id.rightJustify(SECURITY_ID_SIZE, '0');
+ return id;
+}
+
+
+void MyMoneySeqAccessMgr::addTransaction(MyMoneyTransaction& transaction, const bool skipAccountUpdate)
+{
+ // perform some checks to see that the transaction stuff is OK. For
+ // now we assume that
+ // * no ids are assigned
+ // * the date valid (must not be empty)
+ // * the referenced accounts in the splits exist
+
+ // first perform all the checks
+ if(!transaction.id().isEmpty())
+ throw new MYMONEYEXCEPTION("transaction already contains an id");
+ if(!transaction.postDate().isValid())
+ throw new MYMONEYEXCEPTION("invalid post date");
+
+ // now check the splits
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s) {
+ // the following lines will throw an exception if the
+ // account or payee do not exist
+ account((*it_s).accountId());
+ if(!(*it_s).payeeId().isEmpty())
+ payee((*it_s).payeeId());
+ }
+
+ MyMoneyTransaction newTransaction(nextTransactionID(), transaction);
+ QString key = newTransaction.uniqueSortKey();
+
+ m_transactionList.insert(key, newTransaction);
+ m_transactionKeys.insert(newTransaction.id(), key);
+
+ transaction = newTransaction;
+
+ // adjust the balance of all affected accounts
+ for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s) {
+ MyMoneyAccount acc = m_accountList[(*it_s).accountId()];
+ acc.adjustBalance(*it_s);
+ if(!skipAccountUpdate) {
+ acc.touch();
+ invalidateBalanceCache(acc.id());
+ }
+ m_accountList.modify(acc.id(), acc);
+ }
+}
+
+void MyMoneySeqAccessMgr::touch(void)
+{
+ m_dirty = true;
+ m_lastModificationDate = QDate::currentDate();
+}
+
+bool MyMoneySeqAccessMgr::hasActiveSplits(const QString& id) const
+{
+ QMap<QString, MyMoneyTransaction>::ConstIterator it;
+
+ for(it = m_transactionList.begin(); it != m_transactionList.end(); ++it) {
+ if((*it).accountReferenced(id)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+const MyMoneyInstitution MyMoneySeqAccessMgr::institution(const QString& id) const
+{
+ QMap<QString, MyMoneyInstitution>::ConstIterator pos;
+
+ pos = m_institutionList.find(id);
+ if(pos != m_institutionList.end())
+ return *pos;
+ throw new MYMONEYEXCEPTION("unknown institution");
+}
+
+const QValueList<MyMoneyInstitution> MyMoneySeqAccessMgr::institutionList(void) const
+{
+ return m_institutionList.values();
+}
+
+void MyMoneySeqAccessMgr::modifyAccount(const MyMoneyAccount& account, const bool skipCheck)
+{
+ QMap<QString, MyMoneyAccount>::ConstIterator pos;
+
+ // locate the account in the file global pool
+ pos = m_accountList.find(account.id());
+ if(pos != m_accountList.end()) {
+ // check if the new info is based on the old one.
+ // this is the case, when the file and the id
+ // as well as the type are equal.
+ if((((*pos).parentAccountId() == account.parentAccountId())
+ && ((*pos).accountType() == account.accountType()))
+ || (skipCheck == true)) {
+ // make sure that all the referenced objects exist
+ if(!account.institutionId().isEmpty())
+ institution(account.institutionId());
+
+ QValueList<QString>::ConstIterator it_a;
+ for(it_a = account.accountList().begin(); it_a != account.accountList().end(); ++it_a) {
+ this->account(*it_a);
+ }
+
+ // update information in account list
+ m_accountList.modify(account.id(), account);
+
+ // invalidate cached balance
+ invalidateBalanceCache(account.id());
+
+ } else
+ throw new MYMONEYEXCEPTION("Invalid information for update");
+
+ } else
+ throw new MYMONEYEXCEPTION("Unknown account id");
+}
+
+void MyMoneySeqAccessMgr::modifyInstitution(const MyMoneyInstitution& institution)
+{
+ QMap<QString, MyMoneyInstitution>::ConstIterator pos;
+
+ // locate the institution in the file global pool
+ pos = m_institutionList.find(institution.id());
+ if(pos != m_institutionList.end()) {
+ m_institutionList.modify(institution.id(), institution);
+
+ } else
+ throw new MYMONEYEXCEPTION("unknown institution");
+}
+
+void MyMoneySeqAccessMgr::modifyTransaction(const MyMoneyTransaction& transaction)
+{
+ // perform some checks to see that the transaction stuff is OK. For
+ // now we assume that
+ // * ids are assigned
+ // * the pointer to the MyMoneyFile object is not 0
+ // * the date valid (must not be empty)
+ // * the splits must have valid account ids
+
+ // first perform all the checks
+ if(transaction.id().isEmpty()
+// || transaction.file() != this
+ || !transaction.postDate().isValid())
+ throw new MYMONEYEXCEPTION("invalid transaction to be modified");
+
+ // now check the splits
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s) {
+ // the following lines will throw an exception if the
+ // account or payee do not exist
+ account((*it_s).accountId());
+ if(!(*it_s).payeeId().isEmpty())
+ payee((*it_s).payeeId());
+ }
+
+ // new data seems to be ok. find old version of transaction
+ // in our pool. Throw exception if unknown.
+ if(!m_transactionKeys.contains(transaction.id()))
+ throw new MYMONEYEXCEPTION("invalid transaction id");
+
+ QString oldKey = m_transactionKeys[transaction.id()];
+ if(!m_transactionList.contains(oldKey))
+ throw new MYMONEYEXCEPTION("invalid transaction key");
+
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+
+ it_t = m_transactionList.find(oldKey);
+ if(it_t == m_transactionList.end())
+ throw new MYMONEYEXCEPTION("invalid transaction key");
+
+ // adjust account balances
+ for(it_s = (*it_t).splits().begin(); it_s != (*it_t).splits().end(); ++it_s) {
+ MyMoneyAccount acc = m_accountList[(*it_s).accountId()];
+ acc.adjustBalance(*it_s, true); // reverse the adjust operation (reverse = true)
+ acc.touch();
+ invalidateBalanceCache(acc.id());
+ m_accountList.modify(acc.id(), acc);
+ }
+ for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s) {
+ MyMoneyAccount acc = m_accountList[(*it_s).accountId()];
+ acc.adjustBalance(*it_s);
+ acc.touch();
+ invalidateBalanceCache(acc.id());
+ m_accountList.modify(acc.id(), acc);
+ }
+
+ // remove old transaction from lists
+ m_transactionList.remove(oldKey);
+
+ // add new transaction to lists
+ QString newKey = transaction.uniqueSortKey();
+ m_transactionList.insert(newKey, transaction);
+ m_transactionKeys.modify(transaction.id(), newKey);
+}
+
+void MyMoneySeqAccessMgr::reparentAccount(MyMoneyAccount &account, MyMoneyAccount& parent)
+{
+ reparentAccount(account, parent, true);
+}
+
+void MyMoneySeqAccessMgr::reparentAccount(MyMoneyAccount &account, MyMoneyAccount& parent, const bool /* sendNotification */)
+{
+ QMap<QString, MyMoneyAccount>::ConstIterator oldParent;
+ QMap<QString, MyMoneyAccount>::ConstIterator newParent;
+ QMap<QString, MyMoneyAccount>::ConstIterator childAccount;
+
+ // verify that accounts exist. If one does not,
+ // an exception is thrown
+ MyMoneySeqAccessMgr::account(account.id());
+ MyMoneySeqAccessMgr::account(parent.id());
+ if(!account.parentAccountId().isEmpty()) {
+ MyMoneySeqAccessMgr::account(account.parentAccountId());
+ oldParent = m_accountList.find(account.parentAccountId());
+ }
+
+ if(account.accountType() == MyMoneyAccount::Stock && parent.accountType() != MyMoneyAccount::Investment)
+ throw new MYMONEYEXCEPTION("Cannot move a stock acocunt into a non-investment account");
+
+ newParent = m_accountList.find(parent.id());
+ childAccount = m_accountList.find(account.id());
+
+ MyMoneyAccount acc;
+ if(!account.parentAccountId().isEmpty()) {
+ acc = (*oldParent);
+ acc.removeAccountId(account.id());
+ m_accountList.modify(acc.id(), acc);
+ }
+
+ parent = (*newParent);
+ parent.addAccountId(account.id());
+ m_accountList.modify(parent.id(), parent);
+
+ account = (*childAccount);
+ account.setParentAccountId(parent.id());
+ m_accountList.modify(account.id(), account);
+
+#if 0
+ // make sure the type is the same as the new parent. This does not work for stock and investment
+ if(account.accountType() != MyMoneyAccount::Stock && account.accountType() != MyMoneyAccount::Investment)
+ (*childAccount).setAccountType((*newParent).accountType());
+#endif
+}
+
+void MyMoneySeqAccessMgr::removeTransaction(const MyMoneyTransaction& transaction)
+{
+ // first perform all the checks
+ if(transaction.id().isEmpty())
+ throw new MYMONEYEXCEPTION("invalid transaction to be deleted");
+
+ QMap<QString, QString>::ConstIterator it_k;
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+
+ it_k = m_transactionKeys.find(transaction.id());
+ if(it_k == m_transactionKeys.end())
+ throw new MYMONEYEXCEPTION("invalid transaction to be deleted");
+
+ it_t = m_transactionList.find(*it_k);
+ if(it_t == m_transactionList.end())
+ throw new MYMONEYEXCEPTION("invalid transaction key");
+
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+
+ // scan the splits and collect all accounts that need
+ // to be updated after the removal of this transaction
+ for(it_s = (*it_t).splits().begin(); it_s != (*it_t).splits().end(); ++it_s) {
+ MyMoneyAccount acc = m_accountList[(*it_s).accountId()];
+ acc.adjustBalance(*it_s, true); // reverse = true
+ acc.touch();
+ m_accountList.modify(acc.id(), acc);
+ invalidateBalanceCache(acc.id());
+ }
+
+ // FIXME: check if any split is frozen and throw exception
+
+ // remove the transaction from the two lists
+ m_transactionList.remove(*it_k);
+ m_transactionKeys.remove(transaction.id());
+}
+
+void MyMoneySeqAccessMgr::removeAccount(const MyMoneyAccount& account)
+{
+ MyMoneyAccount parent;
+
+ // check that the account and it's parent exist
+ // this will throw an exception if the id is unknown
+ MyMoneySeqAccessMgr::account(account.id());
+ parent = MyMoneySeqAccessMgr::account(account.parentAccountId());
+
+ // check that it's not one of the standard account groups
+ if(isStandardAccount(account.id()))
+ throw new MYMONEYEXCEPTION("Unable to remove the standard account groups");
+
+ if(hasActiveSplits(account.id())) {
+ throw new MYMONEYEXCEPTION("Unable to remove account with active splits");
+ }
+
+ // re-parent all sub-ordinate accounts to the parent of the account
+ // to be deleted. First round check that all accounts exist, second
+ // round do the re-parenting.
+ QStringList::ConstIterator it;
+ for(it = account.accountList().begin(); it != account.accountList().end(); ++it) {
+ MyMoneySeqAccessMgr::account(*it);
+ }
+
+ // if one of the accounts did not exist, an exception had been
+ // thrown and we would not make it until here.
+
+ QMap<QString, MyMoneyAccount>::ConstIterator it_a;
+ QMap<QString, MyMoneyAccount>::ConstIterator it_p;
+
+ // locate the account in the file global pool
+
+ it_a = m_accountList.find(account.id());
+ if(it_a == m_accountList.end())
+ throw new MYMONEYEXCEPTION("Internal error: account not found in list");
+
+ it_p = m_accountList.find(parent.id());
+ if(it_p == m_accountList.end())
+ throw new MYMONEYEXCEPTION("Internal error: parent account not found in list");
+
+ if(!account.institutionId().isEmpty())
+ throw new MYMONEYEXCEPTION("Cannot remove account still attached to an institution");
+
+ removeReferences(account.id());
+
+ // FIXME: check referential integrity for the account to be removed
+
+ // check if the new info is based on the old one.
+ // this is the case, when the file and the id
+ // as well as the type are equal.
+ if((*it_a).id() == account.id()
+ && (*it_a).accountType() == account.accountType()) {
+
+ // second round over sub-ordinate accounts: do re-parenting
+ // but only if the list contains at least one entry
+ // FIXME: move this logic to MyMoneyFile
+ if((*it_a).accountList().count() > 0) {
+ while((*it_a).accountList().count() > 0) {
+ it = (*it_a).accountList().begin();
+ MyMoneyAccount acc(MyMoneySeqAccessMgr::account(*it));
+ reparentAccount(acc, parent, false);
+ }
+ }
+ // remove account from parent's list
+ parent.removeAccountId(account.id());
+ m_accountList.modify(parent.id(), parent);
+
+ // remove account from the global account pool
+ m_accountList.remove(account.id());
+
+ // remove from balance list
+ m_balanceCache.remove(account.id());
+ invalidateBalanceCache(parent.id());
+ }
+}
+
+void MyMoneySeqAccessMgr::removeInstitution(const MyMoneyInstitution& institution)
+{
+ QMap<QString, MyMoneyInstitution>::ConstIterator it_i;
+
+ it_i = m_institutionList.find(institution.id());
+ if(it_i != m_institutionList.end()) {
+ m_institutionList.remove(institution.id());
+
+ } else
+ throw new MYMONEYEXCEPTION("invalid institution");
+}
+
+void MyMoneySeqAccessMgr::transactionList(QValueList<MyMoneyTransaction>& list, MyMoneyTransactionFilter& filter) const
+{
+ list.clear();
+
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+
+ for(it_t = m_transactionList.begin(); it_t != m_transactionList.end(); ++it_t) {
+ // This code is used now. It adds the transaction to the list for
+ // each matching split exactly once. This allows to show information
+ // about different splits in the same register view (e.g. search result)
+ //
+ // I have no idea, if this has some impact on the functionality. So far,
+ // I could not see it. (ipwizard 9/5/2003)
+ if(filter.match(*it_t)) {
+ unsigned int cnt = filter.matchingSplits().count();
+ if(cnt > 1) {
+ for(unsigned i=0; i < cnt; ++i)
+ list.append(*it_t);
+ } else {
+ list.append(*it_t);
+ }
+ }
+ }
+}
+
+void MyMoneySeqAccessMgr::transactionList(QValueList< QPair<MyMoneyTransaction, MyMoneySplit> >& list, MyMoneyTransactionFilter& filter) const
+{
+ list.clear();
+
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+
+ for(it_t = m_transactionList.begin(); it_t != m_transactionList.end(); ++it_t) {
+ if(filter.match(*it_t)) {
+ QValueList<MyMoneySplit>::const_iterator it_s;
+ for(it_s = filter.matchingSplits().begin(); it_s != filter.matchingSplits().end(); ++it_s) {
+ list.append(qMakePair(*it_t, *it_s));
+ }
+ }
+ }
+}
+
+const QValueList<MyMoneyTransaction> MyMoneySeqAccessMgr::transactionList(MyMoneyTransactionFilter& filter) const
+{
+ QValueList<MyMoneyTransaction> list;
+ transactionList(list, filter);
+ return list;
+}
+
+const MyMoneyTransaction MyMoneySeqAccessMgr::transaction(const QString& id) const
+{
+ // get the full key of this transaction, throw exception
+ // if it's invalid (unknown)
+ if(!m_transactionKeys.contains(id)) {
+ QString msg = QString("Invalid transaction id '%1'").arg(id);
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ // check if this key is in the list, throw exception if not
+ QString key = m_transactionKeys[id];
+ if(!m_transactionList.contains(key)) {
+ QString msg = QString("Invalid transaction key '%1'").arg(key);
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ return m_transactionList[key];
+}
+
+const MyMoneyTransaction MyMoneySeqAccessMgr::transaction(const QString& account, const int idx) const
+{
+/* removed with MyMoneyAccount::Transaction
+ QMap<QString, MyMoneyAccount>::ConstIterator acc;
+
+ // find account object in list, throw exception if unknown
+ acc = m_accountList.find(account);
+ if(acc == m_accountList.end())
+ throw new MYMONEYEXCEPTION("unknown account id");
+
+ // get the transaction info from the account
+ MyMoneyAccount::Transaction t = (*acc).transaction(idx);
+
+ // return the transaction, throw exception if not found
+ return transaction(t.transactionID());
+*/
+
+ // new implementation if the above code does not work anymore
+ QValueList<MyMoneyTransaction> list;
+ MyMoneyAccount acc = m_accountList[account];
+ MyMoneyTransactionFilter filter;
+
+ if(acc.accountGroup() == MyMoneyAccount::Income
+ || acc.accountGroup() == MyMoneyAccount::Expense)
+ filter.addCategory(account);
+ else
+ filter.addAccount(account);
+
+ transactionList(list, filter);
+ if(idx < 0 || idx >= static_cast<int> (list.count()))
+ throw new MYMONEYEXCEPTION("Unknown idx for transaction");
+
+ return transaction(list[idx].id());
+}
+
+const MyMoneyMoney MyMoneySeqAccessMgr::balance(const QString& id, const QDate& date) const
+{
+ MyMoneyMoney result(0);
+ MyMoneyAccount acc;
+ // if (date != QDate()) qDebug ("request balance for %s at %s", id.data(), date.toString(Qt::ISODate).latin1());
+ if(!date.isValid() && account(id).accountType() != MyMoneyAccount::Stock) {
+ if(m_accountList.find(id) != m_accountList.end())
+ return m_accountList[id].balance();
+ return MyMoneyMoney(0);
+ }
+ if(m_balanceCache[id].valid == false || date != m_balanceCacheDate) {
+ QMap<QString, MyMoneyMoney> balances;
+ QMap<QString, MyMoneyMoney>::ConstIterator it_b;
+ if (date != m_balanceCacheDate) {
+ m_balanceCache.clear();
+ m_balanceCacheDate = date;
+ }
+
+ QValueList<MyMoneyTransaction> list;
+ QValueList<MyMoneyTransaction>::ConstIterator it_t;
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+
+ MyMoneyTransactionFilter filter;
+ filter.setDateFilter(QDate(), date);
+ filter.setReportAllSplits(false);
+ transactionList(list, filter);
+
+ for(it_t = list.begin(); it_t != list.end(); ++it_t) {
+ for(it_s = (*it_t).splits().begin(); it_s != (*it_t).splits().end(); ++it_s){
+ const QString& aid = (*it_s).accountId();
+ if((*it_s).action() == MyMoneySplit::ActionSplitShares) {
+ balances[aid] = balances[aid] * (*it_s).shares();
+ } else {
+ balances[aid] += (*it_s).shares();
+ }
+ }
+ }
+
+ // fill the found balances into the cache
+ for(it_b = balances.begin(); it_b != balances.end(); ++it_b) {
+ MyMoneyBalanceCacheItem balance(*it_b);
+ m_balanceCache[it_b.key()] = balance;
+ }
+
+ // fill all accounts w/o transactions to zero
+ QMap<QString, MyMoneyAccount>::ConstIterator it_a;
+ for(it_a = m_accountList.begin(); it_a != m_accountList.end(); ++it_a) {
+ if(m_balanceCache[(*it_a).id()].valid == false) {
+ MyMoneyBalanceCacheItem balance(MyMoneyMoney(0,1));
+ m_balanceCache[(*it_a).id()] = balance;
+ }
+ }
+ }
+
+ if(m_balanceCache[id].valid == true)
+ result = m_balanceCache[id].balance;
+ else
+ qDebug("Cache mishit should never happen at this point");
+
+ return result;
+}
+
+const MyMoneyMoney MyMoneySeqAccessMgr::totalBalance(const QString& id, const QDate& date) const
+{
+ QStringList accounts;
+ QStringList::ConstIterator it_a;
+
+ MyMoneyMoney result(balance(id, date));
+
+ accounts = account(id).accountList();
+
+ for(it_a = accounts.begin(); it_a != accounts.end(); ++it_a) {
+ result += totalBalance(*it_a, date);
+ }
+
+ return result;
+}
+
+/**
+ * this was intended to move all splits from one account
+ * to another. This somehow is strange to undo because many
+ * changes to different objects are made within one single call.
+ * I kept the source here but commented it out. If we ever need
+ * the functionality, we can turn it back on. BTW: the stuff is untested ;-)
+ */
+/*
+const unsigned int MyMoneyFile::moveSplits(const QString& oldAccount, const QString& newAccount)
+{
+ QMap<QString, MyMoneyTransaction>::Iterator it_t;
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ unsigned int cnt = 0;
+
+ // scan all transactions
+ for(it_t = m_transactionList.begin(); it_t != m_transactionList.end(); ++it_t) {
+ // scan all splits of this transaction
+ for(it_s = (*it_t).splits().begin(); it_s != (*it_t).splits().end(); ++it_s) {
+ // is it a split in our account?
+ if((*it_s).account() == oldAccount) {
+ MyMoneySplit s = *it_s;
+ s.setAccount(newAccount);
+ (*it_t).modifySplit(s);
+ ++cnt;
+ }
+ }
+ }
+
+ if(cnt != 0) {
+ // now update all the accounts that were referenced
+ QMap<QString, MyMoneyAccount>::Iterator acc;
+ acc = m_accountList.find(oldAccount);
+ if(acc != m_accountList.end()) {
+ (*acc).touch();
+ refreshAccountTransactionList(acc);
+ }
+ acc = m_accountList.find(newAccount);
+ if(acc != m_accountList.end()) {
+ (*acc).touch();
+ refreshAccountTransactionList(acc);
+ }
+
+ // mark file as changed
+ m_dirty = true;
+ }
+ return cnt;
+}
+*/
+
+void MyMoneySeqAccessMgr::invalidateBalanceCache(const QString& id)
+{
+ if(!id.isEmpty()) {
+ try {
+ m_balanceCache[id].valid = false;
+ if(!isStandardAccount(id)) {
+ invalidateBalanceCache(account(id).parentAccountId());
+ }
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+ }
+}
+
+void MyMoneySeqAccessMgr::loadAccounts(const QMap<QString, MyMoneyAccount>& map)
+{
+ m_accountList = map;
+
+ // scan the map to identify the last used id
+ QMap<QString, MyMoneyAccount>::const_iterator it_a;
+ QString lastId("");
+ for(it_a = map.begin(); it_a != map.end(); ++it_a) {
+ if(!isStandardAccount((*it_a).id()) && ((*it_a).id() > lastId))
+ lastId = (*it_a).id();
+ }
+
+ int pos = lastId.find(QRegExp("\\d+"), 0);
+ if(pos != -1) {
+ m_nextAccountID = atol(lastId.mid(pos));
+ }
+}
+
+void MyMoneySeqAccessMgr::loadTransactions(const QMap<QString, MyMoneyTransaction>& map)
+{
+ m_transactionList = map;
+
+ // now fill the key map and
+ // identify the last used id
+ QString lastId("");
+ QMap<QString, QString> keys;
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+ for(it_t = map.begin(); it_t != map.end(); ++it_t) {
+ keys[(*it_t).id()] = it_t.key();
+ if((*it_t).id() > lastId)
+ lastId = (*it_t).id();
+ }
+ m_transactionKeys = keys;
+
+
+ int pos = lastId.find(QRegExp("\\d+"), 0);
+ if(pos != -1) {
+ m_nextTransactionID = atol(lastId.mid(pos));
+ }
+}
+
+void MyMoneySeqAccessMgr::loadInstitutions(const QMap<QString, MyMoneyInstitution>& map)
+{
+ m_institutionList = map;
+
+ // scan the map to identify the last used id
+ QMap<QString, MyMoneyInstitution>::const_iterator it_i;
+ QString lastId("");
+ for(it_i = map.begin(); it_i != map.end(); ++it_i) {
+ if((*it_i).id() > lastId)
+ lastId = (*it_i).id();
+ }
+
+ int pos = lastId.find(QRegExp("\\d+"), 0);
+ if(pos != -1) {
+ m_nextInstitutionID = atol(lastId.mid(pos));
+ }
+}
+
+void MyMoneySeqAccessMgr::loadPayees(const QMap<QString, MyMoneyPayee>& map)
+{
+ m_payeeList = map;
+
+ // scan the map to identify the last used id
+ QMap<QString, MyMoneyPayee>::const_iterator it_p;
+ QString lastId("");
+ for(it_p = map.begin(); it_p != map.end(); ++it_p) {
+ if((*it_p).id().length() <= PAYEE_ID_SIZE+1) {
+ if((*it_p).id() > lastId)
+ lastId = (*it_p).id();
+ } else {
+ }
+ }
+
+ int pos = lastId.find(QRegExp("\\d+"), 0);
+ if(pos != -1) {
+ m_nextPayeeID = atol(lastId.mid(pos));
+ }
+}
+
+void MyMoneySeqAccessMgr::loadSecurities(const QMap<QString, MyMoneySecurity>& map)
+{
+ m_securitiesList = map;
+
+ // scan the map to identify the last used id
+ QMap<QString, MyMoneySecurity>::const_iterator it_s;
+ QString lastId("");
+ for(it_s = map.begin(); it_s != map.end(); ++it_s) {
+ if((*it_s).id() > lastId)
+ lastId = (*it_s).id();
+ }
+
+ int pos = lastId.find(QRegExp("\\d+"), 0);
+ if(pos != -1) {
+ m_nextSecurityID = atol(lastId.mid(pos));
+ }
+}
+
+void MyMoneySeqAccessMgr::loadCurrencies(const QMap<QString, MyMoneySecurity>& map)
+{
+ m_currencyList = map;
+}
+
+void MyMoneySeqAccessMgr::loadPrices(const MyMoneyPriceList& list)
+{
+ m_priceList = list;
+}
+
+void MyMoneySeqAccessMgr::loadAccountId(const unsigned long id)
+{
+ m_nextAccountID = id;
+}
+
+void MyMoneySeqAccessMgr::loadTransactionId(const unsigned long id)
+{
+ m_nextTransactionID = id;
+}
+
+void MyMoneySeqAccessMgr::loadPayeeId(const unsigned long id)
+{
+ m_nextPayeeID = id;
+}
+
+void MyMoneySeqAccessMgr::loadInstitutionId(const unsigned long id)
+{
+ m_nextInstitutionID = id;
+}
+
+void MyMoneySeqAccessMgr::loadSecurityId(const unsigned long id)
+{
+ m_nextSecurityID = id;
+}
+
+void MyMoneySeqAccessMgr::loadReportId(const unsigned long id)
+{
+ m_nextReportID = id;
+}
+
+void MyMoneySeqAccessMgr::loadBudgetId(const unsigned long id)
+{
+ m_nextBudgetID = id;
+}
+
+const QString MyMoneySeqAccessMgr::value(const QString& key) const
+{
+ return MyMoneyKeyValueContainer::value(key);
+}
+
+void MyMoneySeqAccessMgr::setValue(const QString& key, const QString& val)
+{
+ MyMoneyKeyValueContainer::setValue(key, val);
+ touch();
+}
+
+void MyMoneySeqAccessMgr::deletePair(const QString& key)
+{
+ MyMoneyKeyValueContainer::deletePair(key);
+ touch();
+}
+
+const QMap<QString, QString> MyMoneySeqAccessMgr::pairs(void) const
+{
+ return MyMoneyKeyValueContainer::pairs();
+}
+
+void MyMoneySeqAccessMgr::setPairs(const QMap<QString, QString>& list)
+{
+ MyMoneyKeyValueContainer::setPairs(list);
+ touch();
+}
+
+void MyMoneySeqAccessMgr::addSchedule(MyMoneySchedule& sched)
+{
+ // first perform all the checks
+ if(!sched.id().isEmpty())
+ throw new MYMONEYEXCEPTION("schedule already contains an id");
+
+ // The following will throw an exception when it fails
+ sched.validate(false);
+
+ MyMoneySchedule newSched(nextScheduleID(), sched);
+ m_scheduleList.insert(newSched.id(), newSched);
+ sched = newSched;
+}
+
+void MyMoneySeqAccessMgr::modifySchedule(const MyMoneySchedule& sched)
+{
+ QMap<QString, MyMoneySchedule>::ConstIterator it;
+
+ it = m_scheduleList.find(sched.id());
+ if(it == m_scheduleList.end()) {
+ QString msg = "Unknown schedule '" + sched.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_scheduleList.modify(sched.id(), sched);
+}
+
+void MyMoneySeqAccessMgr::removeSchedule(const MyMoneySchedule& sched)
+{
+ QMap<QString, MyMoneySchedule>::ConstIterator it;
+
+ it = m_scheduleList.find(sched.id());
+ if(it == m_scheduleList.end()) {
+ QString msg = "Unknown schedule '" + sched.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ // FIXME: check referential integrity for loan accounts
+ m_scheduleList.remove(sched.id());
+}
+
+const MyMoneySchedule MyMoneySeqAccessMgr::schedule(const QString& id) const
+{
+ QMap<QString, MyMoneySchedule>::ConstIterator pos;
+
+ // locate the schedule and if present, return it's data
+ pos = m_scheduleList.find(id);
+ if(pos != m_scheduleList.end())
+ return (*pos);
+
+ // throw an exception, if it does not exist
+ QString msg = "Unknown schedule id '" + id + "'";
+ throw new MYMONEYEXCEPTION(msg);
+}
+
+const QValueList<MyMoneySchedule> MyMoneySeqAccessMgr::scheduleList(
+ const QString& accountId,
+ const MyMoneySchedule::typeE type,
+ const MyMoneySchedule::occurenceE occurence,
+ const MyMoneySchedule::paymentTypeE paymentType,
+ const QDate& startDate,
+ const QDate& endDate,
+ const bool overdue) const
+{
+ QMap<QString, MyMoneySchedule>::ConstIterator pos;
+ QValueList<MyMoneySchedule> list;
+
+ // qDebug("scheduleList()");
+
+ for(pos = m_scheduleList.begin(); pos != m_scheduleList.end(); ++pos) {
+ // qDebug(" '%s'", qPrintable((*pos).id()));
+
+ if(type != MyMoneySchedule::TYPE_ANY) {
+ if(type != (*pos).type()) {
+ continue;
+ }
+ }
+
+ if(occurence != MyMoneySchedule::OCCUR_ANY) {
+ if(occurence != (*pos).occurence()) {
+ continue;
+ }
+ }
+
+ if(paymentType != MyMoneySchedule::STYPE_ANY) {
+ if(paymentType != (*pos).paymentType()) {
+ continue;
+ }
+ }
+
+ if(!accountId.isEmpty()) {
+ MyMoneyTransaction t = (*pos).transaction();
+ QValueList<MyMoneySplit>::ConstIterator it;
+ QValueList<MyMoneySplit> splits;
+ splits = t.splits();
+ for(it = splits.begin(); it != splits.end(); ++it) {
+ if((*it).accountId() == accountId)
+ break;
+ }
+ if(it == splits.end()) {
+ continue;
+ }
+ }
+
+ if(startDate.isValid() && endDate.isValid()) {
+ if((*pos).paymentDates(startDate, endDate).count() == 0) {
+ continue;
+ }
+ }
+
+ if(startDate.isValid() && !endDate.isValid()) {
+ if(!(*pos).nextPayment(startDate.addDays(-1)).isValid()) {
+ continue;
+ }
+ }
+
+ if(!startDate.isValid() && endDate.isValid()) {
+ if((*pos).startDate() > endDate) {
+ continue;
+ }
+ }
+
+ if(overdue) {
+ if (!(*pos).isOverdue())
+ continue;
+ }
+
+ // qDebug("Adding '%s'", (*pos).name().latin1());
+ list << *pos;
+ }
+ return list;
+}
+
+void MyMoneySeqAccessMgr::loadSchedules(const QMap<QString, MyMoneySchedule>& map)
+{
+ m_scheduleList = map;
+
+ // scan the map to identify the last used id
+ QMap<QString, MyMoneySchedule>::const_iterator it_s;
+ QString lastId("");
+ for(it_s = map.begin(); it_s != map.end(); ++it_s) {
+ if((*it_s).id() > lastId)
+ lastId = (*it_s).id();
+ }
+
+ int pos = lastId.find(QRegExp("\\d+"), 0);
+ if(pos != -1) {
+ m_nextScheduleID = atol(lastId.mid(pos));
+ }
+}
+
+void MyMoneySeqAccessMgr::loadScheduleId(const unsigned long id)
+{
+ m_nextScheduleID = id;
+}
+
+const QValueList<MyMoneySchedule> MyMoneySeqAccessMgr::scheduleListEx(int scheduleTypes,
+ int scheduleOcurrences,
+ int schedulePaymentTypes,
+ QDate date,
+ const QStringList& accounts) const
+{
+// qDebug("scheduleListEx");
+
+ QMap<QString, MyMoneySchedule>::ConstIterator pos;
+ QValueList<MyMoneySchedule> list;
+
+ if (!date.isValid())
+ return list;
+
+ for(pos = m_scheduleList.begin(); pos != m_scheduleList.end(); ++pos)
+ {
+ if (scheduleTypes && !(scheduleTypes & (*pos).type()))
+ continue;
+
+ if (scheduleOcurrences && !(scheduleOcurrences & (*pos).occurence()))
+ continue;
+
+ if (schedulePaymentTypes && !(schedulePaymentTypes & (*pos).paymentType()))
+ continue;
+
+ if((*pos).paymentDates(date, date).count() == 0)
+ continue;
+
+ if ((*pos).isFinished())
+ continue;
+
+ if ((*pos).hasRecordedPayment(date))
+ continue;
+
+ if (accounts.count() > 0)
+ {
+ if (accounts.contains((*pos).account().id()))
+ continue;
+ }
+
+// qDebug("\tAdding '%s'", (*pos).name().latin1());
+ list << *pos;
+ }
+
+ return list;
+}
+
+void MyMoneySeqAccessMgr::addSecurity(MyMoneySecurity& security)
+{
+ // create the account
+ MyMoneySecurity newSecurity(nextSecurityID(), security);
+
+ m_securitiesList.insert(newSecurity.id(), newSecurity);
+
+ security = newSecurity;
+}
+
+void MyMoneySeqAccessMgr::modifySecurity(const MyMoneySecurity& security)
+{
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ it = m_securitiesList.find(security.id());
+ if(it == m_securitiesList.end())
+ {
+ QString msg = "Unknown security '";
+ msg += security.id() + "' during modifySecurity()";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_securitiesList.modify(security.id(), security);
+}
+
+void MyMoneySeqAccessMgr::removeSecurity(const MyMoneySecurity& security)
+{
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ // FIXME: check referential integrity
+
+ it = m_securitiesList.find(security.id());
+ if(it == m_securitiesList.end())
+ {
+ QString msg = "Unknown security '";
+ msg += security.id() + "' during removeSecurity()";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_securitiesList.remove(security.id());
+}
+
+const MyMoneySecurity MyMoneySeqAccessMgr::security(const QString& id) const
+{
+ QMap<QString, MyMoneySecurity>::ConstIterator it = m_securitiesList.find(id);
+ if(it != m_securitiesList.end())
+ {
+ return it.data();
+ }
+
+ return MyMoneySecurity();
+}
+
+const QValueList<MyMoneySecurity> MyMoneySeqAccessMgr::securityList(void) const
+{
+ //qDebug("securityList: Security list size is %d, this=%8p", m_equitiesList.size(), (void*)this);
+ return m_securitiesList.values();
+}
+
+void MyMoneySeqAccessMgr::addCurrency(const MyMoneySecurity& currency)
+{
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ it = m_currencyList.find(currency.id());
+ if(it != m_currencyList.end()) {
+ throw new MYMONEYEXCEPTION(QString("Cannot add currency with existing id %1").arg(currency.id().data()));
+ }
+
+ m_currencyList.insert(currency.id(), currency);
+}
+
+void MyMoneySeqAccessMgr::modifyCurrency(const MyMoneySecurity& currency)
+{
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ it = m_currencyList.find(currency.id());
+ if(it == m_currencyList.end()) {
+ throw new MYMONEYEXCEPTION(QString("Cannot modify currency with unknown id %1").arg(currency.id().data()));
+ }
+
+ m_currencyList.modify(currency.id(), currency);
+}
+
+void MyMoneySeqAccessMgr::removeCurrency(const MyMoneySecurity& currency)
+{
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ // FIXME: check referential integrity
+
+ it = m_currencyList.find(currency.id());
+ if(it == m_currencyList.end()) {
+ throw new MYMONEYEXCEPTION(QString("Cannot remove currency with unknown id %1").arg(currency.id().data()));
+ }
+
+ m_currencyList.remove(currency.id());
+}
+
+const MyMoneySecurity MyMoneySeqAccessMgr::currency(const QString& id) const
+{
+ if(id.isEmpty()) {
+
+ }
+ QMap<QString, MyMoneySecurity>::ConstIterator it;
+
+ it = m_currencyList.find(id);
+ if(it == m_currencyList.end()) {
+ throw new MYMONEYEXCEPTION(QString("Cannot retrieve currency with unknown id '%1'").arg(id.data()));
+ }
+
+ return *it;
+}
+
+const QValueList<MyMoneySecurity> MyMoneySeqAccessMgr::currencyList(void) const
+{
+ return m_currencyList.values();
+}
+
+const QValueList<MyMoneyReport> MyMoneySeqAccessMgr::reportList(void) const
+{
+ return m_reportList.values();
+}
+
+void MyMoneySeqAccessMgr::addReport( MyMoneyReport& report )
+{
+ if(!report.id().isEmpty())
+ throw new MYMONEYEXCEPTION("report already contains an id");
+
+ MyMoneyReport newReport(nextReportID(), report);
+ m_reportList.insert(newReport.id(), newReport);
+ report = newReport;
+}
+
+void MyMoneySeqAccessMgr::loadReports(const QMap<QString, MyMoneyReport>& map)
+{
+ m_reportList = map;
+
+ // scan the map to identify the last used id
+ QMap<QString, MyMoneyReport>::const_iterator it_r;
+ QString lastId("");
+ for(it_r = map.begin(); it_r != map.end(); ++it_r) {
+ if((*it_r).id() > lastId)
+ lastId = (*it_r).id();
+ }
+
+ int pos = lastId.find(QRegExp("\\d+"), 0);
+ if(pos != -1) {
+ m_nextReportID = atol(lastId.mid(pos));
+ }
+}
+
+void MyMoneySeqAccessMgr::modifyReport( const MyMoneyReport& report )
+{
+ QMap<QString, MyMoneyReport>::ConstIterator it;
+
+ it = m_reportList.find(report.id());
+ if(it == m_reportList.end()) {
+ QString msg = "Unknown report '" + report.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+ m_reportList.modify(report.id(), report);
+}
+
+QString MyMoneySeqAccessMgr::nextReportID(void)
+{
+ QString id;
+ id.setNum(++m_nextReportID);
+ id = "R" + id.rightJustify(REPORT_ID_SIZE, '0');
+ return id;
+}
+
+unsigned MyMoneySeqAccessMgr::countReports(void) const
+{
+ return m_reportList.count();
+}
+
+const MyMoneyReport MyMoneySeqAccessMgr::report( const QString& _id ) const
+{
+ return m_reportList[_id];
+}
+
+void MyMoneySeqAccessMgr::removeReport( const MyMoneyReport& report )
+{
+ QMap<QString, MyMoneyReport>::ConstIterator it;
+
+ it = m_reportList.find(report.id());
+ if(it == m_reportList.end()) {
+ QString msg = "Unknown report '" + report.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_reportList.remove(report.id());
+}
+
+const QValueList<MyMoneyBudget> MyMoneySeqAccessMgr::budgetList(void) const
+{
+ return m_budgetList.values();
+}
+
+
+void MyMoneySeqAccessMgr::addBudget( MyMoneyBudget& budget )
+{
+ MyMoneyBudget newBudget(nextBudgetID(), budget);
+ m_budgetList.insert(newBudget.id(), newBudget);
+ budget = newBudget;
+}
+
+void MyMoneySeqAccessMgr::loadBudgets(const QMap<QString, MyMoneyBudget>& map)
+{
+ m_budgetList = map;
+
+ // scan the map to identify the last used id
+ QMap<QString, MyMoneyBudget>::const_iterator it_b;
+ QString lastId("");
+ for(it_b = map.begin(); it_b != map.end(); ++it_b) {
+ if((*it_b).id() > lastId)
+ lastId = (*it_b).id();
+ }
+
+ int pos = lastId.find(QRegExp("\\d+"), 0);
+ if(pos != -1) {
+ m_nextBudgetID = atol(lastId.mid(pos));
+ }
+}
+
+const MyMoneyBudget MyMoneySeqAccessMgr::budgetByName(const QString& budget) const
+{
+ QMap<QString, MyMoneyBudget>::ConstIterator it_p;
+
+ for(it_p = m_budgetList.begin(); it_p != m_budgetList.end(); ++it_p) {
+ if((*it_p).name() == budget) {
+ return *it_p;
+ }
+ }
+
+ throw new MYMONEYEXCEPTION("Unknown budget '" + budget + "'");
+}
+
+void MyMoneySeqAccessMgr::modifyBudget( const MyMoneyBudget& budget )
+{
+ QMap<QString, MyMoneyBudget>::ConstIterator it;
+
+ it = m_budgetList.find(budget.id());
+ if(it == m_budgetList.end()) {
+ QString msg = "Unknown budget '" + budget.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+ m_budgetList.modify(budget.id(), budget);
+}
+
+QString MyMoneySeqAccessMgr::nextBudgetID(void)
+{
+ QString id;
+ id.setNum(++m_nextBudgetID);
+ id = "B" + id.rightJustify(BUDGET_ID_SIZE, '0');
+ return id;
+}
+
+unsigned MyMoneySeqAccessMgr::countBudgets(void) const
+{
+ return m_budgetList.count();
+}
+
+MyMoneyBudget MyMoneySeqAccessMgr::budget( const QString& _id ) const
+{
+ return m_budgetList[_id];
+}
+
+void MyMoneySeqAccessMgr::removeBudget( const MyMoneyBudget& budget )
+{
+ QMap<QString, MyMoneyBudget>::ConstIterator it;
+
+ it = m_budgetList.find(budget.id());
+ if(it == m_budgetList.end()) {
+ QString msg = "Unknown budget '" + budget.id() + "'";
+ throw new MYMONEYEXCEPTION(msg);
+ }
+
+ m_budgetList.remove(budget.id());
+}
+
+void MyMoneySeqAccessMgr::addPrice(const MyMoneyPrice& price)
+{
+ MyMoneySecurityPair pricePair(price.from(), price.to());
+ QMap<MyMoneySecurityPair, MyMoneyPriceEntries>::ConstIterator it_m;
+ it_m = m_priceList.find(pricePair);
+
+ MyMoneyPriceEntries entries;
+ if(it_m != m_priceList.end()) {
+ entries = (*it_m);
+ }
+ // entries contains the current entries for this security pair
+ // in case it_m points to m_priceList.end() we need to create a
+ // new entry in the priceList, otherwise we need to modify
+ // an existing one.
+
+ MyMoneyPriceEntries::ConstIterator it;
+ it = entries.find(price.date());
+ if(it != entries.end()) {
+ if((*it).rate(QString()) == price.rate(QString())
+ && (*it).source() == price.source())
+ // in case the information did not change, we don't do anything
+ return;
+ }
+
+ // store new value in local copy
+ entries[price.date()] = price;
+
+ if(it_m != m_priceList.end()) {
+ m_priceList.modify(pricePair, entries);
+ } else {
+ m_priceList.insert(pricePair, entries);
+ }
+}
+
+void MyMoneySeqAccessMgr::removePrice(const MyMoneyPrice& price)
+{
+ MyMoneySecurityPair pricePair(price.from(), price.to());
+ QMap<MyMoneySecurityPair, MyMoneyPriceEntries>::ConstIterator it_m;
+ it_m = m_priceList.find(pricePair);
+
+ MyMoneyPriceEntries entries;
+ if(it_m != m_priceList.end()) {
+ entries = (*it_m);
+ }
+
+ // store new value in local copy
+ entries.remove(price.date());
+
+ if(entries.count() != 0) {
+ m_priceList.modify(pricePair, entries);
+ } else {
+ m_priceList.remove(pricePair);
+ }
+}
+
+const MyMoneyPriceList MyMoneySeqAccessMgr::priceList(void) const
+{
+ MyMoneyPriceList list;
+ m_priceList.map(list);
+ return list;
+}
+
+const MyMoneyPrice MyMoneySeqAccessMgr::price(const QString& fromId, const QString& toId, const QDate& _date, const bool exactDate) const
+{
+ MyMoneyPrice rc;
+ MyMoneyPriceEntries::ConstIterator it;
+ QDate date(_date);
+
+ // If no valid date is passed, we use today's date.
+ if(!date.isValid())
+ date = QDate::currentDate();
+
+ // If the caller selected an exact entry, we can search for
+ // it using the date as the key
+ if(exactDate) {
+ it = m_priceList[MyMoneySecurityPair(fromId, toId)].find(date);
+ if(it != m_priceList[MyMoneySecurityPair(fromId, toId)].end())
+ rc = *it;
+
+ } else {
+ // otherwise, we must scan the map for the previous price entry
+ for(it = m_priceList[MyMoneySecurityPair(fromId, toId)].begin(); it != m_priceList[MyMoneySecurityPair(fromId, toId)].end(); ++it) {
+ if(date < it.key())
+ break;
+
+ if(date >= it.key())
+ rc = *it;
+ }
+ }
+ return rc;
+}
+
+void MyMoneySeqAccessMgr::clearCache(void)
+{
+ m_balanceCache.clear();
+}
+
+void MyMoneySeqAccessMgr::rebuildAccountBalances(void)
+{
+ // reset the balance of all accounts to 0
+ QMap<QString, MyMoneyAccount> map;
+ m_accountList.map(map);
+
+ QMap<QString, MyMoneyAccount>::iterator it_a;
+ for(it_a = map.begin(); it_a != map.end(); ++it_a) {
+ (*it_a).setBalance(MyMoneyMoney(0));
+ }
+
+ // now scan over all transactions and all splits and setup the balances
+ QMap<QString, MyMoneyTransaction>::const_iterator it_t;
+ for(it_t = m_transactionList.begin(); it_t != m_transactionList.end(); ++it_t) {
+ const QValueList<MyMoneySplit>& splits = (*it_t).splits();
+ QValueList<MyMoneySplit>::const_iterator it_s = splits.begin();
+ for(; it_s != splits.end(); ++it_s ) {
+ if(!(*it_s).shares().isZero()) {
+ const QString& id = (*it_s).accountId();
+ // locate the account and if present, update data
+ if(map.find(id) != map.end()) {
+ map[id].adjustBalance(*it_s);
+ }
+ }
+ }
+ }
+
+ m_accountList = map;
+}
+
+bool MyMoneySeqAccessMgr::isReferenced(const MyMoneyObject& obj, const MyMoneyFileBitArray& skipCheck) const
+{
+ // We delete all references in reports when an object
+ // is deleted, so we don't need to check here. See
+ // MyMoneySeqAccessMgr::removeReferences(). In case
+ // you miss the report checks in the following lines ;)
+
+ bool rc = false;
+ const QString& id = obj.id();
+ QMap<QString, MyMoneyTransaction>::const_iterator it_t;
+ QMap<QString, MyMoneyAccount>::const_iterator it_a;
+ QMap<QString, MyMoneyInstitution>::const_iterator it_i;
+ QMap<QString, MyMoneyPayee>::const_iterator it_p;
+ QMap<QString, MyMoneyBudget>::const_iterator it_b;
+ QMap<QString, MyMoneySchedule>::const_iterator it_sch;
+ QMap<QString, MyMoneySecurity>::const_iterator it_sec;
+ MyMoneyPriceList::const_iterator it_pr;
+
+ // FIXME optimize the list of objects we have to checks
+ // with a bit of knowledge of the internal structure, we
+ // could optimize the number of objects we check for references
+
+ // Scan all engine objects for a reference
+ if(!skipCheck[RefCheckTransaction]) {
+ for(it_t = m_transactionList.begin(); !rc && it_t != m_transactionList.end(); ++it_t) {
+ rc = (*it_t).hasReferenceTo(id);
+ }
+ }
+
+ if(!skipCheck[RefCheckAccount]) {
+ for(it_a = m_accountList.begin(); !rc && it_a != m_accountList.end(); ++it_a) {
+ rc = (*it_a).hasReferenceTo(id);
+ }
+ }
+ if(!skipCheck[RefCheckInstitution]) {
+ for(it_i = m_institutionList.begin(); !rc && it_i != m_institutionList.end(); ++it_i) {
+ rc = (*it_i).hasReferenceTo(id);
+ }
+ }
+ if(!skipCheck[RefCheckPayee]) {
+ for(it_p = m_payeeList.begin(); !rc && it_p != m_payeeList.end(); ++it_p) {
+ rc = (*it_p).hasReferenceTo(id);
+ }
+ }
+
+ if(!skipCheck[RefCheckBudget]) {
+ for(it_b = m_budgetList.begin(); !rc && it_b != m_budgetList.end(); ++it_b) {
+ rc = (*it_b).hasReferenceTo(id);
+ }
+ }
+ if(!skipCheck[RefCheckSchedule]) {
+ for(it_sch = m_scheduleList.begin(); !rc && it_sch != m_scheduleList.end(); ++it_sch) {
+ rc = (*it_sch).hasReferenceTo(id);
+ }
+ }
+ if(!skipCheck[RefCheckSecurity]) {
+ for(it_sec = m_securitiesList.begin(); !rc && it_sec != m_securitiesList.end(); ++it_sec) {
+ rc = (*it_sec).hasReferenceTo(id);
+ }
+ }
+ if(!skipCheck[RefCheckCurrency]) {
+ for(it_sec = m_currencyList.begin(); !rc && it_sec != m_currencyList.end(); ++it_sec) {
+ rc = (*it_sec).hasReferenceTo(id);
+ }
+ }
+ // within the pricelist we don't have to scan each entry. Checking the QPair
+ // members of the MyMoneySecurityPair is enough as they are identical to the
+ // two security ids
+ if(!skipCheck[RefCheckPrice]) {
+ for(it_pr = m_priceList.begin(); !rc && it_pr != m_priceList.end(); ++it_pr) {
+ rc = (it_pr.key().first == id) || (it_pr.key().second == id);
+ }
+ }
+
+ return rc;
+}
+
+void MyMoneySeqAccessMgr::startTransaction(void)
+{
+ m_payeeList.startTransaction(&m_nextPayeeID);
+ m_institutionList.startTransaction(&m_nextInstitutionID);
+ m_accountList.startTransaction(&m_nextPayeeID);
+ m_transactionList.startTransaction(&m_nextTransactionID);
+ m_transactionKeys.startTransaction();
+ m_scheduleList.startTransaction(&m_nextScheduleID);
+ m_securitiesList.startTransaction(&m_nextSecurityID);
+ m_currencyList.startTransaction();
+ m_reportList.startTransaction(&m_nextReportID);
+ m_budgetList.startTransaction(&m_nextBudgetID);
+ m_priceList.startTransaction();
+}
+
+bool MyMoneySeqAccessMgr::commitTransaction(void)
+{
+ bool rc = false;
+ rc |= m_payeeList.commitTransaction();
+ rc |= m_institutionList.commitTransaction();
+ rc |= m_accountList.commitTransaction();
+ rc |= m_transactionList.commitTransaction();
+ rc |= m_transactionKeys.commitTransaction();
+ rc |= m_scheduleList.commitTransaction();
+ rc |= m_securitiesList.commitTransaction();
+ rc |= m_currencyList.commitTransaction();
+ rc |= m_reportList.commitTransaction();
+ rc |= m_budgetList.commitTransaction();
+ rc |= m_priceList.commitTransaction();
+
+ // if there was a change, touch the whole storage object
+ if(rc)
+ touch();
+
+ return rc;
+}
+
+void MyMoneySeqAccessMgr::rollbackTransaction(void)
+{
+ m_payeeList.rollbackTransaction();
+ m_institutionList.rollbackTransaction();
+ m_accountList.rollbackTransaction();
+ m_transactionList.rollbackTransaction();
+ m_transactionKeys.rollbackTransaction();
+ m_scheduleList.rollbackTransaction();
+ m_securitiesList.rollbackTransaction();
+ m_currencyList.rollbackTransaction();
+ m_reportList.rollbackTransaction();
+ m_budgetList.rollbackTransaction();
+ m_priceList.rollbackTransaction();
+}
+
+void MyMoneySeqAccessMgr::removeReferences(const QString& id)
+{
+ QMap<QString, MyMoneyReport>::const_iterator it_r;
+ QMap<QString, MyMoneyBudget>::const_iterator it_b;
+
+ // remove from reports
+ for(it_r = m_reportList.begin(); it_r != m_reportList.end(); ++it_r) {
+ MyMoneyReport r = *it_r;
+ r.removeReference(id);
+ m_reportList.modify(r.id(), r);
+ }
+
+ // remove from budgets
+ for(it_b = m_budgetList.begin(); it_b != m_budgetList.end(); ++it_b) {
+ MyMoneyBudget b = *it_b;
+ b.removeReference(id);
+ m_budgetList.modify(b.id(), b);
+ }
+}
+
+#undef TRY
+#undef CATCH
+#undef PASS
+
+// vim:cin:si:ai:et:ts=2:sw=2:
diff --git a/kmymoney2/mymoney/storage/mymoneyseqaccessmgr.h b/kmymoney2/mymoney/storage/mymoneyseqaccessmgr.h
new file mode 100644
index 0000000..f200bef
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneyseqaccessmgr.h
@@ -0,0 +1,1232 @@
+/***************************************************************************
+ mymoneyseqaccessmgr.h - description
+ -------------------
+ begin : Sun May 5 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#ifndef MYMONEYSEQACCESSMGR_H
+#define MYMONEYSEQACCESSMGR_H
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include "imymoneystorage.h"
+#include "imymoneyserialize.h"
+#include "mymoneymap.h"
+
+/**
+ * @author Thomas Baumgart
+ */
+
+/**
+ * This member represents an item in the balance cache. The balance cache
+ * is used for fast processing of the balance of an account. Several
+ * of these objects are held by the MyMoneySeqAccessMgr() object in a map
+ * with the account Id as key. If such a cache item is present in the map,
+ * the contained balance of it will be used as current balance for this
+ * account. If the balance is changed by any operation, the
+ * MyMoneyBalanceCacheItem for the modified account will be removed from
+ * the map and the next time the balance for this account is requested,
+ * it has to be recalculated. After recalculation, a new MyMoneyBalanceCacheItem
+ * will be created containing the new balance value.
+ *
+ * @see MyMoneySeqAccessMgr::balance() and
+ * MyMoneySeqAccessMgr::invalidateBalanceCache() for a usage example
+ */
+class MyMoneyBalanceCacheItem {
+public:
+ MyMoneyBalanceCacheItem() { valid = false; };
+ MyMoneyBalanceCacheItem(const MyMoneyMoney& val) { balance = val; valid = true; };
+
+ bool operator == (const MyMoneyBalanceCacheItem& right) const;
+ bool valid;
+ MyMoneyMoney balance;
+};
+
+/**
+ * The MyMoneySeqAccessMgr class represents the storage engine for sequential
+ * files. The actual file type and it's internal storage format (e.g. binary
+ * or XML) is not important and handled through the IMyMoneySerialize() interface.
+ *
+ * The MyMoneySeqAccessMgr must be loaded by an application using the
+ * IMyMoneySerialize() interface and can then be accessed through the
+ * IMyMoneyStorage() interface. All data is loaded into memory, modified
+ * and kept there. It is the subject of an outside object to store the
+ * modified data in a persistant storage area using the IMyMoneySerialize()
+ * interface. As indication, if data has been changed, the retrun value
+ * of the method dirty() can be used.
+ */
+class MyMoneySeqAccessMgr : public IMyMoneyStorage, public IMyMoneySerialize,
+ public MyMoneyKeyValueContainer
+{
+public:
+
+ MyMoneySeqAccessMgr();
+ ~MyMoneySeqAccessMgr();
+
+ // general get functions
+ const MyMoneyPayee user(void) const { return m_user; };
+ const QDate creationDate(void) const { return m_creationDate; };
+ const QDate lastModificationDate(void) const { return m_lastModificationDate; };
+ unsigned int currentFixVersion(void) const { return m_currentFixVersion; };
+ unsigned int fileFixVersion(void) const { return m_fileFixVersion; };
+
+
+ // general set functions
+ void setUser(const MyMoneyPayee& user) { m_user = user;
+ touch(); };
+ void setCreationDate(const QDate& val) { m_creationDate = val; touch(); };
+ void setLastModificationDate(const QDate& val) { m_lastModificationDate = val; m_dirty = false; };
+ void setFileFixVersion(const unsigned int v) { m_fileFixVersion = v; };
+ /**
+ * This method is used to get a SQL reader for subsequent database access
+ */
+ KSharedPtr <MyMoneyStorageSql> connectToDatabase (const KURL& url);
+ /**
+ * This method is used when a database file is open, and the data is to
+ * be saved in a different file or format. It will ensure that all data
+ * from the database is available in memory to enable it to be written.
+ */
+ virtual void fillStorage() { };
+
+ /**
+ * This method is used to duplicate the MyMoneySeqAccessMgr object and return
+ * a pointer to the newly created copy. The caller of this method is the
+ * new owner of the object and must destroy it.
+ */
+ MyMoneySeqAccessMgr const * duplicate(void);
+
+ /**
+ * Returns the account addressed by it's id.
+ *
+ * @param id id of the account to locate.
+ * @return reference to MyMoneyAccount object. An exception is thrown
+ * if the id is unknown
+ */
+ const MyMoneyAccount account(const QString& id) const;
+
+ /**
+ * This method is used to check whether a given
+ * account id references one of the standard accounts or not.
+ *
+ * @param id account id
+ * @return true if account-id is one of the standards, false otherwise
+ */
+ bool isStandardAccount(const QString& id) const;
+
+ /**
+ * This method is used to set the name for the specified standard account
+ * within the storage area. An exception will be thrown, if an error
+ * occurs
+ *
+ * @param id QString reference to one of the standard accounts. Possible
+ * values are:
+ *
+ * @li STD_ACC_LIABILITY
+ * @li STD_ACC_ASSET
+ * @li STD_ACC_EXPENSE
+ * @li STD_ACC_INCOME
+ * @li STD_ACC_EQUITY
+ *
+ * @param name QString reference to the name to be set
+ *
+ */
+ void setAccountName(const QString& id, const QString& name);
+
+ /**
+ * This method is used to create a new account
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account MyMoneyAccount filled with data
+ */
+ void addAccount(MyMoneyAccount& account);
+
+ /**
+ * This method is used to create a new payee
+ *
+ * An exception will be thrown upon error conditions
+ *
+ * @param payee MyMoneyPayee reference to payee information
+ */
+ void addPayee(MyMoneyPayee& payee);
+
+ /**
+ * This method is used to retrieve information about a payee
+ * An exception will be thrown upon error conditions.
+ *
+ * @param id QString reference to id of payee
+ *
+ * @return MyMoneyPayee object of payee
+ */
+ const MyMoneyPayee payee(const QString& id) const;
+
+ /**
+ * This method is used to retrieve the id to a corresponding
+ * name of a payee/receiver.
+ * An exception will be thrown upon error conditions.
+ *
+ * @param payee QString reference to name of payee
+ *
+ * @return MyMoneyPayee reference to object of payee
+ */
+ const MyMoneyPayee payeeByName(const QString& payee) const;
+
+ /**
+ * This method is used to modify an existing payee
+ *
+ * An exception will be thrown upon error conditions
+ *
+ * @param payee MyMoneyPayee reference to payee information
+ */
+ void modifyPayee(const MyMoneyPayee& payee);
+
+ /**
+ * This method is used to remove an existing payee
+ *
+ * An exception will be thrown upon error conditions
+ *
+ * @param payee MyMoneyPayee reference to payee information
+ */
+ void removePayee(const MyMoneyPayee& payee);
+
+ /**
+ * This method returns a list of the payees
+ * inside a MyMoneyStorage object
+ *
+ * @return QValueList<MyMoneyPayee> containing the payee information
+ */
+ const QValueList<MyMoneyPayee> payeeList(void) const;
+
+ /**
+ * This method is used to add one account as sub-ordinate to another
+ * (parent) account. The objects passed as arguments will be modified
+ * accordingly.
+ *
+ * @param parent parent account the account should be added to
+ * @param account the account to be added
+ */
+ void addAccount(MyMoneyAccount& parent, MyMoneyAccount& account);
+
+ /**
+ * Adds an institution to the storage. A
+ * respective institution-ID will be generated within this record.
+ * The ID is stored as QString in the object passed as argument.
+ * An exception will be thrown upon error conditions.
+ *
+ * @param institution The complete institution information in a
+ * MyMoneyInstitution object
+ */
+ void addInstitution(MyMoneyInstitution& institution);
+
+ /**
+ * Adds a transaction to the file-global transaction pool. A respective
+ * transaction-ID will be generated within this record. The ID is stored
+ * as QString in the transaction object. The accounts of the referenced splits
+ * will be updated to have a reference to the transaction just added.
+ *
+ * @param transaction reference to the transaction
+ * @param skipAccountUpdate if set, the transaction lists of the accounts
+ * referenced in the splits are not updated. This is used for
+ * bulk loading a lot of transactions but not during normal operation
+ */
+ void addTransaction(MyMoneyTransaction& transaction, const bool skipAccountUpdate = false);
+
+ /**
+ * Modifies an already existing account in the file global account pool.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account reference to the new account information
+ * @param skipCheck if @p true, skips the built in consistency check for
+ * the object to be updated. Do not set this parameter
+ * to true. This is only used for the MyMoneyFile::consistencyCheck()
+ * procedure to be able to reload accounts. The default
+ * setting of this parameter is @p false.
+ */
+ void modifyAccount(const MyMoneyAccount& account, const bool skipCheck = false);
+
+ /**
+ * Modifies an already existing institution in the file global
+ * institution pool.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param institution The complete new institution information
+ */
+ void modifyInstitution(const MyMoneyInstitution& institution);
+
+ /**
+ * This method is used to update a specific transaction in the
+ * transaction pool of the MyMoneyFile object
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param transaction reference to transaction to be changed
+ */
+ void modifyTransaction(const MyMoneyTransaction& transaction);
+
+ /**
+ * This method re-parents an existing account
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account MyMoneyAccount reference to account to be re-parented
+ * @param parent MyMoneyAccount reference to new parent account
+ */
+ void reparentAccount(MyMoneyAccount &account, MyMoneyAccount& parent);
+
+ /**
+ * This method is used to remove a transaction from the transaction
+ * pool (journal).
+ *
+ * @param transaction const reference to transaction to be deleted
+ */
+ void removeTransaction(const MyMoneyTransaction& transaction);
+
+ /**
+ * Deletes an existing account from the file global account pool
+ * This method only allows to remove accounts that are not
+ * referenced by any split. Use moveSplits() to move splits
+ * to another account. An exception is thrown in case of a
+ * problem.
+ *
+ * @param account reference to the account to be deleted.
+ */
+ void removeAccount(const MyMoneyAccount& account);
+
+ /**
+ * Deletes an existing institution from the file global institution pool
+ * Also modifies the accounts that reference this institution as
+ * their institution.
+ *
+ * @param institution institution to be deleted.
+ */
+ void removeInstitution(const MyMoneyInstitution& institution);
+
+ /**
+ * This method is used to extract a transaction from the file global
+ * transaction pool through an id. In case of an invalid id, an
+ * exception will be thrown.
+ *
+ * @param id id of transaction as QString.
+ * @return reference to the requested transaction
+ */
+ const MyMoneyTransaction transaction(const QString& id) const;
+
+ /**
+ * This method is used to extract a transaction from the file global
+ * transaction pool through an index into an account.
+ *
+ * @param account id of the account as QString
+ * @param idx number of transaction in this account
+ * @return reference to MyMoneyTransaction object
+ */
+ const MyMoneyTransaction transaction(const QString& account, const int idx) const;
+
+ /**
+ * This method is used to determince, if the account with the
+ * given ID is referenced by any split in m_transactionList.
+ *
+ * @param id id of the account to be checked for
+ * @return true if account is referenced, false otherwise
+ */
+ bool hasActiveSplits(const QString& id) const;
+
+ /**
+ * This method is used to return the actual balance of an account
+ * without it's sub-ordinate accounts. If a @p date is presented,
+ * the balance at the beginning of this date (not including any
+ * transaction on this date) is returned. Otherwise all recorded
+ * transactions are included in the balance.
+ *
+ * @param id id of the account in question
+ * @param date return balance for specific date
+ * @return balance of the account as MyMoneyMoney object
+ */
+ const MyMoneyMoney balance(const QString& id, const QDate& date = QDate()) const;
+
+ /**
+ * This method is used to return the actual balance of an account
+ * including it's sub-ordinate accounts. If a @p date is presented,
+ * the balance at the beginning of this date (not including any
+ * transaction on this date) is returned. Otherwise all recorded
+ * transactions are included in the balance.
+ *
+ * @param id id of the account in question
+ * @param date return balance for specific date
+ * @return balance of the account as MyMoneyMoney object
+ */
+ const MyMoneyMoney totalBalance(const QString& id, const QDate& date = QDate()) const;
+
+ /**
+ * Returns the institution of a given ID
+ *
+ * @param id id of the institution to locate
+ * @return MyMoneyInstitution object filled with data. If the institution
+ * could not be found, an exception will be thrown
+ */
+ const MyMoneyInstitution institution(const QString& id) const;
+
+ /**
+ * This method returns an indicator if the storage object has been
+ * changed after it has last been saved to permanent storage.
+ *
+ * @return true if changed, false if not
+ */
+ bool dirty(void) const { return m_dirty; }
+
+ /**
+ * This method can be used by an external object to force the
+ * storage object to be dirty. This is used e.g. when an upload
+ * to an external destination failed but the previous storage
+ * to a local disk was ok.
+ */
+ void setDirty(void) { m_dirty = true; };
+
+ /**
+ * This method returns a list of the institutions
+ * inside a MyMoneyFile object
+ *
+ * @return QMap containing the institution information
+ */
+ const QValueList<MyMoneyInstitution> institutionList(void) const;
+
+ /**
+ * This method returns a list of accounts inside the storage object.
+ *
+ * @param list reference to QValueList receiving the account objects
+ *
+ * @note The standard accounts will not be returned
+ */
+ void accountList(QValueList<MyMoneyAccount>& list) const;
+
+ /**
+ * This method is used to pull a list of transactions from the file
+ * global transaction pool. It returns all those transactions
+ * that match the filter passed as argument. If the filter is empty,
+ * the whole journal will be returned.
+ * The list returned is sorted according to the transactions posting date.
+ * If more than one transaction exists for the same date, the order among
+ * them is undefined.
+ *
+ * The @p list will be cleared by this method.
+ *
+ * @param list reference to list
+ * @param filter MyMoneyTransactionFilter object with the match criteria
+ *
+ * @return set of transactions in form of a QValueList<MyMoneyTransaction>
+ */
+ void transactionList(QValueList<MyMoneyTransaction>& list, MyMoneyTransactionFilter& filter) const;
+
+ /**
+ * This method is used to pull a list of transactions from the file
+ * global transaction pool. It returns all those transactions
+ * that match the filter passed as argument. If the filter is empty,
+ * the whole journal will be returned.
+ * The list returned is sorted according to the transactions posting date.
+ * If more than one transaction exists for the same date, the order among
+ * them is undefined.
+ *
+ * The @p list will be cleared by this method.
+ *
+ * @param list reference to list
+ * @param filter MyMoneyTransactionFilter object with the match criteria
+ *
+ * @return set of transactions in form of a QValueList<QPair<MyMoneyTransaction,MyMoneySplit> >
+ */
+ void transactionList(QValueList< QPair<MyMoneyTransaction, MyMoneySplit> >& list, MyMoneyTransactionFilter& filter) const;
+
+ /**
+ * Compatibility interface for the previous method.
+ */
+ const QValueList<MyMoneyTransaction> transactionList(MyMoneyTransactionFilter& filter) const;
+
+ /**
+ * This method returns whether a given transaction is already in memory, to avoid
+ * reloading it from the database
+ */
+ bool isDuplicateTransaction(const QString& id) const { return m_transactionKeys.contains(id); }
+
+ /**
+ * This method returns the number of transactions currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @param account QString reference to account id. If account is empty
+ + all transactions (the journal) will be counted. If account
+ * is not empty it returns the number of transactions
+ * that have splits in this account.
+ *
+ * @return number of transactions in journal/account
+ */
+ unsigned int transactionCount(const QString& account = QString()) const;
+
+ const QMap<QString, unsigned long> transactionCountMap(void) const;
+
+ /**
+ * This method returns the number of institutions currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @return number of institutions known to file
+ */
+ unsigned int institutionCount(void) const;
+
+ /**
+ * This method returns the number of accounts currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @return number of accounts currently known inside a MyMoneyFile object
+ */
+ unsigned int accountCount(void) const;
+
+ /**
+ * This method is used to return the standard liability account
+ * @return MyMoneyAccount liability account(group)
+ */
+ const MyMoneyAccount liability(void) const { return account(STD_ACC_LIABILITY); };
+
+ /**
+ * This method is used to return the standard asset account
+ * @return MyMoneyAccount asset account(group)
+ */
+ const MyMoneyAccount asset(void) const { return account(STD_ACC_ASSET); };
+
+ /**
+ * This method is used to return the standard expense account
+ * @return MyMoneyAccount expense account(group)
+ */
+ const MyMoneyAccount expense(void) const { return account(STD_ACC_EXPENSE); };
+
+ /**
+ * This method is used to return the standard income account
+ * @return MyMoneyAccount income account(group)
+ */
+ const MyMoneyAccount income(void) const { return account(STD_ACC_INCOME); };
+
+ /**
+ * This method is used to return the standard equity account
+ * @return MyMoneyAccount equity account(group)
+ */
+ const MyMoneyAccount equity(void) const { return account(STD_ACC_EQUITY); };
+
+ virtual void loadAccounts(const QMap<QString, MyMoneyAccount>& acc);
+ virtual void loadTransactions(const QMap<QString, MyMoneyTransaction>& map);
+ virtual void loadInstitutions(const QMap<QString, MyMoneyInstitution>& map);
+ virtual void loadPayees(const QMap<QString, MyMoneyPayee>& map);
+ virtual void loadSchedules(const QMap<QString, MyMoneySchedule>& map);
+ virtual void loadSecurities(const QMap<QString, MyMoneySecurity>& map);
+ virtual void loadCurrencies(const QMap<QString, MyMoneySecurity>& map);
+ virtual void loadPrices(const MyMoneyPriceList& list);
+
+ virtual void loadAccountId(const unsigned long id);
+ virtual void loadTransactionId(const unsigned long id);
+ virtual void loadPayeeId(const unsigned long id);
+ virtual void loadInstitutionId(const unsigned long id);
+ virtual void loadScheduleId(const unsigned long id);
+ virtual void loadSecurityId(const unsigned long id);
+ virtual void loadReportId(const unsigned long id);
+ virtual void loadBudgetId(const unsigned long id);
+
+ virtual unsigned long accountId(void) const { return m_nextAccountID; };
+ virtual unsigned long transactionId(void) const { return m_nextTransactionID; };
+ virtual unsigned long payeeId(void) const { return m_nextPayeeID; };
+ virtual unsigned long institutionId(void) const { return m_nextInstitutionID; };
+ virtual unsigned long scheduleId(void) const { return m_nextScheduleID; };
+ virtual unsigned long securityId(void) const { return m_nextSecurityID; };
+ virtual unsigned long reportId(void) const { return m_nextReportID; };
+ virtual unsigned long budgetId(void) const { return m_nextBudgetID; };
+
+
+ /**
+ * This method is used to extract a value from
+ * KeyValueContainer. For details see MyMoneyKeyValueContainer::value().
+ *
+ * @param key const reference to QString containing the key
+ * @return QString containing the value
+ */
+ const QString value(const QString& key) const;
+
+ /**
+ * This method is used to set a value in the
+ * KeyValueContainer. For details see MyMoneyKeyValueContainer::setValue().
+ *
+ * @param key const reference to QString containing the key
+ * @param val const reference to QString containing the value
+ */
+ void setValue(const QString& key, const QString& val);
+
+ /**
+ * This method is used to delete a key-value-pair from the
+ * KeyValueContainer identified by the parameter
+ * @p key. For details see MyMoneyKeyValueContainer::deletePair().
+ *
+ * @param key const reference to QString containing the key
+ */
+ void deletePair(const QString& key);
+
+ // documented in IMyMoneySerialize base class
+ const QMap<QString, QString> pairs(void) const;
+
+ // documented in IMyMoneySerialize base class
+ void setPairs(const QMap<QString, QString>& list);
+
+ /**
+ * This method is used to add a scheduled transaction to the engine.
+ * It must be sure, that the id of the object is not filled. When the
+ * method returns to the caller, the id will be filled with the
+ * newly created object id value.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param sched reference to the MyMoneySchedule object
+ */
+ void addSchedule(MyMoneySchedule& sched);
+
+ /**
+ * This method is used to modify an existing MyMoneySchedule
+ * object. Therefor, the id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param sched const reference to the MyMoneySchedule object to be updated
+ */
+ void modifySchedule(const MyMoneySchedule& sched);
+
+ /**
+ * This method is used to remove an existing MyMoneySchedule object
+ * from the engine. The id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param sched const reference to the MyMoneySchedule object to be updated
+ */
+ void removeSchedule(const MyMoneySchedule& sched);
+
+ /**
+ * This method is used to retrieve a single MyMoneySchedule object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneySchedule object
+ * @return MyMoneySchedule object
+ */
+ const MyMoneySchedule schedule(const QString& id) const;
+
+ /**
+ * This method is used to create a new security object. The ID will be created
+ * automatically. The object passed with the parameter @p security is modified
+ * to contain the assigned id.
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param security MyMoneySecurity filled with data
+ */
+ virtual void addSecurity(MyMoneySecurity& security);
+
+ /**
+ * This method is used to modify an existing MyMoneySchedule
+ * object.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param security reference to the MyMoneySecurity object to be updated
+ */
+ void modifySecurity(const MyMoneySecurity& security);
+
+ /**
+ * This method is used to remove an existing MyMoneySecurity object
+ * from the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param security reference to the MyMoneySecurity object to be removed
+ */
+ void removeSecurity(const MyMoneySecurity& security);
+
+ /**
+ * This method is used to retrieve a single MyMoneySchedule object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneySchedule object
+ * @return MyMoneySchedule object
+ */
+ const MyMoneySecurity security(const QString& id) const;
+
+
+ /**
+ * This method returns a list of security objects that the engine has
+ * knowledge of.
+ */
+ const QValueList<MyMoneySecurity> securityList(void) const;
+
+ /**
+ * This method is used to add a new currency object to the engine.
+ * The ID of the object is the trading symbol, so there is no need for an additional
+ * ID since the symbol is guaranteed to be unique.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param currency reference to the MyMoneyCurrency object
+ */
+ void addCurrency(const MyMoneySecurity& currency);
+
+ /**
+ * This method is used to modify an existing MyMoneyCurrency
+ * object.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param currency reference to the MyMoneyCurrency object
+ */
+ void modifyCurrency(const MyMoneySecurity& currency);
+
+ /**
+ * This method is used to remove an existing MyMoneyCurrency object
+ * from the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param currency reference to the MyMoneyCurrency object
+ */
+ void removeCurrency(const MyMoneySecurity& currency);
+
+ /**
+ * This method is used to retrieve a single MyMoneySchedule object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneySchedule object
+ * @return MyMoneySchedule object
+ */
+ const MyMoneySecurity currency(const QString& id) const;
+
+ /**
+ * This method is used to retrieve the list of all currencies
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneyCurrency objects.
+ */
+ const QValueList<MyMoneySecurity> currencyList(void) const;
+
+ /**
+ * This method is used to extract a list of scheduled transactions
+ * according to the filter criteria passed as arguments.
+ *
+ * @param accountId only search for scheduled transactions that reference
+ * accound @p accountId. If accountId is the empty string,
+ * this filter is off. Default is @p QString().
+ * @param type only schedules of type @p type are searched for.
+ * See MyMoneySchedule::typeE for details.
+ * Default is MyMoneySchedule::TYPE_ANY
+ * @param occurence only schedules of occurence type @p occurence are searched for.
+ * See MyMoneySchedule::occurenceE for details.
+ * Default is MyMoneySchedule::OCCUR_ANY
+ * @param paymentType only schedules of payment method @p paymentType
+ * are searched for.
+ * See MyMoneySchedule::paymentTypeE for details.
+ * Default is MyMoneySchedule::STYPE_ANY
+ * @param startDate only schedules with payment dates after @p startDate
+ * are searched for. Default is all dates (QDate()).
+ * @param endDate only schedules with payment dates ending prior to @p endDate
+ * are searched for. Default is all dates (QDate()).
+ * @param overdue if true, only those schedules that are overdue are
+ * searched for. Default is false (all schedules will be returned).
+ *
+ * @return const QValueList<MyMoneySchedule> list of schedule objects.
+ */
+ const QValueList<MyMoneySchedule> scheduleList(const QString& accountId = QString(),
+ const MyMoneySchedule::typeE type = MyMoneySchedule::TYPE_ANY,
+ const MyMoneySchedule::occurenceE occurence = MyMoneySchedule::OCCUR_ANY,
+ const MyMoneySchedule::paymentTypeE paymentType = MyMoneySchedule::STYPE_ANY,
+ const QDate& startDate = QDate(),
+ const QDate& endDate = QDate(),
+ const bool overdue = false) const;
+
+ const QValueList<MyMoneySchedule> scheduleListEx( int scheduleTypes,
+ int scheduleOcurrences,
+ int schedulePaymentTypes,
+ QDate startDate,
+ const QStringList& accounts=QStringList()) const;
+
+ /**
+ * This method is used to retrieve the list of all reports
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneyReport objects.
+ */
+ const QValueList<MyMoneyReport> reportList( void ) const;
+
+ /**
+ * This method is used to add a new report to the engine.
+ * It must be sure, that the id of the object is not filled. When the
+ * method returns to the caller, the id will be filled with the
+ * newly created object id value.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param report reference to the MyMoneyReport object
+ */
+ void addReport( MyMoneyReport& report );
+
+ /**
+ * This method is used to load a set of reports into the engine. This is
+ * used when loading from storage, and an ID is already known.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param reports reference to the map of MyMoneyReport objects
+ */
+ void loadReports( const QMap<QString, MyMoneyReport>& reports );
+
+ /**
+ * This method is used to modify an existing MyMoneyReport
+ * object. Therefor, the id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param report const reference to the MyMoneyReport object to be updated
+ */
+ void modifyReport( const MyMoneyReport& report );
+
+ /**
+ * This method returns the number of reports currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @return number of reports known to file
+ */
+ unsigned countReports(void) const;
+
+ /**
+ * This method is used to retrieve a single MyMoneyReport object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneyReport object
+ * @return MyMoneyReport object
+ */
+ const MyMoneyReport report( const QString& id ) const;
+
+ /**
+ * This method is used to remove an existing MyMoneyReport object
+ * from the engine. The id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param report const reference to the MyMoneyReport object to be updated
+ */
+ void removeReport(const MyMoneyReport& report);
+
+ /**
+ * This method is used to retrieve the list of all budgets
+ * known to the engine.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @return QValueList of all MyMoneyBudget objects.
+ */
+ const QValueList<MyMoneyBudget> budgetList( void ) const;
+
+ /**
+ * This method is used to add a new budget to the engine.
+ * It must be sure, that the id of the object is not filled. When the
+ * method returns to the caller, the id will be filled with the
+ * newly created object id value.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param budget reference to the MyMoneyBudget object
+ */
+ void addBudget( MyMoneyBudget& budget );
+
+ /**
+ * This method is used to load a set of budgets into the engine. This is
+ * used when loading from storage, and an ID is already known.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param budgets reference to the map of MyMoneyBudget object
+ */
+ void loadBudgets(const QMap<QString, MyMoneyBudget>& budgets);
+
+ /**
+ * This method is used to retrieve the id to a corresponding
+ * name of a budget
+ * An exception will be thrown upon error conditions.
+ *
+ * @param budget QString reference to name of budget
+ *
+ * @return MyMoneyBudget reference to object of budget
+ */
+ const MyMoneyBudget budgetByName(const QString& budget) const;
+
+ /**
+ * This method is used to modify an existing MyMoneyBudget
+ * object. Therefore, the id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param budget const reference to the MyMoneyBudget object to be updated
+ */
+ void modifyBudget( const MyMoneyBudget& budget );
+
+ /**
+ * This method returns the number of budgets currently known to file
+ * in the range 0..MAXUINT
+ *
+ * @return number of budgets known to file
+ */
+ unsigned countBudgets(void) const;
+
+ /**
+ * This method is used to retrieve a single MyMoneyBudget object.
+ * The id of the object must be supplied in the parameter @p id.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param id QString containing the id of the MyMoneyBudget object
+ * @return MyMoneyBudget object
+ */
+ MyMoneyBudget budget( const QString& id ) const;
+
+ /**
+ * This method is used to remove an existing MyMoneyBudget object
+ * from the engine. The id attribute of the object must be set.
+ *
+ * An exception will be thrown upon erronous situations.
+ *
+ * @param budget const reference to the MyMoneyBudget object to be updated
+ */
+ void removeBudget(const MyMoneyBudget& budget);
+
+
+ /**
+ * This method adds/replaces a price to/from the price list
+ */
+ void addPrice(const MyMoneyPrice& price);
+
+ /**
+ * This method removes a price from the price list
+ */
+ void removePrice(const MyMoneyPrice& price);
+
+ /**
+ * This method retrieves a price from the price list.
+ * If @p date is inValid, QDate::currentDate() is assumed.
+ */
+ const MyMoneyPrice price(const QString& fromId, const QString& toId, const QDate& date, const bool exactDate) const;
+
+ /**
+ * This method returns a list of all price entries.
+ */
+ const MyMoneyPriceList priceList(void) const;
+
+ /**
+ * Clear all internal caches (used internally for performance measurements)
+ */
+ void clearCache(void);
+
+ /**
+ * This method checks, if the given @p object is referenced
+ * by another engine object.
+ *
+ * @param obj const reference to object to be checked
+ * @param skipCheck QBitArray with ReferenceCheckBits set for which
+ * the check should be skipped
+ *
+ * @retval false @p object is not referenced
+ * @retval true @p institution is referenced
+ */
+ bool isReferenced(const MyMoneyObject& obj, const MyMoneyFileBitArray& skipCheck = MyMoneyFileBitArray()) const;
+
+ /**
+ * This method recalculates the balances of all accounts
+ * based on the transactions stored in the engine.
+ */
+ void rebuildAccountBalances(void);
+
+ virtual void startTransaction(void);
+ virtual bool commitTransaction(void);
+ virtual void rollbackTransaction(void);
+
+protected:
+ void removeReferences(const QString& id);
+
+private:
+
+ static const int INSTITUTION_ID_SIZE = 6;
+ static const int ACCOUNT_ID_SIZE = 6;
+ static const int TRANSACTION_ID_SIZE = 18;
+ static const int PAYEE_ID_SIZE = 6;
+ static const int SCHEDULE_ID_SIZE = 6;
+ static const int SECURITY_ID_SIZE = 6;
+ static const int REPORT_ID_SIZE = 6;
+ static const int BUDGET_ID_SIZE = 6;
+
+ /**
+ * This method is used to set the dirty flag and update the
+ * date of the last modification.
+ */
+ void touch(void);
+
+ /**
+ * This method is used to invalidate the cached balance for
+ * the selected account and all it's parents.
+ *
+ * @param id id of the account in question
+ */
+ void invalidateBalanceCache(const QString& id);
+
+ /**
+ * This member variable keeps the User information.
+ * @see setUser()
+ */
+ MyMoneyPayee m_user;
+
+ /**
+ * The member variable m_nextInstitutionID keeps the number that will be
+ * assigned to the next institution created. It is maintained by
+ * nextInstitutionID().
+ */
+ unsigned long m_nextInstitutionID;
+
+ /**
+ * The member variable m_nextAccountID keeps the number that will be
+ * assigned to the next institution created. It is maintained by
+ * nextAccountID().
+ */
+ unsigned long m_nextAccountID;
+
+ /**
+ * The member variable m_nextTransactionID keeps the number that will be
+ * assigned to the next transaction created. It is maintained by
+ * nextTransactionID().
+ */
+ unsigned long m_nextTransactionID;
+
+ /**
+ * The member variable m_nextPayeeID keeps the number that will be
+ * assigned to the next payee created. It is maintained by
+ * nextPayeeID()
+ */
+ unsigned long m_nextPayeeID;
+
+ /**
+ * The member variable m_nextScheduleID keeps the number that will be
+ * assigned to the next schedule created. It is maintained by
+ * nextScheduleID()
+ */
+ unsigned long m_nextScheduleID;
+
+ /**
+ * The member variable m_nextSecurityID keeps the number that will be
+ * assigned to the next security object created. It is maintained by
+ * nextSecurityID()
+ */
+ unsigned long m_nextSecurityID;
+
+ unsigned long m_nextReportID;
+
+ /**
+ * The member variable m_nextBudgetID keeps the number that will be
+ * assigned to the next budget object created. It is maintained by
+ * nextBudgetID()
+ */
+ unsigned long m_nextBudgetID;
+
+ /**
+ * The member variable m_institutionList is the container for the
+ * institutions known within this file.
+ */
+ MyMoneyMap<QString, MyMoneyInstitution> m_institutionList;
+
+ /**
+ * The member variable m_accountList is the container for the accounts
+ * known within this file.
+ */
+ MyMoneyMap<QString, MyMoneyAccount> m_accountList;
+
+ /**
+ * The member variable m_balanceCache is the container for the
+ * accounts actual balance
+ */
+ mutable QMap<QString, MyMoneyBalanceCacheItem> m_balanceCache;
+
+ /**
+ * This member keeps the date for which the m_balanceCache member
+ * is valid. In case the whole cache is invalid it is set to
+ * QDate().
+ */
+ mutable QDate m_balanceCacheDate;
+
+ /**
+ * The member variable m_transactionList is the container for all
+ * transactions within this file.
+ * @see m_transactionKeys
+ */
+ MyMoneyMap<QString, MyMoneyTransaction> m_transactionList;
+
+ /**
+ * The member variable m_transactionKeys is used to convert
+ * transaction id's into the corresponding key used in m_transactionList.
+ * @see m_transactionList;
+ */
+ MyMoneyMap<QString, QString> m_transactionKeys;
+
+ /**
+ * A list containing all the payees that have been used
+ */
+ MyMoneyMap<QString, MyMoneyPayee> m_payeeList;
+
+ /**
+ * A list containing all the scheduled transactions
+ */
+ MyMoneyMap<QString, MyMoneySchedule> m_scheduleList;
+
+ /**
+ * A list containing all the security information objects. Each object
+ * can represent a stock, bond, or mutual fund. It contains a price
+ * history that a user can add entries to. The price history will be used
+ * to determine the cost basis for sales, as well as the source of
+ * information for reports in a security account.
+ */
+ MyMoneyMap<QString, MyMoneySecurity> m_securitiesList;
+
+ /**
+ * A list containing all the currency information objects.
+ */
+ MyMoneyMap<QString, MyMoneySecurity> m_currencyList;
+
+ MyMoneyMap<QString, MyMoneyReport> m_reportList;
+
+ /**
+ * A list containing all the budget information objects.
+ */
+ MyMoneyMap<QString, MyMoneyBudget> m_budgetList;
+
+ MyMoneyMap<MyMoneySecurityPair, MyMoneyPriceEntries> m_priceList;
+
+ /**
+ * This member signals if the file has been modified or not
+ */
+ bool m_dirty;
+
+ /**
+ * This member variable keeps the creation date of this MyMoneySeqAccessMgr
+ * object. It is set during the constructor and can only be modified using
+ * the stream read operator.
+ */
+ QDate m_creationDate;
+
+ /**
+ * This member variable keeps the date of the last modification of
+ * the MyMoneySeqAccessMgr object.
+ */
+ QDate m_lastModificationDate;
+
+ /**
+ * This member variable contains the current fix level of application
+ * data files. (see kmymoneyview.cpp)
+ */
+ unsigned int m_currentFixVersion;
+ /**
+ * This member variable contains the current fix level of the
+ * presently open data file. (see kmymoneyview.cpp)
+ */
+ unsigned int m_fileFixVersion;
+ /**
+ * This method is used to get the next valid ID for a institution
+ * @return id for a institution
+ */
+ QString nextInstitutionID(void);
+
+ /**
+ * This method is used to get the next valid ID for an account
+ * @return id for an account
+ */
+ QString nextAccountID(void);
+
+ /**
+ * This method is used to get the next valid ID for a transaction
+ * @return id for a transaction
+ */
+ QString nextTransactionID(void);
+
+ /**
+ * This method is used to get the next valid ID for a payee
+ * @return id for a payee
+ */
+ QString nextPayeeID(void);
+
+ /**
+ * This method is used to get the next valid ID for a scheduled transaction
+ * @return id for a scheduled transaction
+ */
+ QString nextScheduleID(void);
+
+ /**
+ * This method is used to get the next valid ID for an security object.
+ * @return id for an security object
+ */
+ QString nextSecurityID(void);
+
+ QString nextReportID(void);
+
+ /**
+ * This method is used to get the next valid ID for a budget object.
+ * @return id for an budget object
+ */
+ QString nextBudgetID(void);
+
+
+ /**
+ * This method re-parents an existing account
+ *
+ * An exception will be thrown upon error conditions.
+ *
+ * @param account MyMoneyAccount reference to account to be re-parented
+ * @param parent MyMoneyAccount reference to new parent account
+ * @param sendNotification if true, notifications with the ids
+ * of all modified objects are send
+ */
+ void reparentAccount(MyMoneyAccount &account, MyMoneyAccount& parent, const bool sendNotification);
+ /**
+ * This method will close a database and log the use roff
+ */
+ void close(void) {}
+
+ /**
+ * This member variable is set when all transactions have been read from the database.
+ * This is would be probably the case when doing, for e.g., a full report,
+ * or after some types of transaction search which cannot be easily implemented in SQL
+ */
+ bool m_transactionListFull;
+};
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneyseqaccessmgrtest.cpp b/kmymoney2/mymoney/storage/mymoneyseqaccessmgrtest.cpp
new file mode 100644
index 0000000..09bf791
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneyseqaccessmgrtest.cpp
@@ -0,0 +1,1705 @@
+/***************************************************************************
+ mymoneyseqaccessmgrtest.cpp
+ -------------------
+ copyright : (C) 2002 by Thomas Baumgart
+ email : ipwizard@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. *
+ * *
+ ***************************************************************************/
+
+#include "mymoneyseqaccessmgrtest.h"
+#include <iostream>
+
+MyMoneySeqAccessMgrTest::MyMoneySeqAccessMgrTest()
+{
+}
+
+
+void MyMoneySeqAccessMgrTest::setUp()
+{
+ m = new MyMoneySeqAccessMgr;
+ MyMoneyFile* file = MyMoneyFile::instance();
+ file->attachStorage(m);
+ m->startTransaction();
+}
+
+void MyMoneySeqAccessMgrTest::tearDown()
+{
+ m->commitTransaction();
+ MyMoneyFile* file = MyMoneyFile::instance();
+ file->detachStorage(m);
+ delete m;
+}
+
+void MyMoneySeqAccessMgrTest::testEmptyConstructor()
+{
+ MyMoneyPayee user = m->user();
+
+ CPPUNIT_ASSERT(user.name().isEmpty());
+ CPPUNIT_ASSERT(user.address().isEmpty());
+ CPPUNIT_ASSERT(user.city().isEmpty());
+ CPPUNIT_ASSERT(user.state().isEmpty());
+ CPPUNIT_ASSERT(user.postcode().isEmpty());
+ CPPUNIT_ASSERT(user.telephone().isEmpty());
+ CPPUNIT_ASSERT(user.email().isEmpty());
+ CPPUNIT_ASSERT(m->m_nextInstitutionID == 0);
+ CPPUNIT_ASSERT(m->m_nextAccountID == 0);
+ CPPUNIT_ASSERT(m->m_nextTransactionID == 0);
+ CPPUNIT_ASSERT(m->m_nextPayeeID == 0);
+ CPPUNIT_ASSERT(m->m_nextScheduleID == 0);
+ CPPUNIT_ASSERT(m->m_nextReportID == 0);
+ CPPUNIT_ASSERT(m->m_institutionList.count() == 0);
+ CPPUNIT_ASSERT(m->m_accountList.count() == 5);
+ CPPUNIT_ASSERT(m->m_transactionList.count() == 0);
+ CPPUNIT_ASSERT(m->m_transactionKeys.count() == 0);
+ CPPUNIT_ASSERT(m->m_payeeList.count() == 0);
+ CPPUNIT_ASSERT(m->m_scheduleList.count() == 0);
+
+ CPPUNIT_ASSERT(m->m_dirty == false);
+ CPPUNIT_ASSERT(m->m_creationDate == QDate::currentDate());
+
+ CPPUNIT_ASSERT(m->liability().name() == "Liability");
+ CPPUNIT_ASSERT(m->asset().name() == "Asset");
+ CPPUNIT_ASSERT(m->expense().name() == "Expense");
+ CPPUNIT_ASSERT(m->income().name() == "Income");
+ CPPUNIT_ASSERT(m->equity().name() == "Equity");
+}
+
+void MyMoneySeqAccessMgrTest::testSetFunctions() {
+ MyMoneyPayee user = m->user();
+
+ m->m_dirty = false;
+ user.setName("Name");
+ m->setUser(user);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ m->m_dirty = false;
+ user.setAddress("Street");
+ m->setUser(user);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ m->m_dirty = false;
+ user.setCity("Town");
+ m->setUser(user);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ m->m_dirty = false;
+ user.setState("County");
+ m->setUser(user);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ m->m_dirty = false;
+ user.setPostcode("Postcode");
+ m->setUser(user);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ m->m_dirty = false;
+ user.setTelephone("Telephone");
+ m->setUser(user);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ m->m_dirty = false;
+ user.setEmail("Email");
+ m->setUser(user);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ m->m_dirty = false;
+ m->setValue("key", "value");
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+
+ user = m->user();
+ CPPUNIT_ASSERT(user.name() == "Name");
+ CPPUNIT_ASSERT(user.address() == "Street");
+ CPPUNIT_ASSERT(user.city() == "Town");
+ CPPUNIT_ASSERT(user.state() == "County");
+ CPPUNIT_ASSERT(user.postcode() == "Postcode");
+ CPPUNIT_ASSERT(user.telephone() == "Telephone");
+ CPPUNIT_ASSERT(user.email() == "Email");
+ CPPUNIT_ASSERT(m->value("key") == "value");
+
+ m->m_dirty = false;
+ m->deletePair("key");
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+}
+
+void MyMoneySeqAccessMgrTest::testSupportFunctions()
+{
+ CPPUNIT_ASSERT(m->nextInstitutionID() == "I000001");
+ CPPUNIT_ASSERT(m->m_nextInstitutionID == 1);
+ CPPUNIT_ASSERT(m->nextAccountID() == "A000001");
+ CPPUNIT_ASSERT(m->m_nextAccountID == 1);
+ CPPUNIT_ASSERT(m->nextTransactionID() == "T000000000000000001");
+ CPPUNIT_ASSERT(m->m_nextTransactionID == 1);
+ CPPUNIT_ASSERT(m->nextPayeeID() == "P000001");
+ CPPUNIT_ASSERT(m->m_nextPayeeID == 1);
+ CPPUNIT_ASSERT(m->nextScheduleID() == "SCH000001");
+ CPPUNIT_ASSERT(m->m_nextScheduleID == 1);
+ CPPUNIT_ASSERT(m->nextReportID() == "R000001");
+ CPPUNIT_ASSERT(m->m_nextReportID == 1);
+}
+
+void MyMoneySeqAccessMgrTest::testIsStandardAccount()
+{
+ CPPUNIT_ASSERT(m->isStandardAccount(STD_ACC_LIABILITY) == true);
+ CPPUNIT_ASSERT(m->isStandardAccount(STD_ACC_ASSET) == true);
+ CPPUNIT_ASSERT(m->isStandardAccount(STD_ACC_EXPENSE) == true);
+ CPPUNIT_ASSERT(m->isStandardAccount(STD_ACC_INCOME) == true);
+ CPPUNIT_ASSERT(m->isStandardAccount("A0002") == false);
+}
+
+void MyMoneySeqAccessMgrTest::testNewAccount() {
+ MyMoneyAccount a;
+
+ a.setName("AccountName");
+ a.setNumber("AccountNumber");
+
+ m->addAccount(a);
+ m->commitTransaction();
+ m->startTransaction();
+
+ CPPUNIT_ASSERT(m->m_nextAccountID == 1);
+ CPPUNIT_ASSERT(m->dirty() == true);
+ CPPUNIT_ASSERT(m->m_accountList.count() == 6);
+ CPPUNIT_ASSERT(m->m_accountList["A000001"].name() == "AccountName");
+}
+
+void MyMoneySeqAccessMgrTest::testAccount() {
+ testNewAccount();
+ m->m_dirty = false;
+
+ MyMoneyAccount a;
+
+ // make sure that an invalid ID causes an exception
+ try {
+ a = m->account("Unknown ID");
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == false);
+
+ // now make sure, that a real ID works
+ try {
+ a = m->account("A000001");
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(a.name() == "AccountName");
+ CPPUNIT_ASSERT(a.id() == "A000001");
+ CPPUNIT_ASSERT(m->dirty() == true);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testAddNewAccount() {
+ testNewAccount();
+
+ MyMoneyAccount a,b;
+ b.setName("Account2");
+ b.setNumber("Acc2");
+ m->addAccount(b);
+ m->commitTransaction();
+ m->startTransaction();
+
+ m->m_dirty = false;
+
+ CPPUNIT_ASSERT(m->m_nextAccountID == 2);
+ CPPUNIT_ASSERT(m->m_accountList.count() == 7);
+
+ // try to add account to undefined account
+ try {
+ MyMoneyAccount c("UnknownID", b);
+ m->addAccount(c, a);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+ m->commitTransaction();
+ m->startTransaction();
+
+ CPPUNIT_ASSERT(m->dirty() == false);
+ // now try to add account 1 as sub-account to account 2
+ a = m->account("A000001");
+ try {
+ CPPUNIT_ASSERT(m->m_accountList[STD_ACC_ASSET].accountList().count() == 0);
+ m->addAccount(b, a);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->m_accountList["A000002"].accountList()[0] == "A000001");
+ CPPUNIT_ASSERT(m->m_accountList["A000002"].accountList().count() == 1);
+ CPPUNIT_ASSERT(m->m_accountList[STD_ACC_ASSET].accountList().count() == 0);
+ CPPUNIT_ASSERT(m->dirty() == true);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testAddInstitution() {
+ MyMoneyInstitution i;
+
+ i.setName("Inst Name");
+
+ m->addInstitution(i);
+ CPPUNIT_ASSERT(m->m_institutionList.count() == 1);
+ CPPUNIT_ASSERT(m->m_nextInstitutionID == 1);
+ CPPUNIT_ASSERT(m->m_institutionList["I000001"].name() == "Inst Name");
+}
+
+void MyMoneySeqAccessMgrTest::testInstitution() {
+ testAddInstitution();
+ MyMoneyInstitution i;
+
+ m->m_dirty = false;
+
+ // try to find unknown institution
+ try {
+ i = m->institution("Unknown ID");
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ CPPUNIT_ASSERT(m->dirty() == false);
+
+ // now try to find real institution
+ try {
+ i = m->institution("I000001");
+ CPPUNIT_ASSERT(i.name() == "Inst Name");
+ CPPUNIT_ASSERT(m->dirty() == false);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testAccount2Institution() {
+ testAddInstitution();
+ testAddNewAccount();
+
+ MyMoneyInstitution i;
+ MyMoneyAccount a, b;
+
+ try {
+ i = m->institution("I000001");
+ a = m->account("A000001");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->m_dirty = false;
+
+ // try to add to a false institution
+ MyMoneyInstitution fake("Unknown ID", i);
+ a.setInstitutionId(fake.id());
+ try {
+ m->modifyAccount(a);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+ m->commitTransaction();
+ m->startTransaction();
+
+ CPPUNIT_ASSERT(m->dirty() == false);
+ // now try to do it with a real institution
+ try {
+ CPPUNIT_ASSERT(i.accountList().count() == 0);
+ a.setInstitutionId(i.id());
+ m->modifyAccount(a);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ CPPUNIT_ASSERT(a.institutionId() == i.id());
+ b = m->account("A000001");
+ CPPUNIT_ASSERT(b.institutionId() == i.id());
+ CPPUNIT_ASSERT(i.accountList().count() == 0);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testModifyAccount() {
+ testAccount2Institution();
+
+ // test the OK case
+ MyMoneyAccount a = m->account("A000001");
+ a.setName("New account name");
+ m->m_dirty = false;
+ try {
+ m->modifyAccount(a);
+ m->commitTransaction();
+ m->startTransaction();
+ MyMoneyAccount b = m->account("A000001");
+ CPPUNIT_ASSERT(b.parentAccountId() == a.parentAccountId());
+ CPPUNIT_ASSERT(b.name() == "New account name");
+ CPPUNIT_ASSERT(m->dirty() == true);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ // modify institution to unknown id
+ MyMoneyAccount c("Unknown ID", a);
+ m->m_dirty = false;
+ try {
+ m->modifyAccount(c);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ // use different account type
+ MyMoneyAccount d;
+ d.setAccountType(MyMoneyAccount::CreditCard);
+ MyMoneyAccount f("A000001", d);
+ try {
+ m->modifyAccount(f);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ // use different parent
+ a.setParentAccountId("A000002");
+ try {
+ m->modifyAccount(c);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testModifyInstitution() {
+ testAddInstitution();
+ MyMoneyInstitution i = m->institution("I000001");
+
+ m->m_dirty = false;
+ i.setName("New inst name");
+ try {
+ m->modifyInstitution(i);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ i = m->institution("I000001");
+ CPPUNIT_ASSERT(i.name() == "New inst name");
+
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ // try to modify an institution that does not exist
+ MyMoneyInstitution f("Unknown ID", i);
+ try {
+ m->modifyInstitution(f);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testReparentAccount() {
+ // this one adds some accounts to the database
+ MyMoneyAccount ex1;
+ ex1.setAccountType(MyMoneyAccount::Expense);
+ MyMoneyAccount ex2;
+ ex2.setAccountType(MyMoneyAccount::Expense);
+ MyMoneyAccount ex3;
+ ex3.setAccountType(MyMoneyAccount::Expense);
+ MyMoneyAccount ex4;
+ ex4.setAccountType(MyMoneyAccount::Expense);
+ MyMoneyAccount in;
+ in.setAccountType(MyMoneyAccount::Income);
+ MyMoneyAccount ch;
+ ch.setAccountType(MyMoneyAccount::Checkings);
+
+ ex1.setName("Sales Tax");
+ ex2.setName("Sales Tax 16%");
+ ex3.setName("Sales Tax 7%");
+ ex4.setName("Grosseries");
+
+ in.setName("Salary");
+ ch.setName("My checkings account");
+
+ try {
+ m->addAccount(ex1);
+ m->addAccount(ex2);
+ m->addAccount(ex3);
+ m->addAccount(ex4);
+ m->addAccount(in);
+ m->addAccount(ch);
+
+ CPPUNIT_ASSERT(ex1.id() == "A000001");
+ CPPUNIT_ASSERT(ex2.id() == "A000002");
+ CPPUNIT_ASSERT(ex3.id() == "A000003");
+ CPPUNIT_ASSERT(ex4.id() == "A000004");
+ CPPUNIT_ASSERT(in.id() == "A000005");
+ CPPUNIT_ASSERT(ch.id() == "A000006");
+
+ MyMoneyAccount parent = m->expense();
+
+ m->addAccount(parent, ex1);
+ m->addAccount(ex1, ex2);
+ m->addAccount(parent, ex3);
+ m->addAccount(parent, ex4);
+
+ parent = m->income();
+ m->addAccount(parent, in);
+
+ parent = m->asset();
+ m->addAccount(parent, ch);
+
+ CPPUNIT_ASSERT(m->expense().accountCount() == 3);
+ CPPUNIT_ASSERT(m->account(ex1.id()).accountCount() == 1);
+ CPPUNIT_ASSERT(ex3.parentAccountId() == STD_ACC_EXPENSE);
+
+ m->reparentAccount(ex3, ex1);
+ CPPUNIT_ASSERT(m->expense().accountCount() == 2);
+ CPPUNIT_ASSERT(m->account(ex1.id()).accountCount() == 2);
+ CPPUNIT_ASSERT(ex3.parentAccountId() == ex1.id());
+ } catch (MyMoneyException *e) {
+ std::cout << std::endl << e->what() << std::endl;
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testAddTransactions() {
+ testReparentAccount();
+
+ MyMoneyAccount ch;
+ MyMoneyTransaction t1, t2;
+ MyMoneySplit s;
+
+ try {
+ // I made some money, great
+ s.setAccountId("A000006"); // Checkings
+ s.setShares(100000);
+ s.setValue(100000);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t1.addSplit(s);
+
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000005"); // Salary
+ s.setShares(-100000);
+ s.setValue(-100000);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t1.addSplit(s);
+
+ t1.setPostDate(QDate(2002,5,10));
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+
+ m->m_dirty = false;
+ try {
+ m->addTransaction(t1);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ CPPUNIT_ASSERT(t1.id() == "T000000000000000001");
+ CPPUNIT_ASSERT(t1.splitCount() == 2);
+ CPPUNIT_ASSERT(m->transactionCount() == 1);
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+
+ try {
+ // I spent some money, not so great
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000004"); // Grosseries
+ s.setShares(10000);
+ s.setValue(10000);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t2.addSplit(s);
+
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000002"); // 16% sales tax
+ s.setShares(1200);
+ s.setValue(1200);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t2.addSplit(s);
+
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000003"); // 7% sales tax
+ s.setShares(400);
+ s.setValue(400);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t2.addSplit(s);
+
+ s.setId(QString()); // enable re-usage of split variable
+ s.setAccountId("A000006"); // Checkings account
+ s.setShares(-11600);
+ s.setValue(-11600);
+ CPPUNIT_ASSERT(s.id().isEmpty());
+ t2.addSplit(s);
+
+ t2.setPostDate(QDate(2002,5,9));
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+ m->m_dirty = false;
+ try {
+ m->addTransaction(t2);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ CPPUNIT_ASSERT(t2.id() == "T000000000000000002");
+ CPPUNIT_ASSERT(t2.splitCount() == 4);
+ CPPUNIT_ASSERT(m->transactionCount() == 2);
+
+ QMap<QString, QString>::ConstIterator it_k;
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+ it_k = m->m_transactionKeys.begin();
+ it_t = m->m_transactionList.begin();
+
+ CPPUNIT_ASSERT((*it_k) == "2002-05-10-T000000000000000001");
+ CPPUNIT_ASSERT((*it_t).id() == "T000000000000000002");
+ ++it_k;
+ ++it_t;
+ CPPUNIT_ASSERT((*it_k) == "2002-05-09-T000000000000000002");
+ CPPUNIT_ASSERT((*it_t).id() == "T000000000000000001");
+ ++it_k;
+ ++it_t;
+ CPPUNIT_ASSERT(it_k == m->m_transactionKeys.end());
+ CPPUNIT_ASSERT(it_t == m->m_transactionList.end());
+
+ ch = m->account("A000006");
+
+ // check that the account's transaction list is updated
+ QValueList<MyMoneyTransaction> list;
+ MyMoneyTransactionFilter filter("A000006");
+ list = m->transactionList(filter);
+ CPPUNIT_ASSERT(list.size() == 2);
+
+ QValueList<MyMoneyTransaction>::ConstIterator it;
+ it = list.begin();
+ CPPUNIT_ASSERT((*it).id() == "T000000000000000002");
+ ++it;
+ CPPUNIT_ASSERT((*it).id() == "T000000000000000001");
+ ++it;
+ CPPUNIT_ASSERT(it == list.end());
+
+/* removed with MyMoneyAccount::Transaction
+ CPPUNIT_ASSERT(ch.transactionCount() == 2);
+
+ QValueList<MyMoneyAccount::Transaction>::ConstIterator it_l;
+ it_l = ch.transactionList().begin();
+ CPPUNIT_ASSERT((*it_l).transactionID() == "T000000000000000002");
+ CPPUNIT_ASSERT((*it_l).balance() == -11600);
+ ++it_l;
+
+ CPPUNIT_ASSERT((*it_l).transactionID() == "T000000000000000001");
+ CPPUNIT_ASSERT((*it_l).balance() == -11600+100000);
+
+ ++it_l;
+ CPPUNIT_ASSERT(it_l == ch.transactionList().end());
+*/
+
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testTransactionCount() {
+ testAddTransactions();
+ CPPUNIT_ASSERT(m->transactionCount("A000001") == 0);
+ CPPUNIT_ASSERT(m->transactionCount("A000002") == 1);
+ CPPUNIT_ASSERT(m->transactionCount("A000003") == 1);
+ CPPUNIT_ASSERT(m->transactionCount("A000004") == 1);
+ CPPUNIT_ASSERT(m->transactionCount("A000005") == 1);
+ CPPUNIT_ASSERT(m->transactionCount("A000006") == 2);
+}
+
+void MyMoneySeqAccessMgrTest::testBalance() {
+ testAddTransactions();
+
+ CPPUNIT_ASSERT(m->balance("A000001").isZero());
+ CPPUNIT_ASSERT(m->balance("A000002") == MyMoneyMoney(1200));
+ CPPUNIT_ASSERT(m->balance("A000003") == MyMoneyMoney(400));
+ CPPUNIT_ASSERT(m->totalBalance("A000001") == MyMoneyMoney(1600));
+ CPPUNIT_ASSERT(m->balance("A000006", QDate(2002,5,9)) == MyMoneyMoney(-11600));
+ CPPUNIT_ASSERT(m->balance("A000005", QDate(2002,5,10)) == MyMoneyMoney(-100000));
+ CPPUNIT_ASSERT(m->balance("A000006", QDate(2002,5,10)) == MyMoneyMoney(88400));
+}
+
+void MyMoneySeqAccessMgrTest::testModifyTransaction() {
+ testAddTransactions();
+
+ MyMoneyTransaction t = m->transaction("T000000000000000002");
+ MyMoneySplit s;
+ MyMoneyAccount ch;
+
+ // just modify simple stuff (splits)
+ CPPUNIT_ASSERT(t.splitCount() == 4);
+
+ s = t.splits()[0];
+ s.setShares(11000);
+ s.setValue(11000);
+ t.modifySplit(s);
+
+ CPPUNIT_ASSERT(t.splitCount() == 4);
+ s = t.splits()[3];
+ s.setShares(-12600);
+ s.setValue(-12600);
+ t.modifySplit(s);
+
+ try {
+ CPPUNIT_ASSERT(m->balance("A000004") == MyMoneyMoney(10000));
+ CPPUNIT_ASSERT(m->balance("A000006") == MyMoneyMoney(100000-11600));
+ CPPUNIT_ASSERT(m->totalBalance("A000001") == MyMoneyMoney(1600));
+ m->modifyTransaction(t);
+ CPPUNIT_ASSERT(m->balance("A000004") == MyMoneyMoney(11000));
+ CPPUNIT_ASSERT(m->balance("A000006") == MyMoneyMoney(100000-12600));
+ CPPUNIT_ASSERT(m->totalBalance("A000001") == MyMoneyMoney(1600));
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ // now modify the date
+ t.setPostDate(QDate(2002,5,11));
+ try {
+ m->modifyTransaction(t);
+ CPPUNIT_ASSERT(m->balance("A000004") == MyMoneyMoney(11000));
+ CPPUNIT_ASSERT(m->balance("A000006") == MyMoneyMoney(100000-12600));
+ CPPUNIT_ASSERT(m->totalBalance("A000001") == MyMoneyMoney(1600));
+
+ QMap<QString, QString>::ConstIterator it_k;
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+ it_k = m->m_transactionKeys.begin();
+ it_t = m->m_transactionList.begin();
+ CPPUNIT_ASSERT((*it_k) == "2002-05-10-T000000000000000001");
+ CPPUNIT_ASSERT((*it_t).id() == "T000000000000000001");
+ ++it_k;
+ ++it_t;
+ CPPUNIT_ASSERT((*it_k) == "2002-05-11-T000000000000000002");
+ CPPUNIT_ASSERT((*it_t).id() == "T000000000000000002");
+ ++it_k;
+ ++it_t;
+ CPPUNIT_ASSERT(it_k == m->m_transactionKeys.end());
+ CPPUNIT_ASSERT(it_t == m->m_transactionList.end());
+
+ ch = m->account("A000006");
+
+ // check that the account's transaction list is updated
+ QValueList<MyMoneyTransaction> list;
+ MyMoneyTransactionFilter filter("A000006");
+ list = m->transactionList(filter);
+ CPPUNIT_ASSERT(list.size() == 2);
+
+ QValueList<MyMoneyTransaction>::ConstIterator it;
+ it = list.begin();
+ CPPUNIT_ASSERT((*it).id() == "T000000000000000001");
+ ++it;
+ CPPUNIT_ASSERT((*it).id() == "T000000000000000002");
+ ++it;
+ CPPUNIT_ASSERT(it == list.end());
+
+/* removed with MyMoneyAccount::Transaction
+ // CPPUNIT_ASSERT(ch.transactionCount() == 2);
+
+ QValueList<MyMoneyAccount::Transaction>::ConstIterator it_l;
+ it_l = ch.transactionList().begin();
+ CPPUNIT_ASSERT((*it_l).transactionID() == "T000000000000000001");
+ CPPUNIT_ASSERT((*it_l).balance() == 100000);
+ ++it_l;
+
+ CPPUNIT_ASSERT((*it_l).transactionID() == "T000000000000000002");
+ CPPUNIT_ASSERT((*it_l).balance() == -12600+100000);
+
+ ++it_l;
+ CPPUNIT_ASSERT(it_l == ch.transactionList().end());
+*/
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+
+void MyMoneySeqAccessMgrTest::testRemoveUnusedAccount() {
+ testAccount2Institution();
+
+ MyMoneyAccount a = m->account("A000001");
+ MyMoneyInstitution i = m->institution("I000001");
+
+ m->m_dirty = false;
+ // make sure, we cannot remove the standard account groups
+ try {
+ m->removeAccount(m->liability());
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ try {
+ m->removeAccount(m->asset());
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ try {
+ m->removeAccount(m->expense());
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ try {
+ m->removeAccount(m->income());
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ // try to remove the account still attached to the institution
+ try {
+ m->removeAccount(a);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ // now really remove an account
+
+ try {
+ CPPUNIT_ASSERT(i.accountCount() == 0);
+ CPPUNIT_ASSERT(m->accountCount() == 7);
+
+ a.setInstitutionId(QString());
+ m->modifyAccount(a);
+ m->removeAccount(a);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->accountCount() == 6);
+ CPPUNIT_ASSERT(m->dirty() == true);
+ i = m->institution("I000001");
+ CPPUNIT_ASSERT(i.accountCount() == 0);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testRemoveUsedAccount() {
+ testAddTransactions();
+
+ MyMoneyAccount a = m->account("A000006");
+
+ try {
+ m->removeAccount(a);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testRemoveInstitution() {
+ testModifyInstitution();
+ testReparentAccount();
+
+ MyMoneyInstitution i;
+ MyMoneyAccount a;
+
+ // assign the checkings account to the institution
+ try {
+ i = m->institution("I000001");
+ a = m->account("A000006");
+ a.setInstitutionId(i.id());
+ m->modifyAccount(a);
+ CPPUNIT_ASSERT(i.accountCount() == 0);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->m_dirty = false;
+ // now remove the institution and see if the account survived ;-)
+ try {
+ m->removeInstitution(i);
+ a.setInstitutionId(QString());
+ m->modifyAccount(a);
+ m->commitTransaction();
+ m->startTransaction();
+ a = m->account("A000006");
+ CPPUNIT_ASSERT(m->dirty() == true);
+ CPPUNIT_ASSERT(a.institutionId().isEmpty());
+ CPPUNIT_ASSERT(m->institutionCount() == 0);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testRemoveTransaction() {
+ testAddTransactions();
+
+ MyMoneyTransaction t = m->transaction("T000000000000000002");
+
+ m->m_dirty = false;
+ try {
+ m->removeTransaction(t);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ CPPUNIT_ASSERT(m->transactionCount() == 1);
+/* removed with MyMoneyAccount::Transaction
+ CPPUNIT_ASSERT(m->account("A000006").transactionCount() == 1);
+*/
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testTransactionList() {
+ testAddTransactions();
+
+ QValueList<MyMoneyTransaction> list;
+ MyMoneyTransactionFilter filter("A000006");
+ list = m->transactionList(filter);
+ CPPUNIT_ASSERT(list.count() == 2);
+ CPPUNIT_ASSERT((*(list.at(0))).id() == "T000000000000000002");
+ CPPUNIT_ASSERT((*(list.at(1))).id() == "T000000000000000001");
+
+ filter.clear();
+ filter.addAccount(QString("A000003"));
+ list = m->transactionList(filter);
+ CPPUNIT_ASSERT(list.count() == 1);
+ CPPUNIT_ASSERT((*(list.at(0))).id() == "T000000000000000002");
+
+ filter.clear();
+ list = m->transactionList(filter);
+ CPPUNIT_ASSERT(list.count() == 2);
+ CPPUNIT_ASSERT((*(list.at(0))).id() == "T000000000000000002");
+ CPPUNIT_ASSERT((*(list.at(1))).id() == "T000000000000000001");
+}
+
+void MyMoneySeqAccessMgrTest::testAddPayee() {
+ MyMoneyPayee p;
+
+ p.setName("THB");
+ m->m_dirty = false;
+ try {
+ CPPUNIT_ASSERT(m->m_nextPayeeID == 0);
+ m->addPayee(p);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == true);
+ CPPUNIT_ASSERT(m->m_nextPayeeID == 1);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+}
+
+void MyMoneySeqAccessMgrTest::testSetAccountName() {
+ try {
+ m->setAccountName(STD_ACC_LIABILITY, "Verbindlichkeiten");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+ try {
+ m->setAccountName(STD_ACC_ASSET, "Verm�gen");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+ try {
+ m->setAccountName(STD_ACC_EXPENSE, "Ausgaben");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+ try {
+ m->setAccountName(STD_ACC_INCOME, "Einnahmen");
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ CPPUNIT_ASSERT(m->liability().name() == "Verbindlichkeiten");
+ CPPUNIT_ASSERT(m->asset().name() == "Verm�gen");
+ CPPUNIT_ASSERT(m->expense().name() == "Ausgaben");
+ CPPUNIT_ASSERT(m->income().name() == "Einnahmen");
+
+ try {
+ m->setAccountName("A000001", "New account name");
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testModifyPayee() {
+ MyMoneyPayee p;
+
+ testAddPayee();
+
+ p = m->payee("P000001");
+ p.setName("New name");
+ m->m_dirty = false;
+ try {
+ m->modifyPayee(p);
+ m->commitTransaction();
+ m->startTransaction();
+ p = m->payee("P000001");
+ CPPUNIT_ASSERT(p.name() == "New name");
+ CPPUNIT_ASSERT(m->dirty() == true);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testRemovePayee() {
+ testAddPayee();
+ m->m_dirty = false;
+
+ // check that we can remove an unreferenced payee
+ MyMoneyPayee p = m->payee("P000001");
+ try {
+ CPPUNIT_ASSERT(m->m_payeeList.count() == 1);
+ m->removePayee(p);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->m_payeeList.count() == 0);
+ CPPUNIT_ASSERT(m->dirty() == true);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ // add transaction
+ testAddTransactions();
+
+ MyMoneyTransaction tr = m->transaction("T000000000000000001");
+ MyMoneySplit sp;
+ sp = tr.splits()[0];
+ sp.setPayeeId("P000001");
+ tr.modifySplit(sp);
+
+ // check that we cannot add a transaction referencing
+ // an unknown payee
+ try {
+ m->modifyTransaction(tr);
+ CPPUNIT_FAIL("Expected exception");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+
+ m->m_nextPayeeID = 0; // reset here, so that the
+ // testAddPayee will not fail
+ testAddPayee();
+
+ // check that it works when the payee exists
+ try {
+ m->modifyTransaction(tr);
+ } catch (MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->m_dirty = false;
+
+ // now check, that we cannot remove the payee
+ try {
+ m->removePayee(p);
+ CPPUNIT_FAIL("Expected exception");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+ CPPUNIT_ASSERT(m->m_payeeList.count() == 1);
+}
+
+
+void MyMoneySeqAccessMgrTest::testRemoveAccountFromTree() {
+ MyMoneyAccount a, b, c;
+ a.setName("Acc A");
+ b.setName("Acc B");
+ c.setName("Acc C");
+
+ // build a tree A -> B -> C, remove B and see if A -> C
+ // remains in the storag manager
+
+ try {
+ m->addAccount(a);
+ m->addAccount(b);
+ m->addAccount(c);
+ m->reparentAccount(b, a);
+ m->reparentAccount(c, b);
+
+ CPPUNIT_ASSERT(a.accountList().count() == 1);
+ CPPUNIT_ASSERT(m->account(a.accountList()[0]).name() == "Acc B");
+
+ CPPUNIT_ASSERT(b.accountList().count() == 1);
+ CPPUNIT_ASSERT(m->account(b.accountList()[0]).name() == "Acc C");
+
+ CPPUNIT_ASSERT(c.accountList().count() == 0);
+
+ m->removeAccount(b);
+
+ // reload account info from titutionIDtorage
+ a = m->account(a.id());
+ c = m->account(c.id());
+
+ try {
+ b = m->account(b.id());
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+ CPPUNIT_ASSERT(a.accountList().count() == 1);
+ CPPUNIT_ASSERT(m->account(a.accountList()[0]).name() == "Acc C");
+
+ CPPUNIT_ASSERT(c.accountList().count() == 0);
+
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testPayeeName() {
+ testAddPayee();
+
+ MyMoneyPayee p;
+ QString name("THB");
+
+ // OK case
+ try {
+ p = m->payeeByName(name);
+ CPPUNIT_ASSERT(p.name() == "THB");
+ CPPUNIT_ASSERT(p.id() == "P000001");
+ } catch (MyMoneyException *e) {
+ unexpectedException(e);
+ }
+
+ // Not OK case
+ name = "Thb";
+ try {
+ p = m->payeeByName(name);
+ CPPUNIT_FAIL("Exception expected");
+ } catch (MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testAssignment() {
+ testAddTransactions();
+
+ MyMoneyPayee user;
+ user.setName("Thomas");
+ m->setUser(user);
+
+ MyMoneySeqAccessMgr test = *m;
+ testEquality(&test);
+}
+
+void MyMoneySeqAccessMgrTest::testEquality(const MyMoneySeqAccessMgr *t)
+{
+ CPPUNIT_ASSERT(m->user().name() == t->user().name());
+ CPPUNIT_ASSERT(m->user().address() == t->user().address());
+ CPPUNIT_ASSERT(m->user().city() == t->user().city());
+ CPPUNIT_ASSERT(m->user().state() == t->user().state());
+ CPPUNIT_ASSERT(m->user().postcode() == t->user().postcode());
+ CPPUNIT_ASSERT(m->user().telephone() == t->user().telephone());
+ CPPUNIT_ASSERT(m->user().email() == t->user().email());
+ CPPUNIT_ASSERT(m->m_nextInstitutionID == t->m_nextInstitutionID);
+ CPPUNIT_ASSERT(m->m_nextAccountID == t->m_nextAccountID);
+ CPPUNIT_ASSERT(m->m_nextTransactionID == t->m_nextTransactionID);
+ CPPUNIT_ASSERT(m->m_nextPayeeID == t->m_nextPayeeID);
+ CPPUNIT_ASSERT(m->m_nextScheduleID == t->m_nextScheduleID);
+ CPPUNIT_ASSERT(m->m_dirty == t->m_dirty);
+ CPPUNIT_ASSERT(m->m_creationDate == t->m_creationDate);
+ CPPUNIT_ASSERT(m->m_lastModificationDate == t->m_lastModificationDate);
+
+ /*
+ * make sure, that the keys and values are the same
+ * on the left and the right side
+ */
+ CPPUNIT_ASSERT(m->m_payeeList.keys() == t->m_payeeList.keys());
+ CPPUNIT_ASSERT(m->m_payeeList.values() == t->m_payeeList.values());
+ CPPUNIT_ASSERT(m->m_transactionKeys.keys() == t->m_transactionKeys.keys());
+ CPPUNIT_ASSERT(m->m_transactionKeys.values() == t->m_transactionKeys.values());
+ CPPUNIT_ASSERT(m->m_institutionList.keys() == t->m_institutionList.keys());
+ CPPUNIT_ASSERT(m->m_institutionList.values() == t->m_institutionList.values());
+ CPPUNIT_ASSERT(m->m_accountList.keys() == t->m_accountList.keys());
+ CPPUNIT_ASSERT(m->m_accountList.values() == t->m_accountList.values());
+ CPPUNIT_ASSERT(m->m_transactionList.keys() == t->m_transactionList.keys());
+ CPPUNIT_ASSERT(m->m_transactionList.values() == t->m_transactionList.values());
+ CPPUNIT_ASSERT(m->m_balanceCache.keys() == t->m_balanceCache.keys());
+ CPPUNIT_ASSERT(m->m_balanceCache.values() == t->m_balanceCache.values());
+
+// CPPUNIT_ASSERT(m->m_scheduleList.keys() == t->m_scheduleList.keys());
+// CPPUNIT_ASSERT(m->m_scheduleList.values() == t->m_scheduleList.values());
+}
+
+void MyMoneySeqAccessMgrTest::testDuplicate() {
+ const MyMoneySeqAccessMgr* t;
+
+ testModifyTransaction();
+
+ t = m->duplicate();
+ testEquality(t);
+ delete t;
+}
+
+void MyMoneySeqAccessMgrTest::testAddSchedule() {
+ /* Note addSchedule() now calls validate as it should
+ * so we need an account id. Later this will
+ * be checked to make sure its a valid account id. The
+ * tests currently fail because no splits are defined
+ * for the schedules transaction.
+ */
+
+
+ try {
+ CPPUNIT_ASSERT(m->m_scheduleList.count() == 0);
+ MyMoneyTransaction t1;
+ MyMoneySplit s1, s2;
+ s1.setAccountId("A000001");
+ t1.addSplit(s1);
+ s2.setAccountId("A000002");
+ t1.addSplit(s2);
+ MyMoneySchedule schedule("Sched-Name",
+ MyMoneySchedule::TYPE_DEPOSIT,
+ MyMoneySchedule::OCCUR_DAILY, 1,
+ MyMoneySchedule::STYPE_MANUALDEPOSIT,
+ QDate(),
+ QDate(),
+ true,
+ false);
+ t1.setPostDate(QDate(2003,7,10));
+ schedule.setTransaction(t1);
+
+ m->addSchedule(schedule);
+
+ CPPUNIT_ASSERT(m->m_scheduleList.count() == 1);
+ CPPUNIT_ASSERT(schedule.id() == "SCH000001");
+ CPPUNIT_ASSERT(m->m_scheduleList["SCH000001"].id() == "SCH000001");
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ try {
+ MyMoneySchedule schedule("Sched-Name",
+ MyMoneySchedule::TYPE_DEPOSIT,
+ MyMoneySchedule::OCCUR_DAILY, 1,
+ MyMoneySchedule::STYPE_MANUALDEPOSIT,
+ QDate(),
+ QDate(),
+ true,
+ false);
+ m->addSchedule(schedule);
+ CPPUNIT_FAIL("Exception expected");
+ } catch(MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testSchedule() {
+ testAddSchedule();
+ MyMoneySchedule sched;
+
+ sched = m->schedule("SCH000001");
+ CPPUNIT_ASSERT(sched.name() == "Sched-Name");
+ CPPUNIT_ASSERT(sched.id() == "SCH000001");
+
+ try {
+ m->schedule("SCH000002");
+ CPPUNIT_FAIL("Exception expected");
+ } catch(MyMoneyException *e) {
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testModifySchedule() {
+ testAddSchedule();
+ MyMoneySchedule sched;
+
+ sched = m->schedule("SCH000001");
+ sched.setId("SCH000002");
+ try {
+ m->modifySchedule(sched);
+ CPPUNIT_FAIL("Exception expected");
+ } catch(MyMoneyException *e) {
+ delete e;
+ }
+
+ sched = m->schedule("SCH000001");
+ sched.setName("New Sched-Name");
+ try {
+ m->modifySchedule(sched);
+ CPPUNIT_ASSERT(m->m_scheduleList.count() == 1);
+ CPPUNIT_ASSERT(m->m_scheduleList["SCH000001"].name() == "New Sched-Name");
+
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+}
+
+void MyMoneySeqAccessMgrTest::testRemoveSchedule() {
+ testAddSchedule();
+ m->commitTransaction();
+ m->startTransaction();
+ MyMoneySchedule sched;
+
+ sched = m->schedule("SCH000001");
+ sched.setId("SCH000002");
+ try {
+ m->removeSchedule(sched);
+ m->commitTransaction();
+ CPPUNIT_FAIL("Exception expected");
+ } catch(MyMoneyException *e) {
+ m->rollbackTransaction();
+ delete e;
+ }
+ m->startTransaction();
+
+ sched = m->schedule("SCH000001");
+ try {
+ m->removeSchedule(sched);
+ m->commitTransaction();
+ CPPUNIT_ASSERT(m->m_scheduleList.count() == 0);
+
+ } catch(MyMoneyException *e) {
+ m->rollbackTransaction();
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+ m->startTransaction();
+}
+
+void MyMoneySeqAccessMgrTest::testScheduleList() {
+ QDate testDate = QDate::currentDate();
+ QDate notOverdue = testDate.addDays(2);
+ QDate overdue = testDate.addDays(-2);
+
+
+ MyMoneyTransaction t1;
+ MyMoneySplit s1, s2;
+ s1.setAccountId("A000001");
+ t1.addSplit(s1);
+ s2.setAccountId("A000002");
+ t1.addSplit(s2);
+ MyMoneySchedule schedule1("Schedule 1",
+ MyMoneySchedule::TYPE_BILL,
+ MyMoneySchedule::OCCUR_ONCE, 1,
+ MyMoneySchedule::STYPE_DIRECTDEBIT,
+ QDate(),
+ QDate(),
+ false,
+ false);
+ t1.setPostDate(notOverdue);
+ schedule1.setTransaction(t1);
+ schedule1.setLastPayment(notOverdue);
+
+ MyMoneyTransaction t2;
+ MyMoneySplit s3, s4;
+ s3.setAccountId("A000001");
+ t2.addSplit(s3);
+ s4.setAccountId("A000003");
+ t2.addSplit(s4);
+ MyMoneySchedule schedule2("Schedule 2",
+ MyMoneySchedule::TYPE_DEPOSIT,
+ MyMoneySchedule::OCCUR_DAILY, 1,
+ MyMoneySchedule::STYPE_DIRECTDEPOSIT,
+ QDate(),
+ QDate(),
+ false,
+ false);
+ t2.setPostDate(notOverdue.addDays(1));
+ schedule2.setTransaction(t2);
+ schedule2.setLastPayment(notOverdue.addDays(1));
+
+ MyMoneyTransaction t3;
+ MyMoneySplit s5, s6;
+ s5.setAccountId("A000005");
+ t3.addSplit(s5);
+ s6.setAccountId("A000006");
+ t3.addSplit(s6);
+ MyMoneySchedule schedule3("Schedule 3",
+ MyMoneySchedule::TYPE_TRANSFER,
+ MyMoneySchedule::OCCUR_WEEKLY, 1,
+ MyMoneySchedule::STYPE_OTHER,
+ QDate(),
+ QDate(),
+ false,
+ false);
+ t3.setPostDate(notOverdue.addDays(2));
+ schedule3.setTransaction(t3);
+ schedule3.setLastPayment(notOverdue.addDays(2));
+
+ MyMoneyTransaction t4;
+ MyMoneySplit s7, s8;
+ s7.setAccountId("A000005");
+ t4.addSplit(s7);
+ s8.setAccountId("A000006");
+ t4.addSplit(s8);
+ MyMoneySchedule schedule4("Schedule 4",
+ MyMoneySchedule::TYPE_BILL,
+ MyMoneySchedule::OCCUR_WEEKLY, 1,
+ MyMoneySchedule::STYPE_WRITECHEQUE,
+ QDate(),
+ notOverdue.addDays(31),
+ false,
+ false);
+ t4.setPostDate(overdue.addDays(-7));
+ schedule4.setTransaction(t4);
+
+ try {
+ m->addSchedule(schedule1);
+ m->addSchedule(schedule2);
+ m->addSchedule(schedule3);
+ m->addSchedule(schedule4);
+ } catch(MyMoneyException *e) {
+ qDebug("Error: %s", e->what().latin1());
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ QValueList<MyMoneySchedule> list;
+
+ // no filter
+ list = m->scheduleList();
+ CPPUNIT_ASSERT(list.count() == 4);
+
+ // filter by type
+ list = m->scheduleList("", MyMoneySchedule::TYPE_BILL);
+ CPPUNIT_ASSERT(list.count() == 2);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 1");
+ CPPUNIT_ASSERT(list[1].name() == "Schedule 4");
+
+ // filter by occurence
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_DAILY);
+ CPPUNIT_ASSERT(list.count() == 1);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 2");
+
+ // filter by payment type
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_ANY,
+ MyMoneySchedule::STYPE_DIRECTDEPOSIT);
+ CPPUNIT_ASSERT(list.count() == 1);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 2");
+
+ // filter by account
+ list = m->scheduleList("A01");
+ CPPUNIT_ASSERT(list.count() == 0);
+ list = m->scheduleList("A000001");
+ CPPUNIT_ASSERT(list.count() == 2);
+ list = m->scheduleList("A000002");
+ CPPUNIT_ASSERT(list.count() == 1);
+
+ // filter by start date
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_ANY,
+ MyMoneySchedule::STYPE_ANY,
+ notOverdue.addDays(31));
+ CPPUNIT_ASSERT(list.count() == 3);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 2");
+ CPPUNIT_ASSERT(list[1].name() == "Schedule 3");
+ CPPUNIT_ASSERT(list[2].name() == "Schedule 4");
+
+ // filter by end date
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_ANY,
+ MyMoneySchedule::STYPE_ANY,
+ QDate(),
+ notOverdue.addDays(1));
+ CPPUNIT_ASSERT(list.count() == 3);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 1");
+ CPPUNIT_ASSERT(list[1].name() == "Schedule 2");
+ CPPUNIT_ASSERT(list[2].name() == "Schedule 4");
+
+ // filter by start and end date
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_ANY,
+ MyMoneySchedule::STYPE_ANY,
+ notOverdue.addDays(-1),
+ notOverdue.addDays(1));
+ CPPUNIT_ASSERT(list.count() == 2);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 1");
+ CPPUNIT_ASSERT(list[1].name() == "Schedule 2");
+
+ // filter by overdue status
+ list = m->scheduleList("", MyMoneySchedule::TYPE_ANY,
+ MyMoneySchedule::OCCUR_ANY,
+ MyMoneySchedule::STYPE_ANY,
+ QDate(),
+ QDate(),
+ true);
+ CPPUNIT_ASSERT(list.count() == 1);
+ CPPUNIT_ASSERT(list[0].name() == "Schedule 4");
+}
+
+void MyMoneySeqAccessMgrTest::testAddCurrency()
+{
+ MyMoneySecurity curr("EUR", "Euro", "?", 100, 100);
+ CPPUNIT_ASSERT(m->m_currencyList.count() == 0);
+ m->m_dirty = false;
+ try {
+ m->addCurrency(curr);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->m_currencyList.count() == 1);
+ CPPUNIT_ASSERT(m->m_currencyList["EUR"].name() == "Euro");
+ CPPUNIT_ASSERT(m->dirty() == true);
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->m_dirty = false;
+ try {
+ m->addCurrency(curr);
+ CPPUNIT_FAIL("Expected exception missing");
+ } catch(MyMoneyException *e) {
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == false);
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testModifyCurrency()
+{
+ MyMoneySecurity curr("EUR", "Euro", "?", 100, 100);
+ testAddCurrency();
+ m->m_dirty = false;
+ curr.setName("EURO");
+ try {
+ m->modifyCurrency(curr);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->m_currencyList.count() == 1);
+ CPPUNIT_ASSERT(m->m_currencyList["EUR"].name() == "EURO");
+ CPPUNIT_ASSERT(m->dirty() == true);
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->m_dirty = false;
+
+ MyMoneySecurity unknownCurr("DEM", "Deutsche Mark", "DM", 100, 100);
+ try {
+ m->modifyCurrency(unknownCurr);
+ CPPUNIT_FAIL("Expected exception missing");
+ } catch(MyMoneyException *e) {
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == false);
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testRemoveCurrency()
+{
+ MyMoneySecurity curr("EUR", "Euro", "?", 100, 100);
+ testAddCurrency();
+ m->m_dirty = false;
+ try {
+ m->removeCurrency(curr);
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->m_currencyList.count() == 0);
+ CPPUNIT_ASSERT(m->dirty() == true);
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ m->m_dirty = false;
+
+ MyMoneySecurity unknownCurr("DEM", "Deutsche Mark", "DM", 100, 100);
+ try {
+ m->removeCurrency(unknownCurr);
+ CPPUNIT_FAIL("Expected exception missing");
+ } catch(MyMoneyException *e) {
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == false);
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testCurrency()
+{
+ MyMoneySecurity curr("EUR", "Euro", "?", 100, 100);
+ MyMoneySecurity newCurr;
+ testAddCurrency();
+ m->m_dirty = false;
+ try {
+ newCurr = m->currency("EUR");
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == false);
+ CPPUNIT_ASSERT(newCurr.id() == curr.id());
+ CPPUNIT_ASSERT(newCurr.name() == curr.name());
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+
+ try {
+ m->currency("DEM");
+ CPPUNIT_FAIL("Expected exception missing");
+ } catch(MyMoneyException *e) {
+ m->commitTransaction();
+ m->startTransaction();
+ CPPUNIT_ASSERT(m->dirty() == false);
+ delete e;
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testCurrencyList()
+{
+ CPPUNIT_ASSERT(m->currencyList().count() == 0);
+
+ testAddCurrency();
+ CPPUNIT_ASSERT(m->currencyList().count() == 1);
+
+ MyMoneySecurity unknownCurr("DEM", "Deutsche Mark", "DM", 100, 100);
+ try {
+ m->addCurrency(unknownCurr);
+ m->m_dirty = false;
+ CPPUNIT_ASSERT(m->m_currencyList.count() == 2);
+ CPPUNIT_ASSERT(m->currencyList().count() == 2);
+ CPPUNIT_ASSERT(m->dirty() == false);
+ } catch(MyMoneyException *e) {
+ delete e;
+ CPPUNIT_FAIL("Unexpected exception");
+ }
+}
+
+void MyMoneySeqAccessMgrTest::testAccountList()
+{
+ QValueList<MyMoneyAccount> accounts;
+ m->accountList(accounts);
+ CPPUNIT_ASSERT(accounts.count() == 0);
+ testAddNewAccount();
+ accounts.clear();
+ m->accountList(accounts);
+ CPPUNIT_ASSERT(accounts.count() == 2);
+
+ MyMoneyAccount a = m->account("A000001");
+ MyMoneyAccount b = m->account("A000002");
+ m->reparentAccount(b, a);
+ accounts.clear();
+ m->accountList(accounts);
+ CPPUNIT_ASSERT(accounts.count() == 2);
+}
+
+void MyMoneySeqAccessMgrTest::testLoaderFunctions()
+{
+ // we don't need the transaction started by setup() here
+ m->rollbackTransaction();
+
+ // account loader
+ QMap<QString, MyMoneyAccount> amap;
+ MyMoneyAccount acc("A0000176", MyMoneyAccount());
+ amap[acc.id()] = acc;
+ m->loadAccounts(amap);
+ CPPUNIT_ASSERT(m->m_accountList.values() == amap.values());
+ CPPUNIT_ASSERT(m->m_accountList.keys() == amap.keys());
+ CPPUNIT_ASSERT(m->m_nextAccountID == 176);
+
+ // transaction loader
+ QMap<QString, MyMoneyTransaction> tmap;
+ MyMoneyTransaction t("T000000108", MyMoneyTransaction());
+ tmap[t.id()] = t;
+ m->loadTransactions(tmap);
+ CPPUNIT_ASSERT(m->m_transactionList.values() == tmap.values());
+ CPPUNIT_ASSERT(m->m_transactionList.keys() == tmap.keys());
+ CPPUNIT_ASSERT(m->m_nextTransactionID == 108);
+
+ // institution loader
+ QMap<QString, MyMoneyInstitution> imap;
+ MyMoneyInstitution inst("I000028", MyMoneyInstitution());
+ imap[inst.id()] = inst;
+ m->loadInstitutions(imap);
+ CPPUNIT_ASSERT(m->m_institutionList.values() == imap.values());
+ CPPUNIT_ASSERT(m->m_institutionList.keys() == imap.keys());
+ CPPUNIT_ASSERT(m->m_nextInstitutionID == 28);
+
+ // payee loader
+ QMap<QString, MyMoneyPayee> pmap;
+ MyMoneyPayee p("P1234", MyMoneyPayee());
+ pmap[p.id()] = p;
+ m->loadPayees(pmap);
+ CPPUNIT_ASSERT(m->m_payeeList.values() == pmap.values());
+ CPPUNIT_ASSERT(m->m_payeeList.keys() == pmap.keys());
+ CPPUNIT_ASSERT(m->m_nextPayeeID == 1234);
+
+ // security loader
+ QMap<QString, MyMoneySecurity> smap;
+ MyMoneySecurity s("S54321", MyMoneySecurity());
+ smap[s.id()] = s;
+ m->loadSecurities(smap);
+ CPPUNIT_ASSERT(m->m_securitiesList.values() == smap.values());
+ CPPUNIT_ASSERT(m->m_securitiesList.keys() == smap.keys());
+ CPPUNIT_ASSERT(m->m_nextSecurityID == 54321);
+
+ // schedule loader
+ QMap<QString, MyMoneySchedule> schmap;
+ MyMoneySchedule sch("SCH6789", MyMoneySchedule());
+ schmap[sch.id()] = sch;
+ m->loadSchedules(schmap);
+ CPPUNIT_ASSERT(m->m_scheduleList.values() == schmap.values());
+ CPPUNIT_ASSERT(m->m_scheduleList.keys() == schmap.keys());
+ CPPUNIT_ASSERT(m->m_nextScheduleID == 6789);
+
+ // report loader
+ QMap<QString, MyMoneyReport> rmap;
+ MyMoneyReport r("R1298", MyMoneyReport());
+ rmap[r.id()] = r;
+ m->loadReports(rmap);
+ CPPUNIT_ASSERT(m->m_reportList.values() == rmap.values());
+ CPPUNIT_ASSERT(m->m_reportList.keys() == rmap.keys());
+ CPPUNIT_ASSERT(m->m_nextReportID == 1298);
+
+ // budget loader
+ QMap<QString, MyMoneyBudget> bmap;
+ MyMoneyBudget b("B89765", MyMoneyBudget());
+ bmap[b.id()] = b;
+ m->loadBudgets(bmap);
+ CPPUNIT_ASSERT(m->m_budgetList.values() == bmap.values());
+ CPPUNIT_ASSERT(m->m_budgetList.keys() == bmap.keys());
+ CPPUNIT_ASSERT(m->m_nextBudgetID == 89765);
+
+ // restart a transaction so that teardown() is happy
+ m->startTransaction();
+}
+
diff --git a/kmymoney2/mymoney/storage/mymoneyseqaccessmgrtest.h b/kmymoney2/mymoney/storage/mymoneyseqaccessmgrtest.h
new file mode 100644
index 0000000..b9fa763
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneyseqaccessmgrtest.h
@@ -0,0 +1,131 @@
+/***************************************************************************
+ mymoneyseqaccessmgrtest.h
+ -------------------
+ copyright : (C) 2002 by Thomas Baumgart
+ email : ipwizard@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. *
+ * *
+ ***************************************************************************/
+
+#ifndef __MYMONEYSEQACCESSMGRTEST_H__
+#define __MYMONEYSEQACCESSMGRTEST_H__
+
+#include <cppunit/TestCaller.h>
+#include <cppunit/TestCase.h>
+#include <cppunit/TestSuite.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "../autotest.h"
+
+#define private public
+#define protected public
+#include "../mymoneyobject.h"
+#include "mymoneyseqaccessmgr.h"
+#undef private
+
+class MyMoneySeqAccessMgrTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(MyMoneySeqAccessMgrTest);
+ CPPUNIT_TEST(testEmptyConstructor);
+ CPPUNIT_TEST(testSetFunctions);
+ CPPUNIT_TEST(testSupportFunctions);
+ CPPUNIT_TEST(testIsStandardAccount);
+ CPPUNIT_TEST(testNewAccount);
+ CPPUNIT_TEST(testAddNewAccount);
+ CPPUNIT_TEST(testReparentAccount);
+ CPPUNIT_TEST(testAddInstitution);
+ CPPUNIT_TEST(testInstitution);
+ CPPUNIT_TEST(testAccount2Institution);
+ CPPUNIT_TEST(testModifyAccount);
+ CPPUNIT_TEST(testModifyInstitution);
+ CPPUNIT_TEST(testAddTransactions);
+ CPPUNIT_TEST(testTransactionCount);
+ CPPUNIT_TEST(testBalance);
+ CPPUNIT_TEST(testModifyTransaction);
+ CPPUNIT_TEST(testRemoveUnusedAccount);
+ CPPUNIT_TEST(testRemoveUsedAccount);
+ CPPUNIT_TEST(testRemoveInstitution);
+ CPPUNIT_TEST(testRemoveTransaction);
+ CPPUNIT_TEST(testTransactionList);
+ CPPUNIT_TEST(testAddPayee);
+ CPPUNIT_TEST(testSetAccountName);
+ CPPUNIT_TEST(testModifyPayee);
+ CPPUNIT_TEST(testPayeeName);
+ CPPUNIT_TEST(testRemovePayee);
+ CPPUNIT_TEST(testRemoveAccountFromTree);
+ CPPUNIT_TEST(testAssignment);
+ CPPUNIT_TEST(testDuplicate);
+ CPPUNIT_TEST(testAddSchedule);
+ CPPUNIT_TEST(testModifySchedule);
+ CPPUNIT_TEST(testRemoveSchedule);
+ CPPUNIT_TEST(testSchedule);
+ CPPUNIT_TEST(testScheduleList);
+ CPPUNIT_TEST(testAddCurrency);
+ CPPUNIT_TEST(testModifyCurrency);
+ CPPUNIT_TEST(testRemoveCurrency);
+ CPPUNIT_TEST(testCurrency);
+ CPPUNIT_TEST(testCurrencyList);
+ CPPUNIT_TEST(testAccountList);
+ CPPUNIT_TEST(testLoaderFunctions);
+ CPPUNIT_TEST_SUITE_END();
+
+protected:
+ MyMoneySeqAccessMgr *m;
+public:
+ MyMoneySeqAccessMgrTest();
+
+
+ void setUp();
+ void tearDown();
+ void testEmptyConstructor();
+ void testSetFunctions();
+ void testIsStandardAccount();
+ void testNewAccount();
+ void testAccount();
+ void testAddNewAccount();
+ void testAddInstitution();
+ void testInstitution();
+ void testAccount2Institution();
+ void testModifyAccount();
+ void testModifyInstitution();
+ void testReparentAccount();
+ void testAddTransactions();
+ void testTransactionCount();
+ void testBalance();
+ void testModifyTransaction();
+ void testRemoveUnusedAccount();
+ void testRemoveUsedAccount();
+ void testRemoveInstitution();
+ void testRemoveTransaction();
+ void testTransactionList();
+ void testAddPayee();
+ void testSetAccountName();
+ void testModifyPayee();
+ void testPayeeName();
+ void testRemovePayee();
+ void testRemoveAccountFromTree();
+ void testAssignment();
+ void testEquality(const MyMoneySeqAccessMgr* t);
+ void testDuplicate();
+ void testAddSchedule();
+ void testSchedule();
+ void testModifySchedule();
+ void testRemoveSchedule();
+ void testSupportFunctions();
+ void testScheduleList();
+ void testAddCurrency();
+ void testModifyCurrency();
+ void testRemoveCurrency();
+ void testCurrency();
+ void testCurrencyList();
+ void testAccountList();
+ void testLoaderFunctions();
+};
+
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneystorageanon.cpp b/kmymoney2/mymoney/storage/mymoneystorageanon.cpp
new file mode 100644
index 0000000..31f051e
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneystorageanon.cpp
@@ -0,0 +1,294 @@
+/***************************************************************************
+ mymoneystorageanon.cpp
+ -------------------
+ begin : Thu Oct 24 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@users.sourceforge.net>
+ 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. *
+ * *
+ ***************************************************************************/
+
+#include "config.h"
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+#include <qfile.h>
+#include <qdom.h>
+#include <qmap.h>
+
+// ----------------------------------------------------------------------------
+// KDE Includes
+
+#include "kdecompat.h"
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include "mymoneystorageanon.h"
+#include "../mymoneyreport.h"
+#include "../mymoneyinstitution.h"
+
+QStringList MyMoneyStorageANON::zKvpNoModify = QStringList::split(",","kmm-baseCurrency,PreferredAccount,Tax,fixed-interest,interest-calculation,payee,schedule,term,kmm-online-source,kmm-brokerage-account,lastStatementDate,kmm-sort-reconcile,kmm-sort-std,kmm-iconpos,mm-closed,payee,schedule,term,lastImportedTransactionDate,VatAccount,VatRate,kmm-matched-tx,Imported");
+QStringList MyMoneyStorageANON::zKvpXNumber = QStringList::split(",","final-payment,loan-amount,periodic-payment,lastStatementBalance");
+
+
+MyMoneyStorageANON::MyMoneyStorageANON() :
+ MyMoneyStorageXML()
+{
+ // Choose a quasi-random 0.0-100.0 factor which will be applied to all splits this time
+ // around.
+
+ int msec;
+ do {
+ msec = QTime::currentTime().msec();
+ } while(msec == 0);
+ m_factor = MyMoneyMoney(msec, 10).reduce();
+}
+
+MyMoneyStorageANON::~MyMoneyStorageANON()
+{
+}
+
+void MyMoneyStorageANON::readFile(QIODevice* , IMyMoneySerialize* )
+{
+ throw new MYMONEYEXCEPTION("Cannot read a file through MyMoneyStorageANON!!");
+}
+
+void MyMoneyStorageANON::writeUserInformation(QDomElement& userInfo)
+{
+ MyMoneyPayee user = m_storage->user();
+
+ userInfo.setAttribute(QString("name"), hideString(user.name()));
+ userInfo.setAttribute(QString("email"), hideString(user.email()));
+
+ QDomElement address = m_doc->createElement("ADDRESS");
+ address.setAttribute(QString("street"), hideString(user.address()));
+ address.setAttribute(QString("city"), hideString(user.city()));
+ address.setAttribute(QString("county"), hideString(user.state()));
+ address.setAttribute(QString("zipcode"), hideString(user.postcode()));
+ address.setAttribute(QString("telephone"), hideString(user.telephone()));
+
+ userInfo.appendChild(address);
+}
+
+void MyMoneyStorageANON::writeInstitution(QDomElement& institution, const MyMoneyInstitution& _i)
+{
+ MyMoneyInstitution i(_i);
+
+ // mangle fields
+ i.setName(i.id());
+ i.setManager(hideString(i.manager()));
+ i.setSortcode(hideString(i.sortcode()));
+
+ i.setStreet(hideString(i.street()));
+ i.setCity(hideString(i.city()));
+ i.setPostcode(hideString(i.postcode()));
+ i.setTelephone(hideString(i.telephone()));
+
+ MyMoneyStorageXML::writeInstitution(institution, i);
+}
+
+
+void MyMoneyStorageANON::writePayee(QDomElement& payee, const MyMoneyPayee& _p)
+{
+ MyMoneyPayee p(_p);
+
+ p.setName(p.id());
+ p.setReference(hideString(p.reference()));
+
+ p.setAddress(hideString(p.address()));
+ p.setCity(hideString(p.city()));
+ p.setPostcode(hideString(p.postcode()));
+ p.setState(hideString(p.state()));
+ p.setTelephone(hideString(p.telephone()));
+ p.setNotes(hideString(p.notes()));
+ bool ignoreCase;
+ QStringList keys;
+ MyMoneyPayee::payeeMatchType matchType = p.matchData(ignoreCase, keys);
+ QRegExp exp("[A-Za-z]");
+ p.setMatchData(matchType, ignoreCase, QStringList::split(";", keys.join(";").replace(exp, "x")));
+
+ MyMoneyStorageXML::writePayee(payee, p);
+}
+
+void MyMoneyStorageANON::writeAccount(QDomElement& account, const MyMoneyAccount& _p)
+{
+ MyMoneyAccount p(_p);
+
+ p.setNumber(hideString(p.number()));
+ p.setName(p.id());
+ p.setDescription(hideString(p.description()));
+ fakeKeyValuePair(p);
+
+ // Remove the online banking settings entirely.
+ p.setOnlineBankingSettings(MyMoneyKeyValueContainer());
+
+ MyMoneyStorageXML::writeAccount(account, p);
+}
+
+void MyMoneyStorageANON::fakeTransaction(MyMoneyTransaction& tx)
+{
+ MyMoneyTransaction tn = tx;
+
+ // hide transaction data
+ tn.setMemo(tx.id());
+ tn.setBankID(hideString(tx.bankID()));
+
+ // hide split data
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ for(it_s = tx.splits().begin(); it_s != tx.splits().end(); ++it_s) {
+ MyMoneySplit s = (*it_s);
+ s.setMemo(QString("%1/%2").arg(tn.id()).arg(s.id()));
+
+ if(s.value() != MyMoneyMoney::autoCalc) {
+ s.setValue((s.value() * m_factor));
+ s.setShares((s.shares() * m_factor));
+ }
+ s.setNumber(hideString(s.number()));
+
+ // obfuscate a possibly matched transaction as well
+ if(s.isMatched()) {
+ MyMoneyTransaction t = s.matchedTransaction();
+ fakeTransaction(t);
+ s.removeMatch();
+ s.addMatch(t);
+ }
+ tn.modifySplit(s);
+ }
+ tx = tn;
+ fakeKeyValuePair(tx);
+}
+
+void MyMoneyStorageANON::fakeKeyValuePair(MyMoneyKeyValueContainer& kvp)
+{
+ QMap<QString, QString> pairs;
+ QMap<QString, QString>::const_iterator it;
+
+ for(it = kvp.pairs().begin(); it != kvp.pairs().end(); ++it)
+ {
+ if ( zKvpXNumber.contains( it.key() ) || it.key().left(3)=="ir-" )
+ pairs[it.key()] = hideNumber(MyMoneyMoney(it.data())).toString();
+ else if ( zKvpNoModify.contains( it.key() ) )
+ pairs[it.key()] = it.data();
+ else
+ pairs[it.key()] = hideString(it.data());
+ }
+ kvp.setPairs(pairs);
+}
+
+void MyMoneyStorageANON::writeTransaction(QDomElement& transactions, const MyMoneyTransaction& tx)
+{
+ MyMoneyTransaction tn = tx;
+
+ fakeTransaction(tn);
+
+ MyMoneyStorageXML::writeTransaction(transactions, tn);
+}
+
+void MyMoneyStorageANON::writeSchedule(QDomElement& scheduledTx, const MyMoneySchedule& sx)
+{
+ MyMoneySchedule sn = sx;
+ MyMoneyTransaction tn = sn.transaction();
+
+ fakeTransaction(tn);
+
+ sn.setName(sx.id());
+ sn.setTransaction(tn, true);
+
+ MyMoneyStorageXML::writeSchedule(scheduledTx, sn);
+}
+
+void MyMoneyStorageANON::writeSecurity(QDomElement& securityElement, const MyMoneySecurity& security)
+{
+ MyMoneySecurity s = security;
+ s.setName(security.id());
+ fakeKeyValuePair(s);
+
+ MyMoneyStorageXML::writeSecurity(securityElement, s);
+}
+
+QString MyMoneyStorageANON::hideString(const QString& _in) const
+{
+ return QString(_in).fill('x');
+}
+
+MyMoneyMoney MyMoneyStorageANON::hideNumber(const MyMoneyMoney& _in) const
+{
+ MyMoneyMoney result;
+ static MyMoneyMoney counter = MyMoneyMoney(100,100);
+
+ // preserve sign
+ if ( _in.isNegative() )
+ result = MyMoneyMoney(-1);
+ else
+ result = MyMoneyMoney(1);
+
+ result = result * counter;
+ counter += MyMoneyMoney("10/100");
+
+ // preserve > 1000
+ if ( _in >= MyMoneyMoney(1000) )
+ result = result * MyMoneyMoney(1000);
+ if ( _in <= MyMoneyMoney(-1000) )
+ result = result * MyMoneyMoney(1000);
+
+ return result.convert();
+}
+
+void MyMoneyStorageANON::fakeBudget(MyMoneyBudget& bx)
+{
+ MyMoneyBudget bn;
+
+ bn.setName(bx.name());
+ bn.setBudgetStart(bx.budgetStart());
+ bn = MyMoneyBudget(bx.id(), bn);
+
+ QValueList<MyMoneyBudget::AccountGroup> list = bx.getaccounts();
+ QValueList<MyMoneyBudget::AccountGroup>::iterator it;
+ for(it = list.begin(); it != list.end(); ++it) {
+ // only add the account if there is a budget entered
+ if(!(*it).balance().isZero()) {
+ MyMoneyBudget::AccountGroup account;
+ account.setId((*it).id());
+ account.setBudgetLevel((*it).budgetLevel());
+ account.setBudgetSubaccounts((*it).budgetSubaccounts());
+ QMap<QDate, MyMoneyBudget::PeriodGroup> plist = (*it).getPeriods();
+ QMap<QDate, MyMoneyBudget::PeriodGroup>::const_iterator it_p;
+ for(it_p = plist.begin(); it_p != plist.end(); ++it_p) {
+ MyMoneyBudget::PeriodGroup pGroup;
+ pGroup.setAmount((*it_p).amount() * m_factor );
+ pGroup.setStartDate( (*it_p).startDate());
+ account.addPeriod(pGroup.startDate(), pGroup);
+ }
+ bn.setAccount(account, account.id());
+ }
+ }
+
+ bx = bn;
+}
+
+void MyMoneyStorageANON::writeBudget(QDomElement& budgets, const MyMoneyBudget& b)
+{
+ MyMoneyBudget bn = b;
+
+ fakeBudget(bn);
+
+ MyMoneyStorageXML::writeBudget(budgets, bn);
+}
+
+
+// vim:cin:si:ai:et:ts=2:sw=2:
diff --git a/kmymoney2/mymoney/storage/mymoneystorageanon.h b/kmymoney2/mymoney/storage/mymoneystorageanon.h
new file mode 100644
index 0000000..4b7ab95
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneystorageanon.h
@@ -0,0 +1,113 @@
+/***************************************************************************
+ mymoneystorageanon.h
+ -------------------
+ begin : Thu Oct 24 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@users.sourceforge.net>
+ Ace Jone <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. *
+ * *
+ ***************************************************************************/
+
+#ifndef MYMONEYSTORAGEANON_H
+#define MYMONEYSTORAGEANON_H
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+// #include <qdom.h>
+// #include <qdatastream.h>
+// class QIODevice;
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+// #include "imymoneyserialize.h"
+// #include "imymoneystorageformat.h"
+#include "mymoneystoragexml.h"
+
+/**
+ * @author Kevin Tambascio (ktambascio@users.sourceforge.net)
+ */
+
+#define VERSION_0_60_XML 0x10000010 // Version 0.5 file version info
+#define VERSION_0_61_XML 0x10000011 // use 8 bytes for MyMoneyMoney objects
+
+/**
+ * This class provides storage of an anonymized version of the current
+ * file. Any object with an ID (account, transaction, etc) is renamed
+ * with that ID. Any other string value the user typed in is replaced with
+ * x's equal in length to the original string. Any numeric value is
+ * replaced with an arbitrary number which matches the sign of the original.
+ *
+ * The purpose of this class is to give users a way to send a developer
+ * their file without comprimising their financial data. If a user
+ * encounters an error, they should try saving the anonymous version of the
+ * file and see if the error is still there. If so, they should notify the
+ * list of the problem, and then when requested, send the anonymous file
+ * privately to the developer who takes the problem. I still don't think
+ * it's wise to post the file to the public list...maybe I'm just paranoid.
+ *
+ * @author Ace Jones <ace.j@hotpop.com>
+ */
+
+class MyMoneyStorageANON : public MyMoneyStorageXML
+{
+public:
+ MyMoneyStorageANON();
+ virtual ~MyMoneyStorageANON();
+
+protected:
+ void writeUserInformation(QDomElement& userInfo);
+
+ void writeInstitution(QDomElement& institutions, const MyMoneyInstitution& i);
+
+ void writePayee(QDomElement& payees, const MyMoneyPayee& p);
+
+ void writeAccount(QDomElement& accounts, const MyMoneyAccount& p);
+
+ void writeTransaction(QDomElement& transactions, const MyMoneyTransaction& tx);
+
+ void writeSchedule(QDomElement& scheduledTx, const MyMoneySchedule& tx);
+
+ void writeBudget(QDomElement& budgets, const MyMoneyBudget& b);
+
+ void readFile(QIODevice* s, IMyMoneySerialize* storage);
+
+ void writeSecurity(QDomElement& securityElement, const MyMoneySecurity& security);
+
+ QDomElement findChildElement(const QString& name, const QDomElement& root);
+
+private:
+ /**
+ * The list of key-value pairs to not modify
+ */
+ static QStringList zKvpNoModify;
+
+ /**
+ * The list of key-value pairs which are numbers to be hidden
+ */
+ static QStringList zKvpXNumber;
+
+ QString hideString(const QString&) const;
+ MyMoneyMoney hideNumber(const MyMoneyMoney&) const;
+ void fakeTransaction(MyMoneyTransaction& tn);
+ void fakeBudget(MyMoneyBudget& bn);
+ void fakeKeyValuePair(MyMoneyKeyValueContainer& _kvp);
+
+ MyMoneyMoney m_factor;
+};
+
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneystoragebin.h b/kmymoney2/mymoney/storage/mymoneystoragebin.h
new file mode 100644
index 0000000..6ef7e20
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneystoragebin.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ imymoneystoragebin.h - description
+ -------------------
+ begin : Sun May 5 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#ifndef MYMONEYSTORAGEBIN_H
+#define MYMONEYSTORAGEBIN_H
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+/**
+ *@author Thomas Baumgart
+ */
+
+ #define VERSION_0_3_3 0x00000006 // MAGIC1 for version 0.33 files
+ #define VERSION_0_4_0 0x00000007 // MAGIC1 for version 0.4 files
+
+ #define MAGIC_0_50 0x4B4D794D // "KMyM" MAGIC1 for version 0.5 files
+ #define MAGIC_0_51 0x6F6E6579 // "oney" second part of MAGIC
+
+ #define VERSION_0_50 0x00000010 // Version 0.5 file version info
+ #define VERSION_0_51 0x00000011 // use 8 bytes for MyMoneyMoney objects
+
+ // add new definitions above and make sure to adapt MAX_FILE_VERSION below
+ #define MIN_FILE_VERSION VERSION_0_50
+ #define MAX_FILE_VERSION VERSION_0_51
+
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneystoragedump.cpp b/kmymoney2/mymoney/storage/mymoneystoragedump.cpp
new file mode 100644
index 0000000..e0d0083
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneystoragedump.cpp
@@ -0,0 +1,446 @@
+/***************************************************************************
+ mymoneystoragedump.cpp - description
+ -------------------
+ begin : Sun May 5 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include "mymoneystoragedump.h"
+#include "imymoneystorage.h"
+#include "../mymoneyaccount.h"
+#include "../mymoneysecurity.h"
+#include "../mymoneyprice.h"
+
+MyMoneyStorageDump::MyMoneyStorageDump()
+{
+}
+
+MyMoneyStorageDump::~MyMoneyStorageDump()
+{
+}
+
+void MyMoneyStorageDump::readStream(QDataStream& /* s */, IMyMoneySerialize* /* storage */)
+{
+ qDebug("Reading not supported by MyMoneyStorageDump!!");
+}
+
+void MyMoneyStorageDump::writeStream(QDataStream& _s, IMyMoneySerialize* _storage)
+{
+ QTextStream s(_s.device());
+ IMyMoneyStorage* storage = dynamic_cast<IMyMoneyStorage *> (_storage);
+ MyMoneyPayee user = storage->user();
+
+ s << "File-Info\n";
+ s << "---------\n";
+ s << "user name = " << user.name() << "\n";
+ s << "user street = " << user.address() << "\n";
+ s << "user city = " << user.city() << "\n";
+ s << "user city = " << user.state() << "\n";
+ s << "user zip = " << user.postcode() << "\n";
+ s << "user telephone = " << user.telephone() << "\n";
+ s << "user e-mail = " << user.email() << "\n";
+ s << "creation date = " << storage->creationDate().toString(Qt::ISODate) << "\n";
+ s << "last modification date = " << storage->lastModificationDate().toString(Qt::ISODate) << "\n";
+ s << "base currency = " << storage->value("kmm-baseCurrency") << "\n";
+ s << "\n";
+
+ s << "Internal-Info\n";
+ s << "-------------\n";
+ QValueList<MyMoneyAccount> list_a;
+ storage->accountList(list_a);
+ s << "accounts = " << list_a.count() <<", next id = " << _storage->accountId() << "\n";
+ MyMoneyTransactionFilter filter;
+ filter.setReportAllSplits(false);
+ QValueList<MyMoneyTransaction> list_t;
+ storage->transactionList(list_t, filter);
+ QValueList<MyMoneyTransaction>::ConstIterator it_t;
+ s << "transactions = " << list_t.count() << ", next id = " << _storage->transactionId() << "\n";
+ QMap<int,int> xferCount;
+ for(it_t = list_t.begin(); it_t != list_t.end(); ++it_t) {
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ int accountCount = 0;
+ for(it_s = (*it_t).splits().begin(); it_s != (*it_t).splits().end(); ++it_s) {
+ MyMoneyAccount acc = storage->account((*it_s).accountId());
+ if(acc.accountGroup() != MyMoneyAccount::Expense
+ && acc.accountGroup() != MyMoneyAccount::Income)
+ accountCount++;
+ }
+ if(accountCount > 1)
+ xferCount[accountCount] = xferCount[accountCount] + 1;
+ }
+ QMap<int,int>::ConstIterator it_cnt;
+ for(it_cnt = xferCount.begin(); it_cnt != xferCount.end(); ++it_cnt) {
+ s << " " << *it_cnt << " of them references " << it_cnt.key() << " accounts\n";
+ }
+
+ s << "payees = " << _storage->payeeList().count() << ", next id = " << _storage->payeeId() << "\n";
+ s << "institutions = " << _storage->institutionList().count() << ", next id = " << _storage->institutionId() << "\n";
+ s << "schedules = " << _storage->scheduleList().count() << ", next id = " << _storage->scheduleId() << "\n";
+ s << "\n";
+
+ s << "Institutions\n";
+ s << "------------\n";
+
+ QValueList<MyMoneyInstitution> list_i = storage->institutionList();
+ QValueList<MyMoneyInstitution>::ConstIterator it_i;
+ for(it_i = list_i.begin(); it_i != list_i.end(); ++it_i) {
+ s << " ID = " << (*it_i).id() << "\n";
+ s << " Name = " << (*it_i).name() << "\n";
+ s << "\n";
+ }
+ s << "\n";
+
+ s << "Payees" << "\n";
+ s << "------" << "\n";
+
+ QValueList<MyMoneyPayee> list_p = storage->payeeList();
+ QValueList<MyMoneyPayee>::ConstIterator it_p;
+ for(it_p = list_p.begin(); it_p != list_p.end(); ++it_p) {
+ s << " ID = " << (*it_p).id() << "\n";
+ s << " Name = " << (*it_p).name() << "\n";
+ s << " Address = " << (*it_p).address() << "\n";
+ s << " City = " << (*it_p).city() << "\n";
+ s << " State = " << (*it_p).state() << "\n";
+ s << " Zip = " << (*it_p).postcode() << "\n";
+ s << " E-Mail = " << (*it_p).email() << "\n";
+ s << " Telephone = " << (*it_p).telephone() << "\n";
+ s << " Reference = " << (*it_p).reference() << "\n";
+ s << "\n";
+ }
+ s << "\n";
+
+
+ s << "Accounts" << "\n";
+ s << "--------" << "\n";
+
+ list_a.push_front(storage->equity());
+ list_a.push_front(storage->expense());
+ list_a.push_front(storage->income());
+ list_a.push_front(storage->liability());
+ list_a.push_front(storage->asset());
+ QValueList<MyMoneyAccount>::ConstIterator it_a;
+ for(it_a = list_a.begin(); it_a != list_a.end(); ++it_a) {
+ s << " ID = " << (*it_a).id() << "\n";
+ s << " Name = " << (*it_a).name() << "\n";
+ s << " Number = " << (*it_a).number() << "\n";
+ s << " Description = " << (*it_a).description() << "\n";
+ s << " Type = " << (*it_a).accountType() << "\n";
+ if((*it_a).currencyId().isEmpty()) {
+ s << " Currency = unknown\n";
+ } else {
+ if((*it_a).isInvest()) {
+ s << " Equity = " << storage->security((*it_a).currencyId()).name() << "\n";
+ } else {
+ s << " Currency = " << storage->currency((*it_a).currencyId()).name() << "\n";
+ }
+ }
+ s << " Parent = " << (*it_a).parentAccountId();
+ if(!(*it_a).parentAccountId().isEmpty()) {
+ MyMoneyAccount parent = storage->account((*it_a).parentAccountId());
+ s << " (" << parent.name() << ")";
+ } else {
+ s << "n/a";
+ }
+ s << "\n";
+
+ s << " Institution = " << (*it_a).institutionId();
+ if(!(*it_a).institutionId().isEmpty()) {
+ MyMoneyInstitution inst = storage->institution((*it_a).institutionId());
+ s << " (" << inst.name() << ")";
+ } else {
+ s << "n/a";
+ }
+ s << "\n";
+
+ s << " Opening data = " << (*it_a).openingDate().toString(Qt::ISODate) << "\n";
+ s << " Last modified = " << (*it_a).lastModified().toString(Qt::ISODate) << "\n";
+ s << " Last reconciled = " << (*it_a).lastReconciliationDate().toString(Qt::ISODate) << "\n";
+ s << " Balance = " << (*it_a).balance().formatMoney("", 2) << "\n";
+
+ dumpKVP(" KVP: ", s, *it_a);
+ dumpKVP(" OnlineBankingSettings: ", s, (*it_a).onlineBankingSettings());
+
+ QStringList list_s = (*it_a).accountList();
+ QStringList::ConstIterator it_s;
+ if(list_s.count() > 0) {
+ s << " Children =" << "\n";
+ }
+ for(it_s = list_s.begin(); it_s != list_s.end(); ++it_s) {
+ MyMoneyAccount child = storage->account(*it_s);
+ s << " " << *it_s << " (" << child.name() << ")\n";
+ }
+ s << "\n";
+ }
+ s << "\n";
+
+#if 0
+ s << "Currencies" << "\n";
+ s << "----------" << "\n";
+
+ QValueList<MyMoneyCurrency> list_c = storage->currencyList();
+ QValueList<MyMoneyCurrency>::ConstIterator it_c;
+ for(it_c = list_c.begin(); it_c != list_c.end(); ++it_c) {
+ s << " Name = " << (*it_c).name() << "\n";
+ s << " ID = " << (*it_c).id() << "\n";
+ s << " Symbol = " << (*it_c).tradingSymbol() << "\n";
+ s << " Parts/Unit = " << (*it_c).partsPerUnit() << "\n";
+ s << " smallest cash fraction = " << (*it_c).smallestCashFraction() << "\n";
+ s << " smallest account fraction = " << (*it_c).smallestAccountFraction() << "\n";
+ dumpPriceHistory(s, (*it_c).priceHistory());
+ s << "\n";
+ }
+ s << "\n";
+#endif
+
+ s << "Securities" << "\n";
+ s << "----------" << "\n";
+
+ QValueList<MyMoneySecurity> list_e = storage->securityList();
+ QValueList<MyMoneySecurity>::ConstIterator it_e;
+ for(it_e = list_e.begin(); it_e != list_e.end(); ++it_e) {
+ s << " Name = " << (*it_e).name() << "\n";
+ s << " ID = " << (*it_e).id() << "\n";
+ s << " Market = " << (*it_e).tradingMarket() << "\n";
+ s << " Symbol = " << (*it_e).tradingSymbol() << "\n";
+ s << " Currency = " << (*it_e).tradingCurrency() << " (";
+ if((*it_e).tradingCurrency().isEmpty()) {
+ s << "unknown";
+ } else {
+ MyMoneySecurity tradingCurrency = storage->currency((*it_e).tradingCurrency());
+ if(!tradingCurrency.isCurrency()) {
+ s << "invalid currency: ";
+ }
+ s << tradingCurrency.name();
+ }
+ s << ")\n";
+
+ s << " Type = " << MyMoneySecurity::securityTypeToString((*it_e).securityType()) << "\n";
+ s << " smallest account fraction = " << (*it_e).smallestAccountFraction() << "\n";
+
+ s << " KVP: " << "\n";
+ QMap<QString, QString>kvp = (*it_e).pairs();
+ QMap<QString, QString>::Iterator it;
+ for(it = kvp.begin(); it != kvp.end(); ++it) {
+ s << " '" << it.key() << "' = '" << it.data() << "'\n";
+ }
+ s << "\n";
+ }
+ s << "\n";
+
+ s << "Prices" << "\n";
+ s << "--------" << "\n";
+
+ MyMoneyPriceList list_pr = _storage->priceList();
+ MyMoneyPriceList::ConstIterator it_pr;
+ for(it_pr = list_pr.begin(); it_pr != list_pr.end(); ++it_pr) {
+ s << " From = " << it_pr.key().first << "\n";
+ s << " To = " << it_pr.key().second << "\n";
+ MyMoneyPriceEntries::ConstIterator it_pre;
+ for(it_pre = (*it_pr).begin(); it_pre != (*it_pr).end(); ++it_pre) {
+ s << " Date = " << (*it_pre).date().toString() << "\n";
+ s << " Price = " << (*it_pre).rate(QString()).formatMoney("", 8) << "\n";
+ s << " Source = " << (*it_pre).source() << "\n";
+ s << " From = " << (*it_pre).from() << "\n";
+ s << " To = " << (*it_pre).to() << "\n";
+ }
+ s << "\n";
+ }
+ s << "\n";
+
+ s << "Transactions" << "\n";
+ s << "------------" << "\n";
+
+ for(it_t = list_t.begin(); it_t != list_t.end(); ++it_t) {
+ dumpTransaction(s, storage, *it_t);
+ }
+ s << "\n";
+
+
+ s << "Schedules" << "\n";
+ s << "---------" << "\n";
+
+ QValueList<MyMoneySchedule> list_s = storage->scheduleList();
+ QValueList<MyMoneySchedule>::ConstIterator it_s;
+ for(it_s = list_s.begin(); it_s != list_s.end(); ++it_s) {
+ s << " ID = " << (*it_s).id() << "\n";
+ s << " Name = " << (*it_s).name() << "\n";
+ s << " Startdate = " << (*it_s).startDate().toString(Qt::ISODate) << "\n";
+ if((*it_s).willEnd())
+ s << " Enddate = " << (*it_s).endDate().toString(Qt::ISODate) << "\n";
+ else
+ s << " Enddate = not specified\n";
+ s << " Occurence = " << (*it_s).occurenceToString() << "\n";
+ s << " OccurenceMultiplier = " << (*it_s).occurenceMultiplier() << "\n";
+ s << " Type = " << MyMoneySchedule::scheduleTypeToString((*it_s).type()) << "\n";
+ s << " Paymenttype = " << MyMoneySchedule::paymentMethodToString((*it_s).paymentType()) << "\n";
+ s << " Fixed = " << (*it_s).isFixed() << "\n";
+ s << " AutoEnter = " << (*it_s).autoEnter() << "\n";
+
+ if((*it_s).lastPayment().isValid())
+ s << " Last payment = " << (*it_s).lastPayment().toString(Qt::ISODate) << "\n";
+ else
+ s << " Last payment = not defined" << "\n";
+ if((*it_s).isFinished())
+ s << " Next payment = payment finished" << "\n";
+ else {
+ s << " Next payment = " << (*it_s).nextDueDate().toString(Qt::ISODate) << "\n";
+ if((*it_s).isOverdue())
+ s << " = overdue!" << "\n";
+ }
+
+ QValueList<QDate> list_d;
+ QValueList<QDate>::ConstIterator it_d;
+
+ list_d = (*it_s).recordedPayments();
+ if(list_d.count() > 0) {
+ s << " Recorded payments" << "\n";
+ for(it_d = list_d.begin(); it_d != list_d.end(); ++it_d) {
+ s << " " << (*it_d).toString(Qt::ISODate) << "\n";
+ }
+ }
+ s << " TRANSACTION\n";
+ dumpTransaction(s, storage, (*it_s).transaction());
+ }
+ s << "\n";
+
+ s << "Reports" << "\n";
+ s << "-------" << "\n";
+
+ QValueList<MyMoneyReport> list_r = storage->reportList();
+ QValueList<MyMoneyReport>::ConstIterator it_r;
+ for(it_r = list_r.begin(); it_r != list_r.end(); ++it_r) {
+ s << " ID = " << (*it_r).id() << "\n";
+ s << " Name = " << (*it_r).name() << "\n";
+ }
+}
+
+void MyMoneyStorageDump::dumpKVP(const QString& headline, QTextStream& s, const MyMoneyKeyValueContainer &kvp, int indent)
+{
+ QString ind;
+ ind.fill(' ', indent);
+ s << ind << headline << "\n";
+ QMap<QString, QString>::const_iterator it;
+ for(it = kvp.pairs().begin(); it != kvp.pairs().end(); ++it) {
+ s << ind << " '" << it.key() << "' = '" << it.data() << "'\n";
+ }
+}
+
+void MyMoneyStorageDump::dumpTransaction(QTextStream& s, IMyMoneyStorage* storage, const MyMoneyTransaction& it_t)
+{
+ s << " ID = " << it_t.id() << "\n";
+ s << " Postdate = " << it_t.postDate().toString(Qt::ISODate) << "\n";
+ s << " EntryDate = " << it_t.entryDate().toString(Qt::ISODate) << "\n";
+ s << " Commodity = [" << it_t.commodity() << "]\n";
+ s << " Memo = " << it_t.memo() << "\n";
+ s << " BankID = " << it_t.bankID() << "\n";
+ dumpKVP("KVP:", s, it_t, 2);
+
+ s << " Splits\n";
+ s << " ------\n";
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ for(it_s = it_t.splits().begin(); it_s != it_t.splits().end(); ++it_s) {
+ s << " ID = " << (*it_s).id() << "\n";
+ s << " Transaction = " << (*it_s).transactionId() << "\n";
+ s << " Payee = " << (*it_s).payeeId();
+ if(!(*it_s).payeeId().isEmpty()) {
+ MyMoneyPayee p = storage->payee((*it_s).payeeId());
+ s << " (" << p.name() << ")" << "\n";
+ } else
+ s << " ()\n";
+ s << " Account = " << (*it_s).accountId();
+ MyMoneyAccount acc;
+ try {
+ acc = storage->account((*it_s).accountId());
+ s << " (" << acc.name() << ") [" << acc.currencyId() << "]\n";
+ } catch (MyMoneyException *e) {
+ s << " (---) [---]\n";
+ delete e;
+ }
+ s << " Memo = " << (*it_s).memo() << "\n";
+ if((*it_s).value() == MyMoneyMoney::autoCalc)
+ s << " Value = will be calculated" << "\n";
+ else
+ s << " Value = " << (*it_s).value().formatMoney("", 2)
+ << " (" << (*it_s).value().toString() << ")\n";
+ s << " Shares = " << (*it_s).shares().formatMoney("", 2)
+ << " (" << (*it_s).shares().toString() << ")\n";
+ s << " Action = '" << (*it_s).action() << "'\n";
+ s << " Nr = '" << (*it_s).number() << "'\n";
+ s << " ReconcileFlag = '" << reconcileToString((*it_s).reconcileFlag()) << "'\n";
+ if((*it_s).reconcileFlag() != MyMoneySplit::NotReconciled) {
+ s << " ReconcileDate = " << (*it_s).reconcileDate().toString(Qt::ISODate) << "\n";
+ }
+ s << " BankID = " << (*it_s).bankID() << "\n";
+ dumpKVP("KVP:", s, (*it_s), 4);
+ s << "\n";
+ }
+ s << "\n";
+}
+
+#define i18n QString
+
+const QString MyMoneyStorageDump::reconcileToString(MyMoneySplit::reconcileFlagE flag) const
+{
+ QString rc;
+
+ switch(flag) {
+ case MyMoneySplit::NotReconciled:
+ rc = i18n("not reconciled");
+ break;
+ case MyMoneySplit::Cleared:
+ rc = i18n("cleared");
+ break;
+ case MyMoneySplit::Reconciled:
+ rc = i18n("reconciled");
+ break;
+ case MyMoneySplit::Frozen:
+ rc = i18n("frozen");
+ break;
+ default:
+ rc = i18n("unknown");
+ break;
+ }
+ return rc;
+}
+
+#if 0
+void MyMoneyStorageDump::dumpPriceHistory(QTextStream& s, const equity_price_history history)
+{
+ if(history.count() != 0) {
+ s << " Price History:\n";
+
+ equity_price_history::const_iterator it_price = history.begin();
+ while ( it_price != history.end() )
+ {
+ s << " " << it_price.key().toString() << ": " << it_price.data().toDouble() << "\n";
+ it_price++;
+ }
+ }
+}
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneystoragedump.h b/kmymoney2/mymoney/storage/mymoneystoragedump.h
new file mode 100644
index 0000000..e399cde
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneystoragedump.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ mymoneystoragedump.h - description
+ -------------------
+ begin : Sun May 5 2002
+ copyright : (C) 2000-2002 by Michael Edwardes
+ email : mte@users.sourceforge.net
+ Javier Campos Morales <javi_c@users.sourceforge.net>
+ Felix Rodriguez <frodriguez@users.sourceforge.net>
+ John C <thetacoturtle@users.sourceforge.net>
+ Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#ifndef MYMONEYSTORAGEDUMP_H
+#define MYMONEYSTORAGEDUMP_H
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+#include <qdatastream.h>
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include "imymoneyserialize.h"
+#include "../mymoneysecurity.h"
+
+/**
+ * @author Thomas Baumgart
+ */
+
+class MyMoneyStorageDump
+{
+public:
+ MyMoneyStorageDump();
+ ~MyMoneyStorageDump();
+
+ void readStream(QDataStream& s, IMyMoneySerialize* storage);
+ void writeStream(QDataStream& s, IMyMoneySerialize* storage);
+
+private:
+ void dumpTransaction(QTextStream& s, IMyMoneyStorage* storage, const MyMoneyTransaction& it_t);
+ void dumpKVP(const QString& headline, QTextStream& s, const MyMoneyKeyValueContainer &kvp, int indent = 0);
+ const QString reconcileToString(MyMoneySplit::reconcileFlagE flag) const;
+};
+
+#endif
diff --git a/kmymoney2/mymoney/storage/mymoneystoragesql.cpp b/kmymoney2/mymoney/storage/mymoneystoragesql.cpp
new file mode 100644
index 0000000..97b4c55
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneystoragesql.cpp
@@ -0,0 +1,4511 @@
+/***************************************************************************
+ mymoneystoragesql.cpp
+ ---------------------
+ begin : 11 November 2005
+ copyright : (C) 2005 by Tony Bloomfield
+ email : tonybloom@users.sourceforge.net
+ : Fernando Vilas <fvilas@iname.com>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <algorithm>
+#include <numeric>
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+#include <qiodevice.h>
+#include <qsqldriver.h>
+
+// ----------------------------------------------------------------------------
+// KDE Includes
+
+#include <klocale.h>
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include "mymoneystoragesql.h"
+#include "imymoneyserialize.h"
+#include <kmymoney/kmymoneyglobalsettings.h>
+
+#define TRY try {
+#define CATCH } catch (MyMoneyException *e) {
+#define PASS } catch (MyMoneyException *e) { throw; }
+#define ECATCH }
+#define DBG(a) // qDebug(a)
+//#define TRACE(a) qDebug(a)
+#define TRACE(a) ::timetrace(a)
+
+//***************** THE CURRENT VERSION OF THE DATABASE LAYOUT ****************
+unsigned int MyMoneyDbDef::m_currentVersion = 6;
+
+// subclass QSqlQuery for performance tracing
+
+MyMoneySqlQuery::MyMoneySqlQuery (MyMoneyStorageSql* db)
+ : QSqlQuery (static_cast<QSqlDatabase*>(db)) {
+ m_db = db;
+}
+
+bool MyMoneySqlQuery::exec () {
+ TRACE(QString("start sql - %1").arg(lastQuery()));
+ bool rc = QSqlQuery::exec();
+ QString msg("end sql\n%1\n***Query returned %2, row count %3");
+ TRACE (msg.arg(QSqlQuery::executedQuery()).arg(rc).arg(numRowsAffected()));
+ //DBG (QString("%1\n***Query returned %2, row count %3").arg(QSqlQuery::executedQuery()).arg(rc).arg(size()));
+ return (rc);
+}
+
+bool MyMoneySqlQuery::prepare ( const QString & query ) {
+ if (m_db->isSqlite3()) {
+ QString newQuery = query;
+ return (QSqlQuery::prepare (newQuery.replace("FOR UPDATE", "")));
+ }
+ return (QSqlQuery::prepare (query));
+}
+
+//*****************************************************************************
+MyMoneyDbDrivers::MyMoneyDbDrivers () {
+ m_driverMap["QDB2"] = QString("IBM DB2");
+ m_driverMap["QIBASE"] = QString("Borland Interbase");
+ m_driverMap["QMYSQL3"] = QString("MySQL");
+ m_driverMap["QOCI8"] = QString("Oracle Call Interface, version 8 and 9");
+ m_driverMap["QODBC3"] = QString("Open Database Connectivity");
+ m_driverMap["QPSQL7"] = QString("PostgreSQL v6.x and v7.x");
+ m_driverMap["QTDS7"] = QString("Sybase Adaptive Server and Microsoft SQL Server");
+#if QT_VERSION < 0x040000
+ m_driverMap["QSQLITE3"] = QString("SQLite Version 3");
+#else
+ m_driverMap["QSQLITE"] = QString("SQLite Version 3");
+#endif
+}
+
+databaseTypeE MyMoneyDbDrivers::driverToType (const QString& driver) const {
+ if (driver == "QDB2") return(Db2);
+ else if (driver == "QIBASE") return(Interbase);
+ else if (driver == "QMYSQL3") return(Mysql);
+ else if (driver == "QOCI8") return(Oracle8);
+ else if (driver == "QODBC3") return(ODBC3);
+ else if (driver == "QPSQL7") return(Postgresql);
+ else if (driver == "QTDS7") return(Sybase);
+#if QT_VERSION < 0x040000
+ else if (driver == "QSQLITE3") return(Sqlite3);
+#else
+ else if (driver == "QSQLITE") return(Sqlite3);
+#endif
+ else throw new MYMONEYEXCEPTION (QString("Unknown database driver type").arg(driver));
+}
+
+bool MyMoneyDbDrivers::isTested (databaseTypeE dbType) const {
+ switch (dbType) {
+ case Mysql:
+ case Sqlite3:
+ case Postgresql:
+ return (true);
+ default:
+ return(false);
+ }
+ return(false);
+}
+
+//************************ Constructor/Destructor *****************************
+MyMoneyStorageSql::MyMoneyStorageSql (IMyMoneySerialize *storage, const KURL& url)
+ : QSqlDatabase (url.queryItem("driver"), QString("kmmdatabase")) {
+ DBG("*** Entering MyMoneyStorageSql::MyMoneyStorageSql");
+ m_dbVersion = 0;
+ m_progressCallback = 0;
+ m_displayStatus = false;
+ m_storage = storage;
+ m_storagePtr = dynamic_cast<IMyMoneyStorage*>(storage);
+ m_newDatabase = false;
+ m_readingPrices = false;
+ m_loadAll = false;
+ m_override = false;
+ m_preferred.setReportAllSplits(false);
+}
+
+int MyMoneyStorageSql::open(const KURL& url, int openMode, bool clear) {
+ DBG("*** Entering MyMoneyStorageSql::open");
+try {
+ int rc = 0;
+ QString driverName = url.queryItem("driver");
+ m_dbType = m_drivers.driverToType(driverName);
+ //get the input options
+ QStringList options = QStringList::split(',', url.queryItem("options"));
+ m_loadAll = options.contains("loadAll")/*|| m_mode == 0*/;
+ m_override = options.contains("override");
+
+ // create the database connection
+ QString dbName = url.path().right(url.path().length() - 1); // remove separator slash
+ setDatabaseName(dbName);
+ setHostName(url.host());
+ setUserName(url.user());
+ setPassword(url.pass());
+ switch (openMode) {
+ case IO_ReadOnly: // OpenDatabase menu entry (or open last file)
+ case IO_ReadWrite: // Save menu entry with database open
+ if (!QSqlDatabase::open()) {
+ buildError(MyMoneySqlQuery(), __func__, "opening database");
+ rc = 1;
+ } else {
+ rc = createTables(); // check all tables are present, create if not (we may add tables at some time)
+ }
+ break;
+ case IO_WriteOnly: // SaveAs Database - if exists, must be empty, if not will create
+ // Try to open the database.
+ // If that fails, try to create the database, then try to open it again.
+ m_newDatabase = true;
+ if (!QSqlDatabase::open()) {
+ if (createDatabase(url) != 0) {
+ rc = 1;
+ } else {
+ if (!QSqlDatabase::open()) {
+ buildError(MyMoneySqlQuery(), __func__, "opening new database");
+ rc = 1;
+ } else {
+ rc = createTables();
+ }
+ }
+ } else {
+ rc = createTables();
+ if (rc == 0) {
+ if (clear) {
+ clean();
+ } else {
+ rc = isEmpty();
+ }
+ }
+ }
+ break;
+ default:
+ qFatal("%s", QString("%1 - unknown open mode %2").arg(__func__).arg(openMode).data());
+ }
+ if (rc != 0) return (rc);
+ // bypass logon check if we are creating a database
+ if (openMode == IO_WriteOnly) return(0);
+ // check if the database is locked, if not lock it
+ readFileInfo();
+ if (!m_logonUser.isEmpty() && (!m_override)) {
+ m_error = QString
+ (i18n("Database apparently in use\nOpened by %1 on %2 at %3.\nOpen anyway?"))
+ .arg(m_logonUser)
+ .arg(m_logonAt.date().toString(Qt::ISODate))
+ .arg(m_logonAt.time().toString("hh.mm.ss"));
+ qDebug("%s", m_error.data());
+ close(false);
+ rc = -1;
+ } else {
+ m_logonUser = url.user() + "@" + url.host();
+ m_logonAt = QDateTime::currentDateTime();
+ writeFileInfo();
+ }
+ return(rc);
+} catch (QString& s) {
+ qDebug("%s",s.data());
+ return (1);
+}
+}
+
+void MyMoneyStorageSql::close(bool logoff) {
+ DBG("*** Entering MyMoneyStorageSql::close");
+ if (QSqlDatabase::open()) {
+ if (logoff) {
+ startCommitUnit(__func__);
+ m_logonUser = QString();
+ writeFileInfo();
+ endCommitUnit(__func__);
+ }
+ QSqlDatabase::close();
+ QSqlDatabase::removeDatabase(this);
+ }
+}
+
+int MyMoneyStorageSql::createDatabase (const KURL& url) {
+ DBG("*** Entering MyMoneyStorageSql::createDatabase");
+ if (m_dbType == Sqlite3) return(0); // not needed for sqlite
+ if (!m_dbType == Mysql) {
+ m_error =
+ QString(i18n("Cannot currently create database for driver %1; please create manually")).arg(driverName());
+ return (1);
+ }
+ // create the database (only works for mysql at present)
+ QString dbName = url.path().right(url.path().length() - 1); // remove separator slash
+ QSqlDatabase *maindb = QSqlDatabase::addDatabase(driverName());
+ maindb->setDatabaseName ("mysql");
+ maindb->setHostName (url.host());
+ maindb->setUserName (url.user());
+ maindb->setPassword (url.pass());
+ maindb->open();
+ QSqlQuery qm(maindb);
+ QString qs = QString("CREATE DATABASE %1;").arg(dbName);
+ qm.prepare (qs);
+ if (!qm.exec()) {
+ buildError (qm, __func__, QString(i18n("Error in create database %1; do you have create permissions?")).arg(dbName));
+ return (1);
+ }
+ QSqlDatabase::removeDatabase (maindb);
+ return (0);
+}
+
+
+int MyMoneyStorageSql::upgradeDb() {
+ DBG("*** Entering MyMoneyStorageSql::upgradeDb");
+ //signalProgress(0, 1, QObject::tr("Upgrading database..."));
+ MyMoneySqlQuery q(this);
+ q.prepare ("SELECT version FROM kmmFileInfo;");
+ if (!q.exec() || !q.next()) {
+ if (!m_newDatabase) {
+ buildError (q, __func__, "Error retrieving file info(version)");
+ return(1);
+ } else {
+ m_dbVersion = m_db.currentVersion();
+ m_storage->setFileFixVersion(m_storage->currentFixVersion());
+ QSqlQuery q(this);
+ q.prepare("UPDATE kmmFileInfo SET version = :version, \
+ fixLevel = :fixLevel;");
+ q.bindValue(":version", m_dbVersion);
+ q.bindValue(":fixLevel", m_storage->currentFixVersion());
+ if (!q.exec()) {
+ buildError (q, __func__, "Error updating file info(version)");
+ return(1);
+ }
+ return (0);
+ }
+ }
+ // prior to dbv6, 'version' format was 'dbversion.fixLevel+1'
+ // as of dbv6, these are separate fields
+ QString version = q.value(0).toString();
+ if (version.contains('.')) {
+ m_dbVersion = q.value(0).toString().section('.', 0, 0).toUInt();
+ m_storage->setFileFixVersion(q.value(0).toString().section('.', 1, 1).toUInt() - 1);
+ } else {
+ m_dbVersion = version.toUInt();
+ q.prepare ("SELECT fixLevel FROM kmmFileInfo;");
+ if (!q.exec() || !q.next()) {
+ buildError (q, __func__, "Error retrieving file info (fixLevel)");
+ return(1);
+ }
+ m_storage->setFileFixVersion(q.value(0).toUInt());
+ }
+ int rc = 0;
+ while ((m_dbVersion < m_db.currentVersion()) && (rc == 0)) {
+ switch (m_dbVersion) {
+ case 0:
+ if ((rc = upgradeToV1()) != 0) return (1);
+ ++m_dbVersion;
+ break;
+ case 1:
+ if ((rc = upgradeToV2()) != 0) return (1);
+ ++m_dbVersion;
+ break;
+ case 2:
+ if ((rc = upgradeToV3()) != 0) return (1);
+ ++m_dbVersion;
+ break;
+ case 3:
+ if ((rc = upgradeToV4()) != 0) return (1);
+ ++m_dbVersion;
+ break;
+ case 4:
+ if ((rc = upgradeToV5()) != 0) return (1);
+ ++m_dbVersion;
+ break;
+ case 5:
+ if ((rc = upgradeToV6()) != 0) return (1);
+ ++m_dbVersion;
+ break;
+ case 6:
+ break;
+ default:
+ qFatal("Unknown version number in database - %d", m_dbVersion);
+ }
+ }
+ // write updated version to DB
+ //setVersion(QString("%1.%2").arg(m_dbVersion).arg(m_minorVersion));
+ q.prepare (QString("UPDATE kmmFileInfo SET version = :version;"));
+ q.bindValue(":version", m_dbVersion);
+ if (!q.exec()) {
+ buildError (q, __func__, "Error updating db version");
+ return (1);
+ }
+ //signalProgress(-1,-1);
+ return (0);
+}
+// SF bug 2779291
+// check whether a column appears in a table already; if not, add it
+bool MyMoneyStorageSql::addColumn
+ (const QString& table, const QString& col,
+ const QString& after)
+{
+ MyMoneyDbTable t = m_db.m_tables[table];
+ MyMoneyDbTable::field_iterator ft;
+ const MyMoneyDbColumn* c;
+ for (ft = t.begin(); ft != t.end(); ++ft) {
+ c = (*ft);
+ if (c->name() == col)
+ break;
+ }
+ if (ft == t.end()) qFatal("addColumn - get it right");
+ return (addColumn(t, *c, after));
+}
+
+bool MyMoneyStorageSql::addColumn
+ (const MyMoneyDbTable& t, const MyMoneyDbColumn& c,
+ const QString& after){
+ if ((m_dbType == Sqlite3) && (!after.isEmpty()))
+ qFatal("sqlite doesn't support 'AFTER'; use sqliteAlterTable");
+ if (record(t.name()).contains(c.name()))
+ return (true);
+ QSqlQuery q(this);
+ QString afterString = ";";
+ if (!after.isEmpty())
+ afterString = QString("AFTER %1;").arg(after);
+ q.prepare("ALTER TABLE " + t.name() + " ADD COLUMN " +
+ c.generateDDL(m_dbType) + afterString);
+ if (!q.exec()) {
+ buildError (q, __func__,
+ QString("Error adding column %1 to table %2").arg(c.name()).arg(t.name()));
+ return (false);
+ }
+ return (true);
+}
+
+// analogous to above
+bool MyMoneyStorageSql::dropColumn
+ (const QString& table, const QString& col)
+{
+ return (dropColumn(m_db.m_tables[table], col));
+}
+
+bool MyMoneyStorageSql::dropColumn
+ (const MyMoneyDbTable& t, const QString& col){
+ if (m_dbType == Sqlite3)
+ qFatal("sqlite doesn't support 'DROP COLUMN'; use sqliteAlterTable");
+ if (!record(t.name()).contains(col))
+ return (true);
+ QSqlQuery q(this);
+ q.prepare("ALTER TABLE " + t.name() + " DROP COLUMN "
+ + col + ";");
+ if (!q.exec()) {
+ buildError (q, __func__,
+ QString("Error dropping column %1 from table %2").arg(col).arg(t.name()));
+ return (false);
+ }
+ return (true);
+}
+
+int MyMoneyStorageSql::upgradeToV1() {
+ DBG("*** Entering MyMoneyStorageSql::upgradeToV1");
+ if ((m_dbType == Sqlite) || (m_dbType == Sqlite3)) qFatal("SQLite upgrade NYI");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ // change kmmSplits pkey to (transactionId, splitId)
+ q.prepare ("ALTER TABLE kmmSplits ADD PRIMARY KEY (transactionId, splitId);");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error updating kmmSplits pkey");
+ return (1);
+ }
+ // change kmmSplits alter checkNumber varchar(32)
+ q.prepare (m_db.m_tables["kmmSplits"].modifyColumnString(m_dbType, "checkNumber",
+ MyMoneyDbColumn("checkNumber", "varchar(32)")));
+ if (!q.exec()) {
+ buildError (q, __func__, "Error expanding kmmSplits.checkNumber");
+ return (1);
+ }
+ // change kmmSplits add postDate datetime
+ if (!addColumn(m_db.m_tables["kmmSplits"],
+ MyMoneyDbDatetimeColumn("postDate")))
+ return (1);
+ // initialize it to same value as transaction (do it the long way round)
+ q.prepare ("SELECT id, postDate FROM kmmTransactions WHERE txType = 'N';");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error priming kmmSplits.postDate");
+ return (1);
+ }
+ QMap<QString, QDateTime> tids;
+ while (q.next()) tids[q.value(0).toString()] = q.value(1).toDateTime();
+ QMap<QString, QDateTime>::ConstIterator it;
+ for (it = tids.begin(); it != tids.end(); ++it) {
+ q.prepare ("UPDATE kmmSplits SET postDate=:postDate WHERE transactionId = :id;");
+ q.bindValue(":postDate", it.data().toString(Qt::ISODate));
+ q.bindValue(":id", it.key());
+ if (!q.exec()) {
+ buildError (q, __func__, "priming kmmSplits.postDate");
+ return(1);
+ }
+ }
+ // add index to kmmKeyValuePairs to (kvpType,kvpId)
+ QStringList list;
+ list << "kvpType" << "kvpId";
+ q.prepare (MyMoneyDbIndex("kmmKeyValuePairs", "kmmKVPtype_id", list, false).generateDDL(m_dbType) + ";");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error adding kmmKeyValuePairs index");
+ return (1);
+ }
+ // add index to kmmSplits to (accountId, txType)
+ list.clear();
+ list << "accountId" << "txType";
+ q.prepare (MyMoneyDbIndex("kmmSplits", "kmmSplitsaccount_type", list, false).generateDDL(m_dbType) + ";");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error adding kmmSplits index");
+ return (1);
+ }
+ // change kmmSchedulePaymentHistory pkey to (schedId, payDate)
+ q.prepare ("ALTER TABLE kmmSchedulePaymentHistory ADD PRIMARY KEY (schedId, payDate);");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error updating kmmSchedulePaymentHistory pkey");
+ return (1);
+ }
+ // change kmmPrices pkey to (fromId, toId, priceDate)
+ q.prepare ("ALTER TABLE kmmPrices ADD PRIMARY KEY (fromId, toId, priceDate);");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error updating kmmPrices pkey");
+ return (1);
+ }
+ // change kmmReportConfig pkey to (name)
+ // There wasn't one previously, so no need to drop it.
+ q.prepare ("ALTER TABLE kmmReportConfig ADD PRIMARY KEY (name);");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error updating kmmReportConfig pkey");
+ return (1);
+ }
+ // change kmmFileInfo add budgets unsigned bigint after kvps
+ if (!addColumn(m_db.m_tables["kmmFileInfo"],
+ MyMoneyDbIntColumn("budgets", MyMoneyDbIntColumn::BIG, false)))
+ return (1);
+ // change kmmFileInfo add hiBudgetId unsigned bigint after hiReportId
+ if (!addColumn(m_db.m_tables["kmmFileInfo"],
+ MyMoneyDbIntColumn("hiBudgetId", MyMoneyDbIntColumn::BIG, false)))
+ return (1);
+ // change kmmFileInfo add logonUser
+ if (!addColumn(m_db.m_tables["kmmFileInfo"],
+ MyMoneyDbColumn("logonUser", "varchar(255)", false)))
+ return (1);
+ // change kmmFileInfo add logonAt datetime
+ if (!addColumn(m_db.m_tables["kmmFileInfo"],
+ MyMoneyDbDatetimeColumn("logonAt", false)))
+ return (1);
+ // change kmmAccounts add transactionCount unsigned bigint as last field
+ if (!addColumn(m_db.m_tables["kmmAccounts"],
+ MyMoneyDbIntColumn("transactionCount", MyMoneyDbIntColumn::BIG, false)))
+ return (1);
+ // calculate the transaction counts. the application logic defines an account's tx count
+ // in such a way as to count multiple splits in a tx which reference the same account as one.
+ // this is the only way I can think of to do this which will work in sqlite too.
+ // inefficient, but it only gets done once...
+ // get a list of all accounts so we'll get a zero value for those without txs
+ q.prepare ("SELECT id FROM kmmAccounts");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error retrieving accounts for transaction counting");
+ return(1);
+ }
+ while (q.next()) {
+ m_transactionCountMap[q.value(0).toCString()] = 0;
+ }
+ q.prepare ("SELECT accountId, transactionId FROM kmmSplits WHERE txType = 'N' ORDER BY 1, 2");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error retrieving splits for transaction counting");
+ return(1);
+ }
+ QString lastAcc, lastTx;
+ while (q.next()) {
+ QString thisAcc = q.value(0).toCString();
+ QString thisTx = q.value(1).toCString();
+ if ((thisAcc != lastAcc) || (thisTx != lastTx)) ++m_transactionCountMap[thisAcc];
+ lastAcc = thisAcc;
+ lastTx = thisTx;
+ }
+ QMap<QString, unsigned long>::ConstIterator itm;
+ q.prepare("UPDATE kmmAccounts SET transactionCount = :txCount WHERE id = :id;");
+ for (itm = m_transactionCountMap.begin(); itm != m_transactionCountMap.end(); ++itm) {
+ q.bindValue (":txCount", QString::number(itm.data()));
+ q.bindValue (":id", itm.key());
+ if (!q.exec()) {
+ buildError(q, __func__, "Error updating transaction count");
+ return (1);
+ }
+ }
+ m_transactionCountMap.clear();
+ // there were considerable problems with record counts in V0, so rebuild them
+ readFileInfo();
+ m_institutions = getRecCount("kmmInstitutions");
+ m_accounts = getRecCount("kmmAccounts");
+ m_payees = getRecCount("kmmPayees");
+ m_transactions = getRecCount("kmmTransactions WHERE txType = 'N'");
+ m_splits = getRecCount("kmmSplits");
+ m_securities = getRecCount("kmmSecurities");
+ m_prices = getRecCount("kmmPrices");
+ m_currencies = getRecCount("kmmCurrencies");
+ m_schedules = getRecCount("kmmSchedules");
+ m_reports = getRecCount("kmmReportConfig");
+ m_kvps = getRecCount("kmmKeyValuePairs");
+ m_budgets = getRecCount("kmmBudgetConfig");
+ writeFileInfo();
+ /* if sqlite {
+ q.prepare("VACUUM;");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error vacuuming database");
+ return(1);
+ }
+ }*/
+ endCommitUnit(__func__);
+ return (0);
+}
+
+int MyMoneyStorageSql::upgradeToV2() {
+ DBG("*** Entering MyMoneyStorageSql::upgradeToV2");
+ //SQLite3 now supports ALTER TABLE...ADD COLUMN, so only die if version < 3
+ //if (m_dbType == Sqlite3) qFatal("SQLite upgrade NYI");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ // change kmmSplits add price fields
+ if (!addColumn(m_db.m_tables["kmmSplits"],
+ MyMoneyDbTextColumn("price")))
+ return (1);
+ if (!addColumn(m_db.m_tables["kmmSplits"],
+ MyMoneyDbTextColumn("priceFormatted")))
+ return (1);
+ endCommitUnit(__func__);
+ return (0);
+}
+
+int MyMoneyStorageSql::upgradeToV3() {
+ DBG("*** Entering MyMoneyStorageSql::upgradeToV3");
+ //SQLite3 now supports ALTER TABLE...ADD COLUMN, so only die if version < 3
+ //if (m_dbType == Sqlite3) qFatal("SQLite upgrade NYI");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ // The default value is given here to populate the column.
+ q.prepare ("ALTER TABLE kmmSchedules ADD COLUMN " +
+ MyMoneyDbIntColumn("occurenceMultiplier",
+ MyMoneyDbIntColumn::SMALL, false, false, true)
+ .generateDDL(m_dbType) + " DEFAULT 0;");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error adding kmmSchedules.occurenceMultiplier");
+ return (1);
+ }
+ //The default is less than any useful value, so as each schedule is hit, it will update
+ //itself to the appropriate value.
+ endCommitUnit(__func__);
+ return 0;
+}
+
+int MyMoneyStorageSql::upgradeToV4() {
+ DBG("*** Entering MyMoneyStorageSql::upgradeToV4");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ QStringList list;
+ list << "transactionId" << "splitId";
+ q.prepare (MyMoneyDbIndex("kmmSplits", "kmmTx_Split", list, false).generateDDL(m_dbType) + ";");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error adding kmmSplits index on (transactionId, splitId)");
+ return (1);
+ }
+ endCommitUnit(__func__);
+ return 0;
+}
+
+int MyMoneyStorageSql::upgradeToV5() {
+ DBG("*** Entering MyMoneyStorageSql::upgradeToV5");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ if (!addColumn(m_db.m_tables["kmmSplits"],
+ MyMoneyDbTextColumn("bankId")))
+ return (1);
+ if (!addColumn(m_db.m_tables["kmmPayees"],
+ MyMoneyDbTextColumn("notes", MyMoneyDbTextColumn::LONG)))
+ return (1);
+ if (!addColumn(m_db.m_tables["kmmPayees"],
+ MyMoneyDbColumn("defaultAccountId", "varchar(32)")))
+ return (1);
+ if (!addColumn(m_db.m_tables["kmmPayees"],
+ MyMoneyDbIntColumn("matchData", MyMoneyDbIntColumn::TINY,
+ false)))
+ return (1);
+ if (!addColumn(m_db.m_tables["kmmPayees"],
+ MyMoneyDbColumn("matchIgnoreCase", "char(1)")))
+ return (1);
+ if (!addColumn(m_db.m_tables["kmmPayees"],
+ MyMoneyDbTextColumn("matchKeys")))
+ return (1);
+ const MyMoneyDbTable& t = m_db.m_tables["kmmReportConfig"];
+ if (m_dbType != Sqlite3) {
+ q.prepare (t.dropPrimaryKeyString(m_dbType));
+ if (!q.exec()) {
+ buildError (q, __func__, "Error dropping Report table keys");
+ return (1);
+ }
+ } else {
+ if (!sqliteAlterTable(t))
+ return (1);
+ }
+ endCommitUnit(__func__);
+ return 0;
+}
+
+int MyMoneyStorageSql::upgradeToV6() {
+ DBG("*** Entering MyMoneyStorageSql::upgradeToV6");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ // add separate fix level in file info
+ if (!addColumn("kmmFileInfo", "fixLevel"))
+ return (1);
+ // upgrade Mysql to InnoDB transaction-safe engine
+ if (m_dbType == Mysql) {
+ for (QMapConstIterator<QString, MyMoneyDbTable> tt = m_db.tableBegin(); tt != m_db.tableEnd(); ++tt) {
+ q.prepare(QString("ALTER TABLE %1 ENGINE = InnoDB;").arg(tt.data().name()));
+ if (!q.exec()) {
+ buildError (q, __func__, "Error updating to InnoDB");
+ return (1);
+ }
+ }
+ }
+ // add unique id to reports table
+ if (!addColumn(m_db.m_tables["kmmReportConfig"],
+ MyMoneyDbColumn("id", "varchar(32)")))
+ return(1);
+ // read and write reports to get ids inserted
+ readFileInfo();
+ QMap<QString, MyMoneyReport> reportList =
+ fetchReports();
+ // the V5 database allowed lots of duplicate reports with no
+ // way to distinguish between them. The fetchReports call
+ // will have effectively removed all duplicates
+ // so we now delete from the db and re-write them
+ q.prepare("DELETE FROM kmmReportConfig;");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error deleting reports");
+ return (1);
+ }
+ unsigned long long hiReportId = 0;
+ QMap<QString, MyMoneyReport>::const_iterator it_r;
+ for(it_r = reportList.begin(); it_r != reportList.end(); ++it_r) {
+ MyMoneyReport r = *it_r;
+ hiReportId = calcHighId(hiReportId, r.id());
+ q.prepare (m_db.m_tables["kmmReportConfig"].insertString());
+ writeReport(*it_r, q);
+ }
+ m_hiIdReports = hiReportId;
+ m_storage->loadReportId(m_hiIdReports);
+ // sqlite3 doesn't support ADD PRIMARY KEY
+ if (m_dbType == Sqlite3) {
+ if (!sqliteAlterTable(m_db.m_tables["kmmReportConfig"])) {
+ return (1);
+ }
+ } else {
+ q.prepare ("ALTER TABLE kmmReportConfig ADD PRIMARY KEY (id);");
+ if (!q.exec()) {
+ buildError (q, __func__, "Error updating kmmReportConfig pkey");
+ return (1);
+ }
+ }
+ endCommitUnit(__func__);
+ return 0;
+}
+
+/* This function attempts to cater for limitations in the sqlite ALTER TABLE
+ statement. It should enable us to drop a primary key, and drop columns */
+bool MyMoneyStorageSql::sqliteAlterTable(const MyMoneyDbTable& t) {
+ DBG("*** Entering MyMoneyStorageSql::sqliteAlterTable");
+ QString tempTableName = t.name();
+ tempTableName.replace("kmm", "tmp");
+ QSqlQuery q(this);
+ q.prepare (QString("ALTER TABLE " + t.name() + " RENAME TO " + tempTableName + ";"));
+ if (!q.exec()) {
+ buildError (q, __func__, "Error renaming table");
+ return false;
+ }
+ createTable(t);
+ q.prepare (QString("INSERT INTO " + t.name() + " (" + t.columnList() +
+ ") SELECT " + t.columnList() + " FROM " + tempTableName + ";"));
+ if (!q.exec()) {
+ buildError (q, __func__, "Error inserting into new table");
+ return false;
+ }
+ q.prepare (QString("DROP TABLE " + tempTableName + ";"));
+ if (!q.exec()) {
+ buildError (q, __func__, "Error dropping old table");
+ return false;
+ }
+ return true;
+}
+
+long unsigned MyMoneyStorageSql::getRecCount (const QString& table) const {
+ DBG("*** Entering MyMoneyStorageSql::getRecCount");
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ q.prepare(QString("SELECT COUNT(*) FROM %1;").arg(table));
+ if ((!q.exec()) || (!q.next())) {
+ buildError (q, __func__, "error retrieving record count");
+ qFatal("Error retrieving record count"); // definitely shouldn't happen
+ }
+ return ((unsigned long) q.value(0).toULongLong());
+}
+
+int MyMoneyStorageSql::createTables () {
+ DBG("*** Entering MyMoneyStorageSql::createTables");
+ // check tables, create if required
+ // convert everything to lower case, since SQL standard is case insensitive
+ // table and column names (when not delimited), but some DBMSs disagree.
+ QStringList lowerTables = tables(QSql::AllTables);
+ for (QStringList::iterator i = lowerTables.begin(); i != lowerTables.end(); ++i) {
+ (*i) = (*i).lower();
+ }
+
+ for (QMapConstIterator<QString, MyMoneyDbTable> tt = m_db.tableBegin(); tt != m_db.tableEnd(); ++tt) {
+ if (!lowerTables.contains(tt.key().lower())) createTable (tt.data());
+ }
+
+ MyMoneySqlQuery q(this);
+ for (QMapConstIterator<QString, MyMoneyDbView> tt = m_db.viewBegin(); tt != m_db.viewEnd(); ++tt) {
+ if (!lowerTables.contains(tt.key().lower())) {
+ q.prepare (tt.data().createString());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString ("creating view %1").arg(tt.key())));
+ }
+ }
+
+ // get the current db version from kmmFileInfo.
+ // upgrade if necessary.
+
+ return (upgradeDb()); // any errors will be caught by exception handling
+}
+
+void MyMoneyStorageSql::createTable (const MyMoneyDbTable& t) {
+ DBG("*** Entering MyMoneyStorageSql::createTable");
+// create the tables
+ QStringList ql = QStringList::split('\n', t.generateCreateSQL(m_dbType));
+ MyMoneySqlQuery q(this);
+ for (unsigned int i = 0; i < ql.count(); ++i) {
+ q.prepare (ql[i]);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString ("creating table/index %1").arg(t.name())));
+ }
+}
+
+int MyMoneyStorageSql::isEmpty () {
+ DBG("*** Entering MyMoneyStorageSql::isEmpty");
+ // check all tables are empty
+ QMapConstIterator<QString, MyMoneyDbTable> tt = m_db.tableBegin();
+ int recordCount = 0;
+ MyMoneySqlQuery q(this);
+ while ((tt != m_db.tableEnd()) && (recordCount == 0)) {
+ q.prepare (QString("select count(*) from %1;").arg((*tt).name()));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "getting record count"));
+ if (!q.next()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "retrieving record count"));
+ recordCount += q.value(0).toInt();
+ ++tt;
+ }
+
+ if (recordCount != 0) {
+ return (-1); // not empty
+ } else {
+ return (0);
+ }
+}
+
+void MyMoneyStorageSql::clean() {
+ DBG("*** Entering MyMoneyStorageSql::clean");
+// delete all existing records
+ QMapConstIterator<QString, MyMoneyDbTable> it = m_db.tableBegin();
+ MyMoneySqlQuery q(this);
+ while (it != m_db.tableEnd()) {
+ q.prepare(QString("DELETE from %1;").arg(it.key()));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString ("cleaning database")));
+ ++it;
+ }
+}
+
+//////////////////////////////////////////////////////////////////
+
+bool MyMoneyStorageSql::readFile(void) {
+ DBG("*** Entering MyMoneyStorageSql::readFile");
+ m_displayStatus = true;
+ try {
+ readFileInfo();
+ readInstitutions();
+ if (m_loadAll) {
+ readPayees();
+ } else {
+ QValueList<QString> user;
+ user.append(QString("USER"));
+ readPayees(user);
+ }
+ //TRACE("done payees");
+ readCurrencies();
+ //TRACE("done currencies");
+ readSecurities();
+ //TRACE("done securities");
+ readAccounts();
+ if (m_loadAll) {
+ readTransactions();
+ } else {
+ if (m_preferred.filterSet().singleFilter.accountFilter) readTransactions (m_preferred);
+ }
+ //TRACE("done accounts");
+ readSchedules();
+ //TRACE("done schedules");
+ readPrices();
+ //TRACE("done prices");
+ readReports();
+ //TRACE("done reports");
+ readBudgets();
+ //TRACE("done budgets");
+ //FIXME - ?? if (m_mode == 0)
+ //m_storage->rebuildAccountBalances();
+ // this seems to be nonsense, but it clears the dirty flag
+ // as a side-effect.
+ m_storage->setLastModificationDate(m_storage->lastModificationDate());
+ // FIXME?? if (m_mode == 0) m_storage = NULL;
+ // make sure the progress bar is not shown any longer
+ signalProgress(-1, -1);
+ m_displayStatus = false;
+ //MyMoneySqlQuery::traceOn();
+ return true;
+ } catch (QString& s) {
+ return false;
+ }
+}
+
+// The following is called from 'SaveAsDatabase'
+bool MyMoneyStorageSql::writeFile(void) {
+ DBG("*** Entering MyMoneyStorageSql::writeFile");
+ // initialize record counts and hi ids
+ m_institutions = m_accounts = m_payees = m_transactions = m_splits
+ = m_securities = m_prices = m_currencies = m_schedules = m_reports = m_kvps = m_budgets = 0;
+ m_hiIdInstitutions = m_hiIdPayees = m_hiIdAccounts = m_hiIdTransactions =
+ m_hiIdSchedules = m_hiIdSecurities = m_hiIdReports = m_hiIdBudgets = 0;
+ m_displayStatus = true;
+ try{
+ startCommitUnit(__func__);
+ writeInstitutions ();
+ writePayees();
+ writeAccounts();
+ writeTransactions();
+ writeSchedules();
+ writeSecurities();
+ writePrices();
+ writeCurrencies();
+ writeReports();
+ writeBudgets();
+ writeFileInfo();
+ // this seems to be nonsense, but it clears the dirty flag
+ // as a side-effect.
+ //m_storage->setLastModificationDate(m_storage->lastModificationDate());
+ // FIXME?? if (m_mode == 0) m_storage = NULL;
+ endCommitUnit(__func__);
+ // make sure the progress bar is not shown any longer
+ signalProgress(-1, -1);
+ m_displayStatus = false;
+ return true;
+} catch (QString& s) {
+ return false;
+}
+}
+// --------------- SQL Transaction (commit unit) handling -----------------------------------
+void MyMoneyStorageSql::startCommitUnit (const QString& callingFunction) {
+ DBG("*** Entering MyMoneyStorageSql::startCommitUnit");
+ if (m_commitUnitStack.isEmpty()) {
+ if (!transaction()) throw new MYMONEYEXCEPTION(buildError (MyMoneySqlQuery(), __func__, "starting commit unit"));
+ }
+ m_commitUnitStack.push(callingFunction);
+}
+
+bool MyMoneyStorageSql::endCommitUnit (const QString& callingFunction) {
+ DBG("*** Entering MyMoneyStorageSql::endCommitUnit");
+ // for now, we don't know if there were any changes made to the data so
+ // we expect the data to have changed. This assumption causes some unnecessary
+ // repaints of the UI here and there, but for now it's ok. If we can determine
+ // that the commit() really changes the data, we can return that information
+ // as value of this method.
+ bool rc = true;
+ if (callingFunction != m_commitUnitStack.top())
+ qDebug("%s", QString("%1 - %2 s/be %3").arg(__func__).arg(callingFunction).arg(m_commitUnitStack.top()).data());
+ m_commitUnitStack.pop();
+ if (m_commitUnitStack.isEmpty()) {
+ if (!commit()) throw new MYMONEYEXCEPTION(buildError (MyMoneySqlQuery(), __func__, "ending commit unit"));
+ }
+ return rc;
+}
+
+void MyMoneyStorageSql::cancelCommitUnit (const QString& callingFunction) {
+ DBG("*** Entering MyMoneyStorageSql::cancelCommitUnit");
+ if (callingFunction != m_commitUnitStack.top())
+ qDebug("%s", QString("%1 - %2 s/be %3").arg(__func__).arg(callingFunction).arg(m_commitUnitStack.top()).data());
+ if (m_commitUnitStack.isEmpty()) return;
+ m_commitUnitStack.clear();
+ if (!rollback()) throw new MYMONEYEXCEPTION(buildError (MyMoneySqlQuery(), __func__, "cancelling commit unit"));
+}
+
+/////////////////////////////////////////////////////////////////////
+void MyMoneyStorageSql::fillStorage() {
+ DBG("*** Entering MyMoneyStorageSql::fillStorage");
+// if (!m_transactionListRead) // make sure we have loaded everything
+ readTransactions();
+// if (!m_payeeListRead)
+ readPayees();
+}
+
+//------------------------------ Write SQL routines ----------------------------------------
+// **** Institutions ****
+void MyMoneyStorageSql::writeInstitutions() {
+ DBG("*** Entering MyMoneyStorageSql::writeInstitutions");
+ // first, get a list of what's on the database
+ // anything not in the list needs to be inserted
+ // anything which is will be updated and removed from the list
+ // anything left over at the end will need to be deleted
+ // this is an expensive and inconvenient way to do things; find a better way
+ // one way would be to build the lists when reading the db
+ // unfortunately this object does not persist between read and write
+ // it would also be nice if we could tell which objects had been updated since we read them in
+ QValueList<QString> dbList;
+ MyMoneySqlQuery q(this);
+ q.prepare("SELECT id FROM kmmInstitutions;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "building Institution list"));
+ while (q.next()) dbList.append(q.value(0).toString());
+
+ const QValueList<MyMoneyInstitution> list = m_storage->institutionList();
+ QValueList<MyMoneyInstitution>::ConstIterator it;
+ MyMoneySqlQuery q2(this);
+ q.prepare (m_db.m_tables["kmmInstitutions"].updateString());
+ q2.prepare (m_db.m_tables["kmmInstitutions"].insertString());
+ signalProgress(0, list.count(), "Writing Institutions...");
+ for(it = list.begin(); it != list.end(); ++it) {
+ if (dbList.contains((*it).id())) {
+ dbList.remove ((*it).id());
+ writeInstitution(*it, q);
+ } else {
+ writeInstitution(*it, q2);
+ }
+ signalProgress (++m_institutions, 0);
+ }
+
+ if (!dbList.isEmpty()) {
+ QValueList<QString>::const_iterator it = dbList.begin();
+ q.prepare("DELETE FROM kmmInstitutions WHERE id = :id");
+ while (it != dbList.end()) {
+ q.bindValue(":id", (*it));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Institution"));
+ deleteKeyValuePairs("OFXSETTINGS", (*it));
+ ++it;
+ }
+ }
+}
+
+void MyMoneyStorageSql::addInstitution(const MyMoneyInstitution& inst) {
+ DBG("*** Entering MyMoneyStorageSql::addInstitution");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmInstitutions"].insertString());
+ writeInstitution(inst ,q);
+ ++m_institutions;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::modifyInstitution(const MyMoneyInstitution& inst) {
+ DBG("*** Entering MyMoneyStorageSql::modifyInstitution");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmInstitutions"].updateString());
+ deleteKeyValuePairs("OFXSETTINGS", inst.id());
+ writeInstitution(inst ,q);
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::removeInstitution(const MyMoneyInstitution& inst) {
+ DBG("*** Entering MyMoneyStorageSql::removeInstitution");
+ startCommitUnit(__func__);
+ deleteKeyValuePairs("OFXSETTINGS", inst.id());
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmInstitutions"].deleteString());
+ q.bindValue(":id", inst.id());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting Institution")));
+ --m_institutions;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::writeInstitution(const MyMoneyInstitution& i, MyMoneySqlQuery& q) {
+ DBG("*** Entering MyMoneyStorageSql::writeInstitution");
+ q.bindValue(":id", i.id());
+ q.bindValue(":name", i.name());
+ q.bindValue(":manager", i.manager());
+ q.bindValue(":routingCode", i.sortcode());
+ q.bindValue(":addressStreet", i.street());
+ q.bindValue(":addressCity", i.city());
+ q.bindValue(":addressZipcode", i.postcode());
+ q.bindValue(":telephone", i.telephone());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Institution")));
+ writeKeyValuePairs("OFXSETTINGS", i.id(), i.pairs());
+ m_hiIdInstitutions = calcHighId(m_hiIdInstitutions, i.id());
+}
+
+// **** Payees ****
+void MyMoneyStorageSql::writePayees() {
+ DBG("*** Entering MyMoneyStorageSql::writePayees");
+ // first, get a list of what's on the database (see writeInstitutions)
+ QValueList<QString> dbList;
+ MyMoneySqlQuery q(this);
+ q.prepare("SELECT id FROM kmmPayees;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "building Payee list"));
+ while (q.next()) dbList.append(q.value(0).toString());
+
+ QValueList<MyMoneyPayee> list = m_storage->payeeList();
+ MyMoneyPayee user(QString("USER"), m_storage->user());
+ list.prepend(user);
+ signalProgress(0, list.count(), "Writing Payees...");
+ MyMoneySqlQuery q2(this);
+ q.prepare (m_db.m_tables["kmmPayees"].updateString());
+ q2.prepare (m_db.m_tables["kmmPayees"].insertString());
+ QValueList<MyMoneyPayee>::ConstIterator it;
+ for(it = list.begin(); it != list.end(); ++it) {
+ if (dbList.contains((*it).id())) {
+ dbList.remove ((*it).id());
+ writePayee(*it, q);
+ } else {
+ writePayee(*it, q2);
+ }
+ signalProgress(++m_payees, 0);
+ }
+
+ if (!dbList.isEmpty()) {
+ QValueList<QString>::const_iterator it = dbList.begin();
+ q.prepare(m_db.m_tables["kmmPayees"].deleteString());
+ while (it != dbList.end()) {
+ q.bindValue(":id", (*it));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Payee"));
+ m_payees -= q.numRowsAffected();
+ ++it;
+ }
+ }
+}
+
+void MyMoneyStorageSql::addPayee(const MyMoneyPayee& payee) {
+ DBG("*** Entering MyMoneyStorageSql::addPayee");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmPayees"].insertString());
+ writePayee(payee,q);
+ ++m_payees;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::modifyPayee(const MyMoneyPayee& payee) {
+ DBG("*** Entering MyMoneyStorageSql::modifyPayee");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmPayees"].updateString());
+ writePayee(payee,q);
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::modifyUserInfo(const MyMoneyPayee& payee) {
+ DBG("*** Entering MyMoneyStorageSql::modifyUserInfo");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmPayees"].updateString());
+ writePayee(payee,q, true);
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::removePayee(const MyMoneyPayee& payee) {
+ DBG("*** Entering MyMoneyStorageSql::removePayee");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmPayees"].deleteString());
+ q.bindValue(":id", payee.id());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting Payee")));
+ --m_payees;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::writePayee(const MyMoneyPayee& p, MyMoneySqlQuery& q, bool isUserInfo) {
+ DBG("*** Entering MyMoneyStorageSql::writePayee");
+ if (isUserInfo) {
+ q.bindValue(":id", "USER");
+ } else {
+ q.bindValue(":id", p.id());
+ }
+ q.bindValue(":name", p.name());
+ q.bindValue(":reference", p.reference());
+ q.bindValue(":email", p.email());
+ q.bindValue(":addressStreet", p.address());
+ q.bindValue(":addressCity", p.city());
+ q.bindValue(":addressZipcode", p.postcode());
+ q.bindValue(":addressState", p.state());
+ q.bindValue(":telephone", p.telephone());
+ q.bindValue(":notes", p.notes());
+ q.bindValue(":defaultAccountId", p.defaultAccountId());
+ bool ignoreCase;
+ QString matchKeys;
+ MyMoneyPayee::payeeMatchType type = p.matchData(ignoreCase, matchKeys);
+ q.bindValue(":matchData", static_cast<unsigned int>(type));
+ if (ignoreCase) q.bindValue(":matchIgnoreCase", "Y");
+ else q.bindValue(":matchIgnoreCase", "N");
+ q.bindValue(":matchKeys", matchKeys);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString ("writing Payee")));
+ if (!isUserInfo) m_hiIdPayees = calcHighId(m_hiIdPayees, p.id());
+}
+
+// **** Accounts ****
+void MyMoneyStorageSql::writeAccounts() {
+ DBG("*** Entering MyMoneyStorageSql::writeAccounts");
+ // first, get a list of what's on the database (see writeInstitutions)
+ QValueList<QString> dbList;
+ MyMoneySqlQuery q(this);
+ q.prepare("SELECT id FROM kmmAccounts;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "building Account list"));
+ while (q.next()) dbList.append(q.value(0).toString());
+
+ QValueList<MyMoneyAccount> list;
+ m_storage->accountList(list);
+ QValueList<MyMoneyAccount>::ConstIterator it;
+ signalProgress(0, list.count(), "Writing Accounts...");
+ if (dbList.isEmpty()) { // new table, insert standard accounts
+ q.prepare (m_db.m_tables["kmmAccounts"].insertString());
+ } else {
+ q.prepare (m_db.m_tables["kmmAccounts"].updateString());
+ }
+ // Attempt to write the standard accounts. For an empty db, this will fail.
+ TRY
+ writeAccount(m_storage->asset(), q); ++m_accounts;
+ writeAccount(m_storage->liability(), q); ++m_accounts;
+ writeAccount(m_storage->expense(), q); ++m_accounts;
+ writeAccount(m_storage->income(), q); ++m_accounts;
+ writeAccount(m_storage->equity(), q); ++m_accounts;
+ CATCH
+ delete e;
+
+ // If the above failed, assume that the database is empty and create
+ // the standard accounts by hand before writing them.
+ MyMoneyAccount acc_l;
+ acc_l.setAccountType(MyMoneyAccount::Liability);
+ acc_l.setName("Liability");
+ MyMoneyAccount liability(STD_ACC_LIABILITY, acc_l);
+
+ MyMoneyAccount acc_a;
+ acc_a.setAccountType(MyMoneyAccount::Asset);
+ acc_a.setName("Asset");
+ MyMoneyAccount asset(STD_ACC_ASSET, acc_a);
+
+ MyMoneyAccount acc_e;
+ acc_e.setAccountType(MyMoneyAccount::Expense);
+ acc_e.setName("Expense");
+ MyMoneyAccount expense(STD_ACC_EXPENSE, acc_e);
+
+ MyMoneyAccount acc_i;
+ acc_i.setAccountType(MyMoneyAccount::Income);
+ acc_i.setName("Income");
+ MyMoneyAccount income(STD_ACC_INCOME, acc_i);
+
+ MyMoneyAccount acc_q;
+ acc_q.setAccountType(MyMoneyAccount::Equity);
+ acc_q.setName("Equity");
+ MyMoneyAccount equity(STD_ACC_EQUITY, acc_q);
+
+ writeAccount(asset, q); ++m_accounts;
+ writeAccount(expense, q); ++m_accounts;
+ writeAccount(income, q); ++m_accounts;
+ writeAccount(liability, q); ++m_accounts;
+ writeAccount(equity, q); ++m_accounts;
+ ECATCH
+
+ int i = 0;
+ MyMoneySqlQuery q2(this);
+ q.prepare (m_db.m_tables["kmmAccounts"].updateString());
+ q2.prepare (m_db.m_tables["kmmAccounts"].insertString());
+ // Update the accounts that exist; insert the ones that do not.
+ for(it = list.begin(); it != list.end(); ++it, ++i) {
+ m_transactionCountMap[(*it).id()] = m_storagePtr->transactionCount((*it).id());
+ if (dbList.contains((*it).id())) {
+ dbList.remove ((*it).id());
+ writeAccount(*it, q);
+ } else {
+ writeAccount(*it, q2);
+ }
+ signalProgress(++m_accounts, 0);
+ }
+
+ // Delete the accounts that are in the db but no longer in memory.
+ if (!dbList.isEmpty()) {
+ QValueList<QString>::const_iterator it = dbList.begin();
+ q.prepare("DELETE FROM kmmAccounts WHERE id = :id");
+ while (it != dbList.end()) {
+ if (!m_storagePtr->isStandardAccount(*it)) {
+ q.bindValue(":id", (*it));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Account"));
+ deleteKeyValuePairs("ACCOUNT", (*it));
+ deleteKeyValuePairs("ONLINEBANKING", (*it));
+ }
+ ++it;
+ }
+ }
+}
+
+void MyMoneyStorageSql::addAccount(const MyMoneyAccount& acc) {
+ DBG("*** Entering MyMoneyStorageSql::addAccount");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmAccounts"].insertString());
+ writeAccount(acc,q);
+ ++m_accounts;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::modifyAccount(const MyMoneyAccount& acc) {
+ DBG("*** Entering MyMoneyStorageSql::modifyAccount");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmAccounts"].updateString());
+ deleteKeyValuePairs("ACCOUNT", acc.id());
+ deleteKeyValuePairs("ONLINEBANKING", acc.id());
+ writeAccount(acc,q);
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::removeAccount(const MyMoneyAccount& acc) {
+ DBG("*** Entering MyMoneyStorageSql::removeAccount");
+ startCommitUnit(__func__);
+ deleteKeyValuePairs("ACCOUNT", acc.id());
+ deleteKeyValuePairs("ONLINEBANKING", acc.id());
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmAccounts"].deleteString());
+ q.bindValue(":id", acc.id());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting Account")));
+ --m_accounts;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::writeAccount(const MyMoneyAccount& acc, MyMoneySqlQuery& q) {
+ DBG("*** Entering MyMoneyStorageSql::writeAccount");
+ //MyMoneyMoney balance = m_storagePtr->balance(acc.id(), QDate());
+ q.bindValue(":id", acc.id());
+ q.bindValue(":institutionId", acc.institutionId());
+ q.bindValue(":parentId", acc.parentAccountId());
+ if (acc.lastReconciliationDate() == QDate())
+ q.bindValue(":lastReconciled", acc.lastReconciliationDate());
+ else
+ q.bindValue(":lastReconciled", acc.lastReconciliationDate().toString(Qt::ISODate));
+
+ q.bindValue(":lastModified", acc.lastModified());
+ if (acc.openingDate() == QDate())
+ q.bindValue(":openingDate", acc.openingDate());
+ else
+ q.bindValue(":openingDate", acc.openingDate().toString(Qt::ISODate));
+
+ q.bindValue(":accountNumber", acc.number());
+ q.bindValue(":accountType", acc.accountType());
+ q.bindValue(":accountTypeString", MyMoneyAccount::accountTypeToString(acc.accountType()));
+ if (acc.accountType() == MyMoneyAccount::Stock) {
+ q.bindValue(":isStockAccount", "Y");
+ } else {
+ q.bindValue(":isStockAccount", "N");
+ }
+ q.bindValue(":accountName", acc.name());
+ q.bindValue(":description", acc.description());
+ q.bindValue(":currencyId", acc.currencyId());
+
+ // This section attempts to get the balance from the database, if possible
+ // That way, the balance fields are kept in sync. If that fails, then
+ // It is assumed that the account actually knows its correct balance.
+
+ //FIXME: Using exceptions for branching always feels like a kludge.
+ // Look for a better way.
+ TRY
+ MyMoneyMoney bal = m_storagePtr->balance(acc.id(), QDate());
+ q.bindValue(":balance", bal.toString());
+ q.bindValue(":balanceFormatted",
+ bal.formatMoney("", -1, false));
+ CATCH
+ delete e;
+ q.bindValue(":balance", acc.balance().toString());
+ q.bindValue(":balanceFormatted",
+ acc.balance().formatMoney("", -1, false));
+ ECATCH
+
+ q.bindValue(":transactionCount", Q_ULLONG(m_transactionCountMap[acc.id()]));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Account")));
+
+ //Add in Key-Value Pairs for accounts.
+ //MMAccount inherits from KVPContainer AND has a KVPContainer member
+ //so handle both
+ writeKeyValuePairs("ACCOUNT", acc.id(), acc.pairs());
+ writeKeyValuePairs("ONLINEBANKING", acc.id(), acc.onlineBankingSettings().pairs());
+ m_hiIdAccounts = calcHighId(m_hiIdAccounts, acc.id());
+}
+
+// **** Transactions and Splits ****
+void MyMoneyStorageSql::writeTransactions() {
+ DBG("*** Entering MyMoneyStorageSql::writeTransactions");
+ // first, get a list of what's on the database (see writeInstitutions)
+ QValueList<QString> dbList;
+ MyMoneySqlQuery q(this);
+ q.prepare("SELECT id FROM kmmTransactions WHERE txType = 'N';");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "building Transaction list"));
+ while (q.next()) dbList.append(q.value(0).toString());
+
+ MyMoneyTransactionFilter filter;
+ filter.setReportAllSplits(false);
+ QValueList<MyMoneyTransaction> list;
+ m_storage->transactionList(list, filter);
+ signalProgress(0, list.count(), "Writing Transactions...");
+ QValueList<MyMoneyTransaction>::ConstIterator it;
+ int i = 0;
+ MyMoneySqlQuery q2(this);
+ q.prepare (m_db.m_tables["kmmTransactions"].updateString());
+ q2.prepare (m_db.m_tables["kmmTransactions"].insertString());
+ for(it = list.begin(); it != list.end(); ++it, ++i) {
+ if (dbList.contains((*it).id())) {
+ dbList.remove ((*it).id());
+ writeTransaction((*it).id(), *it, q, "N");
+ } else {
+ writeTransaction((*it).id(), *it, q2, "N");
+ }
+ signalProgress(++m_transactions, 0);
+ }
+
+ if (!dbList.isEmpty()) {
+ QValueList<QString>::const_iterator it = dbList.begin();
+ while (it != dbList.end()) {
+ deleteTransaction(*it);
+ ++it;
+ }
+ }
+}
+
+void MyMoneyStorageSql::addTransaction (const MyMoneyTransaction& tx) {
+ DBG("*** Entering MyMoneyStorageSql::addTransaction");
+ startCommitUnit(__func__);
+ // add the transaction and splits
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmTransactions"].insertString());
+ writeTransaction(tx.id(), tx, q, "N");
+ ++m_transactions;
+ // for each split account, update lastMod date, balance, txCount
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ for(it_s = tx.splits().begin(); it_s != tx.splits().end(); ++it_s) {
+ //MyMoneyAccount acc = m_storagePtr->account((*it_s).accountId());
+ MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId());
+ ++m_transactionCountMap[acc.id()];
+ modifyAccount(acc);
+ }
+ // in the fileinfo record, update lastMod, txCount, next TxId
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::modifyTransaction (const MyMoneyTransaction& tx) {
+ DBG("*** Entering MyMoneyStorageSql::modifyTransaction");
+ startCommitUnit(__func__);
+ // remove the splits of the old tx from the count table
+ MyMoneySqlQuery q(this);
+ q.prepare ("SELECT accountId FROM kmmSplits WHERE transactionId = :txId;");
+ q.bindValue(":txId", tx.id());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "retrieving old splits"));
+ while (q.next()) {
+ QString id = q.value(0).toCString();
+ --m_transactionCountMap[id];
+ }
+ // add the transaction and splits
+ q.prepare (m_db.m_tables["kmmTransactions"].updateString());
+ writeTransaction(tx.id(), tx, q, "N");
+ // for each split account, update lastMod date, balance, txCount
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ for(it_s = tx.splits().begin(); it_s != tx.splits().end(); ++it_s) {
+ //MyMoneyAccount acc = m_storagePtr->account((*it_s).accountId());
+ MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId());
+ ++m_transactionCountMap[acc.id()];
+ modifyAccount(acc);
+ }
+ writeSplits(tx.id(), "N", tx.splits());
+ // in the fileinfo record, update lastMod
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::removeTransaction(const MyMoneyTransaction& tx) {
+ DBG("*** Entering MyMoneyStorageSql::removeTransaction");
+ startCommitUnit(__func__);
+ deleteTransaction(tx.id());
+ --m_transactions;
+
+ // for each split account, update lastMod date, balance, txCount
+ QValueList<MyMoneySplit>::ConstIterator it_s;
+ for(it_s = tx.splits().begin(); it_s != tx.splits().end(); ++it_s) {
+ MyMoneyAccount acc = m_storagePtr->account((*it_s).accountId());
+ --m_transactionCountMap[acc.id()];
+ modifyAccount(acc);
+ }
+ // in the fileinfo record, update lastModDate, txCount
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::deleteTransaction(const QString& id) {
+ DBG("*** Entering MyMoneyStorageSql::deleteTransaction");
+ MyMoneySqlQuery q(this);
+ q.prepare("DELETE FROM kmmSplits WHERE transactionId = :transactionId;");
+ q.bindValue(":transactionId", id);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Splits"));
+
+ q.prepare ("DELETE FROM kmmKeyValuePairs WHERE kvpType = 'SPLIT' "
+ "AND kvpId LIKE '" + id + "%'");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Splits KVP"));
+
+ m_splits -= q.numRowsAffected();
+ deleteKeyValuePairs("TRANSACTION", id);
+ q.prepare(m_db.m_tables["kmmTransactions"].deleteString());
+ q.bindValue(":id", id);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Transaction"));
+}
+
+void MyMoneyStorageSql::writeTransaction(const QString& txId, const MyMoneyTransaction& tx, MyMoneySqlQuery& q, const QString& type) {
+ DBG("*** Entering MyMoneyStorageSql::writeTransaction");
+ q.bindValue(":id", txId);
+ q.bindValue(":txType", type);
+ q.bindValue(":postDate", tx.postDate().toString(Qt::ISODate));
+ q.bindValue(":memo", tx.memo());
+ q.bindValue(":entryDate", tx.entryDate().toString(Qt::ISODate));
+ q.bindValue(":currencyId", tx.commodity());
+ q.bindValue(":bankId", tx.bankID());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Transaction")));
+
+ m_txPostDate = tx.postDate(); // FIXME: TEMP till Tom puts date in split object
+ QValueList<MyMoneySplit> splitList = tx.splits();
+ writeSplits(txId, type, splitList);
+
+ //Add in Key-Value Pairs for transactions.
+ deleteKeyValuePairs("TRANSACTION", txId);
+ writeKeyValuePairs("TRANSACTION", txId, tx.pairs());
+ m_hiIdTransactions = calcHighId(m_hiIdTransactions, tx.id());
+}
+
+void MyMoneyStorageSql::writeSplits(const QString& txId, const QString& type, const QValueList<MyMoneySplit>& splitList) {
+ DBG("*** Entering MyMoneyStorageSql::writeSplits");
+ // first, get a list of what's on the database (see writeInstitutions)
+ QValueList<unsigned int> dbList;
+ MyMoneySqlQuery q(this);
+ q.prepare("SELECT splitId FROM kmmSplits where transactionId = :id;");
+ q.bindValue(":id", txId);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "building Split list"));
+ while (q.next()) dbList.append(q.value(0).toUInt());
+
+ QValueList<MyMoneySplit>::const_iterator it;
+ unsigned int i;
+ MyMoneySqlQuery q2(this);
+ q.prepare (m_db.m_tables["kmmSplits"].updateString());
+ q2.prepare (m_db.m_tables["kmmSplits"].insertString());
+ for(it = splitList.begin(), i = 0; it != splitList.end(); ++it, ++i) {
+ if (dbList.contains(i)) {
+ dbList.remove (i);
+ writeSplit(txId, (*it), type, i, q);
+ } else {
+ ++m_splits;
+ writeSplit(txId, (*it), type, i, q2);
+ }
+ }
+
+ if (!dbList.isEmpty()) {
+ q.prepare("DELETE FROM kmmSplits WHERE transactionId = :txId AND splitId = :splitId");
+ QValueList<unsigned int>::const_iterator it = dbList.begin();
+ while (it != dbList.end()) {
+ q.bindValue(":txId", txId);
+ q.bindValue(":splitId", *it);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Splits"));
+ ++it;
+ }
+ }
+}
+
+void MyMoneyStorageSql::writeSplit(const QString& txId, const MyMoneySplit& split,
+ const QString& type, const int splitId, MyMoneySqlQuery& q) {
+ DBG("*** Entering MyMoneyStorageSql::writeSplit");
+ q.bindValue(":transactionId", txId);
+ q.bindValue(":txType", type);
+ q.bindValue(":splitId", splitId);
+ q.bindValue(":payeeId", split.payeeId());
+ if (split.reconcileDate() == QDate())
+ q.bindValue(":reconcileDate", split.reconcileDate());
+ else
+ q.bindValue(":reconcileDate", split.reconcileDate().toString(Qt::ISODate));
+ q.bindValue(":action", split.action());
+ q.bindValue(":reconcileFlag", split.reconcileFlag());
+ q.bindValue(":value", split.value().toString());
+ q.bindValue(":valueFormatted", split.value()
+ .formatMoney("", -1, false)
+ .replace(QChar(','), QChar('.')));
+ q.bindValue(":shares", split.shares().toString());
+ MyMoneyAccount acc = m_storagePtr->account(split.accountId());
+ MyMoneySecurity sec = m_storagePtr->security(acc.currencyId());
+ q.bindValue(":sharesFormatted",
+ split.shares().
+ formatMoney("", MyMoneyMoney::denomToPrec(sec.smallestAccountFraction()), false).
+ replace(QChar(','), QChar('.')));
+ MyMoneyMoney price = split.actualPrice();
+ if (!price.isZero()) {
+ q.bindValue(":price", price.toString());
+ q.bindValue(":priceFormatted", price.formatMoney
+ ("", KMyMoneySettings::pricePrecision(), false)
+ .replace(QChar(','), QChar('.')));
+ } else {
+ q.bindValue(":price", QString());
+ q.bindValue(":priceFormatted", QString());
+ }
+ q.bindValue(":memo", split.memo());
+ q.bindValue(":accountId", split.accountId());
+ q.bindValue(":checkNumber", split.number());
+ q.bindValue(":postDate", m_txPostDate.toString(Qt::ISODate)); // FIXME: when Tom puts date into split object
+ q.bindValue(":bankId", split.bankID());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Split")));
+ deleteKeyValuePairs("SPLIT", txId + QString::number(splitId));
+ writeKeyValuePairs("SPLIT", txId + QString::number(splitId), split.pairs());
+}
+
+// **** Schedules ****
+void MyMoneyStorageSql::writeSchedules() {
+ DBG("*** Entering MyMoneyStorageSql::writeSchedules");
+ // first, get a list of what's on the database (see writeInstitutions)
+ QValueList<QString> dbList;
+ MyMoneySqlQuery q(this);
+ q.prepare("SELECT id FROM kmmSchedules;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "building Schedule list"));
+ while (q.next()) dbList.append(q.value(0).toString());
+
+ const QValueList<MyMoneySchedule> list = m_storage->scheduleList();
+ QValueList<MyMoneySchedule>::ConstIterator it;
+ MyMoneySqlQuery q2(this);
+ //TODO: find a way to prepare the queries outside of the loop. writeSchedule()
+ // modifies the query passed to it, so they have to be re-prepared every pass.
+ signalProgress(0, list.count(), "Writing Schedules...");
+ for(it = list.begin(); it != list.end(); ++it) {
+ q.prepare (m_db.m_tables["kmmSchedules"].updateString());
+ q2.prepare (m_db.m_tables["kmmSchedules"].insertString());
+ bool insert = true;
+ if (dbList.contains((*it).id())) {
+ dbList.remove ((*it).id());
+ insert = false;
+ writeSchedule(*it, q, insert);
+ } else {
+ writeSchedule(*it, q2, insert);
+ }
+ signalProgress(++m_schedules, 0);
+ }
+
+ if (!dbList.isEmpty()) {
+ QValueList<QString>::const_iterator it = dbList.begin();
+ while (it != dbList.end()) {
+ deleteSchedule(*it);
+ ++it;
+ }
+ }
+}
+
+void MyMoneyStorageSql::addSchedule(const MyMoneySchedule& sched) {
+ DBG("*** Entering MyMoneyStorageSql::addSchedule");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmSchedules"].insertString());
+ writeSchedule(sched,q, true);
+ ++m_schedules;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::modifySchedule(const MyMoneySchedule& sched) {
+ DBG("*** Entering MyMoneyStorageSql::modifySchedule");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmSchedules"].updateString());
+ writeSchedule(sched,q, false);
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::removeSchedule(const MyMoneySchedule& sched) {
+ DBG("*** Entering MyMoneyStorageSql::removeSchedule");
+ startCommitUnit(__func__);
+ deleteSchedule(sched.id());
+ --m_schedules;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::deleteSchedule (const QString& id) {
+ DBG("*** Entering MyMoneyStorageSql::deleteSchedule");
+ deleteTransaction(id);
+ MyMoneySqlQuery q(this);
+ q.prepare("DELETE FROM kmmSchedulePaymentHistory WHERE schedId = :id");
+ q.bindValue(":id", id);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Schedule Payment History"));
+ q.prepare(m_db.m_tables["kmmSchedules"].deleteString());
+ q.bindValue(":id", id);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Schedule"));
+ //FIXME: enable when schedules have KVPs.
+ //deleteKeyValuePairs("SCHEDULE", id);
+}
+
+void MyMoneyStorageSql::writeSchedule(const MyMoneySchedule& sch, MyMoneySqlQuery& q, bool insert) {
+ DBG("*** Entering MyMoneyStorageSql::writeSchedule");
+ q.bindValue(":id", sch.id());
+ q.bindValue(":name", sch.name());
+ q.bindValue(":type", sch.type());
+ q.bindValue(":typeString", MyMoneySchedule::scheduleTypeToString(sch.type()));
+ q.bindValue(":occurence", sch.occurencePeriod());
+ q.bindValue(":occurenceMultiplier", sch.occurenceMultiplier());
+ q.bindValue(":occurenceString", sch.occurenceToString());
+ q.bindValue(":paymentType", sch.paymentType());
+ q.bindValue(":paymentTypeString", MyMoneySchedule::paymentMethodToString(sch.paymentType()));
+ q.bindValue(":startDate", sch.startDate().toString(Qt::ISODate));
+ q.bindValue(":endDate", sch.endDate().toString(Qt::ISODate));
+ if (sch.isFixed()) {
+ q.bindValue(":fixed", "Y");
+ } else {
+ q.bindValue(":fixed", "N");
+ }
+ if (sch.autoEnter()) {
+ q.bindValue(":autoEnter", "Y");
+ } else {
+ q.bindValue(":autoEnter", "N");
+ }
+ q.bindValue(":lastPayment", sch.lastPayment());
+ q.bindValue(":nextPaymentDue", sch.nextDueDate().toString(Qt::ISODate));
+ q.bindValue(":weekendOption", sch.weekendOption());
+ q.bindValue(":weekendOptionString", MyMoneySchedule::weekendOptionToString(sch.weekendOption()));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Schedules")));
+
+ //store the payment history for this scheduled task.
+ //easiest way is to delete all and re-insert; it's not a high use table
+ q.prepare("DELETE FROM kmmSchedulePaymentHistory WHERE schedId = :id;");
+ q.bindValue(":id", sch.id());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting Schedule Payment History")));
+
+ q.prepare (m_db.m_tables["kmmSchedulePaymentHistory"].insertString());
+ QValueList<QDate> payments = sch.recordedPayments();
+ QValueList<QDate>::ConstIterator it;
+ for (it=payments.begin(); it!=payments.end(); ++it) {
+ q.bindValue(":schedId", sch.id());
+ q.bindValue(":payDate", (*it).toString(Qt::ISODate));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Schedule Payment History")));
+ }
+
+ //store the transaction data for this task.
+ if (!insert) {
+ q.prepare (m_db.m_tables["kmmTransactions"].updateString());
+ } else {
+ q.prepare (m_db.m_tables["kmmTransactions"].insertString());
+ }
+ writeTransaction(sch.id(), sch.transaction(), q, "S");
+
+ //FIXME: enable when schedules have KVPs.
+
+ //Add in Key-Value Pairs for transactions.
+ //deleteKeyValuePairs("SCHEDULE", sch.id());
+ //writeKeyValuePairs("SCHEDULE", sch.id(), sch.pairs());
+ m_hiIdSchedules = calcHighId(m_hiIdSchedules, sch.id());
+}
+
+// **** Securities ****
+void MyMoneyStorageSql::writeSecurities() {
+ DBG("*** Entering MyMoneyStorageSql::writeSecurities");
+ // first, get a list of what's on the database (see writeInstitutions)
+ QValueList<QString> dbList;
+ MyMoneySqlQuery q(this);
+ MyMoneySqlQuery q2(this);
+ q.prepare("SELECT id FROM kmmSecurities;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "building security list"));
+ while (q.next()) dbList.append(q.value(0).toString());
+
+ const QValueList<MyMoneySecurity> securityList = m_storage->securityList();
+ signalProgress(0, securityList.count(), "Writing Securities...");
+ q.prepare (m_db.m_tables["kmmSecurities"].updateString());
+ q2.prepare (m_db.m_tables["kmmSecurities"].insertString());
+ for(QValueList<MyMoneySecurity>::ConstIterator it = securityList.begin(); it != securityList.end(); ++it) {
+ if (dbList.contains((*it).id())) {
+ dbList.remove ((*it).id());
+ writeSecurity((*it), q);
+ } else {
+ writeSecurity((*it), q2);
+ }
+ signalProgress(++m_securities, 0);
+ }
+
+ if (!dbList.isEmpty()) {
+ q.prepare("DELETE FROM kmmSecurities WHERE id = :id");
+ q2.prepare("DELETE FROM kmmPrices WHERE fromId = :id OR toId = :id");
+ QValueList<QString>::const_iterator it = dbList.begin();
+ while (it != dbList.end()) {
+ q.bindValue(":id", (*it));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Security"));
+ q2.bindValue(":fromId", (*it));
+ q2.bindValue(":toId", (*it));
+ if (!q2.exec()) throw new MYMONEYEXCEPTION(buildError (q2, __func__, "deleting Security"));
+ deleteKeyValuePairs("SECURITY", (*it));
+ ++it;
+ }
+ }
+}
+
+void MyMoneyStorageSql::addSecurity(const MyMoneySecurity& sec) {
+ DBG("*** Entering MyMoneyStorageSql::addSecurity");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmSecurities"].insertString());
+ writeSecurity(sec,q);
+ ++m_securities;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::modifySecurity(const MyMoneySecurity& sec) {
+ DBG("*** Entering MyMoneyStorageSql::modifySecurity");
+ startCommitUnit(__func__);
+ deleteKeyValuePairs("SECURITY", sec.id());
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmSecurities"].updateString());
+ writeSecurity(sec,q);
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::removeSecurity(const MyMoneySecurity& sec) {
+ DBG("*** Entering MyMoneyStorageSql::removeSecurity");
+ startCommitUnit(__func__);
+ deleteKeyValuePairs("SECURITY", sec.id());
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmSecurities"].deleteString());
+ q.bindValue(":id", sec.id());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting Security")));
+ --m_securities;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::writeSecurity(const MyMoneySecurity& security, MyMoneySqlQuery& q) {
+ DBG("*** Entering MyMoneyStorageSql::writeSecurity");
+ q.bindValue(":id", security.id());
+ q.bindValue(":name", security.name());
+ q.bindValue(":symbol", security.tradingSymbol());
+ q.bindValue(":type", static_cast<int>(security.securityType()));
+ q.bindValue(":typeString", MyMoneySecurity::securityTypeToString(security.securityType()));
+ q.bindValue(":smallestAccountFraction", security.smallestAccountFraction());
+ q.bindValue(":tradingCurrency", security.tradingCurrency());
+ q.bindValue(":tradingMarket", security.tradingMarket());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString ("writing Securities")));
+
+ //Add in Key-Value Pairs for security
+ writeKeyValuePairs("SECURITY", security.id(), security.pairs());
+ m_hiIdSecurities = calcHighId(m_hiIdSecurities, security.id());
+}
+
+// **** Prices ****
+void MyMoneyStorageSql::writePrices() {
+ DBG("*** Entering MyMoneyStorageSql::writePrices");
+ // due to difficulties in matching and determining deletes
+ // easiest way is to delete all and re-insert
+ MyMoneySqlQuery q(this);
+ q.prepare("DELETE FROM kmmPrices");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting Prices")));
+ m_prices = 0;
+
+ const MyMoneyPriceList list = m_storage->priceList();
+ signalProgress(0, list.count(), "Writing Prices...");
+ MyMoneyPriceList::ConstIterator it;
+ for(it = list.begin(); it != list.end(); ++it) {
+ writePricePair(*it);
+ }
+}
+
+void MyMoneyStorageSql::writePricePair(const MyMoneyPriceEntries& p) {
+ DBG("*** Entering MyMoneyStorageSql::writePricePair");
+ MyMoneyPriceEntries::ConstIterator it;
+ for(it = p.begin(); it != p.end(); ++it) {
+ writePrice (*it);
+ signalProgress(++m_prices, 0);
+ }
+}
+
+void MyMoneyStorageSql::addPrice(const MyMoneyPrice& p) {
+ DBG("*** Entering MyMoneyStorageSql::addPrice");
+ if (m_readingPrices) return;
+ // the app always calls addPrice, whether or not there is already one there
+ startCommitUnit(__func__);
+ bool newRecord = false;
+ MyMoneySqlQuery q(this);
+ QString s = m_db.m_tables["kmmPrices"].selectAllString(false);
+ s += " WHERE fromId = :fromId AND toId = :toId AND priceDate = :priceDate;";
+ q.prepare (s);
+ q.bindValue(":fromId", p.from());
+ q.bindValue(":toId", p.to());
+ q.bindValue(":priceDate", p.date().toString(Qt::ISODate));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("finding Price")));
+ if (q.next()) {
+ q.prepare(m_db.m_tables["kmmPrices"].updateString());
+ } else {
+ q.prepare(m_db.m_tables["kmmPrices"].insertString());
+ ++m_prices;
+ newRecord = true;
+ }
+ q.bindValue(":fromId", p.from());
+ q.bindValue(":toId", p.to());
+ q.bindValue(":priceDate", p.date().toString(Qt::ISODate));
+ q.bindValue(":price", p.rate(QString()).toString());
+ q.bindValue(":priceFormatted",
+ p.rate(QString()).formatMoney("", KMyMoneySettings::pricePrecision()));
+ q.bindValue(":priceSource", p.source());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Price")));
+
+ if (newRecord) writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::removePrice(const MyMoneyPrice& p) {
+ DBG("*** Entering MyMoneyStorageSql::removePrice");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmPrices"].deleteString());
+ q.bindValue(":fromId", p.from());
+ q.bindValue(":toId", p.to());
+ q.bindValue(":priceDate", p.date().toString(Qt::ISODate));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting Price")));
+ --m_prices;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::writePrice(const MyMoneyPrice& p) {
+ DBG("*** Entering MyMoneyStorageSql::writePrice");
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmPrices"].insertString());
+ q.bindValue(":fromId", p.from());
+ q.bindValue(":toId", p.to());
+ q.bindValue(":priceDate", p.date().toString(Qt::ISODate));
+ q.bindValue(":price", p.rate(QString()).toString());
+ q.bindValue(":priceFormatted", p.rate(QString()).formatMoney("", 2));
+ q.bindValue(":priceSource", p.source());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Prices")));
+}
+
+// **** Currencies ****
+void MyMoneyStorageSql::writeCurrencies() {
+ DBG("*** Entering MyMoneyStorageSql::writeCurrencies");
+ // first, get a list of what's on the database (see writeInstitutions)
+ QValueList<QString> dbList;
+ MyMoneySqlQuery q(this);
+ MyMoneySqlQuery q2(this);
+ q.prepare("SELECT ISOCode FROM kmmCurrencies;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "building Currency list"));
+ while (q.next()) dbList.append(q.value(0).toString());
+
+ const QValueList<MyMoneySecurity> currencyList = m_storage->currencyList();
+ signalProgress(0, currencyList.count(), "Writing Currencies...");
+ q.prepare (m_db.m_tables["kmmCurrencies"].updateString());
+ q2.prepare (m_db.m_tables["kmmCurrencies"].insertString());
+ for(QValueList<MyMoneySecurity>::ConstIterator it = currencyList.begin(); it != currencyList.end(); ++it) {
+ if (dbList.contains((*it).id())) {
+ dbList.remove ((*it).id());
+ writeCurrency((*it), q);
+ } else {
+ writeCurrency((*it), q2);
+ }
+ signalProgress(++m_currencies, 0);
+ }
+
+ if (!dbList.isEmpty()) {
+ q.prepare("DELETE FROM kmmCurrencies WHERE ISOCode = :ISOCode");
+ QValueList<QString>::const_iterator it = dbList.begin();
+ while (it != dbList.end()) {
+ q.bindValue(":ISOCode", (*it));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Currency"));
+ ++it;
+ }
+ }
+}
+
+void MyMoneyStorageSql::addCurrency(const MyMoneySecurity& sec) {
+ DBG("*** Entering MyMoneyStorageSql::addCurrency");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmCurrencies"].insertString());
+ writeCurrency(sec,q);
+ ++m_currencies;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::modifyCurrency(const MyMoneySecurity& sec) {
+ DBG("*** Entering MyMoneyStorageSql::modifyCurrency");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmCurrencies"].updateString());
+ writeCurrency(sec,q);
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::removeCurrency(const MyMoneySecurity& sec) {
+ DBG("*** Entering MyMoneyStorageSql::removeCurrency");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmCurrencies"].deleteString());
+ q.bindValue(":ISOcode", sec.id());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting Currency")));
+ --m_currencies;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::writeCurrency(const MyMoneySecurity& currency, MyMoneySqlQuery& q) {
+ DBG("*** Entering MyMoneyStorageSql::writeCurrency");
+ q.bindValue(":ISOcode", currency.id());
+ q.bindValue(":name", currency.name());
+ q.bindValue(":type", static_cast<int>(currency.securityType()));
+ q.bindValue(":typeString", MyMoneySecurity::securityTypeToString(currency.securityType()));
+ // writing the symbol as three short ints is a PITA, but the
+ // problem is that database drivers have incompatible ways of declaring UTF8
+ QString symbol = currency.tradingSymbol() + " ";
+ q.bindValue(":symbol1", symbol.mid(0,1).unicode()->unicode());
+ q.bindValue(":symbol2", symbol.mid(1,1).unicode()->unicode());
+ q.bindValue(":symbol3", symbol.mid(2,1).unicode()->unicode());
+ q.bindValue(":symbolString", symbol);
+ q.bindValue(":partsPerUnit", currency.partsPerUnit());
+ q.bindValue(":smallestCashFraction", currency.smallestCashFraction());
+ q.bindValue(":smallestAccountFraction", currency.smallestAccountFraction());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Currencies")));
+}
+
+
+void MyMoneyStorageSql::writeReports() {
+ DBG("*** Entering MyMoneyStorageSql::writeReports");
+ // first, get a list of what's on the database (see writeInstitutions)
+ QValueList<QString> dbList;
+ MyMoneySqlQuery q(this);
+ MyMoneySqlQuery q2(this);
+ q.prepare("SELECT id FROM kmmReportConfig;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "building Report list"));
+ while (q.next()) dbList.append(q.value(0).toString());
+
+ QValueList<MyMoneyReport> list = m_storage->reportList();
+ signalProgress(0, list.count(), "Writing Reports...");
+ QValueList<MyMoneyReport>::ConstIterator it;
+ q.prepare (m_db.m_tables["kmmReportConfig"].updateString());
+ q2.prepare (m_db.m_tables["kmmReportConfig"].insertString());
+ for(it = list.begin(); it != list.end(); ++it){
+ if (dbList.contains((*it).id())) {
+ dbList.remove ((*it).id());
+ writeReport(*it, q);
+ } else {
+ writeReport(*it, q2);
+ }
+ signalProgress(++m_reports, 0);
+ }
+
+ if (!dbList.isEmpty()) {
+ q.prepare("DELETE FROM kmmReportConfig WHERE id = :id");
+ QValueList<QString>::const_iterator it = dbList.begin();
+ while (it != dbList.end()) {
+ q.bindValue(":id", (*it));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Report"));
+ ++it;
+ }
+ }
+}
+
+void MyMoneyStorageSql::addReport(const MyMoneyReport& rep) {
+ DBG("*** Entering MyMoneyStorageSql::addReport");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmReportConfig"].insertString());
+ writeReport(rep,q);
+ ++m_reports;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::modifyReport(const MyMoneyReport& rep) {
+ DBG("*** Entering MyMoneyStorageSql::modifyReport");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmReportConfig"].updateString());
+ writeReport(rep,q);
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::removeReport(const MyMoneyReport& rep) {
+ DBG("*** Entering MyMoneyStorageSql::removeReport");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare("DELETE FROM kmmReportConfig WHERE id = :id");
+ q.bindValue(":id", rep.id());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting Report")));
+ --m_reports;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::writeReport (const MyMoneyReport& rep, MyMoneySqlQuery& q) {
+ DBG("*** Entering MyMoneyStorageSql::writeReport");
+ QDomDocument d; // create a dummy XML document
+ QDomElement e = d.createElement("REPORTS");
+ d.appendChild (e);
+ rep.writeXML(d, e); // write the XML to document
+ q.bindValue(":id", rep.id());
+ q.bindValue(":name", rep.name());
+ q.bindValue(":XML", d.toString());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Reports")));
+ //m_hiIdReports = calcHighId(m_hiIdReports, rep.id());
+}
+
+void MyMoneyStorageSql::writeBudgets() {
+ DBG("*** Entering MyMoneyStorageSql::writeBudgets");
+ // first, get a list of what's on the database (see writeInstitutions)
+ QValueList<QString> dbList;
+ MyMoneySqlQuery q(this);
+ MyMoneySqlQuery q2(this);
+ q.prepare("SELECT name FROM kmmBudgetConfig;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "building Budget list"));
+ while (q.next()) dbList.append(q.value(0).toString());
+
+ QValueList<MyMoneyBudget> list = m_storage->budgetList();
+ signalProgress(0, list.count(), "Writing Budgets...");
+ QValueList<MyMoneyBudget>::ConstIterator it;
+ q.prepare (m_db.m_tables["kmmBudgetConfig"].updateString());
+ q2.prepare (m_db.m_tables["kmmBudgetConfig"].insertString());
+ for(it = list.begin(); it != list.end(); ++it){
+ if (dbList.contains((*it).name())) {
+ dbList.remove ((*it).name());
+ writeBudget(*it, q);
+ } else {
+ writeBudget(*it, q2);
+ }
+ signalProgress(++m_budgets, 0);
+ }
+
+ if (!dbList.isEmpty()) {
+ q.prepare("DELETE FROM kmmBudgetConfig WHERE id = :id");
+ QValueList<QString>::const_iterator it = dbList.begin();
+ while (it != dbList.end()) {
+ q.bindValue(":name", (*it));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "deleting Budget"));
+ ++it;
+ }
+ }
+}
+
+void MyMoneyStorageSql::addBudget(const MyMoneyBudget& bud) {
+ DBG("*** Entering MyMoneyStorageSql::addBudget");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmBudgetConfig"].insertString());
+ writeBudget(bud,q);
+ ++m_budgets;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::modifyBudget(const MyMoneyBudget& bud) {
+ DBG("*** Entering MyMoneyStorageSql::modifyBudget");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmBudgetConfig"].updateString());
+ writeBudget(bud,q);
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::removeBudget(const MyMoneyBudget& bud) {
+ DBG("*** Entering MyMoneyStorageSql::removeBudget");
+ startCommitUnit(__func__);
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmBudgetConfig"].deleteString());
+ q.bindValue(":id", bud.id());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting Budget")));
+ --m_budgets;
+ writeFileInfo();
+ endCommitUnit(__func__);
+}
+
+void MyMoneyStorageSql::writeBudget (const MyMoneyBudget& bud, MyMoneySqlQuery& q) {
+ DBG("*** Entering MyMoneyStorageSql::writeBudget");
+ QDomDocument d; // create a dummy XML document
+ QDomElement e = d.createElement("BUDGETS");
+ d.appendChild (e);
+ bud.writeXML(d, e); // write the XML to document
+ q.bindValue(":id", bud.id());
+ q.bindValue(":name", bud.name());
+ q.bindValue(":start", bud.budgetStart());
+ q.bindValue(":XML", d.toString());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing Budgets")));
+}
+
+void MyMoneyStorageSql::writeFileInfo() {
+ DBG("*** Entering MyMoneyStorageSql::writeFileInfo");
+ // we have no real way of knowing when these change, so re-write them every time
+ deleteKeyValuePairs("STORAGE", "");
+ writeKeyValuePairs("STORAGE", "", m_storage->pairs());
+ //
+ MyMoneySqlQuery q(this);
+ q.prepare ("SELECT * FROM kmmFileInfo;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, "checking fileinfo"));
+ QString qs;
+ if (q.next())
+ qs = m_db.m_tables["kmmFileInfo"].updateString();
+ else
+ qs = (m_db.m_tables["kmmFileInfo"].insertString());
+ q.prepare(qs);
+ q.bindValue(":version", m_dbVersion);
+ q.bindValue(":fixLevel", m_storage->fileFixVersion());
+ q.bindValue(":created", m_storage->creationDate().toString(Qt::ISODate));
+ //q.bindValue(":lastModified", m_storage->lastModificationDate().toString(Qt::ISODate));
+ q.bindValue(":lastModified", QDate::currentDate().toString(Qt::ISODate));
+ q.bindValue(":baseCurrency", m_storage->pairs()["kmm-baseCurrency"]);
+ q.bindValue(":institutions", (unsigned long long) m_institutions);
+ q.bindValue(":accounts", (unsigned long long) m_accounts);
+ q.bindValue(":payees", (unsigned long long) m_payees);
+ q.bindValue(":transactions", (unsigned long long) m_transactions);
+ q.bindValue(":splits", (unsigned long long) m_splits);
+ q.bindValue(":securities", (unsigned long long) m_securities);
+ q.bindValue(":prices", (unsigned long long) m_prices);
+ q.bindValue(":currencies", (unsigned long long) m_currencies);
+ q.bindValue(":schedules", (unsigned long long) m_schedules);
+ q.bindValue(":reports", (unsigned long long) m_reports);
+ q.bindValue(":kvps", (unsigned long long) m_kvps);
+ q.bindValue(":budgets", (unsigned long long) m_budgets);
+ q.bindValue(":dateRangeStart", QDate());
+ q.bindValue(":dateRangeEnd", QDate());
+
+ //FIXME: This modifies all m_<variable> used in this function.
+ // Sometimes the memory has been updated.
+
+ // Should most of these be tracked in a view?
+ // Variables actually needed are: version, fileFixVersion, creationDate,
+ // baseCurrency, encryption, update info, and logon info.
+ try {
+ //readFileInfo();
+ } catch (...) {
+ startCommitUnit(__func__);
+ }
+
+ q.bindValue(":hiInstitutionId", (unsigned long long) m_hiIdInstitutions);
+ q.bindValue(":hiPayeeId", (unsigned long long) m_hiIdPayees);
+ q.bindValue(":hiAccountId", (unsigned long long) m_hiIdAccounts);
+ q.bindValue(":hiTransactionId", (unsigned long long) m_hiIdTransactions);
+ q.bindValue(":hiScheduleId", (unsigned long long) m_hiIdSchedules);
+ q.bindValue(":hiSecurityId", (unsigned long long) m_hiIdSecurities);
+ q.bindValue(":hiReportId", (unsigned long long) m_hiIdReports);
+ q.bindValue(":hiBudgetId", (unsigned long long) m_hiIdBudgets);
+
+ q.bindValue(":encryptData", m_encryptData);
+ q.bindValue(":updateInProgress", "N");
+ q.bindValue(":logonUser", m_logonUser);
+ q.bindValue(":logonAt", m_logonAt.toString(Qt::ISODate));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing FileInfo")));
+}
+
+// **** Key/value pairs ****
+void MyMoneyStorageSql::writeKeyValuePairs(const QString& kvpType, const QString& kvpId, const QMap<QString, QString>& pairs) {
+ DBG("*** Entering MyMoneyStorageSql::writeKeyValuePairs");
+ QMap<QString, QString>::const_iterator it;
+ for(it = pairs.begin(); it != pairs.end(); ++it) {
+ writeKeyValuePair (kvpType, kvpId, it.key(), it.data());
+ }
+}
+
+void MyMoneyStorageSql::writeKeyValuePair (const QString& kvpType, const QString& kvpId, const QString& kvpKey, const QString& kvpData) {
+ DBG("*** Entering MyMoneyStorageSql::writeKeyValuePair");
+ MyMoneySqlQuery q(this);
+ q.prepare (m_db.m_tables["kmmKeyValuePairs"].insertString());
+ q.bindValue(":kvpType", kvpType);
+ q.bindValue(":kvpId", kvpId);
+ q.bindValue(":kvpKey", kvpKey);
+ q.bindValue(":kvpData", kvpData);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("writing KVP")));
+ ++m_kvps;
+}
+
+void MyMoneyStorageSql::deleteKeyValuePairs (const QString& kvpType, const QString& kvpId) {
+ DBG("*** Entering MyMoneyStorageSql::deleteKeyValuePairs");
+ MyMoneySqlQuery q(this);
+ q.prepare ("DELETE FROM kmmKeyValuePairs WHERE kvpType = :kvpType AND kvpId = :kvpId;");
+ q.bindValue(":kvpType", kvpType);
+ q.bindValue(":kvpId", kvpId);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("deleting kvp for %1 %2").arg(kvpType).arg(kvpId)));
+ m_kvps -= q.numRowsAffected();
+}
+
+//******************************** read SQL routines **************************************
+#define CASE(a) if ((*ft)->name() == #a)
+#define GETSTRING q.value(i).toString()
+#define GETCSTRING q.value(i).toCString()
+#define GETDATE getDate(GETSTRING)
+#define GETDATETIME getDateTime(GETSTRING)
+#define GETINT q.value(i).toInt()
+#define GETULL q.value(i).toULongLong()
+
+void MyMoneyStorageSql::readFileInfo(void) {
+ DBG("*** Entering MyMoneyStorageSql::readFileInfo");
+ signalProgress(0, 18, QObject::tr("Loading file information..."));
+ MyMoneyDbTable& t = m_db.m_tables["kmmFileInfo"];
+ MyMoneySqlQuery q(this);
+ q.prepare (t.selectAllString());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading FileInfo")));
+ if (!q.next()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("retrieving FileInfo")));
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ while (ft != t.end()) {
+ // versioning is now handled in open routine
+/* CASE(version) setVersion(GETSTRING); // check version == current version...
+ else*/
+ CASE(created) m_storage->setCreationDate(GETDATE);
+ else CASE(lastModified) m_storage->setLastModificationDate(GETDATE);
+ else CASE(hiInstitutionId) m_hiIdInstitutions = (unsigned long) GETULL;
+ else CASE(hiPayeeId) m_hiIdPayees = (unsigned long) GETULL;
+ else CASE(hiAccountId) m_hiIdAccounts = (unsigned long) GETULL;
+ else CASE(hiTransactionId) m_hiIdTransactions = (unsigned long) GETULL;
+ else CASE(hiScheduleId) m_hiIdSchedules = (unsigned long) GETULL;
+ else CASE(hiSecurityId) m_hiIdSecurities = (unsigned long) GETULL;
+ else CASE(hiReportId ) m_hiIdReports = (unsigned long) GETULL;
+ else CASE(hiBudgetId ) m_hiIdBudgets = (unsigned long) GETULL;
+ else CASE(institutions) m_institutions = (unsigned long) GETULL;
+ else CASE(accounts ) m_accounts = (unsigned long) GETULL;
+ else CASE(payees ) m_payees = (unsigned long) GETULL;
+ else CASE(transactions) m_transactions = (unsigned long) GETULL;
+ else CASE(splits ) m_splits = (unsigned long) GETULL;
+ else CASE(securities ) m_securities = (unsigned long) GETULL;
+ else CASE(currencies ) m_currencies = (unsigned long) GETULL;
+ else CASE(schedules ) m_schedules = (unsigned long) GETULL;
+ else CASE(prices ) m_prices = (unsigned long) GETULL;
+ else CASE(kvps ) m_kvps = (unsigned long) GETULL;
+ else CASE(reports ) m_reports = (unsigned long) GETULL;
+ else CASE(budgets ) m_budgets = (unsigned long) GETULL;
+ else CASE(encryptData) m_encryptData = GETSTRING;
+ else CASE(logonUser) m_logonUser = GETSTRING;
+ else CASE(logonAt) m_logonAt = GETDATETIME;
+ ++ft; ++i;
+ signalProgress(i,0);
+ }
+ m_storage->setPairs(readKeyValuePairs("STORAGE", QString("")).pairs());
+}
+
+/*void MyMoneyStorageSql::setVersion (const QString& version) {
+ DBG("*** Entering MyMoneyStorageSql::setVersion");
+ m_dbVersion = version.section('.', 0, 0).toUInt();
+ m_minorVersion = version.section('.', 1, 1).toUInt();
+ // Okay, I made a cockup by forgetting to include a fixversion in the database
+ // design, so we'll use the minor version as fix level (similar to VERSION
+ // and FIXVERSION in XML file format). A second mistake was setting minor version to 1
+ // in the first place, so we need to subtract one on reading and add one on writing (sigh)!!
+ m_storage->setFileFixVersion( m_minorVersion - 1);
+}*/
+
+void MyMoneyStorageSql::readInstitutions(void) {
+ TRY
+ QMap<QString, MyMoneyInstitution> iList = fetchInstitutions();
+ m_storage->loadInstitutions(iList);
+ readFileInfo();
+ m_storage->loadInstitutionId(m_hiIdInstitutions);
+ PASS
+}
+
+const QMap<QString, MyMoneyInstitution> MyMoneyStorageSql::fetchInstitutions (const QStringList& idList, bool forUpdate) const {
+ DBG("*** Entering MyMoneyStorageSql::readInstitutions");
+ signalProgress(0, m_institutions, QObject::tr("Loading institutions..."));
+ int progress = 0;
+ QMap<QString, MyMoneyInstitution> iList;
+ unsigned long lastId = 0;
+ const MyMoneyDbTable& t = m_db.m_tables["kmmInstitutions"];
+ MyMoneySqlQuery sq(const_cast <MyMoneyStorageSql*> (this));
+ sq.prepare ("SELECT id from kmmAccounts where institutionId = :id");
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ QString queryString (t.selectAllString(false));
+
+ // Use bind variables, instead of just inserting the values in the queryString,
+ // so that values containing a ':' will work.
+ if (! idList.empty()) {
+ queryString += " WHERE";
+ for (unsigned i = 0; i < idList.count(); ++i)
+ queryString += " id = :id" + QString::number(i) + " OR";
+ queryString = queryString.left(queryString.length() - 2);
+ }
+ if (forUpdate)
+ queryString += " FOR UPDATE";
+
+ queryString += ";";
+
+ q.prepare (queryString);
+
+ if (! idList.empty()) {
+ QStringList::const_iterator bindVal = idList.begin();
+ for (int i = 0; bindVal != idList.end(); ++i, ++bindVal) {
+ q.bindValue (":id" + QString::number(i), *bindVal);
+ }
+ }
+
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Institution")));
+ while (q.next()) {
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ QString iid;
+ MyMoneyInstitution inst;
+ while (ft != t.end()) {
+ CASE(id) iid = GETSTRING;
+ else CASE(name) inst.setName(GETSTRING);
+ else CASE(manager) inst.setManager(GETSTRING);
+ else CASE(routingCode) inst.setSortcode(GETSTRING);
+ else CASE(addressStreet) inst.setStreet(GETSTRING);
+ else CASE(addressCity) inst.setCity(GETSTRING);
+ else CASE(addressZipcode) inst.setPostcode(GETSTRING);
+ else CASE(telephone) inst.setTelephone(GETSTRING);
+ ++ft; ++i;
+ }
+ // get list of subaccounts
+ sq.bindValue(":id", iid);
+ if (!sq.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Institution AccountList")));
+ QStringList aList;
+ while (sq.next()) aList.append(sq.value(0).toString());
+ for (QStringList::ConstIterator it = aList.begin(); it != aList.end(); ++it)
+ inst.addAccountId(*it);
+
+ iList[iid] = MyMoneyInstitution(iid, inst);
+ unsigned long id = extractId(iid);
+ if(id > lastId)
+ lastId = id;
+
+ signalProgress (++progress, 0);
+ }
+ return iList;
+}
+
+void MyMoneyStorageSql::readPayees (const QString& id) {
+ DBG("*** Entering MyMoneyStorageSql::readPayees");
+ QValueList<QString> list;
+ list.append(id);
+ readPayees(list);
+}
+
+void MyMoneyStorageSql::readPayees(const QValueList<QString> pid) {
+ DBG("*** Entering MyMoneyStorageSql::readPayees");
+ TRY
+ QStringList pidList;
+ qCopy(pid.begin(), pid.end(), qBackInserter(pidList));
+
+ m_storage->loadPayees(fetchPayees(pidList));
+ readFileInfo();
+ m_storage->loadPayeeId(m_hiIdPayees);
+ CATCH
+ delete e; // ignore duplicates
+ ECATCH
+// if (pid.isEmpty()) m_payeeListRead = true;
+}
+
+const QMap<QString, MyMoneyPayee> MyMoneyStorageSql::fetchPayees (const QStringList& idList, bool /*forUpdate*/) const {
+ DBG("*** Entering MyMoneyStorageSql::readPayees");
+ if (m_displayStatus) {
+ signalProgress(0, m_payees, QObject::tr("Loading payees..."));
+ } else {
+// if (m_payeeListRead) return;
+ }
+ int progress = 0;
+ QMap<QString, MyMoneyPayee> pList;
+ //unsigned long lastId;
+ const MyMoneyDbTable& t = m_db.m_tables["kmmPayees"];
+ MyMoneyDbTable::field_iterator payeeEnd = t.end();
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ if (idList.isEmpty()) {
+ q.prepare (t.selectAllString());
+ } else {
+ QString whereClause = " where (";
+ QString itemConnector = "";
+ QStringList::ConstIterator it;
+ for (it = idList.begin(); it != idList.end(); ++it) {
+ whereClause.append(QString("%1id = '%2'").arg(itemConnector).arg(*it));
+ itemConnector = " or ";
+ }
+ whereClause += ")";
+ q.prepare (t.selectAllString(false) + whereClause);
+ }
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Payee")));
+ while (q.next()) {
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ QString pid;
+ QString boolChar;
+ MyMoneyPayee payee;
+ unsigned int type;
+ bool ignoreCase;
+ QString matchKeys;
+ while (ft != payeeEnd) {
+ CASE(id) pid = GETCSTRING;
+ else CASE(name) payee.setName(GETSTRING);
+ else CASE(reference) payee.setReference(GETSTRING);
+ else CASE(email) payee.setEmail(GETSTRING);
+ else CASE(addressStreet) payee.setAddress(GETSTRING);
+ else CASE(addressCity) payee.setCity(GETSTRING);
+ else CASE(addressZipcode) payee.setPostcode(GETSTRING);
+ else CASE(addressState) payee.setState(GETSTRING);
+ else CASE(telephone) payee.setTelephone(GETSTRING);
+ else CASE(notes) payee.setNotes(GETSTRING);
+ else CASE(defaultAccountId) payee.setDefaultAccountId(GETSTRING);
+ else CASE(matchData) type = GETINT;
+ else CASE(matchIgnoreCase) ignoreCase = (GETSTRING == "Y");
+ else CASE(matchKeys) matchKeys = GETSTRING;
+ ++ft; ++i;
+ }
+ payee.setMatchData (static_cast<MyMoneyPayee::payeeMatchType>(type), ignoreCase, matchKeys);
+ if (pid == "USER") {
+ TRY
+ m_storage->setUser(payee);
+ PASS
+ } else {
+ pList[pid] = MyMoneyPayee(pid, payee);
+ //unsigned long id = extractId(QString(pid));
+ //if(id > lastId)
+ // lastId = id;
+ }
+ if (m_displayStatus) signalProgress(++progress, 0);
+ }
+ return pList;
+}
+
+const QMap<QString, MyMoneyAccount> MyMoneyStorageSql::fetchAccounts (const QStringList& idList, bool forUpdate) const {
+ DBG("*** Entering MyMoneyStorageSql::fetchAccounts");
+ signalProgress(0, m_accounts, QObject::tr("Loading accounts..."));
+ int progress = 0;
+ QMap<QString, MyMoneyAccount> accList;
+ QStringList kvpAccountList;
+
+ const MyMoneyDbTable& t = m_db.m_tables["kmmAccounts"];
+ MyMoneyDbTable::field_iterator accEnd = t.end();
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ MyMoneySqlQuery sq(const_cast <MyMoneyStorageSql*> (this));
+
+ QString childQueryString = "SELECT id, parentId FROM kmmAccounts WHERE ";
+ QString queryString (t.selectAllString(false));
+
+ // Use bind variables, instead of just inserting the values in the queryString,
+ // so that values containing a ':' will work.
+ if (! idList.empty()) {
+ kvpAccountList = idList;
+ queryString += " WHERE id IN (";
+ childQueryString += " parentId IN (";
+ for (unsigned i = 0; i < idList.count(); ++i) {
+ queryString += " :id" + QString::number(i) + ", ";
+ childQueryString += ":id" + QString::number(i) + ", ";
+ }
+ queryString = queryString.left(queryString.length() - 2) + ")";
+ childQueryString = childQueryString.left(childQueryString.length() - 2) + ")";
+ } else {
+ childQueryString += " NOT parentId IS NULL";
+ }
+
+ queryString += " ORDER BY id";
+ childQueryString += " ORDER BY parentid, id";
+
+ if (forUpdate) {
+ queryString += " FOR UPDATE";
+ childQueryString += " FOR UPDATE";
+ }
+
+ q.prepare (queryString);
+ sq.prepare (childQueryString);
+
+ if (! idList.empty()) {
+ QStringList::const_iterator bindVal = idList.begin();
+ for (int i = 0; bindVal != idList.end(); ++i, ++bindVal) {
+ q.bindValue (":id" + QString::number(i), *bindVal);
+ sq.bindValue (":id" + QString::number(i), *bindVal);
+ }
+ }
+
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Account")));
+ if (!sq.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading subAccountList")));
+ while (q.next()) {
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ QString aid;
+ QString balance;
+ MyMoneyAccount acc;
+
+ while (ft != accEnd) {
+ CASE(id) aid = GETCSTRING;
+ else CASE(institutionId) acc.setInstitutionId(GETCSTRING);
+ else CASE(parentId) acc.setParentAccountId(GETCSTRING);
+ else CASE(lastReconciled) acc.setLastReconciliationDate(GETDATE);
+ else CASE(lastModified) acc.setLastModified(GETDATE);
+ else CASE(openingDate) acc.setOpeningDate(GETDATE);
+ else CASE(accountNumber) acc.setNumber(GETSTRING);
+ else CASE(accountType) acc.setAccountType(static_cast<MyMoneyAccount::accountTypeE>(GETINT));
+ else CASE(accountName) acc.setName(GETSTRING);
+ else CASE(description) acc.setDescription(GETSTRING);
+ else CASE(currencyId) acc.setCurrencyId(GETCSTRING);
+ else CASE(balance) acc.setBalance(GETSTRING);
+ else CASE(transactionCount)
+ const_cast <MyMoneyStorageSql*> (this)->m_transactionCountMap[aid] = (unsigned long) GETULL;
+ ++ft; ++i;
+ }
+
+ // Process any key value pair
+ if (idList.empty())
+ kvpAccountList.append(aid);
+
+ // in database mode, load the balance from the account record
+ // else we would need to read all the transactions
+ accList.insert(aid, MyMoneyAccount(aid, acc));
+ if (acc.value("PreferredAccount") == "Yes") {
+ const_cast <MyMoneyStorageSql*> (this)->m_preferred.addAccount(aid);
+ }
+ signalProgress(++progress, 0);
+ }
+
+ QMapIterator<QString, MyMoneyAccount> it_acc;
+ QMapIterator<QString, MyMoneyAccount> accListEnd = accList.end();
+ while (sq.next()) {
+ it_acc = accList.find(sq.value(1).toString());
+ if (it_acc != accListEnd && it_acc.data().id() == sq.value(1).toString()) {
+ while (sq.isValid() && it_acc != accListEnd
+ && it_acc.data().id() == sq.value(1).toString()) {
+ it_acc.data().addAccountId(sq.value(0).toString());
+ sq.next();
+ }
+ sq.prev();
+ }
+ }
+
+ //TODO: There should be a better way than this. What's below is O(n log n) or more,
+ // where it may be able to be done in O(n), if things are just right.
+ // The operator[] call in the loop is the most expensive call in this function, according
+ // to several profile runs.
+ QMap <QString, MyMoneyKeyValueContainer> kvpResult = readKeyValuePairs("ACCOUNT", kvpAccountList);
+ QMap <QString, MyMoneyKeyValueContainer>::const_iterator kvp_end = kvpResult.end();
+ for (QMap <QString, MyMoneyKeyValueContainer>::const_iterator it_kvp = kvpResult.begin();
+ it_kvp != kvp_end; ++it_kvp) {
+ accList[it_kvp.key()].setPairs(it_kvp.data().pairs());
+ }
+
+ kvpResult = readKeyValuePairs("ONLINEBANKING", kvpAccountList);
+ kvp_end = kvpResult.end();
+ for (QMap <QString, MyMoneyKeyValueContainer>::const_iterator it_kvp = kvpResult.begin();
+ it_kvp != kvp_end; ++it_kvp) {
+ accList[it_kvp.key()].setOnlineBankingSettings(it_kvp.data());
+ }
+
+ return accList;
+}
+
+void MyMoneyStorageSql::readAccounts(void) {
+ m_storage->loadAccounts(fetchAccounts());
+ m_storage->loadAccountId(m_hiIdAccounts);
+}
+
+const QMap<QString, MyMoneyMoney> MyMoneyStorageSql::fetchBalance(const QStringList& idList, const QDate& date) const {
+
+ QMap<QString, MyMoneyMoney> returnValue;
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ QString queryString = "SELECT action, shares, accountId, postDate "
+ "FROM kmmSplits WHERE txType = 'N' AND accountId in (";
+
+ for (unsigned i = 0; i < idList.count(); ++i) {
+ queryString += " :id" + QString::number(i) + ", ";
+ }
+ queryString = queryString.left(queryString.length() - 2) + " )";
+
+ // SQLite stores dates as YYYY-MM-DDTHH:mm:ss with 0s for the time part. This makes
+ // the <= operator misbehave when the date matches. To avoid this, add a day to the
+ // requested date and use the < operator.
+ if (date.isValid() && !date.isNull())
+ queryString += QString(" AND postDate < '%1'").arg(date.addDays(1).toString(Qt::ISODate));
+ DBG (queryString);
+ q.prepare(queryString);
+
+ QStringList::const_iterator bindVal = idList.begin();
+ for (int i = 0; bindVal != idList.end(); ++i, ++bindVal) {
+ q.bindValue (":id" + QString::number(i), *bindVal);
+ returnValue[*bindVal] = MyMoneyMoney(0);
+ }
+ if (!q.exec())
+ throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("fetching balance")));
+ QString id;
+ QString shares;
+ QString action;
+ while (q.next()) {
+ id = q.value(2).toString();
+ shares = q.value(1).toString();
+ action = q.value(0).toString();
+ if (MyMoneySplit::ActionSplitShares == action)
+ returnValue[id] = returnValue[id] * MyMoneyMoney(shares);
+ else
+ returnValue[id] += MyMoneyMoney(shares);
+ }
+ return returnValue;
+}
+
+void MyMoneyStorageSql::readTransactions(const QString& tidList, const QString& dateClause) {
+ TRY
+ m_storage->loadTransactions(fetchTransactions(tidList, dateClause));
+ m_storage->loadTransactionId(m_hiIdTransactions);
+ PASS
+}
+
+void MyMoneyStorageSql::readTransactions(const MyMoneyTransactionFilter& filter) {
+ TRY
+ m_storage->loadTransactions(fetchTransactions(filter));
+ m_storage->loadTransactionId(m_hiIdTransactions);
+ PASS
+}
+
+const QMap<QString, MyMoneyTransaction> MyMoneyStorageSql::fetchTransactions (const QString& tidList, const QString& dateClause, bool /*forUpdate*/) const {
+ DBG("*** Entering MyMoneyStorageSql::readTransactions");
+// if (m_transactionListRead) return; // all list already in memory
+ if (m_displayStatus) signalProgress(0, m_transactions, QObject::tr("Loading transactions..."));
+ int progress = 0;
+// m_payeeList.clear();
+ QString whereClause;
+ whereClause = " WHERE txType = 'N' ";
+ if (! tidList.isEmpty()) {
+ whereClause += " AND id IN " + tidList;
+ }
+ if (!dateClause.isEmpty()) whereClause += " and " + dateClause;
+ const MyMoneyDbTable& t = m_db.m_tables["kmmTransactions"];
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ q.prepare (t.selectAllString(false) + whereClause + " ORDER BY id;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Transaction")));
+ const MyMoneyDbTable& ts = m_db.m_tables["kmmSplits"];
+ whereClause = " WHERE txType = 'N' ";
+ if (! tidList.isEmpty()) {
+ whereClause += " AND transactionId IN " + tidList;
+ }
+ if (!dateClause.isEmpty()) whereClause += " and " + dateClause;
+ MyMoneySqlQuery qs(const_cast <MyMoneyStorageSql*> (this));
+ QString splitQuery = ts.selectAllString(false) + whereClause
+ + " ORDER BY transactionId, splitId;";
+ qs.prepare (splitQuery);
+ if (!qs.exec()) throw new MYMONEYEXCEPTION(buildError (qs, __func__, "reading Splits"));
+ QString splitTxId = "ZZZ";
+ MyMoneySplit s;
+ if (qs.next()) {
+ splitTxId = qs.value(0).toString();
+ readSplit (s, qs, ts);
+ } else {
+ splitTxId = "ZZZ";
+ }
+ QMap <QString, MyMoneyTransaction> txMap;
+ QStringList txList;
+ MyMoneyDbTable::field_iterator txEnd = t.end();
+ while (q.next()) {
+ MyMoneyTransaction tx;
+ QString txId;
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ while (ft != txEnd) {
+ CASE(id) txId = GETSTRING;
+ else CASE(postDate) tx.setPostDate(GETDATE);
+ else CASE(memo) tx.setMemo(GETSTRING);
+ else CASE(entryDate) tx.setEntryDate(GETDATE);
+ else CASE(currencyId) tx.setCommodity(GETCSTRING);
+ else CASE(bankId) tx.setBankID(GETSTRING);
+ ++ft; ++i;
+ }
+
+ while (txId < splitTxId && splitTxId != "ZZZ") {
+ if (qs.next()) {
+ splitTxId = qs.value(0).toString();
+ readSplit (s, qs, ts);
+ } else {
+ splitTxId = "ZZZ";
+ }
+ }
+
+ while (txId == splitTxId) {
+ tx.addSplit (s);
+ if (qs.next()) {
+ splitTxId = qs.value(0).toString();
+ readSplit (s, qs, ts);
+ } else {
+ splitTxId = "ZZZ";
+ }
+ }
+ // Process any key value pair
+ if (! txId.isEmpty()) {
+ txList.append(txId);
+ tx = MyMoneyTransaction(txId, tx);
+ txMap.insert(tx.uniqueSortKey(), tx);
+ }
+ }
+ QMap <QString, MyMoneyKeyValueContainer> kvpMap = readKeyValuePairs("TRANSACTION", txList);
+ QMap<QString, MyMoneyTransaction> tList;
+ QMapIterator<QString, MyMoneyTransaction> txMapEnd = txMap.end();
+ for (QMapIterator<QString, MyMoneyTransaction> i = txMap.begin();
+ i != txMapEnd; ++i) {
+ i.data().setPairs(kvpMap[i.data().id()].pairs());
+
+ if (m_displayStatus) signalProgress(++progress, 0);
+ }
+
+ if ((tidList.isEmpty()) && (dateClause.isEmpty())) {
+ //qDebug("setting full list read");
+ }
+ return txMap;
+}
+
+int MyMoneyStorageSql::splitState(const MyMoneyTransactionFilter::stateOptionE& state) const
+{
+ int rc = MyMoneySplit::NotReconciled;
+
+ switch(state) {
+ default:
+ case MyMoneyTransactionFilter::notReconciled:
+ break;
+
+ case MyMoneyTransactionFilter::cleared:
+ rc = MyMoneySplit::Cleared;
+ break;
+
+ case MyMoneyTransactionFilter::reconciled:
+ rc = MyMoneySplit::Reconciled;
+ break;
+
+ case MyMoneyTransactionFilter::frozen:
+ rc = MyMoneySplit::Frozen;
+ break;
+ }
+ return rc;
+}
+
+const QMap<QString, MyMoneyTransaction> MyMoneyStorageSql::fetchTransactions (const MyMoneyTransactionFilter& filter) const {
+ DBG("*** Entering MyMoneyStorageSql::readTransactions");
+ // analyze the filter
+// if (m_transactionListRead) return; // all list already in memory
+ // if the filter is restricted to certain accounts/categories
+ // check if we already have them all in memory
+ QStringList accounts;
+ QString inQuery;
+ filter.accounts(accounts);
+ filter.categories(accounts);
+// QStringList::iterator it;
+// bool allAccountsLoaded = true;
+// for (it = accounts.begin(); it != accounts.end(); ++it) {
+// if (m_accountsLoaded.find(*it) == m_accountsLoaded.end()) {
+// allAccountsLoaded = false;
+// break;
+// }
+// }
+// if (allAccountsLoaded) return;
+ /* Some filter combinations do not lend themselves to implementation
+ * in SQL, or are likely to require such extensive reading of the database
+ * as to make it easier to just read everything into memory. */
+ bool canImplementFilter = true;
+ MyMoneyMoney m1, m2;
+ if (filter.amountFilter( m1, m2 )) {
+ alert ("Amount Filter Set");
+ canImplementFilter = false;
+ }
+ QString n1, n2;
+ if (filter.numberFilter(n1, n2)) {
+ alert("Number filter set");
+ canImplementFilter = false;
+ }
+ int t1;
+ if (filter.firstType(t1)) {
+ alert("Type filter set");
+ canImplementFilter = false;
+ }
+// int s1;
+// if (filter.firstState(s1)) {
+// alert("State filter set");
+// canImplementFilter = false;
+// }
+ QRegExp t2;
+ if (filter.textFilter(t2)) {
+ alert("text filter set");
+ canImplementFilter = false;
+ }
+ MyMoneyTransactionFilter::FilterSet s = filter.filterSet();
+ if (s.singleFilter.validityFilter) {
+ alert("Validity filter set");
+ canImplementFilter = false;
+ }
+ if (!canImplementFilter) {
+ QMap<QString, MyMoneyTransaction> transactionList = fetchTransactions();
+ QMap<QString, MyMoneyTransaction>::ConstIterator it_t;
+ QMap<QString, MyMoneyTransaction>::ConstIterator txListEnd = transactionList.end();
+
+ std::remove_if(transactionList.begin(), transactionList.end(), FilterFail(filter, m_storagePtr));
+ return transactionList;
+ }
+
+ bool accountsOnlyFilter = true;
+ bool splitFilterActive = false; // the split filter is active if we are selecting on fields in the split table
+ // get start and end dates
+ QDate start = filter.fromDate();
+ QDate end = filter.toDate();
+ // not entirely sure if the following is correct, but at best, saves a lot of reads, at worst
+ // it only causes us to read a few more transactions that strictly necessary (I think...)
+ if (start == KMyMoneySettings::startDate().date()) start = QDate();
+ bool txFilterActive = ((start != QDate()) || (end != QDate())); // and this for fields in the transaction table
+ if (txFilterActive) accountsOnlyFilter = false;
+
+ QString whereClause = "";
+ QString subClauseconnector = " where txType = 'N' and ";
+ // payees
+ QStringList payees;
+ //filter.payees(payees);
+ if (filter.payees(payees)) {
+ accountsOnlyFilter = false;
+ QString itemConnector = "payeeId in (";
+ QString payeesClause = "";
+ QStringList::const_iterator it;
+ for (it = payees.begin(); it != payees.end(); ++it) {
+ payeesClause.append(QString("%1'%2'")
+ .arg(itemConnector).arg(*it));
+ itemConnector = ", ";
+ }
+ if (!payeesClause.isEmpty()) {
+ whereClause += subClauseconnector + payeesClause + ")";
+ subClauseconnector = " and ";
+ }
+ splitFilterActive = true;
+ }
+
+ // accounts and categories
+ if (!accounts.isEmpty()) {
+ splitFilterActive = true;
+ QString itemConnector = "accountId in (";
+ QString accountsClause = "";
+ QStringList::const_iterator it;
+ for (it = accounts.begin(); it != accounts.end(); ++it) {
+// if (m_accountsLoaded.find(*it) == m_accountsLoaded.end()) {
+ accountsClause.append(QString("%1 '%2'")
+ .arg(itemConnector).arg(*it));
+ itemConnector = ", ";
+ //if (accountsOnlyFilter) m_accountsLoaded.append(*it); // a bit premature...
+// }
+ }
+ if (!accountsClause.isEmpty()) {
+ whereClause += subClauseconnector + accountsClause + ")";
+ subClauseconnector = " and (";
+ }
+ }
+
+ // split states
+ QValueList <int> splitStates;
+ if (filter.states(splitStates)) {
+ splitFilterActive = true;
+ QString itemConnector = " reconcileFlag IN (";
+ QString statesClause = "";
+ for (QValueList<int>::ConstIterator it = splitStates.begin(); it != splitStates.end(); ++it) {
+ statesClause.append(QString(" %1 '%2'")
+ .arg(itemConnector)
+ .arg(splitState(MyMoneyTransactionFilter::stateOptionE(*it))));
+ itemConnector = ",";
+ }
+ if (!statesClause.isEmpty()) {
+ whereClause += subClauseconnector + statesClause + ")";
+ subClauseconnector = " and (";
+ }
+ }
+ // I've given up trying to work out the logic. we keep getting the wrong number of close brackets
+ int obc = whereClause.contains('(');
+ int cbc = whereClause.contains(')');
+ if (cbc > obc) {
+ qFatal("invalid where clause - %s", whereClause.latin1());
+ }
+ while (cbc < obc) {
+ whereClause.append(")");
+ cbc++;
+ }
+ // if the split filter is active, but the where clause is empty
+ // it means we already have all the transactions for the specified filter
+ // in memory, so just exit
+ if ((splitFilterActive) && (whereClause.isEmpty())) {
+ qDebug("all transactions already in storage");
+ return fetchTransactions();
+ }
+
+ // if we have neither a split filter, nor a tx (date) filter
+ // it's effectively a read all
+ if ((!splitFilterActive) && (!txFilterActive)) {
+ //qDebug("reading all transactions");
+ return fetchTransactions();
+ }
+ // build a date clause for the transaction table
+ QString dateClause;
+ QString connector = "";
+ if (end != QDate()) {
+ dateClause = QString("(postDate < '%1')").arg(end.addDays(1).toString(Qt::ISODate));
+ connector = " and ";
+ }
+ if (start != QDate()) {
+ dateClause += QString("%1 (postDate >= '%2')").arg(connector).arg(start.toString(Qt::ISODate));
+ }
+ // now get a list of transaction ids
+ // if we have only a date filter, we need to build the list from the tx table
+ // otherwise we need to build from the split table
+ if (splitFilterActive) {
+ inQuery = QString("(select distinct transactionId from kmmSplits %1)").arg(whereClause);
+ } else {
+ inQuery = QString("(select distinct id from kmmTransactions where %1)").arg(dateClause);
+ txFilterActive = false; // kill off the date filter now
+ }
+
+ return fetchTransactions(inQuery, dateClause);
+ //FIXME: if we have an accounts-only filter, recalc balances on loaded accounts
+}
+
+unsigned long MyMoneyStorageSql::transactionCount (const QString& aid) const {
+ DBG("*** Entering MyMoneyStorageSql::transactionCount");
+ if (aid.length() == 0)
+ return m_transactions;
+ else
+ return m_transactionCountMap[aid];
+}
+
+void MyMoneyStorageSql::readSplit (MyMoneySplit& s, const MyMoneySqlQuery& q, const MyMoneyDbTable& t) const {
+ DBG("*** Entering MyMoneyStorageSql::readSplit");
+ s.clearId();
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ MyMoneyDbTable::field_iterator splitEnd = t.end();
+ int i = 0;
+
+ // Use the QString here instead of CASE, since this is called so often.
+ QString fieldName;
+ while (ft != splitEnd) {
+ fieldName = (*ft)->name();
+ if (fieldName == "payeeId") s.setPayeeId(GETCSTRING);
+ else if (fieldName == "reconcileDate") s.setReconcileDate(GETDATE);
+ else if (fieldName == "action") s.setAction(GETCSTRING);
+ else if (fieldName == "reconcileFlag") s.setReconcileFlag(static_cast<MyMoneySplit::reconcileFlagE>(GETINT));
+ else if (fieldName == "value") s.setValue(MyMoneyMoney(QStringEmpty(GETSTRING)));
+ else if (fieldName == "shares") s.setShares(MyMoneyMoney(QStringEmpty(GETSTRING)));
+ else if (fieldName == "price") s.setPrice(MyMoneyMoney(QStringEmpty(GETSTRING)));
+ else if (fieldName == "memo") s.setMemo(GETSTRING);
+ else if (fieldName == "accountId") s.setAccountId(GETCSTRING);
+ else if (fieldName == "checkNumber") s.setNumber(GETSTRING);
+ //else if (fieldName == "postDate") s.setPostDate(GETDATETIME); // FIXME - when Tom puts date into split object
+ else if (fieldName == "bankId") s.setBankID(GETSTRING);
+ ++ft; ++i;
+ }
+
+ return;
+}
+
+bool MyMoneyStorageSql::isReferencedByTransaction(const QString& id) const {
+ DBG("*** Entering MyMoneyStorageSql::isReferencedByTransaction");
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ q.prepare("SELECT COUNT(*) FROM kmmTransactions "
+ "INNER JOIN kmmSplits ON kmmTransactions.id = kmmSplits.transactionId "
+ "WHERE kmmTransactions.currencyId = :ID OR kmmSplits.payeeId = :ID "
+ "OR kmmSplits.accountId = :ID");
+ q.bindValue(":ID", id);
+ if ((!q.exec()) || (!q.next())) {
+ buildError (q, __func__, "error retrieving reference count");
+ qFatal("Error retrieving reference count"); // definitely shouldn't happen
+ }
+ return (0 != q.value(0).toULongLong());
+}
+
+void MyMoneyStorageSql::readSchedules(void) {
+
+ TRY
+ m_storage->loadSchedules(fetchSchedules());
+ readFileInfo();
+ m_storage->loadScheduleId(m_hiIdSchedules);
+ PASS
+}
+
+const QMap<QString, MyMoneySchedule> MyMoneyStorageSql::fetchSchedules (const QStringList& idList, bool forUpdate) const {
+ DBG("*** Entering MyMoneyStorageSql::readSchedules");
+ signalProgress(0, m_schedules, QObject::tr("Loading schedules..."));
+ int progress = 0;
+ const MyMoneyDbTable& t = m_db.m_tables["kmmSchedules"];
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ QMap<QString, MyMoneySchedule> sList;
+ //unsigned long lastId = 0;
+ const MyMoneyDbTable& ts = m_db.m_tables["kmmSplits"];
+ MyMoneySqlQuery qs(const_cast <MyMoneyStorageSql*> (this));
+ qs.prepare (ts.selectAllString(false) + " WHERE transactionId = :id ORDER BY splitId;");
+ MyMoneySqlQuery sq(const_cast <MyMoneyStorageSql*> (this));
+ sq.prepare ("SELECT payDate from kmmSchedulePaymentHistory where schedId = :id");
+
+ QString queryString (t.selectAllString(false));
+
+ // Use bind variables, instead of just inserting the values in the queryString,
+ // so that values containing a ':' will work.
+ if (! idList.empty()) {
+ queryString += " WHERE";
+ for (unsigned i = 0; i < idList.count(); ++i)
+ queryString += " id = :id" + QString::number(i) + " OR";
+ queryString = queryString.left(queryString.length() - 2);
+ }
+ queryString += " ORDER BY id;";
+
+ if (forUpdate)
+ queryString += " FOR UPDATE";
+
+ queryString += ";";
+
+ q.prepare (queryString);
+
+ if (! idList.empty()) {
+ QStringList::const_iterator bindVal = idList.begin();
+ for (int i = 0; bindVal != idList.end(); ++i, ++bindVal) {
+ q.bindValue (":id" + QString::number(i), *bindVal);
+ }
+ }
+
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Schedules")));
+ while (q.next()) {
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ MyMoneySchedule s;
+ QString sId;
+ QString boolChar;
+ QDate nextPaymentDue;
+ while (ft != t.end()) {
+ CASE(id) sId = GETCSTRING;
+ else CASE(name) s.setName (GETSTRING);
+ else CASE(type) s.setType (static_cast<MyMoneySchedule::typeE>(GETINT));
+ else CASE(occurence) s.setOccurencePeriod (static_cast<MyMoneySchedule::occurenceE>(GETINT));
+ else CASE(occurenceMultiplier) s.setOccurenceMultiplier (GETINT);
+ else CASE(paymentType) s.setPaymentType (static_cast<MyMoneySchedule::paymentTypeE>(GETINT));
+ else CASE(startDate) s.setStartDate (GETDATE);
+ else CASE(endDate) s.setEndDate (GETDATE);
+ else CASE(fixed) {boolChar = GETSTRING; s.setFixed (boolChar == "Y");}
+ else CASE(autoEnter) {boolChar = GETSTRING; s.setAutoEnter (boolChar == "Y");}
+ else CASE(lastPayment) s.setLastPayment (GETDATE);
+ else CASE(weekendOption)
+ s.setWeekendOption (static_cast<MyMoneySchedule::weekendOptionE>(GETINT));
+ else CASE(nextPaymentDue) nextPaymentDue = GETDATE;
+ ++ft; ++i;
+ }
+ // convert simple occurence to compound occurence
+ int mult = s.occurenceMultiplier();
+ MyMoneySchedule::occurenceE occ = s.occurencePeriod();
+ MyMoneySchedule::simpleToCompoundOccurence(mult,occ);
+ s.setOccurencePeriod(occ);
+ s.setOccurenceMultiplier(mult);
+ // now assign the id to the schedule
+ MyMoneySchedule _s(sId, s);
+ s = _s;
+ // read the associated transaction
+// m_payeeList.clear();
+ const MyMoneyDbTable& t = m_db.m_tables["kmmTransactions"];
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ q.prepare (t.selectAllString(false) + " WHERE id = :id;");
+ q.bindValue(":id", s.id());
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Scheduled Transaction")));
+ if (!q.next()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("retrieving scheduled transaction")));
+ MyMoneyTransaction tx(s.id(), MyMoneyTransaction());
+ ft = t.begin();
+ i = 0;
+ while (ft != t.end()) {
+ CASE(postDate) tx.setPostDate(GETDATE);
+ else CASE(memo) tx.setMemo(GETSTRING);
+ else CASE(entryDate) tx.setEntryDate(GETDATE);
+ else CASE(currencyId) tx.setCommodity(GETCSTRING);
+ else CASE(bankId) tx.setBankID(GETSTRING);
+ ++ft; ++i;
+ }
+
+ qs.bindValue(":id", s.id());
+ if (!qs.exec()) throw new MYMONEYEXCEPTION(buildError (qs, __func__, "reading Scheduled Splits"));
+ while (qs.next()) {
+ MyMoneySplit sp;
+ readSplit (sp, qs, ts);
+ tx.addSplit (sp);
+ }
+// if (!m_payeeList.isEmpty())
+// readPayees(m_payeeList);
+ // Process any key value pair
+ tx.setPairs(readKeyValuePairs("TRANSACTION", s.id()).pairs());
+
+ // If the transaction doesn't have a post date, setTransaction will reject it.
+ // The old way of handling things was to store the next post date in the schedule object
+ // and set the transaction post date to QDate().
+ // For compatibility, if this is the case, copy the next post date from the schedule object
+ // to the transaction object post date.
+ if (!tx.postDate().isValid()) {
+ tx.setPostDate(nextPaymentDue);
+ }
+
+ s.setTransaction(tx);
+
+ // read in the recorded payments
+ sq.bindValue(":id", s.id());
+ if (!sq.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading schedule payment history")));
+ while (sq.next()) s.recordPayment (sq.value(0).toDate());
+
+ sList[s.id()] = s;
+
+ //FIXME: enable when schedules have KVPs.
+ // s.setPairs(readKeyValuePairs("SCHEDULE", s.id()).pairs());
+
+ //unsigned long id = extractId(s.id().data());
+ //if(id > lastId)
+ // lastId = id;
+
+ signalProgress(++progress, 0);
+ }
+ return sList;
+}
+
+void MyMoneyStorageSql::readSecurities(void) {
+ TRY
+ m_storage->loadSecurities(fetchSecurities());
+ readFileInfo();
+ m_storage->loadSecurityId(m_hiIdSecurities);
+ PASS
+}
+
+const QMap<QString, MyMoneySecurity> MyMoneyStorageSql::fetchSecurities (const QStringList& /*idList*/, bool /*forUpdate*/) const {
+ DBG("*** Entering MyMoneyStorageSql::readSecurities");
+ signalProgress(0, m_securities, QObject::tr("Loading securities..."));
+ int progress = 0;
+ QMap<QString, MyMoneySecurity> sList;
+ unsigned long lastId = 0;
+ const MyMoneyDbTable& t = m_db.m_tables["kmmSecurities"];
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ q.prepare (t.selectAllString(false) + " ORDER BY id;");
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Securities")));
+ while (q.next()) {
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ MyMoneySecurity e;
+ QString eid;
+ int saf = 0;
+ while (ft != t.end()) {
+ CASE(id) eid = GETSTRING;
+ else CASE(name) e.setName(GETSTRING);
+ else CASE(symbol) e.setTradingSymbol(GETSTRING);
+ else CASE(type) e.setSecurityType(static_cast<MyMoneySecurity::eSECURITYTYPE>(GETINT));
+ else CASE(smallestAccountFraction) saf = GETINT;
+ else CASE(tradingCurrency) e.setTradingCurrency(GETCSTRING);
+ else CASE(tradingMarket) e.setTradingMarket(GETSTRING);
+ ++ft; ++i;
+ }
+ if(e.tradingCurrency().isEmpty())
+ e.setTradingCurrency(m_storage->pairs()["kmm-baseCurrency"]);
+ if(saf == 0)
+ saf = 100;
+ e.setSmallestAccountFraction(saf);
+
+ // Process any key value pairs
+ e.setPairs(readKeyValuePairs("SECURITY", eid).pairs());
+ //tell the storage objects we have a new security object.
+
+ // FIXME: Adapt to new interface make sure, to take care of the currencies as well
+ // see MyMoneyStorageXML::readSecurites()
+ MyMoneySecurity security(eid,e);
+ sList[security.id()] = security;
+
+ unsigned long id = extractId(security.id());
+ if(id > lastId)
+ lastId = id;
+
+ signalProgress(++progress, 0);
+ }
+ return sList;
+}
+
+void MyMoneyStorageSql::readPrices(void) {
+
+ TRY
+// m_storage->addPrice(MyMoneyPrice(from, to, date, rate, source));
+ PASS
+
+}
+
+const MyMoneyPrice MyMoneyStorageSql::fetchSinglePrice (const QString& fromIdList, const QString& toIdList, const QDate& date_, bool exactDate, bool /*forUpdate*/) const {
+ DBG("*** Entering MyMoneyStorageSql::fetchSinglePrice");
+ const MyMoneyDbTable& t = m_db.m_tables["kmmPrices"];
+ MyMoneyDbTable::field_iterator tableEnd = t.end();
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ QString queryString = t.selectAllString(false);
+
+ // Use bind variables, instead of just inserting the values in the queryString,
+ // so that values containing a ':' will work.
+ // See balance query for why the date logic seems odd.
+ queryString += " WHERE fromId = :fromId AND toId = :toId AND priceDate < :priceDate ";
+ if (exactDate)
+ queryString += "AND priceDate > :exactDate ";
+
+ queryString += "ORDER BY priceDate DESC;";
+
+ q.prepare(queryString);
+
+ QDate date (date_);
+
+ if(!date.isValid())
+ date = QDate::currentDate();
+
+ q.bindValue(":fromId", fromIdList);
+ q.bindValue(":toId", toIdList);
+ q.bindValue(":priceDate", date.addDays(1).toString(Qt::ISODate));
+
+ if (exactDate)
+ q.bindValue(":exactDate", date.toString(Qt::ISODate));
+
+ if (! q.exec()) {}
+
+ if (q.next()) {
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ QString from;
+ QString to;
+ QDate date;
+ MyMoneyMoney rate;
+ QString source;
+ bool foundFromId = false;
+ bool foundToId = false;
+ bool foundPriceDate = false;
+ bool foundPrice = false;
+ bool foundPriceSource = false;
+ while (ft != tableEnd) {
+ bool foundSomething = false;
+ if (!foundFromId && !foundSomething) {
+ CASE(fromId) {from = GETCSTRING; foundFromId = true; foundSomething = true;}
+ }
+ if (!foundToId && !foundSomething) {
+ CASE(toId) {to = GETCSTRING; foundToId = true; foundSomething = true;}
+ }
+ if (!foundPriceDate && !foundSomething) {
+ CASE(priceDate) {date = GETDATE; foundPriceDate = true; foundSomething = true;}
+ }
+ if (!foundPrice && !foundSomething) {
+ CASE(price) {rate = GETSTRING; foundPrice = true; foundSomething = true;}
+ }
+ if (!foundPriceSource && !foundSomething) {
+ CASE(priceSource) {source = GETSTRING; foundPriceSource = true; foundSomething = true;}
+ }
+ ++ft; ++i;
+ }
+
+ return MyMoneyPrice(fromIdList, toIdList, date, rate, source);
+ }
+
+ return MyMoneyPrice();
+}
+
+const MyMoneyPriceList MyMoneyStorageSql::fetchPrices (const QStringList& fromIdList, const QStringList& toIdList, bool forUpdate) const {
+ DBG("*** Entering MyMoneyStorageSql::readPrices");
+ signalProgress(0, m_prices, QObject::tr("Loading prices..."));
+ int progress = 0;
+ const_cast <MyMoneyStorageSql*> (this)->m_readingPrices = true;
+ MyMoneyPriceList pList;
+ const MyMoneyDbTable& t = m_db.m_tables["kmmPrices"];
+ MyMoneyDbTable::field_iterator tableEnd = t.end();
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ QString queryString = t.selectAllString(false);
+
+ // Use bind variables, instead of just inserting the values in the queryString,
+ // so that values containing a ':' will work.
+ if (! fromIdList.empty()) {
+ queryString += " WHERE (";
+ for (unsigned i = 0; i < fromIdList.count(); ++i) {
+ queryString += " fromId = :fromId" + QString::number(i) + " OR";
+ }
+ queryString = queryString.left(queryString.length() - 2) + ")";
+ }
+ if (! toIdList.empty()) {
+ queryString += " AND (";
+ for (unsigned i = 0; i < toIdList.count(); ++i) {
+ queryString += " toId = :toId" + QString::number(i) + " OR";
+ }
+ queryString = queryString.left(queryString.length() - 2) + ")";
+ }
+
+
+ if (forUpdate)
+ queryString += " FOR UPDATE";
+
+ queryString += ";";
+
+ q.prepare (queryString);
+
+ if (! fromIdList.empty()) {
+ QStringList::const_iterator bindVal = fromIdList.begin();
+ for (int i = 0; bindVal != fromIdList.end(); ++i, ++bindVal) {
+ q.bindValue (":fromId" + QString::number(i), *bindVal);
+ }
+ }
+ if (! toIdList.empty()) {
+ QStringList::const_iterator bindVal = toIdList.begin();
+ for (int i = 0; bindVal != toIdList.end(); ++i, ++bindVal) {
+ q.bindValue (":toId" + QString::number(i), *bindVal);
+ }
+ }
+
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Prices")));
+ while (q.next()) {
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ QString from;
+ QString to;
+ QDate date;
+ MyMoneyMoney rate;
+ QString source;
+
+ while (ft != tableEnd) {
+ CASE(fromId) from = GETCSTRING;
+ else CASE(toId) to = GETCSTRING;
+ else CASE(priceDate) date = GETDATE;
+ else CASE(price) rate = GETSTRING;
+ else CASE(priceSource) source = GETSTRING;
+ ++ft; ++i;
+ }
+ pList [MyMoneySecurityPair(from, to)].insert(date, MyMoneyPrice(from, to, date, rate, source));
+ signalProgress(++progress, 0);
+ }
+ const_cast <MyMoneyStorageSql*> (this)->m_readingPrices = false;
+
+ return pList;
+}
+
+void MyMoneyStorageSql::readCurrencies(void) {
+ TRY
+ m_storage->loadCurrencies(fetchCurrencies());
+ PASS
+}
+
+const QMap<QString, MyMoneySecurity> MyMoneyStorageSql::fetchCurrencies (const QStringList& idList, bool forUpdate) const {
+ DBG("*** Entering MyMoneyStorageSql::readCurrencies");
+ signalProgress(0, m_currencies, QObject::tr("Loading currencies..."));
+ int progress = 0;
+ QMap<QString, MyMoneySecurity> cList;
+ const MyMoneyDbTable& t = m_db.m_tables["kmmCurrencies"];
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+
+ QString queryString (t.selectAllString(false));
+
+ // Use bind variables, instead of just inserting the values in the queryString,
+ // so that values containing a ':' will work.
+ if (! idList.empty()) {
+ queryString += " WHERE";
+ for (unsigned i = 0; i < idList.count(); ++i)
+ queryString += " isocode = :id" + QString::number(i) + " OR";
+ queryString = queryString.left(queryString.length() - 2);
+ }
+
+ queryString += " ORDER BY ISOcode";
+
+ if (forUpdate)
+ queryString += " FOR UPDATE";
+
+ queryString += ";";
+
+ q.prepare (queryString);
+
+ if (! idList.empty()) {
+ QStringList::const_iterator bindVal = idList.begin();
+ for (int i = 0; bindVal != idList.end(); ++i, ++bindVal) {
+ q.bindValue (":id" + QString::number(i), *bindVal);
+ }
+ }
+
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Currencies")));
+ while (q.next()) {
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ QString id;
+ MyMoneySecurity c;
+ QChar symbol[3];
+ while (ft != t.end()) {
+ CASE(ISOcode) id = GETCSTRING;
+ else CASE(name) c.setName(GETSTRING);
+ else CASE(type) c.setSecurityType(static_cast<MyMoneySecurity::eSECURITYTYPE>(GETINT));
+ else CASE(symbol1) symbol[0] = QChar(GETINT);
+ else CASE(symbol2) symbol[1] = QChar(GETINT);
+ else CASE(symbol3) symbol[2] = QChar(GETINT);
+ else CASE(partsPerUnit) c.setPartsPerUnit(GETINT);
+ else CASE(smallestCashFraction) c.setSmallestCashFraction(GETINT);
+ else CASE(smallestAccountFraction) c.setSmallestAccountFraction(GETINT);
+ ++ft; ++i;
+ }
+ c.setTradingSymbol(QString(symbol, 3).stripWhiteSpace());
+
+ cList[id] = MyMoneySecurity(id, c);
+
+ signalProgress(++progress, 0);
+ }
+ return cList;
+}
+
+void MyMoneyStorageSql::readReports(void) {
+ TRY
+ m_storage->loadReports(fetchReports());
+ readFileInfo();
+ m_storage->loadReportId(m_hiIdReports);
+ PASS
+}
+
+const QMap<QString, MyMoneyReport> MyMoneyStorageSql::fetchReports (const QStringList& /*idList*/, bool /*forUpdate*/) const {
+ DBG("*** Entering MyMoneyStorageSql::readReports");
+ signalProgress(0, m_reports, QObject::tr("Loading reports..."));
+ int progress = 0;
+ const MyMoneyDbTable& t = m_db.m_tables["kmmReportConfig"];
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ q.prepare (t.selectAllString(true));
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading reports")));
+ QMap<QString, MyMoneyReport> rList;
+ while (q.next()) {
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ QDomDocument d;
+ while (ft != t.end()) {
+ CASE(XML) d.setContent(GETSTRING, false);
+ ++ft; ++i;
+ }
+ QDomNode child = d.firstChild();
+ child = child.firstChild();
+ MyMoneyReport report;
+
+ if (report.read(child.toElement()))
+ rList[report.id()] = report;
+
+ signalProgress(++progress, 0);
+ }
+ return rList;
+}
+
+const QMap<QString, MyMoneyBudget> MyMoneyStorageSql::fetchBudgets (const QStringList& idList, bool forUpdate) const {
+ DBG("*** Entering MyMoneyStorageSql::readBudgets");
+ signalProgress(0, m_budgets, QObject::tr("Loading budgets..."));
+ int progress = 0;
+ const MyMoneyDbTable& t = m_db.m_tables["kmmBudgetConfig"];
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ QString queryString (t.selectAllString(false));
+ if (! idList.empty()) {
+ queryString += " WHERE id = '" + idList.join("' OR id = '") + "'";
+ }
+ if (forUpdate)
+ queryString += " FOR UPDATE";
+
+ queryString += ";";
+
+ q.prepare (queryString);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading budgets")));
+ QMap<QString, MyMoneyBudget> budgets;
+ while (q.next()) {
+ MyMoneyDbTable::field_iterator ft = t.begin();
+ int i = 0;
+ QDomDocument d;
+ while (ft != t.end()) {
+ CASE(XML) d.setContent(GETSTRING, false);
+ ++ft; ++i;
+ }
+ QDomNode child = d.firstChild();
+ child = child.firstChild();
+ MyMoneyBudget budget (child.toElement());
+ budgets.insert(budget.id(), budget);
+ signalProgress(++progress, 0);
+ }
+ return budgets;
+}
+
+void MyMoneyStorageSql::readBudgets(void) {
+ m_storage->loadBudgets(fetchBudgets());
+}
+
+const MyMoneyKeyValueContainer MyMoneyStorageSql::readKeyValuePairs (const QString& kvpType, const QString& kvpId) const {
+ DBG("*** Entering MyMoneyStorageSql::readKeyValuePairs");
+ MyMoneyKeyValueContainer list;
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ q.prepare ("SELECT kvpKey, kvpData from kmmKeyValuePairs where kvpType = :type and kvpId = :id;");
+ q.bindValue(":type", kvpType);
+ q.bindValue(":id", kvpId);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Kvp for %1 %2").arg(kvpType)
+ .arg(kvpId)));
+ while (q.next()) list.setValue(q.value(0).toString(), q.value(1).toString());
+ return (list);
+}
+
+const QMap<QString, MyMoneyKeyValueContainer> MyMoneyStorageSql::readKeyValuePairs (const QString& kvpType, const QStringList& kvpIdList) const {
+ DBG("*** Entering MyMoneyStorageSql::readKeyValuePairs");
+ QMap<QString, MyMoneyKeyValueContainer> retval;
+
+ MyMoneySqlQuery q(const_cast <MyMoneyStorageSql*> (this));
+ QString query ("SELECT kvpId, kvpKey, kvpData from kmmKeyValuePairs where kvpType = :type");
+
+ if (!kvpIdList.empty()) {
+ query += " and kvpId IN ('" + kvpIdList.join("', '") + "')";
+ }
+
+ query += " order by kvpId;";
+ q.prepare (query);
+ q.bindValue(":type", kvpType);
+ if (!q.exec()) throw new MYMONEYEXCEPTION(buildError (q, __func__, QString("reading Kvp List for %1").arg(kvpType)));
+ while (q.next()) {
+ retval [q.value(0).toString()].setValue(q.value(1).toString(), q.value(2).toString());
+ }
+
+ return (retval);
+}
+
+long unsigned MyMoneyStorageSql::getNextBudgetId() const {
+ const_cast <MyMoneyStorageSql*> (this)->readFileInfo();
+ return m_hiIdBudgets;
+}
+
+long unsigned MyMoneyStorageSql::getNextAccountId() const {
+ const_cast <MyMoneyStorageSql*> (this)->readFileInfo();
+ return m_hiIdAccounts;
+}
+
+long unsigned MyMoneyStorageSql::getNextInstitutionId() const {
+ const_cast <MyMoneyStorageSql*> (this)->readFileInfo();
+ return m_hiIdInstitutions;
+}
+
+long unsigned MyMoneyStorageSql::getNextPayeeId() const {
+ const_cast <MyMoneyStorageSql*> (this)->readFileInfo();
+ return m_hiIdPayees;
+}
+
+long unsigned MyMoneyStorageSql::getNextReportId() const {
+ const_cast <MyMoneyStorageSql*> (this)->readFileInfo();
+ return m_hiIdReports;
+}
+
+long unsigned MyMoneyStorageSql::getNextScheduleId() const {
+ const_cast <MyMoneyStorageSql*> (this)->readFileInfo();
+ return m_hiIdSchedules;
+}
+
+long unsigned MyMoneyStorageSql::getNextSecurityId() const {
+ const_cast <MyMoneyStorageSql*> (this)->readFileInfo();
+ return m_hiIdSecurities;
+}
+
+long unsigned MyMoneyStorageSql::getNextTransactionId() const {
+ const_cast <MyMoneyStorageSql*> (this)->readFileInfo();
+ return m_hiIdTransactions;
+}
+
+long unsigned MyMoneyStorageSql::incrementBudgetId() {
+ MyMoneySqlQuery q(this);
+
+ startCommitUnit (__func__);
+ q.prepare("SELECT hiBudgetId FROM kmmFileInfo FOR UPDATE");
+ q.exec();
+ q.next();
+ long unsigned returnValue = (unsigned long) q.value(0).toULongLong();
+ ++returnValue;
+ q.prepare("UPDATE kmmFileInfo SET hiBudgetId = " + QString::number(returnValue));
+ q.exec();
+ endCommitUnit (__func__);
+ m_hiIdBudgets = returnValue;
+ return returnValue;
+}
+
+long unsigned MyMoneyStorageSql::incrementAccountId() {
+ MyMoneySqlQuery q(this);
+
+ startCommitUnit (__func__);
+ q.prepare("SELECT hiAccountId FROM kmmFileInfo FOR UPDATE");
+ q.exec();
+ q.next();
+ long unsigned returnValue = (unsigned long) q.value(0).toULongLong();
+ ++returnValue;
+ q.prepare("UPDATE kmmFileInfo SET hiAccountId = " + QString::number(returnValue));
+ q.exec();
+ endCommitUnit (__func__);
+ m_hiIdAccounts = returnValue;
+ return returnValue;
+}
+
+long unsigned MyMoneyStorageSql::incrementInstitutionId() {
+ MyMoneySqlQuery q(this);
+
+ startCommitUnit (__func__);
+ q.prepare("SELECT hiInstitutionId FROM kmmFileInfo FOR UPDATE");
+ q.exec();
+ q.next();
+ long unsigned returnValue = (unsigned long) q.value(0).toULongLong();
+ ++returnValue;
+ q.prepare("UPDATE kmmFileInfo SET hiInstitutionId = " + QString::number(returnValue));
+ q.exec();
+ endCommitUnit (__func__);
+ m_hiIdInstitutions = returnValue;
+ return returnValue;
+}
+
+long unsigned MyMoneyStorageSql::incrementPayeeId() {
+ MyMoneySqlQuery q(this);
+
+ startCommitUnit (__func__);
+ q.prepare("SELECT hiPayeeId FROM kmmFileInfo FOR UPDATE");
+ q.exec();
+ q.next();
+ long unsigned returnValue = (unsigned long) q.value(0).toULongLong();
+ ++returnValue;
+ q.prepare("UPDATE kmmFileInfo SET hiPayeeId = " + QString::number(returnValue));
+ q.exec();
+ endCommitUnit (__func__);
+ m_hiIdPayees = returnValue;
+ return returnValue;
+}
+
+long unsigned MyMoneyStorageSql::incrementReportId() {
+ MyMoneySqlQuery q(this);
+
+ startCommitUnit (__func__);
+ q.prepare("SELECT hiReportId FROM kmmFileInfo FOR UPDATE");
+ q.exec();
+ q.next();
+ long unsigned returnValue = (unsigned long) q.value(0).toULongLong();
+ ++returnValue;
+ q.prepare("UPDATE kmmFileInfo SET hiReportId = " + QString::number(returnValue));
+ q.exec();
+ endCommitUnit (__func__);
+ m_hiIdReports = returnValue;
+ return returnValue;
+}
+
+long unsigned MyMoneyStorageSql::incrementScheduleId() {
+ MyMoneySqlQuery q(this);
+
+ startCommitUnit (__func__);
+ q.prepare("SELECT hiScheduleId FROM kmmFileInfo FOR UPDATE");
+ q.exec();
+ q.next();
+ long unsigned returnValue = (unsigned long) q.value(0).toULongLong();
+ ++returnValue;
+ q.prepare("UPDATE kmmFileInfo SET hiScheduleId = " + QString::number(returnValue));
+ q.exec();
+ endCommitUnit (__func__);
+ m_hiIdSchedules = returnValue;
+ return returnValue;
+}
+
+long unsigned MyMoneyStorageSql::incrementSecurityId() {
+ MyMoneySqlQuery q(this);
+
+ startCommitUnit (__func__);
+ q.prepare("SELECT hiSecurityId FROM kmmFileInfo FOR UPDATE");
+ q.exec();
+ q.next();
+ long unsigned returnValue = (unsigned long) q.value(0).toULongLong();
+ ++returnValue;
+ q.prepare("UPDATE kmmFileInfo SET hiSecurityId = " + QString::number(returnValue));
+ q.exec();
+ endCommitUnit (__func__);
+ m_hiIdSecurities = returnValue;
+ return returnValue;
+}
+
+long unsigned MyMoneyStorageSql::incrementTransactionId() {
+ MyMoneySqlQuery q(this);
+
+ startCommitUnit (__func__);
+ q.prepare("SELECT hiTransactionId FROM kmmFileInfo FOR UPDATE");
+ q.exec();
+ q.next();
+ long unsigned returnValue = (unsigned long) q.value(0).toULongLong();
+ ++returnValue;
+ q.prepare("UPDATE kmmFileInfo SET hiTransactionId = " + QString::number(returnValue));
+ q.exec();
+ endCommitUnit (__func__);
+ m_hiIdTransactions = returnValue;
+ return returnValue;
+}
+
+void MyMoneyStorageSql::loadAccountId(const unsigned long& id)
+{
+ m_hiIdAccounts = id;
+ writeFileInfo();
+}
+
+void MyMoneyStorageSql::loadTransactionId(const unsigned long& id)
+{
+ m_hiIdTransactions = id;
+ writeFileInfo();
+}
+
+void MyMoneyStorageSql::loadPayeeId(const unsigned long& id)
+{
+ m_hiIdPayees = id;
+ writeFileInfo();
+}
+
+void MyMoneyStorageSql::loadInstitutionId(const unsigned long& id)
+{
+ m_hiIdInstitutions = id;
+ writeFileInfo();
+}
+
+void MyMoneyStorageSql::loadScheduleId(const unsigned long& id)
+{
+ m_hiIdSchedules = id;
+ writeFileInfo();
+}
+
+void MyMoneyStorageSql::loadSecurityId(const unsigned long& id)
+{
+ m_hiIdSecurities = id;
+ writeFileInfo();
+}
+
+void MyMoneyStorageSql::loadReportId(const unsigned long& id)
+{
+ m_hiIdReports = id;
+ writeFileInfo();
+}
+
+void MyMoneyStorageSql::loadBudgetId(const unsigned long& id)
+{
+ m_hiIdBudgets = id;
+ writeFileInfo();
+}
+
+//****************************************************
+long unsigned MyMoneyStorageSql::calcHighId
+ (const long unsigned& i, const QString& id) {
+ DBG("*** Entering MyMoneyStorageSql::calcHighId");
+ QString nid = id;
+ long unsigned high = (unsigned long) nid.replace(QRegExp("[A-Z]*"), "").toULongLong();
+ return std::max(high, i);
+}
+
+void MyMoneyStorageSql::setProgressCallback(void(*callback)(int, int, const QString&)) {
+ m_progressCallback = callback;
+}
+
+void MyMoneyStorageSql::signalProgress(int current, int total, const QString& msg) const {
+ if (m_progressCallback != 0)
+ (*m_progressCallback)(current, total, msg);
+}
+
+// **************************** Error display routine *******************************
+QString& MyMoneyStorageSql::buildError (const QSqlQuery& q, const QString& function, const QString& message) const {
+ QString s = QString("Error in function %1 : %2").arg(function).arg(message);
+ QSqlError e = lastError();
+ s += QString ("\nDriver = %1, Host = %2, User = %3, Database = %4")
+ .arg(driverName()).arg(hostName()).arg(userName()).arg(databaseName());
+ s += QString ("\nDriver Error: %1").arg(e.driverText());
+ s += QString ("\nDatabase Error No %1: %2").arg(e.number()).arg(e.databaseText());
+ e = q.lastError();
+ s += QString ("\nExecuted: %1").arg(q.executedQuery());
+ s += QString ("\nQuery error No %1: %2").arg(e.number()).arg(e.text());
+
+ const_cast <MyMoneyStorageSql*> (this)->m_error = s;
+ qDebug("%s", s.ascii());
+ const_cast <MyMoneyStorageSql*> (this)->cancelCommitUnit(function);
+ return (const_cast <MyMoneyStorageSql*> (this)->m_error);
+}
+
+// ************************* Build table descriptions ****************************
+MyMoneyDbDef::MyMoneyDbDef () {
+ FileInfo();
+ Institutions();
+ Payees();
+ Accounts();
+ Transactions();
+ Splits();
+ KeyValuePairs();
+ Schedules();
+ SchedulePaymentHistory();
+ Securities();
+ Prices();
+ Currencies();
+ Reports();
+ Budgets();
+ Balances();
+}
+
+/* PRIMARYKEY - these fields combine to form a unique key field on which the db will create an index
+ NOTNULL - this field should never be null
+ UNSIGNED - for numeric types, indicates the field is UNSIGNED
+ ?ISKEY - where there is no primary key, these fields can be used to uniquely identify a record
+ Default is that a field is not a part of a primary key, nullable, and if numeric, signed */
+
+#define PRIMARYKEY true
+#define NOTNULL true
+#define UNSIGNED false
+//#define ISKEY true
+
+void MyMoneyDbDef::FileInfo(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("version", "varchar(16)"));
+ fields.append(new MyMoneyDbColumn("created", "date"));
+ fields.append(new MyMoneyDbColumn("lastModified", "date"));
+ fields.append(new MyMoneyDbColumn("baseCurrency", "char(3)"));
+ fields.append(new MyMoneyDbIntColumn("institutions", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("accounts", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("payees", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("transactions", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("splits", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("securities", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("prices", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("currencies", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("schedules", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("reports", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("kvps", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbColumn("dateRangeStart", "date"));
+ fields.append(new MyMoneyDbColumn("dateRangeEnd", "date"));
+ fields.append(new MyMoneyDbIntColumn("hiInstitutionId", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("hiPayeeId", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("hiAccountId", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("hiTransactionId", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("hiScheduleId", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("hiSecurityId", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("hiReportId", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbColumn("encryptData", "varchar(255)"));
+ fields.append(new MyMoneyDbColumn("updateInProgress", "char(1)"));
+ fields.append(new MyMoneyDbIntColumn("budgets", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("hiBudgetId", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ fields.append(new MyMoneyDbColumn("logonUser", "varchar(255)"));
+ fields.append(new MyMoneyDbDatetimeColumn("logonAt"));
+ fields.append(new MyMoneyDbIntColumn("fixLevel",
+ MyMoneyDbIntColumn::MEDIUM, UNSIGNED));
+ MyMoneyDbTable t("kmmFileInfo", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Institutions(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("id", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("name", MyMoneyDbTextColumn::NORMAL, false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("manager"));
+ fields.append(new MyMoneyDbTextColumn("routingCode"));
+ fields.append(new MyMoneyDbTextColumn("addressStreet"));
+ fields.append(new MyMoneyDbTextColumn("addressCity"));
+ fields.append(new MyMoneyDbTextColumn("addressZipcode"));
+ fields.append(new MyMoneyDbTextColumn("telephone"));
+ MyMoneyDbTable t("kmmInstitutions", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Payees(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("id", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("name"));
+ fields.append(new MyMoneyDbTextColumn("reference"));
+ fields.append(new MyMoneyDbTextColumn("email"));
+ fields.append(new MyMoneyDbTextColumn("addressStreet"));
+ fields.append(new MyMoneyDbTextColumn("addressCity"));
+ fields.append(new MyMoneyDbTextColumn("addressZipcode"));
+ fields.append(new MyMoneyDbTextColumn("addressState"));
+ fields.append(new MyMoneyDbTextColumn("telephone"));
+ fields.append(new MyMoneyDbTextColumn("notes", MyMoneyDbTextColumn::LONG));
+ fields.append(new MyMoneyDbColumn("defaultAccountId", "varchar(32)"));
+ fields.append(new MyMoneyDbIntColumn("matchData", MyMoneyDbIntColumn::TINY, UNSIGNED));
+ fields.append(new MyMoneyDbColumn("matchIgnoreCase", "char(1)"));
+ fields.append(new MyMoneyDbTextColumn("matchKeys"));
+ MyMoneyDbTable t("kmmPayees", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Accounts(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("id", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbColumn("institutionId", "varchar(32)"));
+ fields.append(new MyMoneyDbColumn("parentId", "varchar(32)"));
+ fields.append(new MyMoneyDbDatetimeColumn("lastReconciled"));
+ fields.append(new MyMoneyDbDatetimeColumn("lastModified"));
+ fields.append(new MyMoneyDbColumn("openingDate", "date"));
+ fields.append(new MyMoneyDbTextColumn("accountNumber"));
+ fields.append(new MyMoneyDbColumn("accountType", "varchar(16)", false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("accountTypeString"));
+ fields.append(new MyMoneyDbColumn("isStockAccount", "char(1)"));
+ fields.append(new MyMoneyDbTextColumn("accountName"));
+ fields.append(new MyMoneyDbTextColumn("description"));
+ fields.append(new MyMoneyDbColumn("currencyId", "varchar(32)"));
+ fields.append(new MyMoneyDbTextColumn("balance"));
+ fields.append(new MyMoneyDbTextColumn("balanceFormatted"));
+ fields.append(new MyMoneyDbIntColumn("transactionCount", MyMoneyDbIntColumn::BIG, UNSIGNED));
+ MyMoneyDbTable t("kmmAccounts", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Transactions(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("id", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbColumn("txType", "char(1)"));
+ fields.append(new MyMoneyDbDatetimeColumn("postDate"));
+ fields.append(new MyMoneyDbTextColumn("memo"));
+ fields.append(new MyMoneyDbDatetimeColumn("entryDate"));
+ fields.append(new MyMoneyDbColumn("currencyId", "char(3)"));
+ fields.append(new MyMoneyDbTextColumn("bankId"));
+ MyMoneyDbTable t("kmmTransactions", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Splits(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("transactionId", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbColumn("txType", "char(1)"));
+ fields.append(new MyMoneyDbIntColumn("splitId", MyMoneyDbIntColumn::SMALL, UNSIGNED, PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbColumn("payeeId", "varchar(32)"));
+ fields.append(new MyMoneyDbDatetimeColumn("reconcileDate"));
+ fields.append(new MyMoneyDbColumn("action", "varchar(16)"));
+ fields.append(new MyMoneyDbColumn("reconcileFlag", "char(1)"));
+ fields.append(new MyMoneyDbTextColumn("value", MyMoneyDbTextColumn::NORMAL, false, NOTNULL));
+ fields.append(new MyMoneyDbColumn("valueFormatted", "text"));
+ fields.append(new MyMoneyDbTextColumn("shares", MyMoneyDbTextColumn::NORMAL, false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("sharesFormatted"));
+ fields.append(new MyMoneyDbTextColumn("price", MyMoneyDbTextColumn::NORMAL, false));
+ fields.append(new MyMoneyDbTextColumn("priceFormatted"));
+ fields.append(new MyMoneyDbTextColumn("memo"));
+ fields.append(new MyMoneyDbColumn("accountId", "varchar(32)", false, NOTNULL));
+ fields.append(new MyMoneyDbColumn("checkNumber", "varchar(32)"));
+ fields.append(new MyMoneyDbDatetimeColumn("postDate"));
+ fields.append(new MyMoneyDbTextColumn("bankId"));
+ MyMoneyDbTable t("kmmSplits", fields);
+ QStringList list;
+ list << "accountId" << "txType";
+ t.addIndex("kmmSplitsaccount_type", list, false);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::KeyValuePairs(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("kvpType", "varchar(16)", false, NOTNULL));
+ fields.append(new MyMoneyDbColumn("kvpId", "varchar(32)"));
+ fields.append(new MyMoneyDbColumn("kvpKey", "varchar(255)", false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("kvpData"));
+ MyMoneyDbTable t("kmmKeyValuePairs", fields);
+ QStringList list;
+ list << "kvpType" << "kvpId";
+ t.addIndex("type_id", list, false);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Schedules(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("id", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("name", MyMoneyDbTextColumn::NORMAL, false, NOTNULL));
+ fields.append(new MyMoneyDbIntColumn("type", MyMoneyDbIntColumn::TINY, UNSIGNED, false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("typeString"));
+ fields.append(new MyMoneyDbIntColumn("occurence", MyMoneyDbIntColumn::SMALL, UNSIGNED, false,
+ NOTNULL));
+ fields.append(new MyMoneyDbIntColumn("occurenceMultiplier", MyMoneyDbIntColumn::SMALL, UNSIGNED,
+ false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("occurenceString"));
+ fields.append(new MyMoneyDbIntColumn("paymentType", MyMoneyDbIntColumn::TINY, UNSIGNED));
+ fields.append(new MyMoneyDbTextColumn("paymentTypeString", MyMoneyDbTextColumn::LONG));
+ fields.append(new MyMoneyDbColumn("startDate", "date", false, NOTNULL));
+ fields.append(new MyMoneyDbColumn("endDate", "date"));
+ fields.append(new MyMoneyDbColumn("fixed", "char(1)", false, NOTNULL));
+ fields.append(new MyMoneyDbColumn("autoEnter", "char(1)", false, NOTNULL));
+ fields.append(new MyMoneyDbColumn("lastPayment", "date"));
+ fields.append(new MyMoneyDbColumn("nextPaymentDue", "date"));
+ fields.append(new MyMoneyDbIntColumn("weekendOption", MyMoneyDbIntColumn::TINY, UNSIGNED, false,
+ NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("weekendOptionString"));
+ MyMoneyDbTable t("kmmSchedules", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::SchedulePaymentHistory(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("schedId", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbColumn("payDate", "date", PRIMARYKEY, NOTNULL));
+ MyMoneyDbTable t("kmmSchedulePaymentHistory", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Securities(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("id", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbColumn("name", "text", false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("symbol"));
+ fields.append(new MyMoneyDbIntColumn("type", MyMoneyDbIntColumn::SMALL, UNSIGNED, false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("typeString"));
+ fields.append(new MyMoneyDbColumn("smallestAccountFraction", "varchar(24)"));
+ fields.append(new MyMoneyDbTextColumn("tradingMarket"));
+ fields.append(new MyMoneyDbColumn("tradingCurrency", "char(3)"));
+ MyMoneyDbTable t("kmmSecurities", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Prices(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("fromId", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbColumn("toId", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbColumn("priceDate", "date", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("price", MyMoneyDbTextColumn::NORMAL, false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("priceFormatted"));
+ fields.append(new MyMoneyDbTextColumn("priceSource"));
+ MyMoneyDbTable t("kmmPrices", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Currencies(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("ISOcode", "char(3)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("name", MyMoneyDbTextColumn::NORMAL, false, NOTNULL));
+ fields.append(new MyMoneyDbIntColumn("type", MyMoneyDbIntColumn::SMALL, UNSIGNED));
+ fields.append(new MyMoneyDbTextColumn("typeString"));
+ fields.append(new MyMoneyDbIntColumn("symbol1", MyMoneyDbIntColumn::SMALL, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("symbol2", MyMoneyDbIntColumn::SMALL, UNSIGNED));
+ fields.append(new MyMoneyDbIntColumn("symbol3", MyMoneyDbIntColumn::SMALL, UNSIGNED));
+ fields.append(new MyMoneyDbColumn("symbolString", "varchar(255)"));
+ fields.append(new MyMoneyDbColumn("partsPerUnit", "varchar(24)"));
+ fields.append(new MyMoneyDbColumn("smallestCashFraction", "varchar(24)"));
+ fields.append(new MyMoneyDbColumn("smallestAccountFraction", "varchar(24)"));
+ MyMoneyDbTable t("kmmCurrencies", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Reports(void) {
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("name", "varchar(255)", false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("XML", MyMoneyDbTextColumn::LONG));
+ fields.append(new MyMoneyDbColumn("id", "varchar(32)", PRIMARYKEY, NOTNULL));
+ MyMoneyDbTable t("kmmReportConfig", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Budgets(void){
+ QValueList<KSharedPtr <MyMoneyDbColumn> > fields;
+ fields.append(new MyMoneyDbColumn("id", "varchar(32)", PRIMARYKEY, NOTNULL));
+ fields.append(new MyMoneyDbColumn("name", "text", false, NOTNULL));
+ fields.append(new MyMoneyDbColumn("start", "date", false, NOTNULL));
+ fields.append(new MyMoneyDbTextColumn("XML", MyMoneyDbTextColumn::LONG));
+ MyMoneyDbTable t("kmmBudgetConfig", fields);
+ t.buildSQLStrings();
+ m_tables[t.name()] = t;
+}
+
+void MyMoneyDbDef::Balances(void){
+ MyMoneyDbView v("kmmBalances", "CREATE VIEW kmmBalances AS "
+ "SELECT kmmAccounts.id AS id, kmmAccounts.currencyId, "
+ "kmmSplits.txType, kmmSplits.value, kmmSplits.shares, "
+ "kmmSplits.postDate AS balDate, "
+ "kmmTransactions.currencyId AS txCurrencyId "
+ "FROM kmmAccounts, kmmSplits, kmmTransactions "
+ "WHERE kmmSplits.txType = 'N' "
+ "AND kmmSplits.accountId = kmmAccounts.id "
+ "AND kmmSplits.transactionId = kmmTransactions.id;");
+ m_views[v.name()] = v;
+}
+
+// function to write create SQL to a stream
+const QString MyMoneyDbDef::generateSQL (const QString& driver) const {
+ QString retval;
+ databaseTypeE dbType = m_drivers.driverToType(driver);
+ table_iterator tt = tableBegin();
+ while (tt != tableEnd()) {
+ retval += (*tt).generateCreateSQL(dbType) + '\n';
+ ++tt;
+ }
+ view_iterator vt = viewBegin();
+ while (vt != viewEnd()) {
+ retval += (*vt).createString() + '\n';
+ ++vt;
+ }
+ retval += '\n';
+
+ MyMoneyDbTable fi = m_tables["kmmFileInfo"];
+ QString qs = fi.insertString();
+ MyMoneyDbTable::field_iterator fit;
+ for (fit = fi.begin(); fit != fi.end(); ++fit) {
+ QString toReplace = (*fit)->name();
+ toReplace.prepend(':');
+ QString replace = "NULL";
+ if ((*fit)->name() == "version")
+ replace = QString::number(m_currentVersion);
+ if ((*fit)->name() == "fixLevel")
+ replace = QString::number
+ (MyMoneyFile::instance()->storage()->currentFixVersion());
+ if ((*fit)->name() == "created")
+ replace = QDate::currentDate().toString(Qt::ISODate);
+ if ((*fit)->name() == "lastModified")
+ replace = QDate::currentDate().toString(Qt::ISODate);
+ if ((*fit)->name() == "updateInProgress")
+ replace = enclose("N");
+ qs.replace(toReplace, replace);
+ }
+ qs += "\n\n";
+ retval += qs;
+
+ qs = QString();
+ unsigned int i;
+ QValueList<MyMoneyAccount> stdList;
+ stdList.append (MyMoneyFile::instance()->asset());
+ stdList.append (MyMoneyFile::instance()->equity());
+ stdList.append (MyMoneyFile::instance()->expense());
+ stdList.append (MyMoneyFile::instance()->income());
+ stdList.append (MyMoneyFile::instance()->liability());
+ for (i = 0; i < stdList.count(); ++i) {
+ MyMoneyAccount* pac = &stdList[i];
+ MyMoneyDbTable ac = m_tables["kmmAccounts"];
+ qs = ac.insertString();
+ MyMoneyDbTable::field_iterator act;
+ // do the following in reverse so the 'formatted' fields are
+ // correctly handled.
+ // Hmm, how does one use a QValueListIterator in reverse
+ // It'll be okay in Qt4 with QListIterator
+ for (act = ac.end(), --act; act != ac.begin(); --act) {
+ QString toReplace = (*act)->name();
+ toReplace.prepend(':');
+ QString replace = "NULL";
+ if ((*act)->name() == "accountType")
+ replace = QString::number(pac->accountType());
+ if ((*act)->name() == "accountTypeString")
+ replace = enclose(pac->name());
+ if ((*act)->name() == "isStockAccount")
+ replace = enclose("N");
+ if ((*act)->name() == "accountName")
+ replace = enclose(pac->name());
+ qs.replace(toReplace, replace);
+ }
+ qs.replace (":id", enclose(pac->id())); // a real kludge
+ qs += "\n\n";
+ retval += qs;
+ }
+ return retval;
+}
+
+//*****************************************************************************
+
+void MyMoneyDbTable::addIndex(const QString& name, const QStringList& columns, bool unique) {
+ m_indices.push_back (MyMoneyDbIndex (m_name, name, columns, unique));
+}
+
+void MyMoneyDbTable::buildSQLStrings (void) {
+ // build fixed SQL strings for this table
+ // build the insert string with placeholders for each field
+ QString qs = QString("INSERT INTO %1 (").arg(name());
+ QString ws = ") VALUES (";
+ field_iterator ft = m_fields.begin();
+ while (ft != m_fields.end()) {
+ qs += QString("%1, ").arg((*ft)->name());
+ ws += QString(":%1, ").arg((*ft)->name());
+ ++ft;
+ }
+ qs = qs.left(qs.length() - 2);
+ ws = ws.left(ws.length() - 2);
+ m_insertString = qs + ws + ");";
+ // build a 'select all' string (select * is deprecated)
+ // don't terminate with semicolon coz we may want a where or order clause
+ m_selectAllString = "SELECT " + columnList() + " FROM " + name();;
+
+ // build an update string; key fields go in the where clause
+ qs = "UPDATE " + name() + " SET ";
+ ws = QString();
+ ft = m_fields.begin();
+ while (ft != m_fields.end()) {
+ if ((*ft)->isPrimaryKey()) {
+ if (!ws.isEmpty()) ws += " AND ";
+ ws += QString("%1 = :%2").arg((*ft)->name()).arg((*ft)->name());
+ } else {
+ qs += QString("%1 = :%2, ").arg((*ft)->name()).arg((*ft)->name());
+ }
+ ++ft;
+ }
+ qs = qs.left(qs.length() - 2);
+ if (!ws.isEmpty()) qs += " WHERE " + ws;
+ m_updateString = qs + ";";
+ // build a delete string; where clause as for update
+ qs = "DELETE FROM " + name();
+ if (!ws.isEmpty()) qs += " WHERE " + ws;
+ m_deleteString = qs + ";";
+ }
+
+const QString MyMoneyDbTable::columnList() const {
+ field_iterator ft = m_fields.begin();
+ QString qs;
+ ft = m_fields.begin();
+ while (ft != m_fields.end()) {
+ qs += QString("%1, ").arg((*ft)->name());
+ ++ft;
+ }
+ return (qs.left(qs.length() - 2));
+}
+
+const QString MyMoneyDbTable::generateCreateSQL (databaseTypeE dbType) const {
+ QString qs = QString("CREATE TABLE %1 (").arg(name());
+ QString pkey;
+ for (field_iterator it = m_fields.begin(); it != m_fields.end(); ++it) {
+ qs += (*it)->generateDDL (dbType) + ", ";
+ if ((*it)->isPrimaryKey ())
+ pkey += (*it)->name () + ", ";
+ }
+
+ if (!pkey.isEmpty()) {
+ qs += "PRIMARY KEY (" + pkey;
+ qs = qs.left(qs.length() -2) + "))";
+ } else {
+ qs = qs.left(qs.length() -2) + ")";
+ }
+
+ if (dbType == Mysql)
+ qs += " ENGINE = InnoDB;\n";
+ else
+ qs += ";\n";
+
+ for (index_iterator ii = m_indices.begin(); ii != m_indices.end(); ++ii) {
+ qs += (*ii).generateDDL(dbType);
+ }
+ return qs;
+}
+
+const QString MyMoneyDbTable::dropPrimaryKeyString(databaseTypeE dbType) const
+{
+ if (dbType == Mysql || dbType == Oracle8)
+ return "ALTER TABLE " + m_name + " DROP PRIMARY KEY;";
+ else if (dbType == Postgresql)
+ return "ALTER TABLE " + m_name + " DROP CONSTRAINT " + m_name + "_pkey;";
+ else if (dbType == Sqlite3)
+ return "";
+
+ return "";
+}
+
+const QString MyMoneyDbTable::modifyColumnString(databaseTypeE dbType, const QString& columnName, const MyMoneyDbColumn& newDef) const {
+ QString qs = "ALTER TABLE " + m_name + " ";
+ if (dbType == Mysql)
+ qs += "CHANGE " + columnName + " " + newDef.generateDDL(dbType);
+ else if (dbType == Postgresql)
+ qs += "ALTER COLUMN " + columnName + " TYPE " + newDef.generateDDL(dbType).section(' ', 1);
+ else if (dbType == Sqlite3)
+ qs = "";
+ else if (dbType == Oracle8)
+ qs = "MODIFY " + columnName + " " + newDef.generateDDL(dbType);
+
+ return qs;
+}
+
+//*****************************************************************************
+const QString MyMoneyDbIndex::generateDDL (databaseTypeE dbType) const
+{
+ Q_UNUSED(dbType);
+
+ QString qs = "CREATE ";
+
+ if (m_unique)
+ qs += "UNIQUE ";
+
+ qs += "INDEX " + m_table + "_" + m_name + "_idx ON "
+ + m_table + " (";
+
+ // The following should probably be revised. MySQL supports an index on
+ // partial columns, but not on a function. Postgres supports an index on
+ // the result of an SQL function, but not a partial column. There should be
+ // a way to merge these, and support other DBMSs like SQLite at the same time.
+ // For now, if we just use plain columns, this will work fine.
+ for (QStringList::const_iterator it = m_columns.begin(); it != m_columns.end(); ++it) {
+ qs += *it + ",";
+ }
+
+ qs = qs.left(qs.length() - 1) + ");\n";
+
+ return qs;
+}
+
+//*****************************************************************************
+// These are the actual column types.
+// TODO: consider changing all the else-if statements to driver classes.
+//
+
+MyMoneyDbColumn* MyMoneyDbColumn::clone () const
+{ return (new MyMoneyDbColumn (*this)); }
+
+MyMoneyDbIntColumn* MyMoneyDbIntColumn::clone () const
+{ return (new MyMoneyDbIntColumn (*this)); }
+
+MyMoneyDbDatetimeColumn* MyMoneyDbDatetimeColumn::clone () const
+{ return (new MyMoneyDbDatetimeColumn (*this)); }
+
+MyMoneyDbTextColumn* MyMoneyDbTextColumn::clone () const
+{ return (new MyMoneyDbTextColumn (*this)); }
+
+const QString MyMoneyDbColumn::generateDDL (databaseTypeE dbType) const
+{
+ Q_UNUSED(dbType);
+
+ QString qs = name() + " " + type();
+ if (isNotNull()) qs += " NOT NULL";
+ return qs;
+}
+
+const QString MyMoneyDbIntColumn::generateDDL (databaseTypeE dbType) const
+{
+ QString qs = name() + " ";
+
+ switch (m_type) {
+ case MyMoneyDbIntColumn::TINY:
+ if (dbType == Mysql || dbType == Sqlite3) {
+ qs += "tinyint ";
+ } else if (dbType == Postgresql) {
+ qs += "int2 ";
+ } else if (dbType == Db2) {
+ qs += "smallint ";
+ } else if (dbType == Oracle8) {
+ qs += "number(3) ";
+ } else {
+ // cross your fingers...
+ qs += "smallint ";
+ }
+ break;
+ case MyMoneyDbIntColumn::SMALL:
+ if (dbType == Mysql || dbType == Db2 || dbType == Sqlite3) {
+ qs += "smallint ";
+ } else if (dbType == Postgresql) {
+ qs += "int2 ";
+ } else if (dbType == Oracle8) {
+ qs += "number(5) ";
+ } else {
+ // cross your fingers...
+ qs += "smallint ";
+ }
+ break;
+ case MyMoneyDbIntColumn::MEDIUM:
+ if (dbType == Mysql || dbType == Db2) {
+ qs += "int ";
+ } else if (dbType == Postgresql) {
+ qs += "int4 ";
+ } else if (dbType == Sqlite3) {
+ qs += "integer ";
+ } else if (dbType == Oracle8) {
+ qs += "number(10) ";
+ } else {
+ // cross your fingers...
+ qs += "int ";
+ }
+ break;
+ case MyMoneyDbIntColumn::BIG:
+ if (dbType == Mysql || dbType == Db2 || dbType == Sqlite3) {
+ qs += "bigint ";
+ } else if (dbType == Postgresql) {
+ qs += "int8 ";
+ } else if (dbType == Oracle8) {
+ qs += "number(20) ";
+ } else {
+ // cross your fingers...
+ qs += "bigint ";
+ }
+ break;
+ default:
+ qs += "int ";
+ break;
+ }
+
+ if ((! m_isSigned) && (dbType == Mysql || dbType == Sqlite3)) {
+ qs += "unsigned ";
+ }
+
+ if (isNotNull()) qs += " NOT NULL";
+ if ((! m_isSigned) && (dbType == Postgresql)) {
+ qs += " check(" + name() + " >= 0)";
+ }
+ return qs;
+}
+
+const QString MyMoneyDbTextColumn::generateDDL (databaseTypeE dbType) const
+{
+ QString qs = name() + " ";
+
+ switch (m_type) {
+ case MyMoneyDbTextColumn::TINY:
+ if (dbType == Mysql || dbType == Sqlite3) {
+ qs += "tinytext ";
+ } else if (dbType == Postgresql) {
+ qs += "text ";
+ } else if (dbType == Db2) {
+ qs += "varchar(255) ";
+ } else if (dbType == Oracle8) {
+ qs += "varchar2(255) ";
+ } else {
+ // cross your fingers...
+ qs += "tinytext ";
+ }
+ break;
+ case MyMoneyDbTextColumn::NORMAL:
+ if (dbType == Mysql || dbType == Sqlite3 || dbType == Postgresql) {
+ qs += "text ";
+ } else if (dbType == Db2) {
+ qs += "clob(64K) ";
+ } else if (dbType == Oracle8) {
+ qs += "clob ";
+ } else {
+ // cross your fingers...
+ qs += "text ";
+ }
+ break;
+ case MyMoneyDbTextColumn::MEDIUM:
+ if (dbType == Mysql || dbType == Sqlite3 ) {
+ qs += "mediumtext ";
+ } else if (dbType == Postgresql) {
+ qs += "text ";
+ } else if (dbType == Db2) {
+ qs += "clob(16M) ";
+ } else if (dbType == Oracle8) {
+ qs += "clob ";
+ } else {
+ // cross your fingers...
+ qs += "mediumtext ";
+ }
+ break;
+ case MyMoneyDbTextColumn::LONG:
+ if (dbType == Mysql || dbType == Sqlite3 ) {
+ qs += "longtext ";
+ } else if (dbType == Postgresql) {
+ qs += "text ";
+ } else if (dbType == Db2) {
+ qs += "clob(2G) ";
+ } else if (dbType == Oracle8) {
+ qs += "clob ";
+ } else {
+ // cross your fingers...
+ qs += "longtext ";
+ }
+ break;
+ default:
+ if (dbType == Oracle8) {
+ qs += "clob ";
+ } else {
+ qs += "text ";
+ }
+ break;
+ }
+
+ if (isNotNull()) qs += " NOT NULL";
+
+ return qs;
+}
+
+const QString MyMoneyDbDatetimeColumn::generateDDL (databaseTypeE dbType) const
+{
+ QString qs = name() + " ";
+ if (dbType == Mysql || dbType == ODBC3) {
+ qs += "datetime ";
+ } else if (dbType == Postgresql || dbType == Db2 || dbType == Oracle8 || dbType == Sqlite3 ) {
+ qs += "timestamp ";
+ } else {
+ qs += "";
+ }
+ if (isNotNull()) qs += " NOT NULL";
+ return qs;
+}
diff --git a/kmymoney2/mymoney/storage/mymoneystoragesql.h b/kmymoney2/mymoney/storage/mymoneystoragesql.h
new file mode 100644
index 0000000..1abe70b
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneystoragesql.h
@@ -0,0 +1,807 @@
+/***************************************************************************
+ mymoneystoragesql.h
+ -------------------
+ begin : 11 November 2005
+ copyright : (C) 2005 by Tony Bloomfield
+ email : tonybloom@users.sourceforge.net
+ : Fernando Vilas <fvilas@iname.com>
+ ***************************************************************************/
+
+#ifndef MYMONEYSTORAGESQL_H
+#define MYMONEYSTORAGESQL_H
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+#include <qsqldatabase.h>
+#include <qsqlquery.h>
+#include <qsqlerror.h>
+#include <qvaluestack.h>
+
+class QIODevice;
+// ----------------------------------------------------------------------------
+// KDE Includes
+
+#include <kurl.h>
+#include <ksharedptr.h>
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include "imymoneystorageformat.h"
+#include "../mymoneyinstitution.h"
+#include "../mymoneypayee.h"
+#include "../mymoneyaccount.h"
+#include "../mymoneytransaction.h"
+#include "../mymoneysplit.h"
+#include "../mymoneyscheduled.h"
+#include "../mymoneysecurity.h"
+#include "../mymoneyprice.h"
+#include "../mymoneyreport.h"
+#include "../mymoneybudget.h"
+#include "../mymoneyfile.h"
+#include "../mymoneykeyvaluecontainer.h"
+#include "mymoneymap.h"
+#include "../mymoneymoney.h"
+#include "../mymoneytransactionfilter.h"
+
+// This is a convenience functor to make it easier to use STL algorithms
+// It will return false if the MyMoneyTransaction DOES match the filter.
+// This functor may disappear when all filtering can be handled in SQL.
+class FilterFail {
+ public:
+ FilterFail (const MyMoneyTransactionFilter& filter,
+ IMyMoneyStorage* storage)
+ : m_filter (filter),
+ m_storage (storage)
+ {}
+
+ inline bool operator() (const QPair<QString, MyMoneyTransaction>& transactionPair)
+ { return (*this) (transactionPair.second); }
+
+ inline bool operator() (const MyMoneyTransaction& transaction)
+ {
+ return (! m_filter.match(transaction)) && (m_filter.matchingSplits().count() == 0);
+ }
+
+ private:
+ MyMoneyTransactionFilter m_filter;
+ IMyMoneyStorage *m_storage;
+};
+
+/**
+@author Tony Bloomfield
+ */
+typedef enum databaseTypeE { // database (driver) type
+ Db2 = 0, //
+ Interbase, //
+ Mysql, //
+ Oracle8, //
+ ODBC3, //
+ Postgresql, //
+ Sqlite, //
+ Sybase, //
+ Sqlite3 //
+} _databaseType;
+
+class MyMoneyStorageSql;
+
+/**
+ * The MyMoneySqlQuery class is derived from QSqlQuery to provide
+ * a way to adjust some queries based on databaseTypeE and make
+ * debugging easier by providing a place to put debug statements.
+ */
+class MyMoneySqlQuery : public QSqlQuery {
+ public:
+ MyMoneySqlQuery (MyMoneyStorageSql* db = 0);
+ virtual ~MyMoneySqlQuery() {}
+ bool exec ();
+ bool prepare ( const QString & query );
+ private:
+ const MyMoneyStorageSql* m_db;
+};
+
+/**
+ * The MyMoneyDbDrivers class is a map from string to enum of db types.
+ */
+class MyMoneyDbDrivers {
+ public:
+ MyMoneyDbDrivers ();
+ /**
+ * @return a list ofsupported Qt database driver types, their qt names and useful names
+ **/
+ const QMap<QString, QString> driverMap() const {return (m_driverMap);};
+ databaseTypeE driverToType (const QString& driver) const;
+ bool isTested (databaseTypeE dbType) const;
+ private:
+ QMap<QString, QString> m_driverMap;
+};
+
+/**
+ * The MyMoneyDbColumn class is a base type for generic db columns.
+ * Derived types exist for several common column types.
+ */
+class MyMoneyDbColumn : public KShared {
+ public:
+ MyMoneyDbColumn (const QString& iname,
+ const QString& itype = QString::null,
+ const bool iprimary = false,
+ const bool inotnull = false,
+ const QString &initVersion = "0.1"):
+ m_name(iname),
+ m_type(itype),
+ m_isPrimary(iprimary),
+ m_isNotNull(inotnull),
+ m_initVersion(initVersion) {}
+ MyMoneyDbColumn (void) {}
+ virtual ~MyMoneyDbColumn () {}
+
+ /**
+ * This method is used to copy column objects. Because there are several derived types,
+ * clone() is more appropriate than a copy ctor in most cases.
+ */
+ virtual MyMoneyDbColumn* clone () const;
+
+ /**
+ * This method generates the DDL (Database Design Language) string for the column.
+ *
+ * @param dbType Database driver type
+ *
+ * @return QString of the DDL for the column, tailored for what the driver supports.
+ */
+ virtual const QString generateDDL (databaseTypeE dbType) const;
+
+ const QString& name(void) const {return (m_name);}
+ const QString& type(void) const {return (m_type);}
+ bool isPrimaryKey(void) const {return (m_isPrimary);}
+ bool isNotNull(void) const {return (m_isNotNull);}
+ private:
+ QString m_name;
+ QString m_type;
+ bool m_isPrimary;
+ bool m_isNotNull;
+ QString m_initVersion;
+};
+
+/**
+ * The MyMoneyDbDatetimeColumn class is a representation of datetime columns.
+ */
+class MyMoneyDbDatetimeColumn : public MyMoneyDbColumn {
+ public:
+ MyMoneyDbDatetimeColumn (const QString& iname,
+ const bool iprimary = false,
+ const bool inotnull = false,
+ const QString &initVersion = "0.1"):
+ MyMoneyDbColumn (iname, "", iprimary, inotnull, initVersion)
+ {}
+ virtual ~MyMoneyDbDatetimeColumn() {}
+ virtual const QString generateDDL (databaseTypeE dbType) const;
+ virtual MyMoneyDbDatetimeColumn* clone () const;
+ private:
+ static const QString calcType(void);
+};
+
+/**
+ * The MyMoneyDbColumn class is a representation of integer db columns.
+ */
+class MyMoneyDbIntColumn : public MyMoneyDbColumn {
+ public:
+ enum size {TINY, SMALL, MEDIUM, BIG};
+ MyMoneyDbIntColumn (const QString& iname,
+ const size type = MEDIUM,
+ const bool isigned = true,
+ const bool iprimary = false,
+ const bool inotnull = false,
+ const QString &initVersion = "0.1"):
+ MyMoneyDbColumn (iname, "", iprimary, inotnull, initVersion),
+ m_type (type),
+ m_isSigned (isigned) {}
+ virtual ~MyMoneyDbIntColumn() {}
+ virtual const QString generateDDL (databaseTypeE dbType) const;
+ virtual MyMoneyDbIntColumn* clone () const;
+ private:
+ size m_type;
+ bool m_isSigned;
+};
+
+/**
+ * The MyMoneyDbTextColumn class is a representation of text db columns,
+ * for drivers that support it. If the driver does not support it, it is
+ * usually some sort of really large varchar or varchar2.
+ */
+class MyMoneyDbTextColumn : public MyMoneyDbColumn {
+ public:
+ enum size {TINY, NORMAL, MEDIUM, LONG};
+ MyMoneyDbTextColumn (const QString& iname,
+ const size type = MEDIUM,
+ const bool iprimary = false,
+ const bool inotnull = false,
+ const QString &initVersion = "0.1"):
+ MyMoneyDbColumn (iname, "", iprimary, inotnull, initVersion),
+ m_type (type) {}
+ virtual ~MyMoneyDbTextColumn() {}
+ virtual const QString generateDDL (databaseTypeE dbType) const;
+ virtual MyMoneyDbTextColumn* clone () const;
+ private:
+ size m_type;
+};
+
+/**
+ * The MyMoneyDbIndex class is a representation of a db index.
+ * To provide generic support for most databases, the table name,
+ * name of the index, and list of columns for the index are required.
+ * Additionally, the user can specify whether the index is unique or not.
+ *
+ * At this time, different types of index are not supported, since the portability
+ * is fairly limited.
+ */
+class MyMoneyDbIndex {
+ public:
+ MyMoneyDbIndex (const QString& table,
+ const QString& name,
+ const QStringList& columns,
+ bool unique = false):
+ m_table(table),
+ m_unique(unique),
+ m_name(name),
+ m_columns(columns)
+ {}
+ MyMoneyDbIndex () {}
+ inline const QString table () const {return m_table;}
+ inline bool isUnique () const {return m_unique;}
+ inline const QString name () const {return m_name;}
+ inline const QStringList columns () const {return m_columns;}
+ const QString generateDDL (databaseTypeE dbType) const;
+ private:
+ QString m_table;
+ bool m_unique;
+ QString m_name;
+ QStringList m_columns;
+};
+
+/**
+ * The MyMoneyDbTable class is a representation of a db table.
+ * It has a list of the columns (pointers to MyMoneyDbColumn types) and a
+ * list of any indices that may be on the table.
+ * Additionally, a string for a parameterized query for each of some common
+ * tasks on a table is created by the ctor.
+ *
+ * Const iterators over the list of columns are provided as a convenience.
+ */
+class MyMoneyDbTable {
+ public:
+ MyMoneyDbTable (const QString& iname,
+ const QValueList<KSharedPtr <MyMoneyDbColumn> >& ifields,
+ const QString& initVersion = "1.0"):
+ m_name(iname),
+ m_fields(ifields),
+ m_initVersion(initVersion) {}
+ MyMoneyDbTable (void) {}
+
+ inline const QString& name(void) const {return (m_name);}
+ inline const QString& insertString(void) const {return (m_insertString);};
+ inline const QString selectAllString(bool terminate = true) const
+ {return (terminate ? QString(m_selectAllString + ";") : m_selectAllString);};
+ inline const QString& updateString(void) const {return (m_updateString);};
+ inline const QString& deleteString(void) const {return (m_deleteString);};
+
+ /**
+ * This method determines the string required to drop the primary key for the table
+ * based on the db specific syntax.
+ *
+ * @param dbType The driver type of the database.
+ *
+ * @return QString for the syntax to drop the primary key.
+ */
+ const QString dropPrimaryKeyString(databaseTypeE dbType) const;
+ /**
+ * This method returns a comma-separated list of all column names in the table
+ *
+ * @return QString column list.
+ */
+ const QString columnList() const;
+ /**
+ * This method returns the string for changing a column's definition. It covers statements
+ * like ALTER TABLE..CHANGE COLUMN, MODIFY COLUMN, etc.
+ *
+ * @param dbType The driver type of the database.
+ * @param columnName The name of the column to be modified.
+ * @param newDef The MyMoneyColumn object of the new column definition.
+ *
+ * @return QString containing DDL to change the column.
+ */
+ const QString modifyColumnString(databaseTypeE dbType, const QString& columnName, const MyMoneyDbColumn& newDef) const;
+
+ /**
+ * This method builds all of the SQL strings for common operations.
+ */
+ void buildSQLStrings(void);
+
+ /**
+ * This method generates the DDL required to create the table.
+ *
+ * @param dbType The driver type of the database.
+ *
+ * @return QString of the DDL.
+ */
+ const QString generateCreateSQL (databaseTypeE dbType) const;
+
+ /**
+ * This method creates a MyMoneyDbIndex object and adds it to the list of indices for the table.
+ *
+ * @param name The name of the index.
+ * @param columns The list of the columns affected.
+ * @param unique Whether or not this should be a unique index.
+ */
+ void addIndex(const QString& name, const QStringList& columns, bool unique = false);
+
+ typedef QValueList<KSharedPtr <MyMoneyDbColumn> >::const_iterator field_iterator;
+ inline field_iterator begin(void) const {return m_fields.constBegin();}
+ inline field_iterator end(void) const {return m_fields.constEnd(); }
+ private:
+ QString m_name;
+ QValueList<KSharedPtr <MyMoneyDbColumn> > m_fields;
+
+ typedef QValueList<MyMoneyDbIndex>::const_iterator index_iterator;
+ QValueList<MyMoneyDbIndex> m_indices;
+ QString m_initVersion;
+ QString m_insertString; // string to insert a record
+ QString m_selectAllString; // to select all fields
+ QString m_updateString; // normal string for record update
+ QString m_deleteString; // string to delete 1 record
+};
+
+/**
+ * The MyMoneyDbView class is a representation of a db view.
+ *
+ * Views will be dropped and recreated on upgrade, so there is no need
+ * to do anything more complex than storing the name of the view and
+ * the CREATE VIEW string.
+ */
+class MyMoneyDbView {
+ public:
+ MyMoneyDbView (const QString& name,
+ const QString& createString,
+ const QString& initVersion = "0.1")
+ : m_name (name), m_createString (createString), m_initVersion (initVersion)
+ {}
+
+ MyMoneyDbView (void) {}
+
+ inline const QString& name(void) const {return (m_name);}
+ inline const QString createString(void) const {return (m_createString);};
+
+ private:
+ QString m_name;
+ QString m_createString;
+ QString m_initVersion;
+};
+
+/**
+ * The MyMoneyDbDef class is
+ */
+class MyMoneyDbDef {
+ friend class MyMoneyStorageSql;
+ friend class MyMoneyDatabaseMgr;
+public:
+ MyMoneyDbDef();
+ ~MyMoneyDbDef() {}
+
+ const QString generateSQL (const QString& driver) const;
+
+ typedef QMap<QString, MyMoneyDbTable>::const_iterator table_iterator;
+ inline table_iterator tableBegin(void) const {return m_tables.constBegin();}
+ inline table_iterator tableEnd(void) const {return m_tables.constEnd();}
+
+ typedef QMap<QString, MyMoneyDbView>::const_iterator view_iterator;
+ inline view_iterator viewBegin(void) const {return m_views.constBegin();}
+ inline view_iterator viewEnd(void) const {return m_views.constEnd();}
+
+ inline unsigned int currentVersion() const {return (m_currentVersion);};
+
+private:
+ const QString enclose(const QString& text) const
+ {return (QString("'" + text + "'"));};
+ static unsigned int m_currentVersion; // The current version of the database layout
+ MyMoneyDbDrivers m_drivers;
+#define TABLE(name) void name();
+#define VIEW(name) void name();
+ TABLE(FileInfo);
+ TABLE(Institutions);
+ TABLE(Payees);
+ TABLE(Accounts);
+ TABLE(Transactions);
+ TABLE(Splits);
+ TABLE(KeyValuePairs);
+ TABLE(Schedules);
+ TABLE(SchedulePaymentHistory);
+ TABLE(Securities);
+ TABLE(Prices);
+ TABLE(Currencies);
+ TABLE(Reports);
+ TABLE(Budgets);
+ VIEW(Balances);
+protected:
+ QMap<QString, MyMoneyDbTable> m_tables;
+ QMap<QString, MyMoneyDbView> m_views;
+};
+
+class IMyMoneySerialize;
+
+/**
+ * The MyMoneyDbColumn class is a base type for generic db columns.
+ * Derived types exist for several common column types.
+ */
+class MyMoneyStorageSql : public IMyMoneyStorageFormat, public QSqlDatabase, public KShared {
+public:
+
+ MyMoneyStorageSql (IMyMoneySerialize *storage, const KURL& = KURL());
+ virtual ~MyMoneyStorageSql() {close(true);}
+
+ unsigned int currentVersion() const {return (m_db.currentVersion());};
+
+ /**
+ * MyMoneyStorageSql - open database file
+ *
+ * @param url pseudo-URL of database to be opened
+ * @param openMode open mode, same as for QFile::open
+ * @param clear whether existing data can be deleted
+
+ * @return 0 - database successfully opened
+ * @return 1 - database not opened, use lastError function for reason
+ * @return -1 - output database not opened, contains data, clean not specified
+ *
+ */
+ int open(const KURL& url, int openMode, bool clear = false);
+ /**
+ * MyMoneyStorageSql close the database
+ *
+ * @return void
+ *
+ */
+ void close(bool logoff = true);
+ /**
+ * MyMoneyStorageSql read all the database into storage
+ *
+ * @return void
+ *
+ */
+ bool readFile(void);
+ /**
+ * MyMoneyStorageSql write/update the database from storage
+ *
+ * @return void
+ *
+ */
+ bool writeFile(void);
+
+ // check database type
+ bool isDb2() const { return (m_dbType == Db2);};
+ bool isInterbase() const { return (m_dbType == Interbase);};
+ bool isMysql() const { return (m_dbType == Mysql);};
+ bool isOracle8() const { return (m_dbType == Oracle8);};
+ bool isODBC3() const { return (m_dbType == ODBC3);};
+ bool isPostgresql() const { return (m_dbType == Postgresql);};
+ bool isSybase() const { return (m_dbType == Sybase);};
+ bool isSqlite3() const { return (m_dbType == Sqlite3);};
+
+ /**
+ * MyMoneyStorageSql generalized error routine
+ *
+ * @return : error message to be displayed
+ *
+ */
+ const QString& lastError() const {return (m_error);};
+ /**
+ * This method is used when a database file is open, and the data is to
+ * be saved in a different file or format. It will ensure that all data
+ * from the database is available in memory to enable it to be written.
+ */
+ virtual void fillStorage();
+ /**
+ * The following functions correspond to the identically named (usually) functions
+ * within the Storage Manager, and are called to update the database
+ */
+ void modifyUserInfo(const MyMoneyPayee& payee);
+ void addInstitution(const MyMoneyInstitution& inst);
+ void modifyInstitution(const MyMoneyInstitution& inst);
+ void removeInstitution(const MyMoneyInstitution& inst);
+ void addPayee(const MyMoneyPayee& payee);
+ void modifyPayee(const MyMoneyPayee& payee);
+ void removePayee(const MyMoneyPayee& payee);
+ void addAccount(const MyMoneyAccount& acc);
+ void modifyAccount(const MyMoneyAccount& acc);
+ void removeAccount(const MyMoneyAccount& acc);
+ void addTransaction(const MyMoneyTransaction& tx);
+ void modifyTransaction(const MyMoneyTransaction& tx);
+ void removeTransaction(const MyMoneyTransaction& tx);
+ void addSchedule(const MyMoneySchedule& sch);
+ void modifySchedule(const MyMoneySchedule& sch);
+ void removeSchedule(const MyMoneySchedule& sch);
+ void addSecurity(const MyMoneySecurity& sec);
+ void modifySecurity(const MyMoneySecurity& sec);
+ void removeSecurity(const MyMoneySecurity& sec);
+ void addPrice(const MyMoneyPrice& p);
+ void removePrice(const MyMoneyPrice& p);
+ void addCurrency(const MyMoneySecurity& sec);
+ void modifyCurrency(const MyMoneySecurity& sec);
+ void removeCurrency(const MyMoneySecurity& sec);
+ void addReport(const MyMoneyReport& rep);
+ void modifyReport(const MyMoneyReport& rep);
+ void removeReport(const MyMoneyReport& rep);
+ void addBudget(const MyMoneyBudget& bud);
+ void modifyBudget(const MyMoneyBudget& bud);
+ void removeBudget(const MyMoneyBudget& bud);
+
+ unsigned long transactionCount (const QString& aid = QString()) const;
+ inline const QMap<QString, unsigned long> transactionCountMap () const
+ {return (m_transactionCountMap);};
+ /**
+ * the storage manager also needs the following read entry points
+ */
+ const QMap<QString, MyMoneyAccount> fetchAccounts (const QStringList& idList = QStringList (), bool forUpdate = false) const;
+ const QMap<QString, MyMoneyMoney> fetchBalance(const QStringList& id, const QDate& date) const;
+ const QMap<QString, MyMoneyBudget> fetchBudgets (const QStringList& idList = QStringList (), bool forUpdate = false) const;
+ const QMap<QString, MyMoneySecurity> fetchCurrencies (const QStringList& idList = QStringList (), bool forUpdate = false) const;
+ const QMap<QString, MyMoneyInstitution> fetchInstitutions (const QStringList& idList = QStringList (), bool forUpdate = false) const;
+ const QMap<QString, MyMoneyPayee> fetchPayees (const QStringList& idList = QStringList (), bool forUpdate = false) const;
+ const MyMoneyPriceList fetchPrices (const QStringList& fromIdList = QStringList (), const QStringList& toIdList = QStringList(), bool forUpdate = false) const;
+ const MyMoneyPrice fetchSinglePrice (const QString& fromIdList, const QString& toIdList, const QDate& date, bool exactDate, bool forUpdate = false) const;
+ const QMap<QString, MyMoneyReport> fetchReports (const QStringList& idList = QStringList (), bool forUpdate = false) const;
+ const QMap<QString, MyMoneySchedule> fetchSchedules (const QStringList& idList = QStringList (), bool forUpdate = false) const;
+ const QMap<QString, MyMoneySecurity> fetchSecurities (const QStringList& idList = QStringList (), bool forUpdate = false) const;
+ const QMap<QString, MyMoneyTransaction> fetchTransactions (const QString& tidList = QString (), const QString& dateClause = QString(), bool forUpdate = false) const;
+ const QMap<QString, MyMoneyTransaction> fetchTransactions (const MyMoneyTransactionFilter& filter) const;
+ bool isReferencedByTransaction(const QString& id) const;
+
+ void readPayees(const QString&);
+ void readPayees(const QValueList<QString> payeeList = QValueList<QString>());
+ void readTransactions(const MyMoneyTransactionFilter& filter);
+ void setProgressCallback(void(*callback)(int, int, const QString&));
+
+ virtual void readFile(QIODevice* s, IMyMoneySerialize* storage) { Q_UNUSED(s); Q_UNUSED(storage) };
+ virtual void writeFile(QIODevice* s, IMyMoneySerialize* storage){ Q_UNUSED(s); Q_UNUSED(storage) };
+
+ void startCommitUnit (const QString& callingFunction);
+ bool endCommitUnit (const QString& callingFunction);
+ void cancelCommitUnit (const QString& callingFunction);
+
+ long unsigned getRecCount(const QString& table) const;
+ long unsigned getNextBudgetId() const;
+ long unsigned getNextAccountId() const;
+ long unsigned getNextInstitutionId() const;
+ long unsigned getNextPayeeId() const;
+ long unsigned getNextReportId() const;
+ long unsigned getNextScheduleId() const;
+ long unsigned getNextSecurityId() const;
+ long unsigned getNextTransactionId() const;
+
+ long unsigned incrementBudgetId();
+ long unsigned incrementAccountId();
+ long unsigned incrementInstitutionId();
+ long unsigned incrementPayeeId();
+ long unsigned incrementReportId();
+ long unsigned incrementScheduleId();
+ long unsigned incrementSecurityId();
+ long unsigned incrementTransactionId();
+
+ void loadAccountId(const unsigned long& id);
+ void loadTransactionId(const unsigned long& id);
+ void loadPayeeId(const unsigned long& id);
+ void loadInstitutionId(const unsigned long& id);
+ void loadScheduleId(const unsigned long& id);
+ void loadSecurityId(const unsigned long& id);
+ void loadReportId(const unsigned long& id);
+ void loadBudgetId(const unsigned long& id);
+
+private:
+ // a function to build a comprehensive error message
+ QString& buildError (const QSqlQuery& q, const QString& function, const QString& message) const;
+ // write routines
+ void writeUserInformation(void);
+ void writeInstitutions(void);
+ void writePayees(void);
+ void writeAccounts(void);
+ void writeTransactions(void);
+ void writeSchedules(void);
+ void writeSecurities(void);
+ void writePrices(void);
+ void writeCurrencies(void);
+ void writeFileInfo(void);
+ void writeReports(void);
+ void writeBudgets(void);
+
+ void writeInstitution(const MyMoneyInstitution& i, MyMoneySqlQuery& q);
+ void writePayee(const MyMoneyPayee& p, MyMoneySqlQuery& q, bool isUserInfo = false);
+ void writeAccount (const MyMoneyAccount& a, MyMoneySqlQuery& q);
+ void writeTransaction(const QString& txId, const MyMoneyTransaction& tx, MyMoneySqlQuery& q, const QString& type);
+ void writeSplits(const QString& txId, const QString& type, const QValueList<MyMoneySplit>& splitList);
+ void writeSplit(const QString& txId, const MyMoneySplit& split, const QString& type, const int splitId, MyMoneySqlQuery& q);
+ void writeSchedule(const MyMoneySchedule& sch, MyMoneySqlQuery& q, bool insert);
+ void writeSecurity(const MyMoneySecurity& security, MyMoneySqlQuery& q);
+ void writePricePair ( const MyMoneyPriceEntries& p);
+ void writePrice (const MyMoneyPrice& p);
+ void writeCurrency(const MyMoneySecurity& currency, MyMoneySqlQuery& q);
+ void writeReport (const MyMoneyReport& rep, MyMoneySqlQuery& q);
+ void writeBudget (const MyMoneyBudget& bud, MyMoneySqlQuery& q);
+ void writeKeyValuePairs(const QString& kvpType, const QString& kvpId, const QMap<QString, QString>& pairs);
+ void writeKeyValuePair(const QString& kvpType, const QString& kvpId,
+ const QString& kvpKey, const QString& kvpData);
+ // read routines
+ void readFileInfo(void);
+ void readLogonData(void);
+ void readUserInformation(void);
+ void readInstitutions(void);
+ void readAccounts(void);
+ void readTransaction(const QString id);
+ void readTransactions(const QString& tidList = QString(), const QString& dateClause = QString());
+ void readTransaction(MyMoneyTransaction &tx, const QString& tid);
+ void readSplit (MyMoneySplit& s, const MyMoneySqlQuery& q, const MyMoneyDbTable& t) const;
+ const MyMoneyKeyValueContainer readKeyValuePairs (const QString& kvpType, const QString& kvpId) const;
+ const QMap<QString, MyMoneyKeyValueContainer> readKeyValuePairs (const QString& kvpType, const QStringList& kvpIdList) const;
+ void readSchedules(void);
+ void readSecurities(void);
+ void readPrices(void);
+ void readCurrencies(void);
+ void readReports(void);
+ void readBudgets(void);
+
+ void deleteTransaction(const QString& id);
+ void deleteSchedule(const QString& id);
+ void deleteKeyValuePairs(const QString& kvpType, const QString& kvpId);
+ long unsigned calcHighId (const long unsigned&, const QString&);
+
+ void setVersion (const QString& version);
+
+ void signalProgress(int current, int total, const QString& = "") const;
+ void (*m_progressCallback)(int, int, const QString&);
+
+ //void startCommitUnit (const QString& callingFunction);
+ //void endCommitUnit (const QString& callingFunction);
+ //void cancelCommitUnit (const QString& callingFunction);
+ int splitState(const MyMoneyTransactionFilter::stateOptionE& state) const;
+
+ inline const QDate getDate (const QString& date) const {
+ return (date.isNull() ? QDate() : QDate::fromString(date, Qt::ISODate));
+ }
+
+ inline const QDateTime getDateTime (const QString& date) const {
+ return (date.isNull() ? QDateTime() : QDateTime::fromString(date, Qt::ISODate));
+ }
+
+ // open routines
+ /**
+ * MyMoneyStorageSql create database
+ *
+ * @param url pseudo-URL of database to be opened
+ *
+ * @return true - creation successful
+ * @return false - could not create
+ *
+ */
+ int createDatabase(const KURL& url);
+ int upgradeDb();
+ int upgradeToV1();
+ int upgradeToV2();
+ int upgradeToV3();
+ int upgradeToV4();
+ int upgradeToV5();
+ int upgradeToV6();
+ bool sqliteAlterTable(const MyMoneyDbTable& t);
+ bool addColumn(const MyMoneyDbTable& t,
+ const MyMoneyDbColumn& c,
+ const QString& after = QString());
+ bool addColumn(const QString& table,
+ const QString& column,
+ const QString& after = QString());
+ bool dropColumn(const MyMoneyDbTable& t,
+ const QString& c);
+ bool dropColumn(const QString& table,
+ const QString& column);
+
+// long long unsigned getRecCount(const QString& table);
+ int createTables();
+ void createTable(const MyMoneyDbTable& t);
+ void clean ();
+ int isEmpty();
+ // data
+ MyMoneyDbDrivers m_drivers;
+ databaseTypeE m_dbType;
+
+ MyMoneyDbDef m_db;
+ unsigned int m_dbVersion;
+ IMyMoneySerialize *m_storage;
+ IMyMoneyStorage *m_storagePtr;
+ // input options
+ bool m_loadAll; // preload all data
+ bool m_override; // override open if already in use
+ // error message
+ QString m_error;
+ // record counts
+ long unsigned m_institutions;
+ long unsigned m_accounts;
+ long unsigned m_payees;
+ long unsigned m_transactions;
+ long unsigned m_splits;
+ long unsigned m_securities;
+ long unsigned m_prices;
+ long unsigned m_currencies;
+ long unsigned m_schedules;
+ long unsigned m_reports;
+ long unsigned m_kvps;
+ long unsigned m_budgets;
+ // next id to use (for future archive)
+ long unsigned m_hiIdInstitutions;
+ long unsigned m_hiIdPayees;
+ long unsigned m_hiIdAccounts;
+ long unsigned m_hiIdTransactions;
+ long unsigned m_hiIdSchedules;
+ long unsigned m_hiIdSecurities;
+ long unsigned m_hiIdReports;
+ long unsigned m_hiIdBudgets;
+ // encrypt option - usage TBD
+ QString m_encryptData;
+
+ /**
+ * This variable is used to suppress status messages except during
+ * initial data load and final write
+
+ */
+ bool m_displayStatus;
+ /**
+ * On occasions, e.g. after a complex transaction search, or for populating a
+ * payee popup list, it becomes necessary to load all data into memory. The
+ * following flags will be set after such a load, to indicate that further
+ * retrievals are not needed.
+ */
+// bool m_transactionListRead;
+// bool m_payeeListRead;
+ /**
+ * This member variable holds a list of those accounts for which all
+ * transactions are in memory, thus saving reading them again
+ */
+// QValueList<QString> m_accountsLoaded;
+ /**
+ * This member variable is used when loading transactions to list all
+ * referenced payees, which can then be read into memory (if not already there)
+ */
+// QValueList<QString> m_payeeList;
+
+ void alert(QString s) const {qDebug("%s", s.ascii());}; // FIXME: remove...
+ /** The following keeps track of commitment units (known as transactions in SQL
+ * though it would be confusing to use that term within KMM). It is implemented
+ * as a stack for debug purposes. Long term, probably a count would suffice
+ */
+ QValueStack<QString> m_commitUnitStack;
+ /**
+ * This member variable is used to preload transactions for preferred accounts
+ */
+ MyMoneyTransactionFilter m_preferred;
+ /**
+ * This member variable is used because reading prices from a file uses the 'add...' function rather than a
+ * 'load...' function which other objects use. Having this variable allows us to avoid needing to check the
+ * database to see if this really is a new or modified price
+ */
+ bool m_readingPrices;
+ /**
+ * This member variable holds a map of transaction counts per account, indexed by
+ * the account id. It is used
+ * to avoid having to scan all transactions whenever a count is needed. It should
+ * probably be moved into the MyMoneyAccount object; maybe we will do that once
+ * the database code has been properly checked out
+ */
+ QMap<QString, unsigned long> m_transactionCountMap;
+ /**
+ * These member variables hold the user name and date/time of logon
+ */
+ QString m_logonUser;
+ QDateTime m_logonAt;
+ QDateTime m_txPostDate; // FIXME: remove when Tom puts date into split object
+
+ //Disable copying
+ MyMoneyStorageSql (const MyMoneyStorageSql& rhs);
+ MyMoneyStorageSql& operator= (const MyMoneyStorageSql& rhs);
+ //
+ bool m_newDatabase;
+};
+#endif // MYMONEYSTORAGESQL_H
diff --git a/kmymoney2/mymoney/storage/mymoneystoragexml.cpp b/kmymoney2/mymoney/storage/mymoneystoragexml.cpp
new file mode 100644
index 0000000..e8027d1
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneystoragexml.cpp
@@ -0,0 +1,908 @@
+/***************************************************************************
+ mymoneystoragexml.cpp - description
+ -------------------
+ begin : Thu Oct 24 2002
+ copyright : (C) 2002 by Kevin Tambascio
+ (C) 2004 by Thomas Baumgart
+ email : Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#include "config.h"
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+#include <qfile.h>
+#include <qdom.h>
+#include <qmap.h>
+#include <qxml.h>
+
+// ----------------------------------------------------------------------------
+// KDE Includes
+
+#include "kdecompat.h"
+#include <klocale.h>
+#include <kdebug.h>
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include "mymoneystoragexml.h"
+#include "../mymoneyreport.h"
+#include "../mymoneybudget.h"
+#include "../mymoneyinstitution.h"
+
+unsigned int MyMoneyStorageXML::fileVersionRead = 0;
+unsigned int MyMoneyStorageXML::fileVersionWrite = 0;
+
+
+class MyMoneyStorageXML::Private
+{
+ friend class MyMoneyStorageXML;
+public:
+ Private() {}
+
+ QMap<QString, MyMoneyInstitution> iList;
+ QMap<QString, MyMoneyAccount> aList;
+ QMap<QString, MyMoneyTransaction> tList;
+ QMap<QString, MyMoneyPayee> pList;
+ QMap<QString, MyMoneySchedule> sList;
+ QMap<QString, MyMoneySecurity> secList;
+ QMap<QString, MyMoneyReport> rList;
+ QMap<QString, MyMoneyBudget> bList;
+ QMap<MyMoneySecurityPair, MyMoneyPriceEntries> prList;
+
+ QString m_fromSecurity;
+ QString m_toSecurity;
+
+};
+
+
+class MyMoneyXmlContentHandler : public QXmlContentHandler
+{
+public:
+ MyMoneyXmlContentHandler(MyMoneyStorageXML* reader);
+ virtual ~MyMoneyXmlContentHandler() {}
+ virtual void setDocumentLocator (QXmlLocator * locator) { m_loc = locator; }
+ virtual bool startDocument (void);
+ virtual bool endDocument (void);
+ virtual bool startPrefixMapping(const QString & prefix, const QString & uri);
+ virtual bool endPrefixMapping(const QString & prefix);
+ virtual bool startElement(const QString & namespaceURI, const QString & localName, const QString & qName, const QXmlAttributes & atts);
+ virtual bool endElement(const QString & namespaceURI, const QString & localName, const QString & qName);
+ virtual bool characters(const QString & ch);
+ virtual bool ignorableWhitespace(const QString & ch);
+ virtual bool processingInstruction(const QString & target, const QString & data);
+ virtual bool skippedEntity(const QString & name);
+ virtual QString errorString(void);
+
+private:
+ MyMoneyStorageXML* m_reader;
+ QXmlLocator* m_loc;
+ int m_level;
+ int m_elementCount;
+ QDomDocument m_doc;
+ QDomElement m_baseNode;
+ QDomElement m_currNode;
+ QString m_errMsg;
+};
+
+MyMoneyXmlContentHandler::MyMoneyXmlContentHandler(MyMoneyStorageXML* reader) :
+ m_reader(reader),
+ m_loc(0),
+ m_level(0),
+ m_elementCount(0)
+{
+}
+
+bool MyMoneyXmlContentHandler::startDocument(void)
+{
+ qDebug("startDocument");
+ return true;
+}
+
+bool MyMoneyXmlContentHandler::endDocument(void)
+{
+ qDebug("endDocument");
+ return true;
+}
+
+bool MyMoneyXmlContentHandler::skippedEntity (const QString & /* name */)
+{
+ // qDebug(QString("Skipped entity '%1'").arg(name));
+ return true;
+}
+
+bool MyMoneyXmlContentHandler::startPrefixMapping (const QString& /*prefix */, const QString & /* uri */)
+{
+ // qDebug(QString("start prefix '%1', '%2'").arg(prefix).arg(uri));
+ return true;
+}
+
+bool MyMoneyXmlContentHandler::endPrefixMapping (const QString& /* prefix */)
+{
+ // qDebug(QString("end prefix '%1'").arg(prefix));
+ return true;
+}
+
+bool MyMoneyXmlContentHandler::startElement (const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName, const QXmlAttributes & atts)
+{
+ if(m_level == 0) {
+ QString s = qName.lower();
+ if(s == "transaction"
+ || s == "account"
+ || s == "price"
+ || s == "payee"
+ || s == "currency"
+ || s == "security"
+ || s == "keyvaluepairs"
+ || s == "institution"
+ || s == "report"
+ || s == "budget"
+ || s == "fileinfo"
+ || s == "user"
+ || s == "scheduled_tx") {
+ m_baseNode = m_doc.createElement(qName);
+ for(int i=0; i < atts.count(); ++i) {
+ m_baseNode.setAttribute(atts.qName(i), atts.value(i));
+ }
+ m_currNode = m_baseNode;
+ m_level = 1;
+
+ } else if(s == "transactions") {
+ qDebug("reading transactions");
+ if(atts.count()) {
+ int count = atts.value(QString("count")).toUInt();
+ m_reader->signalProgress(0, count, i18n("Loading transactions..."));
+ m_elementCount = 0;
+ }
+ } else if(s == "accounts") {
+ qDebug("reading accounts");
+ if(atts.count()) {
+ int count = atts.value(QString("count")).toUInt();
+ m_reader->signalProgress(0, count, i18n("Loading accounts..."));
+ m_elementCount = 0;
+ }
+ } else if(s == "securities") {
+ qDebug("reading securities");
+ if(atts.count()) {
+ int count = atts.value(QString("count")).toUInt();
+ m_reader->signalProgress(0, count, i18n("Loading securities..."));
+ m_elementCount = 0;
+ }
+ } else if(s == "reports") {
+ qDebug("reading reports");
+ if(atts.count()) {
+ int count = atts.value(QString("count")).toUInt();
+ m_reader->signalProgress(0, count, i18n("Loading reports..."));
+ m_elementCount = 0;
+ }
+ } else if(s == "prices") {
+ qDebug("reading prices");
+ m_elementCount = 0;
+ } else if(s == "pricepair") {
+ if(atts.count()) {
+ m_reader->d->m_fromSecurity = atts.value(QString("from"));
+ m_reader->d->m_toSecurity = atts.value(QString("to"));
+ }
+ }
+
+ } else {
+ m_level++;
+ QDomElement node = m_doc.createElement(qName);
+ for(int i=0; i < atts.count(); ++i) {
+ node.setAttribute(atts.qName(i), atts.value(i));
+ }
+ m_currNode.appendChild(node);
+ m_currNode = node;
+ }
+ return true;
+}
+
+bool MyMoneyXmlContentHandler::endElement(const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName)
+{
+ bool rc = true;
+ QString s = qName.lower();
+ if(m_level) {
+ m_currNode = m_currNode.parentNode().toElement();
+ m_level--;
+ if(!m_level) {
+ try {
+ if(s == "transaction") {
+ MyMoneyTransaction t(m_baseNode);
+ if(!t.id().isEmpty())
+ m_reader->d->tList[t.uniqueSortKey()] = t;
+ } else if(s == "account") {
+ MyMoneyAccount a(m_baseNode);
+ if(!a.id().isEmpty())
+ m_reader->d->aList[a.id()] = a;
+ } else if(s == "payee") {
+ MyMoneyPayee p(m_baseNode);
+ if(!p.id().isEmpty())
+ m_reader->d->pList[p.id()] = p;
+ } else if(s == "currency") {
+ MyMoneySecurity s(m_baseNode);
+ if(!s.id().isEmpty())
+ m_reader->d->secList[s.id()] = s;
+ } else if(s == "security") {
+ MyMoneySecurity s(m_baseNode);
+ if(!s.id().isEmpty())
+ m_reader->d->secList[s.id()] = s;
+ } else if(s == "keyvaluepairs") {
+ MyMoneyKeyValueContainer kvp(m_baseNode);
+ m_reader->m_storage->setPairs(kvp.pairs());
+ } else if(s == "institution") {
+ MyMoneyInstitution i(m_baseNode);
+ if(!i.id().isEmpty())
+ m_reader->d->iList[i.id()] = i;
+ } else if(s == "report") {
+ MyMoneyReport r(m_baseNode);
+ if(!r.id().isEmpty())
+ m_reader->d->rList[r.id()] = r;
+ } else if(s == "budget") {
+ MyMoneyBudget b(m_baseNode);
+ if(!b.id().isEmpty())
+ m_reader->d->bList[b.id()] = b;
+ } else if(s == "fileinfo") {
+ rc = m_reader->readFileInformation(m_baseNode);
+ } else if(s == "user") {
+ rc = m_reader->readUserInformation(m_baseNode);
+ } else if(s == "scheduled_tx") {
+ MyMoneySchedule s(m_baseNode);
+ if(!s.id().isEmpty())
+ m_reader->d->sList[s.id()] = s;
+ } else if(s == "price") {
+ MyMoneyPrice p(m_reader->d->m_fromSecurity, m_reader->d->m_toSecurity, m_baseNode);
+ m_reader->d->prList[MyMoneySecurityPair(m_reader->d->m_fromSecurity, m_reader->d->m_toSecurity)][p.date()] = p;
+ } else {
+ m_errMsg = i18n("Unknown XML tag %1 found in line %2").arg(qName).arg(m_loc->lineNumber());
+ kdWarning() << m_errMsg << endl;
+ rc = false;
+ }
+ m_reader->signalProgress(++m_elementCount, 0);
+ } catch(MyMoneyException* e) {
+ m_errMsg = i18n("Exception while creating a %1 element: %2").arg(s).arg(e->what());
+ kdWarning() << m_errMsg << endl;
+ delete e;
+ rc = false;
+ }
+ m_doc = QDomDocument();
+ }
+ } else {
+ if(s == "institutions") {
+ // last institution read, now dump them into the engine
+ m_reader->m_storage->loadInstitutions(m_reader->d->iList);
+ m_reader->d->iList.clear();
+ m_reader->signalProgress(-1, -1);
+ } else if(s == "accounts") {
+ // last account read, now dump them into the engine
+ m_reader->m_storage->loadAccounts(m_reader->d->aList);
+ m_reader->d->aList.clear();
+ m_reader->signalProgress(-1, -1);
+ } else if(s == "payees") {
+ // last payee read, now dump them into the engine
+ m_reader->m_storage->loadPayees(m_reader->d->pList);
+ m_reader->d->pList.clear();
+ m_reader->signalProgress(-1, -1);
+ } else if(s == "transactions") {
+ // last transaction read, now dump them into the engine
+ m_reader->m_storage->loadTransactions(m_reader->d->tList);
+ m_reader->d->tList.clear();
+ m_reader->signalProgress(-1, -1);
+ } else if(s == "schedules") {
+ // last schedule read, now dump them into the engine
+ m_reader->m_storage->loadSchedules(m_reader->d->sList);
+ m_reader->d->sList.clear();
+ m_reader->signalProgress(-1, -1);
+ } else if(s == "securities") {
+ // last security read, now dump them into the engine
+ m_reader->m_storage->loadSecurities(m_reader->d->secList);
+ m_reader->d->secList.clear();
+ m_reader->signalProgress(-1, -1);
+ } else if(s == "currencies") {
+ // last currency read, now dump them into the engine
+ m_reader->m_storage->loadCurrencies(m_reader->d->secList);
+ m_reader->d->secList.clear();
+ m_reader->signalProgress(-1, -1);
+ } else if(s == "reports") {
+ // last report read, now dump them into the engine
+ m_reader->m_storage->loadReports(m_reader->d->rList);
+ m_reader->d->rList.clear();
+ m_reader->signalProgress(-1, -1);
+ } else if(s == "budgets") {
+ // last budget read, now dump them into the engine
+ m_reader->m_storage->loadBudgets(m_reader->d->bList);
+ m_reader->d->bList.clear();
+ m_reader->signalProgress(-1, -1);
+ } else if(s == "prices") {
+ // last price read, now dump them into the engine
+ m_reader->m_storage->loadPrices(m_reader->d->prList);
+ m_reader->d->bList.clear();
+ m_reader->signalProgress(-1, -1);
+ }
+ }
+ return rc;
+}
+
+bool MyMoneyXmlContentHandler::characters(const QString& /* ch */)
+{
+ return true;
+}
+
+bool MyMoneyXmlContentHandler::ignorableWhitespace(const QString& /* ch */)
+{
+ return true;
+}
+
+bool MyMoneyXmlContentHandler::processingInstruction(const QString& /* target */, const QString& /* data */)
+{
+ return true;
+}
+
+QString MyMoneyXmlContentHandler::errorString(void)
+{
+ return m_errMsg;
+}
+
+
+
+
+
+
+
+MyMoneyStorageXML::MyMoneyStorageXML() :
+ m_storage(0),
+ m_doc(0),
+ d(new Private())
+{
+}
+
+MyMoneyStorageXML::~MyMoneyStorageXML()
+{
+ delete d;
+}
+
+// Function to read in the file, send to XML parser.
+void MyMoneyStorageXML::readFile(QIODevice* pDevice, IMyMoneySerialize* storage)
+{
+ Q_CHECK_PTR(storage);
+ Q_CHECK_PTR(pDevice);
+ if(!storage)
+ return;
+
+ m_storage = storage;
+
+ m_doc = new QDomDocument;
+ Q_CHECK_PTR(m_doc);
+
+ qDebug("reading file");
+ // creating the QXmlInputSource object based on a QIODevice object
+ // reads all data of the underlying object into memory. I have not
+ // found an object that reads on the fly. I tried to derive one myself,
+ // but there could be a severe problem with decoding when reading
+ // blocks of data and not a stream. So I left it the way it is. (ipwizard)
+ QXmlInputSource xml(pDevice);
+
+ qDebug("start parsing file");
+ MyMoneyXmlContentHandler mmxml(this);
+ QXmlSimpleReader reader;
+ reader.setContentHandler(&mmxml);
+
+ if(!reader.parse(&xml, false)) {
+ delete m_doc;
+ m_doc = NULL;
+ signalProgress(-1, -1);
+ throw new MYMONEYEXCEPTION("File was not parsable!");
+ }
+
+ // check if we need to build up the account balances
+ if(fileVersionRead < 2)
+ m_storage->rebuildAccountBalances();
+
+ delete m_doc;
+ m_doc = NULL;
+
+ // this seems to be nonsense, but it clears the dirty flag
+ // as a side-effect.
+ m_storage->setLastModificationDate(m_storage->lastModificationDate());
+ m_storage = NULL;
+
+ //hides the progress bar.
+ signalProgress(-1, -1);
+}
+
+void MyMoneyStorageXML::writeFile(QIODevice* qf, IMyMoneySerialize* storage)
+{
+ Q_CHECK_PTR(qf);
+ Q_CHECK_PTR(storage);
+ if(!storage)
+ {
+ return;
+ }
+ m_storage = storage;
+
+ // qDebug("XMLWRITER: Starting file write");
+ m_doc = new QDomDocument("KMYMONEY-FILE");
+ Q_CHECK_PTR(m_doc);
+ QDomProcessingInstruction instruct = m_doc->createProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\"");
+ m_doc->appendChild(instruct);
+
+ QDomElement mainElement = m_doc->createElement("KMYMONEY-FILE");
+ m_doc->appendChild(mainElement);
+
+ QDomElement fileInfo = m_doc->createElement("FILEINFO");
+ writeFileInformation(fileInfo);
+ mainElement.appendChild(fileInfo);
+
+ QDomElement userInfo = m_doc->createElement("USER");
+ writeUserInformation(userInfo);
+ mainElement.appendChild(userInfo);
+
+ QDomElement institutions = m_doc->createElement("INSTITUTIONS");
+ writeInstitutions(institutions);
+ mainElement.appendChild(institutions);
+
+ QDomElement payees = m_doc->createElement("PAYEES");
+ writePayees(payees);
+ mainElement.appendChild(payees);
+
+ QDomElement accounts = m_doc->createElement("ACCOUNTS");
+ writeAccounts(accounts);
+ mainElement.appendChild(accounts);
+
+ QDomElement transactions = m_doc->createElement("TRANSACTIONS");
+ writeTransactions(transactions);
+ mainElement.appendChild(transactions);
+
+ QDomElement keyvalpairs = writeKeyValuePairs(m_storage->pairs());
+ mainElement.appendChild(keyvalpairs);
+
+ QDomElement schedules = m_doc->createElement("SCHEDULES");
+ writeSchedules(schedules);
+ mainElement.appendChild(schedules);
+
+ QDomElement equities = m_doc->createElement("SECURITIES");
+ writeSecurities(equities);
+ mainElement.appendChild(equities);
+
+ QDomElement currencies = m_doc->createElement("CURRENCIES");
+ writeCurrencies(currencies);
+ mainElement.appendChild(currencies);
+
+ QDomElement prices = m_doc->createElement("PRICES");
+ writePrices(prices);
+ mainElement.appendChild(prices);
+
+ QDomElement reports = m_doc->createElement("REPORTS");
+ writeReports(reports);
+ mainElement.appendChild(reports);
+
+ QDomElement budgets = m_doc->createElement("BUDGETS");
+ writeBudgets(budgets);
+ mainElement.appendChild(budgets);
+
+ QTextStream stream(qf);
+ stream.setEncoding(QTextStream::UnicodeUTF8);
+ stream << m_doc->toString();
+
+ delete m_doc;
+ m_doc = NULL;
+
+ //hides the progress bar.
+ signalProgress(-1, -1);
+
+ // this seems to be nonsense, but it clears the dirty flag
+ // as a side-effect.
+ m_storage->setLastModificationDate(m_storage->lastModificationDate());
+
+ m_storage = NULL;
+}
+
+bool MyMoneyStorageXML::readFileInformation(const QDomElement& fileInfo)
+{
+ signalProgress(0, 3, i18n("Loading file information..."));
+ bool rc = true;
+ QDomElement temp = findChildElement("CREATION_DATE", fileInfo);
+ if (temp == QDomElement()) {
+ rc = false;
+ }
+ QString strDate = QStringEmpty(temp.attribute("date"));
+ m_storage->setCreationDate(stringToDate(strDate));
+ signalProgress(1, 0);
+
+ temp = findChildElement("LAST_MODIFIED_DATE", fileInfo);
+ if (temp == QDomElement()) {
+ rc = false;
+ }
+ strDate = QStringEmpty(temp.attribute("date"));
+ m_storage->setLastModificationDate(stringToDate(strDate));
+ signalProgress(2, 0);
+
+ temp = findChildElement("VERSION", fileInfo);
+ if (temp == QDomElement()) {
+ rc = false;
+ }
+ QString strVersion = QStringEmpty(temp.attribute("id"));
+ fileVersionRead = strVersion.toUInt(NULL, 16);
+
+ temp = findChildElement("FIXVERSION", fileInfo);
+ if (temp != QDomElement()) {
+ QString strFixVersion = QStringEmpty(temp.attribute("id"));
+ m_storage->setFileFixVersion (strFixVersion.toUInt());
+ }
+ // FIXME The old version stuff used this rather odd number
+ // We now use increments
+ if(fileVersionRead == VERSION_0_60_XML)
+ fileVersionRead = 1;
+ signalProgress(3, 0);
+
+ return rc;
+}
+
+void MyMoneyStorageXML::writeFileInformation(QDomElement& fileInfo)
+{
+ QDomElement creationDate = m_doc->createElement("CREATION_DATE");
+ creationDate.setAttribute("date", dateToString(m_storage->creationDate()));
+ fileInfo.appendChild(creationDate);
+
+ QDomElement lastModifiedDate = m_doc->createElement("LAST_MODIFIED_DATE");
+ lastModifiedDate.setAttribute("date", dateToString(m_storage->lastModificationDate()));
+ fileInfo.appendChild(lastModifiedDate);
+
+ QDomElement version = m_doc->createElement("VERSION");
+
+ version.setAttribute("id", "1");
+ fileInfo.appendChild(version);
+
+ QDomElement fixVersion = m_doc->createElement("FIXVERSION");
+ fixVersion.setAttribute("id", m_storage->fileFixVersion());
+ fileInfo.appendChild(fixVersion);
+}
+
+void MyMoneyStorageXML::writeUserInformation(QDomElement& userInfo)
+{
+ MyMoneyPayee user = m_storage->user();
+ userInfo.setAttribute("name", user.name());
+ userInfo.setAttribute("email", user.email());
+
+ QDomElement address = m_doc->createElement("ADDRESS");
+ address.setAttribute("street", user.address());
+ address.setAttribute("city", user.city());
+ address.setAttribute("county", user.state());
+ address.setAttribute("zipcode", user.postcode());
+ address.setAttribute("telephone", user.telephone());
+
+ userInfo.appendChild(address);
+}
+
+bool MyMoneyStorageXML::readUserInformation(const QDomElement& userElement)
+{
+ bool rc = true;
+ signalProgress(0, 1, i18n("Loading user information..."));
+
+ MyMoneyPayee user;
+ user.setName(QStringEmpty(userElement.attribute("name")));
+ user.setEmail(QStringEmpty(userElement.attribute("email")));
+
+ QDomElement addressNode = findChildElement("ADDRESS", userElement);
+ if(!addressNode.isNull()) {
+ user.setAddress(QStringEmpty(addressNode.attribute("street")));
+ user.setCity(QStringEmpty(addressNode.attribute("city")));
+ user.setState(QStringEmpty(addressNode.attribute("county")));
+ user.setPostcode(QStringEmpty(addressNode.attribute("zipcode")));
+ user.setTelephone(QStringEmpty(addressNode.attribute("telephone")));
+ }
+
+ m_storage->setUser(user);
+ signalProgress(1, 0);
+
+ return rc;
+}
+
+void MyMoneyStorageXML::writeInstitutions(QDomElement& institutions)
+{
+ const QValueList<MyMoneyInstitution> list = m_storage->institutionList();
+ QValueList<MyMoneyInstitution>::ConstIterator it;
+ institutions.setAttribute("count", list.count());
+
+ for(it = list.begin(); it != list.end(); ++it)
+ writeInstitution(institutions, *it);
+}
+
+void MyMoneyStorageXML::writeInstitution(QDomElement& institution, const MyMoneyInstitution& i)
+{
+ i.writeXML(*m_doc, institution);
+}
+
+void MyMoneyStorageXML::writePayees(QDomElement& payees)
+{
+ const QValueList<MyMoneyPayee> list = m_storage->payeeList();
+ QValueList<MyMoneyPayee>::ConstIterator it;
+ payees.setAttribute("count", list.count());
+
+ for(it = list.begin(); it != list.end(); ++it)
+ writePayee(payees, *it);
+}
+
+void MyMoneyStorageXML::writePayee(QDomElement& payee, const MyMoneyPayee& p)
+{
+ p.writeXML(*m_doc, payee);
+}
+
+void MyMoneyStorageXML::writeAccounts(QDomElement& accounts)
+{
+ QValueList<MyMoneyAccount> list;
+ m_storage->accountList(list);
+ QValueList<MyMoneyAccount>::ConstIterator it;
+ accounts.setAttribute("count", list.count()+5);
+
+ writeAccount(accounts, m_storage->asset());
+ writeAccount(accounts, m_storage->liability());
+ writeAccount(accounts, m_storage->expense());
+ writeAccount(accounts, m_storage->income());
+ writeAccount(accounts, m_storage->equity());
+
+ signalProgress(0, list.count(), i18n("Saving accounts..."));
+ int i = 0;
+ for(it = list.begin(); it != list.end(); ++it, ++i) {
+ writeAccount(accounts, *it);
+ signalProgress(i, 0);
+ }
+}
+
+void MyMoneyStorageXML::writeAccount(QDomElement& account, const MyMoneyAccount& p)
+{
+ p.writeXML(*m_doc, account);
+}
+
+void MyMoneyStorageXML::writeTransactions(QDomElement& transactions)
+{
+ MyMoneyTransactionFilter filter;
+ filter.setReportAllSplits(false);
+ QValueList<MyMoneyTransaction> list;
+ m_storage->transactionList(list, filter);
+ transactions.setAttribute("count", list.count());
+
+ QValueList<MyMoneyTransaction>::ConstIterator it;
+
+ signalProgress(0, list.count(), i18n("Saving transactions..."));
+
+ int i = 0;
+ for(it = list.begin(); it != list.end(); ++it, ++i)
+ {
+ writeTransaction(transactions, *it);
+ signalProgress(i, 0);
+ }
+}
+
+void MyMoneyStorageXML::writeTransaction(QDomElement& transaction, const MyMoneyTransaction& tx)
+{
+ tx.writeXML(*m_doc, transaction);
+}
+
+void MyMoneyStorageXML::writeSchedules(QDomElement& scheduled)
+{
+ const QValueList<MyMoneySchedule> list = m_storage->scheduleList();
+ QValueList<MyMoneySchedule>::ConstIterator it;
+ scheduled.setAttribute("count", list.count());
+
+ for(it = list.begin(); it != list.end(); ++it)
+ {
+ this->writeSchedule(scheduled, *it);
+ }
+}
+
+void MyMoneyStorageXML::writeSchedule(QDomElement& scheduledTx, const MyMoneySchedule& tx)
+{
+ tx.writeXML(*m_doc, scheduledTx);
+}
+
+void MyMoneyStorageXML::writeSecurities(QDomElement& equities)
+{
+ const QValueList<MyMoneySecurity> securityList = m_storage->securityList();
+ equities.setAttribute("count", securityList.count());
+ if(securityList.size())
+ {
+ for(QValueList<MyMoneySecurity>::ConstIterator it = securityList.begin(); it != securityList.end(); ++it)
+ {
+ writeSecurity(equities, (*it));
+ }
+ }
+}
+
+void MyMoneyStorageXML::writeSecurity(QDomElement& securityElement, const MyMoneySecurity& security)
+{
+ security.writeXML(*m_doc, securityElement);
+}
+
+void MyMoneyStorageXML::writeCurrencies(QDomElement& currencies)
+{
+ const QValueList<MyMoneySecurity> currencyList = m_storage->currencyList();
+ currencies.setAttribute("count", currencyList.count());
+ if(currencyList.size())
+ {
+ for(QValueList<MyMoneySecurity>::ConstIterator it = currencyList.begin(); it != currencyList.end(); ++it)
+ {
+ writeSecurity(currencies, (*it));
+ }
+ }
+}
+
+void MyMoneyStorageXML::writeReports(QDomElement& parent)
+{
+ const QValueList<MyMoneyReport> list = m_storage->reportList();
+ QValueList<MyMoneyReport>::ConstIterator it;
+ parent.setAttribute("count", list.count());
+
+ signalProgress(0, list.count(), i18n("Saving reports..."));
+ unsigned i = 0;
+ for(it = list.begin(); it != list.end(); ++it)
+ {
+ (*it).writeXML(*m_doc, parent);
+ signalProgress(++i, 0);
+ }
+}
+
+void MyMoneyStorageXML::writeBudgets(QDomElement& parent)
+{
+ const QValueList<MyMoneyBudget> list = m_storage->budgetList();
+ QValueList<MyMoneyBudget>::ConstIterator it;
+ parent.setAttribute("count", list.count());
+
+ signalProgress(0, list.count(), i18n("Saving budgets..."));
+ unsigned i = 0;
+ for(it = list.begin(); it != list.end(); ++it)
+ {
+ writeBudget(parent, (*it));
+ signalProgress(++i, 0);
+ }
+}
+
+void MyMoneyStorageXML::writeBudget(QDomElement& budget, const MyMoneyBudget& b)
+{
+ b.writeXML(*m_doc, budget);
+}
+
+
+QDomElement MyMoneyStorageXML::findChildElement(const QString& name, const QDomElement& root)
+{
+ QDomNode child = root.firstChild();
+ while(!child.isNull())
+ {
+ if(child.isElement())
+ {
+ QDomElement childElement = child.toElement();
+ if(name == childElement.tagName())
+ {
+ return childElement;
+ }
+ }
+
+ child = child.nextSibling();
+ }
+ return QDomElement();
+}
+
+QDomElement MyMoneyStorageXML::writeKeyValuePairs(const QMap<QString, QString> pairs)
+{
+ if(m_doc)
+ {
+ QDomElement keyValPairs = m_doc->createElement("KEYVALUEPAIRS");
+
+ QMap<QString, QString>::const_iterator it;
+ for(it = pairs.begin(); it != pairs.end(); ++it)
+ {
+ QDomElement pair = m_doc->createElement("PAIR");
+ pair.setAttribute("key", it.key());
+ pair.setAttribute("value", it.data());
+ keyValPairs.appendChild(pair);
+ }
+ return keyValPairs;
+ }
+ return QDomElement();
+}
+
+void MyMoneyStorageXML::writePrices(QDomElement& prices)
+{
+ const MyMoneyPriceList list = m_storage->priceList();
+ MyMoneyPriceList::ConstIterator it;
+ prices.setAttribute("count", list.count());
+
+ for(it = list.begin(); it != list.end(); ++it)
+ {
+ QDomElement price = m_doc->createElement("PRICEPAIR");
+ price.setAttribute("from", it.key().first);
+ price.setAttribute("to", it.key().second);
+ writePricePair(price, *it);
+ prices.appendChild(price);
+ }
+}
+
+void MyMoneyStorageXML::writePricePair(QDomElement& price, const MyMoneyPriceEntries& p)
+{
+ MyMoneyPriceEntries::ConstIterator it;
+ for(it = p.begin(); it != p.end(); ++it) {
+ QDomElement entry = m_doc->createElement("PRICE");
+ writePrice(entry, *it);
+ price.appendChild(entry);
+ }
+}
+
+void MyMoneyStorageXML::writePrice(QDomElement& price, const MyMoneyPrice& p)
+{
+ price.setAttribute("date", p.date().toString(Qt::ISODate));
+ price.setAttribute("price", p.rate(QString()).toString());
+ price.setAttribute("source", p.source());
+}
+
+void MyMoneyStorageXML::setProgressCallback(void(*callback)(int, int, const QString&))
+{
+ m_progressCallback = callback;
+}
+
+void MyMoneyStorageXML::signalProgress(int current, int total, const QString& msg)
+{
+ if(m_progressCallback != 0)
+ (*m_progressCallback)(current, total, msg);
+}
+
+/*!
+ This convenience function returns all of the remaining data in the
+ device.
+
+ @note It's copied from the original Qt sources and modified to
+ fix a problem with KFilterDev that does not correctly return
+ atEnd() status in certain circumstances which caused our
+ application to lock at startup.
+*/
+QByteArray QIODevice::readAll()
+{
+ if ( isDirectAccess() ) {
+ // we know the size
+ int n = size()-at(); // ### fix for 64-bit or large files?
+ int totalRead = 0;
+ QByteArray ba( n );
+ char* c = ba.data();
+ while ( n ) {
+ int r = readBlock( c, n );
+ if ( r < 0 )
+ return QByteArray();
+ n -= r;
+ c += r;
+ totalRead += r;
+ // If we have a translated file, then it is possible that
+ // we read less bytes than size() reports
+ if ( atEnd() ) {
+ ba.resize( totalRead );
+ break;
+ }
+ }
+ return ba;
+ } else {
+ // read until we reach the end
+ const int blocksize = 512;
+ int nread = 0;
+ QByteArray ba;
+ int r = 1;
+ while ( !atEnd() && r != 0) {
+ ba.resize( nread + blocksize );
+ r = readBlock( ba.data()+nread, blocksize );
+ if ( r < 0 )
+ return QByteArray();
+ nread += r;
+ }
+ ba.resize( nread );
+ return ba;
+ }
+}
+
diff --git a/kmymoney2/mymoney/storage/mymoneystoragexml.h b/kmymoney2/mymoney/storage/mymoneystoragexml.h
new file mode 100644
index 0000000..8485978
--- /dev/null
+++ b/kmymoney2/mymoney/storage/mymoneystoragexml.h
@@ -0,0 +1,156 @@
+/***************************************************************************
+ mymoneystoragexml.h - description
+ -------------------
+ begin : Thu Oct 24 2002
+ copyright : (C) 2002 by Kevin Tambascio
+ (C) 2004 by Thomas Baumgart
+ email : Thomas Baumgart <ipwizard@users.sourceforge.net>
+ Kevin Tambascio <ktambascio@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. *
+ * *
+ ***************************************************************************/
+
+#ifndef MYMONEYSTORAGEXML_H
+#define MYMONEYSTORAGEXML_H
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+#include <qdom.h>
+#include <qdatastream.h>
+class QIODevice;
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include "imymoneyserialize.h"
+#include "imymoneystorageformat.h"
+class MyMoneyXmlContentHandler;
+
+/**
+ *@author Kevin Tambascio (ktambascio@users.sourceforge.net)
+ */
+
+#define VERSION_0_60_XML 0x10000010 // Version 0.5 file version info
+#define VERSION_0_61_XML 0x10000011 // use 8 bytes for MyMoneyMoney objects
+
+class MyMoneyStorageXML : public IMyMoneyStorageFormat
+{
+ friend class MyMoneyXmlContentHandler;
+public:
+ MyMoneyStorageXML();
+ virtual ~MyMoneyStorageXML();
+
+ enum fileVersionDirectionType {
+ Reading = 0, /**< version of file to be read */
+ Writing = 1 /**< version to be used when writing a file */
+ };
+
+protected:
+ void setProgressCallback(void(*callback)(int, int, const QString&));
+ void signalProgress(int current, int total, const QString& = "");
+
+ /**
+ * This method returns the version of the underlying file. It
+ * is used by the MyMoney objects contained in a MyMoneyStorageBin object (e.g.
+ * MyMoneyAccount, MyMoneyInstitution, MyMoneyTransaction, etc.) to
+ * determine the layout used when reading/writing a persistant file.
+ * A parameter is used to determine the direction.
+ *
+ * @param dir information about the direction (reading/writing). The
+ * default is reading.
+ *
+ * @return version QString of file's version
+ *
+ * @see m_fileVersionRead, m_fileVersionWrite
+ */
+ static unsigned int fileVersion(fileVersionDirectionType dir = Reading);
+
+ QValueList<QDomElement> readElements(QString groupTag, QString itemTag = QString());
+
+ bool readFileInformation(const QDomElement& fileInfo);
+ virtual void writeFileInformation(QDomElement& fileInfo);
+
+ virtual void writeUserInformation(QDomElement& userInfo);
+
+ virtual void writeInstitution(QDomElement& institutions, const MyMoneyInstitution& i);
+ virtual void writeInstitutions(QDomElement& institutions);
+
+ virtual void writePrices(QDomElement& prices);
+ virtual void writePricePair(QDomElement& price, const MyMoneyPriceEntries& p);
+ virtual void writePrice(QDomElement& prices, const MyMoneyPrice& p);
+
+ virtual void writePayees(QDomElement& payees);
+ virtual void writePayee(QDomElement& payees, const MyMoneyPayee& p);
+
+ virtual void writeAccounts(QDomElement& accounts);
+ virtual void writeAccount(QDomElement& accounts, const MyMoneyAccount& p);
+
+ virtual void writeTransactions(QDomElement& transactions);
+ virtual void writeTransaction(QDomElement& transactions, const MyMoneyTransaction& tx);
+
+ virtual void writeSchedules(QDomElement& scheduled);
+ virtual void writeSchedule(QDomElement& scheduledTx, const MyMoneySchedule& tx);
+
+ virtual void writeReports(QDomElement& e);
+ virtual void writeBudgets(QDomElement& e);
+ virtual void writeBudget(QDomElement& budget, const MyMoneyBudget& b);
+
+ virtual void writeSecurities(QDomElement& securities);
+ virtual void writeSecurity(QDomElement& securityElement, const MyMoneySecurity& security);
+
+ virtual void writeCurrencies(QDomElement& currencies);
+
+ virtual QDomElement writeKeyValuePairs(const QMap<QString, QString> pairs);
+
+ virtual void readFile(QIODevice* s, IMyMoneySerialize* storage);
+ virtual void writeFile(QIODevice* s, IMyMoneySerialize* storage);
+
+ bool readUserInformation(const QDomElement& userElement);
+
+ void readPricePair(const QDomElement& pricePair);
+ const MyMoneyPrice readPrice(const QString& from, const QString& to, const QDomElement& price);
+
+ QDomElement findChildElement(const QString& name, const QDomElement& root);
+
+private:
+ void (*m_progressCallback)(int, int, const QString&);
+
+protected:
+ IMyMoneySerialize *m_storage;
+ QDomDocument *m_doc;
+
+private:
+ /// \internal d-pointer class.
+ class Private;
+ /// \internal d-pointer instance.
+ Private* const d;
+
+ /**
+ * This member is used to store the file version information
+ * obtained while reading a file.
+ */
+ static unsigned int fileVersionRead;
+
+ /**
+ * This member is used to store the file version information
+ * to be used when writing a file.
+ */
+ static unsigned int fileVersionWrite;
+ /**
+ * This member keeps the id of the base currency. We need this
+ * temporarily to convert the price history from the old to the
+ * new format. This should go at some time beyond 0.8 (ipwizard)
+ */
+ QString m_baseCurrencyId;
+
+};
+
+#endif