diff --git a/kmymoney/kmymoney.cpp b/kmymoney/kmymoney.cpp
index 1c50db0b4..cd6efe583 100644
--- a/kmymoney/kmymoney.cpp
+++ b/kmymoney/kmymoney.cpp
@@ -1,3358 +1,3358 @@
/***************************************************************************
kmymoney.cpp
-------------------
copyright : (C) 2000 by Michael Edwardes
(C) 2007 by 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
#include "kmymoney.h"
// ----------------------------------------------------------------------------
// Std C++ / STL Includes
#include
#include
#include
// ----------------------------------------------------------------------------
// QT Includes
#include
#include // only for performance tests
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// ----------------------------------------------------------------------------
// KDE Includes
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef KF5Holidays_FOUND
#include
#include
#endif
// ----------------------------------------------------------------------------
// Project Includes
#include "kmymoneyglobalsettings.h"
#include "kmymoneyadaptor.h"
#include "dialogs/settings/ksettingskmymoney.h"
#include "dialogs/kbackupdlg.h"
#include "dialogs/kenterscheduledlg.h"
#include "dialogs/kconfirmmanualenterdlg.h"
#include "dialogs/kmymoneypricedlg.h"
#include "dialogs/kcurrencyeditdlg.h"
#include "dialogs/kequitypriceupdatedlg.h"
#include "dialogs/kmymoneyfileinfodlg.h"
#include "dialogs/kfindtransactiondlg.h"
#include "dialogs/knewbankdlg.h"
#include "wizards/newinvestmentwizard/knewinvestmentwizard.h"
#include "dialogs/knewaccountdlg.h"
#include "dialogs/editpersonaldatadlg.h"
#include "dialogs/kselectdatabasedlg.h"
#include "dialogs/kcurrencycalculator.h"
#include "dialogs/keditscheduledlg.h"
#include "wizards/newloanwizard/keditloanwizard.h"
#include "dialogs/kpayeereassigndlg.h"
#include "dialogs/kcategoryreassigndlg.h"
#include "wizards/endingbalancedlg/kendingbalancedlg.h"
#include "dialogs/kbalancechartdlg.h"
#include "dialogs/kgeneratesqldlg.h"
#include "dialogs/kloadtemplatedlg.h"
#include "dialogs/kgpgkeyselectiondlg.h"
#include "dialogs/ktemplateexportdlg.h"
#include "dialogs/transactionmatcher.h"
#include "wizards/newuserwizard/knewuserwizard.h"
#include "wizards/newaccountwizard/knewaccountwizard.h"
#include "dialogs/kbalancewarning.h"
#include "widgets/kmymoneyaccountselector.h"
#include "widgets/kmymoneypayeecombo.h"
#include "widgets/onlinejobmessagesview.h"
#include "widgets/kmymoneymvccombo.h"
#include "views/kmymoneyview.h"
#include "views/konlinejoboutbox.h"
#include "models/onlinejobmessagesmodel.h"
#include "mymoney/mymoneyobject.h"
#include "mymoney/mymoneyfile.h"
#include "mymoney/mymoneyinstitution.h"
#include "mymoney/mymoneyaccount.h"
#include "mymoney/mymoneyaccountloan.h"
#include "mymoney/mymoneysecurity.h"
#include "mymoney/mymoneypayee.h"
#include "mymoney/mymoneytag.h"
#include "mymoney/mymoneybudget.h"
#include "mymoney/mymoneyreport.h"
#include "mymoney/mymoneysplit.h"
#include "mymoney/mymoneyutils.h"
#include "mymoney/mymoneystatement.h"
#include "mymoney/storage/mymoneystoragedump.h"
#include "mymoney/storage/imymoneystorage.h"
#include "mymoney/mymoneyforecast.h"
#include "mymoney/mymoneytransactionfilter.h"
#include "mymoney/onlinejobmessage.h"
#include "converter/mymoneystatementreader.h"
#include "converter/mymoneytemplate.h"
#include "plugins/interfaces/kmmviewinterface.h"
#include "plugins/interfaces/kmmstatementinterface.h"
#include "plugins/interfaces/kmmimportinterface.h"
#include "plugins/interfaceloader.h"
#include "plugins/onlinepluginextended.h"
#include "pluginloader.h"
#include "tasks/credittransfer.h"
#include "icons/icons.h"
#include "misc/webconnect.h"
#include "storage/imymoneyserialize.h"
#include "storage/mymoneystoragesql.h"
#include
#include "transactioneditor.h"
#include "konlinetransferform.h"
#include
#include
#include "kmymoneyutils.h"
#include "kcreditswindow.h"
#include "ledgerdelegate.h"
#include "storageenums.h"
#include "mymoneyenums.h"
#include "dialogenums.h"
#include "menuenums.h"
#include "misc/platformtools.h"
// includes needed for shared global settings
#include "mymoney_config.h"
#include "widgets_config.h"
#ifdef KMM_DEBUG
#include "mymoneytracer.h"
#endif
using namespace Icons;
using namespace eMenu;
static constexpr char recoveryKeyId[] = "59B0F826D2B08440";
// define the default period to warn about an expiring recoverkey to 30 days
// but allows to override this setting during build time
#ifndef RECOVER_KEY_EXPIRATION_WARNING
#define RECOVER_KEY_EXPIRATION_WARNING 30
#endif
QHash pActions;
QHash pMenus;
enum backupStateE {
BACKUP_IDLE = 0,
BACKUP_MOUNTING,
BACKUP_COPYING,
BACKUP_UNMOUNTING
};
class KMyMoneyApp::Private
{
public:
Private(KMyMoneyApp *app) :
q(app),
m_ft(0),
m_moveToAccountSelector(0),
m_statementXMLindex(0),
m_balanceWarning(0),
m_collectingStatements(false),
m_backupResult(0),
m_backupMount(0),
m_ignoreBackupExitCode(false),
m_myMoneyView(0),
m_progressBar(0),
m_smtReader(0),
m_searchDlg(0),
m_autoSaveTimer(0),
m_progressTimer(0),
m_inAutoSaving(false),
m_transactionEditor(0),
m_endingBalanceDlg(0),
m_saveEncrypted(0),
m_additionalKeyLabel(0),
m_additionalKeyButton(0),
m_recentFiles(0),
#ifdef KF5Holidays_FOUND
m_holidayRegion(0),
#endif
m_applicationIsReady(true),
m_webConnect(new WebConnect(app)) {
// since the days of the week are from 1 to 7,
// and a day of the week is used to index this bit array,
// resize the array to 8 elements (element 0 is left unused)
m_processingDays.resize(8);
}
void closeFile();
void unlinkStatementXML();
void moveInvestmentTransaction(const QString& fromId,
const QString& toId,
const MyMoneyTransaction& t);
QList > automaticReconciliation(const MyMoneyAccount &account,
const QList > &transactions,
const MyMoneyMoney &amount);
/**
* The public interface.
*/
KMyMoneyApp * const q;
MyMoneyFileTransaction* m_ft;
KMyMoneyAccountSelector* m_moveToAccountSelector;
int m_statementXMLindex;
KBalanceWarning* m_balanceWarning;
bool m_collectingStatements;
QStringList m_statementResults;
QString m_lastPayeeEnteredId;
/** the configuration object of the application */
KSharedConfigPtr m_config;
/**
* @brief Structure of plugins objects by their interfaces
*/
KMyMoneyPlugin::Container m_plugins;
/**
* The following variable represents the state while crafting a backup.
* It can have the following values
*
* - IDLE: the default value if not performing a backup
* - MOUNTING: when a mount command has been issued
* - COPYING: when a copy command has been issued
* - UNMOUNTING: when an unmount command has been issued
*/
backupStateE m_backupState;
/**
* This variable keeps the result of the backup operation.
*/
int m_backupResult;
/**
* This variable is set, when the user selected to mount/unmount
* the backup volume.
*/
bool m_backupMount;
/**
* Flag for internal run control
*/
bool m_ignoreBackupExitCode;
KProcess m_proc;
/// A pointer to the view holding the tabs.
KMyMoneyView *m_myMoneyView;
/// The URL of the file currently being edited when open.
QUrl m_fileName;
bool m_startDialog;
QString m_mountpoint;
QProgressBar* m_progressBar;
QTime m_lastUpdate;
QLabel* m_statusLabel;
MyMoneyStatementReader* m_smtReader;
// allows multiple imports to be launched trough web connect and to be executed sequentially
QQueue m_importUrlsQueue;
KFindTransactionDlg* m_searchDlg;
MyMoneyAccount m_selectedAccount;
MyMoneyAccount m_reconciliationAccount;
MyMoneySchedule m_selectedSchedule;
KMyMoneyRegister::SelectedTransactions m_selectedTransactions;
// This is Auto Saving related
bool m_autoSaveEnabled;
QTimer* m_autoSaveTimer;
QTimer* m_progressTimer;
int m_autoSavePeriod;
bool m_inAutoSaving;
// pointer to the current transaction editor
TransactionEditor* m_transactionEditor;
// Reconciliation dialog
KEndingBalanceDlg* m_endingBalanceDlg;
// Pointer to the combo box used for key selection during
// File/Save as
KComboBox* m_saveEncrypted;
// id's that need to be remembered
QString m_accountGoto, m_payeeGoto;
QStringList m_additionalGpgKeys;
QLabel* m_additionalKeyLabel;
QPushButton* m_additionalKeyButton;
KRecentFilesAction* m_recentFiles;
#ifdef KF5Holidays_FOUND
// used by the calendar interface for schedules
KHolidays::HolidayRegion* m_holidayRegion;
#endif
QBitArray m_processingDays;
QMap m_holidayMap;
QStringList m_consistencyCheckResult;
bool m_applicationIsReady;
WebConnect* m_webConnect;
// methods
void consistencyCheck(bool alwaysDisplayResults);
static void setThemedCSS();
void copyConsistencyCheckResults();
void saveConsistencyCheckResults();
};
KMyMoneyApp::KMyMoneyApp(QWidget* parent) :
KXmlGuiWindow(parent),
d(new Private(this))
{
#ifdef KMM_DBUS
new KmymoneyAdaptor(this);
QDBusConnection::sessionBus().registerObject("/KMymoney", this);
QDBusConnection::sessionBus().interface()->registerService(
"org.kde.kmymoney-" + QString::number(platformTools::processId()), QDBusConnectionInterface::DontQueueService);
#endif
// Register the main engine types used as meta-objects
qRegisterMetaType("MyMoneyMoney");
qRegisterMetaType("MyMoneySecurity");
MyMoney::injectExternalSettings(KMyMoneyGlobalSettings::self());
Widgets::injectExternalSettings(KMyMoneyGlobalSettings::self());
// preset the pointer because we need it during the course of this constructor
kmymoney = this;
d->m_config = KSharedConfig::openConfig();
d->setThemedCSS();
MyMoneyTransactionFilter::setFiscalYearStart(KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay());
updateCaption(true);
QFrame* frame = new QFrame;
frame->setFrameStyle(QFrame::NoFrame);
// values for margin (11) and spacing(6) taken from KDialog implementation
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, frame);
layout->setContentsMargins(2, 2, 2, 2);
layout->setSpacing(6);
{
QString themeName = KMyMoneySettings::iconsTheme(); // get theme user wants
if (!themeName.isEmpty() && themeName != QLatin1Literal("system")) // if it isn't default theme then set it
QIcon::setThemeName(themeName);
Icons::setIconThemeNames(QIcon::themeName()); // get whatever theme user ends up with and hope our icon names will fit that theme
}
initStatusBar();
pActions = initActions();
pMenus = initMenus();
d->m_myMoneyView = new KMyMoneyView(this/*the global variable kmymoney is not yet assigned. So we pass it here*/);
layout->addWidget(d->m_myMoneyView, 10);
connect(d->m_myMoneyView, &KMyMoneyView::aboutToChangeView, this, &KMyMoneyApp::slotResetSelections);
connect(d->m_myMoneyView, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)),
this, SLOT(slotUpdateActions()));
connect(d->m_myMoneyView, &KMyMoneyView::statusMsg, this, &KMyMoneyApp::slotStatusMsg);
connect(d->m_myMoneyView, &KMyMoneyView::statusProgress, this, &KMyMoneyApp::slotStatusProgressBar);
///////////////////////////////////////////////////////////////////
// call inits to invoke all other construction parts
readOptions();
// now initialize the plugin structure
createInterfaces();
KMyMoneyPlugin::pluginHandling(KMyMoneyPlugin::Action::Load, d->m_plugins, this, guiFactory());
onlineJobAdministration::instance()->setOnlinePlugins(d->m_plugins.extended);
d->m_myMoneyView->setOnlinePlugins(d->m_plugins.online);
setCentralWidget(frame);
connect(&d->m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotBackupHandleEvents()));
// force to show the home page if the file is closed
connect(pActions[Action::ViewTransactionDetail], &QAction::toggled, d->m_myMoneyView, &KMyMoneyView::slotShowTransactionDetail);
d->m_backupState = BACKUP_IDLE;
QLocale locale;
int weekStart = locale.firstDayOfWeek();
int weekEnd = weekStart-1;
if (weekEnd < Qt::Monday) {
weekEnd = Qt::Sunday;
}
bool startFirst = (weekStart < weekEnd);
for (int i = 0; i < 8; ++i) {
if (startFirst)
d->m_processingDays.setBit(i, (i >= weekStart && i <= weekEnd));
else
d->m_processingDays.setBit(i, (i >= weekStart || i <= weekEnd));
}
d->m_autoSaveTimer = new QTimer(this);
d->m_progressTimer = new QTimer(this);
connect(d->m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
connect(d->m_progressTimer, SIGNAL(timeout()), this, SLOT(slotStatusProgressDone()));
// make sure, we get a note when the engine changes state
connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotDataChanged()));
// connect the WebConnect server
connect(d->m_webConnect, SIGNAL(gotUrl(QUrl)), this, SLOT(webConnect(QUrl)));
// make sure we have a balance warning object
d->m_balanceWarning = new KBalanceWarning(this);
// setup the initial configuration
slotUpdateConfiguration(QString());
// kickstart date change timer
slotDateChanged();
connect(this, SIGNAL(fileLoaded(QUrl)), onlineJobAdministration::instance(), SLOT(updateOnlineTaskProperties()));
}
KMyMoneyApp::~KMyMoneyApp()
{
// delete cached objects since the are in the way
// when unloading the plugins
onlineJobAdministration::instance()->clearCaches();
// we need to unload all plugins before we destroy anything else
KMyMoneyPlugin::pluginHandling(KMyMoneyPlugin::Action::Unload, d->m_plugins, this, guiFactory());
delete d->m_searchDlg;
delete d->m_transactionEditor;
delete d->m_endingBalanceDlg;
delete d->m_moveToAccountSelector;
#ifdef KF5Holidays_FOUND
delete d->m_holidayRegion;
#endif
Widgets::injectExternalSettings(nullptr);
MyMoney::injectExternalSettings(nullptr);
delete d;
}
QUrl KMyMoneyApp::lastOpenedURL()
{
QUrl url = d->m_startDialog ? QUrl() : d->m_fileName;
if (!url.isValid()) {
url = QUrl::fromUserInput(readLastUsedFile());
}
ready();
return url;
}
void KMyMoneyApp::slotObjectDestroyed(QObject* o)
{
if (o == d->m_moveToAccountSelector) {
d->m_moveToAccountSelector = 0;
}
}
void KMyMoneyApp::slotInstallConsistencyCheckContextMenu()
{
// this code relies on the implementation of KMessageBox::informationList to add a context menu to that list,
// please adjust it if it's necessary or rewrite the way the consistency check results are displayed
if (QWidget* dialog = QApplication::activeModalWidget()) {
if (QListWidget* widget = dialog->findChild()) {
// give the user a hint that the data can be saved
widget->setToolTip(i18n("This is the consistency check log, use the context menu to copy or save it."));
widget->setWhatsThis(widget->toolTip());
widget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(widget, SIGNAL(customContextMenuRequested(QPoint)), SLOT(slotShowContextMenuForConsistencyCheck(QPoint)));
}
}
}
void KMyMoneyApp::slotShowContextMenuForConsistencyCheck(const QPoint &pos)
{
// allow the user to save the consistency check results
if (QWidget* widget = qobject_cast< QWidget* >(sender())) {
QMenu contextMenu(widget);
QAction* copy = new QAction(i18n("Copy to clipboard"), widget);
QAction* save = new QAction(i18n("Save to file"), widget);
contextMenu.addAction(copy);
contextMenu.addAction(save);
QAction *result = contextMenu.exec(widget->mapToGlobal(pos));
if (result == copy) {
// copy the consistency check results to the clipboard
d->copyConsistencyCheckResults();
} else if (result == save) {
// save the consistency check results to a file
d->saveConsistencyCheckResults();
}
}
}
QHash KMyMoneyApp::initMenus()
{
QHash
has not been found in your keyring at this time. Please make sure to import this key into your keyring. You can find it on the KMyMoney web-site. This time your data will not be encrypted with the KMyMoney recover key.
", QString(recoveryKeyId)), i18n("GPG Key not found"));
encryptRecover = false;
}
}
for(const QString& key: keyList.split(',', QString::SkipEmptyParts)) {
if (!KGPGFile::keyAvailable(key)) {
KMessageBox::sorry(this, i18n("You have specified to encrypt your data for the user-id
%1.Unfortunately, a valid key for this user-id was not found in your keyring. Please make sure to import a valid key for this user-id. This time, encryption is disabled.
", key), i18n("GPG Key not found"));
encryptFile = false;
break;
}
}
if (encryptFile == true) {
QString msg = i18n("You have configured to save your data in encrypted form using GPG. Make sure you understand that you might lose all your data if you encrypt it, but cannot decrypt it later on. If unsure, answer No.
");
if (KMessageBox::questionYesNo(this, msg, i18n("Store GPG encrypted"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "StoreEncrypted") == KMessageBox::No) {
encryptFile = false;
}
}
}
}
// Create a temporary file if needed
QString writeFile = localFile;
QTemporaryFile tmpFile;
if (QFile::exists(localFile)) {
tmpFile.open();
writeFile = tmpFile.fileName();
tmpFile.close();
}
/**
* @brief Automatically restore settings when scope is left
*/
struct restorePreviousSettingsHelper {
restorePreviousSettingsHelper()
: m_signalsWereBlocked{MyMoneyFile::instance()->signalsBlocked()}
{
MyMoneyFile::instance()->blockSignals(true);
}
~restorePreviousSettingsHelper()
{
MyMoneyFile::instance()->blockSignals(m_signalsWereBlocked);
}
const bool m_signalsWereBlocked;
} restoreHelper;
MyMoneyFileTransaction ft;
MyMoneyFile::instance()->deletePair("kmm-encryption-key");
std::unique_ptr device;
if (!keyList.isEmpty() && encryptFile && !plaintext) {
std::unique_ptr kgpg = std::unique_ptr(new KGPGFile{writeFile});
if (kgpg) {
for(const QString& key: keyList.split(',', QString::SkipEmptyParts)) {
kgpg->addRecipient(key.toLatin1());
}
if (encryptRecover) {
kgpg->addRecipient(recoveryKeyId);
}
MyMoneyFile::instance()->setValue("kmm-encryption-key", keyList);
device = std::unique_ptr(kgpg.release());
}
} else {
QFile *file = new QFile(writeFile);
// The second parameter of KCompressionDevice means that KCompressionDevice will delete the QFile object
device = std::unique_ptr(new KCompressionDevice{file, true, (plaintext) ? KCompressionDevice::None : COMPRESSION_TYPE});
}
ft.commit();
if (!device || !device->open(QIODevice::WriteOnly)) {
throw MYMONEYEXCEPTION(i18n("Unable to open file '%1' for writing.", localFile));
}
pWriter->setProgressCallback(&KMyMoneyView::progressCallback);
pWriter->writeFile(device.get(), dynamic_cast(MyMoneyFile::instance()->storage()));
device->close();
// Check for errors if possible, only possible for KGPGFile
QFileDevice *fileDevice = qobject_cast(device.get());
if (fileDevice && fileDevice->error() != QFileDevice::NoError) {
throw MYMONEYEXCEPTION(i18n("Failure while writing to '%1'", localFile));
}
if (writeFile != localFile) {
// This simple comparison is possible because the strings are equal if no temporary file was created.
// If a temporary file was created, it is made in a way that the name is definitely different. So no
// symlinks etc. have to be evaluated.
if (!QFile::remove(localFile) || !QFile::rename(writeFile, localFile))
throw MYMONEYEXCEPTION(i18n("Failure while writing to '%1'", localFile));
}
QFile::setPermissions(localFile, m_fmode);
pWriter->setProgressCallback(0);
}
bool KMyMoneyView::saveFile(const QUrl &url, const QString& keyList)
{
QString filename = url.path();
if (!fileOpen()) {
KMessageBox::error(this, i18n("Tried to access a file when it has not been opened"));
return false;
}
emit kmmFilePlugin(preSave);
std::unique_ptr storageWriter;
// If this file ends in ".ANON.XML" then this should be written using the
// anonymous writer.
bool plaintext = filename.right(4).toLower() == ".xml";
if (filename.right(9).toLower() == ".anon.xml") {
//! @todo C++14: use std::make_unique, also some lines below
storageWriter = std::unique_ptr(new MyMoneyStorageANON);
} else {
storageWriter = std::unique_ptr(new MyMoneyStorageXML);
}
// actually, url should be the parameter to this function
// but for now, this would involve too many changes
bool rc = true;
try {
if (! url.isValid()) {
throw MYMONEYEXCEPTION(i18n("Malformed URL '%1'", url.url()));
}
if (url.isLocalFile()) {
filename = url.toLocalFile();
try {
const unsigned int nbak = KMyMoneyGlobalSettings::autoBackupCopies();
if (nbak) {
KBackup::numberedBackupFile(filename, QString(), QString::fromLatin1("~"), nbak);
}
saveToLocalFile(filename, storageWriter.get(), plaintext, keyList);
} catch (const MyMoneyException &) {
throw MYMONEYEXCEPTION(i18n("Unable to write changes to '%1'", filename));
}
} else {
QTemporaryFile tmpfile;
tmpfile.open(); // to obtain the name
tmpfile.close();
saveToLocalFile(tmpfile.fileName(), storageWriter.get(), plaintext, keyList);
Q_CONSTEXPR int permission = -1;
QFile file(tmpfile.fileName());
file.open(QIODevice::ReadOnly);
KIO::StoredTransferJob *putjob = KIO::storedPut(file.readAll(), url, permission, KIO::JobFlag::Overwrite);
if (!putjob->exec()) {
throw MYMONEYEXCEPTION(i18n("Unable to upload to '%1'.
%2", url.toDisplayString(), putjob->errorString()));
}
file.close();
}
m_fileType = KmmXML;
} catch (const MyMoneyException &e) {
KMessageBox::error(this, e.what());
MyMoneyFile::instance()->setDirty();
rc = false;
}
emit kmmFilePlugin(postSave);
return rc;
}
bool KMyMoneyView::saveAsDatabase(const QUrl &url)
{
bool rc = false;
if (!fileOpen()) {
KMessageBox::error(this, i18n("Tried to access a file when it has not been opened"));
return (rc);
}
MyMoneyStorageSql *writer = new MyMoneyStorageSql(dynamic_cast(MyMoneyFile::instance()->storage()), url);
bool canWrite = false;
switch (writer->open(url, QIODevice::WriteOnly)) {
case 0:
canWrite = true;
break;
case -1: // dbase already has data, see if he wants to clear it out
if (KMessageBox::warningContinueCancel(0,
i18n("Database contains data which must be removed before using Save As.\n"
"Do you wish to continue?"), "Database not empty") == KMessageBox::Continue) {
if (writer->open(url, QIODevice::WriteOnly, true) == 0)
canWrite = true;
} else {
delete writer;
return false;
}
break;
}
if (canWrite) {
writer->setProgressCallback(&KMyMoneyView::progressCallback);
if (!writer->writeFile()) {
KMessageBox::detailedError(0,
i18n("An unrecoverable error occurred while writing to the database.\n"
"It may well be corrupt."),
writer->lastError().toLatin1(),
i18n("Database malfunction"));
rc = false;
}
writer->setProgressCallback(0);
rc = true;
} else {
KMessageBox::detailedError(this,
i18n("Cannot open or create database %1.\n"
"Retry Save As Database and click Help"
" for further info.", url.toDisplayString()), writer->lastError());
}
delete writer;
return (rc);
}
bool KMyMoneyView::dirty()
{
if (!fileOpen())
return false;
return MyMoneyFile::instance()->dirty();
}
void KMyMoneyView::finishReconciliation(const MyMoneyAccount& /* account */)
{
Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
m_ledgerView->slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
}
void KMyMoneyView::newFile()
{
closeFile();
m_fileType = KmmXML; // assume native type until saved
m_fileOpen = true;
}
void KMyMoneyView::slotSetBaseCurrency(const MyMoneySecurity& baseCurrency)
{
if (!baseCurrency.id().isEmpty()) {
QString baseId;
try {
baseId = MyMoneyFile::instance()->baseCurrency().id();
} catch (const MyMoneyException &e) {
qDebug("%s", qPrintable(e.what()));
}
if (baseCurrency.id() != baseId) {
MyMoneyFileTransaction ft;
try {
MyMoneyFile::instance()->setBaseCurrency(baseCurrency);
ft.commit();
} catch (const MyMoneyException &e) {
KMessageBox::sorry(this, i18n("Cannot set %1 as base currency: %2", baseCurrency.name(), e.what()), i18n("Set base currency"));
}
}
AmountEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction()));
KMyMoneyEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction()));
}
}
void KMyMoneyView::selectBaseCurrency()
{
auto file = MyMoneyFile::instance();
// check if we have a base currency. If not, we need to select one
QString baseId;
try {
baseId = MyMoneyFile::instance()->baseCurrency().id();
} catch (const MyMoneyException &e) {
qDebug("%s", qPrintable(e.what()));
}
if (baseId.isEmpty()) {
QPointer dlg = new KCurrencyEditDlg(this);
connect(dlg, SIGNAL(selectBaseCurrency(MyMoneySecurity)), this, SLOT(slotSetBaseCurrency(MyMoneySecurity)));
dlg->exec();
delete dlg;
}
try {
baseId = MyMoneyFile::instance()->baseCurrency().id();
} catch (const MyMoneyException &e) {
qDebug("%s", qPrintable(e.what()));
}
if (!baseId.isEmpty()) {
// check that all accounts have a currency
QList list;
file->accountList(list);
QList::Iterator it;
// don't forget those standard accounts
list << file->asset();
list << file->liability();
list << file->income();
list << file->expense();
list << file->equity();
for (it = list.begin(); it != list.end(); ++it) {
QString cid;
try {
if (!(*it).currencyId().isEmpty() || (*it).currencyId().length() != 0)
cid = MyMoneyFile::instance()->currency((*it).currencyId()).id();
} catch (const MyMoneyException& e) {
qDebug() << QLatin1String("Account") << (*it).id() << (*it).name() << e.what();
}
if (cid.isEmpty()) {
(*it).setCurrencyId(baseId);
MyMoneyFileTransaction ft;
try {
file->modifyAccount(*it);
ft.commit();
} catch (const MyMoneyException &e) {
qDebug("Unable to setup base currency in account %s (%s): %s", qPrintable((*it).name()), qPrintable((*it).id()), qPrintable(e.what()));
}
}
}
}
}
void KMyMoneyView::updateCurrencyNames()
{
auto file = MyMoneyFile::instance();
MyMoneyFileTransaction ft;
QList storedCurrencies = MyMoneyFile::instance()->currencyList();
QList availableCurrencies = MyMoneyFile::instance()->availableCurrencyList();
QStringList currencyIDs;
foreach (auto currency, availableCurrencies)
currencyIDs.append(currency.id());
try {
foreach (auto currency, storedCurrencies) {
int i = currencyIDs.indexOf(currency.id());
if (i != -1 && availableCurrencies.at(i).name() != currency.name()) {
currency.setName(availableCurrencies.at(i).name());
file->modifyCurrency(currency);
}
}
ft.commit();
} catch (const MyMoneyException &e) {
qDebug("Error %s updating currency names", qPrintable(e.what()));
}
}
void KMyMoneyView::loadAllCurrencies()
{
auto file = MyMoneyFile::instance();
MyMoneyFileTransaction ft;
if (!file->currencyList().isEmpty())
return;
QMap ancientCurrencies = file->ancientCurrencies();
try {
foreach (auto currency, file->availableCurrencyList()) {
file->addCurrency(currency);
MyMoneyPrice price = ancientCurrencies.value(currency, MyMoneyPrice());
if (price != MyMoneyPrice())
file->addPrice(price);
}
ft.commit();
} catch (const MyMoneyException &e) {
qDebug("Error %s loading currency", qPrintable(e.what()));
}
}
void KMyMoneyView::viewAccountList(const QString& /*selectAccount*/)
{
if (viewFrames[View::Accounts] != currentPage())
showPage(viewFrames[View::Accounts]);
m_accountsView->show();
}
void KMyMoneyView::slotRefreshViews()
{
// turn off sync between ledger and investment view
disconnect(m_investmentView, &KInvestmentView::accountSelected, m_ledgerView, static_cast(&KGlobalLedgerView::slotSelectAccount));
disconnect(m_ledgerView, &KGlobalLedgerView::objectSelected, m_investmentView, static_cast(&KInvestmentView::slotSelectAccount));
// TODO turn sync between ledger and investment view if selected by user
if (KMyMoneyGlobalSettings::syncLedgerInvestment()) {
connect(m_investmentView, &KInvestmentView::accountSelected, m_ledgerView, static_cast(&KGlobalLedgerView::slotSelectAccount));
connect(m_ledgerView, &KGlobalLedgerView::objectSelected, m_investmentView, static_cast(&KInvestmentView::slotSelectAccount));
}
showTitleBar(KMyMoneyGlobalSettings::showTitleBar());
m_accountsView->refresh();
m_institutionsView->refresh();
m_categoriesView->refresh();
m_payeesView->refresh();
m_tagsView->refresh();
m_ledgerView->refresh();
m_budgetView->refresh();
m_homeView->refresh();
m_investmentView->refresh();
m_reportsView->refresh();
m_forecastView->refresh();
m_scheduledView->refresh();
m_payeesView->slotClosePayeeIdentifierSource();
}
void KMyMoneyView::slotShowTransactionDetail(bool detailed)
{
KMyMoneyGlobalSettings::setShowRegisterDetailed(detailed);
slotRefreshViews();
}
void KMyMoneyView::progressCallback(int current, int total, const QString& msg)
{
kmymoney->progressCallback(current, total, msg);
}
void KMyMoneyView::slotCurrentPageChanged(const QModelIndex current, const QModelIndex)
{
// remember the current page
m_lastViewSelected = current.row();
// set the current page's title in the header
if (m_header)
m_header->setText(m_model->data(current, KPageModel::HeaderRole).toString());
}
/* DO NOT ADD code to this function or any of it's called ones.
Instead, create a new function, fixFile_n, and modify the initializeStorage()
logic above to call it */
void KMyMoneyView::fixFile_3()
{
// make sure each storage object contains a (unique) id
MyMoneyFile::instance()->storageId();
}
void KMyMoneyView::fixFile_2()
{
auto file = MyMoneyFile::instance();
MyMoneyTransactionFilter filter;
filter.setReportAllSplits(false);
QList transactionList;
file->transactionList(transactionList, filter);
// scan the transactions and modify transactions with two splits
// which reference an account and a category to have the memo text
// of the account.
auto count = 0;
foreach (const auto transaction, transactionList) {
if (transaction.splitCount() == 2) {
QString accountId;
QString categoryId;
QString accountMemo;
QString categoryMemo;
foreach (const auto split, transaction.splits()) {
auto acc = file->account(split.accountId());
if (acc.isIncomeExpense()) {
categoryId = split.id();
categoryMemo = split.memo();
} else {
accountId = split.id();
accountMemo = split.memo();
}
}
if (!accountId.isEmpty() && !categoryId.isEmpty()
&& accountMemo != categoryMemo) {
MyMoneyTransaction t(transaction);
MyMoneySplit s(t.splitById(categoryId));
s.setMemo(accountMemo);
t.modifySplit(s);
file->modifyTransaction(t);
++count;
}
}
}
qDebug("%d transactions fixed in fixFile_2", count);
}
void KMyMoneyView::fixFile_1()
{
// we need to fix reports. If the account filter list contains
// investment accounts, we need to add the stock accounts to the list
// as well if we don't have the expert mode enabled
if (!KMyMoneyGlobalSettings::expertMode()) {
try {
QList reports = MyMoneyFile::instance()->reportList();
QList::iterator it_r;
for (it_r = reports.begin(); it_r != reports.end(); ++it_r) {
QStringList list;
(*it_r).accounts(list);
QStringList missing;
QStringList::const_iterator it_a, it_b;
for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) {
auto acc = MyMoneyFile::instance()->account(*it_a);
if (acc.accountType() == Account::Type::Investment) {
foreach (const auto accountID, acc.accountList()) {
if (!list.contains(accountID)) {
missing.append(accountID);
}
}
}
}
if (!missing.isEmpty()) {
(*it_r).addAccount(missing);
MyMoneyFile::instance()->modifyReport(*it_r);
}
}
} catch (const MyMoneyException &) {
}
}
}
#if 0
if (!m_accountsView->allItemsSelected())
{
// retrieve a list of selected accounts
QStringList list;
m_accountsView->selectedItems(list);
// if we're not in expert mode, we need to make sure
// that all stock accounts for the selected investment
// account are also selected
if (!KMyMoneyGlobalSettings::expertMode()) {
QStringList missing;
QStringList::const_iterator it_a, it_b;
for (it_a = list.begin(); it_a != list.end(); ++it_a) {
auto acc = MyMoneyFile::instance()->account(*it_a);
if (acc.accountType() == Account::Type::Investment) {
foreach (const auto accountID, acc.accountList()) {
if (!list.contains(accountID)) {
missing.append(accountID);
}
}
}
}
list += missing;
}
m_filter.addAccount(list);
}
#endif
void KMyMoneyView::fixFile_0()
{
/* (Ace) I am on a crusade against file fixups. Whenever we have to fix the
* file, it is really a warning. So I'm going to print a debug warning, and
* then go track them down when I see them to figure out how they got saved
* out needing fixing anyway.
*/
auto file = MyMoneyFile::instance();
QList accountList;
file->accountList(accountList);
QList::Iterator it_a;
QList scheduleList = file->scheduleList();
QList::Iterator it_s;
MyMoneyAccount equity = file->equity();
MyMoneyAccount asset = file->asset();
bool equityListEmpty = equity.accountList().count() == 0;
for (it_a = accountList.begin(); it_a != accountList.end(); ++it_a) {
if ((*it_a).accountType() == Account::Type::Loan
|| (*it_a).accountType() == Account::Type::AssetLoan) {
fixLoanAccount_0(*it_a);
}
// until early before 0.8 release, the equity account was not saved to
// the file. If we have an equity account with no sub-accounts but
// find and equity account that has equity() as it's parent, we reparent
// this account. Need to move it to asset() first, because otherwise
// MyMoneyFile::reparent would act as NOP.
if (equityListEmpty && (*it_a).accountType() == Account::Type::Equity) {
if ((*it_a).parentAccountId() == equity.id()) {
auto acc = *it_a;
// tricky, force parent account to be empty so that we really
// can re-parent it
acc.setParentAccountId(QString());
file->reparentAccount(acc, equity);
qDebug() << Q_FUNC_INFO << " fixed account " << acc.id() << " reparented to " << equity.id();
}
}
}
for (it_s = scheduleList.begin(); it_s != scheduleList.end(); ++it_s) {
fixSchedule_0(*it_s);
}
fixTransactions_0();
}
void KMyMoneyView::fixSchedule_0(MyMoneySchedule sched)
{
MyMoneyTransaction t = sched.transaction();
QList splitList = t.splits();
QList::ConstIterator it_s;
bool updated = false;
try {
// Check if the splits contain valid data and set it to
// be valid.
for (it_s = splitList.constBegin(); it_s != splitList.constEnd(); ++it_s) {
// the first split is always the account on which this transaction operates
// and if the transaction commodity is not set, we take this
if (it_s == splitList.constBegin() && t.commodity().isEmpty()) {
qDebug() << Q_FUNC_INFO << " " << t.id() << " has no commodity";
try {
auto acc = MyMoneyFile::instance()->account((*it_s).accountId());
t.setCommodity(acc.currencyId());
updated = true;
} catch (const MyMoneyException &) {
}
}
// make sure the account exists. If not, remove the split
try {
MyMoneyFile::instance()->account((*it_s).accountId());
} catch (const MyMoneyException &) {
qDebug() << Q_FUNC_INFO << " " << sched.id() << " " << (*it_s).id() << " removed, because account '" << (*it_s).accountId() << "' does not exist.";
t.removeSplit(*it_s);
updated = true;
}
if ((*it_s).reconcileFlag() != eMyMoney::Split::State::NotReconciled) {
qDebug() << Q_FUNC_INFO << " " << sched.id() << " " << (*it_s).id() << " should be 'not reconciled'";
MyMoneySplit split = *it_s;
split.setReconcileDate(QDate());
split.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
t.modifySplit(split);
updated = true;
}
// the schedule logic used to operate only on the value field.
// This is now obsolete.
if ((*it_s).shares().isZero() && !(*it_s).value().isZero()) {
MyMoneySplit split = *it_s;
split.setShares(split.value());
t.modifySplit(split);
updated = true;
}
}
// If there have been changes, update the schedule and
// the engine data.
if (updated) {
sched.setTransaction(t);
MyMoneyFile::instance()->modifySchedule(sched);
}
} catch (const MyMoneyException &e) {
qWarning("Unable to update broken schedule: %s", qPrintable(e.what()));
}
}
void KMyMoneyView::fixLoanAccount_0(MyMoneyAccount acc)
{
if (acc.value("final-payment").isEmpty()
|| acc.value("term").isEmpty()
|| acc.value("periodic-payment").isEmpty()
|| acc.value("loan-amount").isEmpty()
|| acc.value("interest-calculation").isEmpty()
|| acc.value("schedule").isEmpty()
|| acc.value("fixed-interest").isEmpty()) {
KMessageBox::information(this,
i18n("The account \"%1\" was previously created as loan account but some information is missing.
The new loan wizard will be started to collect all relevant information.
Please use KMyMoney version 0.8.7 or later and earlier than version 0.9 to correct the problem.
"
, acc.name()),
i18n("Account problem"));
throw MYMONEYEXCEPTION("Fix LoanAccount0 not supported anymore");
}
}
void KMyMoneyView::createSchedule(MyMoneySchedule newSchedule, MyMoneyAccount& newAccount)
{
// Add the schedule only if one exists
//
// Remember to modify the first split to reference the newly created account
if (!newSchedule.name().isEmpty()) {
MyMoneyFileTransaction ft;
try {
// We assume at least 2 splits in the transaction
MyMoneyTransaction t = newSchedule.transaction();
if (t.splitCount() < 2) {
throw MYMONEYEXCEPTION("Transaction for schedule has less than 2 splits!");
}
// now search the split that does not have an account reference
// and set it up to be the one of the account we just added
// to the account pool. Note: the schedule code used to leave
// this always the first split, but the loan code leaves it as
// the second one. So I thought, searching is a good alternative ....
foreach (const auto split, t.splits()) {
if (split.accountId().isEmpty()) {
MyMoneySplit s = split;
s.setAccountId(newAccount.id());
t.modifySplit(s);
break;
}
}
newSchedule.setTransaction(t);
MyMoneyFile::instance()->addSchedule(newSchedule);
// in case of a loan account, we keep a reference to this
// schedule in the account
if (newAccount.isLoan()) {
newAccount.setValue("schedule", newSchedule.id());
MyMoneyFile::instance()->modifyAccount(newAccount);
}
ft.commit();
} catch (const MyMoneyException &e) {
KMessageBox::information(this, i18n("Unable to add schedule: %1", e.what()));
}
}
}
void KMyMoneyView::fixTransactions_0()
{
auto file = MyMoneyFile::instance();
QList scheduleList = file->scheduleList();
MyMoneyTransactionFilter filter;
filter.setReportAllSplits(false);
QList transactionList;
file->transactionList(transactionList, filter);
QList::Iterator it_x;
QStringList interestAccounts;
KMSTATUS(i18n("Fix transactions"));
kmymoney->slotStatusProgressBar(0, scheduleList.count() + transactionList.count());
int cnt = 0;
// scan the schedules to find interest accounts
for (it_x = scheduleList.begin(); it_x != scheduleList.end(); ++it_x) {
MyMoneyTransaction t = (*it_x).transaction();
QList::ConstIterator it_s;
QStringList accounts;
bool hasDuplicateAccounts = false;
foreach (const auto split, t.splits()) {
if (accounts.contains(split.accountId())) {
hasDuplicateAccounts = true;
qDebug() << Q_FUNC_INFO << " " << t.id() << " has multiple splits with account " << split.accountId();
} else {
accounts << split.accountId();
}
if (split.action() == MyMoneySplit::ActionInterest) {
if (interestAccounts.contains(split.accountId()) == 0) {
interestAccounts << split.accountId();
}
}
}
if (hasDuplicateAccounts) {
fixDuplicateAccounts_0(t);
}
++cnt;
if (!(cnt % 10))
kmymoney->slotStatusProgressBar(cnt);
}
// scan the transactions and modify loan transactions
for (auto& transaction : transactionList) {
const char *defaultAction = 0;
QList splits = transaction.splits();
QStringList accounts;
// check if base commodity is set. if not, set baseCurrency
if (transaction.commodity().isEmpty()) {
qDebug() << Q_FUNC_INFO << " " << transaction.id() << " has no base currency";
transaction.setCommodity(file->baseCurrency().id());
file->modifyTransaction(transaction);
}
bool isLoan = false;
// Determine default action
if (transaction.splitCount() == 2) {
// check for transfer
int accountCount = 0;
MyMoneyMoney val;
foreach (const auto split, splits) {
auto acc = file->account(split.accountId());
if (acc.accountGroup() == Account::Type::Asset
|| acc.accountGroup() == Account::Type::Liability) {
val = split.value();
accountCount++;
if (acc.accountType() == Account::Type::Loan
|| acc.accountType() == Account::Type::AssetLoan)
isLoan = true;
} else
break;
}
if (accountCount == 2) {
if (isLoan)
defaultAction = MyMoneySplit::ActionAmortization;
else
defaultAction = MyMoneySplit::ActionTransfer;
} else {
if (val.isNegative())
defaultAction = MyMoneySplit::ActionWithdrawal;
else
defaultAction = MyMoneySplit::ActionDeposit;
}
}
isLoan = false;
foreach (const auto split, splits) {
auto acc = file->account(split.accountId());
MyMoneyMoney val = split.value();
if (acc.accountGroup() == Account::Type::Asset
|| acc.accountGroup() == Account::Type::Liability) {
if (!val.isPositive()) {
defaultAction = MyMoneySplit::ActionWithdrawal;
break;
} else {
defaultAction = MyMoneySplit::ActionDeposit;
break;
}
}
}
#if 0
// Check for correct actions in transactions referencing credit cards
bool needModify = false;
// The action fields are actually not used anymore in the ledger view logic
// so we might as well skip this whole thing here!
for (it_s = splits.begin(); needModify == false && it_s != splits.end(); ++it_s) {
auto acc = file->account((*it_s).accountId());
MyMoneyMoney val = (*it_s).value();
if (acc.accountType() == Account::Type::CreditCard) {
if (val < 0 && (*it_s).action() != MyMoneySplit::ActionWithdrawal && (*it_s).action() != MyMoneySplit::ActionTransfer)
needModify = true;
if (val >= 0 && (*it_s).action() != MyMoneySplit::ActionDeposit && (*it_s).action() != MyMoneySplit::ActionTransfer)
needModify = true;
}
}
// (Ace) Extended the #endif down to cover this conditional, because as-written
// it will ALWAYS be skipped.
if (needModify == true) {
for (it_s = splits.begin(); it_s != splits.end(); ++it_s) {
(*it_s).setAction(defaultAction);
transaction.modifySplit(*it_s);
file->modifyTransaction(transaction);
}
splits = transaction.splits(); // update local copy
qDebug("Fixed credit card assignment in %s", transaction.id().data());
}
#endif
// Check for correct assignment of ActionInterest in all splits
// and check if there are any duplicates in this transactions
for (auto& split : splits) {
MyMoneyAccount splitAccount = file->account(split.accountId());
if (!accounts.contains(split.accountId())) {
accounts << split.accountId();
}
// if this split references an interest account, the action
// must be of type ActionInterest
if (interestAccounts.contains(split.accountId())) {
if (split.action() != MyMoneySplit::ActionInterest) {
qDebug() << Q_FUNC_INFO << " " << transaction.id() << " contains an interest account (" << split.accountId() << ") but does not have ActionInterest";
split.setAction(MyMoneySplit::ActionInterest);
transaction.modifySplit(split);
file->modifyTransaction(transaction);
qDebug("Fixed interest action in %s", qPrintable(transaction.id()));
}
// if it does not reference an interest account, it must not be
// of type ActionInterest
} else {
if (split.action() == MyMoneySplit::ActionInterest) {
qDebug() << Q_FUNC_INFO << " " << transaction.id() << " does not contain an interest account so it should not have ActionInterest";
split.setAction(defaultAction);
transaction.modifySplit(split);
file->modifyTransaction(transaction);
qDebug("Fixed interest action in %s", qPrintable(transaction.id()));
}
}
// check that for splits referencing an account that has
// the same currency as the transactions commodity the value
// and shares field are the same.
if (transaction.commodity() == splitAccount.currencyId()
&& split.value() != split.shares()) {
qDebug() << Q_FUNC_INFO << " " << transaction.id() << " " << split.id() << " uses the transaction currency, but shares != value";
split.setShares(split.value());
transaction.modifySplit(split);
file->modifyTransaction(transaction);
}
// fix the shares and values to have the correct fraction
if (!splitAccount.isInvest()) {
try {
int fract = splitAccount.fraction();
if (split.shares() != split.shares().convert(fract)) {
qDebug("adjusting fraction in %s,%s", qPrintable(transaction.id()), qPrintable(split.id()));
split.setShares(split.shares().convert(fract));
split.setValue(split.value().convert(fract));
transaction.modifySplit(split);
file->modifyTransaction(transaction);
}
} catch (const MyMoneyException &) {
qDebug("Missing security '%s', split not altered", qPrintable(splitAccount.currencyId()));
}
}
}
++cnt;
if (!(cnt % 10))
kmymoney->slotStatusProgressBar(cnt);
}
kmymoney->slotStatusProgressBar(-1, -1);
}
void KMyMoneyView::fixDuplicateAccounts_0(MyMoneyTransaction& t)
{
qDebug("Duplicate account in transaction %s", qPrintable(t.id()));
}
void KMyMoneyView::slotPrintView()
{
if (viewFrames[View::Reports] == currentPage())
m_reportsView->slotPrintView();
else if (viewFrames[View::Home] == currentPage())
m_homeView->slotPrintView();
}
void KMyMoneyView::resetViewSelection(const View)
{
emit aboutToChangeView();
}
void KMyMoneyView::connectView(const View view)
{
KMyMoneyAccountTreeView *treeView;
switch (view) {
case View::Home:
disconnect(m_homeView, &KHomeView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_homeView, &KHomeView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(m_homeView, &KHomeView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
break;
case View::Accounts:
disconnect(m_accountsView, &KAccountsView::aboutToShow, this, &KMyMoneyView::connectView);
treeView = m_accountsView->getTreeView();
connect(treeView, &KMyMoneyAccountTreeView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(treeView, &KMyMoneyAccountTreeView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
connect(treeView, &KMyMoneyAccountTreeView::columnToggled, this, &KMyMoneyView::slotAccountTreeViewChanged);
connect(m_accountsView, &KAccountsView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(Models::instance()->accountsModel(), &AccountsModel::netWorthChanged, m_accountsView, &KAccountsView::slotNetWorthChanged);
break;
case View::Schedules:
disconnect(m_scheduledView, &KScheduledView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_scheduledView, &KScheduledView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(m_scheduledView, &KScheduledView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(m_scheduledView, &KScheduledView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
break;
case View::Institutions:
disconnect(m_institutionsView, &KInstitutionsView::aboutToShow, this, &KMyMoneyView::connectView);
treeView = m_institutionsView->getTreeView();
connect(treeView, &KMyMoneyAccountTreeView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(treeView, &KMyMoneyAccountTreeView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
connect(treeView, &KMyMoneyAccountTreeView::columnToggled, this, &KMyMoneyView::slotAccountTreeViewChanged);
connect(m_institutionsView, &KInstitutionsView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(Models::instance()->institutionsModel(), &AccountsModel::netWorthChanged, m_institutionsView, &KInstitutionsView::slotNetWorthChanged);
break;
case View::Categories:
disconnect(m_categoriesView, &KCategoriesView::aboutToShow, this, &KMyMoneyView::connectView);
treeView = m_categoriesView->getTreeView();
connect(treeView, &KMyMoneyAccountTreeView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(treeView, &KMyMoneyAccountTreeView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(treeView, &KMyMoneyAccountTreeView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
connect(treeView, &KMyMoneyAccountTreeView::columnToggled, this, &KMyMoneyView::slotAccountTreeViewChanged);
connect(m_categoriesView, &KCategoriesView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(Models::instance()->institutionsModel(), &AccountsModel::profitChanged, m_categoriesView, &KCategoriesView::slotProfitChanged);
break;
case View::Tags:
disconnect(m_tagsView, &KTagsView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_tagsView, &KTagsView::transactionSelected, m_ledgerView, &KGlobalLedgerView::slotLedgerSelected);
break;
case View::Payees:
disconnect(m_payeesView, &KTagsView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_payeesView, &KPayeesView::transactionSelected, m_ledgerView, &KGlobalLedgerView::slotLedgerSelected);
break;
case View::Ledgers:
disconnect(m_ledgerView, &KGlobalLedgerView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_ledgerView, &KGlobalLedgerView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(m_ledgerView, &KGlobalLedgerView::openPayeeRequested, this, &KMyMoneyView::slotPayeeSelected);
connect(m_ledgerView, &KGlobalLedgerView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(m_ledgerView, &KGlobalLedgerView::transactionsSelected, this, &KMyMoneyView::slotTransactionsSelected);
connect(m_ledgerView, &KGlobalLedgerView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
connect(m_ledgerView, &KGlobalLedgerView::transactionsContextMenuRequested, this, &KMyMoneyView::slotTransactionsMenuRequested);
connect(m_ledgerView, &KGlobalLedgerView::statusProgress, this, &KMyMoneyView::statusProgress);
connect(m_ledgerView, &KGlobalLedgerView::statusMsg, this, &KMyMoneyView::statusMsg);
connect(m_ledgerView, &KGlobalLedgerView::accountReconciled, this, &KMyMoneyView::accountReconciled);
connect(m_ledgerView, &KGlobalLedgerView::enterOverdueSchedulesRequested, m_scheduledView, &KScheduledView::slotEnterOverdueSchedules);
connect(m_scheduledView, &KScheduledView::enterOverdueSchedulesFinished, m_ledgerView, &KGlobalLedgerView::slotContinueReconciliation);
break;
case View::Budget:
disconnect(m_budgetView, &KBudgetView::aboutToShow, this, &KMyMoneyView::connectView);
treeView = m_budgetView->getTreeView();
connect(treeView, &KMyMoneyAccountTreeView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(treeView, &KMyMoneyAccountTreeView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
connect(treeView, &KMyMoneyAccountTreeView::columnToggled, this, &KMyMoneyView::slotAccountTreeViewChanged);
connect(m_budgetView, &KBudgetView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
break;
case View::Investments:
disconnect(m_investmentView, &KInvestmentView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_investmentView, &KInvestmentView::accountSelected, kmymoney, &KMyMoneyApp::slotSelectAccount);
connect(m_investmentView, &KInvestmentView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(m_investmentView, &KInvestmentView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
break;
case View::Reports:
disconnect(m_reportsView, &KReportsView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_reportsView, &KReportsView::transactionSelected, m_ledgerView, &KGlobalLedgerView::slotLedgerSelected);
break;
case View::Forecast:
disconnect(m_forecastView, &KForecastView::aboutToShow, this, &KMyMoneyView::connectView);
break;
case View::OnlineJobOutbox:
disconnect(m_onlineJobOutboxView, &KOnlineJobOutbox::aboutToShow, this, &KMyMoneyView::connectView);
break;
default:
break;
}
}
void KMyMoneyView::slotOpenObjectRequested(const MyMoneyObject& obj)
{
if (typeid(obj) == typeid(MyMoneyAccount)) {
const auto& acc = static_cast(obj);
// check if we can open this account
// currently it make's sense for asset and liability accounts
if (!MyMoneyFile::instance()->isStandardAccount(acc.id()))
m_ledgerView->slotLedgerSelected(acc.id(), QString());
} else if (typeid(obj) == typeid(MyMoneyInstitution)) {
// const auto& inst = static_cast(obj);
m_institutionsView->slotEditInstitution();
} else if (typeid(obj) == typeid(MyMoneySchedule)) {
m_scheduledView->slotEditSchedule();
} else if (typeid(obj) == typeid(MyMoneyReport)) {
const auto& rep = static_cast(obj);
m_reportsView->slotOpenReport(rep);
}
}
void KMyMoneyView::slotObjectSelected(const MyMoneyObject& obj)
{
// carrying some slots over to views isn't easy for all slots...
// ...so calls to kmymoney still must be here
if (typeid(obj) == typeid(MyMoneyAccount)) {
kmymoney->slotSelectAccount(obj);
m_investmentView->updateActions(obj);
m_categoriesView->updateActions(obj);
m_accountsView->updateActions(obj);
m_ledgerView->updateActions(obj);
m_reportsView->updateActions(obj);
if (m_onlineJobOutboxView) {
m_onlineJobOutboxView->updateActions(obj);
}
// for plugin only
const auto& acc = static_cast(obj);
if (!acc.isIncomeExpense() &&
!MyMoneyFile::instance()->isStandardAccount(acc.id()))
emit accountSelected(acc);
} else if (typeid(obj) == typeid(MyMoneyInstitution)) {
m_institutionsView->updateActions(obj);
} else if (typeid(obj) == typeid(MyMoneySchedule)) {
kmymoney->slotUpdateActions();
m_scheduledView->updateActions(obj);
}
}
void KMyMoneyView::slotContextMenuRequested(const MyMoneyObject& obj)
{
if (typeid(obj) == typeid(MyMoneyAccount)) {
const auto& acc = static_cast(obj);
if (acc.isInvest()) {
m_investmentView->slotShowInvestmentMenu(acc);
return;
} else if (acc.isIncomeExpense()) {
m_categoriesView->slotShowCategoriesMenu(acc);
} else {
m_accountsView->slotShowAccountMenu(acc);
}
} else if (typeid(obj) == typeid(MyMoneyInstitution)) {
const auto& inst = static_cast(obj);
m_institutionsView->slotShowInstitutionsMenu(inst);
} else if (typeid(obj) == typeid(MyMoneySchedule)) {
const auto& sch = static_cast(obj);
m_scheduledView->slotShowScheduleMenu(sch);
}
}
void KMyMoneyView::slotTransactionsMenuRequested(const KMyMoneyRegister::SelectedTransactions& list)
{
Q_UNUSED(list)
m_ledgerView->slotShowTransactionMenu(MyMoneySplit());
}
void KMyMoneyView::slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& list)
{
m_ledgerView->updateLedgerActions(list);
emit transactionsSelected(list); // for plugins
}