From dadc34655c3ab961b0b0b94a10eaaba710f0b5e8 Mon Sep 17 00:00:00 2001 From: tpearson Date: Mon, 4 Jul 2011 22:38:03 +0000 Subject: Added kmymoney git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kmymoney@1239792 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kmymoney2/dialogs/kequitypriceupdatedlg.cpp | 588 ++++++++++++++++++++++++++++ 1 file changed, 588 insertions(+) create mode 100644 kmymoney2/dialogs/kequitypriceupdatedlg.cpp (limited to 'kmymoney2/dialogs/kequitypriceupdatedlg.cpp') diff --git a/kmymoney2/dialogs/kequitypriceupdatedlg.cpp b/kmymoney2/dialogs/kequitypriceupdatedlg.cpp new file mode 100644 index 0000000..4111bfa --- /dev/null +++ b/kmymoney2/dialogs/kequitypriceupdatedlg.cpp @@ -0,0 +1,588 @@ +/*************************************************************************** + kequitypriceupdatedlg.cpp - description + ------------------- + begin : Mon Sep 1 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales + Felix Rodriguez + John C + Thomas Baumgart + Kevin Tambascio + Ace Jones + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 +#include +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kequitypriceupdatedlg.h" +#include "../mymoney/mymoneyfile.h" +#include "../mymoney/mymoneysecurity.h" +#include "../mymoney/mymoneyprice.h" +#include "../kmymoneyglobalsettings.h" + +#define SYMBOL_COL 0 +#define NAME_COL 1 +#define PRICE_COL 2 +#define DATE_COL 3 +#define ID_COL 4 +#define SOURCE_COL 5 + +KEquityPriceUpdateDlg::KEquityPriceUpdateDlg(QWidget *parent, const QString& securityId) : + KEquityPriceUpdateDlgDecl(parent), + m_fUpdateAll(false) +{ + lvEquityList->setRootIsDecorated(false); + lvEquityList->setColumnText(0, i18n("Symbol")); + lvEquityList->addColumn(i18n("Symbol")); + lvEquityList->addColumn(i18n("Name"),125); + lvEquityList->addColumn(i18n("Price")); + lvEquityList->addColumn(i18n("Date")); + + // This is a "get it up and running" hack. Will replace this in the future. + lvEquityList->addColumn("ID"); + lvEquityList->addColumn("Source"); + lvEquityList->setColumnWidth(ID_COL, 0); + + lvEquityList->setMultiSelection(true); + lvEquityList->setColumnWidthMode(SYMBOL_COL, QListView::Maximum); + lvEquityList->setColumnWidthMode(ID_COL, QListView::Manual); + lvEquityList->setAllColumnsShowFocus(true); + + btnUpdateAll->setEnabled(false); + + MyMoneyFile* file = MyMoneyFile::instance(); + + // + // Add each price pair that we know about + // + + // send in securityId == "XXX YYY" to get a single-shot update for XXX to YYY. + // for consistency reasons, this accepts the same delimiters as WebPriceQuote::launch() + QRegExp splitrx("([0-9a-z\\.]+)[^a-z0-9]+([0-9a-z\\.]+)",false /*case sensitive*/); + MyMoneySecurityPair currencyIds; + if ( splitrx.search(securityId) != -1 ) + currencyIds = MyMoneySecurityPair(splitrx.cap(1).utf8(),splitrx.cap(2).utf8()); + + MyMoneyPriceList prices = file->priceList(); + for(MyMoneyPriceList::ConstIterator it_price = prices.begin(); it_price != prices.end(); ++it_price) + { + const MyMoneySecurityPair& pair = it_price.key(); + if ( file->security( pair.first ).isCurrency() && ( securityId.isEmpty() || ( pair == currencyIds ) ) ) + { + const MyMoneyPriceEntries& entries = (*it_price); + if(entries.count() > 0 && entries.begin().key() <= QDate::currentDate()) { + addPricePair(pair); + btnUpdateAll->setEnabled(true); + } + } + } + + // + // Add each investment + // + + QValueList securities = file->securityList(); + for(QValueList::ConstIterator it = securities.begin(); it != securities.end(); ++it) + { + if ( !(*it).isCurrency() + && ( securityId.isEmpty() || ( (*it).id() == securityId ) ) + && !(*it).value("kmm-online-source").isEmpty() + ) + { + addInvestment(*it); + btnUpdateAll->setEnabled(true); + } + } + + // if list is empty, add the request price pair + if(lvEquityList->firstChild() == 0) { + addPricePair(currencyIds, true); + } + + connect(btnOK, SIGNAL(clicked()), this, SLOT(accept())); + connect(btnCancel, SIGNAL(clicked()), this, SLOT(reject())); + connect(btnUpdateSelected, SIGNAL(clicked()), this, SLOT(slotUpdateSelectedClicked())); + connect(btnUpdateAll, SIGNAL(clicked()), this, SLOT(slotUpdateAllClicked())); + + connect(&m_webQuote,SIGNAL(quote(const QString&, const QString&,const QDate&, const double&)), + this,SLOT(slotReceivedQuote(const QString&, const QString&,const QDate&, const double&))); + connect(&m_webQuote,SIGNAL(failed(const QString&, const QString&)), + this,SLOT(slotQuoteFailed(const QString&, const QString&))); + connect(&m_webQuote,SIGNAL(status(const QString&)), + this,SLOT(logStatusMessage(const QString&))); + connect(&m_webQuote,SIGNAL(error(const QString&)), + this,SLOT(logErrorMessage(const QString&))); + + connect(lvEquityList, SIGNAL(selectionChanged()), this, SLOT(slotUpdateSelection())); + + // Not implemented yet. + btnConfigure->hide(); + //connect(btnConfigure, SIGNAL(clicked()), this, SLOT(slotConfigureClicked())); + + if ( !securityId.isEmpty() ) + { + btnUpdateSelected->hide(); + btnUpdateAll->hide(); + // delete layout1; + + QTimer::singleShot(100,this,SLOT(slotUpdateAllClicked())); + } + + // Hide OK button until we have received the first update + btnOK->setEnabled(false); + + slotUpdateSelection(); + + // previous versions of this dialog allowed to store a "Don't ask again" switch. + // Since we don't support it anymore, we just get rid of it + KConfig* config = KGlobal::config(); + config->setGroup("Notification Messages"); + config->deleteEntry("KEquityPriceUpdateDlg::slotQuoteFailed::Price Update Failed"); +} + +KEquityPriceUpdateDlg::~KEquityPriceUpdateDlg() +{ + +} + +void KEquityPriceUpdateDlg::addPricePair(const MyMoneySecurityPair& pair, bool dontCheckExistance) +{ + MyMoneyFile* file = MyMoneyFile::instance(); + + QString symbol = QString("%1 > %2").arg(pair.first,pair.second); + QString id = QString("%1 %2").arg(pair.first,pair.second); + if ( ! lvEquityList->findItem(id,ID_COL,Qt::ExactMatch) ) + { + MyMoneyPrice pr = file->price(pair.first,pair.second); + if(pr.source() != "KMyMoney") { + bool keep = true; + if((pair.first == file->baseCurrency().id()) + || (pair.second == file->baseCurrency().id())) { + const QString& foreignCurrency = file->foreignCurrency(pair.first, pair.second); + // check that the foreign currency is still in use + QValueList::const_iterator it_a; + QValueList list; + file->accountList(list); + for(it_a = list.begin(); !dontCheckExistance && it_a != list.end(); ++it_a) { + // if it's an account denominated in the foreign currency + // keep it + if(((*it_a).currencyId() == foreignCurrency) + && !(*it_a).isClosed()) + break; + // if it's an investment traded in the foreign currency + // keep it + if((*it_a).isInvest() && !(*it_a).isClosed()) { + MyMoneySecurity sec = file->security((*it_a).currencyId()); + if(sec.tradingCurrency() == foreignCurrency) + break; + } + } + // if it is in use, it_a is not equal to list.end() + if(it_a == list.end() && !dontCheckExistance) + keep = false; + } + + if(keep) { + KListViewItem* item = new KListViewItem(lvEquityList, + symbol, + i18n("%1 units in %2").arg(pair.first,pair.second)); + if(pr.isValid()) { + item->setText(PRICE_COL, pr.rate(pair.second).formatMoney(file->currency(pair.second).tradingSymbol(), KMyMoneyGlobalSettings::pricePrecision())); + item->setText(DATE_COL, pr.date().toString(Qt::ISODate)); + } + item->setText(ID_COL,id); + item->setText(SOURCE_COL, "Yahoo Currency"); // This string value should not be localized + } + } + } +} + +void KEquityPriceUpdateDlg::addInvestment(const MyMoneySecurity& inv) +{ + MyMoneyFile* file = MyMoneyFile::instance(); + + QString symbol = inv.tradingSymbol(); + QString id = inv.id(); + if ( ! lvEquityList->findItem(id, ID_COL, Qt::ExactMatch) ) + { + // check that the security is still in use + QValueList::const_iterator it_a; + QValueList list; + file->accountList(list); + for(it_a = list.begin(); it_a != list.end(); ++it_a) { + if((*it_a).isInvest() + && ((*it_a).currencyId() == inv.id()) + && !(*it_a).isClosed()) + break; + } + // if it is in use, it_a is not equal to list.end() + if(it_a != list.end()) { + KListViewItem* item = new KListViewItem(lvEquityList, symbol, inv.name()); + MyMoneySecurity currency = file->currency(inv.tradingCurrency()); + MyMoneyPrice pr = file->price(id.utf8(), inv.tradingCurrency()); + if(pr.isValid()) { + item->setText(PRICE_COL, pr.rate(currency.id()).formatMoney(currency.tradingSymbol(), KMyMoneyGlobalSettings::pricePrecision())); + item->setText(DATE_COL, pr.date().toString(Qt::ISODate)); + } + item->setText(ID_COL,id); + if (inv.value("kmm-online-quote-system") == "Finance::Quote") + item->setText(SOURCE_COL, QString("Finance::Quote %1").arg( inv.value("kmm-online-source"))); + else + item->setText(SOURCE_COL, inv.value("kmm-online-source")); + + // If this investment is denominated in a foreign currency, ensure that + // the appropriate price pair is also on the list + + if ( currency.id() != file->baseCurrency().id() ) + { + addPricePair(MyMoneySecurityPair(currency.id(),file->baseCurrency().id())); + } + } + } +} + +void KEquityPriceUpdateDlg::logErrorMessage(const QString& message) +{ + logStatusMessage(QString("") + message + QString("")); +} + +void KEquityPriceUpdateDlg::logStatusMessage(const QString& message) +{ + lbStatus->append(message); +} + +MyMoneyPrice KEquityPriceUpdateDlg::price(const QString& id) const +{ + MyMoneyPrice price; + QListViewItem* item; + + if((item = lvEquityList->findItem(id, ID_COL, Qt::ExactMatch)) != 0) { + MyMoneyMoney rate(item->text(PRICE_COL)); + if ( !rate.isZero() ) + { + QString id = item->text(ID_COL).utf8(); + + // if the ID has a space, then this is TWO ID's, so it's a currency quote + if ( QString(id).contains(" ") ) + { + QStringList ids = QStringList::split(" ",QString(id)); + QString fromid = ids[0].utf8(); + QString toid = ids[1].utf8(); + price = MyMoneyPrice(fromid,toid,QDate().fromString(item->text(DATE_COL), Qt::ISODate),rate,item->text(SOURCE_COL)); + } + else + // otherwise, it's a security quote + { + MyMoneySecurity security = MyMoneyFile::instance()->security(id); + price = MyMoneyPrice(id, security.tradingCurrency(), QDate().fromString(item->text(DATE_COL), Qt::ISODate), rate, item->text(SOURCE_COL)); + } + } + } + return price; +} + +void KEquityPriceUpdateDlg::storePrices(void) +{ + // update the new prices into the equities + + MyMoneyFile* file = MyMoneyFile::instance(); + QValueList equities = file->securityList(); + + QListViewItem* item = lvEquityList->firstChild(); + MyMoneyFileTransaction ft; + QString name; + try { + while ( item ) + { + // turn on signals before we modify the last entry in the list + MyMoneyFile::instance()->blockSignals(item->nextSibling() != 0); + + MyMoneyMoney rate(item->text(PRICE_COL)); + if ( !rate.isZero() ) + { + QString id = item->text(ID_COL).utf8(); + + // if the ID has a space, then this is TWO ID's, so it's a currency quote + if ( QString(id).contains(" ") ) + { + QStringList ids = QStringList::split(" ",QString(id)); + QString fromid = ids[0].utf8(); + QString toid = ids[1].utf8(); + name = QString("%1 --> %2").arg(fromid).arg(toid); + MyMoneyPrice price(fromid,toid,QDate().fromString(item->text(DATE_COL), Qt::ISODate),rate,item->text(SOURCE_COL)); + file->addPrice(price); + } + else + // otherwise, it's a security quote + { + MyMoneySecurity security = MyMoneyFile::instance()->security(id); + name = security.name(); + MyMoneyPrice price(id, security.tradingCurrency(), QDate().fromString(item->text(DATE_COL), Qt::ISODate), rate, item->text(SOURCE_COL)); + + // TODO (Ace) Better handling of the case where there is already a price + // for this date. Currently, it just overrides the old value. Really it + // should check to see if the price is the same and prompt the user. + MyMoneyFile::instance()->addPrice(price); + } + + } + item = item->nextSibling(); + } + ft.commit(); + + } catch(MyMoneyException *e) { + qDebug("Unable to add price information for %s", name.data()); + delete e; + } +} + +void KEquityPriceUpdateDlg::slotUpdateSelection(void) +{ + btnUpdateSelected->setEnabled(false); + + QListViewItem* item = lvEquityList->firstChild(); + while ( item && !item->isSelected()) + item = item->nextSibling(); + + if(item) + btnUpdateSelected->setEnabled(true); +} + +void KEquityPriceUpdateDlg::slotUpdateSelectedClicked(void) +{ + QListViewItem* item = lvEquityList->firstChild(); + int skipCnt = 1; + while ( item && !item->isSelected()) + { + skipCnt++; + item = item->nextSibling(); + } + + if(item) { + prgOnlineProgress->setTotalSteps(1+lvEquityList->childCount()); + prgOnlineProgress->setProgress(skipCnt); + m_webQuote.launch(item->text(SYMBOL_COL),item->text(ID_COL),item->text(SOURCE_COL)); + } + else + logErrorMessage("No security selected."); +} + +void KEquityPriceUpdateDlg::slotUpdateAllClicked(void) +{ + QListViewItem* item = lvEquityList->firstChild(); + if ( item ) + { + prgOnlineProgress->setTotalSteps(1+lvEquityList->childCount()); + prgOnlineProgress->setProgress(1); + m_fUpdateAll = true; + m_webQuote.launch(item->text(SYMBOL_COL),item->text(ID_COL),item->text(SOURCE_COL)); + } + else + logErrorMessage("Security list is empty."); +} + +void KEquityPriceUpdateDlg::slotQuoteFailed(const QString& _id, const QString& _symbol) +{ + QListViewItem* item = lvEquityList->findItem(_id,ID_COL,Qt::ExactMatch); + + // Give the user some options + int result; + if(_id.contains(" ")) { + result = KMessageBox::warningContinueCancel(this, i18n("Failed to retrieve an exchange rate for %1 from %2. It will be skipped this time.").arg(_symbol, item->text(SOURCE_COL)), i18n("Price Update Failed")); + } else { + result = KMessageBox::questionYesNoCancel(this, QString("%1").arg(i18n("Failed to retrieve a quote for %1 from %2. Press No to remove the online price source from this security permanently, Yes to continue updating this security during future price updates or Cancel to stop the current update operation.").arg(_symbol, item->text(SOURCE_COL))), i18n("Price Update Failed"), KStdGuiItem::yes(), KStdGuiItem::no()); + } + + if ( result == KMessageBox::No ) + { + // Disable price updates for this security + + MyMoneyFileTransaction ft; + try { + // Get this security (by ID) + MyMoneySecurity security = MyMoneyFile::instance()->security(_id.utf8()); + + // Set the quote source to blank + security.setValue("kmm-online-source",QString()); + security.setValue("kmm-online-quote-system",QString()); + + // Re-commit the security + MyMoneyFile::instance()->modifySecurity(security); + ft.commit(); + } catch(MyMoneyException* e) { + KMessageBox::error(this, QString("")+i18n("Cannot update security %1: %2").arg(_symbol, e->what())+QString(""), i18n("Price Update Failed")); + delete e; + } + } + + // As long as the user doesn't want to cancel, move on! + if ( result != KMessageBox::Cancel ) + { + QListViewItem* next = NULL; + prgOnlineProgress->advance(1); + item->listView()->setSelected(item, false); + + // launch the NEXT one ... in case of m_fUpdateAll == false, we + // need to parse the list to find the next selected one + next = item->nextSibling(); + if ( !m_fUpdateAll ) + { + while(next && !next->isSelected()) + { + prgOnlineProgress->advance(1); + next = next->nextSibling(); + } + } + if (next) + { + m_webQuote.launch(next->text(SYMBOL_COL),next->text(ID_COL),next->text(SOURCE_COL)); + } + else + { + finishUpdate(); + } + } + else + { + finishUpdate(); + } +} + +void KEquityPriceUpdateDlg::slotReceivedQuote(const QString& _id, const QString& _symbol,const QDate& _date, const double& _price) +{ + QListViewItem* item = lvEquityList->findItem(_id,ID_COL,Qt::ExactMatch); + QListViewItem* next = NULL; + + if ( item ) + { + if ( _price > 0.0f && _date.isValid() ) + { + QDate date = _date; + if ( date > QDate::currentDate() ) + date = QDate::currentDate(); + + double price = _price; + QString id = _id.utf8(); + MyMoneySecurity sec; + if ( _id.contains(" ") == 0) { + MyMoneySecurity security = MyMoneyFile::instance()->security(id); + QString factor = security.value("kmm-online-factor"); + if(!factor.isEmpty()) { + price *= MyMoneyMoney(factor).toDouble(); + } + try { + sec = MyMoneyFile::instance()->security(id); + sec = MyMoneyFile::instance()->security(sec.tradingCurrency()); + } catch(MyMoneyException *e) { + sec = MyMoneySecurity(); + delete e; + } + + } else { + QRegExp splitrx("([0-9a-z\\.]+)[^a-z0-9]+([0-9a-z\\.]+)",false /*case sensitive*/); + if ( splitrx.search(_id) != -1 ) { + try { + sec = MyMoneyFile::instance()->security(splitrx.cap(2).utf8()); + } catch(MyMoneyException *e) { + sec = MyMoneySecurity(); + delete e; + } + } + } + item->setText(PRICE_COL, KGlobal::locale()->formatMoney(price, sec.tradingSymbol(), KMyMoneyGlobalSettings::pricePrecision())); + item->setText(DATE_COL, date.toString(Qt::ISODate)); + logStatusMessage(i18n("Price for %1 updated (id %2)").arg(_symbol,_id)); + // make sure to make OK button available + btnOK->setEnabled(true); + } + else + { + logErrorMessage(i18n("Received an invalid price for %1, unable to update.").arg(_symbol)); + } + + prgOnlineProgress->advance(1); + item->listView()->setSelected(item, false); + + // launch the NEXT one ... in case of m_fUpdateAll == false, we + // need to parse the list to find the next selected one + next = item->nextSibling(); + if ( !m_fUpdateAll ) + { + while(next && !next->isSelected()) + { + prgOnlineProgress->advance(1); + next = next->nextSibling(); + } + } + } + else + { + logErrorMessage(i18n("Received a price for %1 (id %2), but this symbol is not on the list! Aborting entire update.").arg(_symbol,_id)); + } + + if (next) + { + m_webQuote.launch(next->text(SYMBOL_COL),next->text(ID_COL),next->text(SOURCE_COL)); + } + else + { + finishUpdate(); + } +} + +void KEquityPriceUpdateDlg::finishUpdate(void) +{ + // we've run past the end, reset to the default value. + m_fUpdateAll = false; + // force progress bar to show 100% + prgOnlineProgress->setProgress(prgOnlineProgress->totalSteps()); +} + +// Make sure, that these definitions are only used within this file +// this does not seem to be necessary, but when building RPMs the +// build option 'final' is used and all CPP files are concatenated. +// So it could well be, that in another CPP file these definitions +// are also used. +#undef SYMBOL_COL +#undef NAME_COL +#undef PRICE_COL +#undef DATE_COL +#undef ID_COL +#undef SOURCE_COL + +#include "kequitypriceupdatedlg.moc" +// vim:cin:si:ai:et:ts=2:sw=2: -- cgit v1.2.1