/*************************************************************************** investactivities.cpp ---------- begin : Fri Dec 15 2006 copyright : (C) 2006 by Thomas Baumgart email : 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. * * * ***************************************************************************/ // ---------------------------------------------------------------------------- // QT Includes #include <tqlabel.h> // ---------------------------------------------------------------------------- // KDE Includes #include <klocale.h> // ---------------------------------------------------------------------------- // Project Includes #include <kmymoney/kmymoneycategory.h> #include <kmymoney/kmymoneyedit.h> #include <kmymoney/kmymoneyaccountselector.h> #include <kmymoney/kmymoneycompletion.h> #include <kmymoney/mymoneyfile.h> #include "investactivities.h" using namespace Invest; using namespace KMyMoneyRegister; bool Activity::isComplete(TQString& reason) const { bool rc = false; KMyMoneySecurity* security = dynamic_cast<KMyMoneySecurity*>(haveWidget("security")); if(!security->currentText().isEmpty()) { rc = security->selector()->contains(security->currentText()); } return rc; } bool Activity::haveAssetAccount(void) const { KMyMoneyCategory* cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("asset-account")); bool rc = true; if(!isMultiSelection()) rc = !cat->currentText().isEmpty(); if(rc && !cat->currentText().isEmpty()) { rc = cat->selector()->contains(cat->currentText()); } return rc; } bool Activity::haveCategoryAndAmount(const TQString& category, const TQString& amount, bool optional) const { KMyMoneyCategory* cat = dynamic_cast<KMyMoneyCategory*>(haveWidget(category)); bool rc = true; if(!isMultiSelection() && !optional) rc = !cat->currentText().isEmpty(); if(rc && !cat->currentText().isEmpty()) { rc = cat->selector()->contains(cat->currentText()) || cat->isSplitTransaction(); if(rc && !amount.isEmpty()) { MyMoneyMoney value = dynamic_cast<kMyMoneyEdit*>(haveWidget(amount))->value(); if(!isMultiSelection()) rc = !value.isZero(); } } return rc; } bool Activity::haveShares(void) const { kMyMoneyEdit* amount = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); if(isMultiSelection() && amount->text().isEmpty()) return true; return !amount->value().isZero(); } bool Activity::havePrice(void) const { kMyMoneyEdit* amount = dynamic_cast<kMyMoneyEdit*>(haveWidget("price")); if(isMultiSelection() && amount->text().isEmpty()) return true; return !amount->value().isZero(); } bool Activity::createCategorySplits(const MyMoneyTransaction& t, KMyMoneyCategory* cat, kMyMoneyEdit* amount, MyMoneyMoney factor, TQValueList<MyMoneySplit>&splits, const TQValueList<MyMoneySplit>& osplits ) const { bool rc = true; if(!isMultiSelection() || (isMultiSelection() && !cat->currentText().isEmpty())) { if(!cat->isSplitTransaction()) { splits.clear(); MyMoneySplit s1; TQString categoryId; categoryId = cat->selectedItem(); if(!categoryId.isEmpty()) { s1.setAccountId(categoryId); s1.setValue(amount->value() * factor); if(!s1.value().isZero()) { rc = m_parent->setupPrice(t, s1); } splits.append(s1); } } else { splits = osplits; } } return rc; } void Activity::createAssetAccountSplit(MyMoneySplit& split, const MyMoneySplit& stockSplit) const { KMyMoneyCategory* cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("asset-account")); if(!isMultiSelection() || (isMultiSelection() && !cat->currentText().isEmpty())) { TQString categoryId; categoryId = cat->selectedItem(); split.setAccountId(categoryId); } split.setMemo(stockSplit.memo()); } MyMoneyMoney Activity::sumSplits(const MyMoneySplit& s0, const TQValueList<MyMoneySplit>& feeSplits, const TQValueList<MyMoneySplit>& interestSplits) const { MyMoneyMoney total; total = s0.value(); TQValueList<MyMoneySplit>::const_iterator it_s; for(it_s = feeSplits.begin(); it_s != feeSplits.end(); ++it_s) { total += (*it_s).value(); } for(it_s = interestSplits.begin(); it_s != interestSplits.end(); ++it_s) { total += (*it_s).value(); } return total; } void Activity::setLabelText(const TQString& idx, const TQString& txt) const { TQLabel* w = dynamic_cast<TQLabel*>(haveWidget(idx)); if(w) { w->setText(txt); } else { tqDebug("Unknown TQLabel named '%s'", idx.data()); } } void Activity::preloadAssetAccount(void) { KMyMoneyCategory* cat; cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("asset-account")); if(cat->isVisible()) { if(cat->currentText().isEmpty()) { MyMoneyAccount acc = MyMoneyFile::instance()->accountByName(i18n("%1 (Brokerage)").arg(m_parent->account().name())); if(!acc.id().isEmpty()) { bool blocked = cat->signalsBlocked(); // block signals, so that the focus does not go crazy cat->blockSignals(true); cat->completion()->setSelected(acc.id()); cat->slotItemSelected(acc.id()); cat->blockSignals(blocked); } } } } void Buy::showWidgets(void) const { KMyMoneyCategory* cat; cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("fee-account")); cat->parentWidget()->show(); cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account")); cat->parentWidget()->show(); kMyMoneyEdit* shareEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); shareEdit->show(); shareEdit->setPrecision(MyMoneyMoney::denomToPrec(m_parent->security().smallestAccountFraction())); cat->parentWidget()->show(); haveWidget("asset-account")->show(); haveWidget("price")->show(); haveWidget("total")->show(); setLabelText("fee-label", i18n("Fees")); setLabelText("interest-label", i18n("Interest")); setLabelText("interest-amount-label", i18n("Amount")); setLabelText("asset-label", i18n("Account")); setLabelText("shares-label", i18n("Shares")); setLabelText("price-label", i18n("Price/share")); setLabelText("total-label", i18n("Total")); } bool Buy::isComplete(TQString& reason) const { bool rc = Activity::isComplete(reason); rc &= haveAssetAccount(); rc &= haveFees(true); rc &= haveInterest(true); rc &= haveShares(); rc &= havePrice(); return rc; } bool Buy::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, TQValueList<MyMoneySplit>& feeSplits, TQValueList<MyMoneySplit>& m_feeSplits, TQValueList<MyMoneySplit>& interestSplits, TQValueList<MyMoneySplit>& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_UNUSED(m_interestSplits); Q_UNUSED(security); Q_UNUSED(currency); TQString reason; if(!isComplete(reason)) return false; kMyMoneyEdit* sharesEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); kMyMoneyEdit* priceEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("price")); s0.setAction(MyMoneySplit::BuyShares); MyMoneyMoney shares = s0.shares(); MyMoneyMoney price; if(!s0.shares().isZero()) price = (s0.value() / s0.shares()).reduce(); if(!isMultiSelection() || (isMultiSelection() && !sharesEdit->text().isEmpty())) { shares = sharesEdit->value().abs(); s0.setShares(shares); s0.setValue((shares * price).reduce()); s0.setPrice(price); } if(!isMultiSelection() || (isMultiSelection() && !priceEdit->text().isEmpty())) { price = priceEdit->value().abs(); if(priceMode() == InvestTransactionEditor::PricePerTransaction) { s0.setValue(price.reduce()); if(!s0.shares().isZero()) s0.setPrice((price / s0.shares()).reduce()); } else { s0.setValue((shares * price).reduce()); s0.setPrice(price); } } if(!createCategorySplits(t, dynamic_cast<KMyMoneyCategory*>(haveWidget("fee-account")), dynamic_cast<kMyMoneyEdit*>(haveWidget("fee-amount")), MyMoneyMoney(1,1), feeSplits, m_feeSplits)) return false; if(!createCategorySplits(t, dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account")), dynamic_cast<kMyMoneyEdit*>(haveWidget("interest-amount")), MyMoneyMoney(-1,1), interestSplits, m_interestSplits)) return false; createAssetAccountSplit(assetAccountSplit, s0); MyMoneyMoney total = sumSplits(s0, feeSplits, interestSplits); assetAccountSplit.setValue(-total); if(!m_parent->setupPrice(t, assetAccountSplit)) return false; return true; } void Sell::showWidgets(void) const { KMyMoneyCategory* cat; cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account")); cat->parentWidget()->show(); cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("fee-account")); cat->parentWidget()->show(); kMyMoneyEdit* shareEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); shareEdit->show(); shareEdit->setPrecision(MyMoneyMoney::denomToPrec(m_parent->security().smallestAccountFraction())); haveWidget("asset-account")->show(); haveWidget("price")->show(); haveWidget("total")->show(); setLabelText("fee-label", i18n("Fees")); setLabelText("interest-label", i18n("Interest")); setLabelText("interest-amount-label", i18n("Amount")); setLabelText("asset-label", i18n("Account")); setLabelText("shares-label", i18n("Shares")); setLabelText("price-label", i18n("Price/share")); setLabelText("total-label", i18n("Total")); } bool Sell::isComplete(TQString& reason) const { bool rc = Activity::isComplete(reason); rc &= haveAssetAccount(); rc &= haveFees(true); rc &= haveInterest(true); rc &= haveShares(); rc &= havePrice(); return rc; } bool Sell::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, TQValueList<MyMoneySplit>& feeSplits, TQValueList<MyMoneySplit>& m_feeSplits, TQValueList<MyMoneySplit>& interestSplits, TQValueList<MyMoneySplit>& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_UNUSED(security); Q_UNUSED(currency); TQString reason; if(!isComplete(reason)) return false; kMyMoneyEdit* sharesEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); kMyMoneyEdit* priceEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("price")); s0.setAction(MyMoneySplit::BuyShares); MyMoneyMoney shares = s0.shares(); MyMoneyMoney price; if(!s0.shares().isZero()) price = (s0.value() / s0.shares()).reduce(); if(!isMultiSelection() || (isMultiSelection() && !sharesEdit->text().isEmpty())) { shares = -sharesEdit->value().abs(); s0.setShares(shares); s0.setValue((shares * price).reduce()); s0.setPrice(price); } if(!isMultiSelection() || (isMultiSelection() && !priceEdit->text().isEmpty())) { price = priceEdit->value().abs(); if(priceMode() == InvestTransactionEditor::PricePerTransaction) { price = -price; s0.setValue(price.reduce()); if(!s0.shares().isZero()) s0.setPrice((price / s0.shares()).reduce()); } else { s0.setValue((shares * price).reduce()); s0.setPrice(price); } } if(!createCategorySplits(t, dynamic_cast<KMyMoneyCategory*>(haveWidget("fee-account")), dynamic_cast<kMyMoneyEdit*>(haveWidget("fee-amount")), MyMoneyMoney(1,1), feeSplits, m_feeSplits)) return false; if(!createCategorySplits(t, dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account")), dynamic_cast<kMyMoneyEdit*>(haveWidget("interest-amount")), MyMoneyMoney(-1,1), interestSplits, m_interestSplits)) return false; createAssetAccountSplit(assetAccountSplit, s0); MyMoneyMoney total = sumSplits(s0, feeSplits, interestSplits); assetAccountSplit.setValue(-total); if(!m_parent->setupPrice(t, assetAccountSplit)) return false; return true; } void Div::showWidgets(void) const { KMyMoneyCategory* cat; cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account")); cat->parentWidget()->show(); haveWidget("asset-account")->show(); haveWidget("total")->show(); setLabelText("interest-amount-label", i18n("Amount")); setLabelText("interest-label", i18n("Interest")); setLabelText("asset-label", i18n("Account")); setLabelText("total-label", i18n("Total")); } bool Div::isComplete(TQString& reason) const { bool rc = haveAssetAccount(); rc &= haveInterest(false); return rc; } bool Div::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, TQValueList<MyMoneySplit>& feeSplits, TQValueList<MyMoneySplit>& m_feeSplits, TQValueList<MyMoneySplit>& interestSplits, TQValueList<MyMoneySplit>& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_UNUSED(m_feeSplits); Q_UNUSED(security); Q_UNUSED(currency); TQString reason; if(!isComplete(reason)) return false; s0.setAction(MyMoneySplit::Dividend); // for dividends, we only use the stock split as a marker MyMoneyMoney shares; s0.setShares(shares); s0.setValue(shares); s0.setPrice(MyMoneyMoney(1,1)); if(!createCategorySplits(t, dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account")), dynamic_cast<kMyMoneyEdit*>(haveWidget("interest-amount")), MyMoneyMoney(-1,1), interestSplits, m_interestSplits)) return false; createAssetAccountSplit(assetAccountSplit, s0); MyMoneyMoney total = sumSplits(s0, feeSplits, interestSplits); assetAccountSplit.setValue(-total); if(!m_parent->setupPrice(t, assetAccountSplit)) return false; return true; } void Reinvest::showWidgets(void) const { KMyMoneyCategory* cat; cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account")); cat->parentWidget()->show(); cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("fee-account")); cat->parentWidget()->show(); kMyMoneyEdit* shareEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); shareEdit->show(); shareEdit->setPrecision(MyMoneyMoney::denomToPrec(m_parent->security().smallestAccountFraction())); haveWidget("price")->show(); haveWidget("total")->show(); setLabelText("fee-label", i18n("Fees")); setLabelText("interest-label", i18n("Interest")); setLabelText("shares-label", i18n("Shares")); setLabelText("price-label", i18n("Price/share")); setLabelText("total-label", i18n("Total")); } bool Reinvest::isComplete(TQString& reason) const { bool rc = Activity::isComplete(reason); rc &= haveCategoryAndAmount("interest-account", TQString(), false); rc &= haveFees(true); rc &= haveShares(); rc &= havePrice(); return rc; } bool Reinvest::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, TQValueList<MyMoneySplit>& feeSplits, TQValueList<MyMoneySplit>& m_feeSplits, TQValueList<MyMoneySplit>& interestSplits, TQValueList<MyMoneySplit>& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_UNUSED(assetAccountSplit); Q_UNUSED(security); Q_UNUSED(currency); TQString reason; if(!isComplete(reason)) return false; kMyMoneyEdit* sharesEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); kMyMoneyEdit* priceEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("price")); s0.setAction(MyMoneySplit::ReinvestDividend); MyMoneyMoney shares = s0.shares(); MyMoneyMoney price; if(!s0.shares().isZero()) price = (s0.value() / s0.shares()).reduce(); if(!isMultiSelection() || (isMultiSelection() && !sharesEdit->text().isEmpty())) { shares = sharesEdit->value().abs(); s0.setShares(shares); s0.setValue((shares * price).reduce()); s0.setPrice(price); } if(!isMultiSelection() || (isMultiSelection() && !priceEdit->text().isEmpty())) { price = priceEdit->value().abs(); if(priceMode() == InvestTransactionEditor::PricePerTransaction) { s0.setValue(price.reduce()); if(!s0.shares().isZero()) s0.setPrice((price / s0.shares()).reduce()); } else { s0.setValue((shares * price).reduce()); s0.setPrice(price); } } if(!createCategorySplits(t, dynamic_cast<KMyMoneyCategory*>(haveWidget("fee-account")), dynamic_cast<kMyMoneyEdit*>(haveWidget("fee-amount")), MyMoneyMoney(1,1), feeSplits, m_feeSplits)) return false; if(!createCategorySplits(t, dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account")), dynamic_cast<kMyMoneyEdit*>(haveWidget("interest-amount")), MyMoneyMoney(-1,1), interestSplits, m_interestSplits)) return false; if(interestSplits.count() != 1) { tqDebug("more or less than one interest split in Reinvest::createTransaction. Not created."); return false; } MyMoneySplit& s1 = interestSplits[0]; MyMoneyMoney total = sumSplits(s0, feeSplits, TQValueList<MyMoneySplit>()); s1.setValue(-total); if(!m_parent->setupPrice(t, s1)) return false; return true; } void Add::showWidgets(void) const { kMyMoneyEdit* shareEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); shareEdit->show(); shareEdit->setPrecision(MyMoneyMoney::denomToPrec(m_parent->security().smallestAccountFraction())); setLabelText("shares-label", i18n("Shares")); } bool Add::isComplete(TQString& reason) const { bool rc = Activity::isComplete(reason); rc &= haveShares(); return rc; } bool Add::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, TQValueList<MyMoneySplit>& feeSplits, TQValueList<MyMoneySplit>& m_feeSplits, TQValueList<MyMoneySplit>& interestSplits, TQValueList<MyMoneySplit>& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_UNUSED(t); Q_UNUSED(assetAccountSplit); Q_UNUSED(m_feeSplits); Q_UNUSED(m_interestSplits); Q_UNUSED(security); Q_UNUSED(currency); TQString reason; if(!isComplete(reason)) return false; kMyMoneyEdit* sharesEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); s0.setAction(MyMoneySplit::AddShares); s0.setShares(sharesEdit->value().abs()); s0.setValue(MyMoneyMoney(0, 1)); s0.setPrice(MyMoneyMoney(0, 1)); feeSplits.clear(); interestSplits.clear(); return true; } void Remove::showWidgets(void) const { kMyMoneyEdit* shareEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); shareEdit->show(); shareEdit->setPrecision(MyMoneyMoney::denomToPrec(m_parent->security().smallestAccountFraction())); setLabelText("shares-label", i18n("Shares")); } bool Remove::isComplete(TQString& reason) const { bool rc = Activity::isComplete(reason); rc &= haveShares(); return rc; } bool Remove::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, TQValueList<MyMoneySplit>& feeSplits, TQValueList<MyMoneySplit>& m_feeSplits, TQValueList<MyMoneySplit>& interestSplits, TQValueList<MyMoneySplit>& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_UNUSED(t); Q_UNUSED(assetAccountSplit); Q_UNUSED(m_feeSplits); Q_UNUSED(m_interestSplits); Q_UNUSED(security); Q_UNUSED(currency); TQString reason; if(!isComplete(reason)) return false; kMyMoneyEdit* sharesEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); s0.setAction(MyMoneySplit::AddShares); s0.setShares(-(sharesEdit->value().abs())); s0.setValue(MyMoneyMoney(0, 1)); s0.setPrice(MyMoneyMoney(0, 1)); feeSplits.clear(); interestSplits.clear(); return true; } void Split::showWidgets(void) const { // TODO do we need a special split ratio widget? // TODO maybe yes, currently the precision is the one of the fraction and might differ from it kMyMoneyEdit* shareEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); shareEdit->show(); shareEdit->setPrecision(-1); setLabelText("shares-label", i18n("Ratio 1/")); } bool Split::isComplete(TQString& reason) const { bool rc = Activity::isComplete(reason); rc &= haveShares(); return rc; } bool Split::createTransaction(MyMoneyTransaction& t, MyMoneySplit& s0, MyMoneySplit& assetAccountSplit, TQValueList<MyMoneySplit>& feeSplits, TQValueList<MyMoneySplit>& m_feeSplits, TQValueList<MyMoneySplit>& interestSplits, TQValueList<MyMoneySplit>& m_interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency) { Q_UNUSED(t); Q_UNUSED(assetAccountSplit); Q_UNUSED(m_feeSplits); Q_UNUSED(m_interestSplits); Q_UNUSED(security); Q_UNUSED(currency); kMyMoneyEdit* sharesEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares")); s0.setAction(MyMoneySplit::SplitShares); s0.setShares(sharesEdit->value().abs()); s0.setValue(MyMoneyMoney(0, 1)); s0.setPrice(MyMoneyMoney(0, 1)); feeSplits.clear(); interestSplits.clear(); return true; }