diff --git a/kmymoney/converter/mymoneytemplate.cpp b/kmymoney/converter/mymoneytemplate.cpp index bfe28c751..b2d06bbcc 100644 --- a/kmymoney/converter/mymoneytemplate.cpp +++ b/kmymoney/converter/mymoneytemplate.cpp @@ -1,490 +1,537 @@ /*************************************************************************** mymoneytemplate.cpp - description ------------------- begin : Sat Aug 14 2004 copyright : (C) 2004 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 "mymoneytemplate.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyutils.h" MyMoneyTemplate::MyMoneyTemplate() : m_progressCallback(0), m_accountsRead(0) { } MyMoneyTemplate::MyMoneyTemplate(const KUrl& url) : m_progressCallback(0), m_accountsRead(0) { loadTemplate(url); } MyMoneyTemplate::~MyMoneyTemplate() { } bool MyMoneyTemplate::loadTemplate(const KUrl& url) { QString filename; if (!url.isValid()) { qDebug("Invalid template URL '%s'", qPrintable(url.url())); return false; } m_source = url; if (url.isLocalFile()) { filename = url.toLocalFile(); } else { bool rc; rc = KIO::NetAccess::download(url, filename, KMyMoneyUtils::mainWindow()); if (!rc) { KMessageBox::detailedError(KMyMoneyUtils::mainWindow(), i18n("Error while loading file '%1'.", url.url()), KIO::NetAccess::lastErrorString(), i18n("File access error")); return false; } } bool rc = true; QFile file(filename); QFileInfo info(file); if (!info.isFile()) { QString msg = i18n("

%1 is not a template file.

", filename); KMessageBox::error(KMyMoneyUtils::mainWindow(), msg, i18n("Filetype Error")); return false; } if (file.open(QIODevice::ReadOnly)) { QString errMsg; int errLine, errColumn; if (!m_doc.setContent(&file, &errMsg, &errLine, &errColumn)) { QString msg = i18n("

Error while reading template file %1 in line %2, column %3

", filename, errLine, errColumn); KMessageBox::detailedError(KMyMoneyUtils::mainWindow(), msg, errMsg, i18n("Template Error")); rc = false; } else { rc = loadDescription(); } file.close(); } else { KMessageBox::sorry(KMyMoneyUtils::mainWindow(), i18n("File '%1' not found.", filename)); rc = false; } // if a temporary file was constructed by NetAccess::download, // then it will be removed with the next call. Otherwise, it // stays untouched on the local filesystem KIO::NetAccess::removeTempFile(filename); return rc; } bool MyMoneyTemplate::loadDescription() { int validMask = 0x00; const int validAccount = 0x01; const int validTitle = 0x02; const int validShort = 0x04; const int validLong = 0x08; const int invalid = 0x10; const int validHeader = 0x0F; QDomElement rootElement = m_doc.documentElement(); if (!rootElement.isNull() && rootElement.tagName() == "kmymoney-account-template") { QDomNode child = rootElement.firstChild(); while (!child.isNull() && child.isElement()) { QDomElement childElement = child.toElement(); // qDebug("MyMoneyTemplate::import: Processing child node %s", childElement.tagName().data()); if (childElement.tagName() == "accounts") { m_accounts = childElement.firstChild(); validMask |= validAccount; } else if (childElement.tagName() == "title") { m_title = childElement.text(); validMask |= validTitle; } else if (childElement.tagName() == "shortdesc") { m_shortDesc = childElement.text(); validMask |= validShort; } else if (childElement.tagName() == "longdesc") { m_longDesc = childElement.text(); validMask |= validLong; } else { KMessageBox::error(KMyMoneyUtils::mainWindow(), i18n("

Invalid tag %1 in template file %2

", childElement.tagName(), m_source.prettyUrl())); validMask |= invalid; } child = child.nextSibling(); } } return validMask == validHeader; } bool MyMoneyTemplate::hierarchy(QMap& list, const QString& parent, QDomNode account) { bool rc = true; while (rc == true && !account.isNull()) { if (account.isElement()) { QDomElement accountElement = account.toElement(); if (accountElement.tagName() == "account") { QString name = QString("%1:%2").arg(parent).arg(accountElement.attribute("name")); list[name] = 0; hierarchy(list, name, account.firstChild()); } } account = account.nextSibling(); } return rc; } void MyMoneyTemplate::hierarchy(QMap& list) { bool rc = !m_accounts.isNull(); QDomNode accounts = m_accounts; while (rc == true && !accounts.isNull() && accounts.isElement()) { QDomElement rootNode = accounts.toElement(); QString name = rootNode.attribute("name"); if (rootNode.tagName() == "account") { rootNode = rootNode.firstChild().toElement(); MyMoneyAccount::accountTypeE type = static_cast(accounts.toElement().attribute("type").toUInt()); switch (type) { case MyMoneyAccount::Asset: case MyMoneyAccount::Liability: case MyMoneyAccount::Income: case MyMoneyAccount::Expense: case MyMoneyAccount::Equity: if (name.isEmpty()) name = MyMoneyAccount::accountTypeToString(type); list[name] = 0; rc = hierarchy(list, name, rootNode); break; default: rc = false; break; } } else { rc = false; } accounts = accounts.nextSibling(); } } bool MyMoneyTemplate::importTemplate(void(*callback)(int, int, const QString&)) { m_progressCallback = callback; bool rc = !m_accounts.isNull(); MyMoneyFile* file = MyMoneyFile::instance(); signalProgress(0, m_doc.elementsByTagName("account").count(), i18n("Loading template %1", m_source.url())); m_accountsRead = 0; while (rc == true && !m_accounts.isNull() && m_accounts.isElement()) { QDomElement childElement = m_accounts.toElement(); if (childElement.tagName() == "account") { ++m_accountsRead; MyMoneyAccount parent; switch (childElement.attribute("type").toUInt()) { case MyMoneyAccount::Asset: parent = file->asset(); break; case MyMoneyAccount::Liability: parent = file->liability(); break; case MyMoneyAccount::Income: parent = file->income(); break; case MyMoneyAccount::Expense: parent = file->expense(); break; case MyMoneyAccount::Equity: parent = file->equity(); break; default: KMessageBox::error(KMyMoneyUtils::mainWindow(), i18n("

Invalid top-level account type %1 in template file %2

", childElement.attribute("type"), m_source.prettyUrl())); rc = false; } if (rc == true) { if (childElement.attribute("name").isEmpty()) rc = createAccounts(parent, childElement.firstChild()); else rc = createAccounts(parent, childElement); } } else { rc = false; } m_accounts = m_accounts.nextSibling(); } + + /* + * Resolve imported vat account assignments + * + * The template account id of the assigned vat account + * is stored temporarly in the account key/value pair + * 'UnresolvedVatAccount' and resolved below. + */ + QList accounts; + file->accountList(accounts); + foreach (MyMoneyAccount acc, accounts) { + if (!acc.pairs().contains("UnresolvedVatAccount")) + continue; + QString id = acc.value("UnresolvedVatAccount"); + acc.setValue("VatAccount", m_vatAccountMap[id]); + acc.deletePair("UnresolvedVatAccount"); + MyMoneyFile::instance()->modifyAccount(acc); + } + signalProgress(-1, -1); return rc; } bool MyMoneyTemplate::createAccounts(MyMoneyAccount& parent, QDomNode account) { bool rc = true; while (rc == true && !account.isNull()) { MyMoneyAccount acc; if (account.isElement()) { QDomElement accountElement = account.toElement(); if (accountElement.tagName() == "account") { signalProgress(++m_accountsRead, 0); QList subAccountList; QList::ConstIterator it; it = subAccountList.constEnd(); if (!parent.accountList().isEmpty()) { MyMoneyFile::instance()->accountList(subAccountList, parent.accountList()); for (it = subAccountList.constBegin(); it != subAccountList.constEnd(); ++it) { if ((*it).name() == accountElement.attribute("name")) { acc = *it; + QString id = accountElement.attribute("id"); + if (!id.isEmpty()) + m_vatAccountMap[id] = acc.id(); break; } } } if (it == subAccountList.constEnd()) { // not found, we need to create it acc.setName(accountElement.attribute("name")); acc.setAccountType(static_cast(accountElement.attribute("type").toUInt())); setFlags(acc, account.firstChild()); try { MyMoneyFile::instance()->addAccount(acc, parent); } catch (const MyMoneyException &) { } + QString id = accountElement.attribute("id"); + if (!id.isEmpty()) + m_vatAccountMap[id] = acc.id(); } createAccounts(acc, account.firstChild()); } } account = account.nextSibling(); } return rc; } bool MyMoneyTemplate::setFlags(MyMoneyAccount& acc, QDomNode flags) { bool rc = true; while (rc == true && !flags.isNull()) { if (flags.isElement()) { QDomElement flagElement = flags.toElement(); if (flagElement.tagName() == "flag") { // make sure, we only store flags we know! QString value = flagElement.attribute("name"); if (value == "Tax") { acc.setValue(value.toLatin1(), "Yes"); } else if (value == "VatRate") { acc.setValue(value.toLatin1(), flagElement.attribute("value")); + } else if (value == "VatAccount") { + // will be resolved later in importTemplate() + acc.setValue("UnresolvedVatAccount", flagElement.attribute("value")); } else if (value == "OpeningBalanceAccount") { acc.setValue("OpeningBalanceAccount", "Yes"); } else { KMessageBox::error(KMyMoneyUtils::mainWindow(), i18n("

Invalid flag type %1 for account %3 in template file %2

", flagElement.attribute("name"), m_source.prettyUrl(), acc.name())); rc = false; } QString currency = flagElement.attribute("currency"); if (!currency.isEmpty()) acc.setCurrencyId(currency); } } flags = flags.nextSibling(); } return rc; } void MyMoneyTemplate::signalProgress(int current, int total, const QString& msg) { if (m_progressCallback != 0) (*m_progressCallback)(current, total, msg); } bool MyMoneyTemplate::exportTemplate(void(*callback)(int, int, const QString&)) { m_progressCallback = callback; + // prepare vat account map + QList accountList; + MyMoneyFile::instance()->accountList(accountList); + int i = 0; + foreach (MyMoneyAccount acc, accountList) { + if (!acc.pairs().contains("VatAccount")) + continue; + m_vatAccountMap[acc.value("VatAccount")] = QString("%1").arg(i++, 3, 10, QLatin1Char('0')); + } + m_doc = QDomDocument("KMYMONEY-TEMPLATE"); QDomProcessingInstruction instruct = m_doc.createProcessingInstruction(QString("xml"), QString("version=\"1.0\" encoding=\"utf-8\"")); m_doc.appendChild(instruct); QDomElement mainElement = m_doc.createElement("kmymoney-account-template"); m_doc.appendChild(mainElement); QDomElement title = m_doc.createElement("title"); QDomText t = m_doc.createTextNode(m_title); title.appendChild(t); mainElement.appendChild(title); QDomElement shortDesc = m_doc.createElement("shortdesc"); t = m_doc.createTextNode(m_shortDesc); shortDesc.appendChild(t); mainElement.appendChild(shortDesc); QDomElement longDesc = m_doc.createElement("longdesc"); t = m_doc.createTextNode(m_longDesc); longDesc.appendChild(t); mainElement.appendChild(longDesc); QDomElement accounts = m_doc.createElement("accounts"); mainElement.appendChild(accounts); addAccountStructure(accounts, MyMoneyFile::instance()->asset()); addAccountStructure(accounts, MyMoneyFile::instance()->expense()); addAccountStructure(accounts, MyMoneyFile::instance()->income()); addAccountStructure(accounts, MyMoneyFile::instance()->liability()); addAccountStructure(accounts, MyMoneyFile::instance()->equity()); return true; } const QString& MyMoneyTemplate::title() const { return m_title; } const QString& MyMoneyTemplate::shortDescription() const { return m_shortDesc; } const QString& MyMoneyTemplate::longDescription() const { return m_longDesc; } void MyMoneyTemplate::setTitle(const QString &s) { m_title = s; } void MyMoneyTemplate::setShortDescription(const QString &s) { m_shortDesc = s; } void MyMoneyTemplate::setLongDescription(const QString &s) { m_longDesc = s; } static bool nameLessThan(MyMoneyAccount &a1, MyMoneyAccount &a2) { return a1.name() < a2.name(); } bool MyMoneyTemplate::addAccountStructure(QDomElement& parent, const MyMoneyAccount& acc) { QDomElement account = m_doc.createElement("account"); parent.appendChild(account); if (MyMoneyFile::instance()->isStandardAccount(acc.id())) account.setAttribute(QString("name"), QString()); else account.setAttribute(QString("name"), acc.name()); account.setAttribute(QString("type"), acc.accountType()); // FIXME: add tax flag stuff + if (m_vatAccountMap.contains(acc.id())) + account.setAttribute(QString("id"), m_vatAccountMap[acc.id()]); + if (acc.pairs().contains("VatRate")) { QDomElement flag = m_doc.createElement("flag"); flag.setAttribute(QString("name"), "VatRate"); flag.setAttribute(QString("value"), acc.value("VatRate")); account.appendChild(flag); } + if (acc.pairs().contains("VatAccount")) { + QDomElement flag = m_doc.createElement("flag"); + flag.setAttribute(QString("name"), "VatAccount"); + flag.setAttribute(QString("value"), m_vatAccountMap[acc.value("VatAccount")]); + account.appendChild(flag); + } if (acc.pairs().contains("OpeningBalanceAccount")) { QString openingBalanceAccount = acc.value("OpeningBalanceAccount"); if (openingBalanceAccount == "Yes") { QDomElement flag = m_doc.createElement("flag"); flag.setAttribute(QString("name"), "OpeningBalanceAccount"); flag.setAttribute(QString("currency"), acc.currencyId()); account.appendChild(flag); } } // any child accounts? if (acc.accountList().count() > 0) { QList list; MyMoneyFile::instance()->accountList(list, acc.accountList(), false); qSort(list.begin(), list.end(), nameLessThan); QList::Iterator it; for (it = list.begin(); it != list.end(); ++it) { addAccountStructure(account, *it); } } return true; } bool MyMoneyTemplate::saveTemplate(const KUrl& url) { QString filename; if (!url.isValid()) { qDebug("Invalid template URL '%s'", qPrintable(url.url())); return false; } if (url.isLocalFile()) { filename = url.toLocalFile(); KSaveFile qfile(filename/*, 0600*/); if (qfile.open()) { saveToLocalFile(&qfile); if (!qfile.finalize()) { throw MYMONEYEXCEPTION(i18n("Unable to write changes to '%1'", filename)); } } else { throw MYMONEYEXCEPTION(i18n("Unable to write changes to '%1'", filename)); } } else { KTemporaryFile tmpfile; KSaveFile qfile(tmpfile.fileName()); if (qfile.open()) { saveToLocalFile(&qfile); if (!qfile.finalize()) { throw MYMONEYEXCEPTION(i18n("Unable to upload to '%1'", url.url())); } } else { throw MYMONEYEXCEPTION(i18n("Unable to upload to '%1'", url.url())); } if (!KIO::NetAccess::upload(tmpfile.fileName(), url, 0)) throw MYMONEYEXCEPTION(i18n("Unable to upload to '%1'", url.url())); } return true; } bool MyMoneyTemplate::saveToLocalFile(KSaveFile* qfile) { QTextStream stream(qfile); stream.setCodec("UTF-8"); stream << m_doc.toString(); stream.flush(); return true; } diff --git a/kmymoney/converter/mymoneytemplate.h b/kmymoney/converter/mymoneytemplate.h index 9619f9a50..ba8d7d865 100644 --- a/kmymoney/converter/mymoneytemplate.h +++ b/kmymoney/converter/mymoneytemplate.h @@ -1,101 +1,102 @@ /*************************************************************************** mymoneytemplate.h - description ------------------- begin : Sat Aug 14 2004 copyright : (C) 2004 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 MYMONEYTEMPLATE_H #define MYMONEYTEMPLATE_H // ---------------------------------------------------------------------------- // QT Includes #include #include class QFile; class QTreeWidgetItem; // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyaccount.h" #include "mymoneyfile.h" /** * @author Thomas Baumgart */ /** * This class represents an account template handler. It is capable * to read an XML formatted account template file and import it into * the current engine. Also, it can save the current account structure * of the engine to an XML formatted template file. */ class MyMoneyTemplate { public: MyMoneyTemplate(); explicit MyMoneyTemplate(const KUrl& url); ~MyMoneyTemplate(); bool loadTemplate(const KUrl& url); bool saveTemplate(const KUrl& url); bool importTemplate(void(*callback)(int, int, const QString&)); bool exportTemplate(void(*callback)(int, int, const QString&)); const QString& title() const; const QString& shortDescription() const; const QString& longDescription() const; void setTitle(const QString &s); void setShortDescription(const QString &s); void setLongDescription(const QString &s); void hierarchy(QMap& list); protected: bool loadDescription(); bool createAccounts(MyMoneyAccount& parent, QDomNode account); bool setFlags(MyMoneyAccount& acc, QDomNode flags); bool saveToLocalFile(KSaveFile* qfile); bool addAccountStructure(QDomElement& parent, const MyMoneyAccount& acc); bool hierarchy(QMap& list, const QString& parent, QDomNode account); /** * This method is used to update the progress information. It * checks if an appropriate function is known and calls it. * * For a parameter description see KMyMoneyView::progressCallback(). */ void signalProgress(int current, int total, const QString& = ""); private: QDomDocument m_doc; QDomNode m_accounts; QString m_title; QString m_shortDesc; QString m_longDesc; KUrl m_source; void (*m_progressCallback)(int, int, const QString&); int m_accountsRead; + QMap m_vatAccountMap; }; #endif