File indexing completed on 2024-05-12 16:43:48

0001 /*
0002     SPDX-FileCopyrightText: 2007 Thomas Baumgart <ipwizard@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kinvestmentview_p.h"
0008 
0009 #include <typeinfo>
0010 
0011 // ----------------------------------------------------------------------------
0012 // QT Includes
0013 
0014 #include <QTimer>
0015 #include <QAction>
0016 #include <QMenu>
0017 #include <QBitArray>
0018 
0019 // ----------------------------------------------------------------------------
0020 // KDE Includes
0021 
0022 #include <KMessageBox>
0023 
0024 // ----------------------------------------------------------------------------
0025 // Project Includes
0026 
0027 #include "mymoneymoney.h"
0028 #include "mymoneyprice.h"
0029 #include "kequitypriceupdatedlg.h"
0030 #include "kcurrencycalculator.h"
0031 #include "knewinvestmentwizard.h"
0032 #include "kmymoneyutils.h"
0033 #include "menuenums.h"
0034 #include "storageenums.h"
0035 
0036 using namespace Icons;
0037 
0038 KInvestmentView::KInvestmentView(QWidget *parent) :
0039     KMyMoneyViewBase(*new KInvestmentViewPrivate(this), parent)
0040 {
0041     connect(pActions[eMenu::Action::NewInvestment],       &QAction::triggered, this, &KInvestmentView::slotNewInvestment);
0042     connect(pActions[eMenu::Action::EditInvestment],      &QAction::triggered, this, &KInvestmentView::slotEditInvestment);
0043     connect(pActions[eMenu::Action::DeleteInvestment],    &QAction::triggered, this, &KInvestmentView::slotDeleteInvestment);
0044     connect(pActions[eMenu::Action::UpdatePriceOnline],   &QAction::triggered, this, &KInvestmentView::slotUpdatePriceOnline);
0045     connect(pActions[eMenu::Action::UpdatePriceManually], &QAction::triggered, this, &KInvestmentView::slotUpdatePriceManually);
0046 }
0047 
0048 KInvestmentView::~KInvestmentView()
0049 {
0050 }
0051 
0052 void KInvestmentView::setDefaultFocus()
0053 {
0054     Q_D(KInvestmentView);
0055     auto tab = static_cast<eView::Investment::Tab>(d->ui->m_tab->currentIndex());
0056 
0057     switch (tab) {
0058     case eView::Investment::Tab::Equities:
0059         QTimer::singleShot(0, d->ui->m_equitiesTree, SLOT(setFocus()));
0060         break;
0061     case eView::Investment::Tab::Securities:
0062         QTimer::singleShot(0, d->ui->m_securitiesTree, SLOT(setFocus()));
0063         break;
0064     }
0065 }
0066 
0067 void KInvestmentView::executeCustomAction(eView::Action action)
0068 {
0069     switch(action) {
0070     case eView::Action::Refresh:
0071         refresh();
0072         break;
0073 
0074     case eView::Action::SetDefaultFocus:
0075         setDefaultFocus();
0076         break;
0077 
0078     default:
0079         break;
0080     }
0081 }
0082 
0083 void KInvestmentView::refresh()
0084 {
0085     Q_D(KInvestmentView);
0086     d->m_needReload[eView::Investment::Tab::Equities] = d->m_needReload[eView::Investment::Tab::Securities] = true;
0087     if (isVisible())
0088         slotLoadTab(d->ui->m_tab->currentIndex());
0089 }
0090 
0091 void KInvestmentView::showEvent(QShowEvent* event)
0092 {
0093     Q_D(KInvestmentView);
0094     if (d->m_needLoad)
0095         d->init();
0096 
0097     emit customActionRequested(View::Investments, eView::Action::AboutToShow);
0098 
0099     d->m_needReload[eView::Investment::Tab::Equities] = true;  // ensure tree view will be reloaded after selecting account in ledger view
0100     if (d->m_needReload[eView::Investment::Tab::Equities] == true ||
0101             d->m_needReload[eView::Investment::Tab::Securities] == true)
0102         refresh();
0103 
0104     // don't forget base class implementation
0105     QWidget::showEvent(event);
0106 }
0107 
0108 void KInvestmentView::updateActions(const MyMoneyObject& obj)
0109 {
0110     Q_D(KInvestmentView);
0111     if (typeid(obj) != typeid(MyMoneyAccount) &&
0112             (obj.id().isEmpty() && d->m_currentEquity.id().isEmpty())) // do not disable actions that were already disabled)))
0113         return;
0114 
0115     const auto& acc = static_cast<const MyMoneyAccount&>(obj);
0116 
0117     const auto file = MyMoneyFile::instance();
0118     auto b = acc.accountType() == eMyMoney::Account::Type::Investment ? true : false;
0119     if (isVisible() && !d->m_idInvAcc.isEmpty())
0120         b = true;
0121 
0122     pActions[eMenu::Action::NewInvestment]->setEnabled(b);
0123 
0124     b = acc.isInvest() ? true : false;
0125     pActions[eMenu::Action::EditInvestment]->setEnabled(b);
0126     pActions[eMenu::Action::DeleteInvestment]->setEnabled(b && !file->isReferenced(acc));
0127     pActions[eMenu::Action::UpdatePriceManually]->setEnabled(b);
0128     pActions[eMenu::Action::UpdatePriceOnline]->setEnabled(b && !file->security(acc.currencyId()).value("kmm-online-source").isEmpty());
0129 
0130     switch (acc.accountType()) {
0131     case eMyMoney::Account::Type::Investment:
0132     case eMyMoney::Account::Type::Stock:
0133         d->m_currentEquity = acc;
0134         break;
0135     default:
0136         d->m_currentEquity = MyMoneyAccount();
0137         break;
0138     }
0139 }
0140 
0141 void KInvestmentView::slotLoadTab(int index)
0142 {
0143     Q_D(KInvestmentView);
0144     auto tab = static_cast<eView::Investment::Tab>(index);
0145     if (d->m_needReload[tab]) {
0146         switch (tab) {
0147         case eView::Investment::Tab::Equities:
0148             d->loadInvestmentTab();
0149             break;
0150         case eView::Investment::Tab::Securities:
0151             d->loadSecuritiesTab();
0152             break;
0153         }
0154         d->m_needReload[tab] = false;
0155     }
0156 }
0157 
0158 void KInvestmentView::slotEquitySelected(const QModelIndex &current, const QModelIndex &previous)
0159 {
0160     Q_D(KInvestmentView);
0161     Q_UNUSED(current);
0162     Q_UNUSED(previous);
0163     emit selectByObject(d->currentEquity(), eView::Intent::None);
0164 }
0165 
0166 void KInvestmentView::slotSecuritySelected(const QModelIndex &current, const QModelIndex &previous)
0167 {
0168     Q_D(KInvestmentView);
0169     Q_UNUSED(current);
0170     Q_UNUSED(previous);
0171     const auto sec = d->currentSecurity();
0172     if (!sec.id().isEmpty()) {
0173         QBitArray skip((int)eStorage::Reference::Count);
0174         skip.fill(false);
0175         skip.setBit((int)eStorage::Reference::Price);
0176         d->ui->m_editSecurityButton->setEnabled(true);
0177         d->ui->m_deleteSecurityButton->setEnabled(!MyMoneyFile::instance()->isReferenced(sec, skip));
0178     } else {
0179         d->ui->m_editSecurityButton->setEnabled(false);
0180         d->ui->m_deleteSecurityButton->setEnabled(false);
0181     }
0182 }
0183 
0184 void KInvestmentView::slotSelectAccount(const QString &id)
0185 {
0186     Q_D(KInvestmentView);
0187     if (!id.isEmpty()) {
0188         d->m_idInvAcc = id;
0189         if (isVisible()) {
0190             d->ui->m_accountComboBox->setSelected(id);
0191             slotSecuritySelected(QModelIndex(), QModelIndex());
0192             slotEquitySelected(QModelIndex(), QModelIndex());
0193         }
0194     }
0195 }
0196 
0197 void KInvestmentView::slotSelectAccount(const MyMoneyObject &obj)
0198 {
0199     if (typeid(obj) != typeid(MyMoneyAccount))
0200         return;
0201     const auto acc = dynamic_cast<const MyMoneyAccount &>(obj);
0202 
0203     if (acc.accountType() == eMyMoney::Account::Type::Investment)
0204         slotSelectAccount(acc.id());
0205 }
0206 
0207 void KInvestmentView::slotLoadAccount(const QString &id)
0208 {
0209     Q_D(KInvestmentView);
0210     const auto indexList = d->m_equitiesProxyModel->match(d->m_equitiesProxyModel->index(0,0), EquitiesModel::InvestmentID, id, 1,
0211                            Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap));
0212 
0213     const auto acc = MyMoneyFile::instance()->account(id);
0214     if (!indexList.isEmpty()) {
0215         d->ui->m_equitiesTree->setRootIndex(indexList.first());
0216         d->m_idInvAcc = id;
0217         if (isVisible())
0218             emit selectByObject(acc, eView::Intent::SynchronizeAccountInLedgersView);
0219     }
0220     updateActions(acc);
0221 }
0222 
0223 void KInvestmentView::slotInvestmentMenuRequested(const QPoint&)
0224 {
0225     Q_D(KInvestmentView);
0226     MyMoneyAccount acc;
0227     auto treeItem = d->ui->m_equitiesTree->currentIndex();
0228     if (treeItem.isValid()) {
0229         auto mdlItem = d->m_equitiesProxyModel->index(treeItem.row(), EquitiesModel::Equity, treeItem.parent());
0230         acc = MyMoneyFile::instance()->account(mdlItem.data(EquitiesModel::EquityID).toString());
0231     }
0232     slotShowInvestmentMenu(acc);
0233 }
0234 
0235 void KInvestmentView::slotShowInvestmentMenu(const MyMoneyAccount& acc)
0236 {
0237     Q_UNUSED(acc);
0238     pMenus[eMenu::Menu::Investment]->exec(QCursor::pos());
0239 }
0240 
0241 void KInvestmentView::slotSelectByObject(const MyMoneyObject& obj, eView::Intent intent)
0242 {
0243     switch(intent) {
0244     case eView::Intent::UpdateActions:
0245         updateActions(obj);
0246         break;
0247 
0248     case eView::Intent::SynchronizeAccountInInvestmentView:
0249         if (KMyMoneySettings::syncLedgerInvestment())
0250             slotSelectAccount(obj);
0251         break;
0252 
0253     case eView::Intent::OpenContextMenu:
0254         slotShowInvestmentMenu(static_cast<const MyMoneyAccount&>(obj));
0255         break;
0256 
0257     default:
0258         break;
0259     }
0260 }
0261 
0262 void KInvestmentView::slotNewInvestment()
0263 {
0264     Q_D(KInvestmentView);
0265     if (!isVisible())
0266         KNewInvestmentWizard::newInvestment(d->m_currentEquity);
0267     else
0268         KNewInvestmentWizard::newInvestment(MyMoneyFile::instance()->account(d->m_idInvAcc));
0269 }
0270 
0271 void KInvestmentView::slotEditInvestment()
0272 {
0273     Q_D(KInvestmentView);
0274     KNewInvestmentWizard::editInvestment(d->m_currentEquity);
0275 }
0276 
0277 void KInvestmentView::slotDeleteInvestment()
0278 {
0279     Q_D(KInvestmentView);
0280     if (KMessageBox::questionYesNo(this,
0281                                    i18n("<p>Do you really want to delete the investment <b>%1</b>?</p>", d->m_currentEquity.name()),
0282                                    i18n("Delete investment"),
0283                                    KStandardGuiItem::yes(), KStandardGuiItem::no(),
0284                                    "DeleteInvestment") == KMessageBox::Yes) {
0285         auto file = MyMoneyFile::instance();
0286         MyMoneyFileTransaction ft;
0287         try {
0288 //      d->m_selectedAccount = MyMoneyAccount(); // CAUTION: deleting equity from investments view needs this, if ID of the equity to be deleted is the smallest from all
0289             file->removeAccount(d->m_currentEquity);
0290             ft.commit();
0291         } catch (const MyMoneyException &e) {
0292             KMessageBox::information(this, i18n("Unable to delete investment: %1", QString::fromLatin1(e.what())));
0293         }
0294     } else {
0295         // we should not keep the 'no' setting because that can confuse people like
0296         // I have seen in some usability tests. So we just delete it right away.
0297         KSharedConfigPtr kconfig = KSharedConfig::openConfig();
0298         if (kconfig) {
0299             kconfig->group(QLatin1String("Notification Messages")).deleteEntry(QLatin1String("DeleteInvestment"));
0300         }
0301     }
0302 }
0303 
0304 void KInvestmentView::slotUpdatePriceOnline()
0305 {
0306     Q_D(KInvestmentView);
0307     if (!d->m_currentEquity.id().isEmpty()) {
0308         QPointer<KEquityPriceUpdateDlg> dlg = new KEquityPriceUpdateDlg(0, d->m_currentEquity.currencyId());
0309         if (dlg->exec() == QDialog::Accepted && dlg != nullptr)
0310             dlg->storePrices();
0311         delete dlg;
0312     }
0313 }
0314 
0315 void KInvestmentView::slotUpdatePriceManually()
0316 {
0317     Q_D(KInvestmentView);
0318     if (!d->m_currentEquity.id().isEmpty()) {
0319         try {
0320             auto security = MyMoneyFile::instance()->security(d->m_currentEquity.currencyId());
0321             auto currency = MyMoneyFile::instance()->security(security.tradingCurrency());
0322             const auto& price = MyMoneyFile::instance()->price(security.id(), currency.id());
0323 
0324             QPointer<KCurrencyCalculator> calc =
0325                 new KCurrencyCalculator(security, currency, MyMoneyMoney::ONE,
0326                                         price.rate(currency.id()), price.date(),
0327                                         MyMoneyMoney::precToDenom(security.pricePrecision()));
0328             calc->setupPriceEditor();
0329 
0330             // The dialog takes care of adding the price if necessary
0331             calc->exec();
0332             delete calc;
0333         } catch (const MyMoneyException &e) {
0334             qDebug("Error in price update: %s", e.what());
0335         }
0336     }
0337 }
0338 
0339 void KInvestmentView::slotEditSecurity()
0340 {
0341     Q_D(KInvestmentView);
0342     auto sec = d->currentSecurity();
0343 
0344     if (!sec.id().isEmpty()) {
0345         QPointer<KNewInvestmentWizard> dlg = new KNewInvestmentWizard(sec, this);
0346         dlg->setObjectName("KNewInvestmentWizard");
0347         if (dlg->exec() == QDialog::Accepted)
0348             dlg->createObjects(QString());
0349         delete dlg;
0350     }
0351 }
0352 
0353 void KInvestmentView::slotDeleteSecurity()
0354 {
0355     Q_D(KInvestmentView);
0356     auto sec = d->currentSecurity();
0357     if (!sec.id().isEmpty())
0358         KMyMoneyUtils::deleteSecurity(sec, this);
0359 }