diff --git a/3rdparty/ext_applications/CMakeLists.txt b/3rdparty/ext_applications/CMakeLists.txt index 91a3733ef..c4d670a7b 100644 --- a/3rdparty/ext_applications/CMakeLists.txt +++ b/3rdparty/ext_applications/CMakeLists.txt @@ -1,45 +1,45 @@ set(EXT_INSTALL_DIR_applications "${EXT_INSTALL_DIR}") ExternalProject_Add( ext_akonadi DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL https://download.kde.org/stable/applications/18.08.2/src/akonadi-18.08.2.tar.xz URL_MD5 bff15765460e954105217831f2786dc2 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_sharedmimeinfo ext_boost ext_xslt ext_kcompletion ext_kconfig ext_kconfigwidgets ext_kcoreaddons ext_kdbusaddons ext_ki18n ext_kiconthemes ext_kitemmodels ext_kitemviews ext_kio ext_kwidgetsaddons ext_kwindowsystem ext_kxmlgui ext_kcrash ) ExternalProject_Add( ext_kpimtextedit DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL https://download.kde.org/stable/applications/18.08.2/src/kpimtextedit-18.08.2.tar.xz URL_MD5 a360356a13ea0ee0f179fa28f267c1d8 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kcodecs ext_kconfig ext_kconfigwidgets ext_kcoreaddons ext_kemoticons ext_ki18n ext_kiconthemes ext_kio ext_sonnet ext_syntaxhighlighting ext_kwidgetsaddons ext_kxmlgui ext_grantlee ) ExternalProject_Add( ext_kidentitymanagement DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL https://download.kde.org/stable/applications/18.08.2/src/kidentitymanagement-18.08.2.tar.xz URL_MD5 c7061788d60a167a3ef92cb5fd7a3ddd CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kcoreaddons ext_kcompletion ext_ktextwidgets ext_kxmlgui ext_kio ext_kconfig ext_kcodecs ext_kiconthemes ext_kpimtextedit ) ExternalProject_Add( ext_kcontacts DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL https://download.kde.org/stable/applications/18.08.2/src/kcontacts-18.08.2.tar.xz URL_MD5 9686d42582cdf6b2a931d2ae036c1433 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kcoreaddons ext_ki18n ext_kconfig ext_kcodecs -) \ No newline at end of file +) diff --git a/3rdparty/ext_frameworks/CMakeLists.txt b/3rdparty/ext_frameworks/CMakeLists.txt index 7528e54cc..70999ce99 100644 --- a/3rdparty/ext_frameworks/CMakeLists.txt +++ b/3rdparty/ext_frameworks/CMakeLists.txt @@ -1,361 +1,361 @@ # All needed frameworks: # Tier 1 # Archive # Codecs # Config # CoreAddons # DBusAddons # GuiAddons # I18n # ItemModels # ItemViews # WidgetsAddons # WindowSystem # Solid # Sonnet # SyntaxHighlighting # Tier 2 # Activities # Auth # Completion # Crash # JobWidgets # Notifications # Package # Tier 3 # Emoticons # ConfigWidgets # IconThemes # Service # TextWidgets # XmlGui # Bookmarks # GlobalAccel # KIO # Declarative # KCMUtils # Wallet ExternalProject_Add( ext_extra_cmake_modules DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/extra-cmake-modules-5.51.0.zip URL_MD5 0c49de5686624746af45f578486c275f CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} ) ExternalProject_Add( ext_karchive DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/karchive-5.51.0.zip URL_MD5 a1df7fa0e563774582a141be902bf9e0 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kcodecs DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kcodecs-5.51.0.zip URL_MD5 690e3fc89a72a64ebf7d4dccfea07cb4 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kconfig DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kconfig-5.51.0.zip URL_MD5 efd15b025d74c6bdb7ba1f6c70eb59d3 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kdbusaddons DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kdbusaddons-5.51.0.zip URL_MD5 a4e3f38596815004815e1d5912dcc9f7 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kcoreaddons DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kcoreaddons-5.51.0.zip URL_MD5 bd29fb792d368dcc031277182e20dd57 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_sharedmimeinfo ext_extra_cmake_modules ) ExternalProject_Add( ext_kguiaddons DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kguiaddons-5.51.0.zip URL_MD5 a4ed02d971740b5a69d3bbd8078cd53c CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_ki18n DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/ki18n-5.51.0.zip URL_MD5 97ea04262c49403ba8c33323b367c362 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ext_gettext ) ExternalProject_Add( ext_kitemmodels DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kitemmodels-5.51.0.zip URL_MD5 82989ebe8c2c76df6fbd3bc4581923a8 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kitemviews DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kitemviews-5.51.0.zip URL_MD5 4b0ac783dd4a08b57442a6b349a29af0 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kwidgetsaddons DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kwidgetsaddons-5.51.0.zip URL_MD5 069a7907a50a573f237b0ae07e143c31 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kwindowsystem DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kwindowsystem-5.51.0.zip URL_MD5 1cc9f16aef24588f56712beacea04173 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_solid DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/solid-5.51.0.zip URL_MD5 20928ec5ac4f24ac50d28e1f859564fc CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_sonnet DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/sonnet-5.51.0.zip URL_MD5 6da10e377b65fd0eb38a893f3b02bda4 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_syntaxhighlighting DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/syntax-highlighting-5.51.0.zip URL_MD5 960aa4322276d5591fe038351c420607 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kholidays DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL https://download.kde.org/stable/frameworks/5.51/kholidays-5.51.0.zip URL_MD5 41b208d432246eb8f012d55bc0c1b9ee CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kactivities DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kactivities-5.51.0.zip URL_MD5 397fe66ad6b2bd4151de87b5bea27550 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_boost ext_kconfig ext_kcoreaddons ext_kwindowsystem ) ExternalProject_Add( ext_kauth DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kauth-5.51.0.zip URL_MD5 c44908198ce80868a0f84d93bfbd2a0b CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kcoreaddons ) ExternalProject_Add( ext_kcompletion DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kcompletion-5.51.0.zip URL_MD5 0538aaf30384c0ac0e69a245d4c99dc5 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kwidgetsaddons ext_kconfig ) ExternalProject_Add( ext_kcrash DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kcrash-5.51.0.zip URL_MD5 4d424ec54f369eb6f6f8d9571e72d0f9 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kcoreaddons ext_kwindowsystem ) ExternalProject_Add( ext_kjobwidgets DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kjobwidgets-5.51.0.zip URL_MD5 e352949fc004cad1a5df7ac6095e061f CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kcoreaddons ext_kwidgetsaddons ) ExternalProject_Add( ext_knotifications DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/knotifications-5.51.0.zip URL_MD5 1727ef67b293228e783d70a8a04fb2b2 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kwindowsystem ext_kconfig ext_kcodecs ext_kcoreaddons ext_phonon ) ExternalProject_Add( ext_kpackage DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kpackage-5.51.0.zip URL_MD5 7e04ab888adb565d6b3c7edab3f5f88b CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_karchive ext_ki18n ext_kcoreaddons ) ExternalProject_Add( ext_kconfigwidgets DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kconfigwidgets-5.51.0.zip URL_MD5 943fe07904feaa3a8b05fe81733d1eb4 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kauth ext_kcoreaddons ext_kcodecs ext_kconfig ext_kguiaddons ext_ki18n ext_kwidgetsaddons ) ExternalProject_Add( ext_kiconthemes DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kiconthemes-5.51.0.zip URL_MD5 6da17c5f2006b893025da305b1dfc011 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_karchive ext_ki18n ext_kcoreaddons ext_kconfigwidgets ext_kwidgetsaddons ext_kitemviews ) ExternalProject_Add( ext_kservice DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kservice-5.51.0.zip URL_MD5 7444dbd3388a1e3cce9d8f7cd64c7d23 PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kservice.diff #this makes finding KCModules in AppImage possible CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kconfig ext_kcoreaddons ext_kcrash ext_kdbusaddons ext_ki18n ) ExternalProject_Add( ext_kemoticons DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kemoticons-5.51.0.zip URL_MD5 f437001a8a5e401a8ac284cf554357ec CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_karchive ext_kconfig ext_kservice ext_kcoreaddons ) ExternalProject_Add( ext_ktextwidgets DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/ktextwidgets-5.51.0.zip URL_MD5 2edb4f80800e47d6ecf52353decbdf10 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ext_kcompletion ext_kconfig ext_kconfigwidgets ext_ki18n ext_kiconthemes ext_kservice ext_kwidgetsaddons ext_kwindowsystem ext_sonnet ) ExternalProject_Add( ext_kglobalaccel DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kglobalaccel-5.51.0.zip URL_MD5 1cf9b73d7e909ed5899496eb52dfedc2 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kconfig ext_kcoreaddons ext_kcrash ext_kdbusaddons ext_kwindowsystem ) ExternalProject_Add( ext_kxmlgui DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kxmlgui-5.51.0.zip URL_MD5 35555839af065708281ecf95f2cd9363 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kcoreaddons ext_kitemviews ext_kconfig ext_kconfigwidgets ext_ki18n ext_kiconthemes ext_ktextwidgets ext_kwidgetsaddons ext_kwindowsystem ext_kglobalaccel ) ExternalProject_Add( ext_kbookmarks DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kbookmarks-5.51.0.zip URL_MD5 036f8e3e2d9c1cd537a275636569d1c6 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kconfig ext_kcoreaddons ext_kcodecs ext_kconfigwidgets ext_kiconthemes ext_kwidgetsaddons ext_kxmlgui ) ExternalProject_Add( ext_kwallet DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kwallet-5.51.0.zip URL_MD5 da6f7e2282f6316b612cb0acaa7a140a CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_gcrypt ext_gpgme ext_kcoreaddons ext_kconfig ext_kwindowsystem ext_ki18n ext_kconfigwidgets ext_knotifications ext_kservice ext_kwidgetsaddons ext_kiconthemes ext_kdbusaddons ) ExternalProject_Add( ext_kio DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kio-5.51.0.zip URL_MD5 ea59ac8326e5856b0ef1202c83d575b3 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_extra_cmake_modules ext_karchive ext_kconfig ext_kcoreaddons ext_kdbusaddons ext_ki18n ext_kservice ext_solid ext_kwindowsystem ext_kwidgetsaddons ext_kcompletion ext_kconfigwidgets ext_kiconthemes ext_kitemviews ext_kjobwidgets ext_kbookmarks ext_kwallet ) ExternalProject_Add( ext_kdeclarative DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kdeclarative-5.51.0.zip URL_MD5 6d8b8a15b7d02ac662e7f22152e92f3f CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kconfig ext_ki18n ext_kiconthemes ext_kio ext_kwidgetsaddons ext_kwindowsystem ext_kglobalaccel ext_kguiaddons ext_kpackage ) ExternalProject_Add( ext_kcmutils DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kcmutils-5.51.0.zip URL_MD5 4f548b92a27716f1d498b9da8887cf32 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kitemviews ext_kconfigwidgets ext_kcoreaddons ext_ki18n ext_kiconthemes ext_kservice ext_kxmlgui ext_kdeclarative ) ExternalProject_Add( ext_kimageformats DOWNLOAD_DIR ${EXT_DOWNLOAD_DIR} URL http://download.kde.org/stable/frameworks/5.51/kimageformats-5.51.0.zip URL_MD5 bb889cbd5a3b39f226d57da4c694ff57 CMAKE_ARGS ${CMAKE_GLOBAL_PROFILE} DEPENDS ext_kitemviews -) \ No newline at end of file +) diff --git a/contrib/getsplitpart.pl b/contrib/getsplitpart.pl index 515ef73b3..5436021a6 100755 --- a/contrib/getsplitpart.pl +++ b/contrib/getsplitpart.pl @@ -1,91 +1,91 @@ #!/usr/bin/perl # # Copyright 2019 Thomas Baumgart # # 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. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # ---------------------------------------------------------------------- # # Extract split field values for a specific account and field into a CSV file # # The script reads a plain text KMyMoney XML data file on stdin and writes # the following columns as CSV format on stdout: # # transactionId;splitId;[;numeric amount] # -# in case the selected field is either 'shares' or 'value' the colum 'amount' +# in case the selected field is either 'shares' or 'value' the column 'amount' # will be added to the output which contains the value in a form that can be # processed by spreadsheet programs # # # usage: zcat data.kmy | getsplitpart.pl --acc=A000002 --field=shares > output.csv # my @args = @ARGV; my $accountid; my $field; my $fields = ";shares;value;payee;"; my @files = (); while ($#args >= 0) { my $a = shift @args; $accountid = $1 if ($a =~ /--acc=(.*)/); $field = $1 if ($a =~ /--field=(.*)/); } die ("Field '$field' not supported.") if ($fields !~ /\;$field\;/); die ("Missing account id. Use --acc= to specify.") if ($accountid eq ""); -die ("Missing field name. Use --field= to specifiy.") if ($field eq ""); +die ("Missing field name. Use --field= to specify.") if ($field eq ""); my $transactionid; my $splitid; # print header line print "transactionId;splitId;$field"; print ";amount" if ($field =~ /^(shares|value)$/); print "\n"; while() { if ($_ =~ / #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "../ofxpartner.h" #include "mymoneyofxconnector.h" using KWallet::Wallet; class KOnlineBankingSetupWizard::Private { public: Private() : m_prevPage(-1), m_wallet(0), m_walletIsOpen(false) {} QFile m_fpTrace; QTextStream m_trace; int m_prevPage; Wallet *m_wallet; bool m_walletIsOpen; }; KOnlineBankingSetupWizard::KOnlineBankingSetupWizard(QWidget *parent): QWizard(parent), d(new Private), m_fDone(false), m_fInit(false), m_appId(0) { setupUi(this); m_applicationEdit->hide(); m_headerVersionEdit->hide(); #ifndef LIBOFX_HAVE_CLIENTUID m_editClientUid->setEnabled(false); m_clientUidLabel->setEnabled(false); #endif m_appId = new OfxAppVersion(m_applicationCombo, m_applicationEdit, ""); m_headerVersion = new OfxHeaderVersion(m_headerVersionCombo, ""); // fill the list view with banks QProgressDialog* dlg = new QProgressDialog(this); dlg->setWindowTitle(i18n("Loading banklist")); dlg->setLabelText(i18n("Getting list of banks from https://www.ofxhome.com/\nThis may take some time depending on the available bandwidth.")); dlg->setModal(true); dlg->setCancelButton(0); // force to show immediately as the call to OfxPartner::BankNames() // does not call the processEvents() loop dlg->setMinimumDuration(0); QCoreApplication::processEvents(); //set password field according to KDE preferences m_editPassword->setPasswordMode(true); // make sure to not exceed data fields m_editUsername->setMaxLength(OFX_USERID_LENGTH-1); m_editPassword->setMaxLength(OFX_USERPASS_LENGTH-1); KListWidgetSearchLine* searchLine = new KListWidgetSearchLine(autoTab, m_listFi); vboxLayout1->insertWidget(0, searchLine); QTimer::singleShot(20, searchLine, SLOT(setFocus())); OfxPartner::setDirectory(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + ""); m_listFi->addItems(OfxPartner::BankNames()); m_fInit = true; delete dlg; checkNextButton(); connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(checkNextButton())); connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(newPage(int))); connect(m_listFi, SIGNAL(itemSelectionChanged()), this, SLOT(checkNextButton())); connect(m_listAccount, SIGNAL(itemSelectionChanged()), this, SLOT(checkNextButton())); connect(m_selectionTab, SIGNAL(currentChanged(int)), this, SLOT(checkNextButton())); connect(m_fid, SIGNAL(userTextChanged(QString)), this, SLOT(checkNextButton())); connect(m_bankName, SIGNAL(userTextChanged(QString)), this, SLOT(checkNextButton())); connect(m_url, SIGNAL(textChanged(QString)), this, SLOT(checkNextButton())); connect(m_editUsername, SIGNAL(userTextChanged(QString)), this, SLOT(checkNextButton())); connect(m_editPassword, SIGNAL(userTextChanged(QString)), this, SLOT(checkNextButton())); connect(m_applicationEdit, SIGNAL(userTextChanged(QString)), this, SLOT(checkNextButton())); connect(m_applicationCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(applicationSelectionChanged())); // setup text on buttons setButtonText(QWizard::NextButton, i18nc("Go to next page of the wizard", "&Next")); setButtonText(QWizard::BackButton, KStandardGuiItem::back().text()); // setup icons button(QWizard::FinishButton)->setIcon(KStandardGuiItem::ok().icon()); button(QWizard::CancelButton)->setIcon(KStandardGuiItem::cancel().icon()); button(QWizard::NextButton)->setIcon(KStandardGuiItem::forward(KStandardGuiItem::UseRTL).icon()); button(QWizard::BackButton)->setIcon(KStandardGuiItem::back(KStandardGuiItem::UseRTL).icon()); m_problemMessages->setHidden(true); m_problemMessages->setWordWrap(true); } KOnlineBankingSetupWizard::~KOnlineBankingSetupWizard() { delete m_appId; delete d; } void KOnlineBankingSetupWizard::applicationSelectionChanged() { m_applicationEdit->setVisible(m_appId->appId().endsWith(':')); checkNextButton(); } void KOnlineBankingSetupWizard::walletOpened(bool ok) { if (ok && (d->m_wallet->hasFolder(KWallet::Wallet::PasswordFolder()) || d->m_wallet->createFolder(KWallet::Wallet::PasswordFolder())) && d->m_wallet->setFolder(KWallet::Wallet::PasswordFolder())) { d->m_walletIsOpen = true; } else { qDebug("Wallet was not opened"); } m_storePassword->setEnabled(d->m_walletIsOpen); } void KOnlineBankingSetupWizard::checkNextButton() { bool enableButton = false; switch (currentId()) { case 0: if (m_selectionTab->currentIndex() == 0) { enableButton = (m_listFi->currentItem() != 0) && m_listFi->currentItem()->isSelected(); } else { enableButton = !(m_url->url().isEmpty() || m_bankName->text().isEmpty()); } break; case 1: enableButton = !(m_editUsername->text().isEmpty() || m_editPassword->text().isEmpty() || !m_appId->isValid()); break; case 2: enableButton = (m_listAccount->currentItem() != 0) && m_listAccount->currentItem()->isSelected(); break; } button(QWizard::NextButton)->setEnabled(enableButton); } void KOnlineBankingSetupWizard::newPage(int id) { QWidget* focus = 0; m_problemMessages->setHidden(true); bool ok = true; if ((id - d->m_prevPage) == 1) { // one page forward? switch (d->m_prevPage) { case 0: ok = finishFiPage(); // open the KDE wallet if not already opened if (ok && !d->m_wallet) { d->m_wallet = Wallet::openWallet(Wallet::NetworkWallet(), winId(), Wallet::Asynchronous); connect(d->m_wallet, SIGNAL(walletOpened(bool)), SLOT(walletOpened(bool))); } focus = m_editUsername; break; case 1: ok = finishLoginPage(); focus = m_listAccount; break; case 2: m_fDone = ok = finishAccountPage(); break; } if (ok) { if (focus) { focus->setFocus(); } } else { // force to go back to prev page back(); } } else { // going backwards, we're never done m_fDone = false; } button(QWizard::FinishButton)->setEnabled(m_fDone); // hide cancel and back button on last page button(QWizard::CancelButton)->setVisible(!m_fDone); button(QWizard::BackButton)->setVisible(!m_fDone); if (ok) d->m_prevPage = id; } bool KOnlineBankingSetupWizard::finishFiPage() { bool result = false; m_bankInfo.clear(); OfxHomeServiceInfo info; if (m_selectionTab->currentIndex() == 0) { // Get the fipids for the selected bank QListWidgetItem* item = m_listFi->currentItem(); if (item && item->isSelected()) { QString bank = item->text(); m_textDetails->clear(); m_textDetails->append(QString("

Details for %1:

").arg(bank)); QStringList fipids = OfxPartner::FipidForBank(bank); QStringList::const_iterator it_fipid = fipids.constBegin(); while (it_fipid != fipids.constEnd()) { // For each fipid, get the connection details info = OfxPartner::ServiceInfo(*it_fipid); // Print them to the text browser QString message = QString("

Fipid: %1
").arg(*it_fipid); // If the bank supports retrieving statements if (info.ofxInfo.accountlist) { m_bankInfo.push_back(info.ofxInfo); message += QString("URL: %1
Org: %2
Fid: %3
").arg(info.ofxInfo.url, info.ofxInfo.org, info.ofxInfo.fid); if (info.ofxInfo.statements) message += i18n("Supports online statements
"); if (info.ofxInfo.investments) message += i18n("Supports investments
"); if (info.ofxInfo.billpay) message += i18n("Supports bill payment (but not supported by KMyMoney yet)
"); QString problemMessage; if (!info.ofxValidated) - problemMessage += i18n("OFX host failed. Last successful access was on '%1'. ").arg(info.lastOfxValidated); + problemMessage += i18n("OFX host failed. Last successful access was on '%1'. ", info.lastOfxValidated); if (!info.sslValidated) - problemMessage += i18n("Certificate verification of OFX host failed. Last successful verification was on '%1'.").arg(info.lastSslValidated); + problemMessage += i18n("Certificate verification of OFX host failed. Last successful verification was on '%1'.", info.lastSslValidated); if (!problemMessage.isEmpty()) { m_problemMessages->setText(problemMessage); m_problemMessages->animatedShow(); } } else { message += i18n("Does not support online banking"); } message += "

"; m_textDetails->append(message); ++it_fipid; } result = true; } else // error! No current item KMessageBox::sorry(this, i18n("Please choose a bank.")); } else { // manual entry of values if (m_fid->text().isEmpty() || m_url->url().isEmpty() || m_bankName->text().isEmpty()) { KMessageBox::sorry(this, i18n("Please fill all fields with values.")); } m_textDetails->clear(); m_textDetails->append(i18n("

Details for %1:

", m_bankName->text())); memset(&info.ofxInfo, 0, sizeof(OfxFiServiceInfo)); strncpy(info.ofxInfo.fid, m_fid->text().toLatin1(), OFX_FID_LENGTH - 1); strncpy(info.ofxInfo.org, m_bankName->text().toLatin1(), OFX_ORG_LENGTH - 1); strncpy(info.ofxInfo.url, m_url->url().url().toLatin1(), OFX_URL_LENGTH - 1); info.ofxInfo.accountlist = 1; info.ofxInfo.statements = 1; info.ofxInfo.billpay = 1; info.ofxInfo.investments = 1; m_bankInfo.push_back(info.ofxInfo); QString message; message += QString("

URL: %1
Org: %2
Fid: %3
").arg(info.ofxInfo.url, info.ofxInfo.org, info.ofxInfo.fid); if (info.ofxInfo.statements) message += i18n("Supports online statements
"); if (info.ofxInfo.investments) message += i18n("Supports investments
"); if (info.ofxInfo.billpay) message += i18n("Supports bill payment (but not supported by KMyMoney yet)
"); message += "

"; m_textDetails->append(message); result = true; } // make sure to display the beginning of the collected information m_textDetails->moveCursor(QTextCursor::Start); return result; } bool KOnlineBankingSetupWizard::finishLoginPage() { bool result = true; QString username = m_editUsername->text(); QString password = m_editPassword->text(); QString clientUid = m_editClientUid->text(); m_listAccount->clear(); // Process an account request for each fipid m_it_info = m_bankInfo.constBegin(); while (m_it_info != m_bankInfo.constEnd()) { OfxFiLogin fi; memset(&fi, 0, sizeof(OfxFiLogin)); Q_ASSERT(sizeof(fi.fid) == sizeof((*m_it_info).fid)); Q_ASSERT(sizeof(fi.org) == sizeof((*m_it_info).org)); memcpy(fi.fid, (*m_it_info).fid, OFX_FID_LENGTH - 1); memcpy(fi.org, (*m_it_info).org, OFX_ORG_LENGTH - 1); strncpy(fi.userid, username.toLatin1().left(OFX_USERID_LENGTH - 1), OFX_USERID_LENGTH - 1); strncpy(fi.userpass, password.toLatin1(), OFX_USERPASS_LENGTH - 1); #ifdef LIBOFX_HAVE_CLIENTUID strncpy(fi.clientuid, clientUid.toLatin1(), OFX_CLIENTUID_LENGTH - 1); #endif // pretend we're Quicken 2008 // http://ofxblog.wordpress.com/2007/06/06/ofx-appid-and-appver-for-intuit-products/ // http://ofxblog.wordpress.com/2007/06/06/ofx-appid-and-appver-for-microsoft-money/ QString appId = m_appId->appId(); QRegExp exp("(.*):(.*)"); if (exp.indexIn(appId) != -1) { strncpy(fi.appid, exp.cap(1).toLatin1(), OFX_APPID_LENGTH - 1); if (exp.cap(2).isEmpty()) { strncpy(fi.appver, m_applicationEdit->text().toLatin1(), OFX_APPVER_LENGTH - 1); } else { strncpy(fi.appver, exp.cap(2).toLatin1(), OFX_APPVER_LENGTH - 1); } } else { strncpy(fi.appid, "QWIN", OFX_APPID_LENGTH - 1); strncpy(fi.appver, "1700", OFX_APPVER_LENGTH - 1); } QString hver = m_headerVersion->headerVersion(); strncpy(fi.header_version, hver.toLatin1(), OFX_HEADERVERSION_LENGTH - 1); QUrl filename(QString("file://%1response.ofx").arg(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/'))); QByteArray req(libofx_request_accountinfo(&fi)); // because the event loop is running while the request is performed disable the back button // (this function is not reentrant so the application might crash when back/next are used) QAbstractButton *backButton = button(QWizard::BackButton); bool backButtonState = backButton->isEnabled(); backButton->setEnabled(false); OfxHttpRequest(QString("POST"), QUrl((*m_it_info).url), req, QMap(), filename, false); backButton->setEnabled(backButtonState); LibofxContextPtr ctx = libofx_get_new_context(); Q_CHECK_PTR(ctx); ofx_set_account_cb(ctx, ofxAccountCallback, this); ofx_set_status_cb(ctx, ofxStatusCallback, this); // Add resulting accounts to the account list libofx_proc_file(ctx, filename.toLocalFile().toLatin1(), AUTODETECT); libofx_free_context(ctx); ++m_it_info; } if (! m_listAccount->topLevelItem(0)) { KMessageBox::sorry(this, i18n("No suitable accounts were found at this bank.")); result = false; } else { m_listAccount->resizeColumnToContents(0); m_listAccount->resizeColumnToContents(1); m_listAccount->resizeColumnToContents(2); m_listAccount->resizeColumnToContents(3); } return result; } bool KOnlineBankingSetupWizard::finishAccountPage() { bool result = true; if (! m_listAccount->currentItem()) { KMessageBox::sorry(this, i18n("Please choose an account")); result = false; } return result; } int KOnlineBankingSetupWizard::ofxAccountCallback(struct OfxAccountData data, void * pv) { KOnlineBankingSetupWizard* pthis = reinterpret_cast(pv); // Put the account info in the view MyMoneyKeyValueContainer kvps; if (data.account_type_valid) { QString type; switch (data.account_type) { case OfxAccountData::OFX_CHECKING: /**< A standard checking account */ type = "CHECKING"; break; case OfxAccountData::OFX_SAVINGS: /**< A standard savings account */ type = "SAVINGS"; break; case OfxAccountData::OFX_MONEYMRKT: /**< A money market account */ type = "MONEY MARKET"; break; case OfxAccountData::OFX_CREDITLINE: /**< A line of credit */ type = "CREDIT LINE"; break; case OfxAccountData::OFX_CMA: /**< Cash Management Account */ type = "CMA"; break; case OfxAccountData::OFX_CREDITCARD: /**< A credit card account */ type = "CREDIT CARD"; break; case OfxAccountData::OFX_INVESTMENT: /**< An investment account */ type = "INVESTMENT"; break; default: break; } kvps.setValue("type", type); } if (data.bank_id_valid) kvps.setValue("bankid", data.bank_id); if (data.broker_id_valid) kvps.setValue("bankid", data.broker_id); if (data.branch_id_valid) kvps.setValue("branchid", data.branch_id); if (data.account_number_valid) kvps.setValue("accountid", data.account_number); if (data.account_id_valid) kvps.setValue("uniqueId", data.account_id); kvps.setValue("username", pthis->m_editUsername->text()); kvps.setValue("password", pthis->m_editPassword->text()); #ifdef LIBOFX_HAVE_CLIENTUID kvps.setValue("clientUid", pthis->m_editClientUid->text()); #endif kvps.setValue("url", (*(pthis->m_it_info)).url); kvps.setValue("fid", (*(pthis->m_it_info)).fid); kvps.setValue("org", (*(pthis->m_it_info)).org); kvps.setValue("fipid", ""); QListWidgetItem* item = pthis->m_listFi->currentItem(); if (item) kvps.setValue("bankname", item->text()); // I removed the bankid here, because for some users it // was not possible to setup the automatic account matching // because the bankid was left empty here as well during // the statement download. In case we don't have it, we // simply use it blank. (ipwizard 2009-06-21) if (/* !kvps.value("bankid").isEmpty() && */ !kvps.value("uniqueId").isEmpty()) { kvps.setValue("kmmofx-acc-ref", QString("%1-%2").arg(kvps.value("bankid"), kvps.value("uniqueId"))); } else { qDebug("Cannot setup kmmofx-acc-ref for '%s'", qPrintable(kvps.value("bankname"))); } kvps.setValue("protocol", "OFX"); new ListViewItem(pthis->m_listAccount, kvps); return 0; } int KOnlineBankingSetupWizard::ofxStatusCallback(struct OfxStatusData data, void * pv) { KOnlineBankingSetupWizard* pthis = reinterpret_cast(pv); QString message; if (data.code_valid) { message += QString("#%1 %2: \"%3\"\n").arg(data.code).arg(data.name, data.description); } if (data.server_message_valid) { message += i18n("Server message: %1\n", data.server_message); } if (data.severity_valid) { switch (data.severity) { case OfxStatusData::INFO : break; case OfxStatusData::WARN : KMessageBox::detailedError(pthis, i18n("Your bank returned warnings when signing on"), i18nc("Warning 'message'", "WARNING %1", message)); break; case OfxStatusData::ERROR : KMessageBox::detailedError(pthis, i18n("Error signing onto your bank"), i18n("ERROR %1", message)); break; default: break; } } return 0; } bool KOnlineBankingSetupWizard::chosenSettings(MyMoneyKeyValueContainer& settings) { bool result = false;; if (m_fDone) { QTreeWidgetItem* qitem = m_listAccount->currentItem(); ListViewItem* item = dynamic_cast(qitem); if (item && item->isSelected()) { settings = *item; settings.deletePair("appId"); settings.deletePair("kmmofx-headerVersion"); QString appId = m_appId->appId(); if (!appId.isEmpty()) { if (appId.endsWith(':')) { appId += m_applicationEdit->text(); } settings.setValue("appId", appId); } QString hVer = m_headerVersion->headerVersion(); if (!hVer.isEmpty()) settings.setValue("kmmofx-headerVersion", hVer); if (m_storePassword->isChecked()) { if (d->m_walletIsOpen) { QString key = OFX_PASSWORD_KEY(settings.value("url"), settings.value("uniqueId")); d->m_wallet->writePassword(key, settings.value("password")); settings.deletePair("password"); } } else { settings.deletePair("password"); } result = true; } } return result; } KOnlineBankingSetupWizard::ListViewItem::ListViewItem(QTreeWidget* parent, const MyMoneyKeyValueContainer& kvps): MyMoneyKeyValueContainer(kvps), QTreeWidgetItem(parent) { setText(0, value("accountid")); setText(1, value("type")); setText(2, value("bankid")); setText(3, value("branchid")); } diff --git a/kmymoney/plugins/qif/export/qifexporter.cpp b/kmymoney/plugins/qif/export/qifexporter.cpp index 2cf802569..6f2cec1c2 100644 --- a/kmymoney/plugins/qif/export/qifexporter.cpp +++ b/kmymoney/plugins/qif/export/qifexporter.cpp @@ -1,108 +1,103 @@ /* * Copyright 2017 Łukasz Wojniłowicz * Copyright 2019 Thomas Baumgart * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "qifexporter.h" // ---------------------------------------------------------------------------- // QT Includes #ifdef IS_APPIMAGE - #include - #include +#include +#include #endif // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kexportdlg.h" #include "mymoneyqifwriter.h" #include "viewinterface.h" -#ifdef IS_APPIMAGE -#include -#include -#endif - QIFExporter::QIFExporter(QObject *parent, const QVariantList &args) : KMyMoneyPlugin::Plugin(parent, "qifexporter"/*must be the same as X-KDE-PluginInfo-Name*/) { Q_UNUSED(args); const auto componentName = QLatin1String("qifexporter"); const auto rcFileName = QLatin1String("qifexporter.rc"); setComponentName(componentName, i18n("QIF exporter")); #ifdef IS_APPIMAGE const QString rcFilePath = QString("%1/../share/kxmlgui5/%2/%3").arg(QCoreApplication::applicationDirPath(), componentName, rcFileName); setXMLFile(rcFilePath); const QString localRcFilePath = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).first() + QLatin1Char('/') + componentName + QLatin1Char('/') + rcFileName; setLocalXMLFile(localRcFilePath); #else setXMLFile(rcFileName); #endif createActions(); // For information, announce that we have been loaded. qDebug("Plugins: qifexporter loaded"); } QIFExporter::~QIFExporter() { qDebug("Plugins: qifexporter unloaded"); } void QIFExporter::createActions() { const auto &kpartgui = QStringLiteral("file_export_qif"); m_action = actionCollection()->addAction(kpartgui); m_action->setText(i18n("QIF...")); connect(m_action, &QAction::triggered, this, &QIFExporter::slotQifExport); connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::viewStateChanged, action(qPrintable(kpartgui)), &QAction::setEnabled); } void QIFExporter::slotQifExport() { m_action->setEnabled(false); QPointer dlg = new KExportDlg(nullptr); if (dlg->exec() == QDialog::Accepted && dlg != nullptr) { // if (okToWriteFile(QUrl::fromLocalFile(dlg->filename()))) { MyMoneyQifWriter writer; connect(&writer, SIGNAL(signalProgress(int,int)), this, SLOT(slotStatusProgressBar(int,int))); writer.write(dlg->filename(), dlg->profile(), dlg->accountId(), dlg->accountSelected(), dlg->categorySelected(), dlg->startDate(), dlg->endDate()); // } } delete dlg; m_action->setEnabled(true); } K_PLUGIN_FACTORY_WITH_JSON(QIFExporterFactory, "qifexporter.json", registerPlugin();) #include "qifexporter.moc" diff --git a/kmymoney/plugins/qif/import/qifimporter.cpp b/kmymoney/plugins/qif/import/qifimporter.cpp index 5513523be..44b94c46b 100644 --- a/kmymoney/plugins/qif/import/qifimporter.cpp +++ b/kmymoney/plugins/qif/import/qifimporter.cpp @@ -1,133 +1,128 @@ /* * Copyright 2017 Łukasz Wojniłowicz * Copyright 2019 Thomas Baumgart * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "qifimporter.h" // ---------------------------------------------------------------------------- // QT Includes #ifdef IS_APPIMAGE - #include - #include +#include +#include #endif // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kimportdlg.h" #include "mymoneyqifreader.h" #include "statementinterface.h" #include "viewinterface.h" -#ifdef IS_APPIMAGE -#include -#include -#endif - class MyMoneyStatement; QIFImporter::QIFImporter(QObject *parent, const QVariantList &args) : KMyMoneyPlugin::Plugin(parent, "qifimporter"/*must be the same as X-KDE-PluginInfo-Name*/), m_qifReader(nullptr) { Q_UNUSED(args); const auto componentName = QLatin1String("qifimporter"); const auto rcFileName = QLatin1String("qifimporter.rc"); setComponentName(componentName, i18n("QIF importer")); #ifdef IS_APPIMAGE const QString rcFilePath = QString("%1/../share/kxmlgui5/%2/%3").arg(QCoreApplication::applicationDirPath(), componentName, rcFileName); setXMLFile(rcFilePath); const QString localRcFilePath = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).first() + QLatin1Char('/') + rcFileName; setLocalXMLFile(localRcFilePath); #else setXMLFile(rcFileName); #endif createActions(); // For information, announce that we have been loaded. qDebug("Plugins: qifimporter loaded"); } QIFImporter::~QIFImporter() { delete m_qifReader; qDebug("Plugins: qifimporter unloaded"); } void QIFImporter::createActions() { const auto &kpartgui = QStringLiteral("file_import_qif"); m_action = actionCollection()->addAction(kpartgui); m_action->setText(i18n("QIF...")); connect(m_action, &QAction::triggered, this, &QIFImporter::slotQifImport); connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::viewStateChanged, action(qPrintable(kpartgui)), &QAction::setEnabled); } void QIFImporter::slotQifImport() { QPointer dlg = new KImportDlg(nullptr); if (dlg->exec() == QDialog::Accepted && dlg != nullptr) { m_action->setEnabled(false); delete m_qifReader; m_qifReader = new MyMoneyQifReader; statementInterface()->resetMessages(); connect(m_qifReader, &MyMoneyQifReader::statementsReady, this, &QIFImporter::slotGetStatements); m_qifReader->setURL(dlg->file()); m_qifReader->setProfile(dlg->profile()); m_qifReader->setCategoryMapping(dlg->m_typeComboBox->currentIndex() == 0); if (!m_qifReader->startImport()) { delete m_qifReader; statementInterface()->showMessages(0); m_action->setEnabled(true); } } delete dlg; } bool QIFImporter::slotGetStatements(const QList &statements) { auto ret = true; for (const auto& statement : statements) { const auto singleImportSummary = statementInterface()->import(statement); if (singleImportSummary.isEmpty()) ret = false; } // inform the user about the result of the operation statementInterface()->showMessages(statements.count()); // allow further QIF imports m_action->setEnabled(true); return ret; } K_PLUGIN_FACTORY_WITH_JSON(QIFImporterFactory, "qifimporter.json", registerPlugin();) #include "qifimporter.moc" diff --git a/kmymoney/plugins/views/forecast/forecastviewsettings.kcfg b/kmymoney/plugins/views/forecast/forecastviewsettings.kcfg index 6c874fe34..e270667b0 100644 --- a/kmymoney/plugins/views/forecast/forecastviewsettings.kcfg +++ b/kmymoney/plugins/views/forecast/forecastviewsettings.kcfg @@ -1,57 +1,56 @@ - diff --git a/kmymoney/plugins/views/reports/reportsviewsettings.kcfg b/kmymoney/plugins/views/reports/reportsviewsettings.kcfg index 9ff45991a..4016b5a37 100644 --- a/kmymoney/plugins/views/reports/reportsviewsettings.kcfg +++ b/kmymoney/plugins/views/reports/reportsviewsettings.kcfg @@ -1,8 +1,7 @@ - diff --git a/kmymoney/plugins/xml/xmlstoragesettings.kcfg b/kmymoney/plugins/xml/xmlstoragesettings.kcfg index a5ee0e71b..ba8336946 100644 --- a/kmymoney/plugins/xml/xmlstoragesettings.kcfg +++ b/kmymoney/plugins/xml/xmlstoragesettings.kcfg @@ -1,8 +1,7 @@ - diff --git a/kmymoney/views/kgloballedgerview.cpp b/kmymoney/views/kgloballedgerview.cpp index cbb4d3718..975c794af 100644 --- a/kmymoney/views/kgloballedgerview.cpp +++ b/kmymoney/views/kgloballedgerview.cpp @@ -1,2177 +1,2177 @@ /*************************************************************************** kgloballedgerview.cpp - description ------------------- begin : Wed Jul 26 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * 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 "kgloballedgerview_p.h" #include // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyaccount.h" #include "mymoneyfile.h" #include "kmymoneyaccountcombo.h" #include "kmymoneypayeecombo.h" #include "keditscheduledlg.h" #include "kendingbalancedlg.h" #include "register.h" #include "transactioneditor.h" #include "selectedtransactions.h" #include "kmymoneysettings.h" #include "registersearchline.h" #include "kfindtransactiondlg.h" #include "accountsmodel.h" #include "models.h" #include "mymoneyschedule.h" #include "mymoneysecurity.h" #include "mymoneytransaction.h" #include "mymoneytransactionfilter.h" #include "mymoneysplit.h" #include "transaction.h" #include "transactionform.h" #include "widgetenums.h" #include "mymoneyenums.h" #include "menuenums.h" using namespace eMenu; QDate KGlobalLedgerViewPrivate::m_lastPostDate; KGlobalLedgerView::KGlobalLedgerView(QWidget *parent) : KMyMoneyViewBase(*new KGlobalLedgerViewPrivate(this), parent) { typedef void(KGlobalLedgerView::*KGlobalLedgerViewFunc)(); const QHash actionConnections { {Action::NewTransaction, &KGlobalLedgerView::slotNewTransaction}, {Action::EditTransaction, &KGlobalLedgerView::slotEditTransaction}, {Action::DeleteTransaction, &KGlobalLedgerView::slotDeleteTransaction}, {Action::DuplicateTransaction, &KGlobalLedgerView::slotDuplicateTransaction}, {Action::EnterTransaction, &KGlobalLedgerView::slotEnterTransaction}, {Action::AcceptTransaction, &KGlobalLedgerView::slotAcceptTransaction}, {Action::CancelTransaction, &KGlobalLedgerView::slotCancelTransaction}, {Action::EditSplits, &KGlobalLedgerView::slotEditSplits}, {Action::CopySplits, &KGlobalLedgerView::slotCopySplits}, {Action::GoToPayee, &KGlobalLedgerView::slotGoToPayee}, {Action::GoToAccount, &KGlobalLedgerView::slotGoToAccount}, {Action::MatchTransaction, &KGlobalLedgerView::slotMatchTransactions}, {Action::CombineTransactions, &KGlobalLedgerView::slotCombineTransactions}, {Action::ToggleReconciliationFlag, &KGlobalLedgerView::slotToggleReconciliationFlag}, {Action::MarkCleared, &KGlobalLedgerView::slotMarkCleared}, {Action::MarkReconciled, &KGlobalLedgerView::slotMarkReconciled}, {Action::MarkNotReconciled, &KGlobalLedgerView::slotMarkNotReconciled}, {Action::SelectAllTransactions, &KGlobalLedgerView::slotSelectAllTransactions}, {Action::NewScheduledTransaction, &KGlobalLedgerView::slotCreateScheduledTransaction}, {Action::AssignTransactionsNumber, &KGlobalLedgerView::slotAssignNumber}, {Action::StartReconciliation, &KGlobalLedgerView::slotStartReconciliation}, {Action::FinishReconciliation, &KGlobalLedgerView::slotFinishReconciliation}, {Action::PostponeReconciliation, &KGlobalLedgerView::slotPostponeReconciliation}, {Action::OpenAccount, &KGlobalLedgerView::slotOpenAccount}, {Action::EditFindTransaction, &KGlobalLedgerView::slotFindTransaction}, }; for (auto a = actionConnections.cbegin(); a != actionConnections.cend(); ++a) connect(pActions[a.key()], &QAction::triggered, this, a.value()); KXmlGuiWindow* mw = KMyMoneyUtils::mainWindow(); KStandardAction::copy(this, &KGlobalLedgerView::slotCopyTransactionToClipboard, mw->actionCollection()); Q_D(KGlobalLedgerView); d->m_balanceWarning.reset(new KBalanceWarning(this)); } KGlobalLedgerView::~KGlobalLedgerView() { } void KGlobalLedgerView::executeCustomAction(eView::Action action) { Q_D(KGlobalLedgerView); switch(action) { case eView::Action::Refresh: refresh(); break; case eView::Action::SetDefaultFocus: // delay the setFocus call until the event loop is running QMetaObject::invokeMethod(d->m_registerSearchLine->searchLine(), "setFocus", Qt::QueuedConnection); break; case eView::Action::DisableViewDepenedendActions: pActions[Action::SelectAllTransactions]->setEnabled(false); break; case eView::Action::InitializeAfterFileOpen: d->m_lastSelectedAccountID.clear(); d->m_currentAccount = MyMoneyAccount(); if (d->m_accountComboBox) { d->m_accountComboBox->setSelected(QString()); } break; case eView::Action::CleanupBeforeFileClose: if (d->m_inEditMode) { d->deleteTransactionEditor(); } break; default: break; } } void KGlobalLedgerView::refresh() { Q_D(KGlobalLedgerView); if (isVisible()) { if (!d->m_inEditMode) { setUpdatesEnabled(false); d->loadView(); setUpdatesEnabled(true); d->m_needsRefresh = false; // force a new account if the current one is empty d->m_newAccountLoaded = d->m_currentAccount.id().isEmpty(); } } else { d->m_needsRefresh = true; } } void KGlobalLedgerView::showEvent(QShowEvent* event) { if (MyMoneyFile::instance()->storageAttached()) { Q_D(KGlobalLedgerView); if (d->m_needLoad) d->init(); emit customActionRequested(View::Ledgers, eView::Action::AboutToShow); if (d->m_needsRefresh) { if (!d->m_inEditMode) { setUpdatesEnabled(false); d->loadView(); setUpdatesEnabled(true); d->m_needsRefresh = false; d->m_newAccountLoaded = false; } } else { if (!d->m_lastSelectedAccountID.isEmpty()) { try { const auto acc = MyMoneyFile::instance()->account(d->m_lastSelectedAccountID); slotSelectAccount(acc.id()); } catch (const MyMoneyException &) { d->m_lastSelectedAccountID.clear(); // account is invalid } } else { slotSelectAccount(d->m_accountComboBox->getSelected()); } KMyMoneyRegister::SelectedTransactions list(d->m_register); updateLedgerActions(list); emit selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions); } } pActions[Action::SelectAllTransactions]->setEnabled(true); // don't forget base class implementation QWidget::showEvent(event); } void KGlobalLedgerView::updateActions(const MyMoneyObject& obj) { Q_D(KGlobalLedgerView); // if (typeid(obj) != typeid(MyMoneyAccount) && // (obj.id().isEmpty() && d->m_currentAccount.id().isEmpty())) // do not disable actions that were already disabled)) // return; const auto& acc = static_cast(obj); const QVector actionsToBeDisabled { Action::StartReconciliation, Action::FinishReconciliation, Action::PostponeReconciliation, Action::OpenAccount, Action::NewTransaction }; for (const auto& a : actionsToBeDisabled) pActions[a]->setEnabled(false); auto b = acc.isClosed() ? false : true; pMenus[Menu::MoveTransaction]->setEnabled(b); QString tooltip; pActions[Action::NewTransaction]->setEnabled(canCreateTransactions(tooltip) || !isVisible()); pActions[Action::NewTransaction]->setToolTip(tooltip); const auto file = MyMoneyFile::instance(); if (!acc.id().isEmpty() && !file->isStandardAccount(acc.id())) { switch (acc.accountGroup()) { case eMyMoney::Account::Type::Asset: case eMyMoney::Account::Type::Liability: case eMyMoney::Account::Type::Equity: pActions[Action::OpenAccount]->setEnabled(true); if (acc.accountGroup() != eMyMoney::Account::Type::Equity) { if (d->m_reconciliationAccount.id().isEmpty()) { pActions[Action::StartReconciliation]->setEnabled(true); pActions[Action::StartReconciliation]->setToolTip(i18n("Reconcile")); } else { auto tip = i18n("Reconcile - disabled because you are currently reconciling %1", d->m_reconciliationAccount.name()); pActions[Action::StartReconciliation]->setToolTip(tip); if (!d->m_transactionEditor) { pActions[Action::FinishReconciliation]->setEnabled(acc.id() == d->m_reconciliationAccount.id()); pActions[Action::PostponeReconciliation]->setEnabled(acc.id() == d->m_reconciliationAccount.id()); } } } break; case eMyMoney::Account::Type::Income : case eMyMoney::Account::Type::Expense : pActions[Action::OpenAccount]->setEnabled(true); break; default: break; } } d->m_currentAccount = acc; // slotSelectAccount(acc); } void KGlobalLedgerView::updateLedgerActions(const KMyMoneyRegister::SelectedTransactions& list) { Q_D(KGlobalLedgerView); d->selectTransactions(list); updateLedgerActionsInternal(); } void KGlobalLedgerView::updateLedgerActionsInternal() { Q_D(KGlobalLedgerView); const QVector actionsToBeDisabled { Action::EditTransaction, Action::EditSplits, Action::EnterTransaction, Action::CancelTransaction, Action::DeleteTransaction, Action::MatchTransaction, Action::AcceptTransaction, Action::DuplicateTransaction, Action::ToggleReconciliationFlag, Action::MarkCleared, Action::GoToAccount, Action::GoToPayee, Action::AssignTransactionsNumber, Action::NewScheduledTransaction, Action::CombineTransactions, Action::CopySplits, }; for (const auto& a : actionsToBeDisabled) pActions[a]->setEnabled(false); const auto file = MyMoneyFile::instance(); pActions[Action::MatchTransaction]->setText(i18nc("Button text for match transaction", "Match")); // pActions[Action::TransactionNew]->setToolTip(i18n("Create a new transaction")); pMenus[Menu::MoveTransaction]->setEnabled(false); pMenus[Menu::MarkTransaction]->setEnabled(false); pMenus[Menu::MarkTransactionContext]->setEnabled(false); if (!d->m_selectedTransactions.isEmpty() && !d->m_selectedTransactions.first().isScheduled()) { // enable 'delete transaction' only if at least one of the // selected transactions does not reference a closed account bool enable = false; KMyMoneyRegister::SelectedTransactions::const_iterator it_t; for (it_t = d->m_selectedTransactions.constBegin(); (enable == false) && (it_t != d->m_selectedTransactions.constEnd()); ++it_t) { enable = !(*it_t).transaction().id().isEmpty() && !file->referencesClosedAccount((*it_t).transaction()); } pActions[Action::DeleteTransaction]->setEnabled(enable); if (!d->m_transactionEditor) { QString tooltip = i18n("Duplicate the current selected transactions"); pActions[Action::DuplicateTransaction]->setEnabled(canDuplicateTransactions(d->m_selectedTransactions, tooltip) && !d->m_selectedTransactions[0].transaction().id().isEmpty()); pActions[Action::DuplicateTransaction]->setToolTip(tooltip); if (canEditTransactions(d->m_selectedTransactions, tooltip)) { pActions[Action::EditTransaction]->setEnabled(true); // editing splits is allowed only if we have one transaction selected if (d->m_selectedTransactions.count() == 1) { pActions[Action::EditSplits]->setEnabled(true); } if (d->m_currentAccount.isAssetLiability() && d->m_currentAccount.accountType() != eMyMoney::Account::Type::Investment) { pActions[Action::NewScheduledTransaction]->setEnabled(d->m_selectedTransactions.count() == 1); } } pActions[Action::EditTransaction]->setToolTip(tooltip); if (!d->m_currentAccount.isClosed()) pMenus[Menu::MoveTransaction]->setEnabled(true); pMenus[Menu::MarkTransaction]->setEnabled(true); pMenus[Menu::MarkTransactionContext]->setEnabled(true); // Allow marking the transaction if at least one is selected pActions[Action::MarkCleared]->setEnabled(true); pActions[Action::MarkReconciled]->setEnabled(true); pActions[Action::MarkNotReconciled]->setEnabled(true); pActions[Action::ToggleReconciliationFlag]->setEnabled(true); if (!d->m_accountGoto.isEmpty()) pActions[Action::GoToAccount]->setEnabled(true); if (!d->m_payeeGoto.isEmpty()) pActions[Action::GoToPayee]->setEnabled(true); // Matching is enabled as soon as one regular and one imported transaction is selected int matchedCount = 0; int importedCount = 0; KMyMoneyRegister::SelectedTransactions::const_iterator it; for (it = d->m_selectedTransactions.constBegin(); it != d->m_selectedTransactions.constEnd(); ++it) { if ((*it).transaction().isImported()) ++importedCount; if ((*it).split().isMatched()) ++matchedCount; } if (d->m_selectedTransactions.count() == 2 /* && pActions[Action::TransactionEdit]->isEnabled() */) { pActions[Action::MatchTransaction]->setEnabled(true); } if (importedCount != 0 || matchedCount != 0) pActions[Action::AcceptTransaction]->setEnabled(true); if (matchedCount != 0) { pActions[Action::MatchTransaction]->setEnabled(true); pActions[Action::MatchTransaction]->setText(i18nc("Button text for unmatch transaction", "Unmatch")); pActions[Action::MatchTransaction]->setIcon(QIcon("process-stop")); } if (d->m_selectedTransactions.count() > 1) { pActions[Action::CombineTransactions]->setEnabled(true); } if (d->m_selectedTransactions.count() >= 2) { int singleSplitTransactions = 0; int multipleSplitTransactions = 0; foreach (const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) { switch (st.transaction().splitCount()) { case 0: break; case 1: singleSplitTransactions++; break; default: multipleSplitTransactions++; break; } } if (singleSplitTransactions > 0 && multipleSplitTransactions == 1) { pActions[Action::CopySplits]->setEnabled(true); } } if (d->m_selectedTransactions.count() >= 2) { int singleSplitTransactions = 0; int multipleSplitTransactions = 0; foreach(const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) { switch(st.transaction().splitCount()) { case 0: break; case 1: singleSplitTransactions++; break; default: multipleSplitTransactions++; break; } } if(singleSplitTransactions > 0 && multipleSplitTransactions == 1) { pActions[Action::CopySplits]->setEnabled(true); } } } else { pActions[Action::AssignTransactionsNumber]->setEnabled(d->m_transactionEditor->canAssignNumber()); pActions[Action::NewTransaction]->setEnabled(false); pActions[Action::DeleteTransaction]->setEnabled(false); QString reason; pActions[Action::EnterTransaction]->setEnabled(d->m_transactionEditor->isComplete(reason)); //FIXME: Port to KDE4 // the next line somehow worked in KDE3 but does not have // any influence under KDE4 /// Works for me when 'reason' is set. Allan pActions[Action::EnterTransaction]->setToolTip(reason); pActions[Action::CancelTransaction]->setEnabled(true); } } } void KGlobalLedgerView::slotAboutToSelectItem(KMyMoneyRegister::RegisterItem* item, bool& okToSelect) { Q_UNUSED(item); slotCancelOrEnterTransactions(okToSelect); } void KGlobalLedgerView::slotUpdateSummaryLine(const KMyMoneyRegister::SelectedTransactions& selection) { Q_D(KGlobalLedgerView); if (selection.count() > 1) { MyMoneyMoney balance; foreach (const KMyMoneyRegister::SelectedTransaction& t, selection) { if (!t.isScheduled()) { balance += t.split().shares(); } } d->m_rightSummaryLabel->setText(QString("%1: %2").arg(QChar(0x2211), balance.formatMoney("", d->m_precision))); } else { if (d->isReconciliationAccount()) { d->m_rightSummaryLabel->setText(i18n("Difference: %1", d->m_totalBalance.formatMoney("", d->m_precision))); } else { if (d->m_currentAccount.accountType() != eMyMoney::Account::Type::Investment) { d->m_rightSummaryLabel->setText(i18n("Balance: %1", d->m_totalBalance.formatMoney("", d->m_precision))); bool showNegative = d->m_totalBalance.isNegative(); if (d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Liability && !d->m_totalBalance.isZero()) showNegative = !showNegative; if (showNegative) { QPalette palette = d->m_rightSummaryLabel->palette(); palette.setColor(d->m_rightSummaryLabel->foregroundRole(), KMyMoneySettings::schemeColor(SchemeColor::Negative)); d->m_rightSummaryLabel->setPalette(palette); } } else { d->m_rightSummaryLabel->setText(i18n("Investment value: %1%2", d->m_balanceIsApproximated ? "~" : "", d->m_totalBalance.formatMoney(MyMoneyFile::instance()->baseCurrency().tradingSymbol(), d->m_precision))); } } } } void KGlobalLedgerView::resizeEvent(QResizeEvent* ev) { if (MyMoneyFile::instance()->storageAttached()) { Q_D(KGlobalLedgerView); if (d->m_needLoad) d->init(); d->m_register->resize((int)eWidgets::eTransaction::Column::Detail); d->m_form->resize((int)eWidgets::eTransactionForm::Column::Value1); } KMyMoneyViewBase::resizeEvent(ev); } void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance) { Q_D(KGlobalLedgerView); if(d->m_needLoad) d->init(); if (d->m_reconciliationAccount.id() != acc.id()) { // make sure the account is selected if (!acc.id().isEmpty()) slotSelectAccount(acc.id()); d->m_reconciliationAccount = acc; d->m_reconciliationDate = reconciliationDate; d->m_endingBalance = endingBalance; if (acc.accountGroup() == eMyMoney::Account::Type::Liability) d->m_endingBalance = -endingBalance; d->m_newAccountLoaded = true; if (acc.id().isEmpty()) { d->m_buttonbar->removeAction(pActions[Action::PostponeReconciliation]); d->m_buttonbar->removeAction(pActions[Action::FinishReconciliation]); } else { d->m_buttonbar->addAction(pActions[Action::PostponeReconciliation]); d->m_buttonbar->addAction(pActions[Action::FinishReconciliation]); // when we start reconciliation, we need to reload the view // because no data has been changed. When postponing or finishing // reconciliation, the data change in the engine takes care of updating // the view. refresh(); } } } void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc, const QDate& reconciliationDate) { slotSetReconcileAccount(acc, reconciliationDate, MyMoneyMoney()); } void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc) { slotSetReconcileAccount(acc, QDate(), MyMoneyMoney()); } void KGlobalLedgerView::slotSetReconcileAccount() { slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); } void KGlobalLedgerView::slotShowTransactionMenu(const MyMoneySplit& sp) { Q_UNUSED(sp) pMenus[Menu::Transaction]->exec(QCursor::pos()); } void KGlobalLedgerView::slotContinueReconciliation() { Q_D(KGlobalLedgerView); const auto file = MyMoneyFile::instance(); MyMoneyAccount account; try { account = file->account(d->m_currentAccount.id()); // get rid of previous run. delete d->m_endingBalanceDlg; d->m_endingBalanceDlg = new KEndingBalanceDlg(account, this); if (account.isAssetLiability()) { if (d->m_endingBalanceDlg->exec() == QDialog::Accepted) { if (KMyMoneySettings::autoReconciliation()) { MyMoneyMoney startBalance = d->m_endingBalanceDlg->previousBalance(); MyMoneyMoney endBalance = d->m_endingBalanceDlg->endingBalance(); QDate endDate = d->m_endingBalanceDlg->statementDate(); QList > transactionList; MyMoneyTransactionFilter filter(account.id()); filter.addState((int)eMyMoney::TransactionFilter::State::Cleared); filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled); filter.setDateFilter(QDate(), endDate); filter.setConsiderCategory(false); filter.setReportAllSplits(true); file->transactionList(transactionList, filter); QList > result = d->automaticReconciliation(account, transactionList, endBalance - startBalance); if (!result.empty()) { QString message = i18n("KMyMoney has detected transactions matching your reconciliation data.\nWould you like KMyMoney to clear these transactions for you?"); if (KMessageBox::questionYesNo(this, message, i18n("Automatic reconciliation"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "AcceptAutomaticReconciliation") == KMessageBox::Yes) { // mark the transactions cleared KMyMoneyRegister::SelectedTransactions oldSelection = d->m_selectedTransactions; d->m_selectedTransactions.clear(); QListIterator > itTransactionSplitResult(result); while (itTransactionSplitResult.hasNext()) { const QPair &transactionSplit = itTransactionSplitResult.next(); d->m_selectedTransactions.append(KMyMoneyRegister::SelectedTransaction(transactionSplit.first, transactionSplit.second, QString())); } // mark all transactions in d->m_selectedTransactions as 'Cleared' d->markTransaction(eMyMoney::Split::State::Cleared); d->m_selectedTransactions = oldSelection; } } } if (!file->isStandardAccount(account.id()) && account.isAssetLiability()) { if (!isVisible()) emit customActionRequested(View::Ledgers, eView::Action::SwitchView); Models::instance()->accountsModel()->slotReconcileAccount(account, d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance()); slotSetReconcileAccount(account, d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance()); // check if the user requests us to create interest // or charge transactions. auto ti = d->m_endingBalanceDlg->interestTransaction(); auto tc = d->m_endingBalanceDlg->chargeTransaction(); MyMoneyFileTransaction ft; try { if (ti != MyMoneyTransaction()) { MyMoneyFile::instance()->addTransaction(ti); } if (tc != MyMoneyTransaction()) { MyMoneyFile::instance()->addTransaction(tc); } ft.commit(); } catch (const MyMoneyException &e) { qWarning("interest transaction not stored: '%s'", e.what()); } // reload the account object as it might have changed in the meantime d->m_reconciliationAccount = file->account(account.id()); updateActions(d->m_currentAccount); updateLedgerActionsInternal(); // slotUpdateActions(); } } } } catch (const MyMoneyException &) { } } void KGlobalLedgerView::slotLedgerSelected(const QString& _accId, const QString& transaction) { auto acc = MyMoneyFile::instance()->account(_accId); QString accId(_accId); switch (acc.accountType()) { case Account::Type::Stock: // if a stock account is selected, we show the // the corresponding parent (investment) account acc = MyMoneyFile::instance()->account(acc.parentAccountId()); accId = acc.id(); // intentional fall through case Account::Type::Checkings: case Account::Type::Savings: case Account::Type::Cash: case Account::Type::CreditCard: case Account::Type::Loan: case Account::Type::Asset: case Account::Type::Liability: case Account::Type::AssetLoan: case Account::Type::Income: case Account::Type::Expense: case Account::Type::Investment: case Account::Type::Equity: if (!isVisible()) emit customActionRequested(View::Ledgers, eView::Action::SwitchView); slotSelectAccount(accId, transaction); break; case Account::Type::CertificateDep: case Account::Type::MoneyMarket: case Account::Type::Currency: qDebug("No ledger view available for account type %d", (int)acc.accountType()); break; default: qDebug("Unknown account type %d in KMyMoneyView::slotLedgerSelected", (int)acc.accountType()); break; } } void KGlobalLedgerView::slotSelectByObject(const MyMoneyObject& obj, eView::Intent intent) { switch(intent) { case eView::Intent::UpdateActions: updateActions(obj); break; case eView::Intent::FinishEnteringOverdueScheduledTransactions: slotContinueReconciliation(); break; case eView::Intent::SynchronizeAccountInLedgersView: slotSelectAccount(obj); break; default: break; } } void KGlobalLedgerView::slotSelectByVariant(const QVariantList& variant, eView::Intent intent) { switch(intent) { case eView::Intent::ShowTransaction: if (variant.count() == 2) slotLedgerSelected(variant.at(0).toString(), variant.at(1).toString()); break; case eView::Intent::SelectRegisterTransactions: if (variant.count() == 1) updateLedgerActions(variant.at(0).value()); break; default: break; } } void KGlobalLedgerView::slotSelectAccount(const MyMoneyObject& obj) { Q_D(KGlobalLedgerView); if (typeid(obj) != typeid(MyMoneyAccount)) return/* false */; d->m_lastSelectedAccountID = obj.id(); } void KGlobalLedgerView::slotSelectAccount(const QString& id) { slotSelectAccount(id, QString()); } bool KGlobalLedgerView::slotSelectAccount(const QString& id, const QString& transactionId) { Q_D(KGlobalLedgerView); auto rc = true; if (!id.isEmpty()) { if (d->m_currentAccount.id() != id) { try { d->m_currentAccount = MyMoneyFile::instance()->account(id); // if a stock account is selected, we show the // the corresponding parent (investment) account if (d->m_currentAccount.isInvest()) { d->m_currentAccount = MyMoneyFile::instance()->account(d->m_currentAccount.parentAccountId()); } d->m_lastSelectedAccountID = d->m_currentAccount.id(); d->m_newAccountLoaded = true; refresh(); } catch (const MyMoneyException &) { qDebug("Unable to retrieve account %s", qPrintable(id)); rc = false; } } else { // we need to refresh m_account.m_accountList, a child could have been deleted d->m_currentAccount = MyMoneyFile::instance()->account(id); emit selectByObject(d->m_currentAccount, eView::Intent::None); emit selectByObject(d->m_currentAccount, eView::Intent::SynchronizeAccountInInvestmentView); } d->selectTransaction(transactionId); } return rc; } bool KGlobalLedgerView::selectEmptyTransaction() { Q_D(KGlobalLedgerView); bool rc = false; if (!d->m_inEditMode) { // in case we don't know the type of transaction to be created, // have at least one selected transaction and the id of // this transaction is not empty, we take it as template for the // transaction to be created KMyMoneyRegister::SelectedTransactions list(d->m_register); if ((d->m_action == eWidgets::eRegister::Action::None) && (!list.isEmpty()) && (!list[0].transaction().id().isEmpty())) { // the new transaction to be created will have the same type // as the one that currently has the focus KMyMoneyRegister::Transaction* t = dynamic_cast(d->m_register->focusItem()); if (t) d->m_action = t->actionType(); d->m_register->clearSelection(); } // if we still don't have an idea which type of transaction // to create, we use the default. if (d->m_action == eWidgets::eRegister::Action::None) { d->setupDefaultAction(); } d->m_register->selectItem(d->m_register->lastItem()); d->m_register->updateRegister(); rc = true; } return rc; } TransactionEditor* KGlobalLedgerView::startEdit(const KMyMoneyRegister::SelectedTransactions& list) { Q_D(KGlobalLedgerView); // we use the warnlevel to keep track, if we have to warn the // user that some or all splits have been reconciled or if the // user cannot modify the transaction if at least one split // has the status frozen. The following value are used: // // 0 - no sweat, user can modify // 1 - user should be warned that at least one split has been reconciled // already // 2 - user will be informed, that this transaction cannot be changed anymore int warnLevel = list.warnLevel(); Q_ASSERT(warnLevel < 2); // otherwise the edit action should not be enabled switch (warnLevel) { case 0: break; case 1: if (KMessageBox::warningContinueCancel(this, i18n( "At least one split of the selected transactions has been reconciled. " "Do you wish to continue to edit the transactions anyway?" ), i18n("Transaction already reconciled"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "EditReconciledTransaction") == KMessageBox::Cancel) { warnLevel = 2; } break; case 2: KMessageBox::sorry(this, i18n("At least one split of the selected transactions has been frozen. " "Editing the transactions is therefore prohibited."), i18n("Transaction already frozen")); break; case 3: KMessageBox::sorry(this, i18n("At least one split of the selected transaction references an account that has been closed. " "Editing the transactions is therefore prohibited."), i18n("Account closed")); break; } if (warnLevel > 1) { d->m_register->endEdit(); return 0; } TransactionEditor* editor = 0; KMyMoneyRegister::Transaction* item = dynamic_cast(d->m_register->focusItem()); if (item) { // in case the current focus item is not selected, we move the focus to the first selected transaction if (!item->isSelected()) { KMyMoneyRegister::RegisterItem* p; for (p = d->m_register->firstItem(); p; p = p->nextItem()) { KMyMoneyRegister::Transaction* t = dynamic_cast(p); if (t && t->isSelected()) { d->m_register->setFocusItem(t); item = t; break; } } } // decide, if we edit in the register or in the form TransactionEditorContainer* parent; if (d->m_formFrame->isVisible()) parent = d->m_form; else { parent = d->m_register; } editor = item->createEditor(parent, list, KGlobalLedgerViewPrivate::m_lastPostDate); // check that we use the same transaction commodity in all selected transactions // if not, we need to update this in the editor's list. The user can also bail out // of this operation which means that we have to stop editing here. if (editor) { if (!editor->fixTransactionCommodity(d->m_currentAccount)) { // if the user wants to quit, we need to destroy the editor // and bail out delete editor; editor = 0; } } if (editor) { if (parent == d->m_register) { // make sure, the height of the table is correct d->m_register->updateRegister(KMyMoneySettings::ledgerLens() | !KMyMoneySettings::transactionForm()); } d->m_inEditMode = true; connect(editor, &TransactionEditor::transactionDataSufficient, pActions[Action::EnterTransaction], &QAction::setEnabled); connect(editor, &TransactionEditor::returnPressed, pActions[Action::EnterTransaction], &QAction::trigger); connect(editor, &TransactionEditor::escapePressed, pActions[Action::CancelTransaction], &QAction::trigger); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets); connect(editor, &TransactionEditor::finishEdit, this, &KGlobalLedgerView::slotLeaveEditMode); connect(editor, &TransactionEditor::objectCreation, d->m_mousePressFilter, &MousePressFilter::setFilterDeactive); connect(editor, &TransactionEditor::lastPostDateUsed, this, &KGlobalLedgerView::slotKeepPostDate); // create the widgets, place them in the parent and load them with data // setup tab order d->m_tabOrderWidgets.clear(); editor->setup(d->m_tabOrderWidgets, d->m_currentAccount, d->m_action); Q_ASSERT(!d->m_tabOrderWidgets.isEmpty()); // install event filter in all taborder widgets QWidgetList::const_iterator it_w = d->m_tabOrderWidgets.constBegin(); for (; it_w != d->m_tabOrderWidgets.constEnd(); ++it_w) { (*it_w)->installEventFilter(this); } // Install a filter that checks if a mouse press happened outside // of one of our own widgets. qApp->installEventFilter(d->m_mousePressFilter); // Check if the editor has some preference on where to set the focus // If not, set the focus to the first widget in the tab order QWidget* focusWidget = editor->firstWidget(); if (!focusWidget) focusWidget = d->m_tabOrderWidgets.first(); // for some reason, this only works reliably if delayed a bit QTimer::singleShot(10, focusWidget, SLOT(setFocus())); // preset to 'I have no idea which type to create' for the next round. d->m_action = eWidgets::eRegister::Action::None; } } return editor; } void KGlobalLedgerView::slotTransactionsContextMenuRequested() { Q_D(KGlobalLedgerView); auto transactions = d->m_selectedTransactions; updateLedgerActionsInternal(); // emit transactionsSelected(d->m_selectedTransactions); // that should select MyMoneySchedule in KScheduledView if (!transactions.isEmpty() && transactions.first().isScheduled()) emit selectByObject(MyMoneyFile::instance()->schedule(transactions.first().scheduleId()), eView::Intent::OpenContextMenu); else slotShowTransactionMenu(MyMoneySplit()); } void KGlobalLedgerView::slotLeaveEditMode(const KMyMoneyRegister::SelectedTransactions& list) { Q_D(KGlobalLedgerView); d->m_inEditMode = false; qApp->removeEventFilter(d->m_mousePressFilter); // a possible focusOut event may have removed the focus, so we // install it back again. d->m_register->focusItem()->setFocus(true); // if we come back from editing a new item, we make sure that // we always select the very last known transaction entry no // matter if the transaction has been created or not. if (list.count() && list[0].transaction().id().isEmpty()) { // block signals to prevent some infinite loops that might occur here. d->m_register->blockSignals(true); d->m_register->clearSelection(); KMyMoneyRegister::RegisterItem* p = d->m_register->lastItem(); if (p && p->prevItem()) p = p->prevItem(); d->m_register->selectItem(p); d->m_register->updateRegister(true); d->m_register->blockSignals(false); // we need to update the form manually as sending signals was blocked KMyMoneyRegister::Transaction* t = dynamic_cast(p); if (t) d->m_form->slotSetTransaction(t); } else { if (!KMyMoneySettings::transactionForm()) { // update the row height of the transactions because it might differ between viewing/editing mode when not using the transaction form d->m_register->blockSignals(true); d->m_register->updateRegister(true); d->m_register->blockSignals(false); } } d->m_needsRefresh = true; // TODO: Why transaction in view doesn't update without this? if (d->m_needsRefresh) refresh(); d->m_register->endEdit(); d->m_register->setFocus(); } bool KGlobalLedgerView::focusNextPrevChild(bool next) { Q_D(KGlobalLedgerView); bool rc = false; // qDebug() << "----------------------------------------------------------"; // qDebug() << "KGlobalLedgerView::focusNextPrevChild, editmode=" << d->m_inEditMode; if (d->m_inEditMode) { QWidget *w = 0; w = qApp->focusWidget(); int currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); const auto startIndex = currentWidgetIndex; // qDebug() << "Focus is at currentWidgetIndex" << currentWidgetIndex << w->objectName(); do { while (w && currentWidgetIndex == -1) { // qDebug() << w->objectName() << "not in list, use parent"; w = w->parentWidget(); currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); } // qDebug() << "Focus is at currentWidgetIndex" << currentWidgetIndex << w->objectName(); if (currentWidgetIndex != -1) { // if(w) qDebug() << "tab order is at" << w->objectName(); currentWidgetIndex += next ? 1 : -1; if (currentWidgetIndex < 0) currentWidgetIndex = d->m_tabOrderWidgets.size() - 1; else if (currentWidgetIndex >= d->m_tabOrderWidgets.size()) currentWidgetIndex = 0; w = d->m_tabOrderWidgets[currentWidgetIndex]; // qDebug() << "currentWidgetIndex" << currentWidgetIndex << w->objectName() << w->isVisible(); if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) { // qDebug() << "Set focus to" << w->objectName(); w->setFocus(next ? Qt::TabFocusReason: Qt::BacktabFocusReason); rc = true; break; } } else { break; } } while(currentWidgetIndex != startIndex); } else rc = KMyMoneyViewBase::focusNextPrevChild(next); return rc; } bool KGlobalLedgerView::eventFilter(QObject* o, QEvent* e) { Q_D(KGlobalLedgerView); bool rc = false; // Need to capture mouse position here as QEvent::ToolTip is too slow d->m_tooltipPosn = QCursor::pos(); if (e->type() == QEvent::KeyPress) { if (d->m_inEditMode) { // qDebug("object = %s, key = %d", o->className(), k->key()); if (o == d->m_register) { // we hide all key press events from the register // while editing a transaction rc = true; } } } if (!rc) rc = KMyMoneyViewBase::eventFilter(o, e); return rc; } void KGlobalLedgerView::slotSortOptions() { Q_D(KGlobalLedgerView); QPointer dlg = new KSortOptionDlg(this); QString key; QString sortOrder, def; if (d->isReconciliationAccount()) { key = "kmm-sort-reconcile"; def = KMyMoneySettings::sortReconcileView(); } else { key = "kmm-sort-std"; def = KMyMoneySettings::sortNormalView(); } // check if we have an account override of the sort order if (!d->m_currentAccount.value(key).isEmpty()) sortOrder = d->m_currentAccount.value(key); QString oldOrder = sortOrder; dlg->setSortOption(sortOrder, def); if (dlg->exec() == QDialog::Accepted) { if (dlg != 0) { sortOrder = dlg->sortOption(); if (sortOrder != oldOrder) { if (sortOrder.isEmpty()) { d->m_currentAccount.deletePair(key); } else { d->m_currentAccount.setValue(key, sortOrder); } MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifyAccount(d->m_currentAccount); ft.commit(); } catch (const MyMoneyException &e) { qDebug("Unable to update sort order for account '%s': %s", qPrintable(d->m_currentAccount.name()), e.what()); } } } } delete dlg; } void KGlobalLedgerView::slotToggleTransactionMark(KMyMoneyRegister::Transaction* /* t */) { Q_D(KGlobalLedgerView); if (!d->m_inEditMode) { slotToggleReconciliationFlag(); } } void KGlobalLedgerView::slotKeepPostDate(const QDate& date) { KGlobalLedgerViewPrivate::m_lastPostDate = date; } QString KGlobalLedgerView::accountId() const { Q_D(const KGlobalLedgerView); return d->m_currentAccount.id(); } bool KGlobalLedgerView::canCreateTransactions(QString& tooltip) const { Q_D(const KGlobalLedgerView); bool rc = true; if (d->m_currentAccount.id().isEmpty()) { tooltip = i18n("Cannot create transactions when no account is selected."); rc = false; } if (d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Income || d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Expense) { tooltip = i18n("Cannot create transactions in the context of a category."); d->showTooltip(tooltip); rc = false; } if (d->m_currentAccount.isClosed()) { tooltip = i18n("Cannot create transactions in a closed account."); d->showTooltip(tooltip); rc = false; } return rc; } bool KGlobalLedgerView::canModifyTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { Q_D(const KGlobalLedgerView); return d->canProcessTransactions(list, tooltip) && list.canModify(); } bool KGlobalLedgerView::canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { Q_D(const KGlobalLedgerView); return d->canProcessTransactions(list, tooltip) && list.canDuplicate(); } bool KGlobalLedgerView::canEditTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { Q_D(const KGlobalLedgerView); // check if we can edit the list of transactions. We can edit, if // // a) no mix of standard and investment transactions exist // b) if a split transaction is selected, this is the only selection // c) none of the splits is frozen // d) the transaction having the current focus is selected // check for d) if (!d->canProcessTransactions(list, tooltip)) return false; // check for c) if (list.warnLevel() == 2) { tooltip = i18n("Cannot edit transactions with frozen splits."); d->showTooltip(tooltip); return false; } bool rc = true; int investmentTransactions = 0; int normalTransactions = 0; if (d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Income || d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Expense) { tooltip = i18n("Cannot edit transactions in the context of a category."); d->showTooltip(tooltip); rc = false; } if (d->m_currentAccount.isClosed()) { tooltip = i18n("Cannot create or edit any transactions in Account %1 as it is closed", d->m_currentAccount.name()); d->showTooltip(tooltip); rc = false; } KMyMoneyRegister::SelectedTransactions::const_iterator it_t; QString action; for (it_t = list.begin(); rc && it_t != list.end(); ++it_t) { if ((*it_t).transaction().id().isEmpty()) { tooltip.clear(); rc = false; continue; } if (KMyMoneyUtils::transactionType((*it_t).transaction()) == KMyMoneyUtils::InvestmentTransaction) { if (action.isEmpty()) { action = (*it_t).split().action(); continue; } if (action == (*it_t).split().action()) { continue; } else { tooltip = (i18n("Cannot edit mixed investment action/type transactions together.")); d->showTooltip(tooltip); rc = false; break; } } if (KMyMoneyUtils::transactionType((*it_t).transaction()) == KMyMoneyUtils::InvestmentTransaction) ++investmentTransactions; else ++normalTransactions; // check for a) if (investmentTransactions != 0 && normalTransactions != 0) { tooltip = i18n("Cannot edit investment transactions and non-investment transactions together."); d->showTooltip(tooltip); rc = false; break; } // check for b) but only for normalTransactions if ((*it_t).transaction().splitCount() > 2 && normalTransactions != 0) { if (list.count() > 1) { tooltip = i18n("Cannot edit multiple split transactions at once."); d->showTooltip(tooltip); rc = false; break; } } } // check for multiple transactions being selected in an investment account // we do not allow editing in this case: https://bugs.kde.org/show_bug.cgi?id=240816 // later on, we might allow to edit investment transactions of the same type /// Can now disable the following check. /* if (rc == true && investmentTransactions > 1) { tooltip = i18n("Cannot edit multiple investment transactions at once"); rc = false; }*/ // now check that we have the correct account type for investment transactions if (rc == true && investmentTransactions != 0) { if (d->m_currentAccount.accountType() != eMyMoney::Account::Type::Investment) { tooltip = i18n("Cannot edit investment transactions in the context of this account."); rc = false; } } return rc; } void KGlobalLedgerView::slotMoveToAccount(const QString& id) { Q_D(KGlobalLedgerView); // close the menu, if it is still open if (pMenus[Menu::Transaction]->isVisible()) pMenus[Menu::Transaction]->close(); if (!d->m_selectedTransactions.isEmpty()) { const auto file = MyMoneyFile::instance(); MyMoneyFileTransaction ft; try { foreach (const auto selection, d->m_selectedTransactions) { if (d->m_currentAccount.accountType() == eMyMoney::Account::Type::Investment) { d->moveInvestmentTransaction(d->m_currentAccount.id(), id, selection.transaction()); } else { // we get the data afresh from the engine as // it might have changed by a previous iteration // in this loop. Use case: two splits point to // the same account and both are selected. auto tid = selection.transaction().id(); auto sid = selection.split().id(); auto t = file->transaction(tid); auto s = t.splitById(sid); s.setAccountId(id); t.modifySplit(s); file->modifyTransaction(t); } } ft.commit(); } catch (const MyMoneyException &) { } } } void KGlobalLedgerView::slotUpdateMoveToAccountMenu() { Q_D(KGlobalLedgerView); d->createTransactionMoveMenu(); // in case we were not able to create the selector, we // better get out of here. Anything else would cause // a crash later on (accountSet.load) if (!d->m_moveToAccountSelector) return; if (!d->m_currentAccount.id().isEmpty()) { AccountSet accountSet; if (d->m_currentAccount.accountType() == eMyMoney::Account::Type::Investment) { accountSet.addAccountType(eMyMoney::Account::Type::Investment); } else if (d->m_currentAccount.isAssetLiability()) { accountSet.addAccountType(eMyMoney::Account::Type::Checkings); accountSet.addAccountType(eMyMoney::Account::Type::Savings); accountSet.addAccountType(eMyMoney::Account::Type::Cash); accountSet.addAccountType(eMyMoney::Account::Type::AssetLoan); accountSet.addAccountType(eMyMoney::Account::Type::CertificateDep); accountSet.addAccountType(eMyMoney::Account::Type::MoneyMarket); accountSet.addAccountType(eMyMoney::Account::Type::Asset); accountSet.addAccountType(eMyMoney::Account::Type::Currency); accountSet.addAccountType(eMyMoney::Account::Type::CreditCard); accountSet.addAccountType(eMyMoney::Account::Type::Loan); accountSet.addAccountType(eMyMoney::Account::Type::Liability); } else if (d->m_currentAccount.isIncomeExpense()) { accountSet.addAccountType(eMyMoney::Account::Type::Income); accountSet.addAccountType(eMyMoney::Account::Type::Expense); } accountSet.load(d->m_moveToAccountSelector); // remove those accounts that we currently reference // with the selected items foreach (const auto selection, d->m_selectedTransactions) { d->m_moveToAccountSelector->removeItem(selection.split().accountId()); } // remove those accounts from the list that are denominated // in a different currency auto list = d->m_moveToAccountSelector->accountList(); QList::const_iterator it_a; for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) { auto acc = MyMoneyFile::instance()->account(*it_a); if (acc.currencyId() != d->m_currentAccount.currencyId()) d->m_moveToAccountSelector->removeItem((*it_a)); } } } void KGlobalLedgerView::slotObjectDestroyed(QObject* o) { Q_D(KGlobalLedgerView); if (o == d->m_moveToAccountSelector) { d->m_moveToAccountSelector = nullptr; } } void KGlobalLedgerView::slotCancelOrEnterTransactions(bool& okToSelect) { Q_D(KGlobalLedgerView); static bool oneTime = false; if (!oneTime) { oneTime = true; auto dontShowAgain = "CancelOrEditTransaction"; // qDebug("KMyMoneyApp::slotCancelOrEndEdit"); if (d->m_transactionEditor) { if (KMyMoneySettings::focusChangeIsEnter() && pActions[Action::EnterTransaction]->isEnabled()) { slotEnterTransaction(); if (d->m_transactionEditor) { // if at this stage the editor is still there that means that entering the transaction was cancelled // for example by pressing cancel on the exchange rate editor so we must stay in edit mode okToSelect = false; } } else { // okToSelect is preset to true if a cancel of the dialog is useful and false if it is not int rc; KGuiItem noGuiItem = KStandardGuiItem::save(); KGuiItem yesGuiItem = KStandardGuiItem::discard(); KGuiItem cancelGuiItem = KStandardGuiItem::cont(); // if the transaction can't be entered make sure that it can't be entered by pressing no either if (!pActions[Action::EnterTransaction]->isEnabled()) { noGuiItem.setEnabled(false); noGuiItem.setToolTip(pActions[Action::EnterTransaction]->toolTip()); } // in case we have a new transaction and cannot save it we simply cancel if (!pActions[Action::EnterTransaction]->isEnabled() && d->m_transactionEditor && d->m_transactionEditor->createNewTransaction()) { rc = KMessageBox::Yes; } else if (okToSelect == true) { rc = KMessageBox::warningYesNoCancel(this, i18n("

Please select what you want to do: discard the changes, save the changes or continue to edit the transaction.

You can also set an option to save the transaction automatically when e.g. selecting another transaction.

"), i18n("End transaction edit"), yesGuiItem, noGuiItem, cancelGuiItem, dontShowAgain); } else { rc = KMessageBox::warningYesNo(this, i18n("

Please select what you want to do: discard or save the changes.

You can also set an option to save the transaction automatically when e.g. selecting another transaction.

"), i18n("End transaction edit"), yesGuiItem, noGuiItem, dontShowAgain); } switch (rc) { case KMessageBox::Yes: slotCancelTransaction(); break; case KMessageBox::No: slotEnterTransaction(); // make sure that we'll see this message the next time no matter // if the user has chosen the 'Don't show again' checkbox KMessageBox::enableMessage(dontShowAgain); if (d->m_transactionEditor) { // if at this stage the editor is still there that means that entering the transaction was cancelled // for example by pressing cancel on the exchange rate editor so we must stay in edit mode okToSelect = false; } break; case KMessageBox::Cancel: // make sure that we'll see this message the next time no matter // if the user has chosen the 'Don't show again' checkbox KMessageBox::enableMessage(dontShowAgain); okToSelect = false; break; } } } oneTime = false; } } void KGlobalLedgerView::slotNewSchedule(const MyMoneyTransaction& _t, eMyMoney::Schedule::Occurrence occurrence) { KEditScheduleDlg::newSchedule(_t, occurrence); } void KGlobalLedgerView::slotNewTransactionForm(eWidgets::eRegister::Action id) { Q_D(KGlobalLedgerView); if (!d->m_inEditMode) { d->m_action = id; // since we jump here via code, we have to make sure to react only // if the action is enabled if (pActions[Action::NewTransaction]->isEnabled()) { if (d->createNewTransaction()) { d->m_transactionEditor = d->startEdit(d->m_selectedTransactions); if (d->m_transactionEditor) { KMyMoneyMVCCombo::setSubstringSearchForChildren(this/*d->m_myMoneyView*/, !KMyMoneySettings::stringMatchFromStart()); KMyMoneyPayeeCombo* payeeEdit = dynamic_cast(d->m_transactionEditor->haveWidget("payee")); if (payeeEdit && !d->m_lastPayeeEnteredId.isEmpty()) { // in case we entered a new transaction before and used a payee, // we reuse it here. Save the text to the edit widget, select it // so that hitting any character will start entering another payee. payeeEdit->setSelectedItem(d->m_lastPayeeEnteredId); payeeEdit->lineEdit()->selectAll(); } if (d->m_transactionEditor) { connect(d->m_transactionEditor.data(), &TransactionEditor::statusProgress, this, &KGlobalLedgerView::slotStatusProgress); connect(d->m_transactionEditor.data(), &TransactionEditor::statusMsg, this, &KGlobalLedgerView::slotStatusMsg); connect(d->m_transactionEditor.data(), &TransactionEditor::scheduleTransaction, this, &KGlobalLedgerView::slotNewSchedule); } updateLedgerActionsInternal(); // emit transactionsSelected(d->m_selectedTransactions); } } } } } void KGlobalLedgerView::slotNewTransaction() { // in case the view is not visible ... if (!isVisible()) { // we switch to it pActions[Action::ShowLedgersView]->activate(QAction::ActionEvent::Trigger); QString tooltip; if (!canCreateTransactions(tooltip)) { // and inform the user via a dialog about the reason // why a transaction cannot be created KMessageBox::sorry(this, tooltip); return; } } slotNewTransactionForm(eWidgets::eRegister::Action::None); } void KGlobalLedgerView::slotEditTransaction() { Q_D(KGlobalLedgerView); // qDebug("KMyMoneyApp::slotTransactionsEdit()"); // since we jump here via code, we have to make sure to react only // if the action is enabled if (pActions[Action::EditTransaction]->isEnabled()) { // as soon as we edit a transaction, we don't remember the last payee entered d->m_lastPayeeEnteredId.clear(); d->m_transactionEditor = d->startEdit(d->m_selectedTransactions); KMyMoneyMVCCombo::setSubstringSearchForChildren(this/*d->m_myMoneyView*/, !KMyMoneySettings::stringMatchFromStart()); updateLedgerActionsInternal(); } } void KGlobalLedgerView::slotDeleteTransaction() { Q_D(KGlobalLedgerView); // since we may jump here via code, we have to make sure to react only // if the action is enabled if (!pActions[Action::DeleteTransaction]->isEnabled()) return; if (d->m_selectedTransactions.isEmpty()) return; if (d->m_selectedTransactions.warnLevel() == 1) { if (KMessageBox::warningContinueCancel(this, i18n("At least one split of the selected transactions has been reconciled. " "Do you wish to delete the transactions anyway?"), i18n("Transaction already reconciled")) == KMessageBox::Cancel) return; } auto msg = i18np("Do you really want to delete the selected transaction?", "Do you really want to delete all %1 selected transactions?", d->m_selectedTransactions.count()); if (KMessageBox::questionYesNo(this, msg, i18n("Delete transaction")) == KMessageBox::Yes) { //KMSTATUS(i18n("Deleting transactions")); d->doDeleteTransactions(); } } void KGlobalLedgerView::slotDuplicateTransaction() { Q_D(KGlobalLedgerView); // since we may jump here via code, we have to make sure to react only // if the action is enabled if (pActions[Action::DuplicateTransaction]->isEnabled()) { KMyMoneyRegister::SelectedTransactions selectionList = d->m_selectedTransactions; KMyMoneyRegister::SelectedTransactions::iterator it_t; int i = 0; int cnt = d->m_selectedTransactions.count(); // KMSTATUS(i18n("Duplicating transactions")); emit selectByVariant(QVariantList {QVariant(0), QVariant(cnt)}, eView::Intent::ReportProgress); MyMoneyFileTransaction ft; MyMoneyTransaction lt; try { foreach (const auto selection, selectionList) { auto t = selection.transaction(); // wipe out any reconciliation information for (auto& split : t.splits()) { split.setReconcileFlag(eMyMoney::Split::State::NotReconciled); split.setReconcileDate(QDate()); split.setBankID(QString()); } // clear invalid data t.setEntryDate(QDate()); t.clearId(); // and set the post date to today t.setPostDate(QDate::currentDate()); MyMoneyFile::instance()->addTransaction(t); lt = t; emit selectByVariant(QVariantList {QVariant(i++), QVariant(0)}, eView::Intent::ReportProgress); } ft.commit(); // select the new transaction in the ledger if (!d->m_currentAccount.id().isEmpty()) slotLedgerSelected(d->m_currentAccount.id(), lt.id()); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to duplicate transaction(s)"), QString::fromLatin1(e.what())); } // switch off the progress bar emit selectByVariant(QVariantList {QVariant(-1), QVariant(-1)}, eView::Intent::ReportProgress); } } void KGlobalLedgerView::slotEnterTransaction() { Q_D(KGlobalLedgerView); // since we jump here via code, we have to make sure to react only // if the action is enabled if (pActions[Action::EnterTransaction]->isEnabled()) { // disable the action while we process it to make sure it's processed only once since // d->m_transactionEditor->enterTransactions(newId) will run QCoreApplication::processEvents // we could end up here twice which will cause a crash slotUpdateActions() will enable the action again pActions[Action::EnterTransaction]->setEnabled(false); if (d->m_transactionEditor) { QString accountId = d->m_currentAccount.id(); QString newId; connect(d->m_transactionEditor.data(), &TransactionEditor::balanceWarning, d->m_balanceWarning.data(), &KBalanceWarning::slotShowMessage); if (d->m_transactionEditor->enterTransactions(newId)) { KMyMoneyPayeeCombo* payeeEdit = dynamic_cast(d->m_transactionEditor->haveWidget("payee")); if (payeeEdit && !newId.isEmpty()) { d->m_lastPayeeEnteredId = payeeEdit->selectedItem(); } d->deleteTransactionEditor(); } if (!newId.isEmpty()) { slotLedgerSelected(accountId, newId); } } updateLedgerActionsInternal(); } } void KGlobalLedgerView::slotAcceptTransaction() { Q_D(KGlobalLedgerView); KMyMoneyRegister::SelectedTransactions list = d->m_selectedTransactions; KMyMoneyRegister::SelectedTransactions::const_iterator it_t; int cnt = list.count(); int i = 0; emit selectByVariant(QVariantList {QVariant(0), QVariant(cnt)}, eView::Intent::ReportProgress); MyMoneyFileTransaction ft; try { for (it_t = list.constBegin(); it_t != list.constEnd(); ++it_t) { // reload transaction in case it got changed during the course of this loop MyMoneyTransaction t = MyMoneyFile::instance()->transaction((*it_t).transaction().id()); if (t.isImported()) { t.setImported(false); if (!d->m_currentAccount.id().isEmpty()) { foreach (const auto split, t.splits()) { if (split.accountId() == d->m_currentAccount.id()) { if (split.reconcileFlag() == eMyMoney::Split::State::NotReconciled) { MyMoneySplit s = split; s.setReconcileFlag(eMyMoney::Split::State::Cleared); t.modifySplit(s); } } } } MyMoneyFile::instance()->modifyTransaction(t); } if ((*it_t).split().isMatched()) { // reload split in case it got changed during the course of this loop MyMoneySplit s = t.splitById((*it_t).split().id()); TransactionMatcher matcher(d->m_currentAccount); matcher.accept(t, s); } emit selectByVariant(QVariantList {QVariant(i++), QVariant(0)}, eView::Intent::ReportProgress); } emit selectByVariant(QVariantList {QVariant(-1), QVariant(-1)}, eView::Intent::ReportProgress); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to accept transaction"), QString::fromLatin1(e.what())); } } void KGlobalLedgerView::slotCancelTransaction() { Q_D(KGlobalLedgerView); // since we jump here via code, we have to make sure to react only // if the action is enabled if (pActions[Action::CancelTransaction]->isEnabled()) { // make sure, we block the enter function pActions[Action::EnterTransaction]->setEnabled(false); // qDebug("KMyMoneyApp::slotTransactionsCancel"); d->deleteTransactionEditor(); updateLedgerActions(d->m_selectedTransactions); emit selectByVariant(QVariantList {QVariant::fromValue(d->m_selectedTransactions)}, eView::Intent::SelectRegisterTransactions); } } void KGlobalLedgerView::slotEditSplits() { Q_D(KGlobalLedgerView); // since we jump here via code, we have to make sure to react only // if the action is enabled if (pActions[Action::EditSplits]->isEnabled()) { // as soon as we edit a transaction, we don't remember the last payee entered d->m_lastPayeeEnteredId.clear(); d->m_transactionEditor = d->startEdit(d->m_selectedTransactions); updateLedgerActions(d->m_selectedTransactions); emit selectByVariant(QVariantList {QVariant::fromValue(d->m_selectedTransactions)}, eView::Intent::SelectRegisterTransactions); if (d->m_transactionEditor) { KMyMoneyMVCCombo::setSubstringSearchForChildren(this/*d->m_myMoneyView*/, !KMyMoneySettings::stringMatchFromStart()); if (d->m_transactionEditor->slotEditSplits() == QDialog::Accepted) { MyMoneyFileTransaction ft; try { QString id; connect(d->m_transactionEditor.data(), &TransactionEditor::balanceWarning, d->m_balanceWarning.data(), &KBalanceWarning::slotShowMessage); d->m_transactionEditor->enterTransactions(id); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to modify transaction"), QString::fromLatin1(e.what())); } } } d->deleteTransactionEditor(); updateLedgerActions(d->m_selectedTransactions); emit selectByVariant(QVariantList {QVariant::fromValue(d->m_selectedTransactions)}, eView::Intent::SelectRegisterTransactions); } } void KGlobalLedgerView::slotCopyTransactionToClipboard() { Q_D(KGlobalLedgerView); // suppress copy transactions if view not visible // or in edit mode if (!isVisible() || d->m_inEditMode) return; // format transactions into text QString txt; const auto file = MyMoneyFile::instance(); const auto acc = file->account(d->m_lastSelectedAccountID); const auto currency = file->currency(acc.currencyId()); foreach (const auto& st, d->m_selectedTransactions) { if (!txt.isEmpty() || (d->m_selectedTransactions.count() > 1)) { txt += QStringLiteral("----------------------------\n"); } try { const auto& s = st.split(); // Date - txt += i18n("Date: %1").arg(st.transaction().postDate().toString(Qt::DefaultLocaleShortDate)); + txt += i18n("Date: %1", st.transaction().postDate().toString(Qt::DefaultLocaleShortDate)); txt += QStringLiteral("\n"); // Payee QString payee = i18nc("Name for unknown payee", "Unknown"); if (!s.payeeId().isEmpty()) { payee = file->payee(s.payeeId()).name(); } - txt += i18n("Payee: %1").arg(payee); + txt += i18n("Payee: %1", payee); txt += QStringLiteral("\n"); // Amount - txt += i18n("Amount: %1").arg(s.value().formatMoney(currency.tradingSymbol(), MyMoneyMoney::denomToPrec(acc.fraction(currency)))); + txt += i18n("Amount: %1", s.value().formatMoney(currency.tradingSymbol(), MyMoneyMoney::denomToPrec(acc.fraction(currency)))); txt += QStringLiteral("\n"); // Memo - txt += i18n("Memo: %1").arg(s.memo()); + txt += i18n("Memo: %1", s.memo()); txt += QStringLiteral("\n"); } catch (MyMoneyException &) { qDebug() << "Cannot copy transaction" << st.transaction().id() << "to clipboard"; } } if (d->m_selectedTransactions.count() > 1) { txt += QStringLiteral("----------------------------\n"); } if (!txt.isEmpty()) { QClipboard *clipboard = QGuiApplication::clipboard(); clipboard->setText(txt); } } void KGlobalLedgerView::slotCopySplits() { Q_D(KGlobalLedgerView); const auto file = MyMoneyFile::instance(); if (d->m_selectedTransactions.count() >= 2) { int singleSplitTransactions = 0; int multipleSplitTransactions = 0; KMyMoneyRegister::SelectedTransaction selectedSourceTransaction; foreach (const auto& st, d->m_selectedTransactions) { switch (st.transaction().splitCount()) { case 0: break; case 1: singleSplitTransactions++; break; default: selectedSourceTransaction = st; multipleSplitTransactions++; break; } } if (singleSplitTransactions > 0 && multipleSplitTransactions == 1) { MyMoneyFileTransaction ft; try { const auto& sourceTransaction = selectedSourceTransaction.transaction(); const auto& sourceSplit = selectedSourceTransaction.split(); foreach (const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) { auto t = st.transaction(); // don't process the source transaction if (sourceTransaction.id() == t.id()) { continue; } const auto& baseSplit = st.split(); if (t.splitCount() == 1) { foreach (const auto& split, sourceTransaction.splits()) { // Don't copy the source split, as we already have that // as part of the destination transaction if (split.id() == sourceSplit.id()) { continue; } MyMoneySplit sp(split); // clear the ID and reconciliation state sp.clearId(); sp.setReconcileFlag(eMyMoney::Split::State::NotReconciled); sp.setReconcileDate(QDate()); // in case it is a simple transaction consisting of two splits, // we can adjust the share and value part of the second split we // just created. We need to keep a possible price in mind in case // of different currencies if (sourceTransaction.splitCount() == 2) { sp.setValue(-baseSplit.value()); sp.setShares(-(baseSplit.shares() * baseSplit.price())); } t.addSplit(sp); } // check if we need to add/update a VAT assignment file->updateVAT(t); // and store the modified transaction file->modifyTransaction(t); } } ft.commit(); } catch (const MyMoneyException &) { qDebug() << "transactionCopySplits() failed"; } } } } void KGlobalLedgerView::slotGoToPayee() { Q_D(KGlobalLedgerView); if (!d->m_payeeGoto.isEmpty()) { try { QString transactionId; if (d->m_selectedTransactions.count() == 1) { transactionId = d->m_selectedTransactions[0].transaction().id(); } // make sure to pass copies, as d->myMoneyView->slotPayeeSelected() overrides // d->m_payeeGoto and d->m_currentAccount while calling slotUpdateActions() QString payeeId = d->m_payeeGoto; QString accountId = d->m_currentAccount.id(); emit selectByVariant(QVariantList {QVariant(payeeId), QVariant(accountId), QVariant(transactionId)}, eView::Intent::ShowPayee); // emit openPayeeRequested(payeeId, accountId, transactionId); } catch (const MyMoneyException &) { } } } void KGlobalLedgerView::slotGoToAccount() { Q_D(KGlobalLedgerView); if (!d->m_accountGoto.isEmpty()) { try { QString transactionId; if (d->m_selectedTransactions.count() == 1) { transactionId = d->m_selectedTransactions[0].transaction().id(); } // make sure to pass a copy, as d->myMoneyView->slotLedgerSelected() overrides // d->m_accountGoto while calling slotUpdateActions() slotLedgerSelected(d->m_accountGoto, transactionId); } catch (const MyMoneyException &) { } } } void KGlobalLedgerView::slotMatchTransactions() { Q_D(KGlobalLedgerView); // if the menu action is retrieved it can contain an '&' character for the accelerator causing the comparison to fail if not removed QString transactionActionText = pActions[Action::MatchTransaction]->text(); transactionActionText.remove('&'); if (transactionActionText == i18nc("Button text for match transaction", "Match")) d->transactionMatch(); else d->transactionUnmatch(); } void KGlobalLedgerView::slotCombineTransactions() { qDebug("slotTransactionCombine() not implemented yet"); } void KGlobalLedgerView::slotToggleReconciliationFlag() { Q_D(KGlobalLedgerView); d->markTransaction(eMyMoney::Split::State::Unknown); } void KGlobalLedgerView::slotMarkCleared() { Q_D(KGlobalLedgerView); d->markTransaction(eMyMoney::Split::State::Cleared); } void KGlobalLedgerView::slotMarkReconciled() { Q_D(KGlobalLedgerView); d->markTransaction(eMyMoney::Split::State::Reconciled); } void KGlobalLedgerView::slotMarkNotReconciled() { Q_D(KGlobalLedgerView); d->markTransaction(eMyMoney::Split::State::NotReconciled); } void KGlobalLedgerView::slotSelectAllTransactions() { Q_D(KGlobalLedgerView); if(d->m_needLoad) d->init(); d->m_register->clearSelection(); KMyMoneyRegister::RegisterItem* p = d->m_register->firstItem(); while (p) { KMyMoneyRegister::Transaction* t = dynamic_cast(p); if (t) { if (t->isVisible() && t->isSelectable() && !t->isScheduled() && !t->id().isEmpty()) { t->setSelected(true); } } p = p->nextItem(); } // this is here only to re-paint the items without selecting anything because the data (including the selection) is not really held in the model right now d->m_register->selectAll(); // inform everyone else about the selected items KMyMoneyRegister::SelectedTransactions list(d->m_register); updateLedgerActions(list); emit selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions); } void KGlobalLedgerView::slotCreateScheduledTransaction() { Q_D(KGlobalLedgerView); if (d->m_selectedTransactions.count() == 1) { // make sure to have the current selected split as first split in the schedule MyMoneyTransaction t = d->m_selectedTransactions[0].transaction(); MyMoneySplit s = d->m_selectedTransactions[0].split(); QString splitId = s.id(); s.clearId(); s.setReconcileFlag(eMyMoney::Split::State::NotReconciled); s.setReconcileDate(QDate()); t.removeSplits(); t.addSplit(s); foreach (const auto split, d->m_selectedTransactions[0].transaction().splits()) { if (split.id() != splitId) { MyMoneySplit s0 = split; s0.clearId(); s0.setReconcileFlag(eMyMoney::Split::State::NotReconciled); s0.setReconcileDate(QDate()); t.addSplit(s0); } } KEditScheduleDlg::newSchedule(t, eMyMoney::Schedule::Occurrence::Monthly); } } void KGlobalLedgerView::slotAssignNumber() { Q_D(KGlobalLedgerView); if (d->m_transactionEditor) d->m_transactionEditor->assignNextNumber(); } void KGlobalLedgerView::slotStartReconciliation() { Q_D(KGlobalLedgerView); // we cannot reconcile standard accounts if (!MyMoneyFile::instance()->isStandardAccount(d->m_currentAccount.id())) emit selectByObject(d->m_currentAccount, eView::Intent::StartEnteringOverdueScheduledTransactions); // asynchronous call to KScheduledView::slotEnterOverdueSchedules is made here // after that all activity should be continued in KGlobalLedgerView::slotContinueReconciliation() } void KGlobalLedgerView::slotFinishReconciliation() { Q_D(KGlobalLedgerView); const auto file = MyMoneyFile::instance(); if (!d->m_reconciliationAccount.id().isEmpty()) { // retrieve list of all transactions that are not reconciled or cleared QList > transactionList; MyMoneyTransactionFilter filter(d->m_reconciliationAccount.id()); filter.addState((int)eMyMoney::TransactionFilter::State::Cleared); filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled); filter.setDateFilter(QDate(), d->m_endingBalanceDlg->statementDate()); filter.setConsiderCategory(false); filter.setReportAllSplits(true); file->transactionList(transactionList, filter); auto balance = MyMoneyFile::instance()->balance(d->m_reconciliationAccount.id(), d->m_endingBalanceDlg->statementDate()); MyMoneyMoney actBalance, clearedBalance; actBalance = clearedBalance = balance; // walk the list of transactions to figure out the balance(s) for (auto it = transactionList.constBegin(); it != transactionList.constEnd(); ++it) { if ((*it).second.reconcileFlag() == eMyMoney::Split::State::NotReconciled) { clearedBalance -= (*it).second.shares(); } } if (d->m_endingBalanceDlg->endingBalance() != clearedBalance) { auto message = i18n("You are about to finish the reconciliation of this account with a difference between your bank statement and the transactions marked as cleared.\n" "Are you sure you want to finish the reconciliation?"); if (KMessageBox::questionYesNo(this, message, i18n("Confirm end of reconciliation"), KStandardGuiItem::yes(), KStandardGuiItem::no()) == KMessageBox::No) return; } MyMoneyFileTransaction ft; // refresh object d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id()); // Turn off reconciliation mode // Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); // slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); // d->m_myMoneyView->finishReconciliation(d->m_reconciliationAccount); // only update the last statement balance here, if we haven't a newer one due // to download of online statements. if (d->m_reconciliationAccount.value("lastImportedTransactionDate").isEmpty() || QDate::fromString(d->m_reconciliationAccount.value("lastImportedTransactionDate"), Qt::ISODate) < d->m_endingBalanceDlg->statementDate()) { d->m_reconciliationAccount.setValue("lastStatementBalance", d->m_endingBalanceDlg->endingBalance().toString()); // in case we override the last statement balance here, we have to make sure // that we don't show the online balance anymore, as it might be different d->m_reconciliationAccount.deletePair("lastImportedTransactionDate"); } d->m_reconciliationAccount.setLastReconciliationDate(d->m_endingBalanceDlg->statementDate()); // keep a record of this reconciliation d->m_reconciliationAccount.addReconciliation(d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance()); d->m_reconciliationAccount.deletePair("lastReconciledBalance"); d->m_reconciliationAccount.deletePair("statementBalance"); d->m_reconciliationAccount.deletePair("statementDate"); try { // update the account data file->modifyAccount(d->m_reconciliationAccount); /* // collect the list of cleared splits for this account filter.clear(); filter.addAccount(d->m_reconciliationAccount.id()); filter.addState(eMyMoney::TransactionFilter::Cleared); filter.setConsiderCategory(false); filter.setReportAllSplits(true); file->transactionList(transactionList, filter); */ // walk the list of transactions/splits and mark the cleared ones as reconciled for (auto it = transactionList.begin(); it != transactionList.end(); ++it) { MyMoneySplit sp = (*it).second; // skip the ones that are not marked cleared if (sp.reconcileFlag() != eMyMoney::Split::State::Cleared) continue; // always retrieve a fresh copy of the transaction because we // might have changed it already with another split MyMoneyTransaction t = file->transaction((*it).first.id()); sp.setReconcileFlag(eMyMoney::Split::State::Reconciled); sp.setReconcileDate(d->m_endingBalanceDlg->statementDate()); t.modifySplit(sp); // update the engine ... file->modifyTransaction(t); // ... and the list (*it) = qMakePair(t, sp); } ft.commit(); // reload account data from engine as the data might have changed in the meantime d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id()); /** * This signal is emitted when an account has been successfully reconciled * and all transactions are updated in the engine. It can be used by plugins * to create reconciliation reports. * * @param account the account data * @param date the reconciliation date as provided through the dialog * @param startingBalance the starting balance as provided through the dialog * @param endingBalance the ending balance as provided through the dialog * @param transactionList reference to QList of QPair containing all * transaction/split pairs processed by the reconciliation. */ emit selectByVariant(QVariantList { QVariant::fromValue(d->m_reconciliationAccount), QVariant::fromValue(d->m_endingBalanceDlg->statementDate()), QVariant::fromValue(d->m_endingBalanceDlg->previousBalance()), QVariant::fromValue(d->m_endingBalanceDlg->endingBalance()), QVariant::fromValue(transactionList) }, eView::Intent::AccountReconciled); } catch (const MyMoneyException &) { qDebug("Unexpected exception when setting cleared to reconcile"); } // Turn off reconciliation mode Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); } // Turn off reconciliation mode d->m_reconciliationAccount = MyMoneyAccount(); updateActions(d->m_currentAccount); updateLedgerActionsInternal(); d->loadView(); // slotUpdateActions(); } void KGlobalLedgerView::slotPostponeReconciliation() { Q_D(KGlobalLedgerView); MyMoneyFileTransaction ft; const auto file = MyMoneyFile::instance(); if (!d->m_reconciliationAccount.id().isEmpty()) { // refresh object d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id()); // Turn off reconciliation mode // Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); // slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); // d->m_myMoneyView->finishReconciliation(d->m_reconciliationAccount); d->m_reconciliationAccount.setValue("lastReconciledBalance", d->m_endingBalanceDlg->previousBalance().toString()); d->m_reconciliationAccount.setValue("statementBalance", d->m_endingBalanceDlg->endingBalance().toString()); d->m_reconciliationAccount.setValue("statementDate", d->m_endingBalanceDlg->statementDate().toString(Qt::ISODate)); try { file->modifyAccount(d->m_reconciliationAccount); ft.commit(); d->m_reconciliationAccount = MyMoneyAccount(); updateActions(d->m_currentAccount); updateLedgerActionsInternal(); // slotUpdateActions(); } catch (const MyMoneyException &) { qDebug("Unexpected exception when setting last reconcile info into account"); ft.rollback(); d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id()); } // Turn off reconciliation mode Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); d->loadView(); } } void KGlobalLedgerView::slotOpenAccount() { Q_D(KGlobalLedgerView); if (!MyMoneyFile::instance()->isStandardAccount(d->m_currentAccount.id())) slotLedgerSelected(d->m_currentAccount.id(), QString()); } void KGlobalLedgerView::slotFindTransaction() { Q_D(KGlobalLedgerView); if (!d->m_searchDlg) { d->m_searchDlg = new KFindTransactionDlg(this); connect(d->m_searchDlg, &QObject::destroyed, this, &KGlobalLedgerView::slotCloseSearchDialog); connect(d->m_searchDlg, &KFindTransactionDlg::transactionSelected, this, &KGlobalLedgerView::slotLedgerSelected); } d->m_searchDlg->show(); d->m_searchDlg->raise(); d->m_searchDlg->activateWindow(); } void KGlobalLedgerView::slotCloseSearchDialog() { Q_D(KGlobalLedgerView); if (d->m_searchDlg) d->m_searchDlg->deleteLater(); d->m_searchDlg = nullptr; } void KGlobalLedgerView::slotStatusMsg(const QString& txt) { emit selectByVariant(QVariantList {QVariant(txt)}, eView::Intent::ReportProgressMessage); } void KGlobalLedgerView::slotStatusProgress(int cnt, int base) { emit selectByVariant(QVariantList {QVariant(cnt), QVariant(base)}, eView::Intent::ReportProgress); } void KGlobalLedgerView::slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& list) { updateLedgerActions(list); emit selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions); }