diff --git a/CMakeLists.txt b/CMakeLists.txt index da350fe5d..223719800 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,343 +1,345 @@ # The CMake version we require cmake_minimum_required(VERSION 3.1) # Setting the name of the main project project(KMyMoney VERSION "4.100.0") # Determine the GIT reference (if we're based on GIT) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") execute_process(COMMAND git rev-parse --short HEAD WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE VERSION_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) set(VERSION_SUFFIX "-${VERSION_SUFFIX}") # Add variables which are similar to the build in names of cmake set(PROJECT_VERSION_SUFFIX "${VERSION_SUFFIX}") set(${PROJECT_NAME}_VERSION_SUFFIX "${VERSION_SUFFIX}") endif() # Automoc all sources set(CMAKE_AUTOMOC TRUE) list(APPEND CMAKE_AUTOMOC_MACRO_NAMES "K_PLUGIN_FACTORY" "K_PLUGIN_FACTORY_WITH_JSON") if (POLICY CMP0063) cmake_policy(SET CMP0063 NEW) # Policy introduced in CMake version 3.3 endif() ######################### General Requirements ########################## find_package(ECM 0.0.11 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) include(KDEInstallDirs) include(KDECMakeSettings) include(FeatureSummary) include(GenerateExportHeader) include(KMyMoneyMacros) set(GPG_ENCRYPTION "no") set (OPT_KF5_COMPONENTS DocTools Holidays Contacts Akonadi IdentityManagement Activities Kross) find_package(Gpgmepp) if (Gpgmepp_FOUND) set(GPG_ENCRYPTION "yes") else() set(OPT_KF5_COMPONENTS ${OPT_KF5_COMPONENTS} Gpgmepp) endif() find_package(Qt5 5.6 REQUIRED COMPONENTS Core DBus Widgets Svg Sql Xml Test PrintSupport) find_package(KF5 5.2 REQUIRED COMPONENTS Archive CoreAddons Config ConfigWidgets I18n Completion KCMUtils ItemModels ItemViews Service Wallet IconThemes XmlGui TextWidgets Notifications KIO OPTIONAL_COMPONENTS ${OPT_KF5_COMPONENTS} ) find_package(LibAlkimia5 7.0.0 REQUIRED) find_package(KChart 2.6.0 REQUIRED) if(KF5Gpgmepp_FOUND) set(GPG_ENCRYPTION "yes") add_definitions(-DGpgmepp_FOUND) endif() add_definitions(-DQT_USE_QSTRINGBUILDER -DQT_NO_CAST_TO_ASCII -DQT_NO_URL_CAST_FROM_STRING) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # use DBus only on Linux if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(KMM_DBUS 1) endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) # check for Doxygen find_package(Doxygen) if(DOXYGEN_FOUND) set(APIDOC_DIR ${CMAKE_CURRENT_BINARY_DIR}/apidocs) make_directory("${APIDOC_DIR}") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmymoney.doxygen.in ${CMAKE_CURRENT_BINARY_DIR}/kmymoney.doxygen IMMEDIATE) add_custom_target(apidoc "${DOXYGEN}" "${CMAKE_CURRENT_BINARY_DIR}/kmymoney.doxygen") endif(DOXYGEN_FOUND) # check some include files exists set(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=500 -D_BSD_SOURCE) include (CheckIncludeFile) check_include_file("sys/types.h" HAVE_SYS_TYPES_H) check_include_file("unistd.h" HAVE_UNISTD_H) check_include_file("pwd.h" HAVE_PWD_H) check_include_file("windows.h" HAVE_WINDOWS_H) check_include_file("lmcons.h" HAVE_LMCONS_H) check_include_file("process.h" HAVE_PROCESS_H) # include check for members in structs include (CheckStructHasMember) ######################### Special Requirements ########################## # This is needed for QtSqlite and QtDesigner # (they'll install files to ${QT_INSTALL_DIR}/plugins/) get_filename_component(QT_BIN_DIR "${QT_MOC_EXECUTABLE}" PATH) get_filename_component(QT_DIR ${QT_BIN_DIR} PATH) set(QT_INSTALL_DIR ${QT_DIR} CACHE PATH "Qt install prefix defaults to the Qt prefix: ${QT_DIR}") if(KF5IdentityManagement_FOUND AND KF5Akonadi_FOUND AND KF5Contacts_FOUND) set(KMM_ADDRESSBOOK_FOUND true) endif() # check for optional QWebEngine option(ENABLE_WEBENGINE "Enable QWebEngine" OFF) if(ENABLE_WEBENGINE) find_package(Qt5WebEngineWidgets 5.8 REQUIRED) if(Qt5WebEngineWidgets_VERSION VERSION_GREATER 5.8.99 AND Qt5WebEngineWidgets_VERSION VERSION_LESS 5.9.3) message(WARNING "QWebEngine version ${Qt5WebEngineWidgets_VERSION} is known to be unstable with KMyMoney") endif() else(ENABLE_WEBENGINE) find_package(KF5WebKit REQUIRED) endif(ENABLE_WEBENGINE) # check for optional OFX support set(LIBOFX_DEFAULT "AUTO") -if(DEFINED ENABLE_LIBOFX) - set(LIBOFX_DEFAULT ${ENABLE_LIBOFX}) -endif(DEFINED ENABLE_LIBOFX) -option(ENABLE_LIBOFX "Enable OFX plugin" ON) -if(ENABLE_LIBOFX) +if(DEFINED ENABLE_OFXIMPORTER) + set(LIBOFX_DEFAULT ${ENABLE_OFXIMPORTER}) +endif(DEFINED ENABLE_OFXIMPORTER) + +if(ENABLE_OFXIMPORTER) find_package(LibOfx) if(NOT LIBOFX_FOUND) if(NOT LIBOFX_DEFAULT STREQUAL "AUTO") message(FATAL_ERROR "LibOFX not found") endif(NOT LIBOFX_DEFAULT STREQUAL "AUTO") - set(ENABLE_LIBOFX OFF CACHE BOOL "Enable OFX plugin" FORCE) + set(ENABLE_OFXIMPORTER OFF CACHE BOOL "Enable OFX plugin" FORCE) else(NOT LIBOFX_FOUND) check_struct_has_member("struct OfxFiLogin" "clientuid" "libofx/libofx.h" LIBOFX_HAVE_CLIENTUID) endif(NOT LIBOFX_FOUND) -endif(ENABLE_LIBOFX) +endif(ENABLE_OFXIMPORTER) # check for optional KBanking support set(KBANKING_FOUND "AUTO") mark_as_advanced(KBANKING_FOUND) if(DEFINED ENABLE_KBANKING) set(KBANKING_FOUND OFF) endif(DEFINED ENABLE_KBANKING) option(ENABLE_KBANKING "Enable KBanking plugin" ON) if(ENABLE_KBANKING) find_package(Qt5QuickWidgets) # Includes Qt5Qml find_package(AQBANKING 5.6.5) find_package(gwenhywfar 4.15.3) find_package(gwengui-cpp) find_package(gwengui-qt5) if (AQBANKING_FOUND AND gwengui-cpp_FOUND AND gwengui-qt5_FOUND AND Qt5QuickWidgets_FOUND) set(KBANKING_FOUND ON) else() if(NOT KBANKING_FOUND STREQUAL "AUTO") message(FATAL_ERROR "KBanking requirements not met") endif(NOT KBANKING_FOUND STREQUAL "AUTO") set(KBANKING_FOUND OFF) set(ENABLE_KBANKING OFF CACHE BOOL "Enable KBanking plugin" FORCE) endif () endif(ENABLE_KBANKING) # check for optional Weboob support set(WEBOOB_FOUND "AUTO") mark_as_advanced(WEBOOB_FOUND) if(DEFINED ENABLE_WEBOOB) set(WEBOOB_FOUND OFF) endif(DEFINED ENABLE_WEBOOB) option(ENABLE_WEBOOB "Enable weboob plugin" ON) if(ENABLE_WEBOOB) if(KF5Kross_FOUND) set(WEBOOB_FOUND ON) else(KF5Kross_FOUND) if(NOT WEBOOB_FOUND STREQUAL "AUTO") message(FATAL_ERROR "Weboob requirements not met") endif(NOT WEBOOB_FOUND STREQUAL "AUTO") set(WEBOOB_FOUND OFF) set(ENABLE_WEBOOB OFF CACHE BOOL "Enable weboob plugin" FORCE) endif(KF5Kross_FOUND) endif(ENABLE_WEBOOB) # check for optional ical support set(LIBICAL_DEFAULT "AUTO") if(DEFINED ENABLE_LIBICAL) set(LIBICAL_DEFAULT ${ENABLE_LIBICAL}) endif(DEFINED ENABLE_LIBICAL) option(ENABLE_LIBICAL "Enable Calendar plugin" ON) if(ENABLE_LIBICAL) find_package(Libical) if(NOT LIBICAL_FOUND) if(NOT LIBICAL_DEFAULT STREQUAL "AUTO") message(FATAL_ERROR "LIBICAL not found") endif(NOT LIBICAL_DEFAULT STREQUAL "AUTO") set(ENABLE_LIBICAL OFF CACHE BOOL "Enable Calendar plugin" FORCE) endif(NOT LIBICAL_FOUND) endif(ENABLE_LIBICAL) option(ENABLE_QIFIMPORTER "Enable QIF Importer" ON) option(ENABLE_QIFEXPORTER "Enable QIF Exporter" ON) option(ENABLE_GNCIMPORTER "Enable GNC Importer" ON) option(ENABLE_CSVIMPORTER "Enable CSV Importer" ON) option(ENABLE_CSVEXPORTER "Enable CSV Exporter" ON) +option(ENABLE_OFXIMPORTER "Enable OFX Importer" ON) + option(ENABLE_UNFINISHEDFEATURES "For devs only" OFF) # TODO: this should be removed enable_testing() ######################### Settings ########################## # If no build type is set, use "Release with Debug Info" if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo) endif(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Choose the type of build. Possible values are: 'Release' 'RelWithDebInfo' 'Debug' 'DebugKMM' 'Debugfull' 'Profile' The default value is: 'RelWithDebInfo'" FORCE) # tells gcc to enable exception handling include(KDECompilerSettings) kde_enable_exceptions() if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") endif() # IDEA: Set on a per target base set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") # be more pedantic about common symbols set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -Wextra") if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(IS_GNU 1) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wlogical-op") endif() # DebugKMM, Debugfull, Profile set(CMAKE_CXX_FLAGS_DEBUGKMM "-g -O2 -fno-reorder-blocks -fno-schedule-insns -fno-inline") set(CMAKE_CXX_FLAGS_DEBUGFULL "-g3 -fno-inline") set(CMAKE_CXX_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") # be pedantic about undefined symbols when linking shared libraries if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "kFreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "GNU") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined -Wl,--as-needed") endif() endif() # preprocessor definitions in case this is a debug build set(CMAKE_CXX_FLAGS_DEBUGFULL "${CMAKE_CXX_FLAGS_DEBUGFULL} -DQT_STRICT_ITERATORS -DKMM_DEBUG") set(CMAKE_CXX_FLAGS_DEBUGKMM "${CMAKE_CXX_FLAGS_DEBUGFULL} -DKMM_DEBUG") option(USE_MODELTEST "Compile with ModelTest code (default=OFF)" OFF) option(USE_QT_DESIGNER "Install KMyMoney specific widget library for Qt-Designer (default=OFF)" OFF) ######################### The Actual Targets ########################## add_subdirectory( libkgpgfile ) add_subdirectory( tools ) add_subdirectory( kmymoney ) if(KF5DocTools_FOUND) add_subdirectory( doc ) endif() ######################### Output Results ############################# # create the config.h file out of the config.h.cmake configure_file("config-kmymoney.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config-kmymoney.h") configure_file("config-kmymoney-version.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config-kmymoney-version.h") # this macro maps the boolean variable ${_varname} to "yes"/"no" # and writes the output to the variable nice_${_varname} macro(nice_yesno _varname) if(${_varname}) set("nice_${_varname}" "yes") else(${_varname}) set("nice_${_varname}" "no") endif(${_varname}) endmacro() nice_yesno("KF5Holidays_FOUND") nice_yesno("Gpgmepp_FOUND") nice_yesno("KMM_ADDRESSBOOK_FOUND") nice_yesno("LIBOFX_FOUND") nice_yesno("LIBOFX_HAVE_CLIENTUID") nice_yesno("KBANKING_FOUND") nice_yesno("WEBOOB_FOUND") nice_yesno("LIBICAL_FOUND") nice_yesno("ENABLE_SQLCIPHER") nice_yesno("USE_QT_DESIGNER") nice_yesno("USE_MODELTEST") nice_yesno("DOXYGEN_FOUND") nice_yesno("ENABLE_WEBENGINE") message(" -------- KMyMoney ${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX} -------- Configure results (user options): -------------------------------------------- GpgME Encryption: ${GPG_ENCRYPTION} KDE PIM holidays: ${nice_KF5Holidays_FOUND} KDE PIM addressbook: ${nice_KMM_ADDRESSBOOK_FOUND} OFX plugin: ${nice_LIBOFX_FOUND} OFX supports CLIENTUID: ${nice_LIBOFX_HAVE_CLIENTUID} KBanking plugin: ${nice_KBANKING_FOUND} weboob plugin: ${nice_WEBOOB_FOUND} iCalendar export plugin: ${nice_LIBICAL_FOUND} SQLCipher plugin: ${nice_ENABLE_SQLCIPHER} QWebEngine: ${nice_ENABLE_WEBENGINE} -------------------------------------------- Configure results (developer options): -------------------------------------------- Qt-Designer library support: ${nice_USE_QT_DESIGNER} Generate modeltest code: ${nice_USE_MODELTEST} Generate API documentation with Doxygen: ${nice_DOXYGEN_FOUND}") message(" Build type: ${CMAKE_BUILD_TYPE}") diff --git a/kmymoney/converter/CMakeLists.txt b/kmymoney/converter/CMakeLists.txt index 554f9b446..19786bcfc 100644 --- a/kmymoney/converter/CMakeLists.txt +++ b/kmymoney/converter/CMakeLists.txt @@ -1,29 +1,29 @@ if(BUILD_TESTING) add_subdirectory( tests ) endif() set (libconverter_a_SOURCES mymoneystatementreader.cpp mymoneytemplate.cpp webpricequote.cpp transactionmatchfinder.cpp existingtransactionmatchfinder.cpp scheduledtransactionmatchfinder.cpp ../widgets/kmymoneymoneyvalidator.cpp ) add_library(converter STATIC ${libconverter_a_SOURCES}) # TODO: clean dependencies -target_link_libraries(converter PUBLIC KF5::Service KF5::XmlGui KF5::Completion KF5::TextWidgets KF5::WidgetsAddons KF5::ConfigCore KF5::I18n Qt5::Widgets Qt5::Gui Qt5::Sql Qt5::Xml Alkimia::alkimia KF5::KIOWidgets kmm_csvimport_core +target_link_libraries(converter PUBLIC KF5::Service KF5::XmlGui KF5::Completion KF5::TextWidgets KF5::WidgetsAddons KF5::ConfigCore KF5::I18n Qt5::Widgets Qt5::Gui Qt5::Sql Qt5::Xml Alkimia::alkimia KF5::KIOWidgets kmm_csvimportercore PRIVATE newaccountwizard) # we rely on some dialogs to be generated add_dependencies(converter dialogs) ########### install files ############### install(FILES mymoneytemplate.h DESTINATION ${INCLUDE_INSTALL_DIR}/kmymoney COMPONENT Devel) diff --git a/kmymoney/converter/webpricequote.cpp b/kmymoney/converter/webpricequote.cpp index e9bfd1e9d..733d1bd8b 100644 --- a/kmymoney/converter/webpricequote.cpp +++ b/kmymoney/converter/webpricequote.cpp @@ -1,1242 +1,1242 @@ /*************************************************************************** webpricequote.cpp ------------------- begin : Thu Dec 30 2004 copyright : (C) 2004 by Ace Jones email : Ace Jones copyright : (C) 2017 by Łukasz Wojniłowicz email : Ł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 "webpricequote.h" // ---------------------------------------------------------------------------- // QT Headers #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Headers #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Headers #include "mymoneyexception.h" #include "mymoneyfile.h" #include "mymoneysecurity.h" Q_DECLARE_LOGGING_CATEGORY(WEBPRICEQUOTE) Q_LOGGING_CATEGORY(WEBPRICEQUOTE, "kmymoney_webpricequote") // define static members QString WebPriceQuote::m_financeQuoteScriptPath; QStringList WebPriceQuote::m_financeQuoteSources; class WebPriceQuote::Private { public: WebPriceQuoteProcess m_filter; QString m_quoteData; QString m_webID; QString m_kmmID; QDate m_date; QDate m_fromDate; QDate m_toDate; double m_price; WebPriceQuoteSource m_source; PricesProfile m_CSVSource; }; WebPriceQuote::WebPriceQuote(QObject* _parent): QObject(_parent), d(new Private) { // only do this once (I know, it is not thread safe, but it should // always yield the same result so we don't do any semaphore foo here) if (m_financeQuoteScriptPath.isEmpty()) { m_financeQuoteScriptPath = QStandardPaths::locate(QStandardPaths::DataLocation, QString("misc/financequote.pl")); } connect(&d->m_filter, SIGNAL(processExited(QString)), this, SLOT(slotParseQuote(QString))); } WebPriceQuote::~WebPriceQuote() { delete d; } void WebPriceQuote::setDate(const QDate& _from, const QDate& _to) { d->m_fromDate = _from; d->m_toDate = _to; } bool WebPriceQuote::launch(const QString& _webID, const QString& _kmmID, const QString& _sourcename) { if (_sourcename.contains("Finance::Quote")) return (launchFinanceQuote(_webID, _kmmID, _sourcename)); else if ((!d->m_fromDate.isValid() || !d->m_toDate.isValid()) || (d->m_fromDate == d->m_toDate && d->m_toDate == QDate::currentDate())) return (launchNative(_webID, _kmmID, _sourcename)); else return launchCSV(_webID, _kmmID, _sourcename); } bool WebPriceQuote::launchCSV(const QString& _webID, const QString& _kmmID, const QString& _sourcename) { d->m_webID = _webID; d->m_kmmID = _kmmID; // emit status(QString("(Debug) symbol=%1 id=%2...").arg(_symbol,_id)); // Get sources from the config file QString sourcename = _sourcename; if (sourcename.isEmpty()) return false; // for historical exchange rates we switch to Stooq if (sourcename == QLatin1String("KMyMoney Currency")) sourcename = QLatin1String("Stooq Currency"); if (quoteSources().contains(sourcename)) d->m_source = WebPriceQuoteSource(sourcename); else { emit error(i18n("Source %1 does not exist.", sourcename)); emit failed(d->m_kmmID, d->m_webID); return false; } int monthOffset = 0; if (sourcename.contains(QLatin1String("Yahoo"), Qt::CaseInsensitive)) monthOffset = -1; QUrl url; QString urlStr = d->m_source.m_csvUrl; int i = urlStr.indexOf(QLatin1String("%y")); if (i != -1) urlStr.replace(i, 2, QString().setNum(d->m_fromDate.year())); i = urlStr.indexOf(QLatin1String("%y")); if (i != -1) urlStr.replace(i, 2, QString().setNum(d->m_toDate.year())); i = urlStr.indexOf(QLatin1String("%m")); if (i != -1) urlStr.replace(i, 2, QString().setNum(d->m_fromDate.month() + monthOffset).rightJustified(2, QLatin1Char('0'))); i = urlStr.indexOf(QLatin1String("%m")); if (i != -1) urlStr.replace(i, 2, QString().setNum(d->m_toDate.month() + monthOffset).rightJustified(2, QLatin1Char('0'))); i = urlStr.indexOf(QLatin1String("%d")); if (i != -1) urlStr.replace(i, 2, QString().setNum(d->m_fromDate.day()).rightJustified(2, QLatin1Char('0'))); i = urlStr.indexOf(QLatin1String("%d")); if (i != -1) urlStr.replace(i, 2, QString().setNum(d->m_toDate.day()).rightJustified(2, QLatin1Char('0'))); if (urlStr.contains(QLatin1String("%y")) || urlStr.contains(QLatin1String("%m")) || urlStr.contains(QLatin1String("%d"))) { emit error(i18n("Cannot resolve input date.")); emit failed(d->m_kmmID, d->m_webID); return false; } bool isCurrency = false; if (urlStr.contains(QLatin1String("%2"))) { d->m_CSVSource.m_profileType = Profile::CurrencyPrices; isCurrency = true; } else d->m_CSVSource.m_profileType = Profile::StockPrices; d->m_CSVSource.m_profileName = sourcename; - if (!d->m_CSVSource.readSettings(CSVImporter::configFile())) { + if (!d->m_CSVSource.readSettings(CSVImporterCore::configFile())) { QMap result = defaultCSVQuoteSources(); d->m_CSVSource = result.value(sourcename); if (d->m_CSVSource.m_profileName.isEmpty()) { emit error(i18n("CSV source %1 does not exist.", sourcename)); emit failed(d->m_kmmID, d->m_webID); return false; } } if (isCurrency) { // this is a two-symbol quote. split the symbol into two. valid symbol // characters are: 0-9, A-Z and the dot. anything else is a separator QRegularExpression splitrx("([0-9a-z\\.]+)[^a-z0-9]+([0-9a-z\\.]+)", QRegularExpression::CaseInsensitiveOption); QRegularExpressionMatch match; // if we've truly found 2 symbols delimited this way... if (d->m_webID.indexOf(splitrx, 0, &match) != -1) { url = QUrl(urlStr.arg(match.captured(1), match.captured(2))); d->m_CSVSource.m_currencySymbol = match.captured(2); d->m_CSVSource.m_securitySymbol = match.captured(1); } else { qCDebug(WEBPRICEQUOTE) << "WebPriceQuote::launch() did not find 2 symbols"; emit error(i18n("Cannot find from and to currency.")); emit failed(d->m_kmmID, d->m_webID); return false; } } else { // a regular one-symbol quote url = QUrl(urlStr.arg(d->m_webID)); d->m_CSVSource.m_securityName = MyMoneyFile::instance()->security(d->m_kmmID).name(); d->m_CSVSource.m_securitySymbol = MyMoneyFile::instance()->security(d->m_kmmID).tradingSymbol(); } if (url.isLocalFile()) { emit error(i18n("Local quote sources aren't supported.")); emit failed(d->m_kmmID, d->m_webID); return false; } else { //silent download emit status(i18n("Fetching URL %1...", url.toDisplayString())); QString tmpFile; { QTemporaryFile tmpFileFile; tmpFileFile.setAutoRemove(false); if (tmpFileFile.open()) qDebug() << "created tmpfile"; tmpFile = tmpFileFile.fileName(); } QFile::remove(tmpFile); const QUrl dest = QUrl::fromLocalFile(tmpFile); KIO::Scheduler::checkSlaveOnHold(true); KIO::Job *job = KIO::file_copy(url, dest, -1, KIO::HideProgressInfo); connect(job, SIGNAL(result(KJob*)), this, SLOT(downloadCSV(KJob*))); } return true; } bool WebPriceQuote::launchNative(const QString& _webID, const QString& _kmmID, const QString& _sourcename) { d->m_webID = _webID; d->m_kmmID = _kmmID; if (_webID == i18n("[No identifier]")) { emit error(i18n("%1 skipped because it doesn't have identification number.", _kmmID)); emit failed(d->m_kmmID, d->m_webID); return false; } // emit status(QString("(Debug) symbol=%1 id=%2...").arg(_symbol,_id)); // Get sources from the config file QString sourcename = _sourcename; if (sourcename.isEmpty()) sourcename = "Yahoo"; if (quoteSources().contains(sourcename)) d->m_source = WebPriceQuoteSource(sourcename); else { emit error(i18n("Source %1 does not exist.", sourcename)); emit failed(d->m_kmmID, d->m_webID); return false; } QUrl url; // if the source has room for TWO symbols.. if (d->m_source.m_url.contains("%2")) { // this is a two-symbol quote. split the symbol into two. valid symbol // characters are: 0-9, A-Z and the dot. anything else is a separator QRegularExpression splitrx("([0-9a-z\\.]+)[^a-z0-9]+([0-9a-z\\.]+)", QRegularExpression::CaseInsensitiveOption); QRegularExpressionMatch match; // if we've truly found 2 symbols delimited this way... if (d->m_webID.indexOf(splitrx, 0, &match) != -1) { url = QUrl(d->m_source.m_url.arg(match.captured(1), match.captured(2))); } else { qCDebug(WEBPRICEQUOTE) << "WebPriceQuote::launch() did not find 2 symbols"; } } else { // a regular one-symbol quote url = QUrl(d->m_source.m_url.arg(d->m_webID)); } if (url.isLocalFile()) { emit status(i18nc("The process x is executing", "Executing %1...", url.toLocalFile())); QString program; QStringList arguments = url.toLocalFile().split(' ', QString::SkipEmptyParts); if (!arguments.isEmpty()) { program = arguments.first(); arguments.removeFirst(); } d->m_filter.setWebID(d->m_webID); d->m_filter.setProcessChannelMode(QProcess::MergedChannels); d->m_filter.start(program, arguments); if (!d->m_filter.waitForStarted()) { emit error(i18n("Unable to launch: %1", url.toLocalFile())); slotParseQuote(QString()); } } else { //silent download emit status(i18n("Fetching URL %1...", url.toDisplayString())); QString tmpFile; { QTemporaryFile tmpFileFile; tmpFileFile.setAutoRemove(false); if (tmpFileFile.open()) qDebug() << "created tmpfile"; tmpFile = tmpFileFile.fileName(); } QFile::remove(tmpFile); const QUrl dest = QUrl::fromLocalFile(tmpFile); KIO::Scheduler::checkSlaveOnHold(true); KIO::Job *job = KIO::file_copy(url, dest, -1, KIO::HideProgressInfo); connect(job, SIGNAL(result(KJob*)), this, SLOT(downloadResult(KJob*))); } return true; } void WebPriceQuote::downloadCSV(KJob* job) { QString tmpFile = dynamic_cast(job)->destUrl().toLocalFile(); QUrl url = dynamic_cast(job)->srcUrl(); if (!job->error()) { qDebug() << "Downloaded" << tmpFile << "from" << url; QFile f(tmpFile); if (f.open(QIODevice::ReadOnly)) { f.close(); slotParseCSVQuote(tmpFile); } else { emit error(i18n("Failed to open downloaded file")); slotParseCSVQuote(QString()); } } else { emit error(job->errorString()); slotParseCSVQuote(QString()); } } void WebPriceQuote::downloadResult(KJob* job) { QString tmpFile = dynamic_cast(job)->destUrl().toLocalFile(); QUrl url = dynamic_cast(job)->srcUrl(); if (!job->error()) { qDebug() << "Downloaded" << tmpFile << "from" << url; QFile f(tmpFile); if (f.open(QIODevice::ReadOnly)) { // Find out the page encoding and convert it to unicode QByteArray page = f.readAll(); KEncodingProber prober(KEncodingProber::Universal); prober.feed(page); QTextCodec* codec = QTextCodec::codecForName(prober.encoding()); if (!codec) codec = QTextCodec::codecForLocale(); QString quote = codec->toUnicode(page); f.close(); slotParseQuote(quote); } else { emit error(i18n("Failed to open downloaded file")); slotParseQuote(QString()); } QFile::remove(tmpFile); } else { emit error(job->errorString()); slotParseQuote(QString()); } } bool WebPriceQuote::launchFinanceQuote(const QString& _webID, const QString& _kmmID, const QString& _sourcename) { bool result = true; d->m_webID = _webID; d->m_kmmID = _kmmID; QString FQSource = _sourcename.section(' ', 1); d->m_source = WebPriceQuoteSource(_sourcename, m_financeQuoteScriptPath, m_financeQuoteScriptPath, "\"([^,\"]*)\",.*", // webIDRegExp WebPriceQuoteSource::identifyBy::Symbol, "[^,]*,[^,]*,\"([^\"]*)\"", // price regexp "[^,]*,([^,]*),.*", // date regexp "%y-%m-%d"); // date format //emit status(QString("(Debug) symbol=%1 id=%2...").arg(_symbol,_id)); QStringList arguments; arguments << m_financeQuoteScriptPath << FQSource << KShell::quoteArg(_webID); d->m_filter.setWebID(d->m_webID); emit status(i18nc("Executing 'script' 'online source' 'investment symbol' ", "Executing %1 %2 %3...", m_financeQuoteScriptPath, FQSource, _webID)); d->m_filter.setProcessChannelMode(QProcess::MergedChannels); d->m_filter.start(QLatin1Literal("perl"), arguments); // This seems to work best if we just block until done. if (d->m_filter.waitForFinished()) { result = true; } else { emit error(i18n("Unable to launch: %1", m_financeQuoteScriptPath)); slotParseQuote(QString()); } return result; } void WebPriceQuote::slotParseCSVQuote(const QString& filename) { bool isOK = true; if (filename.isEmpty()) isOK = false; else { MyMoneyStatement st; - CSVImporter* csvImporter = new CSVImporter; + CSVImporterCore* csvImporter = new CSVImporterCore; st = csvImporter->unattendedImport(filename, &d->m_CSVSource); if (!st.m_listPrices.isEmpty()) emit csvquote(d->m_kmmID, d->m_webID, st); else isOK = false; delete csvImporter; QFile::remove(filename); } if (!isOK) { emit error(i18n("Unable to update price for %1", d->m_webID)); emit failed(d->m_kmmID, d->m_webID); } } void WebPriceQuote::slotParseQuote(const QString& _quotedata) { QString quotedata = _quotedata; d->m_quoteData = quotedata; bool gotprice = false; bool gotdate = false; qCDebug(WEBPRICEQUOTE) << "quotedata" << _quotedata; if (! quotedata.isEmpty()) { if (!d->m_source.m_skipStripping) { // First, remove extranous non-data elements // HTML tags quotedata.remove(QRegularExpression("<[^>]*>")); // &...;'s quotedata.replace(QRegularExpression("&\\w+;"), QLatin1String(" ")); // Extra white space quotedata = quotedata.simplified(); qCDebug(WEBPRICEQUOTE) << "stripped text" << quotedata; } QRegularExpression webIDRegExp(d->m_source.m_webID); QRegularExpression dateRegExp(d->m_source.m_date); QRegularExpression priceRegExp(d->m_source.m_price); QRegularExpressionMatch match; if (quotedata.indexOf(webIDRegExp, 0, &match) > -1) { qCDebug(WEBPRICEQUOTE) << "Identifier" << match.captured(1); emit status(i18n("Identifier found: '%1'", match.captured(1))); } if (quotedata.indexOf(priceRegExp, 0, &match) > -1) { gotprice = true; // Deal with european quotes that come back as X.XXX,XX or XX,XXX // // We will make the assumption that ALL prices have a decimal separator. // So "1,000" always means 1.0, not 1000.0. // // Remove all non-digits from the price string except the last one, and // set the last one to a period. QString pricestr = match.captured(1); int pos = pricestr.lastIndexOf(QRegularExpression("\\D")); if (pos > 0) { pricestr[pos] = QLatin1Char('.'); pos = pricestr.lastIndexOf(QRegularExpression("\\D"), pos - 1); } while (pos > 0) { pricestr.remove(pos, 1); pos = pricestr.lastIndexOf(QRegularExpression("\\D"), pos); } d->m_price = pricestr.toDouble(); qCDebug(WEBPRICEQUOTE) << "Price" << pricestr; emit status(i18n("Price found: '%1' (%2)", pricestr, d->m_price)); } if (quotedata.indexOf(dateRegExp, 0, &match) > -1) { QString datestr = match.captured(1); MyMoneyDateFormat dateparse(d->m_source.m_dateformat); try { d->m_date = dateparse.convertString(datestr, false /*strict*/); gotdate = true; qCDebug(WEBPRICEQUOTE) << "Date" << datestr; emit status(i18n("Date found: '%1'", d->m_date.toString()));; } catch (const MyMoneyException &) { // emit error(i18n("Unable to parse date %1 using format %2: %3").arg(datestr,dateparse.format(),e.what())); d->m_date = QDate::currentDate(); gotdate = true; } } if (gotprice && gotdate) { emit quote(d->m_kmmID, d->m_webID, d->m_date, d->m_price); } else { emit error(i18n("Unable to update price for %1 (no price or no date)", d->m_webID)); emit failed(d->m_kmmID, d->m_webID); } } else { emit error(i18n("Unable to update price for %1 (empty quote data)", d->m_webID)); emit failed(d->m_kmmID, d->m_webID); } } const QMap WebPriceQuote::defaultCSVQuoteSources() { QMap result; // tip: possible delimiter indexes are in csvenums.h result[QLatin1String("Stooq")] = PricesProfile(QLatin1String("Stooq"), 106, 1, 0, DateFormat::YearMonthDay, FieldDelimiter::Semicolon, TextDelimiter::DoubleQuote, DecimalSymbol::Dot, QMap{{Column::Date, 0}, {Column::Price, 4}}, 2, Profile::StockPrices); result[QLatin1String("Stooq Currency")] = PricesProfile(QLatin1String("Stooq Currency"), 106, 1, 0, DateFormat::YearMonthDay, FieldDelimiter::Semicolon, TextDelimiter::DoubleQuote, DecimalSymbol::Dot, QMap{{Column::Date, 0}, {Column::Price, 4}}, 2, Profile::CurrencyPrices); result[QLatin1String("Yahoo")] = PricesProfile(QLatin1String("Yahoo"), 106, 1, 0, DateFormat::YearMonthDay, FieldDelimiter::Comma, TextDelimiter::DoubleQuote, DecimalSymbol::Dot, QMap{{Column::Date, 0}, {Column::Price, 4}}, 2, Profile::StockPrices); result[QLatin1String("Nasdaq Baltic - Shares")] = PricesProfile(QLatin1String("Nasdaq Baltic - Shares"), 106, 1, 0, DateFormat::DayMonthYear, FieldDelimiter::Tab, TextDelimiter::DoubleQuote, DecimalSymbol::Dot, QMap{{Column::Date, 0}, {Column::Price, 5}}, 2, Profile::StockPrices); result[QLatin1String("Nasdaq Baltic - Funds")] = PricesProfile(QLatin1String("Nasdaq Baltic - Funds"), 106, 1, 0, DateFormat::DayMonthYear, FieldDelimiter::Tab, TextDelimiter::DoubleQuote, DecimalSymbol::Dot, QMap{{Column::Date, 0}, {Column::Price, 5}}, 2, Profile::StockPrices); return result; } const QMap WebPriceQuote::defaultQuoteSources() { QMap result; // Use fx-rate.net as the standard currency exchange rate source until // we have the capability to use more than one source. Use a neutral // name for the source. result["KMyMoney Currency"] = WebPriceQuoteSource("KMyMoney Currency", "https://fx-rate.net/%1/%2", QString(), "https://fx-rate.net/([^/]+/[^/]+)", WebPriceQuoteSource::identifyBy::Symbol, "1\\s[^=]+\\s=\\s([^\\s]+)", "updated\\s\\d+:\\d+:\\d+\\(\\w+\\)\\s+(\\d{1,2}/\\d{2}/\\d{4})", "%d/%m/%y", true // skip HTML stripping ); // Update on 2017-06 by Łukasz Wojniłowicz result["Globe & Mail"] = WebPriceQuoteSource("Globe & Mail", "http://globefunddb.theglobeandmail.com/gishome/plsql/gis.price_history?pi_fund_id=%1", QString(), QString(), // webIDRegExp WebPriceQuoteSource::identifyBy::IdentificationNumber, "Fund Price:\\D+(\\d+\\.\\d+)", // priceregexp "Fund Price:.+as at (\\w+ \\d+, \\d+)\\)", // dateregexp "%m %d %y" // dateformat ); // Update on 2017-06 by Łukasz Wojniłowicz result["MSN"] = WebPriceQuoteSource("MSN", "http://www.msn.com/en-us/money/stockdetails/%1", QString(), QString(), // webIDRegExp WebPriceQuoteSource::identifyBy::Symbol, "(\\d+\\.\\d+) [+-]\\d+.\\d+", // priceregexp "(\\d+/\\d+/\\d+)", //dateregexp "%y %m %d" // dateformat ); // Finanztreff (replaces VWD.DE) supplied by Michael Zimmerman result["Finanztreff"] = WebPriceQuoteSource("Finanztreff", "http://finanztreff.de/kurse_einzelkurs_detail.htn?u=100&i=%1", "", QString(), // webIDRegExp WebPriceQuoteSource::identifyBy::IdentificationNumber, "([0-9]+,\\d+).+Gattung:Fonds", // priceregexp "\\).(\\d+\\D+\\d+\\D+\\d+)", // dateregexp (doesn't work; date in chart "%d.%m.%y" // dateformat ); // First revision by Michael Zimmerman // Update on 2017-06 by Łukasz Wojniłowicz result["boerseonlineaktien"] = WebPriceQuoteSource("Börse Online - Aktien", "http://www.boerse-online.de/aktie/%1-Aktie", QString(), QString(), // webIDRegExp WebPriceQuoteSource::identifyBy::Name, "Aktienkurs\\D+(\\d+,\\d{2})", // priceregexp "Datum (\\d{2}\\.\\d{2}\\.\\d{4})", // dateregexp "%d.%m.%y" // dateformat ); // This quote source provided by e-mail and should replace the previous one: // From: David Houlden // To: kmymoney@kde.org // Date: Sat, 6 Apr 2013 13:22:45 +0100 // Updated on 2017-06 by Łukasz Wojniłowicz result["Financial Times - UK Funds"] = WebPriceQuoteSource("Financial Times", "http://funds.ft.com/uk/Tearsheet/Summary?s=%1", QString(), QString(), // webIDRegExp WebPriceQuoteSource::identifyBy::IdentificationNumber, "Price\\D+([\\d,]*\\d+\\.\\d+)", // price regexp "Data delayed at least 15 minutes, as of\\ (.*)\\.", // date regexp "%m %d %y", // date format true // skip HTML stripping ); // The following two price sources were contributed by // Marc Zahnlecker result["Wallstreet-Online.DE (Default)"] = WebPriceQuoteSource("Wallstreet-Online.DE (Default)", "http://www.wallstreet-online.de/si/?k=%1&spid=ws", "", "Symbol:(\\w+)", // webIDRegExp WebPriceQuoteSource::identifyBy::Symbol, "Letzter Kurs: ([0-9.]+,\\d+)", // priceregexp ", (\\d+\\D+\\d+\\D+\\d+)", // dateregexp "%d %m %y" // dateformat ); // (tf2k) The "mpid" is I think the market place id. In this case five // stands for Hamburg. // // Here the id for several market places: 2 Frankfurt, 3 Berlin, 4 // Düsseldorf, 5 Hamburg, 6 München/Munich, 7 Hannover, 9 Stuttgart, 10 // Xetra, 32 NASDAQ, 36 NYSE result["Wallstreet-Online.DE (Hamburg)"] = WebPriceQuoteSource("Wallstreet-Online.DE (Hamburg)", "http://fonds.wallstreet-online.de/si/?k=%1&spid=ws&mpid=5", "", "Symbol:(\\w+)", // webIDRegExp WebPriceQuoteSource::identifyBy::Symbol, "Fonds \\(EUR\\) ([0-9.]+,\\d+)", // priceregexp ", (\\d+\\D+\\d+\\D+\\d+)", // dateregexp "%d %m %y" // dateformat ); // First revision on 2017-06 by Łukasz Wojniłowicz result["Puls Biznesu"] = WebPriceQuoteSource("Puls Biznesu", "http://notowania.pb.pl/instrument/%1", QString(), QString(), // webIDRegExp WebPriceQuoteSource::identifyBy::IdentificationNumber, "(\\d+,\\d{2})\\D+\\d+,\\d{2}%", // price regexp "(\\d{4}-\\d{2}-\\d{2}) \\d{2}:\\d{2}:\\d{2}", // date regexp "%y %m %d" // date format ); // The following price quote was contributed by // Piotr Adacha // I would like to post new Online Query Settings for KMyMoney. This set is // suitable to query stooq.com service, providing quotes for stocks, futures, // mutual funds and other financial instruments from Polish Gielda Papierow // Wartosciowych (GPW). Unfortunately, none of well-known international // services provide quotes for this market (biggest one in central and eastern // Europe), thus, I think it could be helpful for Polish users of KMyMoney (and // I am one of them for almost a year). // Update on 2017-06 by Łukasz Wojniłowicz result["Stooq"] = WebPriceQuoteSource("Stooq", "http://stooq.com/q/?s=%1", "http://stooq.pl/q/d/l/?s=%1&d1=%y%m%d&d2=%y%m%d&i=d&c=1", QString(), // webIDRegExp WebPriceQuoteSource::identifyBy::Symbol, "Last(\\d+\\.\\d+).*Date", // price regexp "(\\d{4,4}-\\d{2,2}-\\d{2,2})", // date regexp "%y %m %d" // date format ); // First revision on 2017-06 by Łukasz Wojniłowicz result[QLatin1String("Stooq Currency")] = WebPriceQuoteSource("Stooq Currency", "http://stooq.com/q/?s=%1%2", "http://stooq.pl/q/d/l/?s=%1%2&d1=%y%m%d&d2=%y%m%d&i=d&c=1", QString(), // webIDRegExp WebPriceQuoteSource::identifyBy::Symbol, "Last.*(\\d+\\.\\d+).*Date", // price regexp "(\\d{4,4}-\\d{2,2}-\\d{2,2})", // date regexp "%y %m %d" // date format ); // First revision on 2017-06 by Łukasz Wojniłowicz result["Nasdaq Baltic - Shares"] = WebPriceQuoteSource("Nasdaq Baltic - Shares", "http://www.nasdaqbaltic.com/market/?pg=details&instrument=%1&lang=en", "http://www.nasdaqbaltic.com/market/?instrument=%1&pg=details&tab=historical&lang=en&date=&start=%d.%m.%y&end=%d.%m.%y&pg=details&pg2=equity&downloadcsv=1&csv_style=english", QString(), // webIDRegExp WebPriceQuoteSource::identifyBy::IdentificationNumber, "lastPrice\\D+(\\d+,\\d+)", // priceregexp "as of: (\\d{2}.\\d{2}.\\d{4})", // dateregexp "%d.%m.%y", // dateformat true ); // First revision on 2017-06 by Łukasz Wojniłowicz result["Nasdaq Baltic - Funds"] = WebPriceQuoteSource("Nasdaq Baltic - Funds", "http://www.nasdaqbaltic.com/market/?pg=details&instrument=%1&lang=en", "http://www.nasdaqbaltic.com/market/?instrument=%1&pg=details&tab=historical&lang=en&date=&start=%d.%m.%y&end=%d.%m.%y&pg=details&pg2=equity&downloadcsv=1&csv_style=english", QString(), // webIDRegExp WebPriceQuoteSource::identifyBy::IdentificationNumber, "marketShareDetailTable(.+\\n){21}\\D+(\\d+)", // priceregexp "as of: (\\d{2}.\\d{2}.\\d{4})", // dateregexp "%d.%m.%y", // dateformat true ); return result; } const QStringList WebPriceQuote::quoteSources(const _quoteSystemE _system) { if (_system == Native) return (quoteSourcesNative()); else return (quoteSourcesFinanceQuote()); } const QStringList WebPriceQuote::quoteSourcesNative() { KSharedConfigPtr kconfig = KSharedConfig::openConfig(); QStringList groups = kconfig->groupList(); QStringList::Iterator it; QRegularExpression onlineQuoteSource(QString("^Online-Quote-Source-(.*)$")); QRegularExpressionMatch match; // get rid of all 'non online quote source' entries for (it = groups.begin(); it != groups.end(); it = groups.erase(it)) { if ((*it).indexOf(onlineQuoteSource, 0, &match) >= 0) { // Insert the name part it = groups.insert(it, match.captured(1)); ++it; } } // if the user has the OLD quote source defined, now is the // time to remove that entry and convert it to the new system. if (! groups.count() && kconfig->hasGroup("Online Quotes Options")) { KConfigGroup grp = kconfig->group("Online Quotes Options"); QString url(grp.readEntry("URL", "http://finance.yahoo.com/d/quotes.csv?s=%1&f=sl1d1")); QString webIDRegExp(grp.readEntry("SymbolRegex", "\"([^,\"]*)\",.*")); QString priceRegExp(grp.readEntry("PriceRegex", "[^,]*,([^,]*),.*")); QString dateRegExp(grp.readEntry("DateRegex", "[^,]*,[^,]*,\"([^\"]*)\"")); kconfig->deleteGroup("Online Quotes Options"); groups += "Old Source"; grp = kconfig->group(QString(QLatin1String("Online-Quote-Source-%1")).arg("Old Source")); grp.writeEntry("URL", url); grp.writeEntry("CSVURL", "http://finance.yahoo.com/d/quotes.csv?s=%1&f=sl1d1"); grp.writeEntry("IDRegex", webIDRegExp); grp.writeEntry("PriceRegex", priceRegExp); grp.writeEntry("DateRegex", dateRegExp); grp.writeEntry("DateFormatRegex", "%m %d %y"); grp.sync(); } // if the user has OLD quote source based only on symbols (and not ISIN) // now is the time to convert it to the new system. foreach (const auto group, groups) { KConfigGroup grp = kconfig->group(QString(QLatin1String("Online-Quote-Source-%1")).arg(group)); if (grp.hasKey("SymbolRegex")) { grp.writeEntry("IDRegex", grp.readEntry("SymbolRegex")); grp.deleteEntry("SymbolRegex"); } else break; } // Set up each of the default sources. These are done piecemeal so that // when we add a new source, it's automatically picked up. And any changes // are also picked up. QMap defaults = defaultQuoteSources(); QMap::const_iterator it_source = defaults.constBegin(); while (it_source != defaults.constEnd()) { if (! groups.contains((*it_source).m_name)) { groups += (*it_source).m_name; (*it_source).write(); kconfig->sync(); } ++it_source; } return groups; } const QStringList WebPriceQuote::quoteSourcesFinanceQuote() { if (m_financeQuoteSources.empty()) { // run the process one time only // since this is a static function it can be called without constructing an object // so we need to make sure that m_financeQuoteScriptPath is properly initialized if (m_financeQuoteScriptPath.isEmpty()) { m_financeQuoteScriptPath = QStandardPaths::locate(QStandardPaths::DataLocation, QString("misc/financequote.pl")); } FinanceQuoteProcess getList; getList.launch(m_financeQuoteScriptPath); while (!getList.isFinished()) { QCoreApplication::processEvents(); } m_financeQuoteSources = getList.getSourceList(); } return (m_financeQuoteSources); } // // Helper class to load/save an individual source // WebPriceQuoteSource::WebPriceQuoteSource(const QString& name, const QString& url, const QString &csvUrl, const QString& id, const identifyBy idBy, const QString& price, const QString& date, const QString& dateformat, bool skipStripping): m_name(name), m_url(url), m_csvUrl(csvUrl), m_webID(id), m_webIDBy(idBy), m_price(price), m_date(date), m_dateformat(dateformat), m_skipStripping(skipStripping) { } WebPriceQuoteSource::WebPriceQuoteSource(const QString& name) { m_name = name; KSharedConfigPtr kconfig = KSharedConfig::openConfig(); KConfigGroup grp = kconfig->group(QString("Online-Quote-Source-%1").arg(m_name)); m_webID = grp.readEntry("IDRegex"); m_webIDBy = static_cast(grp.readEntry("IDBy", "0").toInt()); m_date = grp.readEntry("DateRegex"); m_dateformat = grp.readEntry("DateFormatRegex", "%m %d %y"); m_price = grp.readEntry("PriceRegex"); m_url = grp.readEntry("URL"); m_csvUrl = grp.readEntry("CSVURL"); m_skipStripping = grp.readEntry("SkipStripping", false); } void WebPriceQuoteSource::write() const { KSharedConfigPtr kconfig = KSharedConfig::openConfig(); KConfigGroup grp = kconfig->group(QString("Online-Quote-Source-%1").arg(m_name)); grp.writeEntry("URL", m_url); grp.writeEntry("CSVURL", m_csvUrl); grp.writeEntry("PriceRegex", m_price); grp.writeEntry("DateRegex", m_date); grp.writeEntry("DateFormatRegex", m_dateformat); grp.writeEntry("IDRegex", m_webID); grp.writeEntry("IDBy", static_cast(m_webIDBy)); if (m_skipStripping) grp.writeEntry("SkipStripping", m_skipStripping); else grp.deleteEntry("SkipStripping"); kconfig->sync(); } void WebPriceQuoteSource::rename(const QString& name) { remove(); m_name = name; write(); } void WebPriceQuoteSource::remove() const { KSharedConfigPtr kconfig = KSharedConfig::openConfig(); kconfig->deleteGroup(QString("Online-Quote-Source-%1").arg(m_name)); kconfig->sync(); } // // Helper class to babysit the KProcess used for running the local script in that case // WebPriceQuoteProcess::WebPriceQuoteProcess() { connect(this, SIGNAL(readyReadStandardOutput()), this, SLOT(slotReceivedDataFromFilter())); connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotProcessExited(int,QProcess::ExitStatus))); } void WebPriceQuoteProcess::slotReceivedDataFromFilter() { // qDebug() << "WebPriceQuoteProcess::slotReceivedDataFromFilter(): " << QString(data); m_string += QString(readAllStandardOutput()); } void WebPriceQuoteProcess::slotProcessExited(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { // qDebug() << "WebPriceQuoteProcess::slotProcessExited()"; emit processExited(m_string); m_string.truncate(0); } // // Helper class to babysit the KProcess used for running the Finance Quote sources script // FinanceQuoteProcess::FinanceQuoteProcess() { m_isDone = false; m_string = ""; m_fqNames["aex"] = "AEX"; m_fqNames["aex_futures"] = "AEX Futures"; m_fqNames["aex_options"] = "AEX Options"; m_fqNames["amfiindia"] = "AMFI India"; m_fqNames["asegr"] = "ASE"; m_fqNames["asia"] = "Asia (Yahoo, ...)"; m_fqNames["asx"] = "ASX"; m_fqNames["australia"] = "Australia (ASX, Yahoo, ...)"; m_fqNames["bmonesbittburns"] = "BMO NesbittBurns"; m_fqNames["brasil"] = "Brasil (Yahoo, ...)"; m_fqNames["canada"] = "Canada (Yahoo, ...)"; m_fqNames["canadamutual"] = "Canada Mutual (Fund Library, ...)"; m_fqNames["deka"] = "Deka Investments"; m_fqNames["dutch"] = "Dutch (AEX, ...)"; m_fqNames["dwsfunds"] = "DWS"; m_fqNames["europe"] = "Europe (Yahoo, ...)"; m_fqNames["fidelity"] = "Fidelity (Fidelity, ...)"; m_fqNames["fidelity_direct"] = "Fidelity Direct"; m_fqNames["financecanada"] = "Finance Canada"; m_fqNames["ftportfolios"] = "First Trust (First Trust, ...)"; m_fqNames["ftportfolios_direct"] = "First Trust Portfolios"; m_fqNames["fundlibrary"] = "Fund Library"; m_fqNames["greece"] = "Greece (ASE, ...)"; m_fqNames["indiamutual"] = "India Mutual (AMFI, ...)"; m_fqNames["maninv"] = "Man Investments"; m_fqNames["fool"] = "Motley Fool"; m_fqNames["nasdaq"] = "Nasdaq (Yahoo, ...)"; m_fqNames["nz"] = "New Zealand (Yahoo, ...)"; m_fqNames["nyse"] = "NYSE (Yahoo, ...)"; m_fqNames["nzx"] = "NZX"; m_fqNames["platinum"] = "Platinum Asset Management"; m_fqNames["seb_funds"] = "SEB"; m_fqNames["sharenet"] = "Sharenet"; m_fqNames["za"] = "South Africa (Sharenet, ...)"; m_fqNames["troweprice_direct"] = "T. Rowe Price"; m_fqNames["troweprice"] = "T. Rowe Price"; m_fqNames["tdefunds"] = "TD Efunds"; m_fqNames["tdwaterhouse"] = "TD Waterhouse Canada"; m_fqNames["tiaacref"] = "TIAA-CREF"; m_fqNames["trustnet"] = "Trustnet"; m_fqNames["uk_unit_trusts"] = "U.K. Unit Trusts"; m_fqNames["unionfunds"] = "Union Investments"; m_fqNames["tsp"] = "US Govt. Thrift Savings Plan"; m_fqNames["usfedbonds"] = "US Treasury Bonds"; m_fqNames["usa"] = "USA (Yahoo, Fool ...)"; m_fqNames["vanguard"] = "Vanguard"; m_fqNames["vwd"] = "VWD"; m_fqNames["yahoo"] = "Yahoo"; m_fqNames["yahoo_asia"] = "Yahoo Asia"; m_fqNames["yahoo_australia"] = "Yahoo Australia"; m_fqNames["yahoo_brasil"] = "Yahoo Brasil"; m_fqNames["yahoo_europe"] = "Yahoo Europe"; m_fqNames["yahoo_nz"] = "Yahoo New Zealand"; m_fqNames["zifunds"] = "Zuerich Investments"; connect(this, SIGNAL(readyReadStandardOutput()), this, SLOT(slotReceivedDataFromFilter())); connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotProcessExited())); connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(slotProcessExited())); } void FinanceQuoteProcess::slotReceivedDataFromFilter() { QByteArray data(readAllStandardOutput()); // qDebug() << "WebPriceQuoteProcess::slotReceivedDataFromFilter(): " << QString(data); m_string += QString(data); } void FinanceQuoteProcess::slotProcessExited() { // qDebug() << "WebPriceQuoteProcess::slotProcessExited()"; m_isDone = true; } void FinanceQuoteProcess::launch(const QString& scriptPath) { QStringList arguments; arguments << scriptPath << QLatin1Literal("-l"); setProcessChannelMode(QProcess::ForwardedOutputChannel); start(QLatin1Literal("perl"), arguments); if (! waitForStarted()) qWarning("Unable to start FQ script"); return; } const QStringList FinanceQuoteProcess::getSourceList() const { QStringList raw = m_string.split(0x0A, QString::SkipEmptyParts); QStringList sources; QStringList::iterator it; for (it = raw.begin(); it != raw.end(); ++it) { if (m_fqNames[*it].isEmpty()) sources.append(*it); else sources.append(m_fqNames[*it]); } sources.sort(); return (sources); } const QString FinanceQuoteProcess::crypticName(const QString& niceName) const { QString ret(niceName); fqNameMap::const_iterator it; for (it = m_fqNames.begin(); it != m_fqNames.end(); ++it) { if (niceName == it.value()) { ret = it.key(); break; } } return (ret); } const QString FinanceQuoteProcess::niceName(const QString& crypticName) const { QString ret(m_fqNames[crypticName]); if (ret.isEmpty()) ret = crypticName; return (ret); } // // Universal date converter // // In 'strict' mode, this is designed to be compatable with the QIF profile date // converter. However, that converter deals with the concept of an apostrophe // format in a way I don't understand. So for the moment, they are 99% // compatable, waiting on that issue. (acejones) const QDate MyMoneyDateFormat::convertString(const QString& _in, bool _strict, unsigned _centurymidpoint) const { // // Break date format string into component parts // QRegularExpression formatrex("%([mdy]+)(\\W+)%([mdy]+)(\\W+)%([mdy]+)", QRegularExpression::CaseInsensitiveOption); QRegularExpressionMatch match; if (m_format.indexOf(formatrex, 0, &match) == -1) { throw MYMONEYEXCEPTION("Invalid format string"); } QStringList formatParts; formatParts += match.captured(1); formatParts += match.captured(3); formatParts += match.captured(5); QStringList formatDelimiters; formatDelimiters += match.captured(2); formatDelimiters += match.captured(4); match = QRegularExpressionMatch(); // // Break input string up into component parts, // using the delimiters found in the format string // QRegularExpression inputrex; inputrex.setPatternOptions(QRegularExpression::CaseInsensitiveOption); // strict mode means we must enforce the delimiters as specified in the // format. non-strict allows any delimiters if (_strict) inputrex.setPattern(QString("(\\w+)%1(\\w+)%2(\\w+)").arg(formatDelimiters[0], formatDelimiters[1])); else inputrex.setPattern("(\\w+)\\W+(\\w+)\\W+(\\w+)"); if (_in.indexOf(inputrex, 0, &match) == -1) { throw MYMONEYEXCEPTION("Invalid input string"); } QStringList scannedParts; scannedParts += match.captured(1).toLower(); scannedParts += match.captured(2).toLower(); scannedParts += match.captured(3).toLower(); match = QRegularExpressionMatch(); // // Convert the scanned parts into actual date components // unsigned day = 0, month = 0, year = 0; bool ok; QRegularExpression digitrex("(\\d+)"); QStringList::const_iterator it_scanned = scannedParts.constBegin(); QStringList::const_iterator it_format = formatParts.constBegin(); while (it_scanned != scannedParts.constEnd()) { // decide upon the first character of the part switch ((*it_format).at(0).cell()) { case 'd': // remove any extraneous non-digits (e.g. read "3rd" as 3) ok = false; if ((*it_scanned).indexOf(digitrex, 0, &match) != -1) day = match.captured(1).toUInt(&ok); if (!ok || day > 31) throw MYMONEYEXCEPTION(QString("Invalid day entry: %1").arg(*it_scanned)); break; case 'm': month = (*it_scanned).toUInt(&ok); if (!ok) { month = 0; // maybe it's a textual date unsigned i = 1; // search the name in the current selected locale QLocale locale; while (i <= 12) { if (locale.standaloneMonthName(i).toLower() == *it_scanned || locale.standaloneMonthName(i, QLocale::ShortFormat).toLower() == *it_scanned) { month = i; break; } ++i; } // in case we did not find the month in the current locale, // we look for it in the C locale if(month == 0) { QLocale localeC(QLocale::C); if( !(locale == localeC)) { i = 1; while (i <= 12) { if (localeC.standaloneMonthName(i).toLower() == *it_scanned || localeC.standaloneMonthName(i, QLocale::ShortFormat).toLower() == *it_scanned) { month = i; break; } ++i; } } } } if (month < 1 || month > 12) throw MYMONEYEXCEPTION(QString("Invalid month entry: %1").arg(*it_scanned)); break; case 'y': if (_strict && (*it_scanned).length() != (*it_format).length()) throw MYMONEYEXCEPTION(QString("Length of year (%1) does not match expected length (%2).") .arg(*it_scanned, *it_format)); year = (*it_scanned).toUInt(&ok); if (!ok) throw MYMONEYEXCEPTION(QString("Invalid year entry: %1").arg(*it_scanned)); // // 2-digit year case // // this algorithm will pick a year within +/- 50 years of the // centurymidpoint parameter. i.e. if the midpoint is 2000, // then 0-49 will become 2000-2049, and 50-99 will become 1950-1999 if (year < 100) { unsigned centuryend = _centurymidpoint + 50; unsigned centurybegin = _centurymidpoint - 50; if (year < centuryend % 100) year += 100; year += centurybegin - centurybegin % 100; } if (year < 1900) throw MYMONEYEXCEPTION(QString("Invalid year (%1)").arg(year)); break; default: throw MYMONEYEXCEPTION("Invalid format character"); } ++it_scanned; ++it_format; } QDate result(year, month, day); if (! result.isValid()) throw MYMONEYEXCEPTION(QString("Invalid date (yr%1 mo%2 dy%3)").arg(year).arg(month).arg(day)); return result; } // // Unit test helpers // convertertest::QuoteReceiver::QuoteReceiver(WebPriceQuote* q, QObject* parent) : QObject(parent) { connect(q, SIGNAL(quote(QString,QString,QDate,double)), this, SLOT(slotGetQuote(QString,QString,QDate,double))); connect(q, SIGNAL(status(QString)), this, SLOT(slotStatus(QString))); connect(q, SIGNAL(error(QString)), this, SLOT(slotError(QString))); } convertertest::QuoteReceiver::~QuoteReceiver() { } void convertertest::QuoteReceiver::slotGetQuote(const QString&, const QString&, const QDate& d, const double& m) { // qDebug() << "test::QuoteReceiver::slotGetQuote( , " << d << " , " << m.toString() << " )"; m_price = MyMoneyMoney(m); m_date = d; } void convertertest::QuoteReceiver::slotStatus(const QString& msg) { // qDebug() << "test::QuoteReceiver::slotStatus( " << msg << " )"; m_statuses += msg; } void convertertest::QuoteReceiver::slotError(const QString& msg) { // qDebug() << "test::QuoteReceiver::slotError( " << msg << " )"; m_errors += msg; } // leave this moc until we will have resolved our dependency issues // now 'converter' depends on 'kmymoney' a pointer to the application // defined in main.cpp, which makes this static library unusable without // the --as-needed linker flag; // otherwise the 'moc' methods of this object will be linked into the automoc // object file which contains methods from all the other objects from this // library, thus even if the --as-needed option is used all objects will be // pulled in while linking 'convertertest' which only needs the WebPriceQuote // object - spent a whole day investigating this #include "moc_webpricequote.cpp" diff --git a/kmymoney/converter/webpricequote.h b/kmymoney/converter/webpricequote.h index b73a3c80e..b21e713ff 100644 --- a/kmymoney/converter/webpricequote.h +++ b/kmymoney/converter/webpricequote.h @@ -1,246 +1,246 @@ /*************************************************************************** webpricequote.h ------------------- begin : Thu Dec 30 2004 copyright : (C) 2004 by Ace Jones email : Ace Jones ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef WEBPRICEQUOTE_H #define WEBPRICEQUOTE_H // ---------------------------------------------------------------------------- // QT Headers #include #include // ---------------------------------------------------------------------------- // KDE Headers // ---------------------------------------------------------------------------- // Project Headers -#include "csv/import/core/csvimporter.h" +#include "csv/import/core/csvimportercore.h" class KJob; class QDate; class QTextCodec; /** Helper class to attend the process which is running the script, in the case of a local script being used to fetch the quote. @author Thomas Baumgart & Ace Jones */ class WebPriceQuoteProcess: public QProcess { Q_OBJECT public: WebPriceQuoteProcess(); inline void setWebID(const QString& _webID) { m_webID = _webID; m_string.truncate(0); } public Q_SLOTS: void slotReceivedDataFromFilter(); void slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus); Q_SIGNALS: void processExited(const QString&); private: QString m_webID; QString m_string; }; /** Helper class to run the Finance::Quote process. This is used only for the purpose of obtaining a list of valid sources. The actual price quotes are obtained thru WebPriceQuoteProcess. The class also contains functions to convert between the rather cryptic source names used by the Finance::Quote package, and more user-friendly names. @author Thomas Baumgart & Ace Jones , Tony B */ class FinanceQuoteProcess: public QProcess { Q_OBJECT public: FinanceQuoteProcess(); void launch(const QString& scriptPath); bool isFinished() const { return(m_isDone); }; const QStringList getSourceList() const; const QString crypticName(const QString& niceName) const; const QString niceName(const QString& crypticName) const; public Q_SLOTS: void slotReceivedDataFromFilter(); void slotProcessExited(); private: bool m_isDone; QString m_string; typedef QMap fqNameMap; fqNameMap m_fqNames; }; /** * @author Thomas Baumgart & Ace Jones * * This is a helper class to store information about an online source * for stock prices or currency exchange rates. */ struct WebPriceQuoteSource { enum identifyBy {Symbol, IdentificationNumber, Name}; WebPriceQuoteSource() : m_skipStripping(false) {} explicit WebPriceQuoteSource(const QString& name); WebPriceQuoteSource(const QString& name, const QString& url, const QString& csvUrl, const QString& id, const identifyBy idBy, const QString& price, const QString& date, const QString& dateformat, bool skipStripping = false); ~WebPriceQuoteSource() {} void write() const; void rename(const QString& name); void remove() const; QString m_name; QString m_url; QString m_csvUrl; QString m_webID; identifyBy m_webIDBy; QString m_price; QString m_date; QString m_dateformat; bool m_skipStripping; }; /** Retrieves a price quote from a web-based quote source @author Ace Jones */ class WebPriceQuote: public QObject { Q_OBJECT public: explicit WebPriceQuote(QObject* = 0); ~WebPriceQuote(); typedef enum _quoteSystemE { Native = 0, FinanceQuote } quoteSystemE; void setDate(const QDate& _from, const QDate& _to); /** * This launches a web-based quote update for the given @p _webID. * When the quote is received back from the web source, it will be * emitted on the 'quote' signal. * * @param _webID the identification of the stock to fetch a price for * @param _kmmID an arbitrary identifier, which will be emitted in the quote * signal when a price is sent back. * @param _source the source of the quote (must be a valid value returned * by quoteSources(). Send QString() to use the default * source. * @return bool Whether the quote fetch process was launched successfully */ bool launch(const QString& _webID, const QString& _kmmID, const QString& _source = QString()); /** * This returns a list of the names of the quote sources * currently defined. * * @param _system whether to return Native or Finance::Quote source list * @return QStringList of quote source names */ static const QStringList quoteSources(const _quoteSystemE _system = Native); static const QMap defaultCSVQuoteSources(); Q_SIGNALS: void csvquote(const QString&, const QString&, MyMoneyStatement&); void quote(const QString&, const QString&, const QDate&, const double&); void failed(const QString&, const QString&); void status(const QString&); void error(const QString&); protected Q_SLOTS: void slotParseCSVQuote(const QString& filename); void slotParseQuote(const QString&); void downloadCSV(KJob* job); void downloadResult(KJob* job); protected: static const QMap defaultQuoteSources(); private: bool launchCSV(const QString& _webID, const QString& _kmmID, const QString& _source = QString()); bool launchNative(const QString& _webID, const QString& _kmmID, const QString& _source = QString()); bool launchFinanceQuote(const QString& _webID, const QString& _kmmID, const QString& _source = QString()); void enter_loop(); static const QStringList quoteSourcesNative(); static const QStringList quoteSourcesFinanceQuote(); private: /// \internal d-pointer class. class Private; /// \internal d-pointer instance. Private* const d; static QString m_financeQuoteScriptPath; static QStringList m_financeQuoteSources; }; class MyMoneyDateFormat { public: explicit MyMoneyDateFormat(const QString& _format): m_format(_format) {} const QString convertDate(const QDate& _in) const; const QDate convertString(const QString& _in, bool _strict = true, unsigned _centurymidpoint = QDate::currentDate().year()) const; const QString& format() const { return m_format; } private: QString m_format; }; namespace convertertest { /** Simple class to handle signals/slots for unit tests @author Ace Jones */ class QuoteReceiver : public QObject { Q_OBJECT public: explicit QuoteReceiver(WebPriceQuote* q, QObject *parent = 0); ~QuoteReceiver(); public Q_SLOTS: void slotGetQuote(const QString&, const QString&, const QDate&, const double&); void slotStatus(const QString&); void slotError(const QString&); public: QStringList m_statuses; QStringList m_errors; MyMoneyMoney m_price; QDate m_date; }; } // end namespace convertertest #endif // WEBPRICEQUOTE_H diff --git a/kmymoney/dialogs/settings/CMakeLists.txt b/kmymoney/dialogs/settings/CMakeLists.txt index 31217d941..9d8256353 100644 --- a/kmymoney/dialogs/settings/CMakeLists.txt +++ b/kmymoney/dialogs/settings/CMakeLists.txt @@ -1,53 +1,54 @@ set (libsettings_a_SOURCES ksettingscolors.cpp ksettingsfonts.cpp ksettingsicons.cpp ksettingsforecast.cpp ksettingsgeneral.cpp ksettingsgpg.cpp ksettingshome.cpp ksettingsonlinequotes.cpp ksettingsregister.cpp ksettingsschedules.cpp ksettingsreports.cpp ksettingskmymoney.cpp + ksettingsplugins.cpp ) set (libsettings_a_UI ksettingscolors.ui ksettingsfonts.ui ksettingsicons.ui ksettingsforecast.ui ksettingsgeneral.ui ksettingsgpg.ui ksettingshome.ui ksettingsonlinequotes.ui ksettingsregister.ui ksettingsschedules.ui ksettingsreports.ui ) ki18n_wrap_ui(libsettings_a_SOURCES ${libsettings_a_UI} ) add_library(settings STATIC ${libsettings_a_SOURCES}) # TODO: cleanup dependencies target_link_libraries(settings PUBLIC KF5::Completion KF5::KIOWidgets KF5::TextWidgets KF5::I18n KF5::IconThemes KF5::Completion KF5::KCMUtils KF5::ItemViews Alkimia::alkimia Qt5::Sql ) if (KF5Holidays_FOUND) target_link_libraries(settings PUBLIC KF5::Holidays) endif() add_dependencies(settings widgets kmm_config) diff --git a/kmymoney/dialogs/settings/ksettingskmymoney.cpp b/kmymoney/dialogs/settings/ksettingskmymoney.cpp index 7a0ef4112..e97c3bb04 100644 --- a/kmymoney/dialogs/settings/ksettingskmymoney.cpp +++ b/kmymoney/dialogs/settings/ksettingskmymoney.cpp @@ -1,83 +1,92 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2016 Christian Dávid * (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. * * 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 "ksettingskmymoney.h" #include -#include #include #include "ksettingsgeneral.h" #include "ksettingsregister.h" #include "ksettingsgpg.h" #include "ksettingscolors.h" #include "ksettingsfonts.h" #include "ksettingsicons.h" #include "ksettingsschedules.h" #include "ksettingsonlinequotes.h" #include "ksettingshome.h" #include "ksettingsforecast.h" #include "ksettingsreports.h" +#include "ksettingsplugins.h" -#include "pluginloader.h" -#include "icons/icons.h" +#include "icons.h" using namespace Icons; KSettingsKMyMoney::KSettingsKMyMoney(QWidget *parent, const QString &name, KCoreConfigSkeleton *config) : KConfigDialog(parent, name, config) { // create the pages ... const auto generalPage = new KSettingsGeneral(); const auto registerPage = new KSettingsRegister(); const auto homePage = new KSettingsHome(); const auto schedulesPage = new KSettingsSchedules(); const auto encryptionPage = new KSettingsGpg(); const auto colorsPage = new KSettingsColors(); const auto fontsPage = new KSettingsFonts(); const auto iconsPage = new KSettingsIcons(); const auto onlineQuotesPage = new KSettingsOnlineQuotes(); const auto forecastPage = new KSettingsForecast(); - const auto pluginsPage = KMyMoneyPlugin::PluginLoader::instance()->pluginSelectorWidget(); const auto reportsPage = new KSettingsReports(); + const auto pluginsPage = new KSettingsPlugins(); addPage(generalPage, i18nc("General settings", "General"), Icons::get(Icon::SystemRun).name()); addPage(homePage, i18n("Home"), Icons::get(Icon::ViewHome).name()); addPage(registerPage, i18nc("Ledger view settings", "Ledger"), Icons::get(Icon::ViewFinancialList).name()); addPage(schedulesPage, i18n("Scheduled transactions"), Icons::get(Icon::ViewSchedules).name()); addPage(onlineQuotesPage, i18n("Online Quotes"), Icons::get(Icon::PreferencesNetwork).name()); addPage(reportsPage, i18nc("Report settings", "Reports"), Icons::get(Icon::ViewReports).name()); addPage(forecastPage, i18nc("Forecast settings", "Forecast"), Icons::get(Icon::ViewForecast).name()); addPage(encryptionPage, i18n("Encryption"), Icons::get(Icon::Kgpg).name()); addPage(colorsPage, i18n("Colors"), Icons::get(Icon::PreferencesColor).name()); addPage(fontsPage, i18n("Fonts"), Icons::get(Icon::PreferencesFont).name()); addPage(iconsPage, i18n("Icons"), Icons::get(Icon::PreferencesIcon).name()); - addPage(pluginsPage, i18n("Plugins"), Icons::get(Icon::NetworkDisconect).name()); + addPage(pluginsPage, i18n("Plugins"), Icons::get(Icon::NetworkDisconect).name(), QString(), false); setHelp("details.settings", "kmymoney"); - auto defaultButton = button(QDialogButtonBox::RestoreDefaults); connect(this, &KConfigDialog::rejected, schedulesPage, &KSettingsSchedules::slotResetRegion); connect(this, &KConfigDialog::rejected, iconsPage, &KSettingsIcons::slotResetTheme); connect(this, &KConfigDialog::settingsChanged, generalPage, &KSettingsGeneral::slotUpdateEquitiesVisibility); - connect(this, &KConfigDialog::accepted, pluginsPage, &KPluginSelector::save); - connect(defaultButton, &QPushButton::clicked, pluginsPage, &KPluginSelector::defaults); + auto defaultButton = button(QDialogButtonBox::RestoreDefaults); + auto applyButton = button(QDialogButtonBox::Apply); + connect(this, &KConfigDialog::accepted, pluginsPage, &KSettingsPlugins::slotSavePluginConfiguration); + connect(applyButton, &QPushButton::clicked, pluginsPage, &KSettingsPlugins::slotSavePluginConfiguration); + connect(defaultButton, &QPushButton::clicked, pluginsPage, &KSettingsPlugins::slotResetToDefaults); + connect(pluginsPage, &KSettingsPlugins::changed, this, &KSettingsKMyMoney::slotPluginsChanged); + connect(pluginsPage, &KSettingsPlugins::settingsChanged, this, &KConfigDialog::settingsChanged); +} + +void KSettingsKMyMoney::slotPluginsChanged(bool changed) +{ + auto applyButton = button(QDialogButtonBox::Apply); + applyButton->setEnabled(changed); } diff --git a/kmymoney/dialogs/settings/ksettingskmymoney.h b/kmymoney/dialogs/settings/ksettingskmymoney.h index 365cc7e7d..b283e73af 100644 --- a/kmymoney/dialogs/settings/ksettingskmymoney.h +++ b/kmymoney/dialogs/settings/ksettingskmymoney.h @@ -1,35 +1,41 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2016 Christian Dávid * (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. * * 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 . */ #ifndef KSETTINGSKMYMONEY_H #define KSETTINGSKMYMONEY_H #include /** * @brief The general settings dialog */ class KSettingsKMyMoney : public KConfigDialog { public: explicit KSettingsKMyMoney(QWidget *parent, const QString &name, KCoreConfigSkeleton *config); + +Q_SIGNALS: + void pluginsChanged(); + +private Q_SLOTS: + void slotPluginsChanged(bool changed); }; #endif /* KSETTINGSKMYMONEY_H */ diff --git a/kmymoney/dialogs/settings/ksettingsonlinequotes.cpp b/kmymoney/dialogs/settings/ksettingsonlinequotes.cpp index 827d43eb7..ef02fc008 100644 --- a/kmymoney/dialogs/settings/ksettingsonlinequotes.cpp +++ b/kmymoney/dialogs/settings/ksettingsonlinequotes.cpp @@ -1,376 +1,376 @@ /*************************************************************************** ksettingsonlinequotes.cpp ------------------- begin : Thu Dec 30 2004 copyright : (C) 2004 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 "ksettingsonlinequotes.h" // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_ksettingsonlinequotes.h" #include "kmymoney/converter/webpricequote.h" #include "mymoneyfile.h" #include "mymoneysecurity.h" #include "icons/icons.h" using namespace Icons; class KSettingsOnlineQuotesPrivate { Q_DISABLE_COPY(KSettingsOnlineQuotesPrivate) public: KSettingsOnlineQuotesPrivate() : ui(new Ui::KSettingsOnlineQuotes) { } ~KSettingsOnlineQuotesPrivate() { delete ui; } Ui::KSettingsOnlineQuotes *ui; QList m_resetList; WebPriceQuoteSource m_currentItem; bool m_quoteInEditing; }; KSettingsOnlineQuotes::KSettingsOnlineQuotes(QWidget *parent) : QWidget(parent), d_ptr(new KSettingsOnlineQuotesPrivate) { Q_D(KSettingsOnlineQuotes); d->ui->setupUi(this); QStringList groups = WebPriceQuote::quoteSources(); loadList(true /*updateResetList*/); d->ui->m_updateButton->setEnabled(false); d->ui->m_updateButton->setIcon(Icons::get(Icon::DialogOK)); d->ui->m_deleteButton->setIcon(Icons::get(Icon::EditDelete)); d->ui->m_newButton->setIcon(Icons::get(Icon::DocumentNew)); d->ui->m_editIdentifyBy->addItem(i18n("Symbol"), WebPriceQuoteSource::identifyBy::Symbol); d->ui->m_editIdentifyBy->addItem(i18n("Identification number"), WebPriceQuoteSource::identifyBy::IdentificationNumber); d->ui->m_editIdentifyBy->addItem(i18n("Name"), WebPriceQuoteSource::identifyBy::Name); connect(d->ui->m_dumpCSVProfile, &QAbstractButton::clicked, this, &KSettingsOnlineQuotes::slotDumpCSVProfile); connect(d->ui->m_updateButton, &QAbstractButton::clicked, this, &KSettingsOnlineQuotes::slotUpdateEntry); connect(d->ui->m_newButton, &QAbstractButton::clicked, this, &KSettingsOnlineQuotes::slotNewEntry); connect(d->ui->m_deleteButton, &QAbstractButton::clicked, this, &KSettingsOnlineQuotes::slotDeleteEntry); connect(d->ui->m_quoteSourceList, &QListWidget::itemSelectionChanged, this, &KSettingsOnlineQuotes::slotLoadWidgets); connect(d->ui->m_quoteSourceList, &QListWidget::itemChanged, this, &KSettingsOnlineQuotes::slotEntryRenamed); connect(d->ui->m_quoteSourceList, &QListWidget::itemDoubleClicked, this, &KSettingsOnlineQuotes::slotStartRename); connect(d->ui->m_editURL, &QLineEdit::textChanged, this, static_cast(&KSettingsOnlineQuotes::slotEntryChanged)); connect(d->ui->m_editCSVURL, &QLineEdit::textChanged, this, static_cast(&KSettingsOnlineQuotes::slotEntryChanged)); connect(d->ui->m_editIdentifier, &QLineEdit::textChanged, this, static_cast(&KSettingsOnlineQuotes::slotEntryChanged)); connect(d->ui->m_editIdentifyBy, static_cast(&QComboBox::currentIndexChanged), this, static_cast(&KSettingsOnlineQuotes::slotEntryChanged)); connect(d->ui->m_editDate, &QLineEdit::textChanged, this, static_cast(&KSettingsOnlineQuotes::slotEntryChanged)); connect(d->ui->m_editDateFormat, &QLineEdit::textChanged, this, static_cast(&KSettingsOnlineQuotes::slotEntryChanged)); connect(d->ui->m_editPrice, &QLineEdit::textChanged, this, static_cast(&KSettingsOnlineQuotes::slotEntryChanged)); connect(d->ui->m_skipStripping, &QAbstractButton::toggled, this, static_cast(&KSettingsOnlineQuotes::slotEntryChanged)); } KSettingsOnlineQuotes::~KSettingsOnlineQuotes() { Q_D(KSettingsOnlineQuotes); delete d; } void KSettingsOnlineQuotes::loadList(const bool updateResetList) { Q_D(KSettingsOnlineQuotes); //disconnect the slot while items are being loaded and reconnect at the end disconnect(d->ui->m_quoteSourceList, &QListWidget::itemChanged, this, &KSettingsOnlineQuotes::slotEntryRenamed); d->m_quoteInEditing = false; QStringList groups = WebPriceQuote::quoteSources(); if (updateResetList) d->m_resetList.clear(); d->ui->m_quoteSourceList->clear(); QStringList::Iterator it; for (it = groups.begin(); it != groups.end(); ++it) { QListWidgetItem* item = new QListWidgetItem(*it); item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); d->ui->m_quoteSourceList->addItem(item); if (updateResetList) d->m_resetList += WebPriceQuoteSource(*it); } d->ui->m_quoteSourceList->sortItems(); QListWidgetItem* first = d->ui->m_quoteSourceList->item(0); if (first) d->ui->m_quoteSourceList->setCurrentItem(first); slotLoadWidgets(); d->ui->m_newButton->setEnabled((d->ui->m_quoteSourceList->findItems(i18n("New Quote Source"), Qt::MatchExactly)).count() == 0); connect(d->ui->m_quoteSourceList, &QListWidget::itemChanged, this, &KSettingsOnlineQuotes::slotEntryRenamed); } void KSettingsOnlineQuotes::resetConfig() { Q_D(KSettingsOnlineQuotes); QStringList::ConstIterator it; QStringList groups = WebPriceQuote::quoteSources(); // delete all currently defined entries for (it = groups.constBegin(); it != groups.constEnd(); ++it) { WebPriceQuoteSource(*it).remove(); } // and write back the one's from the reset list QList::ConstIterator itr; for (itr = d->m_resetList.constBegin(); itr != d->m_resetList.constEnd(); ++itr) { (*itr).write(); } loadList(); } void KSettingsOnlineQuotes::slotLoadWidgets() { Q_D(KSettingsOnlineQuotes); d->m_quoteInEditing = false; QListWidgetItem* item = d->ui->m_quoteSourceList->currentItem(); d->ui->m_editURL->setEnabled(true); d->ui->m_editCSVURL->setEnabled(true); d->ui->m_editIdentifier->setEnabled(true); d->ui->m_editIdentifyBy->setEnabled(true); d->ui->m_editPrice->setEnabled(true); d->ui->m_editDate->setEnabled(true); d->ui->m_editDateFormat->setEnabled(true); d->ui->m_skipStripping->setEnabled(true); d->ui->m_dumpCSVProfile->setEnabled(true); d->ui->m_deleteButton->setEnabled(true); d->ui->m_editURL->setText(QString()); d->ui->m_editCSVURL->setText(QString()); d->ui->m_editIdentifier->setText(QString()); d->ui->m_editIdentifyBy->setCurrentIndex(WebPriceQuoteSource::identifyBy::Symbol); d->ui->m_editPrice->setText(QString()); d->ui->m_editDate->setText(QString()); d->ui->m_editDateFormat->setText(QString()); if (item) { d->m_currentItem = WebPriceQuoteSource(item->text()); d->ui->m_editURL->setText(d->m_currentItem.m_url); d->ui->m_editCSVURL->setText(d->m_currentItem.m_csvUrl); d->ui->m_editIdentifier->setText(d->m_currentItem.m_webID); d->ui->m_editIdentifyBy->setCurrentIndex(d->m_currentItem.m_webIDBy); d->ui->m_editPrice->setText(d->m_currentItem.m_price); d->ui->m_editDate->setText(d->m_currentItem.m_date); d->ui->m_editDateFormat->setText(d->m_currentItem.m_dateformat); d->ui->m_skipStripping->setChecked(d->m_currentItem.m_skipStripping); } else { d->ui->m_editURL->setEnabled(false); d->ui->m_editCSVURL->setEnabled(false); d->ui->m_editIdentifier->setEnabled(false); d->ui->m_editIdentifyBy->setEnabled(false); d->ui->m_editPrice->setEnabled(false); d->ui->m_editDate->setEnabled(false); d->ui->m_editDateFormat->setEnabled(false); d->ui->m_skipStripping->setEnabled(false); d->ui->m_dumpCSVProfile->setEnabled(false); d->ui->m_deleteButton->setEnabled(false); } d->ui->m_updateButton->setEnabled(false); } void KSettingsOnlineQuotes::slotEntryChanged() { Q_D(KSettingsOnlineQuotes); bool modified = d->ui->m_editURL->text() != d->m_currentItem.m_url || d->ui->m_editCSVURL->text() != d->m_currentItem.m_csvUrl || d->ui->m_editIdentifier->text() != d->m_currentItem.m_webID || d->ui->m_editIdentifyBy->currentData().toInt() != static_cast(d->m_currentItem.m_webIDBy) || d->ui->m_editDate->text() != d->m_currentItem.m_date || d->ui->m_editDateFormat->text() != d->m_currentItem.m_dateformat || d->ui->m_editPrice->text() != d->m_currentItem.m_price || d->ui->m_skipStripping->isChecked() != d->m_currentItem.m_skipStripping; d->ui->m_updateButton->setEnabled(modified); } void KSettingsOnlineQuotes::slotEntryChanged(int) { slotEntryChanged(); } void KSettingsOnlineQuotes::slotEntryChanged(const QString&) { slotEntryChanged(); } void KSettingsOnlineQuotes::slotEntryChanged(bool) { slotEntryChanged(); } void KSettingsOnlineQuotes::slotDumpCSVProfile() { Q_D(KSettingsOnlineQuotes); - KSharedConfigPtr config = CSVImporter::configFile(); + KSharedConfigPtr config = CSVImporterCore::configFile(); PricesProfile profile; profile.m_profileName = d->m_currentItem.m_name; profile.m_profileType = Profile::StockPrices; bool profileExists = false; bool writeProfile = true; if (profile.readSettings(config)) profileExists = true; else { profile.m_profileType = Profile::CurrencyPrices; if (profile.readSettings(config)) profileExists = true; } if (profileExists) writeProfile = (KMessageBox::questionYesNoCancel(this, i18n("CSV profile %1 already exists.
" "Do you want to overwrite it?", d->m_currentItem.m_name), i18n("CSV Profile Already Exists")) == KMessageBox::Yes ? true : false); if (writeProfile) { QMap quoteSources = WebPriceQuote::defaultCSVQuoteSources(); profile = quoteSources.value(d->m_currentItem.m_name); if (profile.m_profileName.compare(d->m_currentItem.m_name, Qt::CaseInsensitive) == 0) { profile.writeSettings(config); - CSVImporter::profilesAction(profile.type(), ProfileAction::Add, profile.m_profileName, profile.m_profileName); + CSVImporterCore::profilesAction(profile.type(), ProfileAction::Add, profile.m_profileName, profile.m_profileName); } } - CSVImporter::profilesAction(profile.type(), ProfileAction::UpdateLastUsed, profile.m_profileName, profile.m_profileName); + CSVImporterCore::profilesAction(profile.type(), ProfileAction::UpdateLastUsed, profile.m_profileName, profile.m_profileName); } void KSettingsOnlineQuotes::slotUpdateEntry() { Q_D(KSettingsOnlineQuotes); d->m_currentItem.m_url = d->ui->m_editURL->text(); d->m_currentItem.m_csvUrl = d->ui->m_editCSVURL->text(); d->m_currentItem.m_webID = d->ui->m_editIdentifier->text(); d->m_currentItem.m_webIDBy = static_cast(d->ui->m_editIdentifyBy->currentData().toInt()); d->m_currentItem.m_date = d->ui->m_editDate->text(); d->m_currentItem.m_dateformat = d->ui->m_editDateFormat->text(); d->m_currentItem.m_price = d->ui->m_editPrice->text(); d->m_currentItem.m_skipStripping = d->ui->m_skipStripping->isChecked(); d->m_currentItem.write(); slotEntryChanged(); } void KSettingsOnlineQuotes::slotNewEntry() { Q_D(KSettingsOnlineQuotes); WebPriceQuoteSource newSource(i18n("New Quote Source")); newSource.write(); loadList(); QListWidgetItem* item = d->ui->m_quoteSourceList->findItems(i18n("New Quote Source"), Qt::MatchExactly).at(0); if (item) { d->ui->m_quoteSourceList->setCurrentItem(item); slotLoadWidgets(); } } void KSettingsOnlineQuotes::slotDeleteEntry() { Q_D(KSettingsOnlineQuotes); // first check if no security is using this online source auto securities = MyMoneyFile::instance()->securityList(); foreach(const auto security, securities) { if (security.value(QStringLiteral("kmm-online-source")).compare(d->m_currentItem.m_name) == 0) { if (KMessageBox::questionYesNo(this, i18n("Security %1 uses this quote source.
" "Do you really want to remove it?", security.name()), i18n("Delete quote source")) == KMessageBox::Yes) break; // webpricequote can handle missing online quotes, so proceed without any extra action else return; } } // remove online source from webpricequote... d->m_currentItem.remove(); // ...and from setting's list auto row = d->ui->m_quoteSourceList->currentRow(); QListWidgetItem *item = d->ui->m_quoteSourceList->takeItem(row); if (item) delete item; item = nullptr; int count = d->ui->m_quoteSourceList->count(); if (row < count) // select next available entry... item = d->ui->m_quoteSourceList->item(row); else if (row >= count && count > 0) // ...or last entry if this was the last entry... item = d->ui->m_quoteSourceList->item(count - 1); if (item) { d->ui->m_quoteSourceList->setCurrentItem(item); slotLoadWidgets(); } } void KSettingsOnlineQuotes::slotStartRename(QListWidgetItem* item) { Q_D(KSettingsOnlineQuotes); d->m_quoteInEditing = true; d->ui->m_quoteSourceList->editItem(item); } void KSettingsOnlineQuotes::slotEntryRenamed(QListWidgetItem* item) { Q_D(KSettingsOnlineQuotes); //if there is no current item selected, exit if (d->m_quoteInEditing == false || !d->ui->m_quoteSourceList->currentItem() || item != d->ui->m_quoteSourceList->currentItem()) return; d->m_quoteInEditing = false; QString text = item->text(); int nameCount = 0; for (auto i = 0; i < d->ui->m_quoteSourceList->count(); ++i) { if (d->ui->m_quoteSourceList->item(i)->text() == text) ++nameCount; } // Make sure we get a non-empty and unique name if (text.length() > 0 && nameCount == 1) { d->m_currentItem.rename(text); } else { item->setText(d->m_currentItem.m_name); } d->ui->m_quoteSourceList->sortItems(); d->ui->m_newButton->setEnabled(d->ui->m_quoteSourceList->findItems(i18n("New Quote Source"), Qt::MatchExactly).count() == 0); } diff --git a/kmymoney/dialogs/settings/ksettingsplugins.cpp b/kmymoney/dialogs/settings/ksettingsplugins.cpp new file mode 100644 index 000000000..ce4c121db --- /dev/null +++ b/kmymoney/dialogs/settings/ksettingsplugins.cpp @@ -0,0 +1,176 @@ +/*************************************************************************** + ksettingsplugins.cpp + -------------------- + (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 "ksettingsplugins.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include +#include + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "pluginloader.h" + +struct pluginGroupInfo { + QList plugins; + KPluginSelector::PluginLoadMethod loadMethod; + QString categoryName; +}; + +class KSettingsPluginsPrivate +{ + Q_DISABLE_COPY(KSettingsPluginsPrivate) + +public: + KSettingsPluginsPrivate(KSettingsPlugins* qq) : + m_pluginSelector(new KPluginSelector(qq)) + { + } + + ~KSettingsPluginsPrivate() + { + delete m_pluginSelector; + } + + /** + * @brief This should be called after save to kmymoneyrc in order to update cached on/off states + */ + void updateSavedPluginStates() + { + for (auto i = 0 ; i < pluginInfos.size(); ++i) + savedPluginStates[i] = pluginInfos[i].isPluginEnabled(); + } + + /** + * @brief This compares plugin on/off states from KPluginSelector with cached one + * @return true if user changes to plugin on/off state aren't different than initial + */ + bool isEqualToSavedStates() + { + for (auto i = 0 ; i < pluginInfos.size(); ++i) + if (savedPluginStates[i] != pluginInfos[i].isPluginEnabled()) + return false; + return true; + } + + KPluginSelector* const m_pluginSelector; + QList pluginInfos; + /** + * @brief savedPluginStates This caches on/off states as in kmymoneyrc + */ + QBitArray savedPluginStates; +}; + +KSettingsPlugins::KSettingsPlugins(QWidget* parent) : + QWidget(parent), + d_ptr(new KSettingsPluginsPrivate(this)) + +{ + Q_D(KSettingsPlugins); + auto layout = new QVBoxLayout; + setLayout(layout); // otherwise KPluginSelector occupies very little area + layout->addWidget(d->m_pluginSelector); + + auto allPluginDatas = KMyMoneyPlugin::listPlugins(false); // fetch all available KMyMoney plugins + QVector standardPlugins; + QVector payeePlugins; + QVector onlinePlugins; + + // divide plugins in some arbitrary categories + for (const KPluginMetaData& pluginData : allPluginDatas) + switch (KMyMoneyPlugin::pluginCategory(pluginData)) { + case KMyMoneyPlugin::Category::StandardPlugin: + standardPlugins.append(pluginData); + break; + case KMyMoneyPlugin::Category::PayeeIdentifier: + payeePlugins.append(pluginData); + break; + case KMyMoneyPlugin::Category::OnlineBankOperations: + onlinePlugins.append(pluginData); + break; + default: + break; + } + + const QVector pluginGroups { + {KPluginInfo::fromMetaData(standardPlugins), + KPluginSelector::PluginLoadMethod::ReadConfigFile, + i18n("KMyMoney Plugins")}, + + {KPluginInfo::fromMetaData(payeePlugins), + KPluginSelector::PluginLoadMethod::IgnoreConfigFile, + i18n("Payee Identifier")}, + + {KPluginInfo::fromMetaData(onlinePlugins), + KPluginSelector::PluginLoadMethod::IgnoreConfigFile, + i18n("Online Banking Operations")} + }; + + // add all plugins to selector + for(const auto& pluginGroup : pluginGroups) { + if (!pluginGroup.plugins.isEmpty()) { + d->m_pluginSelector->addPlugins(pluginGroup.plugins, + pluginGroup.loadMethod, + pluginGroup.categoryName); // at that step plugin on/off state should be fetched automatically by KPluginSelector + d->pluginInfos.append(pluginGroup.plugins); // store initial on/off state to be able to enable/disable Apply button + } + } + d->savedPluginStates.resize(d->pluginInfos.size()); + d->updateSavedPluginStates(); + + connect(d->m_pluginSelector, &KPluginSelector::changed, this, &KSettingsPlugins::slotPluginsSelectionChanged); +} + +KSettingsPlugins::~KSettingsPlugins() +{ + Q_D(KSettingsPlugins); + delete d; +} + +void KSettingsPlugins::slotPluginsSelectionChanged(bool b) +{ + Q_D(KSettingsPlugins); + if (b) { + d->m_pluginSelector->updatePluginsState(); + emit changed(!d->isEqualToSavedStates()); + } +} + +void KSettingsPlugins::slotResetToDefaults() +{ + Q_D(KSettingsPlugins); + d->m_pluginSelector->defaults(); +} + +void KSettingsPlugins::slotSavePluginConfiguration() +{ + Q_D(KSettingsPlugins); + if (!d->isEqualToSavedStates()) { + d->m_pluginSelector->save(); + d->updateSavedPluginStates(); + emit settingsChanged(QStringLiteral("Plugins")); + } +} diff --git a/kmymoney/plugins/qif/export/qifexporterplugin.h b/kmymoney/dialogs/settings/ksettingsplugins.h similarity index 57% copy from kmymoney/plugins/qif/export/qifexporterplugin.h copy to kmymoney/dialogs/settings/ksettingsplugins.h index 394efff99..78a8b964a 100644 --- a/kmymoney/plugins/qif/export/qifexporterplugin.h +++ b/kmymoney/dialogs/settings/ksettingsplugins.h @@ -1,57 +1,58 @@ /*************************************************************************** - qifexporterplugin.h + ksettingsplugins.h ------------------- - copyright : (C) 2017 by Łukasz Wojniłowicz - email : lukasz.wojnilowicz@gmail.com + (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. * * * ***************************************************************************/ -#ifndef QIFEXPORTERPLUGIN_H -#define QIFEXPORTERPLUGIN_H +#ifndef KSETTINGSPLUGINS_H +#define KSETTINGSPLUGINS_H // ---------------------------------------------------------------------------- -// KDE Includes +// QT Includes + +#include // ---------------------------------------------------------------------------- -// QT Includes +// KDE Includes +// ---------------------------------------------------------------------------- // Project Includes -#include "kmymoneyplugin.h" - -class MyMoneyQifReader; +class KPluginSelector; -class QIFExporterPlugin : public KMyMoneyPlugin::Plugin +class KSettingsPluginsPrivate; +class KSettingsPlugins : public QWidget { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.qifexport" FILE "qifexport.json") - + Q_DISABLE_COPY(KSettingsPlugins) + public: - explicit QIFExporterPlugin(); - ~QIFExporterPlugin(); + explicit KSettingsPlugins(QWidget* parent = nullptr); + ~KSettingsPlugins(); - QAction *m_action; +public Q_SLOTS: + void slotResetToDefaults(); + void slotSavePluginConfiguration(); - MyMoneyQifReader *m_qifReader; +Q_SIGNALS: + void changed(bool); + void settingsChanged(const QString &dialogName); -public Q_SLOTS: - /** - * Called when the user wishes to export some transaction to a - * QIF formatted file. An account must be open for this to work. - * Uses MyMoneyQifWriter() for the actual output. - */ - void slotQifExport(); - -protected: - void createActions(); -}; +private: + KSettingsPluginsPrivate* const d_ptr; + Q_DECLARE_PRIVATE(KSettingsPlugins) +private Q_SLOTS: + void slotPluginsSelectionChanged(bool changed); +}; #endif + diff --git a/kmymoney/kmymoney.cpp b/kmymoney/kmymoney.cpp index 92e344aad..77c64748f 100644 --- a/kmymoney/kmymoney.cpp +++ b/kmymoney/kmymoney.cpp @@ -1,3573 +1,3438 @@ /*************************************************************************** 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 -#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" #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_pluginLoader(0), 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; - KMyMoneyPlugin::PluginLoader* m_pluginLoader; QString m_lastPayeeEnteredId; /** the configuration object of the application */ KSharedConfigPtr m_config; /** - * @brief List of all plugged plugins - * - * The key is the file name of the plugin. + * @brief Structure of plugins objects by their interfaces */ - QMap m_plugins; - - /** - * @brief List of plugged importer plugins - * - * The key is the objectName of the plugin. - */ - QMap m_importerPlugins; - - /** - * @brief List of plugged online plugins - * - * The key is the objectName of the plugin. - */ - QMap m_onlinePlugins; + 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; - QObject* m_pluginInterface; - 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", QDBusConnectionInterface::DontQueueService); #endif // Register the main engine types used as meta-objects qRegisterMetaType("MyMoneyMoney"); qRegisterMetaType("MyMoneySecurity"); // 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(); - loadPlugins(); - d->m_myMoneyView->setOnlinePlugins(d->m_onlinePlugins); + 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(); + 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 - unloadPlugins(); + 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 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 lutMenus; const QHash menuNames { {Menu::Institution, QStringLiteral("institution_context_menu")}, {Menu::Account, QStringLiteral("account_context_menu")}, {Menu::Schedule, QStringLiteral("schedule_context_menu")}, {Menu::Category, QStringLiteral("category_context_menu")}, {Menu::Tag, QStringLiteral("tag_context_menu")}, {Menu::Payee, QStringLiteral("payee_context_menu")}, {Menu::Investment, QStringLiteral("investment_context_menu")}, {Menu::Transaction, QStringLiteral("transaction_context_menu")}, {Menu::MoveTransaction, QStringLiteral("transaction_move_menu")}, {Menu::MarkTransaction, QStringLiteral("transaction_mark_menu")}, {Menu::MarkTransactionContext, QStringLiteral("transaction_context_mark_menu")}, {Menu::OnlineJob, QStringLiteral("onlinejob_context_menu")} }; for (auto it = menuNames.cbegin(); it != menuNames.cend(); ++it) lutMenus.insert(it.key(), qobject_cast(factory()->container(it.value(), this))); return lutMenus; } QHash KMyMoneyApp::initActions() { auto aC = actionCollection(); // ************* // Adding standard actions // ************* KStandardAction::openNew(this, &KMyMoneyApp::slotFileNew, aC); KStandardAction::open(this, &KMyMoneyApp::slotFileOpen, aC); d->m_recentFiles = KStandardAction::openRecent(this, &KMyMoneyApp::slotFileOpenRecent, aC); KStandardAction::save(this, &KMyMoneyApp::slotFileSave, aC); KStandardAction::saveAs(this, &KMyMoneyApp::slotFileSaveAs, aC); KStandardAction::close(this, &KMyMoneyApp::slotFileClose, aC); KStandardAction::quit(this, &KMyMoneyApp::slotFileQuit, aC); KStandardAction::print(this, &KMyMoneyApp::slotPrintView, aC); KStandardAction::preferences(this, &KMyMoneyApp::slotSettings, aC); /* Look-up table for all custom actions. It's required for: 1) building QList with QActions to be added to ActionCollection 2) adding custom features to QActions like e.g. keyboard shortcut */ QHash lutActions; // ************* // Adding all actions // ************* { // struct for creating useless (unconnected) QAction struct actionInfo { Action action; QString name; QString text; Icon icon; }; const QVector actionInfos { // ************* // The File menu // ************* {Action::FileOpenDatabase, QStringLiteral("open_database"), i18n("Open database..."), Icon::SVNUpdate}, {Action::FileSaveAsDatabase, QStringLiteral("saveas_database"), i18n("Save as database..."), Icon::FileArchiver}, {Action::FileBackup, QStringLiteral("file_backup"), i18n("Backup..."), Icon::Empty}, {Action::FileImportStatement, QStringLiteral("file_import_statement"), i18n("Statement file..."), Icon::Empty}, {Action::FileImportTemplate, QStringLiteral("file_import_template"), i18n("Account Template..."), Icon::Empty}, {Action::FileExportTemplate, QStringLiteral("file_export_template"), i18n("Account Template..."), Icon::Empty}, {Action::FilePersonalData, QStringLiteral("view_personal_data"), i18n("Personal Data..."), Icon::UserProperties}, #ifdef KMM_DEBUG {Action::FileDump, QStringLiteral("file_dump"), i18n("Dump Memory"), Icon::Empty}, #endif {Action::FileInformation, QStringLiteral("view_file_info"), i18n("File-Information..."), Icon::DocumentProperties}, // ************* // The Edit menu // ************* {Action::EditFindTransaction, QStringLiteral("edit_find_transaction"), i18n("Find transaction..."), Icon::EditFindTransaction}, // ************* // The View menu // ************* {Action::ViewTransactionDetail, QStringLiteral("view_show_transaction_detail"), i18n("Show Transaction Detail"), Icon::ViewTransactionDetail}, {Action::ViewHideReconciled, QStringLiteral("view_hide_reconciled_transactions"), i18n("Hide reconciled transactions"), Icon::HideReconciled}, {Action::ViewHideCategories, QStringLiteral("view_hide_unused_categories"), i18n("Hide unused categories"), Icon::HideCategories}, {Action::ViewShowAll, QStringLiteral("view_show_all_accounts"), i18n("Show all accounts"), Icon::Empty}, // ********************* // The institutions menu // ********************* {Action::NewInstitution, QStringLiteral("institution_new"), i18n("New institution..."), Icon::InstitutionNew}, {Action::EditInstitution, QStringLiteral("institution_edit"), i18n("Edit institution..."), Icon::InstitutionEdit}, {Action::DeleteInstitution, QStringLiteral("institution_delete"), i18n("Delete institution..."), Icon::InstitutionDelete}, // ***************** // The accounts menu // ***************** {Action::NewAccount, QStringLiteral("account_new"), i18n("New account..."), Icon::AccountNew}, {Action::OpenAccount, QStringLiteral("account_open"), i18n("Open ledger"), Icon::ViewFinancialList}, {Action::StartReconciliation, QStringLiteral("account_reconcile"), i18n("Reconcile..."), Icon::Reconcile}, {Action::FinishReconciliation, QStringLiteral("account_reconcile_finish"), i18nc("Finish reconciliation", "Finish"), Icon::AccountFinishReconciliation}, {Action::PostponeReconciliation, QStringLiteral("account_reconcile_postpone"), i18n("Postpone reconciliation"), Icon::MediaPlaybackPause}, {Action::EditAccount, QStringLiteral("account_edit"), i18n("Edit account..."), Icon::AccountEdit}, {Action::DeleteAccount, QStringLiteral("account_delete"), i18n("Delete account..."), Icon::AccountDelete}, {Action::CloseAccount, QStringLiteral("account_close"), i18n("Close account"), Icon::AccountClose}, {Action::ReopenAccount, QStringLiteral("account_reopen"), i18n("Reopen account"), Icon::AccountReopen}, {Action::ReportAccountTransactions, QStringLiteral("account_transaction_report"), i18n("Transaction report"), Icon::ViewFinancialList}, {Action::ChartAccountBalance, QStringLiteral("account_chart"), i18n("Show balance chart..."), Icon::OfficeChartLine}, {Action::MapOnlineAccount, QStringLiteral("account_online_map"), i18n("Map account..."), Icon::NewsSubscribe}, {Action::UnmapOnlineAccount, QStringLiteral("account_online_unmap"), i18n("Unmap account..."), Icon::NewsUnsubscribe}, {Action::UpdateAccount, QStringLiteral("account_online_update"), i18n("Update account..."), Icon::AccountUpdate}, {Action::UpdateAllAccounts, QStringLiteral("account_online_update_all"), i18n("Update all accounts..."), Icon::AccountUpdateAll}, {Action::AccountCreditTransfer, QStringLiteral("account_online_new_credit_transfer"), i18n("New credit transfer"), Icon::AccountCreditTransfer}, // ******************* // The categories menu // ******************* {Action::NewCategory, QStringLiteral("category_new"), i18n("New category..."), Icon::CategoryNew}, {Action::EditCategory, QStringLiteral("category_edit"), i18n("Edit category..."), Icon::CategoryEdit}, {Action::DeleteCategory, QStringLiteral("category_delete"), i18n("Delete category..."), Icon::CategoryDelete}, // ************** // The tools menu // ************** {Action::ToolCurrencies, QStringLiteral("tools_currency_editor"), i18n("Currencies..."), Icon::ViewCurrencyList}, {Action::ToolPrices, QStringLiteral("tools_price_editor"), i18n("Prices..."), Icon::Empty}, {Action::ToolUpdatePrices, QStringLiteral("tools_update_prices"), i18n("Update Stock and Currency Prices..."), Icon::ToolUpdatePrices}, {Action::ToolConsistency, QStringLiteral("tools_consistency_check"), i18n("Consistency Check"), Icon::Empty}, {Action::ToolPerformance, QStringLiteral("tools_performancetest"), i18n("Performance-Test"), Icon::Fork}, {Action::ToolSQL, QStringLiteral("tools_generate_sql"), i18n("Generate Database SQL"), Icon::Empty}, {Action::ToolCalculator, QStringLiteral("tools_kcalc"), i18n("Calculator..."), Icon::AccessoriesCalculator}, // ***************** // The settings menu // ***************** {Action::SettingsAllMessages, QStringLiteral("settings_enable_messages"), i18n("Enable all messages"), Icon::Empty}, // ************* // The help menu // ************* {Action::HelpShow, QStringLiteral("help_show_tip"), i18n("&Show tip of the day"), Icon::Tip}, // *************************** // Actions w/o main menu entry // *************************** {Action::NewTransaction, QStringLiteral("transaction_new"), i18nc("New transaction button", "New"), Icon::TransactionNew}, {Action::EditTransaction, QStringLiteral("transaction_edit"), i18nc("Edit transaction button", "Edit"), Icon::TransactionEdit}, {Action::EnterTransaction, QStringLiteral("transaction_enter"), i18nc("Enter transaction", "Enter"), Icon::DialogOK}, {Action::EditSplits, QStringLiteral("transaction_editsplits"), i18nc("Edit split button", "Edit splits"), Icon::Split}, {Action::CancelTransaction, QStringLiteral("transaction_cancel"), i18nc("Cancel transaction edit", "Cancel"), Icon::DialogCancel}, {Action::DeleteTransaction, QStringLiteral("transaction_delete"), i18nc("Delete transaction", "Delete"), Icon::EditDelete}, {Action::DuplicateTransaction, QStringLiteral("transaction_duplicate"), i18nc("Duplicate transaction", "Duplicate"), Icon::EditCopy}, {Action::MatchTransaction, QStringLiteral("transaction_match"), i18nc("Button text for match transaction", "Match"),Icon::TransactionMatch}, {Action::AcceptTransaction, QStringLiteral("transaction_accept"), i18nc("Accept 'imported' and 'matched' transaction", "Accept"), Icon::TransactionAccept}, {Action::ToggleReconciliationFlag, QStringLiteral("transaction_mark_toggle"), i18nc("Toggle reconciliation flag", "Toggle"), Icon::Empty}, {Action::MarkCleared, QStringLiteral("transaction_mark_cleared"), i18nc("Mark transaction cleared", "Cleared"), Icon::Empty}, {Action::MarkReconciled, QStringLiteral("transaction_mark_reconciled"), i18nc("Mark transaction reconciled", "Reconciled"), Icon::Empty}, {Action::MarkNotReconciled, QStringLiteral("transaction_mark_notreconciled"), i18nc("Mark transaction not reconciled", "Not reconciled"), Icon::Empty}, {Action::SelectAllTransactions, QStringLiteral("transaction_select_all"), i18nc("Select all transactions", "Select all"), Icon::Empty}, {Action::GoToAccount, QStringLiteral("transaction_goto_account"), i18n("Go to account"), Icon::GoJump}, {Action::GoToPayee, QStringLiteral("transaction_goto_payee"), i18n("Go to payee"), Icon::GoJump}, {Action::NewScheduledTransaction, QStringLiteral("transaction_create_schedule"), i18n("Create scheduled transaction..."), Icon::AppointmentNew}, {Action::AssignTransactionsNumber, QStringLiteral("transaction_assign_number"), i18n("Assign next number"), Icon::Empty}, {Action::CombineTransactions, QStringLiteral("transaction_combine"), i18nc("Combine transactions", "Combine"), Icon::Empty}, {Action::CopySplits, QStringLiteral("transaction_copy_splits"), i18n("Copy splits"), Icon::Empty}, //Investment {Action::NewInvestment, QStringLiteral("investment_new"), i18n("New investment..."), Icon::InvestmentNew}, {Action::EditInvestment, QStringLiteral("investment_edit"), i18n("Edit investment..."), Icon::InvestmentEdit}, {Action::DeleteInvestment, QStringLiteral("investment_delete"), i18n("Delete investment..."), Icon::InvestmentDelete}, {Action::UpdatePriceOnline, QStringLiteral("investment_online_price_update"), i18n("Online price update..."), Icon::InvestmentOnlinePrice}, {Action::UpdatePriceManually, QStringLiteral("investment_manual_price_update"), i18n("Manual price update..."), Icon::Empty}, //Schedule {Action::NewSchedule, QStringLiteral("schedule_new"), i18n("New scheduled transaction"), Icon::AppointmentNew}, {Action::EditSchedule, QStringLiteral("schedule_edit"), i18n("Edit scheduled transaction"), Icon::DocumentEdit}, {Action::DeleteSchedule, QStringLiteral("schedule_delete"), i18n("Delete scheduled transaction"), Icon::EditDelete}, {Action::DuplicateSchedule, QStringLiteral("schedule_duplicate"), i18n("Duplicate scheduled transaction"), Icon::EditCopy}, {Action::EnterSchedule, QStringLiteral("schedule_enter"), i18n("Enter next transaction..."), Icon::KeyEnter}, {Action::SkipSchedule, QStringLiteral("schedule_skip"), i18n("Skip next transaction..."), Icon::MediaSeekForward}, //Payees {Action::NewPayee, QStringLiteral("payee_new"), i18n("New payee"), Icon::ListAddUser}, {Action::RenamePayee, QStringLiteral("payee_rename"), i18n("Rename payee"), Icon::PayeeRename}, {Action::DeletePayee, QStringLiteral("payee_delete"), i18n("Delete payee"), Icon::ListRemoveUser}, {Action::MergePayee, QStringLiteral("payee_merge"), i18n("Merge payees"), Icon::PayeeMerge}, //Tags {Action::NewTag, QStringLiteral("tag_new"), i18n("New tag"), Icon::ListAddTag}, {Action::RenameTag, QStringLiteral("tag_rename"), i18n("Rename tag"), Icon::TagRename}, {Action::DeleteTag, QStringLiteral("tag_delete"), i18n("Delete tag"), Icon::ListRemoveTag}, //debug actions #ifdef KMM_DEBUG {Action::WizardNewUser, QStringLiteral("new_user_wizard"), i18n("Test new feature"), Icon::Empty}, {Action::DebugTraces, QStringLiteral("debug_traces"), i18n("Debug Traces"), Icon::Empty}, #endif {Action::DebugTimers, QStringLiteral("debug_timers"), i18n("Debug Timers"), Icon::Empty}, // onlineJob actions {Action::DeleteOnlineJob, QStringLiteral("onlinejob_delete"), i18n("Remove credit transfer"), Icon::EditDelete}, {Action::EditOnlineJob, QStringLiteral("onlinejob_edit"), i18n("Edit credit transfer"), Icon::DocumentEdit}, {Action::LogOnlineJob, QStringLiteral("onlinejob_log"), i18n("Show log"), Icon::Empty}, }; for (const auto& info : actionInfos) { auto a = new QAction(this); // KActionCollection::addAction by name sets object name anyways, // so, as better alternative, set it here right from the start a->setObjectName(info.name); a->setText(info.text); if (info.icon != Icon::Empty) // no need to set empty icon a->setIcon(Icons::get(info.icon)); a->setEnabled(false); lutActions.insert(info.action, a); // store QAction's pointer for later processing } } { // List with slots that get connected here. Other slots get connected in e.g. appropriate views typedef void(KMyMoneyApp::*KMyMoneyAppFunc)(); const QHash actionConnections { // ************* // The File menu // ************* {Action::FileOpenDatabase, &KMyMoneyApp::slotOpenDatabase}, {Action::FileSaveAsDatabase, &KMyMoneyApp::slotSaveAsDatabase}, {Action::FileBackup, &KMyMoneyApp::slotBackupFile}, {Action::FileImportTemplate, &KMyMoneyApp::slotLoadAccountTemplates}, {Action::FileExportTemplate, &KMyMoneyApp::slotSaveAccountTemplates}, {Action::FilePersonalData, &KMyMoneyApp::slotFileViewPersonal}, #ifdef KMM_DEBUG {Action::FileDump, &KMyMoneyApp::slotFileFileInfo}, #endif {Action::FileInformation, &KMyMoneyApp::slotFileInfoDialog}, // ************* // The Edit menu // ************* {Action::EditFindTransaction, &KMyMoneyApp::slotFindTransaction}, // ************* // The View menu // ************* {Action::ViewTransactionDetail, &KMyMoneyApp::slotShowTransactionDetail}, {Action::ViewHideReconciled, &KMyMoneyApp::slotHideReconciledTransactions}, {Action::ViewHideCategories, &KMyMoneyApp::slotHideUnusedCategories}, {Action::ViewShowAll, &KMyMoneyApp::slotShowAllAccounts}, // ***************** // The accounts menu // ***************** {Action::MapOnlineAccount, &KMyMoneyApp::slotAccountMapOnline}, {Action::UnmapOnlineAccount, &KMyMoneyApp::slotAccountUnmapOnline}, {Action::UpdateAccount, &KMyMoneyApp::slotAccountUpdateOnline}, {Action::UpdateAllAccounts, &KMyMoneyApp::slotAccountUpdateOnlineAll}, // ************** // The tools menu // ************** {Action::ToolCurrencies, &KMyMoneyApp::slotCurrencyDialog}, {Action::ToolPrices, &KMyMoneyApp::slotPriceDialog}, {Action::ToolUpdatePrices, &KMyMoneyApp::slotEquityPriceUpdate}, {Action::ToolConsistency, &KMyMoneyApp::slotFileConsistencyCheck}, {Action::ToolPerformance, &KMyMoneyApp::slotPerformanceTest}, {Action::ToolSQL, &KMyMoneyApp::slotGenerateSql}, {Action::ToolCalculator, &KMyMoneyApp::slotToolsStartKCalc}, // ***************** // The settings menu // ***************** {Action::SettingsAllMessages, &KMyMoneyApp::slotEnableMessages}, // ************* // The help menu // ************* {Action::HelpShow, &KMyMoneyApp::slotShowTipOfTheDay}, // *************************** // Actions w/o main menu entry // *************************** //debug actions #ifdef KMM_DEBUG {Action::WizardNewUser, &KMyMoneyApp::slotNewFeature}, {Action::DebugTraces, &KMyMoneyApp::slotToggleTraces}, #endif {Action::DebugTimers, &KMyMoneyApp::slotToggleTimers}, }; for (auto connection = actionConnections.cbegin(); connection != actionConnections.cend(); ++connection) connect(lutActions[connection.key()], &QAction::triggered, this, connection.value()); } // ************* // Setting some of added actions checkable // ************* { // Some of acitions schould be checkable, // so set them here const QVector checkableActions { Action::ViewTransactionDetail, Action::ViewHideReconciled, Action::ViewHideCategories, #ifdef KMM_DEBUG Action::DebugTraces, #endif Action::ViewShowAll }; for (const auto& it : checkableActions) { lutActions[it]->setCheckable(true); lutActions[it]->setEnabled(true); } } // ************* // Setting keyboard shortcuts for some of added actions // ************* { const QVector> actionShortcuts { {qMakePair(Action::EditFindTransaction, Qt::CTRL + Qt::Key_F)}, {qMakePair(Action::ViewTransactionDetail, Qt::CTRL + Qt::Key_T)}, {qMakePair(Action::ViewHideReconciled, Qt::CTRL + Qt::Key_R)}, {qMakePair(Action::ViewHideCategories, Qt::CTRL + Qt::Key_U)}, {qMakePair(Action::ViewShowAll, Qt::CTRL + Qt::SHIFT + Qt::Key_A)}, {qMakePair(Action::StartReconciliation, Qt::CTRL + Qt::SHIFT + Qt::Key_R)}, {qMakePair(Action::NewTransaction, Qt::CTRL + Qt::Key_Insert)}, {qMakePair(Action::ToggleReconciliationFlag, Qt::CTRL + Qt::Key_Space)}, {qMakePair(Action::MarkCleared, Qt::CTRL + Qt::Key_Alt + Qt::Key_Space)}, {qMakePair(Action::MarkReconciled, Qt::CTRL + Qt::Key_Shift + Qt::Key_Space)}, {qMakePair(Action::SelectAllTransactions, Qt::CTRL + Qt::Key_A)}, #ifdef KMM_DEBUG {qMakePair(Action::WizardNewUser, Qt::CTRL + Qt::Key_G)}, #endif {qMakePair(Action::AssignTransactionsNumber, Qt::CTRL + Qt::Key_Shift + Qt::Key_N)} }; for(const auto& it : actionShortcuts) aC->setDefaultShortcut(lutActions[it.first], it.second); } // ************* // Misc settings // ************* connect(onlineJobAdministration::instance(), &onlineJobAdministration::canSendCreditTransferChanged, lutActions.value(Action::AccountCreditTransfer), &QAction::setEnabled); // Setup transaction detail switch lutActions[Action::ViewTransactionDetail]->setChecked(KMyMoneyGlobalSettings::showRegisterDetailed()); lutActions[Action::ViewHideReconciled]->setChecked(KMyMoneyGlobalSettings::hideReconciledTransactions()); lutActions[Action::ViewHideCategories]->setChecked(KMyMoneyGlobalSettings::hideUnusedCategory()); lutActions[Action::ViewShowAll]->setChecked(false); // ************* // Adding actions to ActionCollection // ************* actionCollection()->addActions(lutActions.values()); connect( lutActions[Action::FileInformation], &QAction::triggered, this, &KMyMoneyApp::slotFileInfoDialog); // ************************ // Currently unused actions // ************************ #if 0 new KToolBarPopupAction(i18n("View back"), "go-previous", 0, this, SLOT(slotShowPreviousView()), actionCollection(), "go_back"); new KToolBarPopupAction(i18n("View forward"), "go-next", 0, this, SLOT(slotShowNextView()), actionCollection(), "go_forward"); action("go_back")->setEnabled(false); action("go_forward")->setEnabled(false); #endif // use the absolute path to your kmymoneyui.rc file for testing purpose in createGUI(); setupGUI(); // reconnect about app entry to dialog with full credits information auto aboutApp = aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::AboutApp))); aboutApp->disconnect(); connect(aboutApp, &QAction::triggered, this, &KMyMoneyApp::slotShowCredits); QMenu *menuContainer; menuContainer = static_cast(factory()->container(QStringLiteral("import"), this)); menuContainer->setIcon(Icons::get(Icon::DocumentImport)); menuContainer = static_cast(factory()->container(QStringLiteral("export"), this)); menuContainer->setIcon(Icons::get(Icon::DocumentExport)); return lutActions; } #ifdef KMM_DEBUG void KMyMoneyApp::dumpActions() const { const QList list = actionCollection()->actions(); foreach (const auto it, list) std::cout << qPrintable(it->objectName()) << ": " << qPrintable(it->text()) << std::endl; } #endif bool KMyMoneyApp::isActionToggled(const Action _a) { return pActions[_a]->isChecked(); } void KMyMoneyApp::initStatusBar() { /////////////////////////////////////////////////////////////////// // STATUSBAR d->m_statusLabel = new QLabel; statusBar()->addWidget(d->m_statusLabel); ready(); // Initialization of progress bar taken from KDevelop ;-) d->m_progressBar = new QProgressBar; statusBar()->addWidget(d->m_progressBar); d->m_progressBar->setFixedHeight(d->m_progressBar->sizeHint().height() - 8); // hide the progress bar for now slotStatusProgressBar(-1, -1); } void KMyMoneyApp::saveOptions() { KConfigGroup grp = d->m_config->group("General Options"); grp.writeEntry("Geometry", size()); grp.writeEntry("Show Statusbar", actionCollection()->action(KStandardAction::name(KStandardAction::ShowStatusbar))->isChecked()); KConfigGroup toolbarGrp = d->m_config->group("mainToolBar"); toolBar("mainToolBar")->saveSettings(toolbarGrp); d->m_recentFiles->saveEntries(d->m_config->group("Recent Files")); } void KMyMoneyApp::readOptions() { KConfigGroup grp = d->m_config->group("General Options"); pActions[Action::ViewHideReconciled]->setChecked(KMyMoneyGlobalSettings::hideReconciledTransactions()); pActions[Action::ViewHideCategories]->setChecked(KMyMoneyGlobalSettings::hideUnusedCategory()); d->m_recentFiles->loadEntries(d->m_config->group("Recent Files")); // Startdialog is written in the settings dialog d->m_startDialog = grp.readEntry("StartDialog", true); } void KMyMoneyApp::resizeEvent(QResizeEvent* ev) { KMainWindow::resizeEvent(ev); updateCaption(true); } int KMyMoneyApp::askSaveOnClose() { int ans; if (KMyMoneyGlobalSettings::autoSaveOnClose()) { ans = KMessageBox::Yes; } else { ans = KMessageBox::warningYesNoCancel(this, i18n("The file has been changed, save it?")); } return ans; } bool KMyMoneyApp::queryClose() { if (!isReady()) return false; if (d->m_myMoneyView->dirty()) { int ans = askSaveOnClose(); if (ans == KMessageBox::Cancel) return false; else if (ans == KMessageBox::Yes) { bool saved = slotFileSave(); saveOptions(); return saved; } } if (d->m_myMoneyView->isDatabase()) slotFileClose(); // close off the database saveOptions(); return true; } ///////////////////////////////////////////////////////////////////// // SLOT IMPLEMENTATION ///////////////////////////////////////////////////////////////////// void KMyMoneyApp::slotFileInfoDialog() { QPointer dlg = new KMyMoneyFileInfoDlg(0); dlg->exec(); delete dlg; } void KMyMoneyApp::slotPerformanceTest() { // dump performance report to stderr int measurement[2]; QTime timer; MyMoneyAccount acc; qDebug("--- Starting performance tests ---"); // AccountList MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; timer.start(); for (int i = 0; i < 1000; ++i) { QList list; MyMoneyFile::instance()->accountList(list); measurement[i != 0] = timer.elapsed(); } std::cerr << "accountList()" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Total time: " << (measurement[0] + measurement[1]) << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 1000 << " msec" << std::endl; // Balance of asset account(s) MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; acc = MyMoneyFile::instance()->asset(); for (int i = 0; i < 1000; ++i) { timer.start(); MyMoneyMoney result = MyMoneyFile::instance()->balance(acc.id()); measurement[i != 0] += timer.elapsed(); } std::cerr << "balance(Asset)" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 1000 << " msec" << std::endl; // total balance of asset account MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; acc = MyMoneyFile::instance()->asset(); for (int i = 0; i < 1000; ++i) { timer.start(); MyMoneyMoney result = MyMoneyFile::instance()->totalBalance(acc.id()); measurement[i != 0] += timer.elapsed(); } std::cerr << "totalBalance(Asset)" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 1000 << " msec" << std::endl; // Balance of expense account(s) MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; acc = MyMoneyFile::instance()->expense(); for (int i = 0; i < 1000; ++i) { timer.start(); MyMoneyMoney result = MyMoneyFile::instance()->balance(acc.id()); measurement[i != 0] += timer.elapsed(); } std::cerr << "balance(Expense)" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 1000 << " msec" << std::endl; // total balance of expense account MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; acc = MyMoneyFile::instance()->expense(); timer.start(); for (int i = 0; i < 1000; ++i) { MyMoneyMoney result = MyMoneyFile::instance()->totalBalance(acc.id()); measurement[i != 0] = timer.elapsed(); } std::cerr << "totalBalance(Expense)" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Total time: " << (measurement[0] + measurement[1]) << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 1000 << " msec" << std::endl; // transaction list MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; if (MyMoneyFile::instance()->asset().accountCount()) { MyMoneyTransactionFilter filter(MyMoneyFile::instance()->asset().accountList()[0]); filter.setDateFilter(QDate(), QDate::currentDate()); QList list; timer.start(); for (int i = 0; i < 100; ++i) { list = MyMoneyFile::instance()->transactionList(filter); measurement[i != 0] = timer.elapsed(); } std::cerr << "transactionList()" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Total time: " << (measurement[0] + measurement[1]) << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 100 << " msec" << std::endl; } // transaction list MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; if (MyMoneyFile::instance()->asset().accountCount()) { MyMoneyTransactionFilter filter(MyMoneyFile::instance()->asset().accountList()[0]); filter.setDateFilter(QDate(), QDate::currentDate()); QList list; timer.start(); for (int i = 0; i < 100; ++i) { MyMoneyFile::instance()->transactionList(list, filter); measurement[i != 0] = timer.elapsed(); } std::cerr << "transactionList(list)" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Total time: " << (measurement[0] + measurement[1]) << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 100 << " msec" << std::endl; } MyMoneyFile::instance()->preloadCache(); } void KMyMoneyApp::slotFileNew() { KMSTATUS(i18n("Creating new document...")); slotFileClose(); if (!d->m_myMoneyView->fileOpen()) { // next line required until we move all file handling out of KMyMoneyView d->m_myMoneyView->newFile(); d->m_fileName = QUrl(); updateCaption(); NewUserWizard::Wizard *wizard = new NewUserWizard::Wizard(); if (wizard->exec() == QDialog::Accepted) { MyMoneyFileTransaction ft; MyMoneyFile* file = MyMoneyFile::instance(); try { // store the user info file->setUser(wizard->user()); // create and setup base currency file->addCurrency(wizard->baseCurrency()); file->setBaseCurrency(wizard->baseCurrency()); // create a possible institution MyMoneyInstitution inst = wizard->institution(); if (inst.name().length()) { file->addInstitution(inst); } // create a possible checking account auto acc = wizard->account(); if (acc.name().length()) { acc.setInstitutionId(inst.id()); MyMoneyAccount asset = file->asset(); file->addAccount(acc, asset); // create possible opening balance transaction if (!wizard->openingBalance().isZero()) { file->createOpeningBalanceTransaction(acc, wizard->openingBalance()); } } // import the account templates QList templates = wizard->templates(); QList::iterator it_t; for (it_t = templates.begin(); it_t != templates.end(); ++it_t) { (*it_t).importTemplate(&progressCallback); } d->m_fileName = wizard->url(); ft.commit(); KMyMoneyGlobalSettings::setFirstTimeRun(false); // FIXME This is a bit clumsy. We re-read the freshly // created file to be able to run through all the // fixup logic and then save it to keep the modified // flag off. slotFileSave(); d->m_myMoneyView->readFile(d->m_fileName); slotFileSave(); // now keep the filename in the recent files used list //KRecentFilesAction *p = dynamic_cast(action(KStandardAction::name(KStandardAction::OpenRecent))); //if(p) d->m_recentFiles->addUrl(d->m_fileName); writeLastUsedFile(d->m_fileName.url()); } catch (const MyMoneyException &) { // next line required until we move all file handling out of KMyMoneyView d->m_myMoneyView->closeFile(); } if (wizard->startSettingsAfterFinished()) slotSettings(); } else { // next line required until we move all file handling out of KMyMoneyView d->m_myMoneyView->closeFile(); } delete wizard; updateCaption(); emit fileLoaded(d->m_fileName); } } // General open void KMyMoneyApp::slotFileOpen() { KMSTATUS(i18n("Open a file.")); QString prevDir = readLastUsedDir(); QPointer dialog = new QFileDialog(this, QString(), prevDir, i18n("KMyMoney files (*.kmy *.xml);;All files")); dialog->setFileMode(QFileDialog::ExistingFile); dialog->setAcceptMode(QFileDialog::AcceptOpen); if (dialog->exec() == QDialog::Accepted && dialog != nullptr) { slotFileOpenRecent(dialog->selectedUrls().first()); } delete dialog; } void KMyMoneyApp::slotOpenDatabase() { KMSTATUS(i18n("Open a file.")); QPointer dialog = new KSelectDatabaseDlg(QIODevice::ReadWrite); if (!dialog->checkDrivers()) { delete dialog; return; } if (dialog->exec() == QDialog::Accepted && dialog != 0) { slotFileOpenRecent(dialog->selectedURL()); } delete dialog; } bool KMyMoneyApp::isImportableFile(const QUrl &url) { bool result = false; // Iterate through the plugins and see if there's a loaded plugin who can handle it - QMap::const_iterator it_plugin = d->m_importerPlugins.constBegin(); - while (it_plugin != d->m_importerPlugins.constEnd()) { + QMap::const_iterator it_plugin = d->m_plugins.importer.constBegin(); + while (it_plugin != d->m_plugins.importer.constEnd()) { if ((*it_plugin)->isMyFormat(url.path())) { result = true; break; } ++it_plugin; } // If we did not find a match, try importing it as a KMM statement file, // which is really just for testing. the statement file is not exposed // to users. - if (it_plugin == d->m_importerPlugins.constEnd()) + if (it_plugin == d->m_plugins.importer.constEnd()) if (MyMoneyStatement::isStatementFile(url.path())) result = true; // Place code here to test for QIF and other locally-supported formats // (i.e. not a plugin). If you add them here, be sure to add it to // the webConnect function. return result; } void KMyMoneyApp::slotFileOpenRecent(const QUrl &url) { KMSTATUS(i18n("Loading file...")); QUrl lastFile = d->m_fileName; // check if there are other instances which might have this file open QList list = instanceList(); QList::ConstIterator it; bool duplicate = false; #ifdef KMM_DBUS for (it = list.constBegin(); duplicate == false && it != list.constEnd(); ++it) { QDBusInterface remoteApp(*it, "/KMymoney", "org.kde.kmymoney"); QDBusReply reply = remoteApp.call("filename"); if (!reply.isValid()) { qDebug("D-Bus error while calling app->filename()"); } else { if (reply.value() == url.url()) { duplicate = true; } } } #endif if (!duplicate) { QUrl newurl = url; if ((newurl.scheme() == QLatin1String("sql"))) { const QString key = QLatin1String("driver"); // take care and convert some old url to their new counterpart QUrlQuery query(newurl); if (query.queryItemValue(key) == QLatin1String("QMYSQL3")) { // fix any old urls query.removeQueryItem(key); query.addQueryItem(key, QLatin1String("QMYSQL")); } if (query.queryItemValue(key) == QLatin1String("QSQLITE3")) { query.removeQueryItem(key); query.addQueryItem(key, QLatin1String("QSQLITE")); } newurl.setQuery(query); if (query.queryItemValue(key) == QLatin1String("QSQLITE")) { newurl.setUserInfo(QString()); newurl.setHost(QString()); } // check if a password is needed. it may be if the URL came from the last/recent file list QPointer dialog = new KSelectDatabaseDlg(QIODevice::ReadWrite, newurl); if (!dialog->checkDrivers()) { delete dialog; return; } // if we need to supply a password, then show the dialog // otherwise it isn't needed if ((query.queryItemValue("secure").toLower() == QLatin1String("yes")) && newurl.password().isEmpty()) { if (dialog->exec() == QDialog::Accepted && dialog != nullptr) { newurl = dialog->selectedURL(); } else { delete dialog; return; } } delete dialog; } if (newurl.scheme() == QLatin1String("sql") || KMyMoneyUtils::fileExists(newurl)) { slotFileClose(); if (!d->m_myMoneyView->fileOpen()) { try { if (d->m_myMoneyView->readFile(newurl)) { if ((d->m_myMoneyView->isNativeFile())) { d->m_fileName = newurl; updateCaption(); d->m_recentFiles->addUrl(newurl); writeLastUsedFile(newurl.toDisplayString(QUrl::PreferLocalFile)); } else { d->m_fileName = QUrl(); // imported files have no filename } // Check the schedules slotCheckSchedules(); } } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot open file as requested. Error was: %1", e.what())); } updateCaption(); emit fileLoaded(d->m_fileName); } else { /*fileOpen failed - should we do something or maybe fileOpen puts out the message... - it does for database*/ } } else { // newurl invalid slotFileClose(); KMessageBox::sorry(this, i18n("

%1 is either an invalid filename or the file does not exist. You can open another file or create a new one.

", url.toDisplayString(QUrl::PreferLocalFile)), i18n("File not found")); } } else { // isDuplicate KMessageBox::sorry(this, i18n("

File %1 is already opened in another instance of KMyMoney

", url.toDisplayString(QUrl::PreferLocalFile)), i18n("Duplicate open")); } } bool KMyMoneyApp::slotFileSave() { // if there's nothing changed, there's no need to save anything if (!d->m_myMoneyView->dirty()) return true; bool rc = false; KMSTATUS(i18n("Saving file...")); if (d->m_fileName.isEmpty()) return slotFileSaveAs(); d->consistencyCheck(false); /*if (myMoneyView->isDatabase()) { rc = myMoneyView->saveDatabase(m_fileName); // the 'save' function is no longer relevant for a database*/ setEnabled(false); rc = d->m_myMoneyView->saveFile(d->m_fileName, MyMoneyFile::instance()->value("kmm-encryption-key")); setEnabled(true); d->m_autoSaveTimer->stop(); updateCaption(); return rc; } void KMyMoneyApp::slotFileSaveAsFilterChanged(const QString& filter) { if (!d->m_saveEncrypted) return; if (filter.compare(QLatin1String("*.kmy"), Qt::CaseInsensitive) != 0) { d->m_saveEncrypted->setCurrentItem(0); d->m_saveEncrypted->setEnabled(false); } else { d->m_saveEncrypted->setEnabled(true); } } void KMyMoneyApp::slotManageGpgKeys() { QPointer dlg = new KGpgKeySelectionDlg(this); dlg->setKeys(d->m_additionalGpgKeys); if (dlg->exec() == QDialog::Accepted && dlg != 0) { d->m_additionalGpgKeys = dlg->keys(); d->m_additionalKeyLabel->setText(i18n("Additional encryption keys to be used: %1", d->m_additionalGpgKeys.count())); } delete dlg; } void KMyMoneyApp::slotKeySelected(int idx) { int cnt = 0; if (idx != 0) { cnt = d->m_additionalGpgKeys.count(); } d->m_additionalKeyLabel->setEnabled(idx != 0); d->m_additionalKeyButton->setEnabled(idx != 0); d->m_additionalKeyLabel->setText(i18n("Additional encryption keys to be used: %1", cnt)); } bool KMyMoneyApp::slotFileSaveAs() { bool rc = false; // in event of it being a database, ensure that all data is read into storage for saveas if (d->m_myMoneyView->isDatabase()) dynamic_cast(MyMoneyFile::instance()->storage())->fillStorage(); KMSTATUS(i18n("Saving file with a new filename...")); // fill the additional key list with the default d->m_additionalGpgKeys = KMyMoneyGlobalSettings::gpgRecipientList(); QWidget* vbox = new QWidget(); QVBoxLayout *vboxVBoxLayout = new QVBoxLayout(vbox); vboxVBoxLayout->setMargin(0); if (KGPGFile::GPGAvailable()) { QWidget* keyBox = new QWidget(vbox); QHBoxLayout *keyBoxHBoxLayout = new QHBoxLayout(keyBox); keyBoxHBoxLayout->setMargin(0); vboxVBoxLayout->addWidget(keyBox); QLabel *keyLabel = new QLabel(i18n("Encryption key to be used"), keyBox); keyBoxHBoxLayout->addWidget(keyLabel); d->m_saveEncrypted = new KComboBox(keyBox); keyBoxHBoxLayout->addWidget(d->m_saveEncrypted); QWidget* labelBox = new QWidget(vbox); QHBoxLayout *labelBoxHBoxLayout = new QHBoxLayout(labelBox); labelBoxHBoxLayout->setMargin(0); vboxVBoxLayout->addWidget(labelBox); d->m_additionalKeyLabel = new QLabel(i18n("Additional encryption keys to be used: %1", d->m_additionalGpgKeys.count()), labelBox); labelBoxHBoxLayout->addWidget(d->m_additionalKeyLabel); d->m_additionalKeyButton = new QPushButton(i18n("Manage additional keys"), labelBox); labelBoxHBoxLayout->addWidget(d->m_additionalKeyButton); connect(d->m_additionalKeyButton, SIGNAL(clicked()), this, SLOT(slotManageGpgKeys())); connect(d->m_saveEncrypted, SIGNAL(activated(int)), this, SLOT(slotKeySelected(int))); // fill the secret key list and combo box QStringList keyList; KGPGFile::secretKeyList(keyList); d->m_saveEncrypted->addItem(i18n("No encryption")); for (QStringList::iterator it = keyList.begin(); it != keyList.end(); ++it) { QStringList fields = (*it).split(':', QString::SkipEmptyParts); if (fields[0] != recoveryKeyId) { // replace parenthesis in name field with brackets auto name = fields[1]; name.replace('(', "["); name.replace(')', "]"); name = QString("%1 (0x%2)").arg(name).arg(fields[0]); d->m_saveEncrypted->addItem(name); if (name.contains(KMyMoneyGlobalSettings::gpgRecipient())) { d->m_saveEncrypted->setCurrentItem(name); } } } } QString prevDir; // don't prompt file name if not a native file if (d->m_myMoneyView->isNativeFile()) prevDir = readLastUsedDir(); QPointer dlg = new QFileDialog(this, i18n("Save As"), prevDir, QString(QLatin1String("%2 (%1);;")).arg(QStringLiteral("*.kmy")).arg(i18nc("KMyMoney (Filefilter)", "KMyMoney files")) + QString(QLatin1String("%2 (%1);;")).arg(QStringLiteral("*.xml")).arg(i18nc("XML (Filefilter)", "XML files")) + QString(QLatin1String("%2 (%1);;")).arg(QStringLiteral("*.anon.xml")).arg(i18nc("Anonymous (Filefilter)", "Anonymous files")) + QString(QLatin1String("%2 (%1);;")).arg(QStringLiteral("*")).arg(i18nc("All files (Filefilter)", "All files"))); dlg->setAcceptMode(QFileDialog::AcceptSave); connect(dlg, SIGNAL(filterChanged(QString)), this, SLOT(slotFileSaveAsFilterChanged(QString))); if (dlg->exec() == QDialog::Accepted && dlg != 0) { QUrl newURL = dlg->selectedUrls().first(); if (!newURL.fileName().isEmpty()) { d->consistencyCheck(false); // deleting the dialog will delete the combobox pointed to by d->m_saveEncrypted so get the key name here QString selectedKeyName; if (d->m_saveEncrypted && d->m_saveEncrypted->currentIndex() != 0) selectedKeyName = d->m_saveEncrypted->currentText(); d->m_saveEncrypted = 0; QString newName = newURL.toDisplayString(QUrl::PreferLocalFile); // append extension if not present if (!newName.endsWith(QLatin1String(".kmy"), Qt::CaseInsensitive) && !newName.endsWith(QLatin1String(".xml"), Qt::CaseInsensitive)) newName.append(QLatin1String(".kmy")); newURL = QUrl::fromUserInput(newName); d->m_recentFiles->addUrl(newURL); setEnabled(false); // If this is the anonymous file export, just save it, don't actually take the // name, or remember it! Don't even try to encrypt it if (newName.endsWith(QLatin1String(".anon.xml"), Qt::CaseInsensitive)) rc = d->m_myMoneyView->saveFile(newURL); else { d->m_fileName = newURL; QString encryptionKeys; QRegExp keyExp(".* \\((.*)\\)"); if (keyExp.indexIn(selectedKeyName) != -1) { encryptionKeys = keyExp.cap(1); } if (!d->m_additionalGpgKeys.isEmpty()) { if (!encryptionKeys.isEmpty()) encryptionKeys.append(QLatin1Char(',')); encryptionKeys.append(d->m_additionalGpgKeys.join(QLatin1Char(','))); } rc = d->m_myMoneyView->saveFile(d->m_fileName, encryptionKeys); //write the directory used for this file as the default one for next time. writeLastUsedDir(newURL.toDisplayString(QUrl::RemoveFilename | QUrl::PreferLocalFile | QUrl::StripTrailingSlash)); writeLastUsedFile(newName); } d->m_autoSaveTimer->stop(); setEnabled(true); } } delete dlg; updateCaption(); return rc; } void KMyMoneyApp::slotSaveAsDatabase() { saveAsDatabase(); } bool KMyMoneyApp::saveAsDatabase() { bool rc = false; QUrl oldUrl; // in event of it being a database, ensure that all data is read into storage for saveas if (d->m_myMoneyView->isDatabase()) { dynamic_cast(MyMoneyFile::instance()->storage())->fillStorage(); oldUrl = d->m_fileName.isEmpty() ? lastOpenedURL() : d->m_fileName; } KMSTATUS(i18n("Saving file to database...")); QPointer dialog = new KSelectDatabaseDlg(QIODevice::WriteOnly); QUrl url = oldUrl; if (!dialog->checkDrivers()) { delete dialog; return (false); } while (oldUrl == url && dialog->exec() == QDialog::Accepted && dialog != 0) { url = dialog->selectedURL(); // If the protocol is SQL for the old and new, and the hostname and database names match // Let the user know that the current database cannot be saved on top of itself. if (url.scheme() == "sql" && oldUrl.scheme() == "sql" && oldUrl.host() == url.host() && QUrlQuery(oldUrl).queryItemValue("driver") == QUrlQuery(url).queryItemValue("driver") && oldUrl.path().right(oldUrl.path().length() - 1) == url.path().right(url.path().length() - 1)) { KMessageBox::sorry(this, i18n("Cannot save to current database.")); } else { try { rc = d->m_myMoneyView->saveAsDatabase(url); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot save to current database: %1", e.what())); } } } delete dialog; if (rc) { //KRecentFilesAction *p = dynamic_cast(action("file_open_recent")); //if(p) d->m_recentFiles->addUrl(url); writeLastUsedFile(url.toDisplayString(QUrl::PreferLocalFile)); } d->m_autoSaveTimer->stop(); updateCaption(); return rc; } void KMyMoneyApp::slotFileCloseWindow() { KMSTATUS(i18n("Closing window...")); if (d->m_myMoneyView->dirty()) { int answer = askSaveOnClose(); if (answer == KMessageBox::Cancel) return; else if (answer == KMessageBox::Yes) slotFileSave(); } close(); } void KMyMoneyApp::slotFileClose() { bool okToSelect = true; // check if transaction editor is open and ask user what he wants to do // slotTransactionsCancelOrEnter(okToSelect); if (!okToSelect) return; // no update status here, as we might delete the status too early. if (d->m_myMoneyView->dirty()) { int answer = askSaveOnClose(); if (answer == KMessageBox::Cancel) return; else if (answer == KMessageBox::Yes) slotFileSave(); } d->closeFile(); } void KMyMoneyApp::slotFileQuit() { // don't modify the status message here as this will prevent quit from working!! // See the beginning of queryClose() and isReady() why. Thomas Baumgart 2005-10-17 bool quitApplication = true; QList memberList = KMainWindow::memberList(); if (!memberList.isEmpty()) { QList::const_iterator w_it = memberList.constBegin(); for (; w_it != memberList.constEnd(); ++w_it) { // only close the window if the closeEvent is accepted. If the user presses Cancel on the saveModified() dialog, // the window and the application stay open. if (!(*w_it)->close()) { quitApplication = false; break; } } } // We will only quit if all windows were processed and not cancelled if (quitApplication) { QCoreApplication::quit(); } } void KMyMoneyApp::slotShowTransactionDetail() { } void KMyMoneyApp::slotHideReconciledTransactions() { KMyMoneyGlobalSettings::setHideReconciledTransactions(pActions[Action::ViewHideReconciled]->isChecked()); d->m_myMoneyView->slotRefreshViews(); } void KMyMoneyApp::slotHideUnusedCategories() { KMyMoneyGlobalSettings::setHideUnusedCategory(pActions[Action::ViewHideCategories]->isChecked()); d->m_myMoneyView->slotRefreshViews(); } void KMyMoneyApp::slotShowAllAccounts() { KMyMoneyGlobalSettings::setShowAllAccounts(pActions[Action::ViewShowAll]->isChecked()); d->m_myMoneyView->slotRefreshViews(); } #ifdef KMM_DEBUG void KMyMoneyApp::slotToggleTraces() { MyMoneyTracer::onOff(pActions[Action::DebugTraces]->isChecked() ? 1 : 0); } #endif void KMyMoneyApp::slotToggleTimers() { extern bool timersOn; // main.cpp timersOn = pActions[Action::DebugTimers]->isChecked(); } QString KMyMoneyApp::slotStatusMsg(const QString &text) { /////////////////////////////////////////////////////////////////// // change status message permanently QString previousMessage = d->m_statusLabel->text(); d->m_applicationIsReady = false; QString currentMessage = text; if (currentMessage.isEmpty() || currentMessage == i18nc("Application is ready to use", "Ready.")) { d->m_applicationIsReady = true; currentMessage = i18nc("Application is ready to use", "Ready."); } statusBar()->clearMessage(); d->m_statusLabel->setText(currentMessage); return previousMessage; } void KMyMoneyApp::ready() { slotStatusMsg(QString()); } bool KMyMoneyApp::isReady() { return d->m_applicationIsReady; } void KMyMoneyApp::slotStatusProgressBar(int current, int total) { if (total == -1 && current == -1) { // reset if (d->m_progressTimer) { d->m_progressTimer->start(500); // remove from screen in 500 msec d->m_progressBar->setValue(d->m_progressBar->maximum()); } } else if (total != 0) { // init d->m_progressTimer->stop(); d->m_progressBar->setMaximum(total); d->m_progressBar->setValue(0); d->m_progressBar->show(); } else { // update QTime currentTime = QTime::currentTime(); // only process painting if last update is at least 250 ms ago if (abs(d->m_lastUpdate.msecsTo(currentTime)) > 250) { d->m_progressBar->setValue(current); d->m_lastUpdate = currentTime; } } } void KMyMoneyApp::slotStatusProgressDone() { d->m_progressTimer->stop(); d->m_progressBar->reset(); d->m_progressBar->hide(); d->m_progressBar->setValue(0); } void KMyMoneyApp::progressCallback(int current, int total, const QString& msg) { if (!msg.isEmpty()) kmymoney->slotStatusMsg(msg); kmymoney->slotStatusProgressBar(current, total); } void KMyMoneyApp::slotFileViewPersonal() { if (!d->m_myMoneyView->fileOpen()) { KMessageBox::information(this, i18n("No KMyMoneyFile open")); return; } KMSTATUS(i18n("Viewing personal data...")); MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyPayee user = file->user(); QPointer editPersonalDataDlg = new EditPersonalDataDlg(user.name(), user.address(), user.city(), user.state(), user.postcode(), user.telephone(), user.email(), this, i18n("Edit Personal Data")); if (editPersonalDataDlg->exec() == QDialog::Accepted && editPersonalDataDlg != 0) { user.setName(editPersonalDataDlg->userName()); user.setAddress(editPersonalDataDlg->userStreet()); user.setCity(editPersonalDataDlg->userTown()); user.setState(editPersonalDataDlg->userCountry()); user.setPostcode(editPersonalDataDlg->userPostcode()); user.setTelephone(editPersonalDataDlg->userTelephone()); user.setEmail(editPersonalDataDlg->userEmail()); MyMoneyFileTransaction ft; try { file->setUser(user); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to store user information: %1", e.what())); } } delete editPersonalDataDlg; } void KMyMoneyApp::slotFileFileInfo() { if (!d->m_myMoneyView->fileOpen()) { KMessageBox::information(this, i18n("No KMyMoneyFile open")); return; } QFile g("kmymoney.dump"); g.open(QIODevice::WriteOnly); QDataStream st(&g); MyMoneyStorageDump dumper; dumper.writeStream(st, dynamic_cast(MyMoneyFile::instance()->storage())); g.close(); } void KMyMoneyApp::slotLoadAccountTemplates() { KMSTATUS(i18n("Importing account templates.")); int rc; QPointer dlg = new KLoadTemplateDlg(); if ((rc = dlg->exec()) == QDialog::Accepted && dlg != 0) { MyMoneyFileTransaction ft; try { // import the account templates QList templates = dlg->templates(); QList::iterator it_t; for (it_t = templates.begin(); it_t != templates.end(); ++it_t) { (*it_t).importTemplate(&progressCallback); } ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to import template(s): %1, thrown in %2:%3", e.what(), e.file(), e.line())); } } delete dlg; } void KMyMoneyApp::slotSaveAccountTemplates() { KMSTATUS(i18n("Exporting account templates.")); QString savePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/templates/" + QLocale().name(); QDir d(savePath); if (!d.exists()) d.mkpath(savePath); QString newName = QFileDialog::getSaveFileName(this, i18n("Save as..."), savePath, i18n("KMyMoney template files (*.kmt);;All files (*)")); // // If there is no file extension, then append a .kmt at the end of the file name. // If there is a file extension, make sure it is .kmt, delete any others. // if (!newName.isEmpty()) { // find last . delimiter int nLoc = newName.lastIndexOf('.'); if (nLoc != -1) { QString strExt, strTemp; strTemp = newName.left(nLoc + 1); strExt = newName.right(newName.length() - (nLoc + 1)); if ((strExt.indexOf("kmt", 0, Qt::CaseInsensitive) == -1)) { strTemp.append("kmt"); //append to make complete file name newName = strTemp; } } else { newName.append(".kmt"); } if (okToWriteFile(QUrl::fromLocalFile(newName))) { QPointer dlg = new KTemplateExportDlg(this); if (dlg->exec() == QDialog::Accepted && dlg) { MyMoneyTemplate templ; templ.setTitle(dlg->title()); templ.setShortDescription(dlg->shortDescription()); templ.setLongDescription(dlg->longDescription()); templ.exportTemplate(&progressCallback); templ.saveTemplate(QUrl::fromLocalFile(newName)); } delete dlg; } } } bool KMyMoneyApp::okToWriteFile(const QUrl &url) { Q_UNUSED(url) // check if the file exists and warn the user bool reallySaveFile = true; if (KMyMoneyUtils::fileExists(url)) { if (KMessageBox::warningYesNo(this, QLatin1String("") + i18n("The file %1 already exists. Do you really want to overwrite it?", url.toDisplayString(QUrl::PreferLocalFile)) + QLatin1String(""), i18n("File already exists")) != KMessageBox::Yes) reallySaveFile = false; } return reallySaveFile; } void KMyMoneyApp::slotSettings() { // if we already have an instance of the settings dialog, then use it if (KConfigDialog::showDialog("KMyMoney-Settings")) return; // otherwise, we have to create it - KConfigDialog* dlg = new KSettingsKMyMoney(this, "KMyMoney-Settings", KMyMoneyGlobalSettings::self()); - connect(dlg, &KConfigDialog::settingsChanged, this, &KMyMoneyApp::slotUpdateConfiguration); + auto dlg = new KSettingsKMyMoney(this, "KMyMoney-Settings", KMyMoneyGlobalSettings::self()); + connect(dlg, &KSettingsKMyMoney::settingsChanged, this, &KMyMoneyApp::slotUpdateConfiguration); dlg->show(); } void KMyMoneyApp::slotShowCredits() { KAboutData aboutData = initializeCreditsData(); KAboutApplicationDialog dlg(aboutData, this); dlg.exec(); } -void KMyMoneyApp::slotUpdateConfiguration() +void KMyMoneyApp::slotUpdateConfiguration(const QString &dialogName) { + if(dialogName.compare(QLatin1String("Plugins")) == 0) { + KMyMoneyPlugin::pluginHandling(KMyMoneyPlugin::Action::Reorganize, d->m_plugins, this, guiFactory()); + onlineJobAdministration::instance()->updateActions(); + return; + } MyMoneyTransactionFilter::setFiscalYearStart(KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay()); #ifdef ENABLE_UNFINISHEDFEATURES LedgerSeperator::setFirstFiscalDate(KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay()); #endif d->m_myMoneyView->updateViewType(); // update the sql storage module settings MyMoneyStorageSql::setStartDate(KMyMoneyGlobalSettings::startDate().date()); // update the report module settings MyMoneyReport::setLineWidth(KMyMoneyGlobalSettings::lineWidth()); // update the holiday region configuration setHolidayRegion(KMyMoneyGlobalSettings::holidayRegion()); d->m_myMoneyView->slotRefreshViews(); // re-read autosave configuration d->m_autoSaveEnabled = KMyMoneyGlobalSettings::autoSaveFile(); d->m_autoSavePeriod = KMyMoneyGlobalSettings::autoSavePeriod(); // stop timer if turned off but running if (d->m_autoSaveTimer->isActive() && !d->m_autoSaveEnabled) { d->m_autoSaveTimer->stop(); } // start timer if turned on and needed but not running if (!d->m_autoSaveTimer->isActive() && d->m_autoSaveEnabled && d->m_myMoneyView->dirty()) { d->m_autoSaveTimer->setSingleShot(true); d->m_autoSaveTimer->start(d->m_autoSavePeriod * 60 * 1000); } d->setThemedCSS(); // check if the recovery key is still valid or expires soon if (KMyMoneySettings::writeDataEncrypted() && KMyMoneySettings::encryptRecover()) { if (KGPGFile::GPGAvailable()) { KGPGFile file; QDateTime expirationDate = file.keyExpires(QLatin1String(recoveryKeyId)); if (expirationDate.isValid() && QDateTime::currentDateTime().daysTo(expirationDate) <= RECOVER_KEY_EXPIRATION_WARNING) { bool skipMessage = false; //get global config object for our app. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); KConfigGroup grp; QDate lastWarned; if (kconfig) { grp = d->m_config->group("General Options"); lastWarned = grp.readEntry("LastRecoverKeyExpirationWarning", QDate()); if (QDate::currentDate() == lastWarned) { skipMessage = true; } } if (!skipMessage) { if (kconfig) { grp.writeEntry("LastRecoverKeyExpirationWarning", QDate::currentDate()); } KMessageBox::information(this, i18np("You have configured KMyMoney to use GPG to protect your data and to encrypt your data also with the KMyMoney recover key. This key is about to expire in %1 day. Please update the key from a keyserver using your GPG frontend (e.g. KGPG).", "You have configured KMyMoney to use GPG to protect your data and to encrypt your data also with the KMyMoney recover key. This key is about to expire in %1 days. Please update the key from a keyserver using your GPG frontend (e.g. KGPG).", QDateTime::currentDateTime().daysTo(expirationDate)), i18n("Recover key expires soon")); } } } } } void KMyMoneyApp::slotBackupFile() { // Save the file first so isLocalFile() works if (d->m_myMoneyView && d->m_myMoneyView->dirty()) { if (KMessageBox::questionYesNo(this, i18n("The file must be saved first " "before it can be backed up. Do you want to continue?")) == KMessageBox::No) { return; } slotFileSave(); } if (d->m_fileName.isEmpty()) return; if (!d->m_fileName.isLocalFile()) { KMessageBox::sorry(this, i18n("The current implementation of the backup functionality only supports local files as source files. Your current source file is '%1'.", d->m_fileName.url()), i18n("Local files only")); return; } QPointer backupDlg = new KBackupDlg(this); #ifdef Q_OS_WIN backupDlg->mountCheckBox->setEnabled(false); #endif int returncode = backupDlg->exec(); if (returncode == QDialog::Accepted && backupDlg != 0) { d->m_backupMount = backupDlg->mountCheckBox(); d->m_proc.clearProgram(); d->m_backupState = BACKUP_MOUNTING; d->m_mountpoint = backupDlg->mountPoint(); if (d->m_backupMount) { slotBackupMount(); } else { progressCallback(0, 300, ""); #ifdef Q_OS_WIN d->m_ignoreBackupExitCode = true; QTimer::singleShot(0, this, SLOT(slotBackupHandleEvents())); #else // If we don't have to mount a device, we just issue // a dummy command to start the copy operation d->m_proc.setProgram("true"); d->m_proc.start(); #endif } } delete backupDlg; } void KMyMoneyApp::slotBackupMount() { progressCallback(0, 300, i18n("Mounting %1", d->m_mountpoint)); d->m_proc.setProgram("mount"); d->m_proc << d->m_mountpoint; d->m_proc.start(); } bool KMyMoneyApp::slotBackupWriteFile() { QFileInfo fi(d->m_fileName.fileName()); QString today = QDate::currentDate().toString("-yyyy-MM-dd.") + fi.suffix(); QString backupfile = d->m_mountpoint + '/' + d->m_fileName.fileName(); KMyMoneyUtils::appendCorrectFileExt(backupfile, today); // check if file already exists and ask what to do QFile f(backupfile); if (f.exists()) { int answer = KMessageBox::warningContinueCancel(this, i18n("Backup file for today exists on that device. Replace?"), i18n("Backup"), KGuiItem(i18n("&Replace"))); if (answer == KMessageBox::Cancel) { return false; } } progressCallback(50, 0, i18n("Writing %1", backupfile)); d->m_proc.clearProgram(); #ifdef Q_OS_WIN d->m_proc << "cmd.exe" << "/c" << "copy" << "/b" << "/y"; d->m_proc << (QDir::toNativeSeparators(d->m_fileName.toLocalFile()) + "+ nul") << QDir::toNativeSeparators(backupfile); #else d->m_proc << "cp" << "-f"; d->m_proc << d->m_fileName.toLocalFile() << backupfile; #endif d->m_backupState = BACKUP_COPYING; d->m_proc.start(); return true; } void KMyMoneyApp::slotBackupUnmount() { progressCallback(250, 0, i18n("Unmounting %1", d->m_mountpoint)); d->m_proc.clearProgram(); d->m_proc.setProgram("umount"); d->m_proc << d->m_mountpoint; d->m_backupState = BACKUP_UNMOUNTING; d->m_proc.start(); } void KMyMoneyApp::slotBackupFinish() { d->m_backupState = BACKUP_IDLE; progressCallback(-1, -1, QString()); ready(); } void KMyMoneyApp::slotBackupHandleEvents() { switch (d->m_backupState) { case BACKUP_MOUNTING: if (d->m_ignoreBackupExitCode || (d->m_proc.exitStatus() == QProcess::NormalExit && d->m_proc.exitCode() == 0)) { d->m_ignoreBackupExitCode = false; d->m_backupResult = 0; if (!slotBackupWriteFile()) { d->m_backupResult = 1; if (d->m_backupMount) slotBackupUnmount(); else slotBackupFinish(); } } else { KMessageBox::information(this, i18n("Error mounting device"), i18n("Backup")); d->m_backupResult = 1; if (d->m_backupMount) slotBackupUnmount(); else slotBackupFinish(); } break; case BACKUP_COPYING: if (d->m_proc.exitStatus() == QProcess::NormalExit && d->m_proc.exitCode() == 0) { if (d->m_backupMount) { slotBackupUnmount(); } else { progressCallback(300, 0, i18nc("Backup done", "Done")); KMessageBox::information(this, i18n("File successfully backed up"), i18n("Backup")); slotBackupFinish(); } } else { qDebug("copy exit code is %d", d->m_proc.exitCode()); d->m_backupResult = 1; KMessageBox::information(this, i18n("Error copying file to device"), i18n("Backup")); if (d->m_backupMount) slotBackupUnmount(); else slotBackupFinish(); } break; case BACKUP_UNMOUNTING: if (d->m_proc.exitStatus() == QProcess::NormalExit && d->m_proc.exitCode() == 0) { progressCallback(300, 0, i18nc("Backup done", "Done")); if (d->m_backupResult == 0) KMessageBox::information(this, i18n("File successfully backed up"), i18n("Backup")); } else { KMessageBox::information(this, i18n("Error unmounting device"), i18n("Backup")); } slotBackupFinish(); break; default: qWarning("Unknown state for backup operation!"); progressCallback(-1, -1, QString()); ready(); break; } } void KMyMoneyApp::slotShowTipOfTheDay() { KTipDialog::showTip(d->m_myMoneyView, "", true); } void KMyMoneyApp::slotShowPreviousView() { } void KMyMoneyApp::slotShowNextView() { } void KMyMoneyApp::slotGenerateSql() { QPointer editor = new KGenerateSqlDlg(this); editor->setObjectName("Generate Database SQL"); editor->exec(); delete editor; } void KMyMoneyApp::slotToolsStartKCalc() { QString cmd = KMyMoneyGlobalSettings::externalCalculator(); // if none is present, we fall back to the default if (cmd.isEmpty()) { #if defined(Q_OS_WIN32) cmd = QLatin1String("calc"); #elif defined(Q_OS_MAC) cmd = QLatin1String("open -a Calculator"); #else cmd = QLatin1String("kcalc"); #endif } KRun::runCommand(cmd, this); } void KMyMoneyApp::slotFindTransaction() { if (d->m_searchDlg == 0) { d->m_searchDlg = new KFindTransactionDlg(this); connect(d->m_searchDlg, SIGNAL(destroyed()), this, SLOT(slotCloseSearchDialog())); connect(d->m_searchDlg, SIGNAL(transactionSelected(QString,QString)), d->m_myMoneyView, SLOT(slotLedgerSelected(QString,QString))); } d->m_searchDlg->show(); d->m_searchDlg->raise(); d->m_searchDlg->activateWindow(); } void KMyMoneyApp::slotCloseSearchDialog() { if (d->m_searchDlg) d->m_searchDlg->deleteLater(); d->m_searchDlg = 0; } void KMyMoneyApp::createAccount(MyMoneyAccount& newAccount, MyMoneyAccount& parentAccount, MyMoneyAccount& brokerageAccount, MyMoneyMoney openingBal) { MyMoneyFile *file = MyMoneyFile::instance(); try { const MyMoneySecurity& sec = file->security(newAccount.currencyId()); // Check the opening balance if (openingBal.isPositive() && newAccount.accountGroup() == eMyMoney::Account::Type::Liability) { QString message = i18n("This account is a liability and if the " "opening balance represents money owed, then it should be negative. " "Negate the amount?\n\n" "Please click Yes to change the opening balance to %1,\n" "Please click No to leave the amount as %2,\n" "Please click Cancel to abort the account creation." , MyMoneyUtils::formatMoney(-openingBal, newAccount, sec) , MyMoneyUtils::formatMoney(openingBal, newAccount, sec)); int ans = KMessageBox::questionYesNoCancel(this, message); if (ans == KMessageBox::Yes) { openingBal = -openingBal; } else if (ans == KMessageBox::Cancel) return; } file->createAccount(newAccount, parentAccount, brokerageAccount, openingBal); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to add account: %1", e.what())); } } void KMyMoneyApp::slotInvestmentNew(MyMoneyAccount& account, const MyMoneyAccount& parent) { KNewInvestmentWizard::newInvestment(account, parent); } void KMyMoneyApp::slotCategoryNew(MyMoneyAccount& account, const MyMoneyAccount& parent) { KNewAccountDlg::newCategory(account, parent); } void KMyMoneyApp::slotCategoryNew(MyMoneyAccount& account) { KNewAccountDlg::newCategory(account, MyMoneyAccount()); } void KMyMoneyApp::slotAccountNew(MyMoneyAccount& account) { NewAccountWizard::Wizard::newAccount(account); } void KMyMoneyApp::createSchedule(MyMoneySchedule newSchedule, MyMoneyAccount& newAccount) { MyMoneyFile* file = MyMoneyFile::instance(); // Add the schedule only if one exists // // Remember to modify the first split to reference the newly created account if (!newSchedule.name().isEmpty()) { 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!"); } MyMoneyFileTransaction ft; try { file->addSchedule(newSchedule); // in case of a loan account, we keep a reference to this // schedule in the account if (newAccount.accountType() == eMyMoney::Account::Type::Loan || newAccount.accountType() == eMyMoney::Account::Type::AssetLoan) { newAccount.setValue("schedule", newSchedule.id()); file->modifyAccount(newAccount); } ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to add scheduled transaction: %1", e.what())); } } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to add scheduled transaction: %1", e.what())); } } } QList > KMyMoneyApp::Private::automaticReconciliation(const MyMoneyAccount &account, const QList > &transactions, const MyMoneyMoney &amount) { static const int NR_OF_STEPS_LIMIT = 300000; static const int PROGRESSBAR_STEPS = 1000; QList > result = transactions; KMSTATUS(i18n("Running automatic reconciliation")); int progressBarIndex = 0; kmymoney->slotStatusProgressBar(progressBarIndex, NR_OF_STEPS_LIMIT / PROGRESSBAR_STEPS); // optimize the most common case - all transactions should be cleared QListIterator > itTransactionSplitResult(result); MyMoneyMoney transactionsBalance; while (itTransactionSplitResult.hasNext()) { const QPair &transactionSplit = itTransactionSplitResult.next(); transactionsBalance += transactionSplit.second.shares(); } if (amount == transactionsBalance) { result = transactions; return result; } kmymoney->slotStatusProgressBar(progressBarIndex++, 0); // only one transaction is uncleared itTransactionSplitResult.toFront(); int index = 0; while (itTransactionSplitResult.hasNext()) { const QPair &transactionSplit = itTransactionSplitResult.next(); if (transactionsBalance - transactionSplit.second.shares() == amount) { result.removeAt(index); return result; } index++; } kmymoney->slotStatusProgressBar(progressBarIndex++, 0); // more than one transaction is uncleared - apply the algorithm result.clear(); const MyMoneySecurity &security = MyMoneyFile::instance()->security(account.currencyId()); double precision = 0.1 / account.fraction(security); QList sumList; sumList << MyMoneyMoney(); QMap > > sumToComponentsMap; // compute the possible matches QListIterator > itTransactionSplit(transactions); while (itTransactionSplit.hasNext()) { const QPair &transactionSplit = itTransactionSplit.next(); QListIterator itSum(sumList); QList tempList; while (itSum.hasNext()) { const MyMoneyMoney &sum = itSum.next(); QList > splitIds; splitIds << qMakePair(transactionSplit.first.id(), transactionSplit.second.id()); if (sumToComponentsMap.contains(sum)) { if (sumToComponentsMap.value(sum).contains(qMakePair(transactionSplit.first.id(), transactionSplit.second.id()))) { continue; } splitIds.append(sumToComponentsMap.value(sum)); } tempList << transactionSplit.second.shares() + sum; sumToComponentsMap[transactionSplit.second.shares() + sum] = splitIds; int size = sumToComponentsMap.size(); if (size % PROGRESSBAR_STEPS == 0) { kmymoney->slotStatusProgressBar(progressBarIndex++, 0); } if (size > NR_OF_STEPS_LIMIT) { return result; // it's taking too much resources abort the algorithm } } QList unionList; unionList.append(tempList); unionList.append(sumList); qSort(unionList); sumList.clear(); MyMoneyMoney smallestSumFromUnion = unionList.first(); sumList.append(smallestSumFromUnion); QListIterator itUnion(unionList); while (itUnion.hasNext()) { MyMoneyMoney sumFromUnion = itUnion.next(); if (smallestSumFromUnion < MyMoneyMoney(1 - precision / transactions.size())*sumFromUnion) { smallestSumFromUnion = sumFromUnion; sumList.append(sumFromUnion); } } } kmymoney->slotStatusProgressBar(NR_OF_STEPS_LIMIT / PROGRESSBAR_STEPS, 0); if (sumToComponentsMap.contains(amount)) { QListIterator > itTransactionSplit(transactions); while (itTransactionSplit.hasNext()) { const QPair &transactionSplit = itTransactionSplit.next(); const QList > &splitIds = sumToComponentsMap.value(amount); if (splitIds.contains(qMakePair(transactionSplit.first.id(), transactionSplit.second.id()))) { result.append(transactionSplit); } } } #ifdef KMM_DEBUG qDebug("For the amount %s a number of %d possible sums where computed from the set of %d transactions: ", qPrintable(MyMoneyUtils::formatMoney(amount, security)), sumToComponentsMap.size(), transactions.size()); #endif kmymoney->slotStatusProgressBar(-1, -1); return result; } void KMyMoneyApp::slotReparentAccount(const MyMoneyAccount& _src, const MyMoneyInstitution& _dst) { MyMoneyAccount src(_src); src.setInstitutionId(_dst.id()); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifyAccount(src); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("

%1 cannot be moved to institution %2. Reason: %3

", src.name(), _dst.name(), e.what())); } } void KMyMoneyApp::slotReparentAccount(const MyMoneyAccount& _src, const MyMoneyAccount& _dst) { MyMoneyAccount src(_src); MyMoneyAccount dst(_dst); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->reparentAccount(src, dst); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("

%1 cannot be moved to %2. Reason: %3

", src.name(), dst.name(), e.what())); } } void KMyMoneyApp::slotScheduleNew(const MyMoneyTransaction& _t, eMyMoney::Schedule::Occurrence occurrence) { KEditScheduleDlg::newSchedule(_t, occurrence); } void KMyMoneyApp::slotPayeeNew(const QString& newnameBase, QString& id) { KMyMoneyUtils::newPayee(newnameBase, id); } void KMyMoneyApp::slotNewFeature() { } // move a stock transaction from one investment account to another void KMyMoneyApp::Private::moveInvestmentTransaction(const QString& /*fromId*/, const QString& toId, const MyMoneyTransaction& tx) { MyMoneyAccount toInvAcc = MyMoneyFile::instance()->account(toId); MyMoneyTransaction t(tx); // first determine which stock we are dealing with. // fortunately, investment transactions have only one stock involved QString stockAccountId; QString stockSecurityId; MyMoneySplit s; foreach (const auto split, t.splits()) { stockAccountId = split.accountId(); stockSecurityId = MyMoneyFile::instance()->account(stockAccountId).currencyId(); if (!MyMoneyFile::instance()->security(stockSecurityId).isCurrency()) { s = split; break; } } // Now check the target investment account to see if it // contains a stock with this id QString newStockAccountId; foreach (const auto sAccount, toInvAcc.accountList()) { if (MyMoneyFile::instance()->account(sAccount).currencyId() == stockSecurityId) { newStockAccountId = sAccount; break; } } // if it doesn't exist, we need to add it as a copy of the old one // no 'copyAccount()' function?? if (newStockAccountId.isEmpty()) { MyMoneyAccount stockAccount = MyMoneyFile::instance()->account(stockAccountId); MyMoneyAccount newStock; newStock.setName(stockAccount.name()); newStock.setNumber(stockAccount.number()); newStock.setDescription(stockAccount.description()); newStock.setInstitutionId(stockAccount.institutionId()); newStock.setOpeningDate(stockAccount.openingDate()); newStock.setAccountType(stockAccount.accountType()); newStock.setCurrencyId(stockAccount.currencyId()); newStock.setClosed(stockAccount.isClosed()); MyMoneyFile::instance()->addAccount(newStock, toInvAcc); newStockAccountId = newStock.id(); } // now update the split and the transaction s.setAccountId(newStockAccountId); t.modifySplit(s); MyMoneyFile::instance()->modifyTransaction(t); } void KMyMoneyApp::showContextMenu(const QString& containerName) { QWidget* w = factory()->container(containerName, this); QMenu *menu = dynamic_cast(w); if (menu) menu->exec(QCursor::pos()); else qDebug("menu '%s' not found: w = %p, menu = %p", qPrintable(containerName), w, menu); } void KMyMoneyApp::slotPrintView() { d->m_myMoneyView->slotPrintView(); } void KMyMoneyApp::updateCaption(bool skipActions) { QString caption; caption = d->m_fileName.fileName(); if (caption.isEmpty() && d->m_myMoneyView && d->m_myMoneyView->fileOpen()) caption = i18n("Untitled"); // MyMoneyFile::instance()->dirty() throws an exception, if // there's no storage object available. In this case, we // assume that the storage object is not changed. Actually, // this can only happen if we are newly created early on. bool modified; try { modified = MyMoneyFile::instance()->dirty(); } catch (const MyMoneyException &) { modified = false; skipActions = true; } #ifdef KMM_DEBUG caption += QString(" (%1 x %2)").arg(width()).arg(height()); #endif setCaption(caption, modified); if (!skipActions) { d->m_myMoneyView->enableViewsIfFileOpen(); slotUpdateActions(); } } void KMyMoneyApp::slotUpdateActions() { const auto file = MyMoneyFile::instance(); const bool fileOpen = d->m_myMoneyView->fileOpen(); const bool modified = file->dirty(); // const bool importRunning = (d->m_smtReader != 0); auto aC = actionCollection(); // ************* // Disabling actions to be disabled at this point // ************* { static const QVector disabledActions { Action::MapOnlineAccount, Action::UnmapOnlineAccount, Action::UpdateAccount, Action::UpdateAllAccounts }; for (const auto& a : disabledActions) pActions[a]->setEnabled(false); } // ************* // Disabling actions based on conditions // ************* { QString tooltip = i18n("Create a new transaction"); const QVector> actionStates { {qMakePair(Action::FileOpenDatabase, true)}, {qMakePair(Action::FileSaveAsDatabase, fileOpen)}, {qMakePair(Action::FilePersonalData, fileOpen)}, {qMakePair(Action::FileBackup, (fileOpen && !d->m_myMoneyView->isDatabase()))}, {qMakePair(Action::FileInformation, fileOpen)}, {qMakePair(Action::FileImportTemplate, fileOpen/* && !importRunning*/)}, {qMakePair(Action::FileExportTemplate, fileOpen/* && !importRunning*/)}, #ifdef KMM_DEBUG {qMakePair(Action::FileDump, fileOpen)}, #endif {qMakePair(Action::EditFindTransaction, fileOpen)}, {qMakePair(Action::ToolCurrencies, fileOpen)}, {qMakePair(Action::ToolPrices, fileOpen)}, {qMakePair(Action::ToolUpdatePrices, fileOpen)}, {qMakePair(Action::ToolConsistency, fileOpen)}, {qMakePair(Action::NewAccount, fileOpen)}, {qMakePair(Action::AccountCreditTransfer, onlineJobAdministration::instance()->canSendCreditTransfer())}, {qMakePair(Action::NewInstitution, fileOpen)}, // {qMakePair(Action::TransactionNew, (fileOpen && d->m_myMoneyView->canCreateTransactions(KMyMoneyRegister::SelectedTransactions(), tooltip)))}, {qMakePair(Action::NewSchedule, fileOpen)}, // {qMakePair(Action::CurrencyNew, fileOpen)}, // {qMakePair(Action::PriceNew, fileOpen)}, }; for (const auto& a : actionStates) pActions[a.first]->setEnabled(a.second); } // ************* // Disabling standard actions based on conditions // ************* aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Save)))->setEnabled(modified && !d->m_myMoneyView->isDatabase()); aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::SaveAs)))->setEnabled(fileOpen); aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Close)))->setEnabled(fileOpen); aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Print)))->setEnabled(fileOpen && d->m_myMoneyView->canPrint()); // ************* // Enabling actions based on conditions // ************* QList accList; file->accountList(accList); QList::const_iterator it_a; - QMap::const_iterator it_p = d->m_onlinePlugins.constEnd(); - for (it_a = accList.constBegin(); (it_p == d->m_onlinePlugins.constEnd()) && (it_a != accList.constEnd()); ++it_a) { + QMap::const_iterator it_p = d->m_plugins.online.constEnd(); + for (it_a = accList.constBegin(); (it_p == d->m_plugins.online.constEnd()) && (it_a != accList.constEnd()); ++it_a) { if ((*it_a).hasOnlineMapping()) { // check if provider is available - it_p = d->m_onlinePlugins.constFind((*it_a).onlineBankingSettings().value("provider")); - if (it_p != d->m_onlinePlugins.constEnd()) { + it_p = d->m_plugins.online.constFind((*it_a).onlineBankingSettings().value("provider").toLower()); + if (it_p != d->m_plugins.online.constEnd()) { QStringList protocols; (*it_p)->protocols(protocols); if (protocols.count() > 0) { pActions[Action::UpdateAllAccounts]->setEnabled(true); } } } } QBitArray skip((int)eStorage::Reference::Count); if (!d->m_selectedAccount.id().isEmpty()) { if (!file->isStandardAccount(d->m_selectedAccount.id())) { switch (d->m_selectedAccount.accountGroup()) { case eMyMoney::Account::Type::Asset: case eMyMoney::Account::Type::Liability: case eMyMoney::Account::Type::Equity: pActions[Action::ReportAccountTransactions]->setEnabled(true); if (d->m_selectedAccount.hasOnlineMapping()) { pActions[Action::UnmapOnlineAccount]->setEnabled(true); // check if provider is available QMap::const_iterator it_p; - it_p = d->m_onlinePlugins.constFind(d->m_selectedAccount.onlineBankingSettings().value(QLatin1String("provider"))); - if (it_p != d->m_onlinePlugins.constEnd()) { + it_p = d->m_plugins.online.constFind(d->m_selectedAccount.onlineBankingSettings().value(QLatin1String("provider"))); + if (it_p != d->m_plugins.online.constEnd()) { QStringList protocols; (*it_p)->protocols(protocols); if (protocols.count() > 0) { pActions[Action::UpdateAccount]->setEnabled(true); } } } else { - pActions[Action::MapOnlineAccount]->setEnabled(d->m_onlinePlugins.count() > 0); + pActions[Action::MapOnlineAccount]->setEnabled(d->m_plugins.online.count() > 0); } break; default: break; } } } } void KMyMoneyApp::slotResetSelections() { slotSelectAccount(MyMoneyAccount()); d->m_myMoneyView->slotObjectSelected(MyMoneyAccount()); d->m_myMoneyView->slotObjectSelected(MyMoneyInstitution()); d->m_myMoneyView->slotObjectSelected(MyMoneySchedule()); d->m_myMoneyView->slotObjectSelected(MyMoneyTag()); d->m_myMoneyView->slotTransactionsSelected(KMyMoneyRegister::SelectedTransactions()); slotUpdateActions(); } void KMyMoneyApp::slotSelectAccount(const MyMoneyObject& obj) { if (typeid(obj) != typeid(MyMoneyAccount)) return; d->m_selectedAccount = MyMoneyAccount(); const MyMoneyAccount& acc = dynamic_cast(obj); if (!acc.isInvest()) d->m_selectedAccount = acc; // qDebug("slotSelectAccount('%s')", d->m_selectedAccount.name().data()); slotUpdateActions(); emit accountSelected(d->m_selectedAccount); } void KMyMoneyApp::slotDataChanged() { // As this method is called every time the MyMoneyFile instance // notifies a modification, it's the perfect place to start the timer if needed if (d->m_autoSaveEnabled && !d->m_autoSaveTimer->isActive()) { d->m_autoSaveTimer->setSingleShot(true); d->m_autoSaveTimer->start(d->m_autoSavePeriod * 60 * 1000); //miliseconds } updateCaption(); } void KMyMoneyApp::slotCurrencyDialog() { QPointer dlg = new KCurrencyEditDlg(this); dlg->exec(); delete dlg; } void KMyMoneyApp::slotPriceDialog() { QPointer dlg = new KMyMoneyPriceDlg(this); dlg->exec(); delete dlg; } void KMyMoneyApp::slotFileConsistencyCheck() { d->consistencyCheck(true); updateCaption(); } void KMyMoneyApp::Private::consistencyCheck(bool alwaysDisplayResult) { KMSTATUS(i18n("Running consistency check...")); MyMoneyFileTransaction ft; try { m_consistencyCheckResult = MyMoneyFile::instance()->consistencyCheck(); ft.commit(); } catch (const MyMoneyException &e) { m_consistencyCheckResult.append(i18n("Consistency check failed: %1", e.what())); // always display the result if the check failed alwaysDisplayResult = true; } // in case the consistency check was OK, we get a single line as result // in all errneous cases, we get more than one line and force the // display of them. if (alwaysDisplayResult || m_consistencyCheckResult.size() > 1) { QString msg = i18n("The consistency check has found no issues in your data. Details are presented below."); if (m_consistencyCheckResult.size() > 1) msg = i18n("The consistency check has found some issues in your data. Details are presented below. Those issues that could not be corrected automatically need to be solved by the user."); // install a context menu for the list after the dialog is displayed QTimer::singleShot(500, q, SLOT(slotInstallConsistencyCheckContextMenu())); KMessageBox::informationList(0, msg, m_consistencyCheckResult, i18n("Consistency check result")); } // this data is no longer needed m_consistencyCheckResult.clear(); } void KMyMoneyApp::Private::copyConsistencyCheckResults() { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(m_consistencyCheckResult.join(QLatin1String("\n"))); } void KMyMoneyApp::Private::saveConsistencyCheckResults() { QUrl fileUrl = QFileDialog::getSaveFileUrl(q); if (!fileUrl.isEmpty()) { QFile file(fileUrl.toLocalFile()); if (file.open(QFile::WriteOnly | QFile::Append | QFile::Text)) { QTextStream out(&file); out << m_consistencyCheckResult.join(QLatin1String("\n")); file.close(); } } } void KMyMoneyApp::Private::setThemedCSS() { const QStringList CSSnames {QStringLiteral("kmymoney.css"), QStringLiteral("welcome.css")}; const QString rcDir("/html/"); const QStringList defaultCSSDirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, rcDir, QStandardPaths::LocateDirectory); // scan the list of directories to find the ones that really // contains all files we look for QString defaultCSSDir; foreach (const auto dir, defaultCSSDirs) { defaultCSSDir = dir; foreach (const auto CSSname, CSSnames) { QFileInfo fileInfo(defaultCSSDir + CSSname); if (!fileInfo.exists()) { defaultCSSDir.clear(); break; } } if (!defaultCSSDir.isEmpty()) { break; } } // make sure we have the local directory where the themed version is stored const QString themedCSSDir = QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation).first() + rcDir; QDir().mkpath(themedCSSDir); foreach (const auto CSSname, CSSnames) { const QString defaultCSSFilename = defaultCSSDir + CSSname; QFileInfo fileInfo(defaultCSSFilename); if (fileInfo.exists()) { const QString themedCSSFilename = themedCSSDir + CSSname; QFile::remove(themedCSSFilename); if (QFile::copy(defaultCSSFilename, themedCSSFilename)) { QFile cssFile (themedCSSFilename); if (cssFile.open(QIODevice::ReadWrite)) { QTextStream cssStream(&cssFile); auto cssText = cssStream.readAll(); cssText.replace(QLatin1String("./"), defaultCSSDir, Qt::CaseSensitive); cssText.replace(QLatin1String("WindowText"), KMyMoneyGlobalSettings::schemeColor(SchemeColor::WindowText).name(), Qt::CaseSensitive); cssText.replace(QLatin1String("Window"), KMyMoneyGlobalSettings::schemeColor(SchemeColor::WindowBackground).name(), Qt::CaseSensitive); cssText.replace(QLatin1String("HighlightText"), KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListHighlightText).name(), Qt::CaseSensitive); cssText.replace(QLatin1String("Highlight"), KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListHighlight).name(), Qt::CaseSensitive); cssText.replace(QLatin1String("black"), KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListGrid).name(), Qt::CaseSensitive); cssStream.seek(0); cssStream << cssText; cssFile.close(); } } } } } void KMyMoneyApp::slotCheckSchedules() { if (KMyMoneyGlobalSettings::checkSchedule() == true) { KMSTATUS(i18n("Checking for overdue scheduled transactions...")); MyMoneyFile *file = MyMoneyFile::instance(); QDate checkDate = QDate::currentDate().addDays(KMyMoneyGlobalSettings::checkSchedulePreview()); QList scheduleList = file->scheduleList(); QList::Iterator it; eDialogs::ScheduleResultCode rc = eDialogs::ScheduleResultCode::Enter; for (it = scheduleList.begin(); (it != scheduleList.end()) && (rc != eDialogs::ScheduleResultCode::Cancel); ++it) { // Get the copy in the file because it might be modified by commitTransaction MyMoneySchedule schedule = file->schedule((*it).id()); if (schedule.autoEnter()) { try { while (!schedule.isFinished() && (schedule.adjustedNextDueDate() <= checkDate) && rc != eDialogs::ScheduleResultCode::Ignore && rc != eDialogs::ScheduleResultCode::Cancel) { rc = d->m_myMoneyView->enterSchedule(schedule, true, true); schedule = file->schedule((*it).id()); // get a copy of the modified schedule } } catch (const MyMoneyException &) { } } if (rc == eDialogs::ScheduleResultCode::Ignore) { // if the current schedule was ignored then we must make sure that the user can still enter the next scheduled transaction rc = eDialogs::ScheduleResultCode::Enter; } } updateCaption(); } } void KMyMoneyApp::writeLastUsedDir(const QString& directory) { //get global config object for our app. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { KConfigGroup grp = kconfig->group("General Options"); //write path entry, no error handling since its void. grp.writeEntry("LastUsedDirectory", directory); } } void KMyMoneyApp::writeLastUsedFile(const QString& fileName) { //get global config object for our app. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { KConfigGroup grp = d->m_config->group("General Options"); // write path entry, no error handling since its void. // use a standard string, as fileName could contain a protocol // e.g. file:/home/thb/.... grp.writeEntry("LastUsedFile", fileName); } } QString KMyMoneyApp::readLastUsedDir() const { QString str; //get global config object for our app. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { KConfigGroup grp = d->m_config->group("General Options"); //read path entry. Second parameter is the default if the setting is not found, which will be the default document path. str = grp.readEntry("LastUsedDirectory", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); // if the path stored is empty, we use the default nevertheless if (str.isEmpty()) str = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); } return str; } QString KMyMoneyApp::readLastUsedFile() const { QString str; // get global config object for our app. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { KConfigGroup grp = d->m_config->group("General Options"); // read filename entry. str = grp.readEntry("LastUsedFile", ""); } return str; } QString KMyMoneyApp::filename() const { return d->m_fileName.url(); } WebConnect* KMyMoneyApp::webConnect() const { return d->m_webConnect; } QList KMyMoneyApp::instanceList() const { QList list; #ifdef KMM_DBUS QDBusReply reply = QDBusConnection::sessionBus().interface()->registeredServiceNames(); if (reply.isValid()) { QStringList apps = reply.value(); QStringList::ConstIterator it; // build a list of service names of all running kmymoney applications without this one for (it = apps.constBegin(); it != apps.constEnd(); ++it) { // please change this method of creating a list of 'all the other kmymoney instances that are running on the system' // since assuming that D-Bus creates service names with org.kde.kmymoney-PID is an observation I don't think that it's documented somwhere if ((*it).indexOf("org.kde.kmymoney-") == 0) { uint thisProcPid = platformTools::processId(); if ((*it).indexOf(QString("org.kde.kmymoney-%1").arg(thisProcPid)) != 0) list += (*it); } } } else { qDebug("D-Bus returned the following error while obtaining instances: %s", qPrintable(reply.error().message())); } #endif return list; } void KMyMoneyApp::slotEquityPriceUpdate() { QPointer dlg = new KEquityPriceUpdateDlg(this); if (dlg->exec() == QDialog::Accepted && dlg != 0) dlg->storePrices(); delete dlg; } void KMyMoneyApp::webConnect(const QString& sourceUrl, const QByteArray& asn_id) { // // Web connect attempts to go through the known importers and see if the file // can be importing using that method. If so, it will import it using that // plugin // Q_UNUSED(asn_id) d->m_importUrlsQueue.enqueue(sourceUrl); // only start processing if this is the only import so far if (d->m_importUrlsQueue.count() == 1) { while (!d->m_importUrlsQueue.isEmpty()) { // get the value of the next item from the queue // but leave it on the queue for now QString url = d->m_importUrlsQueue.head(); // Bring this window to the forefront. This method was suggested by // Lubos Lunak of the KDE core development team. // TODO: port KF5 (WebConnect) //KStartupInfo::setNewStartupId(this, asn_id); // Make sure we have an open file if (! d->m_myMoneyView->fileOpen() && KMessageBox::warningContinueCancel(kmymoney, i18n("You must first select a KMyMoney file before you can import a statement.")) == KMessageBox::Continue) kmymoney->slotFileOpen(); // only continue if the user really did open a file. if (d->m_myMoneyView->fileOpen()) { KMSTATUS(i18n("Importing a statement via Web Connect")); // remove the statement files d->unlinkStatementXML(); - QMap::const_iterator it_plugin = d->m_importerPlugins.constBegin(); - while (it_plugin != d->m_importerPlugins.constEnd()) { + QMap::const_iterator it_plugin = d->m_plugins.importer.constBegin(); + while (it_plugin != d->m_plugins.importer.constEnd()) { if ((*it_plugin)->isMyFormat(url)) { QList statements; if (!(*it_plugin)->import(url)) { KMessageBox::error(this, i18n("Unable to import %1 using %2 plugin. The plugin returned the following error: %3", url, (*it_plugin)->formatName(), (*it_plugin)->lastError()), i18n("Importing error")); } break; } ++it_plugin; } // If we did not find a match, try importing it as a KMM statement file, // which is really just for testing. the statement file is not exposed // to users. - if (it_plugin == d->m_importerPlugins.constEnd()) + if (it_plugin == d->m_plugins.importer.constEnd()) if (MyMoneyStatement::isStatementFile(url)) MyMoneyStatementReader::importStatement(url, false, &progressCallback); } // remove the current processed item from the queue d->m_importUrlsQueue.dequeue(); } } } void KMyMoneyApp::slotEnableMessages() { KMessageBox::enableAllMessages(); KMessageBox::information(this, i18n("All messages have been enabled."), i18n("All messages")); } void KMyMoneyApp::createInterfaces() { // Sets up the plugin interface KMyMoneyPlugin::pluginInterfaces().importInterface = new KMyMoneyPlugin::KMMImportInterface(this); KMyMoneyPlugin::pluginInterfaces().statementInterface = new KMyMoneyPlugin::KMMStatementInterface(this); KMyMoneyPlugin::pluginInterfaces().viewInterface = new KMyMoneyPlugin::KMMViewInterface(d->m_myMoneyView, this); // setup the calendar interface for schedules MyMoneySchedule::setProcessingCalendar(this); } -void KMyMoneyApp::loadPlugins() -{ - Q_ASSERT(!d->m_pluginLoader); - d->m_pluginLoader = new KMyMoneyPlugin::PluginLoader(this); - - //! @todo Junior Job: Improve the config read system - KSharedConfigPtr config = KSharedConfig::openConfig(); - KConfigGroup group{ config->group("Plugins") }; - - const auto plugins = KPluginLoader::findPlugins("kmymoney"); - d->m_pluginLoader->addPluginInfo(plugins); - - for (const KPluginMetaData & pluginData : plugins) { - // Only load plugins which are enabled and have the right serviceType. Other serviceTypes are loaded on demand. - if (isPluginEnabled(pluginData, group)) - slotPluginLoad(pluginData); - } - - connect(d->m_pluginLoader, &KMyMoneyPlugin::PluginLoader::pluginEnabled, this, &KMyMoneyApp::slotPluginLoad); - connect(d->m_pluginLoader, &KMyMoneyPlugin::PluginLoader::pluginDisabled, this, &KMyMoneyApp::slotPluginUnload); -} - -void KMyMoneyApp::unloadPlugins() -{ - Q_ASSERT(d->m_pluginLoader); - delete d->m_pluginLoader; -} - -inline bool KMyMoneyApp::isPluginEnabled(const KPluginMetaData& metaData, const KConfigGroup& configGroup) -{ - //! @fixme: there is a function in KMyMoneyPlugin::PluginLoader which has to have the same content - if (metaData.serviceTypes().contains("KMyMoney/Plugin")) { - const QString keyName{metaData.name() + "Enabled"}; - if (configGroup.hasKey(keyName)) - return configGroup.readEntry(keyName, true); - return metaData.isEnabledByDefault(); - } - return false; -} - -void KMyMoneyApp::slotPluginLoad(const KPluginMetaData& metaData) -{ - std::unique_ptr loader{new QPluginLoader{metaData.fileName()}}; - QObject* plugin = loader->instance(); - if (!plugin) { - qWarning("Could not load plugin '%s', error: %s", qPrintable(metaData.fileName()), qPrintable(loader->errorString())); - return; - } - - if ( d->m_plugins.contains(metaData.fileName()) ) { - /** @fixme Handle a reload e.g. objectNames are equal but the object are different (plugin != d->m_plugins[plugin->objectName()]) - * Also it could be useful to drop the dependence on objectName() - */ - - /* Note: there is nothing to delete here because if the plugin was loaded already, - plugin points to the same object as the previously loaded one. */ - return; - } - - KMyMoneyPlugin::Plugin* kmmPlugin = qobject_cast(plugin); - if (!kmmPlugin) { - qWarning("Could not load plugin '%s'.", qPrintable(metaData.fileName())); - return; - } - - // check for online plugin - KMyMoneyPlugin::OnlinePlugin* op = dynamic_cast(plugin); - // check for extended online plugin - KMyMoneyPlugin::OnlinePluginExtended* ope = dynamic_cast(plugin); - // check for importer plugin - KMyMoneyPlugin::ImporterPlugin* ip = dynamic_cast(plugin); - - // Tell the plugin it is about to get plugged - kmmPlugin->plug(); - - // plug the plugin - guiFactory()->addClient(kmmPlugin); - - d->m_plugins[metaData.fileName()] = kmmPlugin; - - if (op) - d->m_onlinePlugins[plugin->objectName()] = op; - - if (ope) - onlineJobAdministration::instance()->addPlugin(plugin->objectName(), ope); - - if (ip) - d->m_importerPlugins[plugin->objectName()] = ip; - - slotUpdateActions(); -} - -void KMyMoneyApp::slotPluginUnload(const KPluginMetaData& metaData) -{ - KMyMoneyPlugin::Plugin* plugin = d->m_plugins[metaData.fileName()]; - - // Remove and test if the plugin was actually loaded - if (!d->m_plugins.remove(metaData.fileName()) || plugin == nullptr) - return; - - // check for online plugin - KMyMoneyPlugin::OnlinePlugin* op = dynamic_cast(plugin); - // check for importer plugin - KMyMoneyPlugin::ImporterPlugin* ip = dynamic_cast(plugin); - - // unplug the plugin - guiFactory()->removeClient(plugin); - - if (op) - d->m_onlinePlugins.remove(plugin->objectName()); - - if (ip) - d->m_importerPlugins.remove(plugin->objectName()); - - plugin->unplug(); - slotUpdateActions(); - -} - void KMyMoneyApp::slotAutoSave() { if (!d->m_inAutoSaving) { // store the focus widget so we can restore it after save QPointer focusWidget = qApp->focusWidget(); d->m_inAutoSaving = true; KMSTATUS(i18n("Auto saving...")); //calls slotFileSave if needed, and restart the timer //it the file is not saved, reinitializes the countdown. if (d->m_myMoneyView->dirty() && d->m_autoSaveEnabled) { if (!slotFileSave() && d->m_autoSavePeriod > 0) { d->m_autoSaveTimer->setSingleShot(true); d->m_autoSaveTimer->start(d->m_autoSavePeriod * 60 * 1000); } } d->m_inAutoSaving = false; if (focusWidget && focusWidget != qApp->focusWidget()) { // we have a valid focus widget so restore it focusWidget->setFocus(); } } } void KMyMoneyApp::slotDateChanged() { QDateTime dt = QDateTime::currentDateTime(); QDateTime nextDay(QDate(dt.date().addDays(1)), QTime(0, 0, 0)); // +1 is to make sure that we're already in the next day when the // signal is sent (this way we also avoid setting the timer to 0) QTimer::singleShot((dt.secsTo(nextDay) + 1)*1000, this, SLOT(slotDateChanged())); d->m_myMoneyView->slotRefreshViews(); } void KMyMoneyApp::slotOnlineAccountRequested(const MyMoneyAccount& acc, eMenu::Action action) { d->m_selectedAccount = acc; switch (action) { case eMenu::Action::UnmapOnlineAccount: slotAccountUnmapOnline(); break; case eMenu::Action::MapOnlineAccount: slotAccountMapOnline(); break; case eMenu::Action::UpdateAccount: slotAccountUpdateOnline(); break; case eMenu::Action::UpdateAllAccounts: slotAccountUpdateOnlineAll(); break; default: break; } } void KMyMoneyApp::slotAccountUnmapOnline() { // no account selected if (d->m_selectedAccount.id().isEmpty()) return; // not a mapped account if (!d->m_selectedAccount.hasOnlineMapping()) return; if (KMessageBox::warningYesNo(this, QString("%1").arg(i18n("Do you really want to remove the mapping of account %1 to an online account? Depending on the details of the online banking method used, this action cannot be reverted.", d->m_selectedAccount.name())), i18n("Remove mapping to online account")) == KMessageBox::Yes) { MyMoneyFileTransaction ft; try { d->m_selectedAccount.setOnlineBankingSettings(MyMoneyKeyValueContainer()); // delete the kvp that is used in MyMoneyStatementReader too // we should really get rid of it, but since I don't know what it // is good for, I'll keep it around. (ipwizard) d->m_selectedAccount.deletePair("StatementKey"); MyMoneyFile::instance()->modifyAccount(d->m_selectedAccount); ft.commit(); // The mapping could disable the online task system onlineJobAdministration::instance()->updateOnlineTaskProperties(); } catch (const MyMoneyException &e) { KMessageBox::error(this, i18n("Unable to unmap account from online account: %1", e.what())); } } } void KMyMoneyApp::slotAccountMapOnline() { // no account selected if (d->m_selectedAccount.id().isEmpty()) return; // already an account mapped if (d->m_selectedAccount.hasOnlineMapping()) return; // check if user tries to map a brokerageAccount if (d->m_selectedAccount.name().contains(i18n(" (Brokerage)"))) { if (KMessageBox::warningContinueCancel(this, i18n("You try to map a brokerage account to an online account. This is usually not advisable. In general, the investment account should be mapped to the online account. Please cancel if you intended to map the investment account, continue otherwise"), i18n("Mapping brokerage account")) == KMessageBox::Cancel) { return; } } // if we have more than one provider let the user select the current provider QString provider; QMap::const_iterator it_p; - switch (d->m_onlinePlugins.count()) { + switch (d->m_plugins.online.count()) { case 0: break; case 1: - provider = d->m_onlinePlugins.begin().key(); + provider = d->m_plugins.online.begin().key(); break; default: { QMenu popup(this); popup.setTitle(i18n("Select online banking plugin")); // Populate the pick list with all the provider - for (it_p = d->m_onlinePlugins.constBegin(); it_p != d->m_onlinePlugins.constEnd(); ++it_p) { + for (it_p = d->m_plugins.online.constBegin(); it_p != d->m_plugins.online.constEnd(); ++it_p) { popup.addAction(it_p.key())->setData(it_p.key()); } QAction *item = popup.actions()[0]; if (item) { popup.setActiveAction(item); } // cancelled if ((item = popup.exec(QCursor::pos(), item)) == 0) { return; } provider = item->data().toString(); } break; } if (provider.isEmpty()) return; // find the provider - it_p = d->m_onlinePlugins.constFind(provider); - if (it_p != d->m_onlinePlugins.constEnd()) { + it_p = d->m_plugins.online.constFind(provider); + if (it_p != d->m_plugins.online.constEnd()) { // plugin found, call it MyMoneyKeyValueContainer settings; if ((*it_p)->mapAccount(d->m_selectedAccount, settings)) { settings["provider"] = provider; MyMoneyAccount acc(d->m_selectedAccount); acc.setOnlineBankingSettings(settings); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifyAccount(acc); ft.commit(); // The mapping could enable the online task system onlineJobAdministration::instance()->updateOnlineTaskProperties(); } catch (const MyMoneyException &e) { KMessageBox::error(this, i18n("Unable to map account to online account: %1", e.what())); } } } } void KMyMoneyApp::slotAccountUpdateOnlineAll() { QList accList; MyMoneyFile::instance()->accountList(accList); QList::iterator it_a; QMap::const_iterator it_p; d->m_statementResults.clear(); d->m_collectingStatements = true; // remove all those from the list, that don't have a 'provider' or the // provider is not currently present for (it_a = accList.begin(); it_a != accList.end();) { if (!(*it_a).hasOnlineMapping() - || d->m_onlinePlugins.find((*it_a).onlineBankingSettings().value("provider")) == d->m_onlinePlugins.end()) { + || d->m_plugins.online.find((*it_a).onlineBankingSettings().value("provider").toLower()) == d->m_plugins.online.end()) { it_a = accList.erase(it_a); } else ++it_a; } const QVector disabledActions {Action::UpdateAccount, Action::UpdateAllAccounts}; for (const auto& a : disabledActions) pActions[a]->setEnabled(false); // now work on the remaining list of accounts int cnt = accList.count() - 1; for (it_a = accList.begin(); it_a != accList.end(); ++it_a) { - it_p = d->m_onlinePlugins.constFind((*it_a).onlineBankingSettings().value("provider")); + it_p = d->m_plugins.online.constFind((*it_a).onlineBankingSettings().value("provider").toLower()); (*it_p)->updateAccount(*it_a, cnt != 0); --cnt; } d->m_collectingStatements = false; if (!d->m_statementResults.isEmpty()) KMessageBox::informationList(this, i18n("The statements have been processed with the following results:"), d->m_statementResults, i18n("Statement stats")); // re-enable the disabled actions slotUpdateActions(); } void KMyMoneyApp::slotAccountUpdateOnline() { // no account selected if (d->m_selectedAccount.id().isEmpty()) return; // no online account mapped if (!d->m_selectedAccount.hasOnlineMapping()) return; const QVector disabledActions {Action::UpdateAccount, Action::UpdateAllAccounts}; for (const auto& a : disabledActions) pActions[a]->setEnabled(false); // find the provider QMap::const_iterator it_p; - it_p = d->m_onlinePlugins.constFind(d->m_selectedAccount.onlineBankingSettings().value("provider")); - if (it_p != d->m_onlinePlugins.constEnd()) { + it_p = d->m_plugins.online.constFind(d->m_selectedAccount.onlineBankingSettings().value("provider").toLower()); + if (it_p != d->m_plugins.online.constEnd()) { // plugin found, call it d->m_collectingStatements = true; d->m_statementResults.clear(); (*it_p)->updateAccount(d->m_selectedAccount); d->m_collectingStatements = false; if (!d->m_statementResults.isEmpty()) KMessageBox::informationList(this, i18n("The statements have been processed with the following results:"), d->m_statementResults, i18n("Statement stats")); } // re-enable the disabled actions slotUpdateActions(); } void KMyMoneyApp::setHolidayRegion(const QString& holidayRegion) { #ifdef KF5Holidays_FOUND //since the cost of updating the cache is now not negligible //check whether the region has been modified if (!d->m_holidayRegion || d->m_holidayRegion->regionCode() != holidayRegion) { // Delete the previous holidayRegion before creating a new one. delete d->m_holidayRegion; // Create a new holidayRegion. d->m_holidayRegion = new KHolidays::HolidayRegion(holidayRegion); //clear and update the holiday cache preloadHolidays(); } #else Q_UNUSED(holidayRegion); #endif } bool KMyMoneyApp::isProcessingDate(const QDate& date) const { #ifdef KF5Holidays_FOUND if (!d->m_processingDays.testBit(date.dayOfWeek())) return false; if (!d->m_holidayRegion || !d->m_holidayRegion->isValid()) return true; //check first whether it's already in cache if (d->m_holidayMap.contains(date)) { return d->m_holidayMap.value(date, true); } else { bool processingDay = !d->m_holidayRegion->isHoliday(date); d->m_holidayMap.insert(date, processingDay); return processingDay; } #else Q_UNUSED(date); return true; #endif } void KMyMoneyApp::preloadHolidays() { #ifdef KF5Holidays_FOUND //clear the cache before loading d->m_holidayMap.clear(); //only do this if it is a valid region if (d->m_holidayRegion && d->m_holidayRegion->isValid()) { //load holidays for the forecast days plus 1 cycle, to be on the safe side int forecastDays = KMyMoneyGlobalSettings::forecastDays() + KMyMoneyGlobalSettings::forecastAccountCycle(); QDate endDate = QDate::currentDate().addDays(forecastDays); //look for holidays for the next 2 years as a minimum. That should give a good margin for the cache if (endDate < QDate::currentDate().addYears(2)) endDate = QDate::currentDate().addYears(2); KHolidays::Holiday::List holidayList = d->m_holidayRegion->holidays(QDate::currentDate(), endDate); KHolidays::Holiday::List::const_iterator holiday_it; for (holiday_it = holidayList.constBegin(); holiday_it != holidayList.constEnd(); ++holiday_it) { for (QDate holidayDate = (*holiday_it).observedStartDate(); holidayDate <= (*holiday_it).observedEndDate(); holidayDate = holidayDate.addDays(1)) d->m_holidayMap.insert(holidayDate, false); } for (QDate date = QDate::currentDate(); date <= endDate; date = date.addDays(1)) { //if it is not a processing day, set it to false if (!d->m_processingDays.testBit(date.dayOfWeek())) { d->m_holidayMap.insert(date, false); } else if (!d->m_holidayMap.contains(date)) { //if it is not a holiday nor a weekend, it is a processing day d->m_holidayMap.insert(date, true); } } } #endif } KMStatus::KMStatus(const QString &text) { m_prevText = kmymoney->slotStatusMsg(text); } KMStatus::~KMStatus() { kmymoney->slotStatusMsg(m_prevText); } void KMyMoneyApp::Private::unlinkStatementXML() { QDir d(KMyMoneySettings::logPath(), "kmm-statement*"); for (uint i = 0; i < d.count(); ++i) { qDebug("Remove %s", qPrintable(d[i])); d.remove(KMyMoneySettings::logPath() + QString("/%1").arg(d[i])); } m_statementXMLindex = 0; } void KMyMoneyApp::Private::closeFile() { q->slotSelectAccount(MyMoneyAccount()); q->d->m_myMoneyView->slotObjectSelected(MyMoneyAccount()); q->d->m_myMoneyView->slotObjectSelected(MyMoneyInstitution()); q->d->m_myMoneyView->slotObjectSelected(MyMoneySchedule()); q->d->m_myMoneyView->slotObjectSelected(MyMoneyTag()); q->d->m_myMoneyView->slotTransactionsSelected(KMyMoneyRegister::SelectedTransactions()); // q->slotSelectTransactions(KMyMoneyRegister::SelectedTransactions()); m_reconciliationAccount = MyMoneyAccount(); m_myMoneyView->finishReconciliation(MyMoneyAccount()); m_myMoneyView->closeFile(); m_fileName = QUrl(); q->updateCaption(); // just create a new balance warning object delete m_balanceWarning; m_balanceWarning = new KBalanceWarning(q); emit q->fileLoaded(m_fileName); } diff --git a/kmymoney/kmymoney.h b/kmymoney/kmymoney.h index 9e1e0aaac..70509c917 100644 --- a/kmymoney/kmymoney.h +++ b/kmymoney/kmymoney.h @@ -1,737 +1,706 @@ /*************************************************************************** kmymoney.h ------------------- copyright : (C) 2000-2001 by Michael Edwardes (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. * * * ***************************************************************************/ #ifndef KMYMONEY_H #define KMYMONEY_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include #include "kmymoneyutils.h" #include "mymoneyaccount.h" #include "mymoney/onlinejob.h" #include "onlinejobtyped.h" #include "mymoneykeyvaluecontainer.h" #include "mymoneymoney.h" #include "selectedtransactions.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "mymoneyenums.h" class QResizeEvent; -class KPluginMetaData; class MyMoneyObject; class MyMoneyInstitution; class MyMoneyAccount; class MyMoneySecurity; class MyMoneyPayee; class MyMoneyPrice; class MyMoneyTag; class MyMoneySplit; class MyMoneyTransaction; class WebConnect; class creditTransfer; template class onlineJobTyped; namespace eDialogs { enum class ScheduleResultCode; } namespace eMenu { enum class Action; enum class Menu; } /*! \mainpage KMyMoney Main Page for API documentation. * * \section intro Introduction * * This is the API documentation for KMyMoney. It should be used as a reference * for KMyMoney developers and users who wish to see how KMyMoney works. This * documentation will be kept up-to-date as development progresses and should be * read for new features that have been developed in KMyMoney. */ /** * The base class for KMyMoney application windows. It sets up the main * window and reads the config file as well as providing a menubar, toolbar * and statusbar. * * @see KMyMoneyView * * @author Michael Edwardes 2000-2001 * @author Thomas Baumgart 2006-2008 * * @short Main application class. */ class KMyMoneyApp : public KXmlGuiWindow, public IMyMoneyProcessingCalendar { Q_OBJECT private Q_SLOTS: /** * Keep track of objects that are destroyed by external events */ void slotObjectDestroyed(QObject* o); /** * Add a context menu to the list used by KMessageBox::informationList to display the consistency check results. */ void slotInstallConsistencyCheckContextMenu(); /** * Handle the context menu of the list used by KMessageBox::informationList to display the consistency check results. */ void slotShowContextMenuForConsistencyCheck(const QPoint &); protected Q_SLOTS: void slotFileSaveAsFilterChanged(const QString& filter); /** * This slot is intended to be used as part of auto saving. This is used when the * QTimer emits the timeout signal and simply checks that the file is dirty (has * received modifications to its contents), and call the appropriate method to * save the file. Furthermore, re-starts the timer (possibly not needed). * @author mvillarino 2005 * @see KMyMoneyApp::slotDataChanged() */ void slotAutoSave(); /** * This slot re-enables all message for which the "Don't show again" * option had been selected. */ void slotEnableMessages(); /** * Called when the user asks for file information. */ void slotFileFileInfo(); /** * Called to run performance test. */ void slotPerformanceTest(); /** * Called to generate the sql to create kmymoney database tables etc. */ void slotGenerateSql(); #ifdef KMM_DEBUG /** * Debugging only: turn on/off traces */ void slotToggleTraces(); #endif /** * Debugging only: turn on/off timers */ void slotToggleTimers(); /** * Called when the user asks for the personal information. */ void slotFileViewPersonal(); void slotLoadAccountTemplates(); void slotSaveAccountTemplates(); /** * Open up the application wide settings dialog. * * @see KSettingsDlg */ void slotSettings(); /** * Called to show credits window. */ void slotShowCredits(); /** * Called when the user wishes to backup the current file */ void slotBackupFile(); /** * Perform mount operation before making a backup of the current file */ void slotBackupMount(); /** * Perform the backup write operation */ bool slotBackupWriteFile(); /** * Perform unmount operation after making a backup of the current file */ void slotBackupUnmount(); /** * Finish backup of the current file */ void slotBackupFinish(); /** * Handle events on making a backup of the current file */ void slotBackupHandleEvents(); void slotShowTipOfTheDay(); void slotShowPreviousView(); void slotShowNextView(); /** * Brings up a dialog to let the user search for specific transaction(s). It then * opens a results window to display those transactions. */ void slotFindTransaction(); /** * Destroys a possibly open the search dialog */ void slotCloseSearchDialog(); /** * Calls the print logic for the current view */ void slotPrintView(); /** * Call this slot, if any configuration parameter has changed */ - void slotUpdateConfiguration(); + void slotUpdateConfiguration(const QString &dialogName); /** * This slot is used to start new features during the development cycle */ void slotNewFeature(); /** * This slot triggers an update of all views and restarts * a single shot timer to call itself again at beginning of * the next day. */ void slotDateChanged(); /** * This slot will be called when the engine data changed * and the application object needs to update its state. */ void slotDataChanged(); /** * This slot collects information for a new scheduled transaction * based on transaction @a t and @a occurrence and saves it in the engine. */ void slotScheduleNew(const MyMoneyTransaction& t, eMyMoney::Schedule::Occurrence occurrence = eMyMoney::Schedule::Occurrence::Monthly); void slotAccountMapOnline(); void slotAccountUnmapOnline(); void slotAccountUpdateOnline(); void slotAccountUpdateOnlineAll(); void slotManageGpgKeys(); void slotKeySelected(int idx); void slotStatusProgressDone(); public: /** * This method checks if there is at least one asset or liability account * in the current storage object. If not, it starts the new account wizard. */ void createInitialAccount(); /** * This method returns the last URL used or an empty URL * depending on the option setting if the last file should * be opened during startup or the open file dialog should * be displayed. * * @return URL of last opened file or empty if the program * should start with the open file dialog */ QUrl lastOpenedURL(); /** * construtor of KMyMoneyApp, calls all init functions to create the application. */ explicit KMyMoneyApp(QWidget* parent = 0); /** * Destructor */ ~KMyMoneyApp(); static void progressCallback(int current, int total, const QString&); void writeLastUsedDir(const QString& directory); QString readLastUsedDir() const; void writeLastUsedFile(const QString& fileName); QString readLastUsedFile() const; /** * Returns whether there is an importer available that can handle this file */ bool isImportableFile(const QUrl &url); /** * This method is used to update the caption of the application window. * It sets the caption to "filename [modified] - KMyMoney". * * @param skipActions if true, the actions will not be updated. This * is usually onyl required by some early calls when * these widgets are not yet created (the default is false). */ void updateCaption(bool skipActions = false); /** * This method returns a list of all 'other' dcop registered kmymoney processes. * It's a subset of the return of DCOPclient()->registeredApplications(). * * @retval QStringList of process ids */ QList instanceList() const; #ifdef KMM_DEBUG /** * Dump a list of the names of all defined KActions to stdout. */ void dumpActions() const; #endif /** * Popup the context menu with the respective @p containerName. * Valid container names are defined in kmymoneyui.rc */ void showContextMenu(const QString& containerName); void createAccount(MyMoneyAccount& newAccount, MyMoneyAccount& parentAccount, MyMoneyAccount& brokerageAccount, MyMoneyMoney openingBal); QString filename() const; /** * Checks if the file with the @a url already exists. If so, * the user is asked if he/she wants to override the file. * If the user's answer is negative, @p false will be returned. * @p true will be returned in all other cases. */ bool okToWriteFile(const QUrl &url); /** * Return pointer to the WebConnect object */ WebConnect* webConnect() const; protected: /** save general Options like all bar positions and status as well as the geometry and the recent file list to the configuration * file */ void saveOptions(); /** * Creates the interfaces necessary for the plugins to work. Therefore, * this method must be called prior to loadPlugins(). */ void createInterfaces(); - /** - * load all available plugins. Make sure you have called createInterfaces() - * before you call this one. - */ - void loadPlugins(); - - /** - * unload all available plugins. Make sure you have called loadPlugins() - * before you call this one. - */ - void unloadPlugins(); - - /** - * @brief Checks if the given plugin is loaded on start up - * - * This filters plugins which are loaded on demand only and deactivated plugins. - * The configGroup must point to the correct group already. - */ - bool isPluginEnabled(const KPluginMetaData& metaData, const KConfigGroup& configGroup); - /** * read general options again and initialize all variables like the recent file list */ void readOptions(); /** * Gets pointers for menus preset by KXMLGUIFactory * @return pointers for menus */ QHash initMenus(); /** * Initializes QActions (names, object names, icons, some connections, shortcuts) * @return pointers for actions */ QHash initActions(); /** initializes the dynamic menus (account selectors) */ void initDynamicMenus(); /** * sets up the statusbar for the main window by initialzing a statuslabel. */ void initStatusBar(); /** queryClose is called by KMainWindow on each closeEvent of a window. Against the * default implementation (only returns true), this calls saveModified() on the document object to ask if the document shall * be saved if Modified; on cancel the closeEvent is rejected. * The settings are saved using saveOptions() if we are about to close. * @see KMainWindow#queryClose * @see QWidget#closeEvent */ virtual bool queryClose(); void slotCheckSchedules(); virtual void resizeEvent(QResizeEvent*); void createSchedule(MyMoneySchedule newSchedule, MyMoneyAccount& newAccount); /** * This method preloads the holidays for the duration of the default forecast period */ void preloadHolidays(); public Q_SLOTS: void slotFileInfoDialog(); /** */ void slotFileNew(); /** open a file and load it into the document*/ void slotFileOpen(); /** opens a file from the recent files menu */ void slotFileOpenRecent(const QUrl &url); /** open a SQL database */ void slotOpenDatabase(); /** * saves the current document. If it has no name yet, the user * will be queried for it. * * @retval false save operation failed * @retval true save operation was successful */ bool slotFileSave(); /** * ask the user for the filename and save the current document * * @retval false save operation failed * @retval true save operation was successful */ bool slotFileSaveAs(); /** * ask the user to select a database and save the current document * * @retval false save operation failed * @retval true save operation was successful */ bool saveAsDatabase(); void slotSaveAsDatabase(); /** asks for saving if the file is modified, then closes the actual file and window */ void slotFileCloseWindow(); /** asks for saving if the file is modified, then closes the actual file */ void slotFileClose(); /** * closes all open windows by calling close() on each memberList item * until the list is empty, then quits the application. * If queryClose() returns false because the user canceled the * saveModified() dialog, the closing breaks. */ void slotFileQuit(); void slotFileConsistencyCheck(); /** * fires up the price table editor */ void slotPriceDialog(); /** * fires up the currency table editor */ void slotCurrencyDialog(); /** * dummy method needed just for initialization */ void slotShowTransactionDetail(); /** * Toggles the hide reconciled transactions setting */ void slotHideReconciledTransactions(); /** * Toggles the hide unused categories setting */ void slotHideUnusedCategories(); /** * Toggles the show all accounts setting */ void slotShowAllAccounts(); /** * changes the statusbar contents for the standard label permanently, * used to indicate current actions. Returns the previous value for * 'stacked' usage. * * @param text the text that is displayed in the statusbar */ QString slotStatusMsg(const QString &text); /** * This method changes the progress bar in the status line according * to the parameters @p current and @p total. The following special * cases exist: * * - current = -1 and total = -1 will reset the progress bar * - current = ?? and total != 0 will setup the 100% mark to @p total * - current = xx and total == 0 will set the percentage * * @param current the current value with respect to the initialised * 100% mark * @param total the total value (100%) */ void slotStatusProgressBar(int current, int total = 0); /** * Called to update stock and currency prices from the user menu */ void slotEquityPriceUpdate(); void slotOnlineAccountRequested(const MyMoneyAccount& acc, eMenu::Action action); /** * This slot reparents account @p src to be a child of account @p dest * * @param src account to be reparented * @param dest new parent */ void slotReparentAccount(const MyMoneyAccount& src, const MyMoneyAccount& dest); /** * This slot reparents account @p src to be a held at institution @p dest * * @param src account to be reparented * @param dest new parent institution */ void slotReparentAccount(const MyMoneyAccount& src, const MyMoneyInstitution& dest); /** * Create a new investment in a given @p parent investment account */ void slotInvestmentNew(MyMoneyAccount& account, const MyMoneyAccount& parent); /** * Brings up the new category editor and saves the information. * The dialog will be preset with the name and parent account. * * @param account reference of category to be created. The @p name member * should be filled by the caller. The object will be filled * with additional information during the creation process * esp. the @p id member. * @param parent reference to parent account (defaults to none) */ void slotCategoryNew(MyMoneyAccount& account, const MyMoneyAccount& parent); void slotCategoryNew(MyMoneyAccount& account); /** */ void slotPayeeNew(const QString& newnameBase, QString& id); /** * This slot fires up the KCalc application */ void slotToolsStartKCalc(); void slotResetSelections(); void slotSelectAccount(const MyMoneyObject& account); /** * Brings up the new account wizard and saves the information. */ void slotAccountNew(MyMoneyAccount&); /** * This method updates all KAction items to the current state. */ void slotUpdateActions(); - /** - * Loads a plugin - */ - void slotPluginLoad(const KPluginMetaData& metaData); - - /** - * Unloads a plugin - */ - void slotPluginUnload(const KPluginMetaData& metaData); - void webConnect(const QString& sourceUrl, const QByteArray &asn_id); void webConnect(const QUrl url) { webConnect(url.path(), QByteArray()); } private: /** * This method sets the holidayRegion for use by the processing calendar. */ void setHolidayRegion(const QString& holidayRegion); /** * Load the status bar with the 'ready' message. This is hold in a single * place, so that is consistent with isReady(). */ void ready(); /** * Check if the status bar contains the 'ready' message. The return * value is used e.g. to detect if a quit operation is allowed or not. * * @retval true application is idle * @retval false application is active working on a longer operation */ bool isReady(); /** * Re-implemented from IMyMoneyProcessingCalendar */ bool isProcessingDate(const QDate& date) const; /** * Depending on the setting of AutoSaveOnQuit, this method * asks the user to save the file or not. * * @returns see return values of KMessageBox::warningYesNoCancel() */ int askSaveOnClose(); Q_SIGNALS: /** * This signal is emitted when a new file is loaded. In the case file * is closed, this signal is also emitted with an empty url. */ void fileLoaded(const QUrl &url); /** * This signal is emitted when a transaction/list of transactions has been selected by * the GUI. If no transaction is selected or the selection is removed, * @p transactions is identical to an empty QList. This signal is used * by plugins to get information about changes. */ void transactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions); /** * This signal is sent out, when the user presses Ctrl+A or activates * the Select all transactions action. */ void selectAllTransactions(); /** * This signal is emitted when a new account has been selected by * the GUI. If no account is selected or the selection is removed, * @a account is identical to MyMoneyAccount(). This signal is used * by plugins to get information about changes. */ void accountSelected(const MyMoneyAccount& account); /** * This signal is emitted when a new institution has been selected by * the GUI. If no institution is selected or the selection is removed, * @a institution is identical to MyMoneyInstitution(). This signal is used * by plugins to get information about changes. */ void institutionSelected(const MyMoneyInstitution& institution); /** * This signal is emitted when a new schedule has been selected by * the GUI. If no schedule is selected or the selection is removed, * @a schedule is identical to MyMoneySchedule(). This signal is used * by plugins to get information about changes. */ void scheduleSelected(const MyMoneySchedule& schedule); void startMatchTransaction(const MyMoneyTransaction& t); void cancelMatchTransaction(); public: bool isActionToggled(const eMenu::Action _a); static const QHash s_Actions; private: /// \internal d-pointer class. class Private; /* * Actually, one should write "Private * const d" but that confuses the KIDL * compiler in this context. It complains about the const keyword. So we leave * it out here */ /// \internal d-pointer instance. Private* d; }; extern KMyMoneyApp *kmymoney; class KMStatus { public: explicit KMStatus(const QString &text); ~KMStatus(); private: QString m_prevText; }; #define KMSTATUS(msg) KMStatus _thisStatus(msg) #endif // KMYMONEY_H diff --git a/kmymoney/mymoney/onlinejobadministration.cpp b/kmymoney/mymoney/onlinejobadministration.cpp index 302a9e519..fe890674e 100644 --- a/kmymoney/mymoney/onlinejobadministration.cpp +++ b/kmymoney/mymoney/onlinejobadministration.cpp @@ -1,412 +1,422 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 Christian Dávid * * 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 "onlinejobadministration.h" // ---------------------------------------------------------------------------- // Std Includes #include // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoney/mymoneyfile.h" #include "mymoney/mymoneyaccount.h" #include "mymoney/mymoneykeyvaluecontainer.h" #include "plugins/onlinepluginextended.h" #include "onlinetasks/unavailabletask/tasks/unavailabletask.h" #include "onlinetasks/interfaces/tasks/credittransfer.h" #include "tasks/onlinetask.h" onlineJobAdministration::onlineJobAdministration(QObject *parent) : QObject(parent) { } onlineJobAdministration::~onlineJobAdministration() { clearCaches(); } void onlineJobAdministration::clearCaches() { qDeleteAll(m_onlineTasks); m_onlineTasks.clear(); qDeleteAll(m_onlineTaskConverter); m_onlineTaskConverter.clear(); } KMyMoneyPlugin::OnlinePluginExtended* onlineJobAdministration::getOnlinePlugin(const QString& accountId) const { MyMoneyAccount acc = MyMoneyFile::instance()->account(accountId); QMap::const_iterator it_p; - it_p = m_onlinePlugins.constFind(acc.onlineBankingSettings().value("provider")); + it_p = m_onlinePlugins->constFind(acc.onlineBankingSettings().value("provider").toLower()); - if (it_p != m_onlinePlugins.constEnd()) { + if (it_p != m_onlinePlugins->constEnd()) { // plugin found, use it return *it_p; } return 0; } -void onlineJobAdministration::addPlugin(const QString& pluginName, KMyMoneyPlugin::OnlinePluginExtended *plugin) +void onlineJobAdministration::setOnlinePlugins(QMap& plugins) { - const bool sendAnyTask = canSendAnyTask(); - const bool sendCreditTransfer = canSendCreditTransfer(); - - m_onlinePlugins.insert(pluginName, plugin); + m_onlinePlugins = &plugins; + updateActions(); +} - if (!sendAnyTask && canSendAnyTask()) - emit canSendAnyTaskChanged(true); - if (!sendCreditTransfer && canSendCreditTransfer()) - emit canSendCreditTransferChanged(true); +void onlineJobAdministration::updateActions() +{ + emit canSendAnyTaskChanged(canSendAnyTask()); + emit canSendCreditTransferChanged(canSendCreditTransfer()); } QStringList onlineJobAdministration::availableOnlineTasks() { auto plugins = KPluginLoader::findPlugins("kmymoney", [](const KPluginMetaData& data) { return !(data.rawData()["KMyMoney"].toObject()["OnlineTask"].isNull()); }); QStringList list; for(const KPluginMetaData& plugin: plugins) { QJsonValue array = plugin.rawData()["KMyMoney"].toObject()["OnlineTask"].toObject()["Iid"]; if (array.isArray()) list.append(array.toVariant().toStringList()); } return list; } /** * @internal The real work is done here. */ bool onlineJobAdministration::isJobSupported(const QString& accountId, const QString& name) const { - foreach (KMyMoneyPlugin::OnlinePluginExtended* plugin, m_onlinePlugins) { + if (!m_onlinePlugins) + return false; + foreach (KMyMoneyPlugin::OnlinePluginExtended* plugin, *m_onlinePlugins) { if (plugin->availableJobs(accountId).contains(name)) return true; } return false; } bool onlineJobAdministration::isJobSupported(const QString& accountId, const QStringList& names) const { foreach (QString name, names) { if (isJobSupported(accountId, name)) return true; } return false; } bool onlineJobAdministration::isAnyJobSupported(const QString& accountId) const { if (accountId.isEmpty()) return false; - foreach (KMyMoneyPlugin::OnlinePluginExtended* plugin, m_onlinePlugins) { + if (!m_onlinePlugins) + return false; + + foreach (KMyMoneyPlugin::OnlinePluginExtended* plugin, *m_onlinePlugins) { if (!(plugin->availableJobs(accountId).isEmpty())) return true; } return false; } onlineJob onlineJobAdministration::createOnlineJob(const QString& name, const QString& id) const { return (onlineJob(createOnlineTask(name), id)); } onlineTask* onlineJobAdministration::createOnlineTask(const QString& name) const { const onlineTask* task = rootOnlineTask(name); if (task != 0) return task->clone(); return 0; } onlineTask* onlineJobAdministration::createOnlineTaskByXml(const QString& iid, const QDomElement& element) const { onlineTask* task = rootOnlineTask(iid); if (task != 0) { return task->createFromXml(element); } qWarning("In the file is a onlineTask for which I could not find the plugin ('%s')", qPrintable(iid)); return new unavailableTask(element); } onlineTask* onlineJobAdministration::createOnlineTaskFromSqlDatabase(const QString& iid, const QString& onlineTaskId, QSqlDatabase connection) const { onlineTask* task = rootOnlineTask(iid); if (task != 0) return task->createFromSqlDatabase(connection, onlineTaskId); qWarning("In the file is a onlineTask for which I could not find the plugin ('%s')", qPrintable(iid)); return 0; } /** * @interanl Using KPluginFactory to create the plugins seemed to be good idea. The drawback is that it does not support to create non QObjects directly. * This made this function way longer than needed and adds many checks. * * @fixme Delete created tasks */ onlineTask* onlineJobAdministration::rootOnlineTask(const QString& name) const { auto plugins = KPluginLoader::findPlugins("kmymoney", [&name](const KPluginMetaData& data) { QJsonValue array = data.rawData()["KMyMoney"].toObject()["OnlineTask"].toObject()["Iids"]; if (array.isArray()) return (array.toVariant().toStringList().contains(name)); return false; }); if (plugins.isEmpty()) return nullptr; if (plugins.length() != 1) qWarning() << "Multiple plugins which offer the online task \"" << name << "\" were found. Loading a random one."; // Load plugin std::unique_ptr loader = std::unique_ptr(new QPluginLoader{plugins.first().fileName()}); QObject* plugin = loader->instance(); if (!plugin) { qWarning() << "Could not load plugin for online task \"" << name << "\", file name \"" << plugins.first().fileName() << "\"."; return nullptr; } // Cast to KPluginFactory KPluginFactory* pluginFactory = qobject_cast< KPluginFactory* >(plugin); if (!pluginFactory) { qWarning() << "Could not create plugin factory for online task \"" << name << "\", file name \"" << plugins.first().fileName() << "\"."; return nullptr; } // Create onlineTaskFactory const QString pluginKeyword = plugins.first().rawData()["KMyMoney"].toObject()["OnlineTask"].toObject()["PluginKeyword"].toString(); // Can create only objects which inherit from QObject directly QObject* taskFactoryObject = pluginFactory->create(pluginKeyword, onlineJobAdministration::instance()); KMyMoneyPlugin::onlineTaskFactory* taskFactory = qobject_cast< KMyMoneyPlugin::onlineTaskFactory* >(taskFactoryObject); if (!taskFactory) { qWarning() << "Could not create online task factory for online task \"" << name << "\", file name \"" << plugins.first().fileName() << "\"."; return nullptr; } // Finally create task onlineTask* task = taskFactory->createOnlineTask(name); if (task) // Add to our cache as this is still used in several places onlineJobAdministration::instance()->registerOnlineTask(taskFactory->createOnlineTask(name)); return task; } onlineTaskConverter::convertType onlineJobAdministration::canConvert(const QString& originalTaskIid, const QString& convertTaskIid) const { return canConvert(originalTaskIid, QStringList(convertTaskIid)); } onlineTaskConverter::convertType onlineJobAdministration::canConvert(const QString& originalTaskIid, const QStringList& convertTaskIids) const { Q_ASSERT(false); //! @todo Make alive onlineTaskConverter::convertType bestConvertType = onlineTaskConverter::convertImpossible; #if 0 foreach (QString destinationName, destinationNames) { onlineTask::convertType type = canConvert(original, destinationName); if (type == onlineTask::convertionLossy) bestConvertType = onlineTask::convertionLossy; else if (type == onlineTask::convertionLoseless) return onlineTask::convertionLoseless; } #else Q_UNUSED(originalTaskIid); Q_UNUSED(convertTaskIids); #endif return bestConvertType; } /** * @todo if more than one converter offers the convert, use best */ onlineJob onlineJobAdministration::convert(const onlineJob& original, const QString& convertTaskIid, onlineTaskConverter::convertType& convertType, QString& userInformation, const QString& onlineJobId) const { onlineJob newJob; QList converterList = m_onlineTaskConverter.values(convertTaskIid); foreach (onlineTaskConverter* converter, converterList) { if (converter->convertibleTasks().contains(original.taskIid())) { onlineTask* task = converter->convert(*original.task(), convertType, userInformation); Q_ASSERT_X(convertType != onlineTaskConverter::convertImpossible || task != 0, qPrintable("converter for " + converter->convertedTask()), "Converter returned convertType 'impossible' but return was not null_ptr."); if (task != 0) { newJob = onlineJob(task, onlineJobId); break; } } } return newJob; } onlineJob onlineJobAdministration::convertBest(const onlineJob& original, const QStringList& convertTaskIids, onlineTaskConverter::convertType& convertType, QString& userInformation) const { return convertBest(original, convertTaskIids, convertType, userInformation, original.id()); } onlineJob onlineJobAdministration::convertBest(const onlineJob& original, const QStringList& convertTaskIids, onlineTaskConverter::convertType& bestConvertType, QString& bestUserInformation, const QString& onlineJobId) const { onlineJob bestConvert; bestConvertType = onlineTaskConverter::convertImpossible; bestUserInformation = QString(); foreach (QString taskIid, convertTaskIids) { // Try convert onlineTaskConverter::convertType convertType = onlineTaskConverter::convertImpossible; QString userInformation; onlineJob convertJob = convert(original, taskIid, convertType, userInformation, onlineJobId); // Check if it was successful if (bestConvertType < convertType) { bestConvert = convertJob; bestUserInformation = userInformation; bestConvertType = convertType; if (convertType == onlineTaskConverter::convertionLoseless) break; } } return bestConvert; } void onlineJobAdministration::registerOnlineTask(onlineTask *const task) { if (Q_UNLIKELY(task == 0)) return; const bool sendAnyTask = canSendAnyTask(); const bool sendCreditTransfer = canSendCreditTransfer(); m_onlineTasks.insert(task->taskName(), task); qDebug() << "onlineTask available" << task->taskName(); if (sendAnyTask != canSendAnyTask()) emit canSendAnyTaskChanged(!sendAnyTask); if (sendCreditTransfer != canSendCreditTransfer()) emit canSendCreditTransferChanged(!sendCreditTransfer); } void onlineJobAdministration::registerOnlineTaskConverter(onlineTaskConverter* const converter) { if (Q_UNLIKELY(converter == 0)) return; m_onlineTaskConverter.insertMulti(converter->convertedTask(), converter); qDebug() << "onlineTaskConverter available" << converter->convertedTask() << converter->convertibleTasks(); } onlineJobAdministration::onlineJobEditOffers onlineJobAdministration::onlineJobEdits() { auto plugins = KPluginLoader::findPlugins("kmymoney", [](const KPluginMetaData& data) { return !(data.rawData()["KMyMoney"].toObject()["OnlineTask"].toObject()["Editors"].isNull()); }); onlineJobAdministration::onlineJobEditOffers list; list.reserve(plugins.size()); for(const KPluginMetaData& data: plugins) { QJsonArray editorsArray = data.rawData()["KMyMoney"].toObject()["OnlineTask"].toObject()["Editors"].toArray(); for(QJsonValue entry: editorsArray) { if (!entry.toObject()["OnlineTaskIds"].isNull()) { list.append(onlineJobAdministration::onlineJobEditOffer{ data.fileName(), entry.toObject()["PluginKeyword"].toString(), KPluginMetaData::readTranslatedString(entry.toObject(), "Name") }); } } } return list; } IonlineTaskSettings::ptr onlineJobAdministration::taskSettings(const QString& taskName, const QString& accountId) const { KMyMoneyPlugin::OnlinePluginExtended* plugin = getOnlinePlugin(accountId); if (plugin != 0) return (plugin->settings(accountId, taskName)); return IonlineTaskSettings::ptr(); } bool onlineJobAdministration::canSendAnyTask() { + if (!m_onlinePlugins) + return false; + // Check if any plugin supports a loaded online task - foreach (KMyMoneyPlugin::OnlinePluginExtended* plugin, m_onlinePlugins) { + foreach (KMyMoneyPlugin::OnlinePluginExtended* plugin, *m_onlinePlugins) { QList accounts; MyMoneyFile::instance()->accountList(accounts, QStringList(), true); foreach (MyMoneyAccount account, accounts) { foreach (QString onlineTaskIid, plugin->availableJobs(account.id())) { if (m_onlineTasks.contains(onlineTaskIid)) return true; } } } return false; } bool onlineJobAdministration::canSendCreditTransfer() { + if (!m_onlinePlugins) + return false; + foreach (onlineTask* task, m_onlineTasks) { // Check if a online task has the correct type if (dynamic_cast(task) != 0) { - foreach (KMyMoneyPlugin::OnlinePluginExtended* plugin, m_onlinePlugins) { + foreach (KMyMoneyPlugin::OnlinePluginExtended* plugin, *m_onlinePlugins) { QList accounts; MyMoneyFile::instance()->accountList(accounts, QStringList(), true); foreach (MyMoneyAccount account, accounts) { if (plugin->availableJobs(account.id()).contains(task->taskName())) return true; } } } } return false; } //! @fixme plugin query bool onlineJobAdministration::canEditOnlineJob(const onlineJob& job) { return (!job.taskIid().isEmpty() && !KServiceTypeTrader::self()->query(QLatin1String("KMyMoney/OnlineTaskUi"), QString("'%1' ~in [X-KMyMoney-onlineTaskIds]").arg(job.taskIid())).isEmpty()); } void onlineJobAdministration::updateOnlineTaskProperties() { emit canSendAnyTaskChanged(canSendAnyTask()); emit canSendCreditTransferChanged(canSendCreditTransfer()); } diff --git a/kmymoney/mymoney/onlinejobadministration.h b/kmymoney/mymoney/onlinejobadministration.h index 88670b02d..5467f04ed 100644 --- a/kmymoney/mymoney/onlinejobadministration.h +++ b/kmymoney/mymoney/onlinejobadministration.h @@ -1,343 +1,352 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 Christian Dávid * * 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 . */ #ifndef ONLINEJOBADMINISTRATION_H #define ONLINEJOBADMINISTRATION_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "onlinejob.h" #include "onlinetasks/interfaces/tasks/onlinetask.h" #include "onlinetasks/interfaces/tasks/ionlinetasksettings.h" #include "onlinetasks/interfaces/tasks/credittransfer.h" #include "onlinetasks/interfaces/converter/onlinetaskconverter.h" class onlineTask; class IonlineJobEdit; class QSqlDatabase; namespace KMyMoneyPlugin { class OnlinePluginExtended; } /** * @brief Connection between KMyMoney and the plugins * * It's main task is the communication with plugins * and caching their information during run-time. During * sending this class selects the correct plugin for each * onlineJob. * * This class keeps an overview which account can handle which job and * offers methods to access these information. * * onlineJobAdministration is created with singleton pattern. Get the * instance with @ref onlineJobAdministration::instance() . */ class KMM_MYMONEY_EXPORT onlineJobAdministration : public QObject { Q_OBJECT KMM_MYMONEY_UNIT_TESTABLE Q_PROPERTY(bool canSendAnyTask READ canSendAnyTask NOTIFY canSendAnyTaskChanged STORED false); Q_PROPERTY(bool canSendCreditTransfer READ canSendCreditTransfer NOTIFY canSendCreditTransferChanged STORED false); public: explicit onlineJobAdministration(QObject *parent = 0); ~onlineJobAdministration(); struct onlineJobEditOffer { QString fileName; QString pluginKeyword; QString name; }; using onlineJobEditOffers = QVector; /** * @brief List all available onlineTasks */ QStringList availableOnlineTasks(); static onlineJobAdministration* instance() { static onlineJobAdministration m_instance; return &m_instance; } /** @brief clear the internal caches for shutdown */ void clearCaches(); /** @brief Use onlineTask::name() to create a corresponding onlineJob */ onlineJob createOnlineJob(const QString& name, const QString& id = QString()) const; /** * @brief Return list of IonlineJobEdits * * Method is temporary! * * @return I stay owner of all pointers. */ onlineJobEditOffers onlineJobEdits(); QString onlineJobEditName(onlineJobEditOffer); bool isJobSupported(const QString& accountId, const QString& name) const; bool isJobSupported(const QString& accountId, const QStringList& names) const; bool isAnyJobSupported(const QString& accountId) const; onlineTaskConverter::convertType canConvert(const QString& originalTaskIid, const QString& convertTaskIid) const; onlineTaskConverter::convertType canConvert(const QString& originalTaskIid, const QStringList& convertTaskIids) const; #if 0 template onlineJobTyped convert(const onlineJob& original, const QString& convertTaskIid, onlineTaskConverter::convertType& convertType, QString& userInformation, const QString& onlineJobId) const; template onlineJobTyped convert(const onlineJob& original, const QString& convertTaskIid, onlineTaskConverter::convertType& convertType, QString& userInformation) const; #endif /** * @brief Convert an onlineTask to another type * * @param original onlineJob to convert * @param convertTaskIid onlineTask iid you want to convert into * @param convertType OUT result of conversion. Note: this depends on original * @param userInformation OUT A translated html-string with information about the changes which were done * @param onlineJobId The id of the new onlineJob, if none is given original.id() is used */ onlineJob convert(const onlineJob& original, const QString& convertTaskIid, onlineTaskConverter::convertType& convertType, QString& userInformation, const QString& onlineJobId) const; /** * @copydoc convert() */ onlineJob convert(const onlineJob& original, const QString& convertTaskIid, onlineTaskConverter::convertType& convertType, QString& userInformation) const; /** * @brief Converts a onlineTask to best fitting type of a set of onlineTasks * * Will look for best conversion possible from original to any of convertTaskIids. * * @param original onlineJob to convert * @param convertTaskIids onlineTask-iids you want to convert into. * @param convertType OUT result of conversion. Note: this depends on original * @param userInformation OUT A translated html-string with information about the changes which were done * @param onlineJobId The id of the new onlineJob, if none is given original.id() is used */ onlineJob convertBest(const onlineJob& original, const QStringList& convertTaskIids, onlineTaskConverter::convertType& convertType, QString& userInformation, const QString& onlineJobId) const; /** * @brief Convinient for convertBest() which crates an onlineJob with the same id as original. */ onlineJob convertBest(const onlineJob& original, const QStringList& convertTaskIids, onlineTaskConverter::convertType& convertType, QString& userInformation) const; /** * @brief Request onlineTask::settings from plugin * * @return QSharedPointer to settings from plugin, can be a nullptr */ template QSharedPointer taskSettings(const QString& taskId, const QString& accountId) const; /** * @brief Request onlineTask::settings from plugin * * @see onlineTask::settings * * @param taskId onlineTask::name() * @param accountId MyMoneyAccount.id() * @return QSharedPointer to settings. QSharedPointer::isNull() is true if an error occurs * (e.g. plugin does not support the task). */ QSharedPointer taskSettings(const QString& taskId, const QString& accountId) const; /** * @brief Check if the onlineTask system can do anything * * This is true if at least one plugin can process one of the available onlineTasks for at least one available account. */ bool canSendAnyTask(); /** * @brief Are there plugins and accounts to send a credit transfers? * * Like @r canSendAnyTask() but restricts the onlineTasks to credit transfers. This is useful * to disable the create credit transfer buttons. */ bool canSendCreditTransfer(); /** * @brief Are all preconditions set to edit the given job? */ bool canEditOnlineJob(const onlineJob& job); /** * @brief See if a online task has a specified base * * This is usable if you want to see if e.g. taskIid is * of type creditTransfer */ template bool isInherited(const QString& taskIid) const; + /** + * @brief makes plugins loaded in KMyMoneyApp available here + * @param plugins + */ + void setOnlinePlugins(QMap& plugins); + + /** + * @brief updates online actions and should be called after plugin enable or disable + */ + void updateActions(); + Q_SIGNALS: /** * @brief Emitted if canSendAnyTask() changed * * At the moment it this signal can be sent even if the status did not change. */ void canSendAnyTaskChanged(bool); /** * @brief Emitted if canSendCreditTransfer changed * * At the moment it this signal can be sent even if the status did not change. */ void canSendCreditTransferChanged(bool); public Q_SLOTS: - void addPlugin(const QString& pluginName, KMyMoneyPlugin::OnlinePluginExtended*); - /** * @brief Slot for plugins to make an onlineTask available. * @param task the task to register, I take ownership */ void registerOnlineTask(onlineTask *const task); /** * @brief Slot for plugins to make an onlineTaskConverter available. * @param converter the converter to register, I take ownership */ void registerOnlineTaskConverter(onlineTaskConverter *const converter); /** * @brief Check if the properties about available and sendable online tasks are still valid */ void updateOnlineTaskProperties(); private: /** * @brief Find onlinePlugin which is responsible for accountId * @param accountId * @return Pointer to onlinePluginExtended, do not delete. */ KMyMoneyPlugin::OnlinePluginExtended* getOnlinePlugin(const QString& accountId) const; /** * @brief Creates an onlineTask by iid * @return pointer to task, caller gains ownership. Can be 0. */ onlineTask* createOnlineTask(const QString& iid) const; /** * @brief Creates an onlineTask by its iid and xml data * @return pointer to task, caller gains ownership. Can be 0. */ onlineTask* createOnlineTaskByXml(const QString& iid, const QDomElement& element) const; /** * @brief Creates an onlineTask by its iid and xml data * @return pointer to task, caller gains ownership. Can be 0. */ onlineTask* createOnlineTaskFromSqlDatabase(const QString& iid, const QString& onlineJobId, QSqlDatabase connection) const; // Must be able to call createOnlineTaskByXml friend class onlineJob; // Must be able to call createOnlineTask template friend class onlineJobTyped; // Must be able to call createOnlineTaskFromSqlDatabase() friend class MyMoneyStorageSql; /** * @brief Get root instance of an onlineTask * * Returns a pointer from m_onlineTasks or tries to load/create * a approiate root element. * * Only createOnlineTask and createOnlineTaskByXml use it. * * @return A pointer, you do *not* gain ownership! Can be 0 if something went wrong. * * @internal Made to be forward compatible when onlineTask are loaded as plugins. */ inline onlineTask* rootOnlineTask(const QString& name) const; /** * The key is the onlinePlugin's name */ - QMap m_onlinePlugins; + QMap* m_onlinePlugins; /** * The key is the name of the task */ QMap m_onlineTasks; /** * Key is the task the converter converts to */ QMultiMap m_onlineTaskConverter; /** * Intances of editors */ QList m_onlineTaskEditors; }; template QSharedPointer onlineJobAdministration::taskSettings(const QString& taskName, const QString& accountId) const { IonlineTaskSettings::ptr settings = taskSettings(taskName, accountId); if (!settings.isNull()) { QSharedPointer settingsFinal = settings.dynamicCast(); if (Q_LIKELY(!settingsFinal.isNull())) // This can only happen if the onlinePlugin has a bug. return settingsFinal; } return QSharedPointer(); } template< class baseTask > bool onlineJobAdministration::isInherited(const QString& taskIid) const { return (dynamic_cast(rootOnlineTask(taskIid)) != 0); } #if 0 template onlineJobTyped onlineJobAdministration::convert(const onlineJob& original, const QString& convertTaskIid, onlineTaskConverter::convertType& convertType, QString& userInformation, const QString& onlineJobId) const { onlineJob job = convert(original, convertTaskIid, convertType, userInformation, onlineJobId); return onlineJobTyped(job); } template onlineJobTyped< T > onlineJobAdministration::convert(const onlineJob& original, const QString& convertTaskIid, onlineTaskConverter::convertType& convertType, QString& userInformation) const { return convert(original, convertTaskIid, convertType, userInformation, original.id()); } #endif #endif // ONLINEJOBADMINISTRATION_H diff --git a/kmymoney/pluginloader.cpp b/kmymoney/pluginloader.cpp index df6c9a78e..bb6581fc8 100644 --- a/kmymoney/pluginloader.cpp +++ b/kmymoney/pluginloader.cpp @@ -1,147 +1,148 @@ /*************************************************************************** pluginloader.cpp ------------------- - begin : Thu Feb 12 2009 - copyright : (C) 2009 Cristian Onet - email : onet.cristian@gmail.com + (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 "pluginloader.h" // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes -#include -#include #include -#include +#include +#include +#include +#include #include // ---------------------------------------------------------------------------- // Project Includes +#include "kmymoneyplugin.h" +#include "onlinepluginextended.h" + namespace KMyMoneyPlugin { + Category pluginCategory(const KPluginMetaData& pluginInfo) + { + if (!pluginInfo.serviceTypes().contains(QStringLiteral("KMyMoney/Plugin"))) { + auto jsonKMyMoneyData = pluginInfo.rawData()[QLatin1String("KMyMoney")].toObject(); + if (!jsonKMyMoneyData[QLatin1String("OnlineTask")].isNull()) + return OnlineBankOperations; + else if (!jsonKMyMoneyData[QLatin1String("PayeeIdentifier")].isNull()) + return PayeeIdentifier; + } + return StandardPlugin; + } -//--------------------------------------------------------------------- -// -// PluginLoader -// -//--------------------------------------------------------------------- -static PluginLoader* s_instance = nullptr; + bool isPluginEnabled(const KPluginMetaData& pluginData, const KConfigGroup& pluginSection) + { + return pluginSection.readEntry(QString::fromLatin1("%1Enabled"). // we search here for e.g. "csvimporterEnabled = true" + arg(pluginData.pluginId()), + pluginData.isEnabledByDefault()); // if not found, then get default from plugin's json file + } -PluginLoader::PluginLoader(QObject* parent) - : QObject(parent), - m_displayedPlugins{} -{ - Q_ASSERT(s_instance == nullptr); - s_instance = this; + QMap listPlugins(bool onlyEnabled) + { + QMap plugins; + const auto pluginDatas = KPluginLoader::findPlugins(QStringLiteral("kmymoney")); // that means search for plugins in "/lib64/plugins/kmymoney/" + const auto pluginSection(KSharedConfig::openConfig()->group(QStringLiteral("Plugins"))); // section of config where plugin on/off were saved + + for (const KPluginMetaData& pluginData : pluginDatas) { + if (pluginData.serviceTypes().contains(QStringLiteral("KMyMoney/Plugin"))) { + if (!onlyEnabled || + (onlyEnabled && isPluginEnabled(pluginData, pluginSection))) { + plugins.insert(pluginData.pluginId(), pluginData); + } + } + } + return plugins; + } - m_categoryKMyMoneyPlugin = i18n("KMyMoney Plugins"); - m_categoryOnlineTask = i18n("Online Banking Operations"); - m_categoryPayeeIdentifier = i18n("Payee Identifier"); + void pluginHandling(Action action, Container& ctnPlugins, QObject* parent, KXMLGUIFactory* guiFactory) + { + + QMap referencePluginDatas; + if (action == Action::Load || + action == Action::Reorganize) + referencePluginDatas = listPlugins(true); + + if (action == Action::Unload || + action == Action::Reorganize) { + auto& plugins = ctnPlugins.standard; + auto& refPlugins = referencePluginDatas; + for (auto it = plugins.begin(); it != plugins.end();) { + if (!refPlugins.contains(it.key())) { + + ctnPlugins.online.remove(it.key()); + ctnPlugins.extended.remove(it.key()); + ctnPlugins.importer.remove(it.key()); + + guiFactory->removeClient(it.value()); + it.value()->unplug(); + delete it.value(); + it = plugins.erase(it); + continue; + + } + ++it; + } + } - m_pluginSelector = new KPluginSelector(); - connect(m_pluginSelector, &KPluginSelector::configCommitted, this, &PluginLoader::changed); -} + if (action == Action::Load || + action == Action::Reorganize) { -PluginLoader::~PluginLoader() -{ - Q_ASSERT(s_instance != nullptr); + auto& refPlugins = referencePluginDatas; + for (auto it = refPlugins.cbegin(); it != refPlugins.cend(); ++it) { + if (!ctnPlugins.standard.contains(it.key())) { - KSharedConfigPtr config = KSharedConfig::openConfig(); - KConfigGroup group{ config->group("Plugins") }; + KPluginLoader loader((*it).fileName()); + auto factory = loader.factory(); + if (!factory) { + qWarning("Could not load plugin '%s', error: %s", qPrintable((*it).fileName()), qPrintable(loader.errorString())); + loader.unload(); + continue; + } - for (const QString& fileName: m_displayedPlugins) { - KPluginMetaData pluginData{fileName}; - pluginDisabled(pluginData); - } + Plugin* plugin = factory->create(parent); + if (!plugin) { + qWarning("This is not KMyMoney plugin: '%s'", qPrintable((*it).fileName())); + loader.unload(); + continue; + } - delete m_pluginSelector; - s_instance = nullptr; -} + ctnPlugins.standard.insert((*it).pluginId(), plugin); + plugin->plug(); + guiFactory->addClient(plugin); -void PluginLoader::detectPlugins() -{ - addPluginInfo(KPluginLoader::findPlugins("kmymoney")); -} + auto IOnline = qobject_cast(plugin); + if (IOnline) + ctnPlugins.online.insert((*it).pluginId(), IOnline); -inline bool PluginLoader::isPluginEnabled(const KPluginMetaData& metaData, const KConfigGroup& configGroup) -{ - //! @fixme: there is a function in KMyMoneyApp which has to have the same content - if (metaData.serviceTypes().contains("KMyMoney/Plugin")) { - const QString keyName{metaData.name() + "Enabled"}; - if (configGroup.hasKey(keyName)) - return configGroup.readEntry(keyName, true); - return metaData.isEnabledByDefault(); - } - return false; -} + auto IExtended = qobject_cast(plugin); + if (IExtended ) + ctnPlugins.extended.insert((*it).pluginId(), IExtended ); -void PluginLoader::addPluginInfo(const QVector& metaData) -{ - m_displayedPlugins.reserve(metaData.size()); - for(const auto& plugin: metaData) { - m_displayedPlugins.insert(plugin.fileName()); - KPluginInfo info {plugin}; - KPluginSelector::PluginLoadMethod loadMethod = (plugin.serviceTypes().contains("KMyMoney/Plugin")) - ? KPluginSelector::PluginLoadMethod::ReadConfigFile - : KPluginSelector::PluginLoadMethod::IgnoreConfigFile; - m_pluginSelector->addPlugins(QList {info}, loadMethod, categoryByPluginType(plugin)); - //! @fixme The not all plugins are deactivated correctly at the moment. This has to change or the user should not get any option to enable and disable them - } -} + auto IImporter = qobject_cast(plugin); + if (IImporter) + ctnPlugins.importer.insert((*it).pluginId(), IImporter); -QString PluginLoader::categoryByPluginType(const KPluginMetaData& mataData) -{ - if (!mataData.serviceTypes().contains("KMyMoney/Plugin")) { - QJsonObject jsonKMyMoneyData = mataData.rawData()["KMyMoney"].toObject(); - if (!jsonKMyMoneyData["OnlineTask"].isNull()) - return m_categoryOnlineTask; - else if (!jsonKMyMoneyData["PayeeIdentifier"].isNull()) - return m_categoryPayeeIdentifier; - } - return m_categoryKMyMoneyPlugin; -} + } + } -void PluginLoader::changed() -{ - KSharedConfigPtr config = KSharedConfig::openConfig(); - KConfigGroup group{ config->group("Plugins") }; - - for (const QString& fileName: m_displayedPlugins) { - KPluginMetaData pluginData{fileName}; - if (isPluginEnabled(pluginData, group)) { - pluginEnabled(pluginData); - } else { - pluginDisabled(pluginData); } } } - -PluginLoader* PluginLoader::instance() -{ - Q_ASSERT(s_instance); - return s_instance; -} - -KPluginSelector* PluginLoader::pluginSelectorWidget() -{ - return m_pluginSelector; -} - -} // namespace diff --git a/kmymoney/pluginloader.h b/kmymoney/pluginloader.h index 1c1d0c1dc..d9643b7a1 100644 --- a/kmymoney/pluginloader.h +++ b/kmymoney/pluginloader.h @@ -1,95 +1,94 @@ /*************************************************************************** pluginloader.h ------------------- - begin : Thu Feb 12 2009 - copyright : (C) 2009 Cristian Onet - email : onet.cristian@gmail.com + (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. * * * ***************************************************************************/ #ifndef PLUGINLOADER_H #define PLUGINLOADER_H -#include -#include -#include +// ---------------------------------------------------------------------------- +// QT Includes -class KPluginSelector; -class KConfigGroup; +#include -namespace KMyMoneyPlugin -{ -class Plugin; - -/** - * @brief User Interface for plugins - * - * For historic reasons it is still called plugin loader even though it does not load any plugin anymore. - */ -class PluginLoader : public QObject -{ - Q_OBJECT -public: - explicit PluginLoader(QObject* parent); +// ---------------------------------------------------------------------------- +// KDE Includes +// ---------------------------------------------------------------------------- +// Project Includes - /** - * Needed to delete the unique_ptr which is of incomplete type in the header file - */ - virtual ~PluginLoader(); - static PluginLoader* instance(); +class KPluginMetaData; +class KXMLGUIFactory; - KPluginSelector* pluginSelectorWidget(); - static inline bool isPluginEnabled(const KPluginMetaData& metaData, const KConfigGroup& configGroup); +class QObject; +class QString; -public Q_SLOTS: - /** @brief Adds the given plugins to the plugin selection ui */ - void addPluginInfo(const QVector& metaData); +template class QMap; - /** @brief Find all available plugins */ - void detectPlugins(); +namespace KMyMoneyPlugin +{ + class Plugin; + class OnlinePlugin; + class OnlinePluginExtended; + class ImporterPlugin; -Q_SIGNALS: - /** Load the shared module and emits plug() */ - void pluginEnabled(const KPluginMetaData& metaData); - void pluginDisabled(const KPluginMetaData& metaData); - void configChanged(Plugin*); // configuration of the plugin has changed not the enabled/disabled state + enum class eListing; -private Q_SLOTS: - void changed(); + /** + * @brief The Action enum is for specifing action on plugins + */ + enum Action { + Load, // load all enabled plugins + Unload, // unload all loaded plugins + Reorganize // load requested and unload unneeded plugins + }; -private: - KPluginSelector* m_pluginSelector; + /** + * @brief The Category enum is some arbitrary categorization of plugins + */ + enum Category { + OnlineBankOperations, + PayeeIdentifier, + StandardPlugin + }; /** - * @{ - * Translated strings - * - * They are created on creation time because they are used as identifiers. + * @brief The Container struct to hold all plugin interfaces */ - QString m_categoryKMyMoneyPlugin; - QString m_categoryOnlineTask; - QString m_categoryPayeeIdentifier; - /** @} */ + struct Container { + QMap standard; // this should contain all loaded plugins because every plugin should inherit Plugin class + QMap online; // casted standard plugin, if such interface is available + QMap extended; // casted standard plugin, if such interface is available + QMap importer; // casted standard plugin, if such interface is available + }; + + Category pluginCategory(const KPluginMetaData& pluginInfo); /** - * @brief All plugins which are listed in the UI - * - * The set contains the plugin file name. - * Maybe it can/should be replaced by something different. + * @brief It lists all kmymoney plugins + * @param onlyEnabled = true if plugins should be listed according to on/off saved state in kmymoneyrc + * @return */ - QSet m_displayedPlugins; + QMap listPlugins(bool onlyEnabled); - QString categoryByPluginType(const KPluginMetaData& mataData); -}; + /** + * @brief It should be used to handle all plugin actions + * @param action Action to be taken to all plugins + * @param ctnPlugins Plugin container to be loaded/unloaded with plugins + * @param parent Parent of plugins. This should be KMyMoneyApp + * @param guiFactory GUI Factory of plugins. This should be GUI Factory of KMyMoneyApp + */ + void pluginHandling(Action action, Container& ctnPlugins, QObject* parent, KXMLGUIFactory* guiFactory); } -#endif /* PLUGINLOADER_H */ +#endif diff --git a/kmymoney/plugins/CMakeLists.txt b/kmymoney/plugins/CMakeLists.txt index 12477dc3b..52db2f6da 100644 --- a/kmymoney/plugins/CMakeLists.txt +++ b/kmymoney/plugins/CMakeLists.txt @@ -1,83 +1,76 @@ add_subdirectory( onlinetasks ) add_subdirectory( ibanbicdata ) add_subdirectory( interfaces ) add_subdirectory( csv ) add_subdirectory( qif ) add_subdirectory( gnc ) - -if(ENABLE_LIBOFX) - add_subdirectory( ofximport ) -endif(ENABLE_LIBOFX) - -if (LIBICAL_FOUND) - add_subdirectory( icalendarexport ) -endif (LIBICAL_FOUND) - +add_subdirectory( ofx ) +add_subdirectory( icalendar ) add_subdirectory( reconciliationreport ) add_subdirectory( printcheck ) if (KBANKING_FOUND) add_subdirectory( kbanking ) endif (KBANKING_FOUND) option(ENABLE_SQLCIPHER "Enable SQLCipher plugin" OFF) if (ENABLE_SQLCIPHER) add_subdirectory(sqlcipher) endif(ENABLE_SQLCIPHER) option(ENABLE_ONLINEJOBPLUGINMOCKUP "Enable onlineJob-plugin mockup (only for developers)" OFF) if (ENABLE_ONLINEJOBPLUGINMOCKUP) add_subdirectory(onlinejobpluginmockup) endif() if (WEBOOB_FOUND) add_subdirectory( weboob ) endif (WEBOOB_FOUND) ########### next target ############### set(kmm_plugin_LIB_SRCS importinterface.cpp kmymoneyplugin.cpp statementinterface.cpp viewinterface.cpp onlinepluginextended.cpp interfaceloader.cpp ) set(plugins_HEADERS importinterface.h kmymoneyplugin.h statementinterface.h viewinterface.h ${CMAKE_CURRENT_BINARY_DIR}/kmm_plugin_export.h onlinepluginextended.h ) add_library(kmm_plugin SHARED ${kmm_plugin_LIB_SRCS}) generate_export_header(kmm_plugin BASE_NAME kmm_plugin) target_link_libraries(kmm_plugin PUBLIC KF5::XmlGui KF5::KCMUtils KF5::KIOWidgets Qt5::Gui Qt5::Widgets kmm_mymoney ) set_target_properties(kmm_plugin PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) ########### install files ############### install(TARGETS kmm_plugin ${INSTALL_TARGETS_DEFAULT_ARGS} ) install(FILES ${plugins_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/kmymoney COMPONENT Devel) install(FILES kmymoney-payeeidentifierdelegate.desktop kmymoney-importerplugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR} ) diff --git a/kmymoney/plugins/csv/export/CMakeLists.txt b/kmymoney/plugins/csv/export/CMakeLists.txt index 454d7d254..138aed8ea 100644 --- a/kmymoney/plugins/csv/export/CMakeLists.txt +++ b/kmymoney/plugins/csv/export/CMakeLists.txt @@ -1,35 +1,35 @@ # patch the version with the version defined in the build system -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/csvexport.json.in ${CMAKE_CURRENT_BINARY_DIR}/csvexport.json @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/csvexporter.json.in ${CMAKE_CURRENT_BINARY_DIR}/csvexporter.json @ONLY) ########### next target ############### -set(kmm_csvexport_PART_SRCS +set(csvexporter_PART_SRCS + csvexporter.cpp csvwriter.cpp - csvexporterplugin.cpp csvexportdlg.cpp ) -ki18n_wrap_ui(kmm_csvexport_PART_SRCS +ki18n_wrap_ui(csvexporter_PART_SRCS csvexportdlg.ui ) -add_library(kmm_csvexport MODULE - ${kmm_csvexport_PART_SRCS} +add_library(csvexporter MODULE + ${csvexporter_PART_SRCS} ) -target_link_libraries(kmm_csvexport +target_link_libraries(csvexporter kmm_mymoney kmm_plugin Alkimia::alkimia ) ########### install files ############### -install(FILES kmm_csvexport.rc - DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_csvexport") +install(FILES csvexporter.rc + DESTINATION "${KXMLGUI_INSTALL_DIR}/csvexporter") -install(TARGETS kmm_csvexport +install(TARGETS csvexporter DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") #INSTALL(FILES csvexporterrc # DESTINATION ${CONFIG_INSTALL_DIR}) diff --git a/kmymoney/plugins/csv/export/csvexporterplugin.cpp b/kmymoney/plugins/csv/export/csvexporter.cpp similarity index 70% rename from kmymoney/plugins/csv/export/csvexporterplugin.cpp rename to kmymoney/plugins/csv/export/csvexporter.cpp index 531da8762..abf9e64fe 100644 --- a/kmymoney/plugins/csv/export/csvexporterplugin.cpp +++ b/kmymoney/plugins/csv/export/csvexporter.cpp @@ -1,99 +1,105 @@ /*************************************************************************** - csvexporterplugin.cpp - (based on ofximporterplugin.cpp) + csvexporter.cpp ------------------- begin : Wed Mar 20 2013 copyright : (C) 2013 by Allan Anderson email : agander93@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "csvexporterplugin.h" +#include "csvexporter.h" #include "csvexportdlg.h" #include "csvwriter.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes +#include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes -CsvExporterPlugin::CsvExporterPlugin() : - KMyMoneyPlugin::Plugin(nullptr, "csvexport"/*must be the same as X-KDE-PluginInfo-Name*/) +CSVExporter::CSVExporter(QObject *parent, const QVariantList &args) : + KMyMoneyPlugin::Plugin(parent, "csvexporter"/*must be the same as X-KDE-PluginInfo-Name*/) { - setComponentName("kmm_csvexport", i18n("CSV exporter")); - setXMLFile("kmm_csvexport.rc"); + Q_UNUSED(args); + setComponentName("csvexporter", i18n("CSV exporter")); + setXMLFile("csvexporter.rc"); createActions(); // For information, announce that we have been loaded. - qDebug("KMyMoney csvexport plugin loaded"); + qDebug("Plugins: csvexporter loaded"); } -CsvExporterPlugin::~CsvExporterPlugin() +CSVExporter::~CSVExporter() { + qDebug("Plugins: csvexporter unloaded"); } -void CsvExporterPlugin::createActions() +void CSVExporter::createActions() { m_action = actionCollection()->addAction("file_export_csv"); m_action->setText(i18n("&CSV...")); - connect(m_action, SIGNAL(triggered(bool)), this, SLOT(slotCsvExport())); + connect(m_action, &QAction::triggered, this, &CSVExporter::slotCsvExport); } -void CsvExporterPlugin::slotCsvExport() +void CSVExporter::slotCsvExport() { m_dlg = new CsvExportDlg(); if (m_dlg->exec()) { if (okToWriteFile(QUrl::fromUserInput(m_dlg->filename()))) { m_dlg->setWindowTitle(i18nc("CSV Exporter dialog title", "CSV Exporter")); CsvWriter* writer = new CsvWriter; writer->m_plugin = this; - connect(writer, SIGNAL(signalProgress(int,int)), m_dlg, SLOT(slotStatusProgressBar(int,int))); + connect(writer, &CsvWriter::signalProgress, m_dlg, &CsvExportDlg::slotStatusProgressBar); writer->write(m_dlg->filename(), m_dlg->accountId(), m_dlg->accountSelected(), m_dlg->categorySelected(), m_dlg->startDate(), m_dlg->endDate(), m_dlg->separator()); } } } -bool CsvExporterPlugin::okToWriteFile(const QUrl &url) +bool CSVExporter::okToWriteFile(const QUrl &url) { // check if the file exists and warn the user bool reallySaveFile = true; bool fileExists = false; if (url.isValid()) { short int detailLevel = 0; // Lowest level: file/dir/symlink/none KIO::StatJob* statjob = KIO::stat(url, KIO::StatJob::SourceSide, detailLevel); bool noerror = statjob->exec(); if (noerror) { // We want a file fileExists = !statjob->statResult().isDir(); } } if (fileExists) { - if (KMessageBox::warningYesNo(0, QString("") + i18n("The file %1 already exists. Do you really want to overwrite it?", url.toDisplayString(QUrl::PreferLocalFile)) + QString(""), i18n("File already exists")) != KMessageBox::Yes) + if (KMessageBox::warningYesNo(0, i18n("The file %1 already exists. Do you really want to overwrite it?", url.toDisplayString(QUrl::PreferLocalFile)), i18n("File already exists")) != KMessageBox::Yes) reallySaveFile = false; } return reallySaveFile; } + +K_PLUGIN_FACTORY_WITH_JSON(CSVExporterFactory, "csvexporter.json", registerPlugin();) + +#include "csvexporter.moc" diff --git a/kmymoney/plugins/csv/export/csvexporterplugin.h b/kmymoney/plugins/csv/export/csvexporter.h similarity index 83% rename from kmymoney/plugins/csv/export/csvexporterplugin.h rename to kmymoney/plugins/csv/export/csvexporter.h index 984406c6a..2c04eaf40 100644 --- a/kmymoney/plugins/csv/export/csvexporterplugin.h +++ b/kmymoney/plugins/csv/export/csvexporter.h @@ -1,58 +1,57 @@ /*************************************************************************** - csvexporterplugin.h + csvexporter.h ------------------- begin : Wed Mar 20 2013 copyright : (C) 2013 by Allan Anderson email : agander93@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ -#ifndef CSVEXPORTERPLUGIN_H -#define CSVEXPORTERPLUGIN_H +#ifndef CSVEXPORTER_H +#define CSVEXPORTER_H // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // QT Includes // Project Includes #include "kmymoneyplugin.h" class CsvExportDlg; -class CsvExporterPlugin : public KMyMoneyPlugin::Plugin +class CSVExporter : public KMyMoneyPlugin::Plugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.csvexport" FILE "csvexport.json") public: - explicit CsvExporterPlugin(); - ~CsvExporterPlugin(); + explicit CSVExporter(QObject *parent, const QVariantList &args); + ~CSVExporter() override; QAction* m_action; bool okToWriteFile(const QUrl &url); CsvExportDlg* exporterDialog() { return m_dlg; } private: CsvExportDlg* m_dlg; protected Q_SLOTS: void slotCsvExport(); protected: void createActions(); }; #endif diff --git a/kmymoney/plugins/csv/export/csvexport.json.in b/kmymoney/plugins/csv/export/csvexporter.json.in similarity index 99% rename from kmymoney/plugins/csv/export/csvexport.json.in rename to kmymoney/plugins/csv/export/csvexporter.json.in index c376c1a6f..c597ce1c2 100644 --- a/kmymoney/plugins/csv/export/csvexport.json.in +++ b/kmymoney/plugins/csv/export/csvexporter.json.in @@ -1,80 +1,80 @@ { "KPlugin": { "Authors": [ { "Email": "aganders93@gmail.com", "Name": "Allan Anderson" } ], "Description": "Add CSV exporting to KMyMoney", "Description[ar]": "أضف تصدير CSV إلى أموالك", "Description[bs]": "Dodaje CSV izvoz KMyMoney", "Description[ca@valencia]": "Afig l'exportació CSV al KMyMoney", "Description[ca]": "Afegeix l'exportació CSV al KMyMoney", "Description[da]": "Føj CSV-eksport til KMyMoney", "Description[de]": "Fügt CSV-Export zu KMyMoney hinzu", "Description[el]": "Προσθήκη της εξαγωγής CSV στο KMyMoney", "Description[en_GB]": "Add CSV exporting to KMyMoney", "Description[es]": "Añade la exportación a CSV a KMyMoney", "Description[eu]": "Erantsi CSV-ren esportazioa KMyMoney-ri", "Description[fi]": "Lisää CSV-vienti KMyMoney-ohjelmaan", "Description[fr]": "Ajoute l'exportation « CVS » à KMyMoney", "Description[gl]": "Engade exportación a CSV ao KMyMoney", "Description[hu]": "CSV hozzáadása a KMyMoney-ba exportáláshoz", "Description[it]": "Aggiungi l'esportazione CSV a KMyMoney", "Description[kk]": "KMyMoney-ге CSV файлды экспорттап қосу", "Description[lt]": "Įdėti CSV eksportavimą į KMyMoney", "Description[nl]": "Voeg CSV exporteren toe aan KMyMoney", "Description[pl]": "Dodaje eksportowanie CSV do KMyMoney", "Description[pt]": "Adição da exportação de CSV ao KMyMoney", "Description[pt_BR]": "Adiciona a exportação de CSV ao KMyMoney", "Description[ro]": "Exportă fișier csv (câmpuri separate de virgulă) din KMyMoney", "Description[ru]": "Экспорт CSV из KMyMoney", "Description[sk]": "Pridať CSV exportovanie do KMyMoney", "Description[sv]": "Lägg till CSV-export till KMyMoney", "Description[tr]": "KMyMoney'e CSV aktarımı ekle", "Description[uk]": "Додає можливість експортування CSV з KMyMoney", "Description[x-test]": "xxAdd CSV exporting to KMyMoneyxx", "Description[zh_TW]": "匯入 CSV 檔到 KMyMoney", "EnabledByDefault": true, "Icon": "document-export", - "Id": "csvexport", + "Id": "csvexporter", "License": "GPL", "Name": "CSV Exporter", "Name[ar]": "تصدير CSV", "Name[bs]": "csvexport", "Name[ca@valencia]": "csvexport", "Name[ca]": "csvexport", "Name[cs]": "csvexport", "Name[da]": "csvexport", "Name[de]": "CSV-Export", "Name[el]": "Εξαγωγή csv", "Name[en_GB]": "csvexport", "Name[es]": "csvexport", "Name[eu]": "csvexport", "Name[fi]": "csvexport", "Name[fr]": "Exportation « csv »", "Name[gl]": "csvexport", "Name[hu]": "csvexport", "Name[it]": "csvexport", "Name[kk]": "csvexport", "Name[lt]": "csvexport", "Name[nl]": "csvexport", "Name[pl]": "csveksport", "Name[pt]": "csvexport", "Name[pt_BR]": "csvexport", "Name[ro]": "csvexport", "Name[ru]": "csvexport", "Name[sk]": "csvexport", "Name[sv]": "CSV-export", "Name[tr]": "csvexport", "Name[uk]": "csvexport", "Name[x-test]": "xxcsvexportxx", "Name[zh_TW]": "csvexport", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/csv/export/kmm_csvexport.rc b/kmymoney/plugins/csv/export/csvexporter.rc similarity index 100% rename from kmymoney/plugins/csv/export/kmm_csvexport.rc rename to kmymoney/plugins/csv/export/csvexporter.rc diff --git a/kmymoney/plugins/csv/export/csvwriter.cpp b/kmymoney/plugins/csv/export/csvwriter.cpp index 10c4d1846..aac23847d 100644 --- a/kmymoney/plugins/csv/export/csvwriter.cpp +++ b/kmymoney/plugins/csv/export/csvwriter.cpp @@ -1,459 +1,459 @@ /*************************************************************************** csvwriter.cpp - description -------------------- begin : Wed Mar 20 2013 copyright : (C) 2013-03-20 by Allan Anderson email : Allan Anderson agander93@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "csvwriter.h" // ---------------------------------------------------------------------------- // QT Headers #include #include #include #include // ---------------------------------------------------------------------------- // KDE Headers #include #include // ---------------------------------------------------------------------------- // Project Headers #include "mymoneymoney.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneytransaction.h" #include "mymoneytransactionfilter.h" #include "mymoneysplit.h" #include "mymoneypayee.h" #include "mymoneyexception.h" #include "csvexportdlg.h" -#include "csvexporterplugin.h" +#include "csvexporter.h" #include "mymoneyenums.h" CsvWriter::CsvWriter() : m_plugin(0), m_firstSplit(false), m_highestSplitCount(0), m_noError(true) { } CsvWriter::~CsvWriter() { } void CsvWriter::write(const QString& filename, const QString& accountId, const bool accountData, const bool categoryData, const QDate& startDate, const QDate& endDate, const QString& separator) { m_separator = separator; QFile csvFile(filename); if (csvFile.open(QIODevice::WriteOnly)) { QTextStream s(&csvFile); s.setCodec("UTF-8"); m_plugin->exporterDialog()->show(); try { if (categoryData) { writeCategoryEntries(s); } if (accountData) { writeAccountEntry(s, accountId, startDate, endDate); } emit signalProgress(-1, -1); } catch (const MyMoneyException &e) { QString errMsg = i18n("Unexpected exception '%1' thrown in %2, line %3 " "caught in MyMoneyCsvWriter::write()", e.what(), e.file(), e.line()); KMessageBox::error(0, errMsg); } csvFile.close(); qDebug() << i18n("Export completed.\n"); delete m_plugin->exporterDialog(); // Can now delete as export finished } else { KMessageBox::error(0, i18n("Unable to open file '%1' for writing", filename)); } } void CsvWriter::writeAccountEntry(QTextStream& stream, const QString& accountId, const QDate& startDate, const QDate& endDate) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyAccount account; QString data; account = file->account(accountId); MyMoneyTransactionFilter filter(accountId); QString type = account.accountTypeToString(account.accountType()); data = QString(i18n("Account Type:")); if (type == QLatin1String("Investment")) { data += QString("%1\n\n").arg(type); m_headerLine << QString(i18n("Date")) << QString(i18n("Security")) << QString(i18n("Action/Type")) << QString(i18n("Amount")) << QString(i18n("Quantity")) << QString(i18n("Price")) << QString(i18n("Interest")) << QString(i18n("Fees")) << QString(i18n("Account")) << QString(i18n("Memo")) << QString(i18n("Status")); data += m_headerLine.join(m_separator); extractInvestmentEntries(accountId, startDate, endDate); } else { data += QString("%1\n\n").arg(type); m_headerLine << QString(i18n("Date")) << QString(i18n("Payee")) << QString(i18n("Amount")) << QString(i18n("Account/Cat")) << QString(i18n("Memo")) << QString(i18n("Status")) << QString(i18n("Number")); filter.setDateFilter(startDate, endDate); QList trList = file->transactionList(filter); QList::ConstIterator it; signalProgress(0, trList.count()); int count = 0; m_highestSplitCount = 0; for (it = trList.constBegin(); it != trList.constEnd(); ++it) { writeTransactionEntry(*it, accountId, ++count); if (m_noError) signalProgress(count, 0); } data += m_headerLine.join(m_separator); } QString result; QMap::const_iterator it_map = m_map.constBegin(); while (it_map != m_map.constEnd()) { result += it_map.value(); ++it_map; } stream << data << result << QLatin1Char('\n'); } void CsvWriter::writeCategoryEntries(QTextStream &s) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyAccount income; MyMoneyAccount expense; income = file->income(); expense = file->expense(); QStringList list = income.accountList() + expense.accountList(); emit signalProgress(0, list.count()); QStringList::Iterator it_catList; int count = 0; for (it_catList = list.begin(); it_catList != list.end(); ++it_catList) { writeCategoryEntry(s, *it_catList, ""); emit signalProgress(++count, 0); } } void CsvWriter::writeCategoryEntry(QTextStream &s, const QString& accountId, const QString& leadIn) { MyMoneyAccount acc = MyMoneyFile::instance()->account(accountId); QString name = acc.name(); s << leadIn << name << m_separator; s << (acc.accountGroup() == eMyMoney::Account::Type::Expense ? QLatin1Char('E') : QLatin1Char('I')); s << endl; name += m_separator; foreach (const auto sAccount, acc.accountList()) writeCategoryEntry(s, sAccount, name); } void CsvWriter::writeTransactionEntry(const MyMoneyTransaction& t, const QString& accountId, const int count) { m_firstSplit = true; m_noError = true; MyMoneyFile* file = MyMoneyFile::instance(); MyMoneySplit split = t.splitByAccount(accountId); QList splits = t.splits(); if (splits.count() < 2) { KMessageBox::sorry(0, i18n("Transaction number '%1' is missing an account assignment.\n" "Date '%2', Payee '%3'.\nTransaction dropped.\n", count, t.postDate().toString(Qt::ISODate), file->payee(split.payeeId()).name()), i18n("Invalid transaction")); m_noError = false; return; } QString str; str += QLatin1Char('\n'); str += QString("%1" + m_separator).arg(t.postDate().toString(Qt::ISODate)); MyMoneyPayee payee = file->payee(split.payeeId()); str += QString("%1" + m_separator).arg(payee.name()); QString txt = split.value().formatMoney("", 2, false); str += QString("%1" + m_separator).arg(txt); if (splits.count() > 1) { MyMoneySplit sp = t.splitByAccount(accountId, false); QString tmp = QString("%1").arg(file->accountToCategory(sp.accountId())); str += tmp + m_separator; } QString memo = split.memo(); memo.replace('\n', '~').remove('\''); QString localeThousands = QLocale().groupSeparator(); // In case of clash with field separator if (m_separator == localeThousands) { memo.replace(localeThousands, QString()); } str += QString("%1" + m_separator).arg(memo); switch (split.reconcileFlag()) { case eMyMoney::Split::State::Cleared: str += QLatin1String("C") + m_separator; break; case eMyMoney::Split::State::Reconciled: case eMyMoney::Split::State::Frozen: str += QLatin1String("R") + m_separator; break; default: str += m_separator; break; } str += split.number(); if (splits.count() > 2) { QList::ConstIterator it; for (it = splits.constBegin(); it != splits.constEnd(); ++it) { if (!((*it) == split)) { writeSplitEntry(str, *it, splits.count() - 1); } } } QString date = t.postDate().toString(Qt::ISODate); m_map.insertMulti(date, str); } void CsvWriter::writeSplitEntry(QString &str, const MyMoneySplit& split, const int splitCount) { if (m_firstSplit) { m_firstSplit = false; str += m_separator; } MyMoneyFile* file = MyMoneyFile::instance(); QString splt = QString("%1").arg(file->accountToCategory(split.accountId())); str += splt + m_separator; if (splitCount > m_highestSplitCount) { m_highestSplitCount++; m_headerLine << QString(i18n("splitCategory")) << QString(i18n("splitMemo")) << QString(i18n("splitAmount")); m_headerLine.join(m_separator); } QString m = split.memo(); m.replace(QLatin1Char('\n'), QLatin1Char('~')); QString localeThousands = QLocale().groupSeparator(); // In case of clash with field separator if (m_separator == localeThousands) { m.replace(localeThousands, QString()); } str += QString("%1" + m_separator).arg(m); QString txt = QString("%1" + m_separator).arg(split.value().formatMoney("", 2, false)); str += txt; } void CsvWriter::extractInvestmentEntries(const QString& accountId, const QDate& startDate, const QDate& endDate) { MyMoneyFile* file = MyMoneyFile::instance(); foreach (const auto sAccount, file->account(accountId).accountList()) { MyMoneyTransactionFilter filter(sAccount); filter.setDateFilter(startDate, endDate); QList list = file->transactionList(filter); QList::ConstIterator itList; signalProgress(0, list.count()); int count = 0; for (itList = list.constBegin(); itList != list.constEnd(); ++itList) { writeInvestmentEntry(*itList, ++count); signalProgress(count, 0); } } } void CsvWriter::writeInvestmentEntry(const MyMoneyTransaction& t, const int count) { QString strQuantity; QString strAmount; QString strPrice; QString strAccName; QString strCheckingAccountName; QString strMemo; QString strAction; QString strStatus; QString strInterest; QString strFees; MyMoneyFile* file = MyMoneyFile::instance(); QString chkAccnt; QList lst = t.splits(); QList::Iterator itSplit; eMyMoney::Account::Type typ; QString chkAccntId; MyMoneyMoney qty; MyMoneyMoney value; QMap map; for (int i = 0; i < lst.count(); i++) { MyMoneyAccount acc = file->account(lst[i].accountId()); QString accName = acc.name(); typ = acc.accountType(); map.insert(typ, lst[i].accountId()); if (typ == eMyMoney::Account::Type::Stock) { switch (lst[i].reconcileFlag()) { case eMyMoney::Split::State::Cleared: strStatus = QLatin1Char('C'); break; case eMyMoney::Split::State::Reconciled: case eMyMoney::Split::State::Frozen: strStatus = QLatin1Char('R'); break; default: strStatus.clear(); break; } strStatus += m_separator; } } // // Add date. // QString str = QString("\n%1" + m_separator).arg(t.postDate().toString(Qt::ISODate)); QString localeThousands = QLocale().groupSeparator(); // In case of clash with field separator for (itSplit = lst.begin(); itSplit != lst.end(); ++itSplit) { MyMoneyAccount acc = file->account((*itSplit).accountId()); // // eMyMoney::Account::Type::Checkings. // if ((acc.accountType() == eMyMoney::Account::Type::Checkings) || (acc.accountType() == eMyMoney::Account::Type::Cash) || (acc.accountType() == eMyMoney::Account::Type::Savings)) { chkAccntId = (*itSplit).accountId(); chkAccnt = file->account(chkAccntId).name(); strCheckingAccountName = file->accountToCategory(chkAccntId) + m_separator; strAmount = (*itSplit).value().formatMoney("", 2).remove(localeThousands) + m_separator; } else if (acc.accountType() == eMyMoney::Account::Type::Income) { // // eMyMoney::Account::Type::Income. // qty = (*itSplit).shares(); value = (*itSplit).value(); strInterest = value.formatMoney("", 2, false) + m_separator; } else if (acc.accountType() == eMyMoney::Account::Type::Expense) { // // eMyMoney::Account::Type::Expense. // qty = (*itSplit).shares(); value = (*itSplit).value(); strFees = value.formatMoney("", 2, false) + m_separator; } else if (acc.accountType() == eMyMoney::Account::Type::Stock) { // // eMyMoney::Account::Type::Stock. // strMemo = QString("%1" + m_separator).arg((*itSplit).memo()); strMemo.replace(QLatin1Char('\n'), QLatin1Char('~')).remove('\''); // // Actions. // if ((*itSplit).action() == QLatin1String("Dividend")) { strAction = QLatin1String("DivX"); } else if ((*itSplit).action() == QLatin1String("IntIncome")) { strAction = QLatin1String("IntIncX"); } if ((strAction == QLatin1String("DivX")) || (strAction == QLatin1String("IntIncX"))) { if ((map.value(eMyMoney::Account::Type::Checkings).isEmpty()) && (map.value(eMyMoney::Account::Type::Cash).isEmpty())) { KMessageBox::sorry(0, i18n("Transaction number '%1' is missing an account assignment.\n" "Date '%2', Amount '%3'.\nTransaction dropped.\n", count, t.postDate().toString(Qt::ISODate), strAmount), i18n("Invalid transaction")); return; } } else if ((*itSplit).action() == QLatin1String("Buy")) { qty = (*itSplit).shares(); if (qty.isNegative()) { strAction = QLatin1String("Sell"); } else { strAction = QLatin1String("Buy"); } } else if ((*itSplit).action() == QLatin1String("Add")) { qty = (*itSplit).shares(); if (qty.isNegative()) { strAction = QLatin1String("Shrsout"); } else { strAction = QLatin1String("Shrsin"); } } else if ((*itSplit).action() == QLatin1String("Reinvest")) { qty = (*itSplit).shares(); strAmount = (*itSplit).value().formatMoney("", 2).remove(localeThousands) + m_separator; strAction = QLatin1String("ReinvDiv"); } else { strAction = (*itSplit).action(); } // // Add action. // if ((strAction == QLatin1String("Buy")) || (strAction == QLatin1String("Sell")) || (strAction == QLatin1String("ReinvDiv"))) { // // Add total. // if (strAction == QLatin1String("Sell")) { value = -value; } // // Add price. // strPrice = (*itSplit).price().formatMoney("", 6, false); if (!qty.isZero()) { // // Add quantity. // if (strAction == QLatin1String("Sell")) { qty = -qty; } strQuantity = qty.formatMoney("", 2, false); } } else if ((strAction == QLatin1String("Shrsin")) || (strAction == QLatin1String("Shrsout"))) { // // Add quantity for "Shrsin" || "Shrsout". // if (strAction == QLatin1String("Shrsout")) { qty = -qty; } strQuantity = qty.formatMoney("", 2, false); } strAccName = acc.name(); strAccName += m_separator; strAction += m_separator; strQuantity += m_separator; strPrice += m_separator; } if (strCheckingAccountName.isEmpty()) { strCheckingAccountName = m_separator; } if (strInterest.isEmpty()) { strInterest = m_separator; } if (strFees.isEmpty()) { strFees = m_separator; } } // end of itSplit loop str += strAccName + strAction + strAmount + strQuantity + strPrice + strInterest + strFees + strCheckingAccountName + strMemo + strStatus; QString date = t.postDate().toString(Qt::ISODate); m_map.insertMulti(date, str); } diff --git a/kmymoney/plugins/csv/export/csvwriter.h b/kmymoney/plugins/csv/export/csvwriter.h index 0969475af..89e045e80 100644 --- a/kmymoney/plugins/csv/export/csvwriter.h +++ b/kmymoney/plugins/csv/export/csvwriter.h @@ -1,148 +1,148 @@ /*************************************************************************** csvwriter.h - description ------------------ begin : Wed Mar 20 2013 copyright : (C) 2013-03-20 by Allan Anderson email : Allan Anderson agander93@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef CSVWRITER_H #define CSVWRITER_H // ---------------------------------------------------------------------------- // QT Headers #include #include #include // ---------------------------------------------------------------------------- // KDE Headers // ---------------------------------------------------------------------------- // Project Headers class QTextStream; class MyMoneyTransaction; class MyMoneySplit; -class CsvExporterPlugin; +class CSVExporter; /** * @author Thomas Baumgart * @author Allan Anderson */ /** * This class represents the CSV writer. All conversion between the * internal representation of accounts, transactions is handled in this * object. */ class CsvWriter : public QObject { Q_OBJECT public: CsvWriter(); ~CsvWriter(); - CsvExporterPlugin* m_plugin; + CSVExporter* m_plugin; /** * This method is used to start the conversion. The parameters control * the destination of the data and the parts that will be exported. * Individual errors will be reported using message boxes. * * @param filename The name of the output file with full path information * @param accountId The id of the account that will be exported * @param accountData If true, the transactions will be exported * @param categoryData If true, the categories will be exported as well * @param startDate Transactions before this date will not be exported * @param endDate Transactions after this date will not be exported * @param fieldSeparator Current field separator */ void write(const QString& filename, const QString& accountId, const bool accountData, const bool categoryData, const QDate& startDate, const QDate& endDate, const QString& separator); private: bool m_firstSplit; QMap m_map; /** * This method writes the entries necessary for an account. First * the leadin, and then the transactions that are in the account * specified by @p accountId in the range from @p startDate to @p * endDate. * * @param s reference to textstream * @param accountId id of the account to be written * @param startDate date from which entries are written * @param endDate date until which entries are written */ void writeAccountEntry(QTextStream &s, const QString &accountId, const QDate &startDate, const QDate &endDate); /** * This method writes the category entries to the stream * @p s. It writes the leadin and uses writeCategoryEntries() * to write the entries and emits signalProgess() where needed. * * @param s reference to textstream */ void writeCategoryEntries(QTextStream &s); /** * This method writes the category entry for account with * the ID @p accountId to the stream @p s. All subaccounts * are processed as well. * * @param s reference to textstream * @param accountId id of the account to be written * @param leadIn constant text that will be prepended to the account's name */ void writeCategoryEntry(QTextStream &s, const QString &accountId, const QString &leadIn); void writeTransactionEntry(const MyMoneyTransaction& t, const QString& accountId, const int count); void writeSplitEntry(QString& str, const MyMoneySplit& split, const int splitCount); void extractInvestmentEntries(const QString& accountId, const QDate& startDate, const QDate& endDate); void writeInvestmentEntry(const MyMoneyTransaction& t, const int count); Q_SIGNALS: /** * This signal is emitted while the operation progresses. * When the operation starts, the signal is emitted with * @p current being 0 and @p max having the maximum value. * * During the operation, the signal is emitted with @p current * containing the current value on the way to the maximum value. * @p max will be 0 in this case. * * When the operation is finished, the signal is emitted with * @p current and @p max set to -1 to identify the end of the * operation. * * @param current see above * @param max see above */ void signalProgress(int current, int max); private: QStringList m_headerLine; QString m_separator; int m_highestSplitCount; bool m_noError; }; #endif diff --git a/kmymoney/plugins/csv/import/CMakeLists.txt b/kmymoney/plugins/csv/import/CMakeLists.txt index b05699562..1c3568139 100644 --- a/kmymoney/plugins/csv/import/CMakeLists.txt +++ b/kmymoney/plugins/csv/import/CMakeLists.txt @@ -1,80 +1,80 @@ add_subdirectory(core) if(NOT ENABLE_CSVIMPORTER) return() endif() # patch the version with the version defined in the build system -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/csvimport.json.in ${CMAKE_CURRENT_BINARY_DIR}/csvimport.json @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/csvimporter.json.in ${CMAKE_CURRENT_BINARY_DIR}/csvimporter.json @ONLY) ########### next target ############### -set(kmm_csvimport_PART_SRCS - csvimporterplugin.cpp +set(csvimporter_PART_SRCS + csvimporter.cpp csvwizard.cpp bankingwizardpage.cpp investmentwizardpage.cpp priceswizardpage.cpp transactiondlg.cpp securitiesdlg.cpp securitydlg.cpp currenciesdlg.cpp ) -kconfig_add_kcfg_files(kmm_csvimport_PART_SRCS pluginsettings.kcfgc) +kconfig_add_kcfg_files(csvimporter_PART_SRCS pluginsettings.kcfgc) -ki18n_wrap_ui(kmm_csvimport_PART_SRCS +ki18n_wrap_ui(csvimporter_PART_SRCS csvwizard.ui transactiondlg.ui introwizardpage.ui separatorwizardpage.ui rowswizardpage.ui bankingwizardpage.ui investmentwizardpage.ui priceswizardpage.ui formatswizardpage.ui securitiesdlg.ui securitydlg.ui currenciesdlg.ui ) # Use static library for tests and prices importer only -add_library(kmm_csvimport MODULE ${kmm_csvimport_PART_SRCS}) +add_library(csvimporter MODULE ${csvimporter_PART_SRCS}) -target_link_libraries(kmm_csvimport +target_link_libraries(csvimporter PUBLIC - kmm_csvimport_core + kmm_csvimportercore kmm_plugin ) ########### install files ############### -install(FILES kmm_csvimport.rc - DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_csvimport") +install(FILES csvimporter.rc + DESTINATION "${KXMLGUI_INSTALL_DIR}/csvimporter") -install(TARGETS kmm_csvimport +install(TARGETS csvimporter DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") # the KCM module -set(kcm_kmm_csvimport_PART_SRCS - kcm_csvimport.cpp +set(kcm_csvimporter_PART_SRCS + kcm_csvimporter.cpp ) -kconfig_add_kcfg_files(kcm_kmm_csvimport_PART_SRCS pluginsettings.kcfgc) +kconfig_add_kcfg_files(kcm_csvimporter_PART_SRCS pluginsettings.kcfgc) -ki18n_wrap_ui(kcm_kmm_csvimport_PART_SRCS pluginsettingsdecl.ui) +ki18n_wrap_ui(kcm_csvimporter_PART_SRCS pluginsettingsdecl.ui) -add_library(kcm_kmm_csvimport MODULE ${kcm_kmm_csvimport_PART_SRCS}) +add_library(kcm_csvimporter MODULE ${kcm_csvimporter_PART_SRCS}) -kcoreaddons_desktop_to_json(kcm_kmm_csvimport kcm_kmm_csvimport.desktop) +kcoreaddons_desktop_to_json(kcm_csvimporter kcm_csvimporter.desktop) -target_link_libraries(kcm_kmm_csvimport +target_link_libraries(kcm_csvimporter KF5::I18n KF5::ConfigWidgets ) -install(TARGETS kcm_kmm_csvimport +install(TARGETS kcm_csvimporter DESTINATION ${KDE_INSTALL_PLUGINDIR}) -install(FILES kcm_kmm_csvimport.desktop +install(FILES kcm_csvimporter.desktop DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/kmymoney/plugins/csv/import/bankingwizardpage.cpp b/kmymoney/plugins/csv/import/bankingwizardpage.cpp index aa2d0dd62..b03f02878 100644 --- a/kmymoney/plugins/csv/import/bankingwizardpage.cpp +++ b/kmymoney/plugins/csv/import/bankingwizardpage.cpp @@ -1,396 +1,396 @@ /******************************************************************************* * bankingwizardpage.cpp * -------------------- * begin : Thur Jan 01 2015 * copyright : (C) 2015 by Allan Anderson * email : agander93@gmail.com * copyright : (C) 2016 by Łukasz Wojniłowicz * email : lukasz.wojnilowicz@gmail.com ********************************************************************************/ /******************************************************************************* * * * 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 "bankingwizardpage.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "csvwizard.h" -#include "csvimporter.h" +#include "core/csvimportercore.h" #include "ui_bankingwizardpage.h" // ---------------------------------------------------------------------------- -BankingPage::BankingPage(CSVWizard *dlg, CSVImporter *imp) : +BankingPage::BankingPage(CSVWizard *dlg, CSVImporterCore *imp) : CSVWizardPage(dlg, imp), ui(new Ui::BankingPage) { ui->setupUi(this); connect(ui->m_clear, &QAbstractButton::clicked, this, &BankingPage::clearColumns); connect(ui->m_radioAmount, &QAbstractButton::toggled, this, &BankingPage::amountToggled); connect(ui->m_radioDebitCredit, &QAbstractButton::toggled, this, &BankingPage::debitCreditToggled); connect(ui->m_oppositeSigns, &QAbstractButton::clicked, this, &BankingPage::oppositeSignsClicked); // initialize column names m_dlg->m_colTypeName.insert(Column::Payee,i18n("Payee")); m_dlg->m_colTypeName.insert(Column::Number,i18n("Number")); m_dlg->m_colTypeName.insert(Column::Debit,i18n("Debit")); m_dlg->m_colTypeName.insert(Column::Credit,i18n("Credit")); m_dlg->m_colTypeName.insert(Column::Date,i18n("Date")); m_dlg->m_colTypeName.insert(Column::Amount,i18n("Amount")); m_dlg->m_colTypeName.insert(Column::Category,i18n("Category")); m_dlg->m_colTypeName.insert(Column::Memo,i18n("Memo")); m_profile = dynamic_cast(m_imp->m_profile); connect(ui->m_amountCol, SIGNAL(currentIndexChanged(int)), this, SLOT(amountColSelected(int))); connect(ui->m_debitCol, SIGNAL(currentIndexChanged(int)), this, SLOT(debitColSelected(int))); connect(ui->m_creditCol, SIGNAL(currentIndexChanged(int)), this, SLOT(creditColSelected(int))); connect(ui->m_memoCol, SIGNAL(currentIndexChanged(int)), this, SLOT(memoColSelected(int))); connect(ui->m_numberCol, SIGNAL(currentIndexChanged(int)), this, SLOT(numberColSelected(int))); connect(ui->m_dateCol, SIGNAL(currentIndexChanged(int)), this, SLOT(dateColSelected(int))); connect(ui->m_payeeCol, SIGNAL(currentIndexChanged(int)), this, SLOT(payeeColSelected(int))); connect(ui->m_categoryCol, SIGNAL(currentIndexChanged(int)), this, SLOT(categoryColSelected(int))); } BankingPage::~BankingPage() { delete ui; } void BankingPage::initializePage() { QHash columns {{Column::Amount, ui->m_amountCol}, {Column::Debit, ui->m_debitCol}, {Column::Credit, ui->m_creditCol}, {Column::Memo, ui->m_memoCol}, {Column::Number, ui->m_numberCol}, {Column::Date, ui->m_dateCol}, {Column::Payee, ui->m_payeeCol}, {Column::Category, ui->m_categoryCol}}; if (ui->m_dateCol->count() != m_imp->m_file->m_columnCount) m_dlg->initializeComboBoxes(columns); columns.remove(Column::Memo); for (auto it = columns.cbegin(); it != columns.cend(); ++it) it.value()->setCurrentIndex(m_profile->m_colTypeNum.value(it.key())); ui->m_oppositeSigns->setChecked(m_profile->m_oppositeSigns); if (m_profile->m_memoColList.count() > 0) { for (int i = 0; i < m_profile->m_memoColList.count(); ++i) ui->m_memoCol->setCurrentIndex(m_profile->m_memoColList.value(i)); } else ui->m_memoCol->setCurrentIndex(-1); if (this->m_profile->m_colTypeNum.value(Column::Debit) == -1) // If amount previously selected, set check radio_amount ui->m_radioAmount->setChecked(true); else // ...else set check radio_debCred to clear amount col ui->m_radioDebitCredit->setChecked(true); } int BankingPage::nextId() const { return CSVWizard::PageFormats; } bool BankingPage::isComplete() const { return ui->m_dateCol->currentIndex() > -1 && ui->m_payeeCol->currentIndex() > -1 && (ui->m_amountCol->currentIndex() > -1 || (ui->m_debitCol->currentIndex() > -1 && ui->m_creditCol->currentIndex() > -1)); } bool BankingPage::validateMemoComboBox() { if (m_profile->m_memoColList.count() == 0) return true; for (int i = 0; i < ui->m_memoCol->count(); ++i) { QString txt = ui->m_memoCol->itemText(i); if (txt.contains(QLatin1Char('*'))) // check if text containing '*' belongs to valid column types if (m_profile->m_colNumType.value(i) != Column::Payee) { ui->m_memoCol->setItemText(i, QString::number(i + 1)); m_profile->m_memoColList.removeOne(i); return false; } } return true; } void BankingPage::memoColSelected(int col) { if (m_profile->m_colNumType.value(col) == Column::Payee ) { int rc = KMessageBox::Yes; if (isVisible()) rc = KMessageBox::questionYesNo(m_dlg, i18n("
The '%1' field already has this column selected.
" "
If you wish to copy the Payee data to the memo field, click 'Yes'.
", m_dlg->m_colTypeName.value(m_profile->m_colNumType.value(col)))); if (rc == KMessageBox::Yes) { ui->m_memoCol->setItemText(col, QString::number(col + 1) + QLatin1Char('*')); if (!m_profile->m_memoColList.contains(col)) m_profile->m_memoColList.append(col); } else { ui->m_memoCol->setItemText(col, QString::number(col + 1)); m_profile->m_memoColList.removeOne(col); } //allow only separate memo field occupy combobox ui->m_memoCol->blockSignals(true); if (m_profile->m_colTypeNum.value(Column::Memo) != -1) ui->m_memoCol->setCurrentIndex(m_profile->m_colTypeNum.value(Column::Memo)); else ui->m_memoCol->setCurrentIndex(-1); ui->m_memoCol->blockSignals(false); return; } if (m_profile->m_colTypeNum.value(Column::Memo) != -1) // check if this memo has any column 'number' assigned... m_profile->m_memoColList.removeOne(col); // ...if true remove it from memo list if(validateSelectedColumn(col, Column::Memo)) if (col != - 1 && !m_profile->m_memoColList.contains(col)) m_profile->m_memoColList.append(col); } void BankingPage::categoryColSelected(int col) { validateSelectedColumn(col, Column::Category); } void BankingPage::numberColSelected(int col) { validateSelectedColumn(col, Column::Number); } void BankingPage::payeeColSelected(int col) { if (validateSelectedColumn(col, Column::Payee)) if (!validateMemoComboBox()) // user could have it already in memo so... memoColSelected(col); // ...if true set memo field again } void BankingPage::dateColSelected(int col) { validateSelectedColumn(col, Column::Date); } void BankingPage::debitColSelected(int col) { validateSelectedColumn(col, Column::Debit); } void BankingPage::creditColSelected(int col) { validateSelectedColumn(col, Column::Credit); } void BankingPage::amountColSelected(int col) { validateSelectedColumn(col, Column::Amount); } void BankingPage::amountToggled(bool checked) { if (checked) { ui->m_amountCol->setEnabled(true); // disable credit & debit ui choices ui->labelBnk_amount->setEnabled(true); ui->labelBnk_credits->setEnabled(false); ui->labelBnk_debits->setEnabled(false); ui->m_debitCol->setEnabled(false); ui->m_debitCol->setCurrentIndex(-1); ui->m_creditCol->setEnabled(false); ui->m_creditCol->setCurrentIndex(-1); } } void BankingPage::debitCreditToggled(bool checked) { if (checked) { ui->m_debitCol->setEnabled(true); // if 'debit/credit' selected ui->labelBnk_debits->setEnabled(true); ui->m_creditCol->setEnabled(true); ui->labelBnk_credits->setEnabled(true); ui->m_amountCol->setEnabled(false); // disable 'amount' ui choices ui->m_amountCol->setCurrentIndex(-1); // as credit/debit chosen ui->labelBnk_amount->setEnabled(false); } } void BankingPage::oppositeSignsClicked(bool checked) { m_profile->m_oppositeSigns = checked; } void BankingPage::clearColumns() { ui->m_dateCol->setCurrentIndex(-1); ui->m_payeeCol->setCurrentIndex(-1); ui->m_memoCol->setCurrentIndex(-1); ui->m_numberCol->setCurrentIndex(-1); ui->m_amountCol->setCurrentIndex(-1); ui->m_debitCol->setCurrentIndex(-1); ui->m_creditCol->setCurrentIndex(-1); ui->m_categoryCol->setCurrentIndex(-1); } void BankingPage::resetComboBox(const Column comboBox) { switch (comboBox) { case Column::Amount: ui->m_amountCol->setCurrentIndex(-1); break; case Column::Credit: ui->m_creditCol->setCurrentIndex(-1); break; case Column::Date: ui->m_dateCol->setCurrentIndex(-1); break; case Column::Debit: ui->m_debitCol->setCurrentIndex(-1); break; case Column::Memo: ui->m_memoCol->setCurrentIndex(-1); break; case Column::Number: ui->m_numberCol->setCurrentIndex(-1); break; case Column::Payee: ui->m_payeeCol->setCurrentIndex(-1); break; case Column::Category: ui->m_categoryCol->setCurrentIndex(-1); break; default: KMessageBox::sorry(m_dlg, i18n("
Field name not recognised.
'%1'
Please re-enter your column selections." , (int)comboBox), i18n("CSV import")); } } bool BankingPage::validateSelectedColumn(const int col, const Column type) { if (m_profile->m_colTypeNum.value(type) != -1) // check if this 'type' has any column 'number' assigned... m_profile->m_colNumType.remove(m_profile->m_colTypeNum[type]); // ...if true remove 'type' assigned to this column 'number' bool ret = true; if (col == -1) { // user only wanted to reset his column so allow him m_profile->m_colTypeNum[type] = col; // assign new column 'number' to this 'type' } else if (m_profile->m_colNumType.contains(col)) { // if this column 'number' has already 'type' assigned KMessageBox::information(m_dlg, i18n("The '%1' field already has this column selected.
Please reselect both entries as necessary.
", m_dlg->m_colTypeName.value(m_profile->m_colNumType.value(col)))); resetComboBox(m_profile->m_colNumType.value(col)); resetComboBox(type); ret = false; } else { m_profile->m_colTypeNum[type] = col; // assign new column 'number' to this 'type' m_profile->m_colNumType[col] = type; // assign new 'type' to this column 'number' } emit completeChanged(); return ret; } bool BankingPage::validateCreditDebit() { for (int row = m_profile->m_startLine; row <= m_profile->m_endLine; ++row) { // process credit/debit field if (m_profile->m_colTypeNum.value(Column::Credit) != -1 && m_profile->m_colTypeNum.value(Column::Debit) != -1) { QString credit = m_imp->m_file->m_model->item(row, m_profile->m_colTypeNum.value(Column::Credit))->text(); QString debit = m_imp->m_file->m_model->item(row, m_profile->m_colTypeNum.value(Column::Debit))->text(); m_imp->processCreditDebit(credit, debit); if (!credit.isEmpty() && !debit.isEmpty()) { int ret = KMessageBox::questionYesNoCancel(m_dlg, i18n("
The %1 field contains '%2'
" "
and the %3 field contains '%4'.
" "
Please choose which you wish to accept.
", m_dlg->m_colTypeName.value(Column::Debit), debit, m_dlg->m_colTypeName.value(Column::Credit), credit), i18n("CSV invalid field values"), KGuiItem(i18n("Accept %1", m_dlg->m_colTypeName.value(Column::Debit))), KGuiItem(i18n("Accept %1", m_dlg->m_colTypeName.value(Column::Credit))), KGuiItem(i18n("Cancel"))); switch(ret) { case KMessageBox::Cancel: return false; case KMessageBox::Yes: m_imp->m_file->m_model->item(row, m_profile->m_colTypeNum.value(Column::Credit))->setText(QString()); break; case KMessageBox::No: m_imp->m_file->m_model->item(row, m_profile->m_colTypeNum.value(Column::Debit))->setText(QString()); break; } } } } return true; } void BankingPage::makeQIF(const MyMoneyStatement& st, const QString& outFileName) { QFile oFile(outFileName); oFile.open(QIODevice::WriteOnly); QTextStream out(&oFile); QString buffer; QString strEType; switch (st.m_eType) { case eMyMoney::Statement::Type::CreditCard: strEType = QStringLiteral("CCard"); case eMyMoney::Statement::Type::Savings: case eMyMoney::Statement::Type::Checkings: default: strEType = QStringLiteral("Bank"); } if (!st.m_strAccountName.isEmpty()) { buffer.append(QStringLiteral("!Account\n")); buffer.append(QChar(QLatin1Char('N')) + st.m_strAccountName + QChar(QLatin1Char('\n'))); buffer.append(QChar(QLatin1Char('T')) + strEType + QChar(QLatin1Char('\n'))); buffer.append(QStringLiteral("^\n")); } buffer.append(QStringLiteral("!Type:") + strEType + QChar(QLatin1Char('\n'))); for (QList::const_iterator it = st.m_listTransactions.constBegin(); it != st.m_listTransactions.constEnd(); ++it) { buffer.append(QChar(QLatin1Char('D')) + it->m_datePosted.toString(QStringLiteral("MM/dd/yyyy")) + QChar(QLatin1Char('\n'))); QString txt; txt.setNum(it->m_amount.toDouble(), 'f', 4); buffer.append(QChar(QLatin1Char('T')) + txt + QChar(QLatin1Char('\n'))); buffer.append(QChar(QLatin1Char('P')) + it->m_strPayee + QChar(QLatin1Char('\n'))); if (!it->m_listSplits.isEmpty()) buffer.append(QChar(QLatin1Char('L')) + it->m_listSplits.first().m_strCategoryName + QChar(QLatin1Char('\n'))); if (!it->m_strNumber.isEmpty()) buffer.append(QChar(QLatin1Char('N')) + it->m_strNumber + QChar(QLatin1Char('\n'))); if (!it->m_strMemo.isEmpty()) buffer.append(QChar(QLatin1Char('M')) + it->m_strMemo + QChar(QLatin1Char('\n'))); buffer.append(QStringLiteral("^\n")); out << buffer;// output qif file buffer.clear(); } oFile.close(); } diff --git a/kmymoney/plugins/csv/import/bankingwizardpage.h b/kmymoney/plugins/csv/import/bankingwizardpage.h index 09455928a..f68eff54c 100644 --- a/kmymoney/plugins/csv/import/bankingwizardpage.h +++ b/kmymoney/plugins/csv/import/bankingwizardpage.h @@ -1,85 +1,85 @@ /******************************************************************************* * bankingwizardpage.h * ------------------ * begin : Thur Jan 01 2015 * copyright : (C) 2015 by Allan Anderson * email : agander93@gmail.com * copyright : (C) 2016 by Łukasz Wojniłowicz * email : lukasz.wojnilowicz@gmail.com ********************************************************************************/ /******************************************************************************* * * * 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. * * * ********************************************************************************/ #ifndef BANKINGWIZARDPAGE_H #define BANKINGWIZARDPAGE_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "csvwizardpage.h" // ---------------------------------------------------------------------------- class BankingProfile; class MyMoneyStatement; namespace Ui { class BankingPage; } class BankingPage : public CSVWizardPage { Q_OBJECT public: - explicit BankingPage(CSVWizard *dlg, CSVImporter *imp); + explicit BankingPage(CSVWizard *dlg, CSVImporterCore *imp); ~BankingPage(); bool validateCreditDebit(); /** * This method fills QIF file with bank/credit card data */ void makeQIF(const MyMoneyStatement &st, const QString &outFileName); private: void initializePage(); bool isComplete() const; int nextId() const; bool validateMemoComboBox(); void resetComboBox(const Column comboBox); bool validateSelectedColumn(const int col, const Column type); BankingProfile *m_profile; Ui::BankingPage *ui; private Q_SLOTS: void memoColSelected(int col); void categoryColSelected(int col); void numberColSelected(int col); void payeeColSelected(int col); void dateColSelected(int col); void debitColSelected(int col); void creditColSelected(int col); void amountColSelected(int col); void amountToggled(bool checked); void debitCreditToggled(bool checked); void oppositeSignsClicked(bool checked); void clearColumns(); }; #endif // BANKINGWIZARDPAGE_H diff --git a/kmymoney/plugins/csv/import/core/CMakeLists.txt b/kmymoney/plugins/csv/import/core/CMakeLists.txt index 23c4fb9b3..866a80ad5 100644 --- a/kmymoney/plugins/csv/import/core/CMakeLists.txt +++ b/kmymoney/plugins/csv/import/core/CMakeLists.txt @@ -1,23 +1,23 @@ if(BUILD_TESTING) add_subdirectory(tests) endif() ########### next target ############### -set(kmm_csvimport_core_PART_SRCS - csvimporter.cpp +set(csvimportercore_PART_SRCS + csvimportercore.cpp convdate.cpp csvutil.cpp ) -add_library(kmm_csvimport_core SHARED ${kmm_csvimport_core_PART_SRCS}) -generate_export_header(kmm_csvimport_core) -target_link_libraries(kmm_csvimport_core +add_library(kmm_csvimportercore SHARED ${csvimportercore_PART_SRCS}) +generate_export_header(kmm_csvimportercore) +target_link_libraries(kmm_csvimportercore PUBLIC kmm_mymoney ) ########### install files ############### -install(TARGETS kmm_csvimport_core +install(TARGETS kmm_csvimportercore ${INSTALL_TARGETS_DEFAULT_ARGS} ) diff --git a/kmymoney/plugins/csv/import/core/convdate.h b/kmymoney/plugins/csv/import/core/convdate.h index 958c2d4e4..c01077c33 100644 --- a/kmymoney/plugins/csv/import/core/convdate.h +++ b/kmymoney/plugins/csv/import/core/convdate.h @@ -1,53 +1,53 @@ /*************************************************************************** convdate.h ------------------- begin : Sat Jan 01 2010 copyright : (C) 2010 by Allan Anderson email : agander93@gmail.com copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef CONVDATE_H #define CONVDATE_H #include #include "csvenums.h" -#include "csv/import/core/kmm_csvimport_core_export.h" +#include "csv/import/core/kmm_csvimportercore_export.h" -class KMM_CSVIMPORT_CORE_EXPORT ConvertDate +class KMM_CSVIMPORTERCORE_EXPORT ConvertDate { public: ConvertDate(); ~ConvertDate(); /** * This method is used to convert a QString date into QDate() format. * If the date is invalid, QDate() is returned. */ QDate convertDate(const QString& txt); /** * This method converts the selected date setting into * a QString date format string. */ QString stringFormat(); void setDateFormatIndex(const DateFormat _d); private: DateFormat m_dateFormatIndex; } ; #endif diff --git a/kmymoney/plugins/csv/import/core/csvimporter.cpp b/kmymoney/plugins/csv/import/core/csvimportercore.cpp similarity index 83% rename from kmymoney/plugins/csv/import/core/csvimporter.cpp rename to kmymoney/plugins/csv/import/core/csvimportercore.cpp index 8026d7dde..5542418c6 100644 --- a/kmymoney/plugins/csv/import/core/csvimporter.cpp +++ b/kmymoney/plugins/csv/import/core/csvimportercore.cpp @@ -1,1762 +1,1762 @@ /*************************************************************************** - csvimporter.cpp + csvimportercore.cpp ------------------- begin : Sun May 21 2017 copyright : (C) 2010 by Allan Anderson email : agander93@gmail.com copyright : (C) 2016-2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "csvimporter.h" +#include "csvimportercore.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneysecurity.h" #include "mymoneytransaction.h" #include "csvutil.h" #include "convdate.h" #include "mymoneyenums.h" -const QHash CSVImporter::m_profileConfPrefix { +const QHash CSVImporterCore::m_profileConfPrefix { {Profile::Banking, QStringLiteral("Bank")}, {Profile::Investment, QStringLiteral("Invest")}, {Profile::CurrencyPrices, QStringLiteral("CPrices")}, {Profile::StockPrices, QStringLiteral("SPrices")} }; -const QHash CSVImporter::m_colTypeConfName { +const QHash CSVImporterCore::m_colTypeConfName { {Column::Date, QStringLiteral("DateCol")}, {Column::Memo, QStringLiteral("MemoCol")}, {Column::Number, QStringLiteral("NumberCol")}, {Column::Payee, QStringLiteral("PayeeCol")}, {Column::Amount, QStringLiteral("AmountCol")}, {Column::Credit, QStringLiteral("CreditCol")}, {Column::Debit, QStringLiteral("DebitCol")}, {Column::Category, QStringLiteral("CategoryCol")}, {Column::Type, QStringLiteral("TypeCol")}, {Column::Price, QStringLiteral("PriceCol")}, {Column::Quantity, QStringLiteral("QuantityCol")}, {Column::Fee, QStringLiteral("FeeCol")}, {Column::Symbol, QStringLiteral("SymbolCol")}, {Column::Name, QStringLiteral("NameCol")}, }; -const QHash CSVImporter::m_miscSettingsConfName { +const QHash CSVImporterCore::m_miscSettingsConfName { {ConfDirectory, QStringLiteral("Directory")}, {ConfEncoding, QStringLiteral("Encoding")}, {ConfDateFormat, QStringLiteral("DateFormat")}, {ConfFieldDelimiter, QStringLiteral("FieldDelimiter")}, {ConfTextDelimiter, QStringLiteral("TextDelimiter")}, {ConfDecimalSymbol, QStringLiteral("DecimalSymbol")}, {ConfStartLine, QStringLiteral("StartLine")}, {ConfTrailerLines, QStringLiteral("TrailerLines")}, {ConfOppositeSigns, QStringLiteral("OppositeSigns")}, {ConfFeeIsPercentage, QStringLiteral("FeeIsPercentage")}, {ConfFeeRate, QStringLiteral("FeeRate")}, {ConfMinFee, QStringLiteral("MinFee")}, {ConfSecurityName, QStringLiteral("SecurityName")}, {ConfSecuritySymbol, QStringLiteral("SecuritySymbol")}, {ConfCurrencySymbol, QStringLiteral("CurrencySymbol")}, {ConfPriceFraction, QStringLiteral("PriceFraction")}, {ConfDontAsk, QStringLiteral("DontAsk")}, {ConfHeight, QStringLiteral("Height")}, {ConfWidth, QStringLiteral("Width")} }; -const QHash CSVImporter::m_transactionConfName { +const QHash CSVImporterCore::m_transactionConfName { {eMyMoney::Transaction::Action::Buy, QStringLiteral("BuyParam")}, {eMyMoney::Transaction::Action::Sell, QStringLiteral("SellParam")}, {eMyMoney::Transaction::Action::ReinvestDividend, QStringLiteral("ReinvdivParam")}, {eMyMoney::Transaction::Action::CashDividend, QStringLiteral("DivXParam")}, {eMyMoney::Transaction::Action::Interest, QStringLiteral("IntIncParam")}, {eMyMoney::Transaction::Action::Shrsin, QStringLiteral("ShrsinParam")}, {eMyMoney::Transaction::Action::Shrsout, QStringLiteral("ShrsoutParam")} }; -const QString CSVImporter::m_confProfileNames = QStringLiteral("ProfileNames"); -const QString CSVImporter::m_confPriorName = QStringLiteral("Prior"); -const QString CSVImporter::m_confMiscName = QStringLiteral("Misc"); +const QString CSVImporterCore::m_confProfileNames = QStringLiteral("ProfileNames"); +const QString CSVImporterCore::m_confPriorName = QStringLiteral("Prior"); +const QString CSVImporterCore::m_confMiscName = QStringLiteral("Misc"); -CSVImporter::CSVImporter() +CSVImporterCore::CSVImporterCore() : m_profile(0) { m_convertDate = new ConvertDate; m_file = new CSVFile; m_priceFractions << MyMoneyMoney(0.01) << MyMoneyMoney(0.1) << MyMoneyMoney::ONE << MyMoneyMoney(10) << MyMoneyMoney(100); validateConfigFile(); readMiscSettings(); } -CSVImporter::~CSVImporter() +CSVImporterCore::~CSVImporterCore() { delete m_convertDate; delete m_file; } -MyMoneyStatement CSVImporter::unattendedImport(const QString &filename, CSVProfile *profile) +MyMoneyStatement CSVImporterCore::unattendedImport(const QString &filename, CSVProfile *profile) { MyMoneyStatement st; m_profile = profile; m_convertDate->setDateFormatIndex(m_profile->m_dateFormat); if (m_file->getInFileName(filename)) { m_file->readFile(m_profile); m_file->setupParser(m_profile); if (profile->m_decimalSymbol == DecimalSymbol::Auto) { auto columns = getNumericalColumns(); if (detectDecimalSymbols(columns) != -2) return st; } if (!createStatement(st)) st = MyMoneyStatement(); } return st; } -KSharedConfigPtr CSVImporter::configFile() +KSharedConfigPtr CSVImporterCore::configFile() { return KSharedConfig::openConfig(QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)) .filePath(QStringLiteral("csvimporterrc"))); } -void CSVImporter::profileFactory(const Profile type, const QString &name) +void CSVImporterCore::profileFactory(const Profile type, const QString &name) { // delete current profile if (m_profile) { delete m_profile; m_profile = nullptr; } switch (type) { default: case Profile::Investment: m_profile = new InvestmentProfile; break; case Profile::Banking: m_profile = new BankingProfile; break; case Profile::CurrencyPrices: case Profile::StockPrices: m_profile = new PricesProfile(type); break; } m_profile->m_profileName = name; } -void CSVImporter::readMiscSettings() { +void CSVImporterCore::readMiscSettings() { KConfigGroup miscGroup(configFile(), m_confMiscName); m_autodetect.clear(); m_autodetect.insert(AutoFieldDelimiter, miscGroup.readEntry(QStringLiteral("AutoFieldDelimiter"), true)); m_autodetect.insert(AutoDecimalSymbol, miscGroup.readEntry(QStringLiteral("AutoDecimalSymbol"), true)); m_autodetect.insert(AutoDateFormat, miscGroup.readEntry(QStringLiteral("AutoDateFormat"), true)); m_autodetect.insert(AutoAccountInvest, miscGroup.readEntry(QStringLiteral("AutoAccountInvest"), true)); m_autodetect.insert(AutoAccountBank, miscGroup.readEntry(QStringLiteral("AutoAccountBank"), true)); } -void CSVImporter::validateConfigFile() +void CSVImporterCore::validateConfigFile() { const KSharedConfigPtr config = configFile(); KConfigGroup profileNamesGroup(config, m_confProfileNames); if (!profileNamesGroup.exists()) { profileNamesGroup.writeEntry(m_profileConfPrefix.value(Profile::Banking), QStringList()); profileNamesGroup.writeEntry(m_profileConfPrefix.value(Profile::Investment), QStringList()); profileNamesGroup.writeEntry(m_profileConfPrefix.value(Profile::CurrencyPrices), QStringList()); profileNamesGroup.writeEntry(m_profileConfPrefix.value(Profile::StockPrices), QStringList()); profileNamesGroup.writeEntry(m_confPriorName + m_profileConfPrefix.value(Profile::Banking), int()); profileNamesGroup.writeEntry(m_confPriorName + m_profileConfPrefix.value(Profile::Investment), int()); profileNamesGroup.writeEntry(m_confPriorName + m_profileConfPrefix.value(Profile::CurrencyPrices), int()); profileNamesGroup.writeEntry(m_confPriorName + m_profileConfPrefix.value(Profile::StockPrices), int()); profileNamesGroup.sync(); } KConfigGroup miscGroup(config, m_confMiscName); if (!miscGroup.exists()) { - miscGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfHeight), "400"); - miscGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfWidth), "800"); + miscGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfHeight), "400"); + miscGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfWidth), "800"); miscGroup.sync(); } QList confVer = miscGroup.readEntry("KMMVer", QList {0, 0, 0}); if (updateConfigFile(confVer)) // write kmmVer only if there were no errors miscGroup.writeEntry("KMMVer", confVer); } -bool CSVImporter::updateConfigFile(QList &confVer) +bool CSVImporterCore::updateConfigFile(QList &confVer) { bool ret = true; QList kmmVer = QList {5, 0, 0}; int kmmVersion = kmmVer.at(0) * 100 + kmmVer.at(1) * 10 + kmmVer.at(2); int confVersion = confVer.at(0) * 100 + confVer.at(1) * 10 + confVer.at(2); if (confVersion > kmmVersion) { KMessageBox::information(0, i18n("Version of your CSV config file is %1.%2.%3 and is newer than supported version %4.%5.%6. Expect troubles.", confVer.at(0), confVer.at(1), confVer.at(2), kmmVer.at(0), kmmVer.at(1), kmmVer.at(2))); ret = false; return ret; } else if (confVersion == kmmVersion) return true; confVer = kmmVer; const KSharedConfigPtr config = configFile(); QString configFilePath = config.constData()->name(); QFile::copy(configFilePath, configFilePath + QLatin1String(".bak")); KConfigGroup profileNamesGroup(config, m_confProfileNames); QStringList bankProfiles = profileNamesGroup.readEntry(m_profileConfPrefix.value(Profile::Banking), QStringList()); QStringList investProfiles = profileNamesGroup.readEntry(m_profileConfPrefix.value(Profile::Investment), QStringList()); QStringList invalidBankProfiles = profileNamesGroup.readEntry(QLatin1String("Invalid") + m_profileConfPrefix.value(Profile::Banking), QStringList()); // get profiles that was marked invalid during last update QStringList invalidInvestProfiles = profileNamesGroup.readEntry(QLatin1String("Invalid") + m_profileConfPrefix.value(Profile::Investment), QStringList()); QString bankPrefix = m_profileConfPrefix.value(Profile::Banking) + QLatin1Char('-'); QString investPrefix = m_profileConfPrefix.value(Profile::Investment) + QLatin1Char('-'); // for kmm < 5.0.0 change 'BankNames' to 'ProfileNames' and remove 'MainWindow' group if (confVersion < 500 && bankProfiles.isEmpty()) { KConfigGroup oldProfileNamesGroup(config, "BankProfiles"); bankProfiles = oldProfileNamesGroup.readEntry("BankNames", QStringList()); // profile names are under 'BankNames' entry for kmm < 5.0.0 bankPrefix = QLatin1String("Profiles-"); // needed to remove non-existent profiles in first run oldProfileNamesGroup.deleteGroup(); KConfigGroup oldMainWindowGroup(config, "MainWindow"); oldMainWindowGroup.deleteGroup(); KConfigGroup oldSecuritiesGroup(config, "Securities"); oldSecuritiesGroup.deleteGroup(); } bool firstTry = false; if (invalidBankProfiles.isEmpty() && invalidInvestProfiles.isEmpty()) // if there is no invalid profiles then this might be first update try firstTry = true; int invalidProfileResponse = QDialogButtonBox::No; for (auto profileName = bankProfiles.begin(); profileName != bankProfiles.end();) { KConfigGroup bankProfile(config, bankPrefix + *profileName); if (!bankProfile.exists() && !invalidBankProfiles.contains(*profileName)) { // if there is reference to profile but no profile then remove this reference profileName = bankProfiles.erase(profileName); continue; } // for kmm < 5.0.0 remove 'FileType' and 'ProfileName' and assign them to either "Bank=" or "Invest=" if (confVersion < 500) { QString lastUsedDirectory; KConfigGroup oldBankProfile(config, QLatin1String("Profiles-") + *profileName); // if half of configuration is updated and the other one untouched this is needed QString oldProfileType = oldBankProfile.readEntry("FileType", QString()); KConfigGroup newProfile; if (oldProfileType == QLatin1String("Invest")) { oldBankProfile.deleteEntry("BrokerageParam"); oldBankProfile.writeEntry(m_colTypeConfName.value(Column::Type), oldBankProfile.readEntry("PayeeCol")); oldBankProfile.deleteEntry("PayeeCol"); oldBankProfile.deleteEntry("Filter"); oldBankProfile.deleteEntry("SecurityName"); lastUsedDirectory = oldBankProfile.readEntry("InvDirectory"); newProfile = KConfigGroup(config, m_profileConfPrefix.value(Profile::Investment) + QLatin1Char('-') + *profileName); investProfiles.append(*profileName); profileName = bankProfiles.erase(profileName); } else if (oldProfileType == QLatin1String("Banking")) { lastUsedDirectory = oldBankProfile.readEntry("CsvDirectory"); newProfile = KConfigGroup(config, m_profileConfPrefix.value(Profile::Banking) + QLatin1Char('-') + *profileName); ++profileName; } else { if (invalidProfileResponse != QDialogButtonBox::YesToAll && invalidProfileResponse != QDialogButtonBox::NoToAll) { if (!firstTry && !invalidBankProfiles.contains(*profileName)) { // if it isn't first update run and profile isn't on the list of invalid ones then don't bother ++profileName; continue; } invalidProfileResponse = KMessageBox::createKMessageBox(nullptr, new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::YesToAll | QDialogButtonBox::No | QDialogButtonBox::NoToAll), QMessageBox::Warning, i18n("
During update of %1
" "the profile type for %2 could not be recognized.
" "The profile cannot be used because of that.
" "Do you want to delete it?
", configFilePath, *profileName), QStringList(), QString(), nullptr, KMessageBox::Dangerous); } switch (invalidProfileResponse) { case QDialogButtonBox::YesToAll: case QDialogButtonBox::Yes: oldBankProfile.deleteGroup(); invalidBankProfiles.removeOne(*profileName); profileName = bankProfiles.erase(profileName); break; case QDialogButtonBox::NoToAll: case QDialogButtonBox::No: if (!invalidBankProfiles.contains(*profileName)) // on user request: don't delete profile but keep eye on it invalidBankProfiles.append(*profileName); ret = false; ++profileName; break; } continue; } oldBankProfile.deleteEntry("FileType"); oldBankProfile.deleteEntry("ProfileName"); oldBankProfile.deleteEntry("DebitFlag"); oldBankProfile.deleteEntry("InvDirectory"); oldBankProfile.deleteEntry("CsvDirectory"); oldBankProfile.sync(); oldBankProfile.copyTo(&newProfile); oldBankProfile.deleteGroup(); newProfile.writeEntry(m_miscSettingsConfName.value(ConfDirectory), lastUsedDirectory); newProfile.writeEntry(m_miscSettingsConfName.value(ConfEncoding), "106" /*UTF-8*/ ); // in 4.8 encoding wasn't supported well so set it to utf8 by default newProfile.sync(); } } for (auto profileName = investProfiles.begin(); profileName != investProfiles.end();) { KConfigGroup investProfile(config, investPrefix + *profileName); if (!investProfile.exists() && !invalidInvestProfiles.contains(*profileName)) { // if there is reference to profile but no profile then remove this reference profileName = investProfiles.erase(profileName); continue; } ++profileName; } profileNamesGroup.writeEntry(m_profileConfPrefix.value(Profile::Banking), bankProfiles); // update profile names as some of them might have been changed profileNamesGroup.writeEntry(m_profileConfPrefix.value(Profile::Investment), investProfiles); if (invalidBankProfiles.isEmpty()) // if no invalid profiles then we don't need this variable anymore profileNamesGroup.deleteEntry("InvalidBank"); else profileNamesGroup.writeEntry("InvalidBank", invalidBankProfiles); if (invalidInvestProfiles.isEmpty()) profileNamesGroup.deleteEntry("InvalidInvest"); else profileNamesGroup.writeEntry("InvalidInvest", invalidInvestProfiles); if (ret) QFile::remove(configFilePath + ".bak"); // remove backup if all is ok return ret; } -bool CSVImporter::profilesAction(const Profile type, const ProfileAction action, const QString &name, const QString &newname) +bool CSVImporterCore::profilesAction(const Profile type, const ProfileAction action, const QString &name, const QString &newname) { bool ret = false; const KSharedConfigPtr config = configFile(); KConfigGroup profileNamesGroup(config, m_confProfileNames); QString profileTypeStr = m_profileConfPrefix.value(type); QStringList profiles = profileNamesGroup.readEntry(profileTypeStr, QStringList()); KConfigGroup profileName(config, profileTypeStr + QLatin1Char('-') + name); switch (action) { case ProfileAction::UpdateLastUsed: profileNamesGroup.writeEntry(m_confPriorName + profileTypeStr, profiles.indexOf(name)); break; case ProfileAction::Add: if (!profiles.contains(newname)) { profiles.append(newname); ret = true; } break; case ProfileAction::Remove: { profiles.removeOne(name); profileName.deleteGroup(); profileName.sync(); ret = true; break; } case ProfileAction::Rename: { if (!newname.isEmpty() && name != newname) { int idx = profiles.indexOf(name); if (idx != -1) { profiles[idx] = newname; KConfigGroup newProfileName(config, profileTypeStr + QLatin1Char('-') + newname); if (profileName.exists() && !newProfileName.exists()) { profileName.copyTo(&newProfileName); profileName.deleteGroup(); profileName.sync(); newProfileName.sync(); ret = true; } } } break; } } profileNamesGroup.writeEntry(profileTypeStr, profiles); profileNamesGroup.sync(); return ret; } -bool CSVImporter::validateDateFormat(const int col) +bool CSVImporterCore::validateDateFormat(const int col) { bool isOK = true; for (int row = m_profile->m_startLine; row <= m_profile->m_endLine; ++row) { QStandardItem* item = m_file->m_model->item(row, col); QDate dat = m_convertDate->convertDate(item->text()); if (dat == QDate()) { isOK = false; break; } } return isOK; } -bool CSVImporter::validateDecimalSymbols(const QList &columns) +bool CSVImporterCore::validateDecimalSymbols(const QList &columns) { bool isOK = true; foreach (const auto column, columns) { m_file->m_parse->setDecimalSymbol(m_decimalSymbolIndexMap.value(column)); for (int row = m_profile->m_startLine; row <= m_profile->m_endLine; ++row) { QStandardItem *item = m_file->m_model->item(row, column); QString rawNumber = item->text(); m_file->m_parse->possiblyReplaceSymbol(rawNumber); if (m_file->m_parse->invalidConversion() && !rawNumber.isEmpty()) { // empty strings are welcome isOK = false; break; } } } return isOK; } -bool CSVImporter::validateCurrencies(const PricesProfile *profile) +bool CSVImporterCore::validateCurrencies(const PricesProfile *profile) { if (profile->m_securitySymbol.isEmpty() || profile->m_currencySymbol.isEmpty()) return false; return true; } -bool CSVImporter::validateSecurity(const PricesProfile *profile) +bool CSVImporterCore::validateSecurity(const PricesProfile *profile) { if (profile->m_securitySymbol.isEmpty() || profile->m_securityName.isEmpty()) return false; return true; } -bool CSVImporter::validateSecurity(const InvestmentProfile *profile) +bool CSVImporterCore::validateSecurity(const InvestmentProfile *profile) { if (profile->m_securitySymbol.isEmpty() || profile->m_securityName.isEmpty()) return false; return true; } -bool CSVImporter::validateSecurities() +bool CSVImporterCore::validateSecurities() { QSet onlySymbols; QSet onlyNames; sortSecurities(onlySymbols, onlyNames, m_mapSymbolName); if (!onlySymbols.isEmpty() || !onlyNames.isEmpty()) return false; return true; } -eMyMoney::Transaction::Action CSVImporter::processActionTypeField(const InvestmentProfile *profile, const int row, const int col) +eMyMoney::Transaction::Action CSVImporterCore::processActionTypeField(const InvestmentProfile *profile, const int row, const int col) { if (col == -1) return eMyMoney::Transaction::Action::None; QString type = m_file->m_model->item(row, col)->text(); QList actions; actions << eMyMoney::Transaction::Action::Buy << eMyMoney::Transaction::Action::Sell << // first and second most frequent action eMyMoney::Transaction::Action::ReinvestDividend << eMyMoney::Transaction::Action::CashDividend << // we don't want "reinv-dividend" to be accidentaly caught by "dividend" eMyMoney::Transaction::Action::Interest << eMyMoney::Transaction::Action::Shrsin << eMyMoney::Transaction::Action::Shrsout; foreach (const auto action, actions) { if (profile->m_transactionNames.value(action).contains(type, Qt::CaseInsensitive)) return action; } return eMyMoney::Transaction::Action::None; } -validationResultE CSVImporter::validateActionType(MyMoneyStatement::Transaction &tr) +validationResultE CSVImporterCore::validateActionType(MyMoneyStatement::Transaction &tr) { validationResultE ret = ValidActionType; QList validActionTypes = createValidActionTypes(tr); if (validActionTypes.isEmpty()) ret = InvalidActionValues; else if (!validActionTypes.contains(tr.m_eAction)) ret = NoActionType; return ret; } -bool CSVImporter::calculateFee() +bool CSVImporterCore::calculateFee() { InvestmentProfile *profile = dynamic_cast(m_profile); if (!profile) return false; if ((profile->m_feeRate.isEmpty() || // check whether feeRate... profile->m_colTypeNum.value(Column::Amount) == -1)) // ...and amount is in place return false; QString decimalSymbol; if (profile->m_decimalSymbol == DecimalSymbol::Auto) { DecimalSymbol detectedSymbol = detectDecimalSymbol(profile->m_colTypeNum.value(Column::Amount), QString()); if (detectedSymbol == DecimalSymbol::Auto) return false; m_file->m_parse->setDecimalSymbol(detectedSymbol); decimalSymbol = m_file->m_parse->decimalSymbol(detectedSymbol); } else decimalSymbol = m_file->m_parse->decimalSymbol(profile->m_decimalSymbol); MyMoneyMoney feePercent(m_file->m_parse->possiblyReplaceSymbol(profile->m_feeRate)); // convert 0.67% ... feePercent /= MyMoneyMoney(100); // ... to 0.0067 if (profile->m_minFee.isEmpty()) profile->m_minFee = QString::number(0.00, 'f', 2); MyMoneyMoney minFee(m_file->m_parse->possiblyReplaceSymbol(profile->m_minFee)); QList items; for (int row = 0; row < profile->m_startLine; ++row) // fill rows above with whitespace for nice effect with markUnwantedRows items.append(new QStandardItem(QString())); for (int row = profile->m_startLine; row <= profile->m_endLine; ++row) { QString txt, numbers; bool ok = false; numbers = txt = m_file->m_model->item(row, profile->m_colTypeNum.value(Column::Amount))->text(); numbers.remove(QRegularExpression(QStringLiteral("[,. ]"))).toInt(&ok); if (!ok) { // check if it's numerical string... items.append(new QStandardItem(QString())); continue; // ...and skip if not (TODO: allow currency symbols and IDs) } if (txt.startsWith(QLatin1Char('('))) { txt.remove(QRegularExpression(QStringLiteral("[()]"))); txt.prepend(QLatin1Char('-')); } txt = m_file->m_parse->possiblyReplaceSymbol(txt); MyMoneyMoney fee(txt); fee *= feePercent; if (fee < minFee) fee = minFee; txt.setNum(fee.toDouble(), 'f', 4); txt.replace(QLatin1Char('.'), decimalSymbol); //make sure decimal symbol is uniform in whole line items.append(new QStandardItem(txt)); } for (int row = profile->m_endLine + 1; row < m_file->m_rowCount; ++row) // fill rows below with whitespace for nice effect with markUnwantedRows items.append(new QStandardItem(QString())); int col = profile->m_colTypeNum.value(Column::Fee, -1); if (col == -1) { // fee column isn't present m_file->m_model->appendColumn(items); ++m_file->m_columnCount; } else if (col >= m_file->m_columnCount) { // column number must have been stored in profile m_file->m_model->appendColumn(items); ++m_file->m_columnCount; } else { // fee column is present and has been recalculated m_file->m_model->removeColumn(m_file->m_columnCount - 1); m_file->m_model->appendColumn(items); } profile->m_colTypeNum[Column::Fee] = m_file->m_columnCount - 1; return true; } -DecimalSymbol CSVImporter::detectDecimalSymbol(const int col, const QString &exclude) +DecimalSymbol CSVImporterCore::detectDecimalSymbol(const int col, const QString &exclude) { DecimalSymbol detectedSymbol = DecimalSymbol::Auto; QString pattern; QRegularExpression re("^[\\(+-]?\\d+[\\)]?$"); // matches '0' ; '+12' ; '-345' ; '(6789)' bool dotIsDecimalSeparator = false; bool commaIsDecimalSeparator = false; for (int row = m_profile->m_startLine; row <= m_profile->m_endLine; ++row) { QString txt = m_file->m_model->item(row, col)->text(); if (txt.isEmpty()) // nothing to process, so go to next row continue; int dotPos = txt.lastIndexOf(QLatin1Char('.')); // get last positions of decimal/thousand separator... int commaPos = txt.lastIndexOf(QLatin1Char(',')); // ...to be able to determine which one is the last if (dotPos != -1 && commaPos != -1) { if (dotPos > commaPos && commaIsDecimalSeparator == false) // following case 1,234.56 dotIsDecimalSeparator = true; else if (dotPos < commaPos && dotIsDecimalSeparator == false) // following case 1.234,56 commaIsDecimalSeparator = true; else // following case 1.234,56 and somwhere earlier there was 1,234.56 so unresolvable conflict return detectedSymbol; } else if (dotPos != -1) { // following case 1.23 if (dotIsDecimalSeparator) // it's already know that dotIsDecimalSeparator continue; if (!commaIsDecimalSeparator) // if there is no conflict with comma as decimal separator dotIsDecimalSeparator = true; else { if (txt.count(QLatin1Char('.')) > 1) // following case 1.234.567 so OK continue; else if (txt.length() - 4 == dotPos) // following case 1.234 and somwhere earlier there was 1.234,56 so OK continue; else // following case 1.23 and somwhere earlier there was 1,23 so unresolvable conflict return detectedSymbol; } } else if (commaPos != -1) { // following case 1,23 if (commaIsDecimalSeparator) // it's already know that commaIsDecimalSeparator continue; else if (!dotIsDecimalSeparator) // if there is no conflict with dot as decimal separator commaIsDecimalSeparator = true; else { if (txt.count(QLatin1Char(',')) > 1) // following case 1,234,567 so OK continue; else if (txt.length() - 4 == commaPos) // following case 1,234 and somwhere earlier there was 1,234.56 so OK continue; else // following case 1,23 and somwhere earlier there was 1.23 so unresolvable conflict return detectedSymbol; } } else { // following case 123 if (pattern.isEmpty()) { } txt.remove(QRegularExpression(QLatin1String("[ ") + QRegularExpression::escape(exclude) + QLatin1String("]"))); QRegularExpressionMatch match = re.match(txt); if (match.hasMatch()) // if string is pure numerical then go forward... continue; else // ...if not then it's non-numerical garbage return detectedSymbol; } } if (dotIsDecimalSeparator) detectedSymbol = DecimalSymbol::Dot; else if (commaIsDecimalSeparator) detectedSymbol = DecimalSymbol::Comma; else { // whole column was empty, but we don't want to fail so take OS's decimal symbol if (QLocale().decimalPoint() == QLatin1Char('.')) detectedSymbol = DecimalSymbol::Dot; else detectedSymbol = DecimalSymbol::Comma; } return detectedSymbol; } -int CSVImporter::detectDecimalSymbols(const QList &columns) +int CSVImporterCore::detectDecimalSymbols(const QList &columns) { int ret = -2; // get list of used currencies to remove them from col QList accounts; MyMoneyFile *file = MyMoneyFile::instance(); file->accountList(accounts); QList accountTypes; accountTypes << eMyMoney::Account::Type::Checkings << eMyMoney::Account::Type::Savings << eMyMoney::Account::Type::Liability << eMyMoney::Account::Type::Checkings << eMyMoney::Account::Type::Savings << eMyMoney::Account::Type::Cash << eMyMoney::Account::Type::CreditCard << eMyMoney::Account::Type::Loan << eMyMoney::Account::Type::Asset << eMyMoney::Account::Type::Liability; QSet currencySymbols; foreach (const auto account, accounts) { if (accountTypes.contains(account.accountType())) { // account must actually have currency property currencySymbols.insert(account.currencyId()); // add currency id currencySymbols.insert(file->currency(account.currencyId()).tradingSymbol()); // add currency symbol } } QString filteredCurrencies = QStringList(currencySymbols.values()).join(""); QString pattern = QString::fromLatin1("%1%2").arg(QLocale().currencySymbol()).arg(filteredCurrencies); foreach (const auto column, columns) { DecimalSymbol detectedSymbol = detectDecimalSymbol(column, pattern); if (detectedSymbol == DecimalSymbol::Auto) { ret = column; return ret; } m_decimalSymbolIndexMap.insert(column, detectedSymbol); } return ret; } -QList CSVImporter::findAccounts(const QList &accountTypes, const QString &statementHeader) +QList CSVImporterCore::findAccounts(const QList &accountTypes, const QString &statementHeader) { MyMoneyFile* file = MyMoneyFile::instance(); QList accountList; file->accountList(accountList); QList filteredTypes; QList filteredAccounts; QList::iterator account; QRegularExpression filterOutChars(QStringLiteral("[-., ]")); foreach (const auto account, accountList) { if (accountTypes.contains(account.accountType()) && !(account).isClosed()) filteredTypes.append(account); } // filter out accounts whose names aren't in statements header foreach (const auto account, filteredTypes) { QString txt = account.name(); txt.remove(filterOutChars); if (txt.isEmpty() || txt.length() < 3) continue; if (statementHeader.contains(txt, Qt::CaseInsensitive)) filteredAccounts.append(account); } // if filtering returned more results, filter out accounts whose numbers aren't in statements header if (filteredAccounts.count() > 1) { for (account = filteredAccounts.begin(); account != filteredAccounts.end();) { QString txt = (*account).number(); txt.remove(filterOutChars); if (txt.isEmpty() || txt.length() < 3) { ++account; continue; } if (statementHeader.contains(txt, Qt::CaseInsensitive)) ++account; else account = filteredAccounts.erase(account); } } // if filtering returned more results, filter out accounts whose numbers are the shortest if (filteredAccounts.count() > 1) { for (auto i = 1; i < filteredAccounts.count();) { auto firstAccNumber = filteredAccounts.at(0).number(); auto secondAccNumber = filteredAccounts.at(i).number(); if (firstAccNumber.length() > secondAccNumber.length()) { filteredAccounts.removeAt(i); } else if (firstAccNumber.length() < secondAccNumber.length()) { filteredAccounts.removeAt(0); --i; } else { ++i; } } } // if filtering returned more results, filter out accounts whose names are the shortest if (filteredAccounts.count() > 1) { for (auto i = 1; i < filteredAccounts.count();) { auto firstAccName = filteredAccounts.at(0).name(); auto secondAccName = filteredAccounts.at(i).name(); if (firstAccName.length() > secondAccName.length()) { filteredAccounts.removeAt(i); } else if (firstAccName.length() < secondAccName.length()) { filteredAccounts.removeAt(0); --i; } else { ++i; } } } // if filtering by name and number didn't return nothing, then try filtering by number only if (filteredAccounts.isEmpty()) { foreach (const auto account, filteredTypes) { QString txt = account.number(); txt.remove(filterOutChars); if (txt.isEmpty() || txt.length() < 3) continue; if (statementHeader.contains(txt, Qt::CaseInsensitive)) filteredAccounts.append(account); } } return filteredAccounts; } -bool CSVImporter::detectAccount(MyMoneyStatement &st) +bool CSVImporterCore::detectAccount(MyMoneyStatement &st) { QString statementHeader; for (int row = 0; row < m_profile->m_startLine; ++row) // concatenate header for better search for (int col = 0; col < m_file->m_columnCount; ++col) statementHeader.append(m_file->m_model->item(row, col)->text()); statementHeader.remove(QRegularExpression(QStringLiteral("[-., ]"))); QList accounts; QList accountTypes; switch(m_profile->type()) { default: case Profile::Banking: accountTypes << eMyMoney::Account::Type::Checkings << eMyMoney::Account::Type::Savings << eMyMoney::Account::Type::Liability << eMyMoney::Account::Type::Checkings << eMyMoney::Account::Type::Savings << eMyMoney::Account::Type::Cash << eMyMoney::Account::Type::CreditCard << eMyMoney::Account::Type::Loan << eMyMoney::Account::Type::Asset << eMyMoney::Account::Type::Liability; accounts = findAccounts(accountTypes, statementHeader); break; case Profile::Investment: accountTypes << eMyMoney::Account::Type::Investment; // take investment accounts... accounts = findAccounts(accountTypes, statementHeader); //...and search them in statement header break; } if (accounts.count() == 1) { // set account in statement, if it was the only one match st.m_strAccountName = accounts.first().name(); st.m_strAccountNumber = accounts.first().number(); st.m_accountId = accounts.first().id(); switch (accounts.first().accountType()) { case eMyMoney::Account::Type::Checkings: st.m_eType = eMyMoney::Statement::Type::Checkings; break; case eMyMoney::Account::Type::Savings: st.m_eType = eMyMoney::Statement::Type::Savings; break; case eMyMoney::Account::Type::Investment: st.m_eType = eMyMoney::Statement::Type::Investment; break; case eMyMoney::Account::Type::CreditCard: st.m_eType = eMyMoney::Statement::Type::CreditCard; break; default: st.m_eType = eMyMoney::Statement::Type::None; } return true; } return false; } -bool CSVImporter::processBankRow(MyMoneyStatement &st, const BankingProfile *profile, const int row) +bool CSVImporterCore::processBankRow(MyMoneyStatement &st, const BankingProfile *profile, const int row) { MyMoneyStatement::Transaction tr; QString memo; QString txt; // process number field if (profile->m_colTypeNum.value(Column::Number, -1) != -1) tr.m_strNumber = txt; // process date field int col = profile->m_colTypeNum.value(Column::Date, -1); tr.m_datePosted = processDateField(row, col); if (tr.m_datePosted == QDate()) return false; // process payee field col = profile->m_colTypeNum.value(Column::Payee, -1); if (col != -1) tr.m_strPayee = m_file->m_model->item(row, col)->text(); // process memo field col = profile->m_colTypeNum.value(Column::Memo, -1); if (col != -1) memo.append(m_file->m_model->item(row, col)->text()); for (int i = 0; i < profile->m_memoColList.count(); ++i) { if (profile->m_memoColList.at(i) != col) { if (!memo.isEmpty()) memo.append(QLatin1Char('\n')); if (profile->m_memoColList.at(i) < m_file->m_columnCount) memo.append(m_file->m_model->item(row, profile->m_memoColList.at(i))->text()); } } tr.m_strMemo = memo; // process amount field col = profile->m_colTypeNum.value(Column::Amount, -1); tr.m_amount = processAmountField(profile, row, col); if (col != -1 && profile->m_oppositeSigns) // change signs to opposite if requested by user tr.m_amount *= MyMoneyMoney(-1); // process credit/debit field if (profile->m_colTypeNum.value(Column::Credit, -1) != -1 && profile->m_colTypeNum.value(Column::Debit, -1) != -1) { QString credit = m_file->m_model->item(row, profile->m_colTypeNum.value(Column::Credit))->text(); QString debit = m_file->m_model->item(row, profile->m_colTypeNum.value(Column::Debit))->text(); tr.m_amount = processCreditDebit(credit, debit); if (!credit.isEmpty() && !debit.isEmpty()) return false; } MyMoneyStatement::Split s1; s1.m_amount = tr.m_amount; s1.m_strMemo = tr.m_strMemo; MyMoneyStatement::Split s2 = s1; s2.m_reconcile = tr.m_reconcile; s2.m_amount = -s1.m_amount; // process category field col = profile->m_colTypeNum.value(Column::Category, -1); if (col != -1) { txt = m_file->m_model->item(row, col)->text(); QString accountId = MyMoneyFile::instance()->checkCategory(txt, s1.m_amount, s2.m_amount); if (!accountId.isEmpty()) { s2.m_accountId = accountId; s2.m_strCategoryName = txt; tr.m_listSplits.append(s2); } } // calculate hash txt.clear(); for (int i = 0; i < m_file->m_columnCount; ++i) txt.append(m_file->m_model->item(row, i)->text()); QString hashBase = QString::fromLatin1("%1-%2") .arg(tr.m_datePosted.toString(Qt::ISODate)) .arg(MyMoneyTransaction::hash(txt)); QString hash; for (uchar idx = 0; idx < 0xFF; ++idx) { // assuming threre will be no more than 256 transactions with the same hashBase hash = QString::fromLatin1("%1-%2").arg(hashBase).arg(idx); QSet::const_iterator it = m_hashSet.constFind(hash); if (it == m_hashSet.constEnd()) break; } m_hashSet.insert(hash); tr.m_strBankID = hash; st.m_listTransactions.append(tr); // Add the MyMoneyStatement::Transaction to the statement return true; } -bool CSVImporter::processInvestRow(MyMoneyStatement &st, const InvestmentProfile *profile, const int row) +bool CSVImporterCore::processInvestRow(MyMoneyStatement &st, const InvestmentProfile *profile, const int row) { MyMoneyStatement::Transaction tr; QString memo; QString txt; // process date field int col = profile->m_colTypeNum.value(Column::Date, -1); tr.m_datePosted = processDateField(row, col); if (tr.m_datePosted == QDate()) return false; // process quantity field col = profile->m_colTypeNum.value(Column::Quantity, -1); tr.m_shares = processQuantityField(profile, row, col); // process price field col = profile->m_colTypeNum.value(Column::Price, -1); tr.m_price = processPriceField(profile, row, col); // process amount field col = profile->m_colTypeNum.value(Column::Amount, -1); tr.m_amount = processAmountField(profile, row, col); // process type field col = profile->m_colTypeNum.value(Column::Type, -1); tr.m_eAction = processActionTypeField(profile, row, col); if (!m_isActionTypeValidated && col != -1 && // if action type wasn't validated in wizard then... validateActionType(tr) != ValidActionType) // ...check if price, amount, quantity is appropriate return false; // process fee field col = profile->m_colTypeNum.value(Column::Fee, -1); if (col != -1) { if (profile->m_decimalSymbol == DecimalSymbol::Auto) { DecimalSymbol decimalSymbol = m_decimalSymbolIndexMap.value(col); m_file->m_parse->setDecimalSymbol(decimalSymbol); } txt = m_file->m_model->item(row, col)->text(); if (txt.startsWith(QLatin1Char('('))) // check if brackets notation is used for negative numbers txt.remove(QRegularExpression(QStringLiteral("[()]"))); if (txt.isEmpty()) tr.m_fees = MyMoneyMoney(); else { MyMoneyMoney fee(m_file->m_parse->possiblyReplaceSymbol(txt)); if (profile->m_feeIsPercentage && profile->m_feeRate.isEmpty()) // fee is percent fee *= tr.m_amount / MyMoneyMoney(100); // as percentage fee.abs(); tr.m_fees = fee; } } // process symbol and name field col = profile->m_colTypeNum.value(Column::Symbol, -1); if (col != -1) tr.m_strSymbol = m_file->m_model->item(row, col)->text(); col = profile->m_colTypeNum.value(Column::Name, -1); if (col != -1 && tr.m_strSymbol.isEmpty()) { // case in which symbol field is empty txt = m_file->m_model->item(row, col)->text(); tr.m_strSymbol = m_mapSymbolName.key(txt); // it's all about getting the right symbol } else if (!profile->m_securitySymbol.isEmpty()) tr.m_strSymbol = profile->m_securitySymbol; else if (tr.m_strSymbol.isEmpty()) return false; tr.m_strSecurity = m_mapSymbolName.value(tr.m_strSymbol); // take name from prepared names to avoid potential name mismatch // process memo field col = profile->m_colTypeNum.value(Column::Memo, -1); if (col != -1) memo.append(m_file->m_model->item(row, col)->text()); for (int i = 0; i < profile->m_memoColList.count(); ++i) { if (profile->m_memoColList.at(i) != col) { if (!memo.isEmpty()) memo.append(QLatin1Char('\n')); if (profile->m_memoColList.at(i) < m_file->m_columnCount) memo.append(m_file->m_model->item(row, profile->m_memoColList.at(i))->text()); } } tr.m_strMemo = memo; tr.m_strInterestCategory.clear(); // no special category tr.m_strBrokerageAccount.clear(); // no brokerage account auto-detection MyMoneyStatement::Split s1; s1.m_amount = tr.m_amount; s1.m_strMemo = tr.m_strMemo; MyMoneyStatement::Split s2 = s1; s2.m_amount = -s1.m_amount; s2.m_accountId = MyMoneyFile::instance()->checkCategory(tr.m_strInterestCategory, s1.m_amount, s2.m_amount); // deduct fees from amount if (tr.m_eAction == eMyMoney::Transaction::Action::CashDividend || tr.m_eAction == eMyMoney::Transaction::Action::Sell || tr.m_eAction == eMyMoney::Transaction::Action::Interest) tr.m_amount -= tr.m_fees; else if (tr.m_eAction == eMyMoney::Transaction::Action::Buy) { if (tr.m_amount.isPositive()) tr.m_amount = -tr.m_amount; //if broker doesn't use minus sings for buy transactions, set it manually here tr.m_amount -= tr.m_fees; } else if (tr.m_eAction == eMyMoney::Transaction::Action::None) tr.m_listSplits.append(s2); st.m_listTransactions.append(tr); // Add the MyMoneyStatement::Transaction to the statement return true; } -bool CSVImporter::processPriceRow(MyMoneyStatement &st, const PricesProfile *profile, const int row) +bool CSVImporterCore::processPriceRow(MyMoneyStatement &st, const PricesProfile *profile, const int row) { MyMoneyStatement::Price pr; // process date field int col = profile->m_colTypeNum.value(Column::Date, -1); pr.m_date = processDateField(row, col); if (pr.m_date == QDate()) return false; // process price field col = profile->m_colTypeNum.value(Column::Price, -1); pr.m_amount = processPriceField(profile, row, col); switch (profile->type()) { case Profile::CurrencyPrices: if (profile->m_securitySymbol.isEmpty() || profile->m_currencySymbol.isEmpty()) return false; pr.m_strSecurity = profile->m_securitySymbol; pr.m_strCurrency = profile->m_currencySymbol; break; case Profile::StockPrices: if (profile->m_securityName.isEmpty()) return false; pr.m_strSecurity = profile->m_securityName; break; default: return false; } pr.m_sourceName = profile->m_profileName; st.m_listPrices.append(pr); // Add price to the statement return true; } -QDate CSVImporter::processDateField(const int row, const int col) +QDate CSVImporterCore::processDateField(const int row, const int col) { QDate date; if (col != -1) { QString txt = m_file->m_model->item(row, col)->text(); date = m_convertDate->convertDate(txt); // Date column } return date; } -MyMoneyMoney CSVImporter::processCreditDebit(QString &credit, QString &debit) +MyMoneyMoney CSVImporterCore::processCreditDebit(QString &credit, QString &debit) { MyMoneyMoney amount; if (m_profile->m_decimalSymbol == DecimalSymbol::Auto) setupFieldDecimalSymbol(m_profile->m_colTypeNum.value(Column::Credit)); if (credit.startsWith(QLatin1Char('('))) { // check if brackets notation is used for negative numbers credit.remove(QRegularExpression(QStringLiteral("[()]"))); credit.prepend(QLatin1Char('-')); } if (debit.startsWith(QLatin1Char('('))) { // check if brackets notation is used for negative numbers debit.remove(QRegularExpression(QStringLiteral("[()]"))); debit.prepend(QLatin1Char('-')); } if (!credit.isEmpty() && !debit.isEmpty()) { // we do not expect both fields to be non-zero if (MyMoneyMoney(credit).isZero()) credit = QString(); if (MyMoneyMoney(debit).isZero()) debit = QString(); } if (!debit.startsWith(QLatin1Char('-')) && !debit.isEmpty()) // ensure debit field is negative debit.prepend(QLatin1Char('-')); if (!credit.isEmpty() && debit.isEmpty()) amount = MyMoneyMoney(m_file->m_parse->possiblyReplaceSymbol(credit)); else if (credit.isEmpty() && !debit.isEmpty()) amount = MyMoneyMoney(m_file->m_parse->possiblyReplaceSymbol(debit)); else if (!credit.isEmpty() && !debit.isEmpty()) { // both fields are non-empty and non-zero so let user decide return amount; } else amount = MyMoneyMoney(); // both fields are empty and zero so set amount to zero return amount; } -MyMoneyMoney CSVImporter::processQuantityField(const CSVProfile *profile, const int row, const int col) +MyMoneyMoney CSVImporterCore::processQuantityField(const CSVProfile *profile, const int row, const int col) { MyMoneyMoney shares; if (col != -1) { if (profile->m_decimalSymbol == DecimalSymbol::Auto) setupFieldDecimalSymbol(col); QString txt = m_file->m_model->item(row, col)->text(); txt.remove(QRegularExpression(QStringLiteral("-+"))); // remove unwanted sings in quantity if (!txt.isEmpty()) shares = MyMoneyMoney(m_file->m_parse->possiblyReplaceSymbol(txt)); } return shares; } -MyMoneyMoney CSVImporter::processAmountField(const CSVProfile *profile, const int row, const int col) +MyMoneyMoney CSVImporterCore::processAmountField(const CSVProfile *profile, const int row, const int col) { MyMoneyMoney amount; if (col != -1) { if (profile->m_decimalSymbol == DecimalSymbol::Auto) setupFieldDecimalSymbol(col); QString txt = m_file->m_model->item(row, col)->text(); if (txt.startsWith(QLatin1Char('('))) { // check if brackets notation is used for negative numbers txt.remove(QRegularExpression(QStringLiteral("[()]"))); txt.prepend(QLatin1Char('-')); } if (!txt.isEmpty()) amount = MyMoneyMoney(m_file->m_parse->possiblyReplaceSymbol(txt)); } return amount; } -MyMoneyMoney CSVImporter::processPriceField(const InvestmentProfile *profile, const int row, const int col) +MyMoneyMoney CSVImporterCore::processPriceField(const InvestmentProfile *profile, const int row, const int col) { MyMoneyMoney price; if (col != -1) { if (profile->m_decimalSymbol == DecimalSymbol::Auto) setupFieldDecimalSymbol(col); QString txt = m_file->m_model->item(row, col)->text(); if (!txt.isEmpty()) { price = MyMoneyMoney(m_file->m_parse->possiblyReplaceSymbol(txt)); price *= m_priceFractions.at(profile->m_priceFraction); } } return price; } -MyMoneyMoney CSVImporter::processPriceField(const PricesProfile *profile, const int row, const int col) +MyMoneyMoney CSVImporterCore::processPriceField(const PricesProfile *profile, const int row, const int col) { MyMoneyMoney price; if (col != -1) { if (profile->m_decimalSymbol == DecimalSymbol::Auto) setupFieldDecimalSymbol(col); QString txt = m_file->m_model->item(row, col)->text(); if (!txt.isEmpty()) { price = MyMoneyMoney(m_file->m_parse->possiblyReplaceSymbol(txt)); price *= m_priceFractions.at(profile->m_priceFraction); } } return price; } -QList CSVImporter::createValidActionTypes(MyMoneyStatement::Transaction &tr) +QList CSVImporterCore::createValidActionTypes(MyMoneyStatement::Transaction &tr) { QList validActionTypes; if (tr.m_shares.isPositive() && tr.m_price.isPositive() && !tr.m_amount.isZero()) validActionTypes << eMyMoney::Transaction::Action::ReinvestDividend << eMyMoney::Transaction::Action::Buy << eMyMoney::Transaction::Action::Sell; else if (tr.m_shares.isZero() && tr.m_price.isZero() && !tr.m_amount.isZero()) validActionTypes << eMyMoney::Transaction::Action::CashDividend << eMyMoney::Transaction::Action::Interest; else if (tr.m_shares.isPositive() && tr.m_price.isZero() && tr.m_amount.isZero()) validActionTypes << eMyMoney::Transaction::Action::Shrsin << eMyMoney::Transaction::Action::Shrsout; return validActionTypes; } -bool CSVImporter::sortSecurities(QSet& onlySymbols, QSet& onlyNames, QMap& mapSymbolName) +bool CSVImporterCore::sortSecurities(QSet& onlySymbols, QSet& onlyNames, QMap& mapSymbolName) { QList securityList = MyMoneyFile::instance()->securityList(); int symbolCol = m_profile->m_colTypeNum.value(Column::Symbol, -1); int nameCol = m_profile->m_colTypeNum.value(Column::Name, -1); // sort by availability of symbol and name for (int row = m_profile->m_startLine; row <= m_profile->m_endLine; ++row) { QString symbol; QString name; if (symbolCol != -1) symbol = m_file->m_model->item(row, symbolCol)->text().trimmed(); if (nameCol != -1) name = m_file->m_model->item(row, nameCol)->text().trimmed(); if (!symbol.isEmpty() && !name.isEmpty()) mapSymbolName.insert(symbol, name); else if (!symbol.isEmpty()) onlySymbols.insert(symbol); else if (!name.isEmpty()) onlyNames.insert(name); else return false; } // try to find names for symbols for (QSet::iterator symbol = onlySymbols.begin(); symbol != onlySymbols.end();) { QList filteredSecurities; foreach (const auto sec, securityList) { if ((*symbol).compare(sec.tradingSymbol(), Qt::CaseInsensitive) == 0) filteredSecurities.append(sec); // gather all securities that by matched by symbol } if (filteredSecurities.count() == 1) { // single security matched by the symbol so... mapSymbolName.insert(*symbol, filteredSecurities.first().name()); symbol = onlySymbols.erase(symbol); // ...it's no longer unknown } else if (!filteredSecurities.isEmpty()) { // multiple securities matched by the symbol // TODO: Ask user which security should we match to mapSymbolName.insert(*symbol, filteredSecurities.first().name()); symbol = onlySymbols.erase(symbol); } else // no security matched, so leave it as unknown ++symbol; } // try to find symbols for names for (QSet::iterator name = onlyNames.begin(); name != onlyNames.end();) { QList filteredSecurities; foreach (const auto sec, securityList) { if ((*name).compare(sec.name(), Qt::CaseInsensitive) == 0) filteredSecurities.append(sec); // gather all securities that by matched by name } if (filteredSecurities.count() == 1) { // single security matched by the name so... mapSymbolName.insert(filteredSecurities.first().tradingSymbol(), *name); name = onlyNames.erase(name); // ...it's no longer unknown } else if (!filteredSecurities.isEmpty()) { // multiple securities matched by the name // TODO: Ask user which security should we match to mapSymbolName.insert(filteredSecurities.first().tradingSymbol(), *name); name = onlySymbols.erase(name); } else // no security matched, so leave it as unknown ++name; } return true; } -void CSVImporter::setupFieldDecimalSymbol(int col) { +void CSVImporterCore::setupFieldDecimalSymbol(int col) { m_file->m_parse->setDecimalSymbol(m_decimalSymbolIndexMap.value(col)); } -QList CSVImporter::getNumericalColumns() +QList CSVImporterCore::getNumericalColumns() { QList columns; switch(m_profile->type()) { case Profile::Banking: if (m_profile->m_colTypeNum.value(Column::Amount, -1) != -1) { columns << m_profile->m_colTypeNum.value(Column::Amount); } else { columns << m_profile->m_colTypeNum.value(Column::Debit); columns << m_profile->m_colTypeNum.value(Column::Credit); } break; case Profile::Investment: columns << m_profile->m_colTypeNum.value(Column::Amount); columns << m_profile->m_colTypeNum.value(Column::Price); columns << m_profile->m_colTypeNum.value(Column::Quantity); if (m_profile->m_colTypeNum.value(Column::Fee, -1) != -1) columns << m_profile->m_colTypeNum.value(Column::Fee); break; case Profile::CurrencyPrices: case Profile::StockPrices: columns << m_profile->m_colTypeNum.value(Column::Price); break; default: break; } return columns; } -bool CSVImporter::createStatement(MyMoneyStatement &st) +bool CSVImporterCore::createStatement(MyMoneyStatement &st) { switch (m_profile->type()) { case Profile::Banking: { if (!st.m_listTransactions.isEmpty()) // don't create statement if there is one return true; st.m_eType = eMyMoney::Statement::Type::None; if (m_autodetect.value(AutoAccountBank)) detectAccount(st); m_hashSet.clear(); BankingProfile *profile = dynamic_cast(m_profile); for (int row = m_profile->m_startLine; row <= m_profile->m_endLine; ++row) if (!processBankRow(st, profile, row)) { // parse fields st = MyMoneyStatement(); return false; } return true; break; } case Profile::Investment: { if (!st.m_listTransactions.isEmpty()) // don't create statement if there is one return true; st.m_eType = eMyMoney::Statement::Type::Investment; if (m_autodetect.value(AutoAccountInvest)) detectAccount(st); InvestmentProfile *profile = dynamic_cast(m_profile); if ((m_profile->m_colTypeNum.value(Column::Fee, -1) == -1 || m_profile->m_colTypeNum.value(Column::Fee, -1) >= m_file->m_columnCount) && !profile->m_feeRate.isEmpty()) // fee column has not been calculated so do it now calculateFee(); for (int row = m_profile->m_startLine; row <= m_profile->m_endLine; ++row) if (!processInvestRow(st, profile, row)) { // parse fields st = MyMoneyStatement(); return false; } for (QMap::const_iterator it = m_mapSymbolName.cbegin(); it != m_mapSymbolName.cend(); ++it) { MyMoneyStatement::Security security; security.m_strSymbol = it.key(); security.m_strName = it.value(); st.m_listSecurities.append(security); } return true; break; } default: case Profile::CurrencyPrices: case Profile::StockPrices: { if (!st.m_listPrices.isEmpty()) // don't create statement if there is one return true; st.m_eType = eMyMoney::Statement::Type::None; PricesProfile *profile = dynamic_cast(m_profile); for (int row = m_profile->m_startLine; row <= m_profile->m_endLine; ++row) if (!processPriceRow(st, profile, row)) { // parse fields st = MyMoneyStatement(); return false; } for (QMap::const_iterator it = m_mapSymbolName.cbegin(); it != m_mapSymbolName.cend(); ++it) { MyMoneyStatement::Security security; security.m_strSymbol = it.key(); security.m_strName = it.value(); st.m_listSecurities.append(security); } return true; } } return true; } void CSVProfile::readSettings(const KConfigGroup &profilesGroup) { - m_lastUsedDirectory = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfDirectory), QString()); - m_startLine = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfStartLine), 0); - m_trailerLines = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfTrailerLines), 0); - m_encodingMIBEnum = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfEncoding), 106 /* UTF-8 */); - - m_dateFormat = static_cast(profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfDateFormat), (int)DateFormat::YearMonthDay)); - m_textDelimiter = static_cast(profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfTextDelimiter), (int)TextDelimiter::DoubleQuote)); - m_fieldDelimiter = static_cast(profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfFieldDelimiter), (int)FieldDelimiter::Auto)); - m_decimalSymbol = static_cast(profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfDecimalSymbol), (int)DecimalSymbol::Auto)); + m_lastUsedDirectory = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfDirectory), QString()); + m_startLine = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfStartLine), 0); + m_trailerLines = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfTrailerLines), 0); + m_encodingMIBEnum = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfEncoding), 106 /* UTF-8 */); + + m_dateFormat = static_cast(profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfDateFormat), (int)DateFormat::YearMonthDay)); + m_textDelimiter = static_cast(profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfTextDelimiter), (int)TextDelimiter::DoubleQuote)); + m_fieldDelimiter = static_cast(profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfFieldDelimiter), (int)FieldDelimiter::Auto)); + m_decimalSymbol = static_cast(profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfDecimalSymbol), (int)DecimalSymbol::Auto)); initColNumType(); } void CSVProfile::writeSettings(KConfigGroup &profilesGroup) { QFileInfo fileInfo (m_lastUsedDirectory); if (fileInfo.isFile()) m_lastUsedDirectory = fileInfo.absolutePath(); if (m_lastUsedDirectory.startsWith(QDir::homePath())) // replace /home/user with ~/ for brevity m_lastUsedDirectory.replace(0, QDir::homePath().length(), QLatin1Char('~')); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfDirectory), m_lastUsedDirectory); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfEncoding), m_encodingMIBEnum); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfDateFormat), (int)m_dateFormat); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfFieldDelimiter), (int)m_fieldDelimiter); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfTextDelimiter), (int)m_textDelimiter); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfDecimalSymbol), (int)m_decimalSymbol); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfStartLine), m_startLine); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfTrailerLines), m_trailerLines); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfDirectory), m_lastUsedDirectory); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfEncoding), m_encodingMIBEnum); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfDateFormat), (int)m_dateFormat); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfFieldDelimiter), (int)m_fieldDelimiter); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfTextDelimiter), (int)m_textDelimiter); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfDecimalSymbol), (int)m_decimalSymbol); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfStartLine), m_startLine); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfTrailerLines), m_trailerLines); } bool BankingProfile::readSettings(const KSharedConfigPtr &config) { bool exists = true; - KConfigGroup profilesGroup(config, CSVImporter::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); + KConfigGroup profilesGroup(config, CSVImporterCore::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); if (!profilesGroup.exists()) exists = false; - m_colTypeNum[Column::Payee] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Payee), -1); - m_colTypeNum[Column::Number] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Number), -1); - m_colTypeNum[Column::Amount] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Amount), -1); - m_colTypeNum[Column::Debit] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Debit), -1); - m_colTypeNum[Column::Credit] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Credit), -1); - m_colTypeNum[Column::Date] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Date), -1); - m_colTypeNum[Column::Category] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Category), -1); + m_colTypeNum[Column::Payee] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Payee), -1); + m_colTypeNum[Column::Number] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Number), -1); + m_colTypeNum[Column::Amount] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Amount), -1); + m_colTypeNum[Column::Debit] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Debit), -1); + m_colTypeNum[Column::Credit] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Credit), -1); + m_colTypeNum[Column::Date] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Date), -1); + m_colTypeNum[Column::Category] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Category), -1); m_colTypeNum[Column::Memo] = -1; // initialize, otherwise random data may go here - m_oppositeSigns = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfOppositeSigns), 0); - m_memoColList = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Memo), QList()); + m_oppositeSigns = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfOppositeSigns), 0); + m_memoColList = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Memo), QList()); CSVProfile::readSettings(profilesGroup); return exists; } void BankingProfile::writeSettings(const KSharedConfigPtr &config) { - KConfigGroup profilesGroup(config, CSVImporter::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); + KConfigGroup profilesGroup(config, CSVImporterCore::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); CSVProfile::writeSettings(profilesGroup); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfOppositeSigns), m_oppositeSigns); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Payee), + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfOppositeSigns), m_oppositeSigns); + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Payee), m_colTypeNum.value(Column::Payee)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Number), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Number), m_colTypeNum.value(Column::Number)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Amount), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Amount), m_colTypeNum.value(Column::Amount)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Debit), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Debit), m_colTypeNum.value(Column::Debit)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Credit), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Credit), m_colTypeNum.value(Column::Credit)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Date), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Date), m_colTypeNum.value(Column::Date)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Category), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Category), m_colTypeNum.value(Column::Category)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Memo), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Memo), m_memoColList); profilesGroup.config()->sync(); } bool InvestmentProfile::readSettings(const KSharedConfigPtr &config) { bool exists = true; - KConfigGroup profilesGroup(config, CSVImporter::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); + KConfigGroup profilesGroup(config, CSVImporterCore::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); if (!profilesGroup.exists()) exists = false; - m_transactionNames[eMyMoney::Transaction::Action::Buy] = profilesGroup.readEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::Buy), + m_transactionNames[eMyMoney::Transaction::Action::Buy] = profilesGroup.readEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::Buy), QString(i18nc("Type of operation as in financial statement", "buy")).split(',', QString::SkipEmptyParts)); - m_transactionNames[eMyMoney::Transaction::Action::Sell] = profilesGroup.readEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::Sell), + m_transactionNames[eMyMoney::Transaction::Action::Sell] = profilesGroup.readEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::Sell), QString(i18nc("Type of operation as in financial statement", "sell,repurchase")).split(',', QString::SkipEmptyParts)); - m_transactionNames[eMyMoney::Transaction::Action::ReinvestDividend] = profilesGroup.readEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::ReinvestDividend), + m_transactionNames[eMyMoney::Transaction::Action::ReinvestDividend] = profilesGroup.readEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::ReinvestDividend), QString(i18nc("Type of operation as in financial statement", "reinvest,reinv,re-inv")).split(',', QString::SkipEmptyParts)); - m_transactionNames[eMyMoney::Transaction::Action::CashDividend] = profilesGroup.readEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::CashDividend), + m_transactionNames[eMyMoney::Transaction::Action::CashDividend] = profilesGroup.readEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::CashDividend), QString(i18nc("Type of operation as in financial statement", "dividend")).split(',', QString::SkipEmptyParts)); - m_transactionNames[eMyMoney::Transaction::Action::Interest] = profilesGroup.readEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::Interest), + m_transactionNames[eMyMoney::Transaction::Action::Interest] = profilesGroup.readEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::Interest), QString(i18nc("Type of operation as in financial statement", "interest,income")).split(',', QString::SkipEmptyParts)); - m_transactionNames[eMyMoney::Transaction::Action::Shrsin] = profilesGroup.readEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::Shrsin), + m_transactionNames[eMyMoney::Transaction::Action::Shrsin] = profilesGroup.readEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::Shrsin), QString(i18nc("Type of operation as in financial statement", "add,stock dividend,divd reinv,transfer in,re-registration in,journal entry")).split(',', QString::SkipEmptyParts)); - m_transactionNames[eMyMoney::Transaction::Action::Shrsout] = profilesGroup.readEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::Shrsout), + m_transactionNames[eMyMoney::Transaction::Action::Shrsout] = profilesGroup.readEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::Shrsout), QString(i18nc("Type of operation as in financial statement", "remove")).split(',', QString::SkipEmptyParts)); - m_colTypeNum[Column::Date] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Date), -1); - m_colTypeNum[Column::Type] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Type), -1); //use for type col. - m_colTypeNum[Column::Price] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Price), -1); - m_colTypeNum[Column::Quantity] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Quantity), -1); - m_colTypeNum[Column::Amount] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Amount), -1); - m_colTypeNum[Column::Name] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Name), -1); - m_colTypeNum[Column::Fee] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Fee), -1); - m_colTypeNum[Column::Symbol] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Symbol), -1); + m_colTypeNum[Column::Date] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Date), -1); + m_colTypeNum[Column::Type] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Type), -1); //use for type col. + m_colTypeNum[Column::Price] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Price), -1); + m_colTypeNum[Column::Quantity] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Quantity), -1); + m_colTypeNum[Column::Amount] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Amount), -1); + m_colTypeNum[Column::Name] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Name), -1); + m_colTypeNum[Column::Fee] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Fee), -1); + m_colTypeNum[Column::Symbol] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Symbol), -1); m_colTypeNum[Column::Memo] = -1; // initialize, otherwise random data may go here - m_feeIsPercentage = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfFeeIsPercentage), false); - m_feeRate = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfFeeRate), QString()); - m_minFee = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfMinFee), QString()); + m_feeIsPercentage = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfFeeIsPercentage), false); + m_feeRate = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfFeeRate), QString()); + m_minFee = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfMinFee), QString()); - m_memoColList = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Memo), QList()); - m_securityName = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfSecurityName), QString()); - m_securitySymbol = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfSecuritySymbol), QString()); - m_dontAsk = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfDontAsk), 0); - m_priceFraction = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfPriceFraction), 2); + m_memoColList = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Memo), QList()); + m_securityName = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfSecurityName), QString()); + m_securitySymbol = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfSecuritySymbol), QString()); + m_dontAsk = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfDontAsk), 0); + m_priceFraction = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfPriceFraction), 2); CSVProfile::readSettings(profilesGroup); return exists; } void InvestmentProfile::writeSettings(const KSharedConfigPtr &config) { - KConfigGroup profilesGroup(config, CSVImporter::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); + KConfigGroup profilesGroup(config, CSVImporterCore::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); CSVProfile::writeSettings(profilesGroup); - profilesGroup.writeEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::Buy), + profilesGroup.writeEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::Buy), m_transactionNames.value(eMyMoney::Transaction::Action::Buy)); - profilesGroup.writeEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::Sell), + profilesGroup.writeEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::Sell), m_transactionNames.value(eMyMoney::Transaction::Action::Sell)); - profilesGroup.writeEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::ReinvestDividend), + profilesGroup.writeEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::ReinvestDividend), m_transactionNames.value(eMyMoney::Transaction::Action::ReinvestDividend)); - profilesGroup.writeEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::CashDividend), + profilesGroup.writeEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::CashDividend), m_transactionNames.value(eMyMoney::Transaction::Action::CashDividend)); - profilesGroup.writeEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::Interest), + profilesGroup.writeEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::Interest), m_transactionNames.value(eMyMoney::Transaction::Action::Interest)); - profilesGroup.writeEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::Shrsin), + profilesGroup.writeEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::Shrsin), m_transactionNames.value(eMyMoney::Transaction::Action::Shrsin)); - profilesGroup.writeEntry(CSVImporter::m_transactionConfName.value(eMyMoney::Transaction::Action::Shrsout), + profilesGroup.writeEntry(CSVImporterCore::m_transactionConfName.value(eMyMoney::Transaction::Action::Shrsout), m_transactionNames.value(eMyMoney::Transaction::Action::Shrsout)); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfPriceFraction), m_priceFraction); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfFeeIsPercentage), m_feeIsPercentage); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfFeeRate), m_feeRate); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfMinFee), m_minFee); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfSecurityName), m_securityName); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfSecuritySymbol), m_securitySymbol); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfDontAsk), m_dontAsk); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfPriceFraction), m_priceFraction); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfFeeIsPercentage), m_feeIsPercentage); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfFeeRate), m_feeRate); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfMinFee), m_minFee); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfSecurityName), m_securityName); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfSecuritySymbol), m_securitySymbol); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfDontAsk), m_dontAsk); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Date), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Date), m_colTypeNum.value(Column::Date)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Type), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Type), m_colTypeNum.value(Column::Type)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Quantity), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Quantity), m_colTypeNum.value(Column::Quantity)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Amount), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Amount), m_colTypeNum.value(Column::Amount)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Price), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Price), m_colTypeNum.value(Column::Price)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Symbol), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Symbol), m_colTypeNum.value(Column::Symbol)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Name), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Name), m_colTypeNum.value(Column::Name)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Fee), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Fee), m_colTypeNum.value(Column::Fee)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Memo), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Memo), m_memoColList); profilesGroup.config()->sync(); } bool PricesProfile::readSettings(const KSharedConfigPtr &config) { bool exists = true; - KConfigGroup profilesGroup(config, CSVImporter::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); + KConfigGroup profilesGroup(config, CSVImporterCore::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); if (!profilesGroup.exists()) exists = false; - m_colTypeNum[Column::Date] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Date), -1); - m_colTypeNum[Column::Price] = profilesGroup.readEntry(CSVImporter::m_colTypeConfName.value(Column::Price), -1); - m_priceFraction = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfPriceFraction), 2); - m_securityName = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfSecurityName), QString()); - m_securitySymbol = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfSecuritySymbol), QString()); - m_currencySymbol = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfCurrencySymbol), QString()); - m_dontAsk = profilesGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfDontAsk), 0); + m_colTypeNum[Column::Date] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Date), -1); + m_colTypeNum[Column::Price] = profilesGroup.readEntry(CSVImporterCore::m_colTypeConfName.value(Column::Price), -1); + m_priceFraction = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfPriceFraction), 2); + m_securityName = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfSecurityName), QString()); + m_securitySymbol = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfSecuritySymbol), QString()); + m_currencySymbol = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfCurrencySymbol), QString()); + m_dontAsk = profilesGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfDontAsk), 0); CSVProfile::readSettings(profilesGroup); return exists; } void PricesProfile::writeSettings(const KSharedConfigPtr &config) { - KConfigGroup profilesGroup(config, CSVImporter::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); + KConfigGroup profilesGroup(config, CSVImporterCore::m_profileConfPrefix.value(type()) + QLatin1Char('-') + m_profileName); CSVProfile::writeSettings(profilesGroup); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Date), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Date), m_colTypeNum.value(Column::Date)); - profilesGroup.writeEntry(CSVImporter::m_colTypeConfName.value(Column::Price), + profilesGroup.writeEntry(CSVImporterCore::m_colTypeConfName.value(Column::Price), m_colTypeNum.value(Column::Price)); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfPriceFraction), m_priceFraction); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfSecurityName), m_securityName); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfSecuritySymbol), m_securitySymbol); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfCurrencySymbol), m_currencySymbol); - profilesGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfDontAsk), m_dontAsk); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfPriceFraction), m_priceFraction); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfSecurityName), m_securityName); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfSecuritySymbol), m_securitySymbol); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfCurrencySymbol), m_currencySymbol); + profilesGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfDontAsk), m_dontAsk); profilesGroup.config()->sync(); } CSVFile::CSVFile() { m_parse = new Parse; m_model = new QStandardItemModel; } CSVFile::~CSVFile() { delete m_parse; delete m_model; } void CSVFile::getStartEndRow(CSVProfile *profile) { profile->m_endLine = m_rowCount - 1; if (profile->m_endLine > profile->m_trailerLines) profile->m_endLine -= profile->m_trailerLines; if (profile->m_startLine > profile->m_endLine) // Don't allow m_startLine > m_endLine profile->m_startLine = profile->m_endLine; } void CSVFile::getColumnCount(CSVProfile *profile, const QStringList &rows) { if (rows.isEmpty()) return; QVector delimiterIndexes; if (profile->m_fieldDelimiter == FieldDelimiter::Auto) delimiterIndexes = QVector{FieldDelimiter::Comma, FieldDelimiter::Semicolon, FieldDelimiter::Colon, FieldDelimiter::Tab}; // include all delimiters to test or ... else delimiterIndexes = QVector{profile->m_fieldDelimiter}; // ... only the one specified QList totalDelimiterCount({0, 0, 0, 0}); // Total in file for each delimiter QList thisDelimiterCount({0, 0, 0, 0}); // Total in this line for each delimiter int colCount = 0; // Total delimiters in this line FieldDelimiter possibleDelimiter = FieldDelimiter::Comma; m_columnCount = 0; foreach (const auto row, rows) { foreach(const auto delimiterIndex, delimiterIndexes) { m_parse->setFieldDelimiter(delimiterIndex); colCount = m_parse->parseLine(row).count(); // parse each line using each delimiter if (colCount > thisDelimiterCount.at((int)delimiterIndex)) thisDelimiterCount[(int)delimiterIndex] = colCount; if (thisDelimiterCount[(int)delimiterIndex] > m_columnCount) m_columnCount = thisDelimiterCount.at((int)delimiterIndex); totalDelimiterCount[(int)delimiterIndex] += colCount; if (totalDelimiterCount.at((int)delimiterIndex) > totalDelimiterCount.at((int)possibleDelimiter)) possibleDelimiter = delimiterIndex; } } if (delimiterIndexes.count() != 1) // if purpose was to autodetect... profile->m_fieldDelimiter = possibleDelimiter; // ... then change field delimiter m_parse->setFieldDelimiter(profile->m_fieldDelimiter); // restore original field delimiter } bool CSVFile::getInFileName(QString inFileName) { QFileInfo fileInfo; if (!inFileName.isEmpty()) { if (inFileName.startsWith(QLatin1Char('~'))) inFileName.replace(0, 1, QDir::homePath()); fileInfo = QFileInfo(inFileName); if (fileInfo.isFile()) { // if it is file... if (fileInfo.exists()) { // ...and exists... m_inFileName = inFileName; // ...then set as valid filename return true; // ...and return success... } else { // ...but if not... fileInfo.setFile(fileInfo.absolutePath()); //...then set start directory to directory of that file... if (!fileInfo.exists()) //...and if it doesn't exist too... fileInfo.setFile(QDir::homePath()); //...then set start directory to home path } } else if (fileInfo.isDir()) { if (fileInfo.exists()) fileInfo = QFileInfo(inFileName); else fileInfo.setFile(QDir::homePath()); } } else fileInfo = QFileInfo(QDir::homePath()); QPointer dialog = new QFileDialog(nullptr, QString(), fileInfo.absoluteFilePath(), i18n("CSV Files (*.csv)")); dialog->setFileMode(QFileDialog::ExistingFile); QUrl url; if (dialog->exec() == QDialog::Accepted) url = dialog->selectedUrls().first(); delete dialog; if (url.isEmpty()) { m_inFileName.clear(); return false; } else m_inFileName = url.toDisplayString(QUrl::PreferLocalFile); return true; } void CSVFile::setupParser(CSVProfile *profile) { if (profile->m_decimalSymbol != DecimalSymbol::Auto) m_parse->setDecimalSymbol(profile->m_decimalSymbol); m_parse->setFieldDelimiter(profile->m_fieldDelimiter); m_parse->setTextDelimiter(profile->m_textDelimiter); } void CSVFile::readFile(CSVProfile *profile) { QFile inFile(m_inFileName); if (!inFile.exists()) return; inFile.open(QIODevice::ReadOnly); QTextStream inStream(&inFile); QTextCodec* codec = QTextCodec::codecForMib(profile->m_encodingMIBEnum); inStream.setCodec(codec); QString buf = inStream.readAll(); inFile.close(); m_parse->setTextDelimiter(profile->m_textDelimiter); QStringList rows = m_parse->parseFile(buf); // parse the buffer m_rowCount = m_parse->lastLine(); // won't work without above line getColumnCount(profile, rows); getStartEndRow(profile); // prepare model from rows having rowCount and columnCount m_model->clear(); for (int i = 0; i < m_rowCount; ++i) { QList itemList; QStringList columns = m_parse->parseLine(rows.takeFirst()); // take instead of read from rows to preserve memory for (int j = 0; j < m_columnCount; ++j) itemList.append(new QStandardItem(columns.value(j, QString()))); m_model->appendRow(itemList); } } diff --git a/kmymoney/plugins/csv/import/core/csvimporter.h b/kmymoney/plugins/csv/import/core/csvimportercore.h similarity index 95% rename from kmymoney/plugins/csv/import/core/csvimporter.h rename to kmymoney/plugins/csv/import/core/csvimportercore.h index 3ef65578f..b2436beab 100644 --- a/kmymoney/plugins/csv/import/core/csvimporter.h +++ b/kmymoney/plugins/csv/import/core/csvimportercore.h @@ -1,362 +1,362 @@ /*************************************************************************** - csvimporter.h + csvimportercore.h ------------------- begin : Sun May 21 2017 copyright : (C) 2015 by Allan Anderson email : agander93@gmail.com copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ -#ifndef CSVIMPORTER_H -#define CSVIMPORTER_H +#ifndef CSVIMPORTERCORE_H +#define CSVIMPORTERCORE_H // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // QT Includes #include // Project Includes #include "mymoneystatement.h" #include "csvenums.h" -#include "csv/import/core/kmm_csvimport_core_export.h" +#include "csv/import/core/kmm_csvimportercore_export.h" class MyMoneyAccount; class KConfigGroup; class QStandardItemModel; class Parse; class ConvertDate; namespace eMyMoney { namespace Account { enum class Type; } } enum autodetectTypeE { AutoFieldDelimiter, AutoDecimalSymbol, AutoDateFormat, AutoAccountInvest, AutoAccountBank }; enum miscSettingsE { ConfDirectory, ConfEncoding, ConfDateFormat, ConfFieldDelimiter, ConfTextDelimiter, ConfDecimalSymbol, ConfStartLine, ConfTrailerLines, ConfOppositeSigns, ConfFeeIsPercentage, ConfFeeRate, ConfMinFee, ConfSecurityName, ConfSecuritySymbol, ConfCurrencySymbol, ConfPriceFraction, ConfDontAsk, ConfHeight, ConfWidth }; enum validationResultE { ValidActionType, InvalidActionValues, NoActionType }; -class KMM_CSVIMPORT_CORE_NO_EXPORT CSVProfile +class KMM_CSVIMPORTERCORE_NO_EXPORT CSVProfile { protected: CSVProfile() {} CSVProfile(const QString &profileName, int encodingMIBEnum, int startLine, int trailerLines, DateFormat dateFormat, FieldDelimiter fieldDelimiter, TextDelimiter textDelimiter, DecimalSymbol decimalSymbol, QMap &colTypeNum) : m_profileName(profileName), m_encodingMIBEnum(encodingMIBEnum), m_startLine(startLine), m_trailerLines(trailerLines), m_dateFormat(dateFormat), m_fieldDelimiter(fieldDelimiter), m_textDelimiter(textDelimiter), m_decimalSymbol(decimalSymbol), m_colTypeNum(colTypeNum) { initColNumType(); } void readSettings(const KConfigGroup &profilesGroup); void writeSettings(KConfigGroup &profilesGroup); void initColNumType() { for (auto it = m_colTypeNum.constBegin(); it != m_colTypeNum.constEnd(); ++it) m_colNumType.insert(it.value(), it.key()); } public: virtual ~CSVProfile() {} virtual Profile type() const = 0; virtual bool readSettings(const KSharedConfigPtr &config) = 0; virtual void writeSettings(const KSharedConfigPtr &config) = 0; QString m_profileName; QString m_lastUsedDirectory; int m_encodingMIBEnum; int m_startLine; int m_endLine; int m_trailerLines; DateFormat m_dateFormat; FieldDelimiter m_fieldDelimiter; TextDelimiter m_textDelimiter; DecimalSymbol m_decimalSymbol; QMap m_colTypeNum; QMap m_colNumType; }; -class KMM_CSVIMPORT_CORE_EXPORT BankingProfile : public CSVProfile +class KMM_CSVIMPORTERCORE_EXPORT BankingProfile : public CSVProfile { public: explicit BankingProfile() : CSVProfile() {} BankingProfile(QString profileName, int encodingMIBEnum, int startLine, int trailerLines, DateFormat dateFormat, FieldDelimiter fieldDelimiter, TextDelimiter textDelimiter, DecimalSymbol decimalSymbol, QMap colTypeNum, bool oppositeSigns) : CSVProfile(profileName, encodingMIBEnum, startLine, trailerLines, dateFormat, fieldDelimiter, textDelimiter, decimalSymbol, colTypeNum), m_oppositeSigns(oppositeSigns) {} Profile type() const { return Profile::Banking; } bool readSettings(const KSharedConfigPtr &config); void writeSettings(const KSharedConfigPtr &config); QList m_memoColList; bool m_oppositeSigns; }; -class KMM_CSVIMPORT_CORE_EXPORT InvestmentProfile : public CSVProfile +class KMM_CSVIMPORTERCORE_EXPORT InvestmentProfile : public CSVProfile { public: explicit InvestmentProfile() : CSVProfile() {} InvestmentProfile(QString profileName, int encodingMIBEnum, int startLine, int trailerLines, DateFormat dateFormat, FieldDelimiter fieldDelimiter, TextDelimiter textDelimiter, DecimalSymbol decimalSymbol, QMap colTypeNum, int priceFraction, QMap transactionNames) : CSVProfile(profileName, encodingMIBEnum, startLine, trailerLines, dateFormat, fieldDelimiter, textDelimiter, decimalSymbol, colTypeNum), m_transactionNames(transactionNames), m_priceFraction(priceFraction), m_feeIsPercentage(false) {} Profile type() const { return Profile::Investment; } bool readSettings(const KSharedConfigPtr &config); void writeSettings(const KSharedConfigPtr &config); QMap m_transactionNames; QString m_feeRate; QString m_minFee; QString m_securityName; QString m_securitySymbol; QList m_memoColList; int m_priceFraction; int m_dontAsk; bool m_feeIsPercentage; }; -class KMM_CSVIMPORT_CORE_EXPORT PricesProfile : public CSVProfile +class KMM_CSVIMPORTERCORE_EXPORT PricesProfile : public CSVProfile { public: explicit PricesProfile() : CSVProfile() {} explicit PricesProfile(const Profile profileType) : CSVProfile(), m_profileType(profileType) {} PricesProfile(QString profileName, int encodingMIBEnum, int startLine, int trailerLines, DateFormat dateFormat, FieldDelimiter fieldDelimiter, TextDelimiter textDelimiter, DecimalSymbol decimalSymbol, QMap colTypeNum, int priceFraction, Profile profileType) : CSVProfile(profileName, encodingMIBEnum, startLine, trailerLines, dateFormat, fieldDelimiter, textDelimiter, decimalSymbol, colTypeNum), m_priceFraction(priceFraction), m_profileType(profileType) {} Profile type() const { return m_profileType; } bool readSettings(const KSharedConfigPtr &config); void writeSettings(const KSharedConfigPtr &config); QString m_securityName; QString m_securitySymbol; QString m_currencySymbol; int m_dontAsk; int m_priceFraction; Profile m_profileType; }; -class KMM_CSVIMPORT_CORE_EXPORT CSVFile +class KMM_CSVIMPORTERCORE_EXPORT CSVFile { public: explicit CSVFile(); ~CSVFile(); void getStartEndRow(CSVProfile *profile); /** * If delimiter = -1 this method tries different field * delimiters to get the one with which file has the most columns. * Otherwise it gets only column count for specified delimiter. */ void getColumnCount(CSVProfile *profile, const QStringList &rows); /** * This method gets the filename of * the financial statement. */ bool getInFileName(QString startDir = QString()); void setupParser(CSVProfile *profile); /** * This method gets file into buffer * It will laso store file's end column and row. */ void readFile(CSVProfile *profile); Parse *m_parse; QStandardItemModel *m_model; QString m_inFileName; int m_columnCount; int m_rowCount; }; -class KMM_CSVIMPORT_CORE_EXPORT CSVImporter +class KMM_CSVIMPORTERCORE_EXPORT CSVImporterCore { public: - explicit CSVImporter(); - ~CSVImporter(); + explicit CSVImporterCore(); + ~CSVImporterCore(); /** * This method will silently import csv file. Main purpose of this method are online quotes. */ MyMoneyStatement unattendedImport(const QString &filename, CSVProfile *profile); static KSharedConfigPtr configFile(); void profileFactory(const Profile type, const QString &name); void readMiscSettings(); /** * This method ensures that configuration file contains all necessary fields * and that it is up to date. */ void validateConfigFile(); /** * This method contains routines to update configuration file * from kmmVer to latest. */ bool updateConfigFile(QList &confVer); /** * This method will update [ProfileNames] in csvimporterrrc */ static bool profilesAction(const Profile type, const ProfileAction action, const QString &name, const QString &newname); /** * This methods will ensure that fields of input rows are correct. */ bool validateDateFormat(const int col); bool validateDecimalSymbols(const QList &columns); bool validateCurrencies(const PricesProfile *profile); bool validateSecurity(const PricesProfile *profile); bool validateSecurity(const InvestmentProfile *profile); bool validateSecurities(); validationResultE validateActionType(MyMoneyStatement::Transaction &tr); /** * This method will try to detect decimal symbol in input column. */ int detectDecimalSymbols(const QList &columns); DecimalSymbol detectDecimalSymbol(const int col, const QString &exclude); /** * This method will try to detect account from csv header. */ QList findAccounts(const QList &accountTypes, const QString &statementHeader); bool detectAccount(MyMoneyStatement &st); /** * This methods will evaluate input row and append it to a statement. */ bool processBankRow(MyMoneyStatement &st, const BankingProfile *profile, const int row); bool processInvestRow(MyMoneyStatement &st, const InvestmentProfile *profile, const int row); bool processPriceRow(MyMoneyStatement &st, const PricesProfile *profile, const int row); /** * This methods will evaluate fields of input row and return statement's useful value. */ QDate processDateField(const int row, const int col); MyMoneyMoney processCreditDebit(QString &credit, QString &debit ); MyMoneyMoney processPriceField(const InvestmentProfile *profile, const int row, const int col); MyMoneyMoney processPriceField(const PricesProfile *profile, const int row, const int col); MyMoneyMoney processAmountField(const CSVProfile *profile, const int row, const int col); MyMoneyMoney processQuantityField(const CSVProfile *profile, const int row, const int col); eMyMoney::Transaction::Action processActionTypeField(const InvestmentProfile *profile, const int row, const int col); /** * This method creates valid set of possible transactions * according to quantity, amount and price */ QList createValidActionTypes(MyMoneyStatement::Transaction &tr); /** * This method will add fee column to model based on amount and fee rate. */ bool calculateFee(); /** * This method gets securities from investment statement and * tries to get pairs of symbol and name either * from KMM or from statement data. * In case it's not successful onlySymbols and onlyNames won't be empty. */ bool sortSecurities(QSet& onlySymbols, QSet& onlyNames, QMap& mapSymbolName); /** * Helper method to set decimal symbol in case it was set to autodetect. */ void setupFieldDecimalSymbol(int col); /** * Helper method to get all column numbers that were pointed as nummeric */ QList getNumericalColumns(); bool createStatement(MyMoneyStatement &st); ConvertDate *m_convertDate; CSVFile *m_file; CSVProfile *m_profile; KSharedConfigPtr m_config; bool m_isActionTypeValidated; QList m_priceFractions; QSet m_hashSet; QMap m_decimalSymbolIndexMap; QMap m_mapSymbolName; QMap m_autodetect; static const QHash m_colTypeConfName; static const QHash m_profileConfPrefix; static const QHash m_transactionConfName; static const QHash m_miscSettingsConfName; static const QString m_confProfileNames; static const QString m_confPriorName; static const QString m_confMiscName; }; #endif diff --git a/kmymoney/plugins/csv/import/core/csvutil.h b/kmymoney/plugins/csv/import/core/csvutil.h index 87f422d02..8dab74468 100644 --- a/kmymoney/plugins/csv/import/core/csvutil.h +++ b/kmymoney/plugins/csv/import/core/csvutil.h @@ -1,81 +1,81 @@ /*************************************************************************** csvutil.h ----------- begin : Sat Jan 01 2010 copyright : (C) 2010 by Allan Anderson email : agander93@gmail.com copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef CSVUTIL_H #define CSVUTIL_H #include #include "csvenums.h" -#include "csv/import/core/kmm_csvimport_core_export.h" +#include "csv/import/core/kmm_csvimportercore_export.h" -class KMM_CSVIMPORT_CORE_EXPORT Parse +class KMM_CSVIMPORTERCORE_EXPORT Parse { public: Parse(); ~Parse(); /** * This method is used to parse each line of data, splitting it into * separate fields, via the field delimiter character. It also detects * where a string has been erroneously split because it contains one or * more 'thousand separators' which happen to be the same as the field * delimiter, and re-assembles the string. */ QStringList parseLine(const QString &data); QStringList parseFile(const QString &buf); QChar decimalSymbol(const DecimalSymbol _d); /** * Check for presence of the selected decimal symbol * and evaluate if the proposed conversion is valid. * If so, change the symbol. */ QString possiblyReplaceSymbol(const QString& str); void setFieldDelimiter(const FieldDelimiter _d); void setTextDelimiter(const TextDelimiter _d); void setDecimalSymbol(const DecimalSymbol _d); bool invalidConversion(); int lastLine(); private : QVector m_fieldDelimiters; QVector m_textDelimiters; QVector m_decimalSymbols; QVector m_thousandsSeparators; QChar m_fieldDelimiter; QChar m_textDelimiter; QChar m_decimalSymbol; QChar m_thousandsSeparator; int m_lastLine; bool m_symbolFound; bool m_invalidConversion; }; #endif diff --git a/kmymoney/plugins/csv/import/core/tests/CMakeLists.txt b/kmymoney/plugins/csv/import/core/tests/CMakeLists.txt index 9e064ce4f..71eb37c63 100644 --- a/kmymoney/plugins/csv/import/core/tests/CMakeLists.txt +++ b/kmymoney/plugins/csv/import/core/tests/CMakeLists.txt @@ -1,21 +1,21 @@ include(ECMAddTests) add_library(csvimport_testcommon STATIC csvimporttestcommon.cpp ) target_link_libraries(csvimport_testcommon PUBLIC Qt5::Core kmm_mymoney ) file(GLOB tests_sources "*-test.cpp") ecm_add_tests(${tests_sources} NAME_PREFIX "csvimport-" LINK_LIBRARIES Qt5::Test - kmm_csvimport_core + kmm_csvimportercore csvimport_testcommon ) diff --git a/kmymoney/plugins/csv/import/core/tests/csvimporter-test.cpp b/kmymoney/plugins/csv/import/core/tests/csvimportercore-test.cpp similarity index 94% rename from kmymoney/plugins/csv/import/core/tests/csvimporter-test.cpp rename to kmymoney/plugins/csv/import/core/tests/csvimportercore-test.cpp index 0f7ad3b04..b8f651211 100644 --- a/kmymoney/plugins/csv/import/core/tests/csvimporter-test.cpp +++ b/kmymoney/plugins/csv/import/core/tests/csvimportercore-test.cpp @@ -1,331 +1,330 @@ /*************************************************************************** - csvimporter-test.cpp + csvimportercore.cpp ------------------- copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "csvimporter-test.h" +#include "csvimportercore-test.h" #include -#include #include "mymoneyfile.h" #include "mymoneyaccount.h" #include - +#include "csvimportercore.h" #include "csvimporttestcommon.h" -QTEST_GUILESS_MAIN(CsvImporterTest) +QTEST_GUILESS_MAIN(CSVImporterCoreTest) -void CsvImporterTest::initTestCase() +void CSVImporterCoreTest::initTestCase() { // setup the MyMoneyMoney locale settings according to the KDE settings MyMoneyMoney::setThousandSeparator(QLocale().groupSeparator()); MyMoneyMoney::setDecimalSeparator(QLocale().decimalPoint()); } -void CsvImporterTest::init() +void CSVImporterCoreTest::init() { storage = new MyMoneySeqAccessMgr; file = MyMoneyFile::instance(); file->attachStorage(storage); - csvImporter = new CSVImporter; + csvImporter = new CSVImporterCore; csvImporter->m_mapSymbolName.insert("STK1", "Stock 1"); csvImporter->m_mapSymbolName.insert("STK2", "Stock 2"); csvImporter->m_mapSymbolName.insert("STK3", "Stock 3"); investmentProfile = new InvestmentProfile ("investment", 106, 1, 0, DateFormat::YearMonthDay, FieldDelimiter::Semicolon, TextDelimiter::DoubleQuote, DecimalSymbol::Dot, QMap {{Column::Date, 0}, {Column::Name, 1}, {Column::Type, 2}, {Column::Quantity, 3}, {Column::Price, 4}, {Column::Amount, 5}}, 2, QMap { {eMyMoney::Transaction::Action::Buy, QStringList {"buy"}}, {eMyMoney::Transaction::Action::Sell, QStringList {"sell"}}} ); pricesProfile = new PricesProfile ("price source", 106, 1, 0, DateFormat::YearMonthDay, FieldDelimiter::Comma, TextDelimiter::DoubleQuote, DecimalSymbol::Dot, QMap{{Column::Date, 0}, {Column::Price, 4}}, 2, Profile::StockPrices); amountProfile = new BankingProfile ("amount", 106, 1, 0, DateFormat::MonthDayYear, FieldDelimiter::Comma, TextDelimiter::DoubleQuote, DecimalSymbol::Dot, QMap{{Column::Date, 1}, {Column::Memo, 2}, {Column::Amount, 3}, {Column::Category, 4}}, false); debitCreditProfile = new BankingProfile ("debit credit", 106, 1, 0, DateFormat::MonthDayYear, FieldDelimiter::Comma, TextDelimiter::DoubleQuote, DecimalSymbol::Dot, QMap{{Column::Date, 1}, {Column::Memo, 2}, {Column::Debit, 3}, {Column::Credit, 4}, {Column::Category, 5}}, false); } -void CsvImporterTest::cleanup() +void CSVImporterCoreTest::cleanup() { delete investmentProfile; delete pricesProfile; delete amountProfile; delete csvImporter; file->detachStorage(storage); delete storage; } -void CsvImporterTest::testBasicPriceTable() +void CSVImporterCoreTest::testBasicPriceTable() { QString csvContent; csvContent += QLatin1String("Date,Open,High,Low,Close,Volume\n"); csvContent += QLatin1String("2017-08-01,1.23,2.34,3.45,4.56,2\n"); csvContent += QLatin1String("2017-08-02,5.67,6.78,7.89,9.10,2\n"); csvContent += QLatin1String("2017-08-03,2.23,3.34,4.45,5.56,2\n"); QString filename("basic-price-table.csv"); writeStatementToCSV(csvContent, filename); pricesProfile->m_securityName = QLatin1String("APPLE"); auto st = csvImporter->unattendedImport(filename, pricesProfile); QVERIFY(st.m_eType == eMyMoney::Statement::Type::None); QVERIFY(st.m_listPrices.count() == 3); QVERIFY(st.m_listPrices[2].m_date == QDate(2017, 8, 3)); QCOMPARE(st.m_listPrices[2].m_amount.toString(), MyMoneyMoney(5.56).toString()); QVERIFY(st.m_listPrices[2].m_sourceName == "price source"); QVERIFY(st.m_listPrices[2].m_strSecurity == pricesProfile->m_securityName); } -void CsvImporterTest::testPriceFractionSetting() { +void CSVImporterCoreTest::testPriceFractionSetting() { QString csvContent; csvContent += QLatin1String("Date;Open;High;Low;Close;Volume\n"); csvContent += QLatin1String(";;;;;\n"); csvContent += QLatin1String("8/1/2017;1,234;2,345;3,456;4,567;2\n"); csvContent += QLatin1String("8/2/2017;5,679;6,789;7,891;9,101;2\n"); csvContent += QLatin1String("8/3/2017;2,234;3,345;4,456;5,567;2\n"); csvContent += QLatin1String("8/4/2017;3,456;4,567;5,678;6,789;2\n"); QString filename("price-fraction-setting.csv"); writeStatementToCSV(csvContent, filename); pricesProfile->m_securityName = QLatin1String("APPLE"); pricesProfile->m_dateFormat = DateFormat::MonthDayYear; pricesProfile->m_fieldDelimiter = FieldDelimiter::Semicolon; pricesProfile->m_decimalSymbol = DecimalSymbol::Comma; pricesProfile->m_priceFraction = 1; // price *= 0.1 pricesProfile->m_startLine = 2; pricesProfile->m_trailerLines = 1; auto st = csvImporter->unattendedImport(filename, pricesProfile); QVERIFY(st.m_listPrices.count() == 3); QVERIFY(st.m_listPrices[2].m_date == QDate(2017, 8, 3)); QVERIFY(st.m_listPrices[2].m_amount == MyMoneyMoney(0.5567, 10000)); // user reported that visible price (5.567) should be treated as a fraction of real price (0.5567) } -void CsvImporterTest::testImportByDebitCredit() +void CSVImporterCoreTest::testImportByDebitCredit() { QString csvContent; csvContent += QLatin1String("\"Trans Date\",\"Post Date\",\"Description\",\"Debit\",\"Credit\",\"Category\"\n"); csvContent += QLatin1String("05/16/2016,05/17/2016,FOO1,1.234,0.00,BAR\n"); // debit is intentionally positive here csvContent += QLatin1String("06/17/2016,06/18/2016,FOO2,0,90.12,BAR\n"); csvContent += QLatin1String("07/18/2016,07/19/2016,FOO3,-910.12,,BAR\n"); csvContent += QLatin1String("08/19/2016,08/20/2016,FOO4,,,BAR\n"); csvContent += QLatin1String("09/20/2016,09/21/2016,FOO5,0,0,BAR\n"); QString filename("import-by-debit-credit.csv"); writeStatementToCSV(csvContent, filename); auto st = csvImporter->unattendedImport(filename, debitCreditProfile); QVERIFY(st.m_listTransactions.count() == 5); QVERIFY(st.m_listTransactions[0].m_amount == MyMoneyMoney(-1.234, 1000)); QVERIFY(st.m_listTransactions[0].m_listSplits.count() == 1); QVERIFY(st.m_listTransactions[0].m_listSplits.first().m_amount == MyMoneyMoney(1.234, 1000)); QVERIFY(st.m_listTransactions[1].m_amount == MyMoneyMoney(90.12)); QVERIFY(st.m_listTransactions[2].m_amount == MyMoneyMoney(-910.12)); QVERIFY(st.m_listTransactions[3].m_amount == MyMoneyMoney()); QVERIFY(st.m_listTransactions[4].m_amount == MyMoneyMoney()); } -void CsvImporterTest::testImportByAmount() +void CSVImporterCoreTest::testImportByAmount() { QString csvContent; csvContent += QLatin1String("\"Trans Date\",\"Post Date\",\"Description\",\"Amount\",\"Category\"\n"); csvContent += QLatin1String("05/16/2016,05/17/2016,FOO1,1.234,BAR\n"); csvContent += QLatin1String("06/17/2016,06/18/2016,FOO2,56.78,BAR\n"); csvContent += QLatin1String("07/18/2016,07/19/2016,FOO3,910.12,BAR\n"); QString filename("import-by-amount.csv"); writeStatementToCSV(csvContent, filename); auto st = csvImporter->unattendedImport(filename, amountProfile); QVERIFY(st.m_listTransactions.count() == 3); QVERIFY(st.m_listTransactions[1].m_datePosted == QDate(2016, 6, 18)); QVERIFY(st.m_listTransactions[1].m_strMemo == "FOO2"); QVERIFY(st.m_listTransactions[1].m_listSplits.count() == 1); QVERIFY(st.m_listTransactions[1].m_listSplits.first().m_strCategoryName == "BAR"); QVERIFY(st.m_listTransactions[1].m_listSplits.first().m_amount == MyMoneyMoney(-56.78)); QVERIFY(st.m_listTransactions[1].m_amount == MyMoneyMoney(56.78)); } -void CsvImporterTest::testImportByName() +void CSVImporterCoreTest::testImportByName() { auto stockNames = csvImporter->m_mapSymbolName.values(); auto stockSymbols = csvImporter->m_mapSymbolName.keys(); auto csvContent = csvDataset(0); QString filename("import-by-name.csv"); writeStatementToCSV(csvContent, filename); auto st = csvImporter->unattendedImport(filename, investmentProfile); QVERIFY(st.m_eType == eMyMoney::Statement::Type::Investment); QVERIFY(st.m_listSecurities.count() == 3); QVERIFY(st.m_listSecurities[0].m_strName == stockNames.at(0)); QVERIFY(st.m_listSecurities[0].m_strSymbol == stockSymbols.at(0)); QVERIFY(st.m_listTransactions.count() == 3); QVERIFY(st.m_listTransactions[0].m_datePosted == QDate(2017, 8, 1)); QVERIFY(st.m_listTransactions[0].m_amount == MyMoneyMoney(-125)); // KMM handled correctly positive amount in buy transaction QVERIFY(st.m_listTransactions[0].m_shares == MyMoneyMoney(100)); QVERIFY(st.m_listTransactions[0].m_strSecurity == stockNames.at(0)); QVERIFY(st.m_listTransactions[0].m_strSymbol == stockSymbols.at(0)); } -void CsvImporterTest::testImportBySymbol() +void CSVImporterCoreTest::testImportBySymbol() { auto stockNames = csvImporter->m_mapSymbolName.values(); auto stockSymbols = csvImporter->m_mapSymbolName.keys(); auto csvContent = csvDataset(0); for (auto i = 0; i < stockNames.count(); ++i) csvContent.replace(stockNames.at(i), stockSymbols.at(i)); csvContent.replace("Name", "Symbol"); QString filename("import-by-symbol.csv"); writeStatementToCSV(csvContent, filename); investmentProfile->m_colTypeNum.remove(Column::Name); investmentProfile->m_colNumType.remove(1); investmentProfile->m_colTypeNum.insert(Column::Symbol, 1); investmentProfile->m_colNumType.insert(1, Column::Symbol); auto st = csvImporter->unattendedImport(filename, investmentProfile); QVERIFY(st.m_listSecurities.count() == 3); QVERIFY(st.m_listTransactions.count() == 3); QVERIFY(st.m_listTransactions[0].m_strSecurity == stockNames.at(0)); QVERIFY(st.m_listTransactions[0].m_strSymbol == stockSymbols.at(0)); } -void CsvImporterTest::testFeeColumn() +void CSVImporterCoreTest::testFeeColumn() { auto csvContent = csvDataset(0); QString filename("fee-column.csv"); writeStatementToCSV(csvContent, filename); investmentProfile->m_colTypeNum.insert(Column::Fee, 6); investmentProfile->m_colNumType.insert(6, Column::Fee); auto st = csvImporter->unattendedImport(filename, investmentProfile); QVERIFY(st.m_listTransactions[0].m_amount == MyMoneyMoney(-129)); // new_amount = original_amount + fee QVERIFY(st.m_listTransactions[0].m_fees == MyMoneyMoney(4)); // fee taken literally QVERIFY(st.m_listTransactions[1].m_amount == MyMoneyMoney(450)); QVERIFY(st.m_listTransactions[1].m_fees == MyMoneyMoney(6)); investmentProfile->m_feeIsPercentage = true; st = csvImporter->unattendedImport(filename, investmentProfile); QVERIFY(st.m_listTransactions[0].m_amount == MyMoneyMoney(-130)); // new_amount = original_amount (1 + fee/100) QVERIFY(st.m_listTransactions[0].m_fees == MyMoneyMoney(5)); // new_fee = original_amount * fee/100 } -void CsvImporterTest::testAutoDecimalSymbol() +void CSVImporterCoreTest::testAutoDecimalSymbol() { auto csvContent = csvDataset(0); csvContent += QLatin1String("2017-08-04-12.02.10;Stock 1;sell;101;1.25;126,25;4\n"); // mixed decimal symbols are on purpose here QString filename("auto-decimal-symbol.csv"); writeStatementToCSV(csvContent, filename); investmentProfile->m_decimalSymbol = DecimalSymbol::Auto; auto st = csvImporter->unattendedImport(filename, investmentProfile); QVERIFY(st.m_listTransactions.count() == 4); QVERIFY(st.m_listTransactions[3].m_datePosted == QDate(2017, 8, 4)); QVERIFY(st.m_listTransactions[3].m_amount == MyMoneyMoney(126.25)); QVERIFY(st.m_listTransactions[3].m_shares == MyMoneyMoney(101)); } -void CsvImporterTest::testInvAccountAutodetection() +void CSVImporterCoreTest::testInvAccountAutodetection() { MyMoneyFileTransaction ft; makeAccount("Eas", "123", eMyMoney::Account::Type::Investment, QDate(2017, 8, 1), file->asset().id()); makeAccount("BigInvestments", "", eMyMoney::Account::Type::Investment, QDate(2017, 8, 1), file->asset().id()); makeAccount("BigInvestments", "1234567890", eMyMoney::Account::Type::Investment, QDate(2017, 8, 1), file->asset().id()); makeAccount("EasyAccount", "", eMyMoney::Account::Type::Investment, QDate(2017, 8, 1), file->asset().id()); auto toBeClosedAccID = makeAccount("EasyAccount", "123456789", eMyMoney::Account::Type::Investment, QDate(2017, 8, 1), file->asset().id()); // this account has the most characters matching the statement auto accID = makeAccount("Easy", "123456789", eMyMoney::Account::Type::Investment, QDate(2017, 8, 1), file->asset().id()); makeAccount("EasyAccount", "123456789", eMyMoney::Account::Type::Checkings, QDate(2017, 8, 1), file->asset().id()); ft.commit(); auto csvContent = csvDataset(0); csvContent.prepend("Bank name:;\n"); csvContent.prepend("BigInvestments;\n"); csvContent.prepend("Account number:;\n"); csvContent.prepend("123456789;\n"); csvContent.prepend("Account name:;\n"); csvContent.prepend("EasyAccount;\n"); QString filename("account-autodetection.csv"); writeStatementToCSV(csvContent, filename); investmentProfile->m_startLine = 7; csvImporter->m_autodetect[AutoAccountInvest] = true; auto st = csvImporter->unattendedImport(filename, investmentProfile); QVERIFY(st.m_listTransactions.count() == 3); QVERIFY(st.m_accountId == toBeClosedAccID); // closed account shouldn't be autodetected auto closedAcc = file->account(toBeClosedAccID); ft.restart(); closedAcc.setClosed(true); file->modifyAccount(closedAcc); ft.commit(); st = csvImporter->unattendedImport(filename, investmentProfile); QVERIFY(st.m_accountId == accID); } -void CsvImporterTest::testCalculatedFeeColumn() +void CSVImporterCoreTest::testCalculatedFeeColumn() { auto csvContent = csvDataset(0); QString filename("calculated-fee-column.csv"); writeStatementToCSV(csvContent, filename); investmentProfile->m_feeRate = QLatin1String("4"); investmentProfile->m_feeIsPercentage = true; // fee is calculated always as percentage auto st = csvImporter->unattendedImport(filename, investmentProfile); QVERIFY(st.m_listTransactions[0].m_amount == MyMoneyMoney(-130)); QVERIFY(st.m_listTransactions[0].m_fees == MyMoneyMoney(5)); investmentProfile->m_minFee = QLatin1String("6"); investmentProfile->m_colTypeNum.remove(Column::Fee); // hack st = csvImporter->unattendedImport(filename, investmentProfile); QVERIFY(st.m_listTransactions[0].m_amount == MyMoneyMoney(-131)); QVERIFY(st.m_listTransactions[0].m_fees == MyMoneyMoney(6)); // minimal fee is 6 now, so fee of 5 from above test must be increased to 6 } diff --git a/kmymoney/plugins/csv/import/core/tests/csvimporter-test.h b/kmymoney/plugins/csv/import/core/tests/csvimportercore-test.h similarity index 89% rename from kmymoney/plugins/csv/import/core/tests/csvimporter-test.h rename to kmymoney/plugins/csv/import/core/tests/csvimportercore-test.h index a65f36bf3..e932d381e 100644 --- a/kmymoney/plugins/csv/import/core/tests/csvimporter-test.h +++ b/kmymoney/plugins/csv/import/core/tests/csvimportercore-test.h @@ -1,55 +1,55 @@ /*************************************************************************** - csvimporter-test.h + csvimportercore.h ------------------- copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ -#ifndef CSVIMPORTERTEST_H -#define CSVIMPORTERTEST_H +#ifndef CSVIMPORTERCORETEST_H +#define CSVIMPORTERCORETEST_H #include -class CSVImporter; +class CSVImporterCore; class BankingProfile; class PricesProfile; class InvestmentProfile; class MyMoneyFile; class MyMoneySeqAccessMgr; -class CsvImporterTest : public QObject +class CSVImporterCoreTest : public QObject { Q_OBJECT - CSVImporter *csvImporter; + CSVImporterCore *csvImporter; BankingProfile *debitCreditProfile; BankingProfile *amountProfile; PricesProfile *pricesProfile; InvestmentProfile *investmentProfile; MyMoneyFile *file; MyMoneySeqAccessMgr *storage; private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testBasicPriceTable(); void testPriceFractionSetting(); void testImportByDebitCredit(); void testImportByAmount(); void testImportByName(); void testImportBySymbol(); void testFeeColumn(); void testAutoDecimalSymbol(); void testInvAccountAutodetection(); void testCalculatedFeeColumn(); }; #endif diff --git a/kmymoney/plugins/csv/import/csvimporterplugin.cpp b/kmymoney/plugins/csv/import/csvimporter.cpp similarity index 66% rename from kmymoney/plugins/csv/import/csvimporterplugin.cpp rename to kmymoney/plugins/csv/import/csvimporter.cpp index f7595a590..d9fa17f71 100644 --- a/kmymoney/plugins/csv/import/csvimporterplugin.cpp +++ b/kmymoney/plugins/csv/import/csvimporter.cpp @@ -1,118 +1,124 @@ /*************************************************************************** - csvimporterplugin.cpp - (based on ofximporterplugin.cpp) + csvimporter.cpp ------------------- begin : Sat Jan 01 2010 copyright : (C) 2010 by Allan Anderson email : agander93@gmail.com copyright : (C) 2016 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "csvimporterplugin.h" +#include "csvimporter.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes +#include #include #include // ---------------------------------------------------------------------------- // Project Includes -#include "csvimporter.h" +#include "core/csvimportercore.h" #include "csvwizard.h" #include "statementinterface.h" -CsvImporterPlugin::CsvImporterPlugin() : - KMyMoneyPlugin::Plugin(nullptr, "csvimport"/*must be the same as X-KDE-PluginInfo-Name*/) +CSVImporter::CSVImporter(QObject *parent, const QVariantList &args) : + KMyMoneyPlugin::Plugin(parent, "csvimporter"/*must be the same as X-KDE-PluginInfo-Name*/) { - setComponentName("kmm_csvimport", i18n("CSV importer")); - setXMLFile("kmm_csvimport.rc"); + Q_UNUSED(args); + setComponentName("csvimporter", i18n("CSV importer")); + setXMLFile("csvimporter.rc"); createActions(); // For information, announce that we have been loaded. - qDebug("KMyMoney csvimport plugin loaded"); + qDebug("Plugins: csvimporter loaded"); } -CsvImporterPlugin::~CsvImporterPlugin() +CSVImporter::~CSVImporter() { + qDebug("Plugins: csvimporter unloaded"); } -void CsvImporterPlugin::createActions() +void CSVImporter::createActions() { m_action = actionCollection()->addAction("file_import_csv"); m_action->setText(i18n("CSV...")); - connect(m_action, SIGNAL(triggered(bool)), this, SLOT(startWizardRun())); + connect(m_action, &QAction::triggered, this, &CSVImporter::startWizardRun); } -void CsvImporterPlugin::startWizardRun() +void CSVImporter::startWizardRun() { m_action->setEnabled(false); - m_importer = new CSVImporter; + m_importer = new CSVImporterCore; m_wizard = new CSVWizard(this, m_importer); m_silent = false; - connect(m_wizard, SIGNAL(statementReady(MyMoneyStatement&)), this, SLOT(slotGetStatement(MyMoneyStatement&))); + connect(m_wizard, &CSVWizard::statementReady, this, &CSVImporter::slotGetStatement); m_action->setEnabled(false);// don't allow further plugins to start while this is open } -bool CsvImporterPlugin::slotGetStatement(MyMoneyStatement& s) +bool CSVImporter::slotGetStatement(MyMoneyStatement& s) { bool ret = statementInterface()->import(s, m_silent); delete m_importer; return ret; } -QString CsvImporterPlugin::formatName() const +QString CSVImporter::formatName() const { return QLatin1String("CSV"); } -QString CsvImporterPlugin::formatFilenameFilter() const +QString CSVImporter::formatFilenameFilter() const { return "*.csv"; } -bool CsvImporterPlugin::isMyFormat(const QString& filename) const +bool CSVImporter::isMyFormat(const QString& filename) const { // filename is considered a CSV file if it can be opened // and the filename ends in ".csv". bool result = false; QFile f(filename); if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { result = f.fileName().endsWith(QLatin1String(".csv")); f.close(); } return result; } -bool CsvImporterPlugin::import(const QString& filename) +bool CSVImporter::import(const QString& filename) { bool rc = true; - m_importer = new CSVImporter; + m_importer = new CSVImporterCore; m_wizard = new CSVWizard(this, m_importer); m_wizard->presetFilename(filename); m_silent = false; - connect(m_wizard, SIGNAL(statementReady(MyMoneyStatement&)), this, SLOT(slotGetStatement(MyMoneyStatement&))); + connect(m_wizard, &CSVWizard::statementReady, this, &CSVImporter::slotGetStatement); return rc; } -QString CsvImporterPlugin::lastError() const +QString CSVImporter::lastError() const { return QString(); } + +K_PLUGIN_FACTORY_WITH_JSON(CSVImporterFactory, "csvimporter.json", registerPlugin();) + +#include "csvimporter.moc" diff --git a/kmymoney/plugins/csv/import/csvimporterplugin.h b/kmymoney/plugins/csv/import/csvimporter.h similarity index 81% rename from kmymoney/plugins/csv/import/csvimporterplugin.h rename to kmymoney/plugins/csv/import/csvimporter.h index a1da12d29..8493960e6 100644 --- a/kmymoney/plugins/csv/import/csvimporterplugin.h +++ b/kmymoney/plugins/csv/import/csvimporter.h @@ -1,108 +1,107 @@ /*************************************************************************** - csvimporterplugin.h + csvimporter.h ------------------- begin : Sat Jan 01 2010 copyright : (C) 2010 by Allan Anderson email : agander93@gmail.com copyright : (C) 2016-2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ -#ifndef CSVIMPORTERPLUGIN_H -#define CSVIMPORTERPLUGIN_H +#ifndef CSVIMPORTER_H +#define CSVIMPORTER_H // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // QT Includes // Project Includes #include "kmymoneyplugin.h" -class CSVImporter; +class CSVImporterCore; class CSVWizard; class MyMoneyStatement; -class CsvImporterPlugin : public KMyMoneyPlugin::Plugin, public KMyMoneyPlugin::ImporterPlugin +class CSVImporter : public KMyMoneyPlugin::Plugin, public KMyMoneyPlugin::ImporterPlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.csvimport" FILE "csvimport.json") Q_INTERFACES(KMyMoneyPlugin::ImporterPlugin) public: - explicit CsvImporterPlugin(); - ~CsvImporterPlugin(); + explicit CSVImporter(QObject *parent, const QVariantList &args); + ~CSVImporter() override; QAction* m_action; CSVWizard* m_wizard; - CSVImporter* m_importer; + CSVImporterCore* m_importer; /** * This method returns the english-language name of the format * this plugin imports, e.g. "OFX" * * @return QString Name of the format */ - virtual QString formatName() const; + virtual QString formatName() const override; /** * This method returns the filename filter suitable for passing to * KFileDialog::setFilter(), e.g. "*.csv" which describes how * files of this format are likely to be named in the file system * * @return QString Filename filter string */ - virtual QString formatFilenameFilter() const; + virtual QString formatFilenameFilter() const override; /** * This method returns whether this plugin is able to import * a particular file. * * @param filename Fully-qualified pathname to a file * * @return bool Whether the indicated file is importable by this plugin */ - virtual bool isMyFormat(const QString& filename) const; + virtual bool isMyFormat(const QString& filename) const override; /** * Import a file * * @param filename File to import * * @return bool Whether the import was successful. */ - virtual bool import(const QString& filename); + virtual bool import(const QString& filename) override; /** * Returns the error result of the last import * * @return QString English-language name of the error encountered in the * last import, or QString() if it was successful. * */ - virtual QString lastError() const; + virtual QString lastError() const override; private: bool m_silent; public Q_SLOTS: bool slotGetStatement(MyMoneyStatement& s); protected Q_SLOTS: void startWizardRun(); protected: void createActions(); }; #endif diff --git a/kmymoney/plugins/csv/import/csvimport.json.in b/kmymoney/plugins/csv/import/csvimporter.json.in similarity index 99% rename from kmymoney/plugins/csv/import/csvimport.json.in rename to kmymoney/plugins/csv/import/csvimporter.json.in index 74afcdef9..c11a2109d 100644 --- a/kmymoney/plugins/csv/import/csvimport.json.in +++ b/kmymoney/plugins/csv/import/csvimporter.json.in @@ -1,86 +1,86 @@ { "KPlugin": { "Authors": [ { "Email": "aganders93@gmail.com,lukasz.wojnilowicz@gmail.com", "Name": "Allan Anderson,Łukasz Wojniłowicz" } ], "Description": "Add CSV importing to KMyMoney", "Description[ar]": "أضف استيراد CSV إلى أموالك", "Description[bs]": "Dodaj uvoz CSV za KMyMoney", "Description[ca@valencia]": "Afig la importació CSV al KMyMoney", "Description[ca]": "Afegeix la importació CSV al KMyMoney", "Description[da]": "Føj CSV-import til KMyMoney", "Description[de]": "Fügt CSV-Import zu KMyMoney hinzu", "Description[el]": "Προσθήκη εισαγωγής CSV στο KMyMoney", "Description[en_GB]": "Add CSV importing to KMyMoney", "Description[es]": "Añade la importación de CSV a KMyMoney", "Description[et]": "CSV import KMyMoneysse", "Description[eu]": "Erantsi CSV-ren inportazioa KMyMoney-ri", "Description[fi]": "Lisää CSV-tuonti KMyMoney-ohjelmaan", "Description[fr]": "Ajoute l'importation « CVS » à KMyMoney", "Description[gl]": "Engadir importación de CSV ao KMyMoney", "Description[hu]": "CSV hozzáadása a KMyMoney-ba importáláshoz", "Description[it]": "Aggiungi l'importazione CSV a KMyMoney", "Description[kk]": "CSV файлды KMyMoney-ге импорттап қосу", "Description[lt]": "Įdėti CSV importavimą į KMyMoney", "Description[nds]": "CSV-Import för KMyMoney", "Description[nl]": "Voeg CSV importeren toe aan KMyMoney", "Description[pl]": "Dodaje importowanie CSV do KMyMoney", "Description[pt]": "Adição da importação de CSV ao KMyMoney", "Description[pt_BR]": "Adiciona a importação de CSV ao KMyMoney", "Description[ro]": "Importă fișier csv (câmpuri separate de virgulă) în KMyMoney", "Description[ru]": "Импорт CSV в KMyMoney", "Description[sk]": "Pridať CSV importovanie do KMyMoney", "Description[sv]": "Lägg till CSV-import till KMyMoney", "Description[tr]": "KMyMoney'e CSV aktarımı ekle", "Description[uk]": "Додає можливість імпортування CSV до KMyMoney", "Description[x-test]": "xxAdd CSV importing to KMyMoneyxx", "Description[zh_TW]": "匯入 CSV 檔到 KMyMoney", "EnabledByDefault": true, "Icon": "document-import", - "Id": "csvimport", + "Id": "csvimporter", "License": "GPL", "Name": "CSV Importer", "Name[ar]": "استيراد CSV", "Name[bs]": "csvimport", "Name[ca@valencia]": "csvimport", "Name[ca]": "csvimport", "Name[cs]": "csvimport", "Name[da]": "csvimport", "Name[de]": "CSV-Import", "Name[el]": "csvimport", "Name[en_GB]": "csvimport", "Name[es]": "csvimport", "Name[et]": "CSV import", "Name[eu]": "csvimport", "Name[fi]": "csvimport", "Name[fr]": "Importation « csv »", "Name[ga]": "csvimport", "Name[gl]": "csvimport", "Name[hu]": "csvimport", "Name[it]": "csvimport", "Name[kk]": "csvimport", "Name[lt]": "csvimport", "Name[nds]": "CSV-Import", "Name[nl]": "csvimport", "Name[pl]": "Importowanie CSV", "Name[pt]": "csvimport", "Name[pt_BR]": "csvimport", "Name[ro]": "csvimport", "Name[ru]": "csvimport", "Name[sk]": "csvimport", "Name[sv]": "CSV-import", "Name[tr]": "csvimport", "Name[uk]": "csvimport", "Name[x-test]": "xxcsvimportxx", "Name[zh_CN]": "csv 导入", "Name[zh_TW]": "csvimport", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/csv/import/kmm_csvimport.rc b/kmymoney/plugins/csv/import/csvimporter.rc similarity index 100% rename from kmymoney/plugins/csv/import/kmm_csvimport.rc rename to kmymoney/plugins/csv/import/csvimporter.rc diff --git a/kmymoney/plugins/csv/import/csvwizard.cpp b/kmymoney/plugins/csv/import/csvwizard.cpp index be880aa1f..e0e40f7fe 100644 --- a/kmymoney/plugins/csv/import/csvwizard.cpp +++ b/kmymoney/plugins/csv/import/csvwizard.cpp @@ -1,1120 +1,1120 @@ /******************************************************************************* * csvwizard.cpp * -------------------- * begin : Thur Jan 01 2015 * copyright : (C) 2015 by Allan Anderson * email : agander93@gmail.com * copyright : (C) 2016 by Łukasz Wojniłowicz * email : lukasz.wojnilowicz@gmail.com ********************************************************************************/ /******************************************************************************* * * * 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 "csvwizard.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes -#include "csvimporterplugin.h" -#include "csvutil.h" -#include "convdate.h" #include "csvimporter.h" +#include "core/csvutil.h" +#include "core/convdate.h" +#include "core/csvimportercore.h" #include "investmentwizardpage.h" #include "bankingwizardpage.h" #include "priceswizardpage.h" #include "icons/icons.h" #include "ui_csvwizard.h" #include "ui_introwizardpage.h" #include "ui_separatorwizardpage.h" #include "ui_rowswizardpage.h" #include "ui_formatswizardpage.h" using namespace Icons; -CSVWizard::CSVWizard(CsvImporterPlugin* plugin, CSVImporter* importer) : +CSVWizard::CSVWizard(CSVImporter *plugin, CSVImporterCore* importer) : ui(new Ui::CSVWizard), m_plugin(plugin), m_imp(importer), m_wiz(new QWizard) { ui->setupUi(this); ui->tableView->setModel(m_imp->m_file->m_model); - readWindowSize(CSVImporter::configFile()); + readWindowSize(CSVImporterCore::configFile()); m_wiz->setWizardStyle(QWizard::ClassicStyle); ui->horizontalLayout->addWidget(m_wiz); m_curId = -1; m_lastId = -1; m_wiz->installEventFilter(this); // event filter for escape key presses m_wiz->button(QWizard::BackButton)->setIcon(Icons::get(Icon::ArrowLeft)); m_wiz->button(QWizard::CancelButton)->setIcon(Icons::get(Icon::DialogCancel)); m_wiz->button(QWizard::CustomButton2)->setIcon(Icons::get(Icon::KMyMoney)); m_wiz->button(QWizard::FinishButton)->setIcon(Icons::get(Icon::DialogOKApply)); m_wiz->button(QWizard::CustomButton1)->setIcon(Icons::get(Icon::FileArchiver)); m_wiz->button(QWizard::CustomButton3)->setIcon(Icons::get(Icon::InvestApplet)); m_wiz->button(QWizard::NextButton)->setIcon(Icons::get(Icon::ArrowRight)); m_pageIntro = new IntroPage(this, m_imp); m_wiz->setPage(PageIntro, m_pageIntro); m_pageSeparator = new SeparatorPage(this, m_imp); m_wiz->setPage(PageSeparator, m_pageSeparator); m_pageRows = new RowsPage(this, m_imp); m_wiz->setPage(PageRows, m_pageRows); m_pageFormats = new FormatsPage(this, m_imp); m_wiz->setPage(PageFormats, m_pageFormats); showStage(); m_wiz->button(QWizard::CustomButton1)->setEnabled(false); m_stageLabels << ui->label_intro << ui->label_separators << ui->label_rows << ui->label_columns << ui->label_columns << ui->label_columns << ui->label_formats; m_pageFormats->setFinalPage(true); this->setAttribute(Qt::WA_DeleteOnClose, true); connect(m_wiz->button(QWizard::FinishButton), &QAbstractButton::clicked, this, &CSVWizard::slotClose); connect(m_wiz->button(QWizard::CancelButton), &QAbstractButton::clicked, this, &QWidget::close); connect(m_wiz->button(QWizard::CustomButton1), &QAbstractButton::clicked, this, &CSVWizard::fileDialogClicked); connect(m_wiz->button(QWizard::CustomButton2), &QAbstractButton::clicked, this, &CSVWizard::importClicked); connect(m_wiz->button(QWizard::CustomButton3), &QAbstractButton::clicked, this, &CSVWizard::saveAsQIFClicked); connect(m_wiz, SIGNAL(currentIdChanged(int)), this, SLOT(slotIdChanged(int))); ui->tableView->setWordWrap(false); m_vScrollBar = ui->tableView->verticalScrollBar(); m_vScrollBar->setTracking(false); m_clearBrush = KColorScheme(QPalette::Normal).background(KColorScheme::NormalBackground); m_clearBrushText = KColorScheme(QPalette::Normal).foreground(KColorScheme::NormalText); m_colorBrush = KColorScheme(QPalette::Normal).background(KColorScheme::PositiveBackground); m_colorBrushText = KColorScheme(QPalette::Normal).foreground(KColorScheme::PositiveText); m_errorBrush = KColorScheme(QPalette::Normal).background(KColorScheme::NegativeBackground); m_errorBrushText = KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText); m_wiz->setSideWidget(ui->wizardBox); show(); } CSVWizard::~CSVWizard() { delete ui; } void CSVWizard::presetFilename(const QString& name) { m_fileName = name; } void CSVWizard::showStage() { QString str = ui->label_intro->text(); ui->label_intro->setText(QString::fromLatin1("%1").arg(str)); } void CSVWizard::readWindowSize(const KSharedConfigPtr& config) { - KConfigGroup miscGroup(config, CSVImporter::m_confMiscName); - m_initialWidth = miscGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfWidth), 800); - m_initialHeight = miscGroup.readEntry(CSVImporter::m_miscSettingsConfName.value(ConfHeight), 400); + KConfigGroup miscGroup(config, CSVImporterCore::m_confMiscName); + m_initialWidth = miscGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfWidth), 800); + m_initialHeight = miscGroup.readEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfHeight), 400); } void CSVWizard::saveWindowSize(const KSharedConfigPtr& config) { - KConfigGroup miscGroup(config, CSVImporter::m_confMiscName); + KConfigGroup miscGroup(config, CSVImporterCore::m_confMiscName); m_initialHeight = this->geometry().height(); m_initialWidth = this->geometry().width(); - miscGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfWidth), m_initialWidth); - miscGroup.writeEntry(CSVImporter::m_miscSettingsConfName.value(ConfHeight), m_initialHeight); + miscGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfWidth), m_initialWidth); + miscGroup.writeEntry(CSVImporterCore::m_miscSettingsConfName.value(ConfHeight), m_initialHeight); miscGroup.sync(); } void CSVWizard::slotIdChanged(int id) { QString txt; m_lastId = m_curId; m_curId = id; if ((m_lastId == -1) || (m_curId == -1)) { return; } txt = m_stageLabels[m_lastId]->text(); txt.remove(QRegularExpression(QStringLiteral("[/]"))); m_stageLabels[m_lastId]->setText(txt); txt = m_stageLabels[m_curId]->text(); txt = QString::fromLatin1("%1").arg(txt); m_stageLabels[m_curId]->setText(txt); } void CSVWizard::clearColumnsBackground(const int col) { QList columnList; columnList << col; clearColumnsBackground(columnList); } void CSVWizard::clearColumnsBackground(const QList &columnList) { QStandardItemModel *model = m_imp->m_file->m_model; for (int i = m_imp->m_profile->m_startLine; i <= m_imp->m_profile->m_endLine; ++i) { foreach (const auto j, columnList) { model->item(i, j)->setBackground(m_clearBrush); model->item(i, j)->setForeground(m_clearBrushText); } } } void CSVWizard::clearBackground() { QStandardItemModel *model = m_imp->m_file->m_model; int rowCount = model->rowCount(); int colCount = model->columnCount(); for (int i = 0; i < rowCount; ++i) { for (int j = 0; j < colCount; ++j) { model->item(i, j)->setBackground(m_clearBrush); model->item(i, j)->setForeground(m_clearBrushText); } } } void CSVWizard::markUnwantedRows() { QStandardItemModel *model = m_imp->m_file->m_model; int rowCount = model->rowCount(); int colCount = model->columnCount(); QBrush brush; QBrush brushText; for (int i = 0; i < rowCount; ++i) { if ((i < m_imp->m_profile->m_startLine) || (i > m_imp->m_profile->m_endLine)) { brush = m_errorBrush; brushText = m_errorBrushText; } else { brush = m_clearBrush; brushText = m_clearBrushText; } for (int j = 0; j < colCount; ++j) { model->item(i, j)->setBackground(brush); model->item(i, j)->setForeground(brushText); } } } void CSVWizard::updateWindowSize() { QTableView *table = this->ui->tableView; table->resizeColumnsToContents(); this->repaint(); QRect screen = QApplication::desktop()->availableGeometry(); //get available screen size QRect wizard = this->frameGeometry(); //get current wizard size int newWidth = table->contentsMargins().left() + table->contentsMargins().right() + table->horizontalHeader()->length() + table->verticalHeader()->width() + (wizard.width() - table->width()); if (table->verticalScrollBar()->isEnabled()) { if (!table->verticalScrollBar()->isVisible() && // vertical scrollbar may be not visible after repaint... table->horizontalScrollBar()->isVisible()) // ...so use horizontal scrollbar dimension newWidth += table->horizontalScrollBar()->height(); else newWidth += table->verticalScrollBar()->width(); } int newHeight = table->contentsMargins().top() + table->contentsMargins().bottom() + table->verticalHeader()->length() + table->horizontalHeader()->height() + (wizard.height() - table->height()); if (table->horizontalScrollBar()->isEnabled()) { if (!table->horizontalScrollBar()->isVisible() && // horizontal scrollbar may be not visible after repaint... table->verticalScrollBar()->isVisible()) // ...so use vertical scrollbar dimension newHeight += table->verticalScrollBar()->width(); else newHeight += table->horizontalScrollBar()->height(); } // limit wizard size to screen size if (newHeight > screen.height()) newHeight = screen.height(); if (newWidth > screen.width()) newWidth = screen.width(); // don't shrink wizard if required size is less than initial if (newWidth < this->m_initialWidth) newWidth = this->m_initialWidth; if (newHeight < this->m_initialHeight) newHeight = this->m_initialHeight; newWidth -= (wizard.width() - this->geometry().width()); // remove window frame newHeight -= (wizard.height() - this->geometry().height()); wizard.setWidth(newWidth); wizard.setHeight(newHeight); wizard.moveTo((screen.width() - wizard.width()) / 2, (screen.height() - wizard.height()) / 2); this->setGeometry(wizard); } void CSVWizard::closeEvent(QCloseEvent *event) { this->m_plugin->m_action->setEnabled(true); // reenable File->Import->CSV event->accept(); } bool CSVWizard::eventFilter(QObject *object, QEvent *event) { // prevent the QWizard part of CSVWizard window from closing on Escape key press if (object == this->m_wiz) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Escape) { close(); return true; } } } return false; } void CSVWizard::slotClose() { m_imp->m_profile->m_lastUsedDirectory = m_imp->m_file->m_inFileName; - m_imp->m_profile->writeSettings(CSVImporter::configFile()); + m_imp->m_profile->writeSettings(CSVImporterCore::configFile()); m_imp->profilesAction(m_imp->m_profile->type(), ProfileAction::UpdateLastUsed, m_imp->m_profile->m_profileName, m_imp->m_profile->m_profileName); close(); } void CSVWizard::fileDialogClicked() { m_imp->profileFactory(m_pageIntro->m_profileType, m_pageIntro->ui->m_profiles->currentText()); - bool profileExists = m_imp->m_profile->readSettings(CSVImporter::configFile()); + bool profileExists = m_imp->m_profile->readSettings(CSVImporterCore::configFile()); if (!m_fileName.isEmpty()) { if (!m_imp->m_file->getInFileName(m_fileName)) { if (!m_imp->m_file->getInFileName(m_imp->m_profile->m_lastUsedDirectory)) { return; } } } else if (!m_imp->m_file->getInFileName(m_imp->m_profile->m_lastUsedDirectory)) return; - saveWindowSize(CSVImporter::configFile()); + saveWindowSize(CSVImporterCore::configFile()); m_imp->m_file->readFile(m_imp->m_profile); m_imp->m_file->setupParser(m_imp->m_profile); m_skipSetup = m_pageIntro->ui->m_skipSetup->isChecked(); switch(m_imp->m_profile->type()) { case Profile::Investment: if (!m_pageInvestment) { m_pageInvestment = new InvestmentPage(this, m_imp); m_wiz->setPage(CSVWizard::PageInvestment, m_pageInvestment); } break; case Profile::Banking: if (!m_pageBanking) { m_pageBanking = new BankingPage(this, m_imp); m_wiz->setPage(CSVWizard::PageBanking, m_pageBanking); } break; case Profile::StockPrices: case Profile::CurrencyPrices: if (!m_pagePrices) { m_pagePrices = new PricesPage(this, m_imp); m_wiz->setPage(CSVWizard::PagePrices, m_pagePrices); } break; default: return; } m_wiz->next(); //go to separator page if (m_skipSetup && profileExists) for (int i = 0; i < 4; i++) //programmaticaly go through separator-, rows-, investment-/bank-, formatspage m_wiz->next(); } void CSVWizard::importClicked() { switch (m_imp->m_profile->type()) { case Profile::Banking: if (!m_pageBanking->validateCreditDebit()) return; break; case Profile::Investment: if (!m_pageInvestment->validateActionType()) return; break; default: break; } if (!m_imp->createStatement(m_st)) return; slotClose(); emit statementReady(m_st); } void CSVWizard::saveAsQIFClicked() { switch (m_imp->m_profile->type()) { case Profile::Banking: if (!m_pageBanking->validateCreditDebit()) return; break; case Profile::Investment: if (!m_pageInvestment->validateActionType()) return; break; default: break; } bool isOK = m_imp->createStatement(m_st); if (!isOK || m_st.m_listTransactions.isEmpty()) return; QString outFileName = m_imp->m_file->m_inFileName; outFileName.truncate(outFileName.lastIndexOf('.')); outFileName.append(QLatin1String(".qif")); outFileName = QFileDialog::getSaveFileName(this, i18n("Save QIF"), outFileName, i18n("QIF Files (*.qif)")); if (outFileName.isEmpty()) return; switch (m_imp->m_profile->type()) { case Profile::Banking: m_pageBanking->makeQIF(m_st, outFileName); break; case Profile::Investment: m_pageInvestment->makeQIF(m_st, outFileName); break; default: break; } } void CSVWizard::initializeComboBoxes(const QHash &columns) { QStringList columnNumbers; for (int i = 0; i < m_imp->m_file->m_columnCount; ++i) columnNumbers.append(QString::number(i + 1)); foreach (const auto column, columns) { // disable widgets allowing their initialization column->blockSignals(true); // clear all existing items before adding new ones column->clear(); // populate comboboxes with col # values column->addItems(columnNumbers); // all comboboxes are set to 0 so set them to -1 column->setCurrentIndex(-1); // enable widgets after their initialization column->blockSignals(false); } } //------------------------------------------------------------------------------------------------------- -IntroPage::IntroPage(CSVWizard *dlg, CSVImporter *imp) : +IntroPage::IntroPage(CSVWizard *dlg, CSVImporterCore *imp) : CSVWizardPage(dlg, imp), ui(new Ui::IntroPage) { ui->setupUi(this); } IntroPage::~IntroPage() { delete ui; } int IntroPage::nextId() const { return CSVWizard::PageSeparator; } void IntroPage::initializePage() { m_imp->m_file->m_model->clear(); wizard()->setButtonText(QWizard::CustomButton1, i18n("Select File")); wizard()->button(QWizard::CustomButton1)->setToolTip(i18n("A profile must be selected before selecting a file.")); QList layout; layout << QWizard::Stretch << QWizard::CustomButton1 << QWizard::CancelButton; wizard()->setButtonLayout(layout); ui->m_profiles->lineEdit()->setClearButtonEnabled(true); connect(ui->m_profiles, SIGNAL(currentIndexChanged(int)), this, SLOT(slotComboSourceIndexChanged(int))); connect(ui->m_add, &QAbstractButton::clicked, this, &IntroPage::slotAddProfile); connect(ui->m_remove, &QAbstractButton::clicked, this, &IntroPage::slotRemoveProfile); connect(ui->m_rename, &QAbstractButton::clicked, this, &IntroPage::slotRenameProfile); connect(ui->m_profilesBank, &QAbstractButton::toggled, this, &IntroPage::slotBankRadioToggled); connect(ui->m_profilesInvest, &QAbstractButton::toggled, this, &IntroPage::slotInvestRadioToggled); connect(ui->m_profilesCurrencyPrices, &QAbstractButton::toggled, this, &IntroPage::slotCurrencyPricesRadioToggled); connect(ui->m_profilesStockPrices, &QAbstractButton::toggled, this, &IntroPage::slotStockPricesRadioToggled); if (m_dlg->m_initialHeight == -1 || m_dlg->m_initialWidth == -1) { m_dlg->m_initialHeight = m_dlg->geometry().height(); m_dlg->m_initialWidth = m_dlg->geometry().width(); } else { //resize wizard to its initial size and center it m_dlg->setGeometry( QStyle::alignedRect( Qt::LeftToRight, Qt::AlignCenter, QSize(m_dlg->m_initialWidth, m_dlg->m_initialHeight), QApplication::desktop()->availableGeometry() ) ); } m_dlg->ui->tableView->hide(); } bool IntroPage::validatePage() { return true; } void IntroPage::slotAddProfile() { profileChanged(ProfileAction::Add); } void IntroPage::slotRemoveProfile() { profileChanged(ProfileAction::Remove); } void IntroPage::slotRenameProfile() { profileChanged(ProfileAction::Rename); } void IntroPage::profileChanged(const ProfileAction action) { QString cbText = ui->m_profiles->currentText(); if (cbText.isEmpty()) // you cannot neither add nor remove empty name profile or rename to empty name return; int cbIndex = ui->m_profiles->currentIndex(); switch (action) { case ProfileAction::Rename: case ProfileAction::Add: { int dupIndex = m_profiles.indexOf(QRegularExpression (cbText)); if (dupIndex == cbIndex && cbIndex != -1) // if profile name wasn't changed then return return; else if (dupIndex != -1) { // profile with the same name already exists ui->m_profiles->setItemText(cbIndex, m_profiles.value(cbIndex)); KMessageBox::information(m_dlg, i18n("
Profile %1 already exists.
" "Please enter another name
", cbText)); return; } break; } case ProfileAction::Remove: if (m_profiles.value(cbIndex) != cbText) // user changed name of the profile and tries to remove it return; break; default: break; } - if (CSVImporter::profilesAction(m_profileType, action, m_profiles.value(cbIndex), cbText)) { + if (CSVImporterCore::profilesAction(m_profileType, action, m_profiles.value(cbIndex), cbText)) { switch (action) { case ProfileAction::Add: m_profiles.append(cbText); ui->m_profiles->addItem(cbText); ui->m_profiles->setCurrentIndex(m_profiles.count() - 1); KMessageBox::information(m_dlg, i18n("
Profile %1 has been added.
", cbText)); break; case ProfileAction::Remove: m_profiles.removeAt(cbIndex); ui->m_profiles->removeItem(cbIndex); KMessageBox::information(m_dlg, i18n("
Profile %1 has been removed.
", cbText)); break; case ProfileAction::Rename: ui->m_profiles->setItemText(cbIndex, cbText); KMessageBox::information(m_dlg, i18n("
Profile name has been renamed from %1 to %2.
", m_profiles.value(cbIndex), cbText)); m_profiles[cbIndex] = cbText; break; default: break; } } } void IntroPage::slotComboSourceIndexChanged(int idx) { if (idx == -1) { wizard()->button(QWizard::CustomButton1)->setEnabled(false); ui->m_skipSetup->setEnabled(false); ui->m_remove->setEnabled(false); ui->m_rename->setEnabled(false); } else { wizard()->button(QWizard::CustomButton1)->setEnabled(true); ui->m_skipSetup->setEnabled(true); ui->m_remove->setEnabled(true); ui->m_rename->setEnabled(true); } } void IntroPage::profileTypeChanged(const Profile profileType, bool toggled) { if (!toggled) return; - KConfigGroup profilesGroup(CSVImporter::configFile(), CSVImporter::m_confProfileNames); + KConfigGroup profilesGroup(CSVImporterCore::configFile(), CSVImporterCore::m_confProfileNames); m_profileType = profileType; QString profileTypeStr; switch (m_profileType) { case Profile::Banking: ui->m_profilesInvest->setChecked(false); ui->m_profilesStockPrices->setChecked(false); ui->m_profilesCurrencyPrices->setChecked(false); break; case Profile::Investment: ui->m_profilesBank->setChecked(false); ui->m_profilesStockPrices->setChecked(false); ui->m_profilesCurrencyPrices->setChecked(false); break; case Profile::StockPrices: ui->m_profilesBank->setChecked(false); ui->m_profilesInvest->setChecked(false); ui->m_profilesCurrencyPrices->setChecked(false); break; case Profile::CurrencyPrices: ui->m_profilesBank->setChecked(false); ui->m_profilesInvest->setChecked(false); ui->m_profilesStockPrices->setChecked(false); break; default: break; } - profileTypeStr = CSVImporter::m_profileConfPrefix.value(m_profileType); + profileTypeStr = CSVImporterCore::m_profileConfPrefix.value(m_profileType); m_profiles = profilesGroup.readEntry(profileTypeStr, QStringList()); - int priorProfile = profilesGroup.readEntry(CSVImporter::m_confPriorName + profileTypeStr, 0); + int priorProfile = profilesGroup.readEntry(CSVImporterCore::m_confPriorName + profileTypeStr, 0); ui->m_profiles->clear(); ui->m_profiles->addItems(m_profiles); ui->m_profiles->setCurrentIndex(priorProfile); ui->m_profiles->setEnabled(true); ui->m_add->setEnabled(true); } void IntroPage::slotBankRadioToggled(bool toggled) { profileTypeChanged(Profile::Banking, toggled); } void IntroPage::slotInvestRadioToggled(bool toggled) { profileTypeChanged(Profile::Investment, toggled); } void IntroPage::slotCurrencyPricesRadioToggled(bool toggled) { profileTypeChanged(Profile::CurrencyPrices, toggled); } void IntroPage::slotStockPricesRadioToggled(bool toggled) { profileTypeChanged(Profile::StockPrices, toggled); } -SeparatorPage::SeparatorPage(CSVWizard *dlg, CSVImporter *imp) : +SeparatorPage::SeparatorPage(CSVWizard *dlg, CSVImporterCore *imp) : CSVWizardPage(dlg, imp), ui(new Ui::SeparatorPage) { ui->setupUi(this); connect(ui->m_encoding, SIGNAL(currentIndexChanged(int)), this, SLOT(encodingChanged(int))); connect(ui->m_fieldDelimiter, SIGNAL(currentIndexChanged(int)), this, SLOT(fieldDelimiterChanged(int))); connect(ui->m_textDelimiter, SIGNAL(currentIndexChanged(int)), this, SLOT(textDelimiterChanged(int))); } SeparatorPage::~SeparatorPage() { delete ui; } void SeparatorPage::initializePage() { m_dlg->ui->tableView->show(); // comboboxes are preset to -1 and, in new profile case, can be set here to -1 as well ... // ... so block their signals until setting them ... ui->m_encoding->blockSignals(true); ui->m_fieldDelimiter->blockSignals(true); ui->m_textDelimiter->blockSignals(true); initializeEncodingCombobox(); ui->m_encoding->setCurrentIndex(ui->m_encoding->findData(m_imp->m_profile->m_encodingMIBEnum)); ui->m_fieldDelimiter->setCurrentIndex((int)m_imp->m_profile->m_fieldDelimiter); ui->m_textDelimiter->setCurrentIndex((int)m_imp->m_profile->m_textDelimiter); ui->m_encoding->blockSignals(false); ui->m_fieldDelimiter->blockSignals(false); ui->m_textDelimiter->blockSignals(false); // ... and ensure that their signal receivers will always be called emit ui->m_encoding->currentIndexChanged(ui->m_encoding->currentIndex()); emit ui->m_fieldDelimiter->currentIndexChanged(ui->m_fieldDelimiter->currentIndex()); emit ui->m_textDelimiter->currentIndexChanged(ui->m_textDelimiter->currentIndex()); m_dlg->updateWindowSize(); QList layout; layout << QWizard::Stretch << QWizard::BackButton << QWizard::NextButton << QWizard::CancelButton; wizard()->setButtonLayout(layout); } void SeparatorPage::initializeEncodingCombobox() { ui->m_encoding->clear(); QList codecs; QMap codecMap; QRegExp iso8859RegExp(QLatin1Literal("ISO[- ]8859-([0-9]+).*")); foreach (const auto mib, QTextCodec::availableMibs()) { QTextCodec *codec = QTextCodec::codecForMib(mib); QString sortKey = codec->name().toUpper(); int rank; if (sortKey.startsWith(QLatin1String("UTF-8"))) { // krazy:exclude=strings rank = 1; } else if (sortKey.startsWith(QLatin1String("UTF-16"))) { // krazy:exclude=strings rank = 2; } else if (iso8859RegExp.exactMatch(sortKey)) { if (iso8859RegExp.cap(1).size() == 1) rank = 3; else rank = 4; } else { rank = 5; } sortKey.prepend(QChar('0' + rank)); codecMap.insert(sortKey, codec); } codecs = codecMap.values(); foreach (const auto codec, codecs) ui->m_encoding->addItem(codec->name(), codec->mibEnum()); } void SeparatorPage::encodingChanged(const int index) { if (index == -1) { ui->m_encoding->setCurrentIndex(ui->m_encoding->findText("UTF-8")); return; } else if (index == ui->m_encoding->findData(m_imp->m_profile->m_encodingMIBEnum)) return; m_imp->m_profile->m_encodingMIBEnum = ui->m_encoding->currentData().toInt(); m_imp->m_file->readFile(m_imp->m_profile); emit completeChanged(); } void SeparatorPage::fieldDelimiterChanged(const int index) { if (index == -1 && // if field delimiter isn't set... !m_imp->m_autodetect.value(AutoFieldDelimiter)) // ... and user disabled autodetecting... return; // ... then wait for him to choose else if (index == (int)m_imp->m_profile->m_fieldDelimiter) return; m_imp->m_profile->m_fieldDelimiter = static_cast(int(index)); m_imp->m_file->readFile(m_imp->m_profile); // get column count, we get with this fieldDelimiter m_imp->m_file->setupParser(m_imp->m_profile); if (index == -1) { ui->m_fieldDelimiter->blockSignals(true); ui->m_fieldDelimiter->setCurrentIndex((int)m_imp->m_profile->m_fieldDelimiter); ui->m_fieldDelimiter->blockSignals(false); } m_dlg->updateWindowSize(); emit completeChanged(); } void SeparatorPage::textDelimiterChanged(const int index) { if (index == -1) { // if text delimiter isn't set... ui->m_textDelimiter->setCurrentIndex(0); // ...then set it to 0, as for now there is no better idea how to detect it return; } m_imp->m_profile->m_textDelimiter = static_cast(index); m_imp->m_file->setupParser(m_imp->m_profile); if (index == -1) { ui->m_textDelimiter->blockSignals(true); ui->m_textDelimiter->setCurrentIndex((int)m_imp->m_profile->m_textDelimiter); ui->m_textDelimiter->blockSignals(false); } emit completeChanged(); } bool SeparatorPage::isComplete() const { bool rc = false; if (ui->m_encoding->currentIndex() != -1 && ui->m_fieldDelimiter->currentIndex() != -1 && ui->m_textDelimiter->currentIndex() != -1) { switch(m_imp->m_profile->type()) { case Profile::Banking: if (m_imp->m_file->m_columnCount > 2) rc = true; break; case Profile::Investment: if (m_imp->m_file->m_columnCount > 3) rc = true; break; case Profile::CurrencyPrices: case Profile::StockPrices: if (m_imp->m_file->m_columnCount > 1) rc = true; break; default: break; } } return rc; } bool SeparatorPage::validatePage() { return true; } void SeparatorPage::cleanupPage() { // On completion with error force use of 'Back' button. // ...to allow resetting of 'Skip setup' m_dlg->m_pageIntro->initializePage(); // Need to show button(QWizard::CustomButton1) not 'NextButton' } -RowsPage::RowsPage(CSVWizard *dlg, CSVImporter *imp) : +RowsPage::RowsPage(CSVWizard *dlg, CSVImporterCore *imp) : CSVWizardPage(dlg, imp), ui(new Ui::RowsPage) { ui->setupUi(this); connect(ui->m_startLine, SIGNAL(valueChanged(int)), this, SLOT(startRowChanged(int)));; connect(ui->m_endLine, SIGNAL(valueChanged(int)), this, SLOT(endRowChanged(int))); } RowsPage::~RowsPage() { delete ui; } void RowsPage::initializePage() { ui->m_startLine->blockSignals(true); ui->m_endLine->blockSignals(true); ui->m_startLine->setMaximum(m_imp->m_file->m_rowCount); ui->m_endLine->setMaximum(m_imp->m_file->m_rowCount); ui->m_startLine->setValue(m_imp->m_profile->m_startLine + 1); ui->m_endLine->setValue(m_imp->m_profile->m_endLine + 1); ui->m_startLine->blockSignals(false); ui->m_endLine->blockSignals(false); m_dlg->markUnwantedRows(); m_dlg->m_vScrollBar->setValue(m_imp->m_profile->m_startLine); QList layout; layout << QWizard::Stretch << QWizard::BackButton << QWizard::NextButton << QWizard::CancelButton; wizard()->setButtonLayout(layout); } void RowsPage::cleanupPage() { m_dlg->clearBackground(); } int RowsPage::nextId() const { int ret; switch (m_imp->m_profile->type()) { case Profile::Banking: ret = CSVWizard::PageBanking; break; case Profile::Investment: ret = CSVWizard::PageInvestment; break; case Profile::StockPrices: case Profile::CurrencyPrices: ret = CSVWizard::PagePrices; break; default: ret = CSVWizard::PageRows; break; } return ret; } void RowsPage::startRowChanged(int val) { if (val > m_imp->m_file->m_rowCount) { ui->m_startLine->setValue(m_imp->m_file->m_rowCount - 1); return; } --val; if (val > m_imp->m_profile->m_endLine) { if (m_imp->m_profile->m_endLine <= m_imp->m_file->m_rowCount) ui->m_startLine->setValue(m_imp->m_profile->m_endLine + 1); return; } m_imp->m_profile->m_startLine = val; m_dlg->m_vScrollBar->setValue(val); m_dlg->markUnwantedRows(); } void RowsPage::endRowChanged(int val) { if (val > m_imp->m_file->m_rowCount) { ui->m_endLine->setValue(m_imp->m_file->m_rowCount - 1); return; } --val; if (val < m_imp->m_profile->m_startLine) { if (m_imp->m_profile->m_startLine <= m_imp->m_file->m_rowCount) ui->m_endLine->setValue(m_imp->m_profile->m_startLine + 1); return; } m_imp->m_profile->m_trailerLines = m_imp->m_file->m_rowCount - val; m_imp->m_profile->m_endLine = val; m_dlg->markUnwantedRows(); } -FormatsPage::FormatsPage(CSVWizard *dlg, CSVImporter *imp) : +FormatsPage::FormatsPage(CSVWizard *dlg, CSVImporterCore *imp) : CSVWizardPage(dlg, imp), ui(new Ui::FormatsPage) { ui->setupUi(this); connect(ui->m_dateFormat, SIGNAL(currentIndexChanged(int)), this, SLOT(dateFormatChanged(int))); connect(ui->m_decimalSymbol, SIGNAL(currentIndexChanged(int)), this, SLOT(decimalSymbolChanged(int))); } FormatsPage::~FormatsPage() { delete ui; } void FormatsPage::initializePage() { m_isDecimalSymbolOK = false; m_isDateFormatOK = false; QList layout; layout << QWizard::Stretch << QWizard::CustomButton3 << QWizard::CustomButton2 << QWizard::BackButton << QWizard::FinishButton << QWizard::CancelButton; wizard()->setOption(QWizard::HaveCustomButton2, true); wizard()->setButtonText(QWizard::CustomButton2, i18n("Import CSV")); wizard()->setOption(QWizard::HaveCustomButton3, true); wizard()->setButtonText(QWizard::CustomButton3, i18n("Make QIF File")); wizard()->setButtonLayout(layout); wizard()->button(QWizard::CustomButton2)->setEnabled(false); wizard()->button(QWizard::CustomButton3)->setEnabled(false); wizard()->button(QWizard::FinishButton)->setEnabled(false); ui->m_thousandsDelimiter->setEnabled(false); ui->m_dateFormat->blockSignals(true); ui->m_dateFormat->setCurrentIndex((int)m_imp->m_profile->m_dateFormat); ui->m_dateFormat->blockSignals(false); emit ui->m_dateFormat->currentIndexChanged((int)m_imp->m_profile->m_dateFormat); // emit update signal manually regardless of change to combobox ui->m_decimalSymbol->blockSignals(true); if (m_imp->m_profile->m_decimalSymbol == DecimalSymbol::Auto && !m_imp->m_autodetect.value(AutoDecimalSymbol)) ui->m_decimalSymbol->setCurrentIndex(-1); else ui->m_decimalSymbol->setCurrentIndex((int)m_imp->m_profile->m_decimalSymbol); ui->m_decimalSymbol->blockSignals(false); emit ui->m_decimalSymbol->currentIndexChanged((int)m_imp->m_profile->m_decimalSymbol); // emit update signal manually regardless of change to combobox if (m_dlg->m_skipSetup && wizard()->button(QWizard::CustomButton2)->isEnabled()) m_dlg->importClicked(); } void FormatsPage::decimalSymbolChanged(int index) { const QList columns = m_imp->getNumericalColumns(); switch (index) { case -1: if (!m_imp->m_autodetect.value(AutoDecimalSymbol)) { break; } // intentional fall through case 2: { ui->m_decimalSymbol->blockSignals(true); m_imp->m_profile->m_decimalSymbol = DecimalSymbol::Auto; int failColumn = m_imp->detectDecimalSymbols(columns); if (failColumn != -2) { KMessageBox::sorry(this, i18n("
Autodetect could not detect your decimal symbol in column %1.
" "
Try manual selection to see problematic cells and correct your data.
", failColumn), i18n("CSV import")); ui->m_decimalSymbol->setCurrentIndex(-1); ui->m_thousandsDelimiter->setCurrentIndex(-1); } else if (index == -1) { // if detection went well and decimal symbol was unspecified then we'll be specifying it DecimalSymbol firstDecSymbol = m_imp->m_decimalSymbolIndexMap.first(); bool allSymbolsEqual = true; foreach (const auto mapDecSymbol, m_imp->m_decimalSymbolIndexMap) { if (firstDecSymbol != mapDecSymbol) allSymbolsEqual = false; } if (allSymbolsEqual) { // if symbol in all columns is equal then set it... m_imp->m_profile->m_decimalSymbol = firstDecSymbol; ui->m_decimalSymbol->setCurrentIndex((int)firstDecSymbol); ui->m_thousandsDelimiter->setCurrentIndex((int)firstDecSymbol); } else { // else set to auto m_imp->m_profile->m_decimalSymbol = DecimalSymbol::Auto; ui->m_decimalSymbol->setCurrentIndex((int)DecimalSymbol::Auto); ui->m_thousandsDelimiter->setCurrentIndex((int)DecimalSymbol::Auto); } } ui->m_decimalSymbol->blockSignals(false); break; } default: foreach (const auto column, columns) m_imp->m_decimalSymbolIndexMap.insert(column, static_cast(index)); ui->m_thousandsDelimiter->setCurrentIndex(index); m_imp->m_profile->m_decimalSymbol = static_cast(index); } m_isDecimalSymbolOK = validateDecimalSymbols(columns); emit completeChanged(); } bool FormatsPage::validateDecimalSymbols(const QList &columns) { bool isOK = true; foreach (const auto col, columns) { m_imp->m_file->m_parse->setDecimalSymbol(m_imp->m_decimalSymbolIndexMap.value(col)); m_dlg->clearColumnsBackground(col); for (int row = m_imp->m_profile->m_startLine; row <= m_imp->m_profile->m_endLine; ++row) { QStandardItem *item = m_imp->m_file->m_model->item(row, col); QString rawNumber = item->text(); m_imp->m_file->m_parse->possiblyReplaceSymbol(rawNumber); if (!m_imp->m_file->m_parse->invalidConversion() || rawNumber.isEmpty()) { // empty strings are welcome item->setBackground(m_dlg->m_colorBrush); item->setForeground(m_dlg->m_colorBrushText); } else { isOK = false; m_dlg->ui->tableView->scrollTo(item->index(), QAbstractItemView::EnsureVisible); item->setBackground(m_dlg->m_errorBrush); item->setForeground(m_dlg->m_errorBrushText); } } } return isOK; } void FormatsPage::dateFormatChanged(const int index) { if (index == -1) return; int col = m_imp->m_profile->m_colTypeNum.value(Column::Date); m_imp->m_profile->m_dateFormat = static_cast(index); m_imp->m_convertDate->setDateFormatIndex(static_cast(index)); m_isDateFormatOK = validateDateFormat(col); if (!m_isDateFormatOK) { KMessageBox::sorry(this, i18n("
There are invalid date formats in column '%1'.
" "
Please check your selections.
" , col + 1), i18n("CSV import")); } emit completeChanged(); } bool FormatsPage::validateDateFormat(const int col) { m_dlg->clearColumnsBackground(col); QDate emptyDate; bool isOK = true; for (int row = m_imp->m_profile->m_startLine; row <= m_imp->m_profile->m_endLine; ++row) { QStandardItem* item = m_imp->m_file->m_model->item(row, col); QDate dat = m_imp->m_convertDate->convertDate(item->text()); if (dat == emptyDate) { isOK = false; m_dlg->ui->tableView->scrollTo(item->index(), QAbstractItemView::EnsureVisible); item->setBackground(m_dlg->m_errorBrush); item->setForeground(m_dlg->m_errorBrushText); } else { item->setBackground(m_dlg->m_colorBrush); item->setForeground(m_dlg->m_colorBrushText); } } return isOK; } bool FormatsPage::isComplete() const { const bool enable = m_isDecimalSymbolOK && m_isDateFormatOK; wizard()->button(QWizard::CustomButton2)->setEnabled(enable); if (m_imp->m_profile->type() != Profile::StockPrices && m_imp->m_profile->type() != Profile::CurrencyPrices) wizard()->button(QWizard::CustomButton3)->setEnabled(enable); return enable; } void FormatsPage::cleanupPage() { QList columns = m_imp->getNumericalColumns(); columns.append(m_imp->m_profile->m_colTypeNum.value(Column::Date)); m_dlg->clearColumnsBackground(columns); m_dlg->m_st = MyMoneyStatement(); // any change on investment/banking page invalidates created statement QList layout; layout << QWizard::Stretch << QWizard::BackButton << QWizard::NextButton << QWizard::CancelButton; wizard()->setButtonLayout(layout); } diff --git a/kmymoney/plugins/csv/import/csvwizard.h b/kmymoney/plugins/csv/import/csvwizard.h index 9da580180..ad7b4e94d 100644 --- a/kmymoney/plugins/csv/import/csvwizard.h +++ b/kmymoney/plugins/csv/import/csvwizard.h @@ -1,283 +1,283 @@ /******************************************************************************* * csvwizard.h * ------------------ * begin : Thur Jan 01 2015 * copyright : (C) 2015 by Allan Anderson * email : agander93@gmail.com * copyright : (C) 2016 by Łukasz Wojniłowicz * email : lukasz.wojnilowicz@gmail.com ********************************************************************************/ /******************************************************************************* * * * 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. * * * ********************************************************************************/ #ifndef CSVWIZARD_H #define CSVWIZARD_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "csvwizardpage.h" #include "mymoneystatement.h" -class CsvImporterPlugin; class CSVImporter; +class CSVImporterCore; class IntroPage; class SeparatorPage; class RowsPage; class BankingPage; class InvestmentPage; class PricesPage; class FormatsPage; namespace Ui { class CSVWizard; } class QScrollBar; class QComboBox; class QLabel; class CSVWizard : public QDialog { Q_OBJECT public: - CSVWizard(CsvImporterPlugin *plugin, CSVImporter *importer); + explicit CSVWizard(CSVImporter *plugin, CSVImporterCore *importer); ~CSVWizard(); enum wizardPageE { PageIntro, PageSeparator, PageRows, PageBanking, PageInvestment, PagePrices, PageFormats }; Ui::CSVWizard *ui; MyMoneyStatement m_st; QScrollBar *m_vScrollBar; IntroPage *m_pageIntro; int m_initialHeight; int m_initialWidth; QBrush m_clearBrush; QBrush m_clearBrushText; QBrush m_colorBrush; QBrush m_colorBrushText; QBrush m_errorBrush; QBrush m_errorBrushText; QMap m_colTypeName; bool m_skipSetup; void clearColumnsBackground(const int col); void clearColumnsBackground(const QList &columnList); void clearBackground(); void markUnwantedRows(); void importClicked(); /** * Called in order to adjust window size to suit the file, */ void updateWindowSize(); void initializeComboBoxes(const QHash &columns); void presetFilename(const QString& name); private: QList m_stageLabels; int m_curId; int m_lastId; SeparatorPage *m_pageSeparator; RowsPage *m_pageRows; QPointer m_pageBanking; QPointer m_pageInvestment; QPointer m_pagePrices; FormatsPage *m_pageFormats; - CsvImporterPlugin* m_plugin; - CSVImporter* m_imp; + CSVImporter* m_plugin; + CSVImporterCore* m_imp; QWizard* m_wiz; QString m_fileName; void readWindowSize(const KSharedConfigPtr& config); void saveWindowSize(const KSharedConfigPtr& config); void showStage(); - void closeEvent(QCloseEvent *event); - bool eventFilter(QObject *object, QEvent *event); + void closeEvent(QCloseEvent *event) override; + bool eventFilter(QObject *object, QEvent *event) override; private Q_SLOTS: /** * This method is called when 'Exit' is clicked. The plugin settings will * be saved and the plugin will be terminated. */ void slotClose(); void slotIdChanged(int id); void fileDialogClicked(); void saveAsQIFClicked(); Q_SIGNALS: void statementReady(MyMoneyStatement&); }; namespace Ui { class IntroPage; } class IntroPage : public CSVWizardPage { Q_OBJECT public: - explicit IntroPage(CSVWizard *dlg, CSVImporter *imp); + explicit IntroPage(CSVWizard *dlg, CSVImporterCore *imp); ~IntroPage(); - void initializePage(); + void initializePage() override; Profile m_profileType; Ui::IntroPage *ui; Q_SIGNALS: void signalBankClicked(bool); void activated(int); void returnPressed(); private: QStringList m_profiles; - bool validatePage(); - int nextId() const; + bool validatePage() override; + int nextId() const override; void profileChanged(const ProfileAction action); void profileTypeChanged(const Profile profileType, bool toggled); private Q_SLOTS: void slotAddProfile(); void slotRemoveProfile(); void slotRenameProfile(); void slotComboSourceIndexChanged(int idx); void slotBankRadioToggled(bool toggled); void slotInvestRadioToggled(bool toggled); void slotCurrencyPricesRadioToggled(bool toggled); void slotStockPricesRadioToggled(bool toggled); }; namespace Ui { class SeparatorPage; } class SeparatorPage : public CSVWizardPage { Q_OBJECT public: - explicit SeparatorPage(CSVWizard *dlg, CSVImporter *imp); + explicit SeparatorPage(CSVWizard *dlg, CSVImporterCore *imp); ~SeparatorPage(); private Q_SLOTS: void encodingChanged(const int index); void fieldDelimiterChanged(const int index); void textDelimiterChanged(const int index); Q_SIGNALS: void completeChanged(); private: Ui::SeparatorPage *ui; void initializeEncodingCombobox(); - void initializePage(); - bool isComplete() const; - void cleanupPage(); - bool validatePage(); + void initializePage() override; + bool isComplete() const override; + void cleanupPage() override; + bool validatePage() override; }; namespace Ui { class RowsPage; } class RowsPage : public CSVWizardPage { Q_OBJECT public: - explicit RowsPage(CSVWizard *dlg, CSVImporter *imp); + explicit RowsPage(CSVWizard *dlg, CSVImporterCore *imp); ~RowsPage(); private Q_SLOTS: /** * This method is called when the user edits the startLine setting. */ void startRowChanged(int val); /** * This method is called when the user edits the lastLine setting. */ void endRowChanged(int val); private: Ui::RowsPage *ui; - void initializePage(); - int nextId() const; - void cleanupPage(); + void initializePage() override; + int nextId() const override; + void cleanupPage() override; }; namespace Ui { class FormatsPage; } class FormatsPage : public CSVWizardPage { Q_OBJECT public: - explicit FormatsPage(CSVWizard *dlg, CSVImporter *imp); + explicit FormatsPage(CSVWizard *dlg, CSVImporterCore *imp); ~FormatsPage(); private: Ui::FormatsPage *ui; bool m_isDecimalSymbolOK; bool m_isDateFormatOK; /** * This method is called when the user selects a new decimal symbol. The * UI is updated using the new symbol, and on importing, the new symbol * also will be used. */ bool validateDecimalSymbols(const QList &columns); /** * This method checks if all dates in date column are valid. */ bool validateDateFormat(const int index); - void initializePage(); - bool isComplete() const; - void cleanupPage(); + void initializePage() override; + bool isComplete() const override; + void cleanupPage() override; Q_SIGNALS: void completeChanged(); private Q_SLOTS: void decimalSymbolChanged(int); void dateFormatChanged(const int index); }; #endif // CSVWIZARD_H diff --git a/kmymoney/plugins/csv/import/csvwizardpage.h b/kmymoney/plugins/csv/import/csvwizardpage.h index d3217cb0e..c24104b34 100644 --- a/kmymoney/plugins/csv/import/csvwizardpage.h +++ b/kmymoney/plugins/csv/import/csvwizardpage.h @@ -1,36 +1,36 @@ /******************************************************************************* * csvwizardpage.h * ------------------ * copyright : (C) 2017 by Łukasz Wojniłowicz * email : lukasz.wojnilowicz@gmail.com ********************************************************************************/ /******************************************************************************* * * * 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. * * * ********************************************************************************/ #ifndef CSVWIZARDPAGE_H #define CSVWIZARDPAGE_H #include -#include "csvenums.h" +#include "core/csvenums.h" class CSVWizard; -class CSVImporter; +class CSVImporterCore; class CSVWizardPage : public QWizardPage { public: - CSVWizardPage(CSVWizard *dlg, CSVImporter *imp) : QWizardPage(nullptr), m_dlg(dlg), m_imp(imp) {} + CSVWizardPage(CSVWizard *dlg, CSVImporterCore *imp) : QWizardPage(nullptr), m_dlg(dlg), m_imp(imp) {} protected: CSVWizard *m_dlg; - CSVImporter *m_imp; + CSVImporterCore *m_imp; }; #endif // CSVWIZARDPAGE_H diff --git a/kmymoney/plugins/csv/import/investmentwizardpage.cpp b/kmymoney/plugins/csv/import/investmentwizardpage.cpp index 8306d409a..25b49b2ae 100644 --- a/kmymoney/plugins/csv/import/investmentwizardpage.cpp +++ b/kmymoney/plugins/csv/import/investmentwizardpage.cpp @@ -1,666 +1,666 @@ /******************************************************************************* * investmentwizardpage.cpp * -------------------- * begin : Thur Jan 01 2015 * copyright : (C) 2015 by Allan Anderson * email : agander93@gmail.com * copyright : (C) 2016 by Łukasz Wojniłowicz * email : lukasz.wojnilowicz@gmail.com ********************************************************************************/ /******************************************************************************* * * * 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 "investmentwizardpage.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyfile.h" #include "mymoneysecurity.h" #include "csvwizard.h" -#include "csvimporter.h" +#include "core/csvimportercore.h" #include "transactiondlg.h" #include "securitydlg.h" #include "securitiesdlg.h" #include "ui_investmentwizardpage.h" #include "ui_securitiesdlg.h" #include "ui_securitydlg.h" // ---------------------------------------------------------------------------- -InvestmentPage::InvestmentPage(CSVWizard *dlg, CSVImporter *imp) : +InvestmentPage::InvestmentPage(CSVWizard *dlg, CSVImporterCore *imp) : CSVWizardPage(dlg, imp), ui(new Ui::InvestmentPage) { ui->setupUi(this); connect(ui->m_clear, &QAbstractButton::clicked, this, &InvestmentPage::clearColumns); connect(ui->m_clearFee, &QAbstractButton::clicked, this, &InvestmentPage::clearFee); connect(ui->m_calculateFee, &QAbstractButton::clicked, this, &InvestmentPage::calculateFee); // initialize column names m_dlg->m_colTypeName.insert(Column::Type, i18n("Type")); m_dlg->m_colTypeName.insert(Column::Price, i18n("Price")); m_dlg->m_colTypeName.insert(Column::Quantity, i18n("Quantity")); m_dlg->m_colTypeName.insert(Column::Fee, i18n("Fee")); m_dlg->m_colTypeName.insert(Column::Date, i18n("Date")); m_dlg->m_colTypeName.insert(Column::Amount, i18n("Amount")); m_dlg->m_colTypeName.insert(Column::Symbol, i18n("Symbol")); m_dlg->m_colTypeName.insert(Column::Name, i18n("Name")); m_dlg->m_colTypeName.insert(Column::Memo, i18n("Memo")); m_profile = dynamic_cast(m_imp->m_profile); connect(ui->m_memoCol, SIGNAL(currentIndexChanged(int)), this, SLOT(memoColSelected(int))); connect(ui->m_typeCol, SIGNAL(currentIndexChanged(int)), this, SLOT(typeColSelected(int))); connect(ui->m_dateCol, SIGNAL(currentIndexChanged(int)), this, SLOT(dateColSelected(int))); connect(ui->m_quantityCol, SIGNAL(currentIndexChanged(int)), this, SLOT(quantityColSelected(int))); connect(ui->m_priceCol, SIGNAL(currentIndexChanged(int)), this, SLOT(priceColSelected(int))); connect(ui->m_priceFraction, SIGNAL(currentIndexChanged(int)), this, SLOT(fractionChanged(int))); connect(ui->m_amountCol, SIGNAL(currentIndexChanged(int)), this, SLOT(amountColSelected(int))); connect(ui->m_feeCol, SIGNAL(currentIndexChanged(int)), this, SLOT(feeColSelected(int))); connect(ui->m_symbolCol, SIGNAL(currentIndexChanged(int)), this, SLOT(symbolColSelected(int))); connect(ui->m_nameCol, SIGNAL(currentIndexChanged(int)), this, SLOT(nameColSelected(int))); connect(ui->m_feeIsPercentage, &QAbstractButton::clicked, this, &InvestmentPage::feeIsPercentageClicked); connect(ui->m_feeRate, &QLineEdit::editingFinished, this, &InvestmentPage::feeInputsChanged); connect(ui->m_feeRate, &QLineEdit::textChanged, this, &InvestmentPage::feeRateChanged); connect(ui->m_minFee, &QLineEdit::textChanged, this, &InvestmentPage::minFeeChanged); } InvestmentPage::~InvestmentPage() { delete m_securitiesDlg; delete ui; } void InvestmentPage::calculateFee() { m_imp->calculateFee(); m_dlg->updateWindowSize(); m_dlg->markUnwantedRows(); } void InvestmentPage::initializePage() { QHash columns {{Column::Amount, ui->m_amountCol}, {Column::Type, ui->m_typeCol}, {Column::Quantity, ui->m_quantityCol}, {Column::Memo, ui->m_memoCol}, {Column::Price, ui->m_priceCol}, {Column::Date, ui->m_dateCol}, {Column::Fee, ui->m_feeCol}, {Column::Symbol, ui->m_symbolCol}, {Column::Name, ui->m_nameCol}}; if (ui->m_dateCol->count() != m_imp->m_file->m_columnCount) m_dlg->initializeComboBoxes(columns); ui->m_feeIsPercentage->setChecked(m_profile->m_feeIsPercentage); columns.remove(Column::Memo); for (auto it = columns.cbegin(); it != columns.cend(); ++it) it.value()->setCurrentIndex(m_profile->m_colTypeNum.value(it.key())); ui->m_priceFraction->blockSignals(true); foreach (const auto priceFraction, m_imp->m_priceFractions) ui->m_priceFraction->addItem(QString::number(priceFraction.toDouble(), 'g', 3)); ui->m_priceFraction->blockSignals(false); ui->m_priceFraction->setCurrentIndex(m_profile->m_priceFraction); ui->m_feeRate->setText(m_profile->m_feeRate); ui->m_minFee->setText(m_profile->m_minFee); ui->m_feeRate->setValidator(new QRegularExpressionValidator(QRegularExpression(QStringLiteral("[0-9]{1,2}[") + QLocale().decimalPoint() + QStringLiteral("]{1,1}[0-9]{0,2}")), this) ); ui->m_minFee->setValidator(new QRegularExpressionValidator(QRegularExpression(QStringLiteral("[0-9]{1,}[") + QLocale().decimalPoint() + QStringLiteral("]{0,1}[0-9]{0,}")), this) ); if (!m_profile->m_feeRate.isEmpty()) { // fee rate indicates that fee column needs to be calculated if (m_imp->calculateFee()) { ui->m_feeCol->blockSignals(true); int feeCol = ui->m_feeCol->count(); ui->m_feeCol->addItem(QString::number(feeCol + 1)); ui->m_feeCol->setEnabled(false); ui->m_feeCol->setCurrentIndex(feeCol); ui->m_feeCol->blockSignals(false); m_dlg->updateWindowSize(); m_dlg->markUnwantedRows(); } } else if (m_profile->m_colTypeNum.value(Column::Fee, -1) >= ui->m_feeCol->count()) { // no fee rate, calculated fee column index exist, but the column doesn't exist and that's not ok m_profile->m_colTypeNum[Column::Fee] = -1; m_profile->m_colNumType.remove(m_profile->m_colNumType.key(Column::Fee)); } for (int i = 0; i < m_profile->m_memoColList.count(); ++i) { // Set up all memo fields... int tmp = m_profile->m_memoColList.at(i); if (tmp < m_profile->m_colTypeNum.count()) ui->m_memoCol->setCurrentIndex(tmp); } feeInputsChanged(); } bool InvestmentPage::isComplete() const { return ui->m_dateCol->currentIndex() > -1 && ui->m_typeCol->currentIndex() > -1 && ui->m_quantityCol->currentIndex() > -1 && ui->m_priceCol->currentIndex() > -1 && ui->m_amountCol->currentIndex() > -1 && ui->m_priceFraction->currentIndex() > -1; } bool InvestmentPage::validatePage() { if (ui->m_symbolCol->currentIndex() == -1 && ui->m_nameCol->currentIndex() == -1) return validateSecurity(); else return validateSecurities(); } void InvestmentPage::cleanupPage() { clearFeeCol(); } void InvestmentPage::memoColSelected(int col) { if (m_profile->m_colNumType.value(col) == Column::Type || m_profile->m_colNumType.value(col) == Column::Name) { int rc = KMessageBox::Yes; if (isVisible()) rc = KMessageBox::questionYesNo(m_dlg, i18n("
The '%1' field already has this column selected.
" "
If you wish to copy that data to the memo field, click 'Yes'.
", m_dlg->m_colTypeName.value(m_profile->m_colNumType.value(col)))); if (rc == KMessageBox::Yes) { ui->m_memoCol->setItemText(col, QString::number(col + 1) + QChar(QLatin1Char('*'))); if (!m_profile->m_memoColList.contains(col)) m_profile->m_memoColList.append(col); } else { ui->m_memoCol->setItemText(col, QString::number(col + 1)); m_profile->m_memoColList.removeOne(col); } //allow only separate memo field occupy combobox ui->m_memoCol->blockSignals(true); if (m_profile->m_colTypeNum.value(Column::Memo) != -1) ui->m_memoCol->setCurrentIndex(m_profile->m_colTypeNum.value(Column::Memo)); else ui->m_memoCol->setCurrentIndex(-1); ui->m_memoCol->blockSignals(false); return; } if (m_profile->m_colTypeNum.value(Column::Memo) != -1) // check if this memo has any column 'number' assigned... m_profile->m_memoColList.removeOne(col); // ...if true remove it from memo list if(validateSelectedColumn(col, Column::Memo)) if (col != - 1 && !m_profile->m_memoColList.contains(col)) m_profile->m_memoColList.append(col); } void InvestmentPage::dateColSelected(int col) { validateSelectedColumn(col, Column::Date); } void InvestmentPage::feeColSelected(int col) { validateSelectedColumn(col, Column::Fee); feeInputsChanged(); } void InvestmentPage::typeColSelected(int col) { if (validateSelectedColumn(col, Column::Type)) if (!validateMemoComboBox()) // user could have it already in memo so... memoColSelected(col); // ...if true set memo field again } void InvestmentPage::quantityColSelected(int col) { validateSelectedColumn(col, Column::Quantity); } void InvestmentPage::priceColSelected(int col) { validateSelectedColumn(col, Column::Price); } void InvestmentPage::amountColSelected(int col) { validateSelectedColumn(col, Column::Amount); clearFeeCol(); feeInputsChanged(); } void InvestmentPage::symbolColSelected(int col) { validateSelectedColumn(col, Column::Symbol); m_imp->m_mapSymbolName.clear(); // new symbol column so this map is no longer valid } void InvestmentPage::nameColSelected(int col) { if (validateSelectedColumn(col, Column::Name)) if (!validateMemoComboBox()) // user could have it already in memo so... memoColSelected(col); // ...if true set memo field again m_imp->m_mapSymbolName.clear(); // new name column so this map is no longer valid } void InvestmentPage::feeIsPercentageClicked(bool checked) { m_profile->m_feeIsPercentage = checked; } void InvestmentPage::fractionChanged(int col) { m_profile->m_priceFraction = col; emit completeChanged(); } void InvestmentPage::clearColumns() { ui->m_amountCol->setCurrentIndex(-1); ui->m_dateCol->setCurrentIndex(-1); ui->m_priceCol->setCurrentIndex(-1); ui->m_quantityCol->setCurrentIndex(-1); ui->m_memoCol->setCurrentIndex(-1); ui->m_typeCol->setCurrentIndex(-1); ui->m_nameCol->setCurrentIndex(-1); ui->m_symbolCol->setCurrentIndex(-1); ui->m_priceFraction->setCurrentIndex(-1); clearFee(); } void InvestmentPage::clearFee() { clearFeeCol(); ui->m_feeCol->setCurrentIndex(-1); ui->m_feeIsPercentage->setChecked(false); ui->m_calculateFee->setEnabled(false); ui->m_feeRate->setEnabled(true); ui->m_minFee->setEnabled(false); ui->m_feeRate->clear(); ui->m_minFee->clear(); } void InvestmentPage::feeInputsChanged() { // if (ui->comboBoxInv_feeCol->currentIndex() < m_importer->m_file->m_columnCount && // ui->comboBoxInv_feeCol->currentIndex() > -1) { // ui->lineEdit_minFee->setEnabled(false); // ui->lineEdit_feeRate->setEnabled(false); // ui->lineEdit_minFee->clear(); // ui->lineEdit_feeRate->clear(); // } if (m_profile->m_feeRate.isEmpty()) { ui->m_feeCol->setEnabled(true); ui->m_feeIsPercentage->setEnabled(true); ui->m_minFee->setEnabled(false); ui->m_calculateFee->setEnabled(false); } else { ui->m_feeCol->setEnabled(false); ui->m_feeIsPercentage->setEnabled(false); ui->m_feeIsPercentage->setChecked(true); ui->m_minFee->setEnabled(true); ui->m_feeRate->setEnabled(true); if (m_profile->m_colTypeNum.value(Column::Amount) != -1) ui->m_calculateFee->setEnabled(true); } } void InvestmentPage::feeRateChanged(const QString &text) { m_profile->m_feeRate = text; } void InvestmentPage::minFeeChanged(const QString &text) { m_profile->m_minFee = text; } void InvestmentPage::clearFeeCol() { if (!m_profile->m_feeRate.isEmpty() && // if fee rate isn't empty... m_profile->m_colTypeNum.value(Column::Fee) >= m_imp->m_file->m_columnCount - 1 && !ui->m_feeCol->isEnabled()) { // ...and fee column is last... --m_imp->m_file->m_columnCount; m_imp->m_file->m_model->removeColumn(m_imp->m_file->m_columnCount); int feeCol = ui->m_feeCol->currentIndex(); ui->m_feeCol->setCurrentIndex(-1); ui->m_feeCol->removeItem(feeCol); m_dlg->updateWindowSize(); } ui->m_feeCol->setEnabled(true); ui->m_feeIsPercentage->setEnabled(true); ui->m_feeIsPercentage->setChecked(false); } void InvestmentPage::resetComboBox(const Column comboBox) { switch (comboBox) { case Column::Amount: ui->m_amountCol->setCurrentIndex(-1); break; case Column::Date: ui->m_dateCol->setCurrentIndex(-1); break; case Column::Fee: ui->m_feeCol->setCurrentIndex(-1); break; case Column::Memo: ui->m_memoCol->setCurrentIndex(-1); break; case Column::Price: ui->m_priceCol->setCurrentIndex(-1); break; case Column::Quantity: ui->m_quantityCol->setCurrentIndex(-1); break; case Column::Type: ui->m_typeCol->setCurrentIndex(-1); break; case Column::Symbol: ui->m_symbolCol->setCurrentIndex(-1); break; case Column::Name: ui->m_nameCol->setCurrentIndex(-1); break; default: KMessageBox::sorry(m_dlg, i18n("
Field name not recognised.
'%1'
Please re-enter your column selections.", (int)comboBox), i18n("CSV import")); } } bool InvestmentPage::validateActionType() { for (int row = m_profile->m_startLine; row <= m_profile->m_endLine; ++row) { MyMoneyStatement::Transaction tr; // process quantity field int col = m_profile->m_colTypeNum.value(Column::Quantity); tr.m_shares = m_imp->processQuantityField(m_profile, row, col); // process price field col = m_profile->m_colTypeNum.value(Column::Price); tr.m_price = m_imp->processPriceField(m_profile, row, col); // process amount field col = m_profile->m_colTypeNum.value(Column::Amount); tr.m_amount = m_imp->processAmountField(m_profile, row, col); // process type field col = m_profile->m_colTypeNum.value(Column::Type); tr.m_eAction = m_imp->processActionTypeField(m_profile, row, col); switch(m_imp->validateActionType(tr)) { case InvalidActionValues: KMessageBox::sorry(m_dlg, i18n("The values in the columns you have selected\ndo not match any expected investment type.\n" "Please check the fields in the current transaction,\nand also your selections."), i18n("CSV import")); return false; break; case NoActionType: { bool unknownType = false; if (tr.m_eAction == eMyMoney::Transaction::Action::None) unknownType = true; QStringList colList; QStringList colHeaders; for (int col = 0; col < m_imp->m_file->m_columnCount; ++col) { colHeaders.append(m_dlg->m_colTypeName.value(m_profile->m_colNumType.value(col, Column::Invalid), QString(i18nc("Unused column", "Unused")))); colList.append(m_imp->m_file->m_model->item(row, col)->text()); } QList validActionTypes = m_imp->createValidActionTypes(tr); QPointer transactionDlg = new TransactionDlg(colList, colHeaders, m_profile->m_colTypeNum.value(Column::Type), validActionTypes); if (transactionDlg->exec() == QDialog::Rejected) { KMessageBox::information(m_dlg, i18n("
No valid action type found for this transaction.
" "
Please check the parameters supplied.
"), i18n("CSV import")); return false; } else tr.m_eAction = transactionDlg->getActionType(); delete transactionDlg; if (unknownType) { // type was unknown so store it col = m_profile->m_colTypeNum.value(Column::Type); m_profile->m_transactionNames[tr.m_eAction].append(m_imp->m_file->m_model->item(row, col)->text()); // store action type } } default: break; } } m_imp->m_isActionTypeValidated = true; return true; } bool InvestmentPage::validateSelectedColumn(const int col, const Column type) { if (m_profile->m_colTypeNum.value(type) != -1) // check if this 'type' has any column 'number' assigned... m_profile->m_colNumType.remove(m_profile->m_colTypeNum.value(type)); // ...if true remove 'type' assigned to this column 'number' bool ret = true; if (col == -1) { // user only wanted to reset his column so allow him m_profile->m_colTypeNum[type] = col; // assign new column 'number' to this 'type' } else if (m_profile->m_colNumType.contains(col)) { // if this column 'number' has already 'type' assigned KMessageBox::information(m_dlg, i18n("The '%1' field already has this column selected.
Please reselect both entries as necessary.
", m_dlg->m_colTypeName.value(m_profile->m_colNumType.value(col)))); resetComboBox(m_profile->m_colNumType[col]); resetComboBox(type); ret = false; } else { m_profile->m_colTypeNum[type] = col; // assign new column 'number' to this 'type' m_profile->m_colNumType[col] = type; // assign new 'type' to this column 'number' } emit completeChanged(); return ret; } bool InvestmentPage::validateMemoComboBox() { if (m_profile->m_memoColList.count() == 0) return true; for (int i = 0; i < ui->m_memoCol->count(); ++i) { QString txt = ui->m_memoCol->itemText(i); if (txt.contains(QLatin1Char('*'))) // check if text containing '*' belongs to valid column types if (m_profile->m_colNumType.value(i) != Column::Name && m_profile->m_colNumType.value(i) != Column::Type) { ui->m_memoCol->setItemText(i, QString::number(i + 1)); m_profile->m_memoColList.removeOne(i); return false; } } return true; } bool InvestmentPage::validateSecurities() { if (m_securitiesDlg.isNull() && m_imp->m_mapSymbolName.isEmpty()) { QSet onlySymbols; QSet onlyNames; m_imp->sortSecurities(onlySymbols, onlyNames, m_imp->m_mapSymbolName); if (!onlySymbols.isEmpty() || !onlyNames.isEmpty()) { m_securitiesDlg = new SecuritiesDlg; for (QSet::const_iterator symbol = onlySymbols.cbegin(); symbol != onlySymbols.cend(); ++symbol) m_securitiesDlg->displayLine(*symbol, QString()); for (QSet::const_iterator name = onlyNames.cbegin(); name != onlyNames.cend(); ++name) m_securitiesDlg->displayLine(QString(), *name); } } if (!m_securitiesDlg.isNull()) { QTableWidget* symbolTable = m_securitiesDlg->ui->tableWidget; if (m_securitiesDlg->exec() == QDialog::Rejected) { return false; } else { for (int row = 0; row < symbolTable->rowCount(); ++row) { QString symbol = symbolTable->item(row, 1)->text(); QString name = symbolTable->item(row, 2)->text(); m_imp->m_mapSymbolName.insert(symbol, name); } delete m_securitiesDlg; } } return true; } bool InvestmentPage::validateSecurity() { if (!m_profile->m_securitySymbol.isEmpty() && !m_profile->m_securityName.isEmpty()) m_imp->m_mapSymbolName.insert(m_profile->m_securitySymbol, m_profile->m_securityName); MyMoneyFile* file = MyMoneyFile::instance(); if (m_securityDlg.isNull() && (m_imp->m_mapSymbolName.isEmpty() || !(m_profile->m_dontAsk && m_dlg->m_skipSetup))) { m_securityDlg = new SecurityDlg; m_securityDlg->initializeSecurities(m_profile->m_securitySymbol, m_profile->m_securityName); m_securityDlg->ui->cbDontAsk->setChecked(m_profile->m_dontAsk); } if (!m_securityDlg.isNull()) { if (m_securityDlg->exec() == QDialog::Rejected) { return false; } else { QString securityID = m_securityDlg->security(); if (!securityID.isEmpty()) { m_profile->m_securityName = file->security(securityID).name(); m_profile->m_securitySymbol = file->security(securityID).tradingSymbol(); } else { m_profile->m_securityName = m_securityDlg->name(); m_profile->m_securitySymbol = m_securityDlg->symbol(); } m_profile->m_dontAsk = m_securityDlg->dontAsk(); m_imp->m_mapSymbolName.clear(); m_imp->m_mapSymbolName.insert(m_profile->m_securitySymbol, m_profile->m_securityName); // probably we should check if security with given symbol and name exists... delete m_securityDlg; // ...but KMM allows creating duplicates, so don't bother } } if (m_imp->m_mapSymbolName.isEmpty()) return false; return true; } void InvestmentPage::makeQIF(const MyMoneyStatement& st, const QString& outFileName) { QFile oFile(outFileName); oFile.open(QIODevice::WriteOnly); QTextStream out(&oFile); QString buffer; QString strEType; switch (st.m_eType) { case eMyMoney::Statement::Type::Investment: default: strEType = QStringLiteral("Invst"); } if (!st.m_strAccountName.isEmpty()) { buffer.append(QStringLiteral("!Account\n")); buffer.append(QChar(QLatin1Char('N')) + st.m_strAccountName + QChar(QLatin1Char('\n'))); buffer.append(QChar(QLatin1Char('T')) + strEType + QChar(QLatin1Char('\n'))); buffer.append(QStringLiteral("^\n")); } for (QList::const_iterator it = st.m_listSecurities.constBegin(); it != st.m_listSecurities.constEnd(); ++it) { buffer.append(QStringLiteral("!Type:Security\n")); buffer.append(QChar(QLatin1Char('N')) + (*it).m_strName + QChar(QLatin1Char('\n'))); buffer.append(QChar(QLatin1Char('S')) + (*it).m_strSymbol + QChar(QLatin1Char('\n'))); buffer.append(QStringLiteral("TStock\n^\n")); } if (!st.m_strAccountName.isEmpty()) { buffer.append(QStringLiteral("!Account\n")); buffer.append(QChar(QLatin1Char('N')) + st.m_strAccountName + QChar(QLatin1Char('\n'))); buffer.append(QChar(QLatin1Char('T')) + strEType + QChar(QLatin1Char('\n'))); buffer.append(QStringLiteral("^\n")); } buffer.append(QStringLiteral("!Type:") + strEType + QChar(QLatin1Char('\n'))); for (QList::const_iterator it = st.m_listTransactions.constBegin(); it != st.m_listTransactions.constEnd(); ++it) { buffer.append(QChar(QLatin1Char('D')) + it->m_datePosted.toString(QStringLiteral("MM/dd/yyyy")) + QChar(QLatin1Char('\n'))); buffer.append(QChar(QLatin1Char('Y')) + it->m_strSecurity + QChar(QLatin1Char('\n'))); QString txt; switch (it->m_eAction) { case eMyMoney::Transaction::Action::Buy: txt = QStringLiteral("Buy"); break; case eMyMoney::Transaction::Action::Sell: txt = QStringLiteral("Sell"); break; case eMyMoney::Transaction::Action::ReinvestDividend: txt = QStringLiteral("ReinvDiv"); break; case eMyMoney::Transaction::Action::CashDividend: txt = QStringLiteral("Div"); break; case eMyMoney::Transaction::Action::Interest: txt = QStringLiteral("IntInc"); break; case eMyMoney::Transaction::Action::Shrsin: txt = QStringLiteral("ShrsIn"); break; case eMyMoney::Transaction::Action::Shrsout: txt = QStringLiteral("ShrsOut"); break; case eMyMoney::Transaction::Action::Stksplit: txt = QStringLiteral("stksplit"); break; default: txt = QStringLiteral("unknown"); // shouldn't happen } buffer.append(QChar(QLatin1Char('N')) + txt + QChar(QLatin1Char('\n'))); if (it->m_eAction == eMyMoney::Transaction::Action::Buy) // we added 'N' field so buy transaction should have any sign txt.setNum(it->m_amount.abs().toDouble(), 'f', 4); else txt.setNum(it->m_amount.toDouble(), 'f', 4); buffer.append(QChar(QLatin1Char('T')) + txt + QChar(QLatin1Char('\n'))); txt.setNum(it->m_shares.toDouble(), 'f', 4); buffer.append(QChar(QLatin1Char('Q')) + txt + QChar(QLatin1Char('\n'))); txt.setNum(it->m_price.toDouble(), 'f', 4); buffer.append(QChar(QLatin1Char('I')) + txt + QChar(QLatin1Char('\n'))); if (!it->m_fees.isZero()) { txt.setNum(it->m_fees.toDouble(), 'f', 4); buffer.append(QChar(QLatin1Char('O')) + txt + QChar(QLatin1Char('\n'))); } if (!it->m_strBrokerageAccount.isEmpty()) buffer.append(QChar(QLatin1Char('L')) + it->m_strBrokerageAccount + QChar(QLatin1Char('\n'))); if (!it->m_strMemo.isEmpty()) buffer.append(QChar(QLatin1Char('M')) + it->m_strMemo + QChar(QLatin1Char('\n'))); buffer.append(QStringLiteral("^\n")); out << buffer;// output qif file buffer.clear(); } oFile.close(); } diff --git a/kmymoney/plugins/csv/import/investmentwizardpage.h b/kmymoney/plugins/csv/import/investmentwizardpage.h index fa6243c85..8560a29a4 100644 --- a/kmymoney/plugins/csv/import/investmentwizardpage.h +++ b/kmymoney/plugins/csv/import/investmentwizardpage.h @@ -1,120 +1,120 @@ /******************************************************************************* * investmentwizardpage.h * ------------------ * begin : Thur Jan 01 2015 * copyright : (C) 2015 by Allan Anderson * email : agander93@gmail.com * copyright : (C) 2016 by Łukasz Wojniłowicz * email : lukasz.wojnilowicz@gmail.com ********************************************************************************/ /******************************************************************************* * * * 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. * * * ********************************************************************************/ #ifndef INVESTMENTWIZARDPAGE_H #define INVESTMENTWIZARDPAGE_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "csvwizardpage.h" // ---------------------------------------------------------------------------- class InvestmentProfile; class SecurityDlg; class SecuritiesDlg; class MyMoneyStatement; namespace Ui { class InvestmentPage; } class InvestmentPage : public CSVWizardPage { Q_OBJECT public: - explicit InvestmentPage(CSVWizard *dlg, CSVImporter *imp); + explicit InvestmentPage(CSVWizard *dlg, CSVImporterCore *imp); ~InvestmentPage(); /** * This method fills QIF file with investment data */ void makeQIF(const MyMoneyStatement &st, const QString &outFileName); /** * This method validates the column numbers entered by the user. It then * checks the values in those columns for compatibility with the input * investment activity type. */ bool validateActionType(); private: void initializePage(); bool isComplete() const; bool validatePage(); void cleanupPage(); void clearFeeCol(); /** * This method will check whether memo combobox is still valid * after changing name or type column. */ bool validateMemoComboBox(); void resetComboBox(const Column comboBox); /** * This method is called column on investment page is selected. * It sets m_colTypeNum, m_colNumType and runs column validation. */ bool validateSelectedColumn(const int col, const Column type); /** * This method ensures that every security has symbol and name. */ bool validateSecurity(); bool validateSecurities(); QPointer m_securityDlg; QPointer m_securitiesDlg; InvestmentProfile *m_profile; Ui::InvestmentPage *ui; private Q_SLOTS: void clearFee(); void memoColSelected(int col); void dateColSelected(int col); void feeColSelected(int col); void typeColSelected(int col); void quantityColSelected(int col); void priceColSelected(int col); void amountColSelected(int col); void symbolColSelected(int col); void nameColSelected(int col); void feeIsPercentageClicked(bool checked); void fractionChanged(int col); void clearColumns(); void feeInputsChanged(); void feeRateChanged(const QString &text); void minFeeChanged(const QString &text); void calculateFee(); }; #endif // INVESTMENTWIZARDPAGE_H diff --git a/kmymoney/plugins/csv/import/kcm_csvimport.cpp b/kmymoney/plugins/csv/import/kcm_csvimporter.cpp similarity index 72% rename from kmymoney/plugins/csv/import/kcm_csvimport.cpp rename to kmymoney/plugins/csv/import/kcm_csvimporter.cpp index bc5e7ef0f..c466de9cf 100644 --- a/kmymoney/plugins/csv/import/kcm_csvimport.cpp +++ b/kmymoney/plugins/csv/import/kcm_csvimporter.cpp @@ -1,63 +1,53 @@ /*************************************************************************** * Copyright 2016 Łukasz Wojniłowicz lukasz.wojnilowicz@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 "kcm_csvimport.h" +#include "kcm_csvimporter.h" #include // KDE includes #include #include #include "pluginsettings.h" PluginSettingsWidget::PluginSettingsWidget(QWidget* parent) : QWidget(parent) { setupUi(this); } -K_PLUGIN_FACTORY_WITH_JSON(KCMcsvimportFactory, "kcm_kmm_csvimport.json", registerPlugin();) - -KCMcsvimport::KCMcsvimport(QWidget *parent, const QVariantList& args) +KCMCSVImporter::KCMCSVImporter(QWidget *parent, const QVariantList& args) : KCModule(parent, args) { - KAboutData *about = new KAboutData(QStringLiteral("kmm_csvimport"), - i18n("KMyMoney CSV importer"), - QStringLiteral(VERSION), QString(), - KAboutLicense::GPL, - i18n("Copyright 2010-2017" ) ); - about->addAuthor( QLatin1String("Allan Anderson") ); - about->addAuthor( QString::fromUtf8("Łukasz Wojniłowicz") ); - - setAboutData( about ); - PluginSettingsWidget* w = new PluginSettingsWidget(this); addConfig(PluginSettings::self(), w); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); layout->addWidget(w); setButtons(NoAdditionalButton); load(); } -KCMcsvimport::~KCMcsvimport() +KCMCSVImporter::~KCMCSVImporter() { } -#include "kcm_csvimport.moc" +K_PLUGIN_FACTORY_WITH_JSON(KCMCSVImporterFactory, "kcm_csvimporter.json", registerPlugin();) + +#include "kcm_csvimporter.moc" diff --git a/kmymoney/plugins/csv/import/kcm_kmm_csvimport.desktop b/kmymoney/plugins/csv/import/kcm_csvimporter.desktop similarity index 94% rename from kmymoney/plugins/csv/import/kcm_kmm_csvimport.desktop rename to kmymoney/plugins/csv/import/kcm_csvimporter.desktop index a24463d00..3e758fbed 100644 --- a/kmymoney/plugins/csv/import/kcm_kmm_csvimport.desktop +++ b/kmymoney/plugins/csv/import/kcm_csvimporter.desktop @@ -1,66 +1,66 @@ [Desktop Entry] Name=CSV Importer configuration Name[ca]=Configuració de l'importador de CSV Name[ca@valencia]=Configuració de l'importador de CSV Name[cs]=Nastavení importu CSV Name[de]=CSV-Importeinrichtung Name[en_GB]=CSV Importer configuration Name[es]=Configuración del importador CSV Name[fr]=Configuration de l'importation de CSV Name[gl]=Configuración do importador de CSV Name[it]=Configurazione dello strumento di importazione CSV Name[nl]=Instellingen van CSV importeren Name[pl]=Ustawienia importera CSV Name[pt]=Configuração da Importação de CSV Name[pt_BR]=Configuração do importador CSV Name[sk]=Nastavenie CSV importéra Name[sv]=CSV-importinställning Name[tr]=CSV içe aktarım yapılandırması Name[uk]=Налаштування засобу імпортування CSV Name[x-test]=xxCSV Importer configurationxx Icon=document-import Type=Service ServiceTypes=KCModule -Exec=kcmshell5 kmm_csvimport +Exec=kcmshell5 csvimporter Categories=Qt;KDE;kmymoney; -Keywords=csvimport;kmymoney; +Keywords=csvimporter;kmymoney; Keywords[ca]=csvimport;kmymoney; Keywords[ca@valencia]=csvimport;kmymoney; Keywords[cs]=csvimport;kmymoney; Keywords[de]=csvimport;kmymoney; Keywords[en_GB]=csvimport;kmymoney; Keywords[es]=csvimport;kmymoney; Keywords[fr]=csvimport;kmymoney; Keywords[gl]=csvimport;kmymoney; Keywords[it]=csvimport;kmymoney; Keywords[nl]=csvimport;kmymoney; Keywords[pl]=csvimport;kmymoney; Keywords[pt]=importação csv;kmymoney; Keywords[pt_BR]=csvimport;kmymoney; Keywords[sk]=csvimport;kmymoney; Keywords[sv]=csv-import;kmymoney; Keywords[tr]=csvimport;kmymoney; Keywords[uk]=csvimport;kmymoney;імпортування;кома;кмаймані; Keywords[x-test]=xxcsvimportxx;xxkmymoneyxx; -X-KDE-Library=kcm_kmm_csvimport -X-KDE-ParentApp=csvimport -X-KDE-ParentComponents=csvimport +X-KDE-Library=kcm_csvimporter +X-KDE-ParentApp=csvimporter +X-KDE-ParentComponents=csvimporter Comment=Configuration for CSV Importer plugin Comment[ca]=Configuració del connector d'importació de CSV Comment[ca@valencia]=Configuració del connector d'importació de CSV Comment[cs]=Nastavení modulu pro import CSV Comment[de]=Einrichtung des CSV-Importmoduls Comment[en_GB]=Configuration for CSV Importer plugin Comment[es]=Configuración para el complemento de importación CSV Comment[fr]=Configurer le module externe d'importation « CSV » Comment[gl]=Configuración do complemento de importación de CSV. Comment[it]=Configurazione dell'estensione di importazione CSV Comment[nl]=Instellingen voor plug-in voor CSV importeren Comment[pl]=Ustawienia dla wtyczki Importera CSV Comment[pt]=Configuração do 'plugin' de Importação de CSV Comment[pt_BR]=Configuração para o plugin de importação CSV Comment[sk]=Nastavenie pre importný plugin CSV Comment[sv]=Anpassa CSV-importinsticksprogram Comment[tr]=CSV içe aktarım eklentisi için yapılandırma Comment[uk]=Налаштування додатка імпортування у форматі CSV Comment[x-test]=xxConfiguration for CSV Importer pluginxx diff --git a/kmymoney/plugins/csv/import/kcm_csvimport.h b/kmymoney/plugins/csv/import/kcm_csvimporter.h similarity index 89% rename from kmymoney/plugins/csv/import/kcm_csvimport.h rename to kmymoney/plugins/csv/import/kcm_csvimporter.h index 303011507..8206813dd 100644 --- a/kmymoney/plugins/csv/import/kcm_csvimport.h +++ b/kmymoney/plugins/csv/import/kcm_csvimporter.h @@ -1,46 +1,42 @@ /*************************************************************************** * Copyright 2016 Łukasz Wojniłowicz lukasz.wojnilowicz@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 * ***************************************************************************/ -#ifndef KCM_CSVIMPORT_H -#define KCM_CSVIMPORT_H +#ifndef KCM_CSVIMPORTER_H +#define KCM_CSVIMPORTER_H #include #include #include "ui_pluginsettingsdecl.h" class PluginSettingsWidget : public QWidget, public Ui::PluginSettingsDecl { Q_OBJECT public: explicit PluginSettingsWidget(QWidget* parent = 0); - -public Q_SLOTS: - -private: }; -class KCMcsvimport : public KCModule +class KCMCSVImporter : public KCModule { public: - explicit KCMcsvimport(QWidget* parent, const QVariantList& args); - ~KCMcsvimport(); + explicit KCMCSVImporter(QWidget* parent, const QVariantList& args); + ~KCMCSVImporter(); }; #endif // KCM_CSVIMPORT_H diff --git a/kmymoney/plugins/csv/import/priceswizardpage.cpp b/kmymoney/plugins/csv/import/priceswizardpage.cpp index de7389c79..e9f25d52b 100644 --- a/kmymoney/plugins/csv/import/priceswizardpage.cpp +++ b/kmymoney/plugins/csv/import/priceswizardpage.cpp @@ -1,234 +1,234 @@ /******************************************************************************* * priceswizardpage.cpp * -------------------- * begin : Sat Jan 21 2017 * copyright : (C) 2016 by Łukasz Wojniłowicz * email : lukasz.wojnilowicz@gmail.com ********************************************************************************/ /******************************************************************************* * * * 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 "priceswizardpage.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyfile.h" #include "mymoneysecurity.h" #include "csvwizard.h" -#include "csvimporter.h" +#include "core/csvimportercore.h" #include "securitydlg.h" #include "currenciesdlg.h" #include "ui_priceswizardpage.h" #include "ui_currenciesdlg.h" #include "ui_securitydlg.h" // ---------------------------------------------------------------------------- -PricesPage::PricesPage(CSVWizard *dlg, CSVImporter *imp) : +PricesPage::PricesPage(CSVWizard *dlg, CSVImporterCore *imp) : CSVWizardPage(dlg, imp), ui(new Ui::PricesPage) { ui->setupUi(this); connect(ui->button_clear, &QAbstractButton::clicked, this, &PricesPage::clearColumns); m_profile = dynamic_cast(m_imp->m_profile); // initialize column names m_dlg->m_colTypeName.insert(Column::Price, i18n("Price")); m_dlg->m_colTypeName.insert(Column::Date, i18n("Date")); connect(ui->m_dateCol, SIGNAL(currentIndexChanged(int)), this, SLOT(dateColSelected(int))); connect(ui->m_priceCol, SIGNAL(currentIndexChanged(int)), this, SLOT(priceColSelected(int))); connect(ui->m_priceFraction, SIGNAL(currentIndexChanged(int)), this, SLOT(fractionChanged(int))); } PricesPage::~PricesPage() { delete m_securityDlg; delete m_currenciesDlg; delete ui; } void PricesPage::initializePage() { const QHash columns {{Column::Price, ui->m_priceCol}, {Column::Date, ui->m_dateCol}}; if (ui->m_dateCol->count() != m_imp->m_file->m_columnCount) m_dlg->initializeComboBoxes(columns); for (auto it = columns.cbegin(); it != columns.cend(); ++it) it.value()->setCurrentIndex(m_profile->m_colTypeNum.value(it.key())); ui->m_priceFraction->blockSignals(true); foreach (const auto priceFraction, m_imp->m_priceFractions) ui->m_priceFraction->addItem(QString::number(priceFraction.toDouble(), 'g', 3)); ui->m_priceFraction->blockSignals(false); ui->m_priceFraction->setCurrentIndex(m_profile->m_priceFraction); QList layout; layout << QWizard::Stretch << QWizard::BackButton << QWizard::NextButton << QWizard::CancelButton; wizard()->setButtonLayout(layout); } bool PricesPage::isComplete() const { return ui->m_dateCol->currentIndex() > -1 && ui->m_priceCol->currentIndex() > -1 && ui->m_priceFraction->currentIndex() > -1; } bool PricesPage::validatePage() { switch (m_imp->m_profile->type()) { case Profile::CurrencyPrices: return validateCurrencies(); case Profile::StockPrices: return validateSecurity(); default: return false; } } void PricesPage::dateColSelected(int col) { validateSelectedColumn(col, Column::Date); } void PricesPage::priceColSelected(int col) { validateSelectedColumn(col, Column::Price); } void PricesPage::fractionChanged(int col) { m_profile->m_priceFraction = col; emit completeChanged(); } void PricesPage::clearColumns() { ui->m_dateCol->setCurrentIndex(-1); ui->m_priceCol->setCurrentIndex(-1); ui->m_priceFraction->setCurrentIndex(-1); } void PricesPage::resetComboBox(const Column comboBox) { switch (comboBox) { case Column::Date: ui->m_dateCol->setCurrentIndex(-1); break; case Column::Price: ui->m_priceCol->setCurrentIndex(-1); break; default: KMessageBox::sorry(m_dlg, i18n("
Field name not recognised.
'%1'
Please re-enter your column selections.", (int)comboBox), i18n("CSV import")); } } bool PricesPage::validateSelectedColumn(const int col, const Column type) { QMap &colTypeNum = m_imp->m_profile->m_colTypeNum; QMap &colNumType = m_imp->m_profile->m_colNumType; if (colTypeNum.value(type) != -1) // check if this 'type' has any column 'number' assigned... colNumType.remove(colTypeNum.value(type)); // ...if true remove 'type' assigned to this column 'number' bool ret = true; if (col == -1) { // user only wanted to reset his column so allow him colTypeNum[type] = col; // assign new column 'number' to this 'type' } else if (colNumType.contains(col)) { // if this column 'number' has already 'type' assigned KMessageBox::information(m_dlg, i18n("The '%1' field already has this column selected.
Please reselect both entries as necessary.
", m_dlg->m_colTypeName.value(colNumType.value(col)))); resetComboBox(colNumType.value(col)); resetComboBox(type); ret = false; } else { colTypeNum[type] = col; // assign new column 'number' to this 'type' colNumType[col] = type; // assign new 'type' to this column 'number' } emit completeChanged(); return ret; } bool PricesPage::validateCurrencies() { if ((m_currenciesDlg.isNull() || !m_imp->validateCurrencies(m_profile)) && !(m_profile->m_dontAsk && m_dlg->m_skipSetup)) { m_currenciesDlg = new CurrenciesDlg; m_currenciesDlg->initializeCurrencies(m_profile->m_currencySymbol, m_profile->m_securitySymbol); m_currenciesDlg->ui->cbDontAsk->setChecked(m_profile->m_dontAsk); } if (!m_currenciesDlg.isNull()) { if (m_currenciesDlg->exec() == QDialog::Rejected) { return false; } else { m_profile->m_currencySymbol = m_currenciesDlg->fromCurrency(); m_profile->m_securitySymbol = m_currenciesDlg->toCurrency(); m_profile->m_dontAsk = m_currenciesDlg->dontAsk(); delete m_currenciesDlg; } } return true; } bool PricesPage::validateSecurity() { if (m_imp->validateSecurity(m_profile)) m_imp->m_mapSymbolName.insert(m_profile->m_securitySymbol, m_profile->m_securityName); MyMoneyFile* file = MyMoneyFile::instance(); if (m_securityDlg.isNull() && (m_imp->m_mapSymbolName.isEmpty() || !(m_profile->m_dontAsk && m_dlg->m_skipSetup))) { m_securityDlg = new SecurityDlg; m_securityDlg->initializeSecurities(m_profile->m_securitySymbol, m_profile->m_securityName); m_securityDlg->ui->cbDontAsk->setChecked(m_profile->m_dontAsk); } if (!m_securityDlg.isNull()) { if (m_securityDlg->exec() == QDialog::Rejected) { return false; } else { QString securityID = m_securityDlg->security(); if (!securityID.isEmpty()) { m_profile->m_securityName = file->security(securityID).name(); m_profile->m_securitySymbol = file->security(securityID).tradingSymbol(); } else { m_profile->m_securityName = m_securityDlg->name(); m_profile->m_securitySymbol = m_securityDlg->symbol(); } m_profile->m_dontAsk = m_securityDlg->dontAsk(); m_imp->m_mapSymbolName.clear(); m_imp->m_mapSymbolName.insert(m_profile->m_securitySymbol, m_profile->m_securityName); // probably we should check if security with given symbol and name exists... delete m_securityDlg; // ...but KMM allows creating duplicates, so don't bother } } if (m_imp->m_mapSymbolName.isEmpty()) return false; return true; } diff --git a/kmymoney/plugins/csv/import/priceswizardpage.h b/kmymoney/plugins/csv/import/priceswizardpage.h index c67931adf..60b1afb0a 100644 --- a/kmymoney/plugins/csv/import/priceswizardpage.h +++ b/kmymoney/plugins/csv/import/priceswizardpage.h @@ -1,88 +1,88 @@ /******************************************************************************* * priceswizardpage.h * ------------------ * begin : Sat Jan 21 2017 * copyright : (C) 2016 by Łukasz Wojniłowicz * email : lukasz.wojnilowicz@gmail.com ********************************************************************************/ /******************************************************************************* * * * 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. * * * ********************************************************************************/ #ifndef PRICESWIZARDPAGE_H #define PRICESWIZARDPAGE_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "csvwizardpage.h" // ---------------------------------------------------------------------------- class PricesProfile; class SecurityDlg; class CurrenciesDlg; namespace Ui { class PricesPage; } class PricesPage : public CSVWizardPage { Q_OBJECT public: - explicit PricesPage(CSVWizard *dlg, CSVImporter *imp); + explicit PricesPage(CSVWizard *dlg, CSVImporterCore *imp); ~PricesPage(); private: void initializePage(); bool isComplete() const; bool validatePage(); void resetComboBox(const Column comboBox); /** * This method is called column on prices page is selected. * It sets m_colTypeNum, m_colNumType and runs column validation. */ bool validateSelectedColumn(const int col, const Column type); /** * This method ensures that there is security for price import. */ bool validateSecurity(); /** * This method ensures that there are currencies for price import. */ bool validateCurrencies(); PricesProfile *m_profile; Ui::PricesPage *ui; QPointer m_securityDlg; QPointer m_currenciesDlg; private Q_SLOTS: void dateColSelected(int col); void priceColSelected(int col); void fractionChanged(int col); void clearColumns(); }; #endif // PRICESWIZARDPAGE_H diff --git a/kmymoney/plugins/gnc/import/CMakeLists.txt b/kmymoney/plugins/gnc/import/CMakeLists.txt index 7741e997f..7d97e30a3 100644 --- a/kmymoney/plugins/gnc/import/CMakeLists.txt +++ b/kmymoney/plugins/gnc/import/CMakeLists.txt @@ -1,34 +1,34 @@ # patch the version with the version defined in the build system -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gncimport.json.in ${CMAKE_CURRENT_BINARY_DIR}/gncimport.json @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gncimporter.json.in ${CMAKE_CURRENT_BINARY_DIR}/gncimporter.json @ONLY) ########### next target ############### -set(kmm_gncimport_PART_SRCS - gncimporterplugin.cpp +set(gncimporter_PART_SRCS + gncimporter.cpp kgncimportoptionsdlg.cpp kgncpricesourcedlg.cpp ../../../widgets/kmymoneymoneyvalidator.cpp mymoneygncreader.cpp ) -set(kmm_gncimport_PART_UI +set(gncimporter_PART_UI kgncimportoptionsdlg.ui kgncpricesourcedlg.ui ) -ki18n_wrap_ui(kmm_gncimport_PART_SRCS ${kmm_gncimport_PART_UI}) +ki18n_wrap_ui(gncimporter_PART_SRCS ${gncimporter_PART_UI}) -add_library(kmm_gncimport MODULE ${kmm_gncimport_PART_SRCS}) +add_library(gncimporter MODULE ${gncimporter_PART_SRCS}) -target_link_libraries(kmm_gncimport +target_link_libraries(gncimporter kmm_plugin Alkimia::alkimia ) ########### install files ############### -install(FILES kmm_gncimport.rc - DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_gncimport") +install(FILES gncimporter.rc + DESTINATION "${KXMLGUI_INSTALL_DIR}/gncimporter") -install(TARGETS kmm_gncimport +install(TARGETS gncimporter DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") diff --git a/kmymoney/plugins/gnc/import/gncimporterplugin.cpp b/kmymoney/plugins/gnc/import/gncimporter.cpp similarity index 75% rename from kmymoney/plugins/gnc/import/gncimporterplugin.cpp rename to kmymoney/plugins/gnc/import/gncimporter.cpp index 755f711e9..058520206 100644 --- a/kmymoney/plugins/gnc/import/gncimporterplugin.cpp +++ b/kmymoney/plugins/gnc/import/gncimporter.cpp @@ -1,83 +1,89 @@ /*************************************************************************** - gncimporterplugin.cpp + gncimporter.cpp ------------------- copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "gncimporterplugin.h" +#include "gncimporter.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneygncreader.h" #include "viewinterface.h" #include "mymoneyfile.h" #include "mymoneyexception.h" #include "mymoneyseqaccessmgr.h" class MyMoneyStatement; -GNCImporterPlugin::GNCImporterPlugin() : - KMyMoneyPlugin::Plugin(nullptr, "gncimport"/*must be the same as X-KDE-PluginInfo-Name*/) +GNCImporter::GNCImporter(QObject *parent, const QVariantList &args) : + KMyMoneyPlugin::Plugin(parent, "gncimporter"/*must be the same as X-KDE-PluginInfo-Name*/) { - setComponentName("kmm_gncimport", i18n("GnuCash importer")); - setXMLFile("kmm_gncimport.rc"); + Q_UNUSED(args) + setComponentName("gncimporter", i18n("GnuCash importer")); + setXMLFile("gncimporter.rc"); createActions(); // For information, announce that we have been loaded. - qDebug("KMyMoney gncimport plugin loaded"); + qDebug("Plugins: gncimporter loaded"); } -GNCImporterPlugin::~GNCImporterPlugin() +GNCImporter::~GNCImporter() { + qDebug("Plugins: gncimporter unloaded"); } -void GNCImporterPlugin::createActions() +void GNCImporter::createActions() { m_action = actionCollection()->addAction("file_import_gnc"); m_action->setText(i18n("GnuCash...")); - connect(m_action, &QAction::triggered, this, &GNCImporterPlugin::slotGNCImport); + connect(m_action, &QAction::triggered, this, &GNCImporter::slotGNCImport); } -void GNCImporterPlugin::slotGNCImport() +void GNCImporter::slotGNCImport() { m_action->setEnabled(false); if (viewInterface()->fileOpen()) { KMessageBox::information(nullptr, i18n("You cannot import GnuCash data into an existing file. Please close it.")); m_action->setEnabled(true); return; } auto url = QFileDialog::getOpenFileUrl(nullptr, QString(), QUrl(), i18n("GnuCash files (*.gnucash *.xac *.gnc);;All files (*)")); if (url.isLocalFile()) { auto pReader = new MyMoneyGncReader; if (viewInterface()->readFile(url, pReader)) viewInterface()->slotRefreshViews(); } m_action->setEnabled(true); } + +K_PLUGIN_FACTORY_WITH_JSON(GNCImporterFactory, "gncimporter.json", registerPlugin();) + +#include "gncimporter.moc" diff --git a/kmymoney/plugins/gnc/import/gncimporterplugin.h b/kmymoney/plugins/gnc/import/gncimporter.h similarity index 84% rename from kmymoney/plugins/gnc/import/gncimporterplugin.h rename to kmymoney/plugins/gnc/import/gncimporter.h index 20970240b..0c68dc015 100644 --- a/kmymoney/plugins/gnc/import/gncimporterplugin.h +++ b/kmymoney/plugins/gnc/import/gncimporter.h @@ -1,61 +1,60 @@ /*************************************************************************** - gncimporterplugin.h + gncimporter.h ------------------- copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ -#ifndef GNCIMPORTERPLUGIN_H -#define GNCIMPORTERPLUGIN_H +#ifndef GNCIMPORTER_H +#define GNCIMPORTER_H // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // QT Includes // Project Includes #include "kmymoneyplugin.h" class MyMoneyGncReader; -class GNCImporterPlugin : public KMyMoneyPlugin::Plugin +class GNCImporter : public KMyMoneyPlugin::Plugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.gncimport" FILE "gncimport.json") public: - explicit GNCImporterPlugin(); - ~GNCImporterPlugin(); + explicit GNCImporter(QObject *parent, const QVariantList &args); + ~GNCImporter() override; QAction *m_action; private: MyMoneyGncReader *m_gncReader; private Q_SLOTS: /** * Called when the user wishes to import tab delimeted transactions * into the current account. An account must be open for this to * work. Calls KMyMoneyView::slotAccountImportAscii. * * @see MyMoneyAccount */ void slotGNCImport(); protected: void createActions(); }; #endif diff --git a/kmymoney/plugins/gnc/import/gncimport.json.in b/kmymoney/plugins/gnc/import/gncimporter.json.in similarity index 95% rename from kmymoney/plugins/gnc/import/gncimport.json.in rename to kmymoney/plugins/gnc/import/gncimporter.json.in index 4db08d84c..cf5c5bcfc 100644 --- a/kmymoney/plugins/gnc/import/gncimport.json.in +++ b/kmymoney/plugins/gnc/import/gncimporter.json.in @@ -1,21 +1,21 @@ { "KPlugin": { "Authors": [ { "Email": "mte@users.sourceforge.net", "Name": "Michael Edwardes" } ], "Description": "Add GnuCash importing to KMyMoney", "EnabledByDefault": true, "Icon": "document-import", - "Id": "gncimport", + "Id": "gncimporter", "License": "GPL", "Name": "GNC Importer", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/gnc/import/kmm_gncimport.rc b/kmymoney/plugins/gnc/import/gncimporter.rc similarity index 100% rename from kmymoney/plugins/gnc/import/kmm_gncimport.rc rename to kmymoney/plugins/gnc/import/gncimporter.rc diff --git a/kmymoney/plugins/icalendar/CMakeLists.txt b/kmymoney/plugins/icalendar/CMakeLists.txt new file mode 100644 index 000000000..e4b3a7d35 --- /dev/null +++ b/kmymoney/plugins/icalendar/CMakeLists.txt @@ -0,0 +1,3 @@ +if(LIBICAL_FOUND) + add_subdirectory( export ) +endif(LIBICAL_FOUND) diff --git a/kmymoney/plugins/icalendar/export/CMakeLists.txt b/kmymoney/plugins/icalendar/export/CMakeLists.txt new file mode 100644 index 000000000..f9f70c084 --- /dev/null +++ b/kmymoney/plugins/icalendar/export/CMakeLists.txt @@ -0,0 +1,54 @@ +# patch the version with the version defined in the build system +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/icalendarexporter.json.in + ${CMAKE_CURRENT_BINARY_DIR}/icalendarexporter.json + @ONLY +) + +set(icalendarexporter_PART_SRCS + schedulestoicalendar.cpp + icalendarexporter.cpp +) + +kconfig_add_kcfg_files(icalendarexporter_PART_SRCS pluginsettings.kcfgc) +add_library(icalendarexporter MODULE ${icalendarexporter_PART_SRCS}) + +target_include_directories(icalendarexporter PRIVATE ${LIBICAL_INCLUDE_DIRS}) +target_link_libraries(icalendarexporter + KF5::KIOWidgets + kmm_mymoney + kmm_plugin + ${LIBICAL_LIBRARIES} +) + +install(FILES icalendarexporter.rc + DESTINATION "${KXMLGUI_INSTALL_DIR}/icalendarexporter") + +install(TARGETS icalendarexporter + DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") + +# the KCM module + +set(kcm_icalendarexporter_PART_SRCS + kcm_icalendarexporter.cpp + ) + +kconfig_add_kcfg_files(kcm_icalendarexporter_PART_SRCS pluginsettings.kcfgc) +ki18n_wrap_ui(kcm_icalendarexporter_PART_SRCS pluginsettingsdecl.ui) + +add_library(kcm_icalendarexporter MODULE ${kcm_icalendarexporter_PART_SRCS}) +kcoreaddons_desktop_to_json(kcm_icalendarexporter kcm_icalendarexporter.desktop) + +target_link_libraries(kcm_icalendarexporter + KF5::I18n + KF5::ConfigWidgets + KF5::KIOWidgets + KF5::Completion + Qt5::Widgets + Qt5::Gui +) + +install(TARGETS kcm_icalendarexporter + DESTINATION ${KDE_INSTALL_PLUGINDIR}) + +install(FILES kcm_icalendarexporter.desktop + DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/kmymoney/plugins/icalendarexport/icalendarexport.cpp b/kmymoney/plugins/icalendar/export/icalendarexporter.cpp similarity index 80% rename from kmymoney/plugins/icalendarexport/icalendarexport.cpp rename to kmymoney/plugins/icalendar/export/icalendarexporter.cpp index eb246f52c..8d1f74875 100644 --- a/kmymoney/plugins/icalendarexport/icalendarexport.cpp +++ b/kmymoney/plugins/icalendar/export/icalendarexporter.cpp @@ -1,137 +1,142 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 "icalendarexport.h" +#include "icalendarexporter.h" #include #include #include // KDE includes #include #include #include #include #include // KMyMoney includes #include "mymoneyfile.h" #include "pluginloader.h" #include "schedulestoicalendar.h" #include "pluginsettings.h" -struct KMMiCalendarExportPlugin::Private { +struct iCalendarExporter::Private { QAction* m_action; QString m_profileName; QString m_iCalendarFileEntryName; KMMSchedulesToiCalendar m_exporter; }; -KMMiCalendarExportPlugin::KMMiCalendarExportPlugin() - : KMyMoneyPlugin::Plugin(nullptr, "iCalendar"/*must be the same as X-KDE-PluginInfo-Name*/), +iCalendarExporter::iCalendarExporter(QObject *parent, const QVariantList &args) : + KMyMoneyPlugin::Plugin(parent, "icalendarexporter"/*must be the same as X-KDE-PluginInfo-Name*/), d(std::unique_ptr(new Private)) { + Q_UNUSED(args); d->m_profileName = "iCalendarPlugin"; d->m_iCalendarFileEntryName = "iCalendarFile"; // Tell the host application to load my GUI component - setComponentName("kmm_icalendarexport", i18n("iCalendar exporter")); - setXMLFile("kmm_icalendarexport.rc"); + setComponentName("icalendarexporter", i18n("iCalendar exporter")); + setXMLFile("icalendarexporter.rc"); // For ease announce that we have been loaded. - qDebug("KMyMoney iCalendar plugin loaded"); + qDebug("Plugins: icalendarexporter loaded"); // Create the actions of this plugin QString actionName = i18n("Schedules to iCalendar"); QString icalFilePath; // Note the below code only exists to move existing settings to the new plugin specific config KConfigGroup config = KSharedConfig::openConfig()->group(d->m_profileName); icalFilePath = config.readEntry(d->m_iCalendarFileEntryName, icalFilePath); // read the settings PluginSettings::self()->load(); if (!icalFilePath.isEmpty()) { // move the old setting to the new config PluginSettings::setIcalendarFile(icalFilePath); PluginSettings::self()->save(); KSharedConfig::openConfig()->deleteGroup(d->m_profileName); } else { // read it from the new config icalFilePath = PluginSettings::icalendarFile(); } if (!icalFilePath.isEmpty()) actionName = i18n("Schedules to iCalendar [%1]", icalFilePath); d->m_action = actionCollection()->addAction("file_export_icalendar"); d->m_action->setText(actionName); - connect(d->m_action, &QAction::triggered, this, &KMMiCalendarExportPlugin::slotFirstExport); + connect(d->m_action, &QAction::triggered, this, &iCalendarExporter::slotFirstExport); } -KMMiCalendarExportPlugin::~KMMiCalendarExportPlugin() +iCalendarExporter::~iCalendarExporter() { + qDebug("Plugins: icalendarexporter unloaded"); } -void KMMiCalendarExportPlugin::slotFirstExport() +void iCalendarExporter::slotFirstExport() { QPointer fileDialog = new QFileDialog(d->m_action->parentWidget(), QString(), QString(), QString("%1|%2\n").arg("*.ics").arg(i18nc("ICS (Filefilter)", "iCalendar files"))); fileDialog->setAcceptMode(QFileDialog::AcceptSave); fileDialog->setWindowTitle(i18n("Export as")); if (fileDialog->exec() == QDialog::Accepted) { QUrl newURL = fileDialog->selectedUrls().front(); if (newURL.isLocalFile()) { PluginSettings::setIcalendarFile(newURL.toLocalFile()); PluginSettings::self()->save(); slotExport(); } } delete fileDialog; } -void KMMiCalendarExportPlugin::slotExport() +void iCalendarExporter::slotExport() { QString icalFilePath = PluginSettings::icalendarFile(); if (!icalFilePath.isEmpty()) d->m_exporter.exportToFile(icalFilePath); } -void KMMiCalendarExportPlugin::plug() +void iCalendarExporter::plug() { - connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, this, &KMMiCalendarExportPlugin::slotExport); + connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, this, &iCalendarExporter::slotExport); } -void KMMiCalendarExportPlugin::unplug() +void iCalendarExporter::unplug() { - disconnect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, this, &KMMiCalendarExportPlugin::slotExport); + disconnect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, this, &iCalendarExporter::slotExport); } -void KMMiCalendarExportPlugin::configurationChanged() +void iCalendarExporter::configurationChanged() { PluginSettings::self()->load(); // export the schedules because the configuration has changed QString icalFilePath = PluginSettings::icalendarFile(); if (!icalFilePath.isEmpty()) d->m_exporter.exportToFile(icalFilePath); } +K_PLUGIN_FACTORY_WITH_JSON(iCalendarExporterFactory, "icalendarexporter.json", registerPlugin();) + +#include "icalendarexporter.moc" diff --git a/kmymoney/plugins/icalendarexport/icalendarexport.h b/kmymoney/plugins/icalendar/export/icalendarexporter.h similarity index 88% rename from kmymoney/plugins/icalendarexport/icalendarexport.h rename to kmymoney/plugins/icalendar/export/icalendarexporter.h index a86922606..fa11fba9e 100644 --- a/kmymoney/plugins/icalendarexport/icalendarexport.h +++ b/kmymoney/plugins/icalendar/export/icalendarexporter.h @@ -1,64 +1,63 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 * ***************************************************************************/ -#ifndef ICALENDAREXPORT_H -#define ICALENDAREXPORT_H +#ifndef ICALENDAREXPORTER_H +#define ICALENDAREXPORTER_H #include #include "kmymoneyplugin.h" #include "mymoneyaccount.h" #include "mymoneykeyvaluecontainer.h" class QStringList; class KPluginInfo; -class KMMiCalendarExportPlugin: public KMyMoneyPlugin::Plugin +class iCalendarExporter: public KMyMoneyPlugin::Plugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.icalendarexport" FILE "kmm_icalendarexport.json") public: - KMMiCalendarExportPlugin(); - ~KMMiCalendarExportPlugin(); + explicit iCalendarExporter(QObject *parent, const QVariantList &args); + ~iCalendarExporter() override; protected Q_SLOTS: // this is the export function called when the user selects the interface menu void slotFirstExport(); // this is the export method called automatically void slotExport(); // the plugin loader plugs in a plugin void plug() override; // the plugin loader unplugs a plugin void unplug() override; // the plugin's configurations has changed void configurationChanged() override; private: struct Private; std::unique_ptr d; }; #endif // ICALENDAREXPORT_H diff --git a/kmymoney/plugins/icalendarexport/kmm_icalendarexport.json.in b/kmymoney/plugins/icalendar/export/icalendarexporter.json.in similarity index 99% rename from kmymoney/plugins/icalendarexport/kmm_icalendarexport.json.in rename to kmymoney/plugins/icalendar/export/icalendarexporter.json.in index a8a23c6ec..f972a2605 100644 --- a/kmymoney/plugins/icalendarexport/kmm_icalendarexport.json.in +++ b/kmymoney/plugins/icalendar/export/icalendarexporter.json.in @@ -1,86 +1,86 @@ { "KPlugin": { "Authors": [ { "Email": "onet.cristian@gmail.com", "Name": "Cristian Oneț" } ], "Description": "Exports schedules to iCalendar files", "Description[ar]": "تصدّر الجداول إلى ملفّات iCalendar", "Description[bs]": "Izvozi rasporede u iCalendar datoteke", "Description[ca@valencia]": "Exporta les programacions als fitxers de l'iCalendar", "Description[ca]": "Exporta les programacions als fitxers de l'iCalendar", "Description[da]": "Eksporterer skemaer til iCalendar-filer", "Description[de]": "Exportiert geplante Buchungen in iCalendar-Dateien", "Description[el]": "Εξαγωγή ημερολογίων σε αρχεία iCalendar", "Description[en_GB]": "Exports schedules to iCalendar files", "Description[es]": "Exporta programaciones a archivos de iCalendar", "Description[et]": "Ajakavade eksport iCalendari failidena", "Description[eu]": "Programazioak iCalendar-en fitxategietara esportatzen ditu", "Description[fi]": "Vie aikataulut iCalendar-tiedostoihin", "Description[fr]": "Exportes des calendriers dans des fichiers « iCalendar »", "Description[gl]": "Exporta planificacións a ficheiros iCalendar", "Description[hu]": "Exportálja az időbeosztásokat iCalendar fájlokba", "Description[it]": "Esporta pianificazioni in file iCalendar", "Description[kk]": "Кестелерін iCalendar файлдарына экспорттау", "Description[nds]": "Planen as iCalendar-Dateien exporteren", "Description[nl]": "Exporteert planningen naar iCalender bestanden", "Description[pl]": "Eksportuje harmonogramy do plików iCalendar", "Description[pt]": "Exporta os calendários para ficheiros iCalendar", "Description[pt_BR]": "Exporta os calendários para arquivos iCalendar", "Description[ro]": "Exportă tranzacțiile programate în fișiere iCalendar", "Description[ru]": "Экспорт плана платежей в файл iCalendar", "Description[sk]": "Exportovať rozvrhy do súborov iCalendar", "Description[sv]": "Exporterar scheman till iCalendar-filer", "Description[tr]": "Zamanlamaları iCalendar dosyalarına aktarır", "Description[uk]": "Експортує розклади до файлів iCalendar", "Description[x-test]": "xxExports schedules to iCalendar filesxx", "Description[zh_TW]": "匯出排程到 iCalendar 檔", "EnabledByDefault": true, "Icon": "x-office-calendar", - "Id": "iCalendar", + "Id": "icalendarexporter", "License": "GPL", "Name": "iCalendar", "Name[bs]": "iCalendar", "Name[ca@valencia]": "iCalendar", "Name[ca]": "iCalendar", "Name[cs]": "iCalendar", "Name[da]": "iCalendar", "Name[de]": "iCalendar", "Name[el]": "iCalendar", "Name[en_GB]": "iCalendar", "Name[es]": "iCalendar", "Name[et]": "iCalendar", "Name[eu]": "iCalendar", "Name[fi]": "iCalendar", "Name[fr]": "iCalendar", "Name[ga]": "iCalendar", "Name[gl]": "iCalendar", "Name[hu]": "iCalendar", "Name[ia]": "iCalendar", "Name[it]": "iCalendar", "Name[kk]": "iCalendar", "Name[lt]": "iCalendar", "Name[nds]": "iCalendar", "Name[nl]": "iCalendar", "Name[pl]": "iCalendar", "Name[pt]": "iCalendar", "Name[pt_BR]": "iCalendar", "Name[ro]": "iCalendar", "Name[ru]": "iCalendar", "Name[sk]": "iCalendar", "Name[sv]": "iCalendar", "Name[tr]": "iCalendar", "Name[ug]": "iCalendar", "Name[uk]": "iCalendar", "Name[x-test]": "xxiCalendarxx", "Name[zh_CN]": "iCalendar", "Name[zh_TW]": "iCalendar", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/icalendarexport/kmm_icalendarexport.rc b/kmymoney/plugins/icalendar/export/icalendarexporter.rc similarity index 100% rename from kmymoney/plugins/icalendarexport/kmm_icalendarexport.rc rename to kmymoney/plugins/icalendar/export/icalendarexporter.rc diff --git a/kmymoney/plugins/icalendarexport/kcm_icalendarexport.cpp b/kmymoney/plugins/icalendar/export/kcm_icalendarexporter.cpp similarity index 88% rename from kmymoney/plugins/icalendarexport/kcm_icalendarexport.cpp rename to kmymoney/plugins/icalendar/export/kcm_icalendarexporter.cpp index 772116813..c610f45e4 100644 --- a/kmymoney/plugins/icalendarexport/kcm_icalendarexport.cpp +++ b/kmymoney/plugins/icalendar/export/kcm_icalendarexporter.cpp @@ -1,84 +1,84 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 "kcm_icalendarexport.h" +#include "kcm_icalendarexporter.h" #include // Qt includes #include #include // KDE includes #include #include #include #include "pluginsettings.h" #include "ui_pluginsettingsdecl.h" class PluginSettingsWidget : public QWidget, public Ui::PluginSettingsDecl { public: PluginSettingsWidget(QWidget* parent = 0) : QWidget(parent) { setupUi(this); kcfg_timeUnitInSeconds->addItem(i18n("Minutes")); kcfg_timeUnitInSeconds->addItem(i18n("Hours")); kcfg_timeUnitInSeconds->addItem(i18nc("Time unit", "Days")); kcfg_intervalBetweenRemindersTimeUnitInSeconds->addItem(i18n("Minutes")); kcfg_intervalBetweenRemindersTimeUnitInSeconds->addItem(i18n("Hours")); kcfg_intervalBetweenRemindersTimeUnitInSeconds->addItem(i18nc("Time unit", "Days")); kcfg_beforeAfter->addItem(i18n("Before")); kcfg_beforeAfter->addItem(i18n("After")); } }; -K_PLUGIN_FACTORY_WITH_JSON(KCMiCalendarExportFactory, - "kcm_kmm_icalendarexport.json", - registerPlugin(); - ) - -KCMiCalendarExport::KCMiCalendarExport(QWidget *parent, const QVariantList& args) +KCMiCalendarExporter::KCMiCalendarExporter(QWidget *parent, const QVariantList& args) : KCModule(parent, args) { KAboutData *about = new KAboutData(QStringLiteral("kmm_printcheck"), i18n("KMyMoney print check"), QStringLiteral(VERSION), QString(), KAboutLicense::GPL, i18n("Copyright 2009" ) ); about->addAuthor( QString::fromUtf8("Cristian Oneț") ); setAboutData( about ); PluginSettingsWidget *w = new PluginSettingsWidget(this); addConfig(PluginSettings::self(), w); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); layout->addWidget(w); load(); } -KCMiCalendarExport::~KCMiCalendarExport() +KCMiCalendarExporter::~KCMiCalendarExporter() { } -#include "kcm_icalendarexport.moc" +K_PLUGIN_FACTORY_WITH_JSON(KCMiCalendarExporterFactory, + "kcm_icalendarexporter.json", + registerPlugin(); + ) + +#include "kcm_icalendarexporter.moc" diff --git a/kmymoney/plugins/icalendarexport/kcm_kmm_icalendarexport.desktop b/kmymoney/plugins/icalendar/export/kcm_icalendarexporter.desktop similarity index 95% rename from kmymoney/plugins/icalendarexport/kcm_kmm_icalendarexport.desktop rename to kmymoney/plugins/icalendar/export/kcm_icalendarexporter.desktop index 399647589..8800a9885 100644 --- a/kmymoney/plugins/icalendarexport/kcm_kmm_icalendarexport.desktop +++ b/kmymoney/plugins/icalendar/export/kcm_icalendarexporter.desktop @@ -1,102 +1,102 @@ [Desktop Entry] Name=Configure icalendar export plugin Name[ar]=اضبط ملحقة تصدير icalendar Name[bs]=Konfiguriši izvozni dodatak za icalendar Name[ca]=Configura el connector d'exportació de l'icalendar Name[ca@valencia]=Configura el connector d'exportació de l'icalendar Name[da]=Indstil plugin til iCalendar-eksport Name[de]=iCalendar-Export-Modul konfigurieren Name[el]=Ρύθμιση πρόσθετου εξαγωγής icalendar Name[en_GB]=Configure icalendar export plugin Name[es]=Configurar el complemento de exportación de icalendar Name[et]=iCalendari ekspordiplugina seadistamine Name[eu]=Konfiguratu icalendar-en esportazio-plugina Name[fi]=Aseta icalendar-vientiliitännäinen -Name[fr]=Configurer le module externe d'exportation « iCalendar » +Name[fr]=Configurer le module externe d'exportation « iCalendar » Name[gl]=Configurar o complemento de exportación a icalendar Name[hu]=icalendar export bővítmény beállítása Name[it]=Configura l'estensione di esportazione iCalendar Name[kk]=icalendar-ға экспортау плагинің баптау Name[lt]=Derinti icalendar eksportavimo papildinį Name[nds]=Exportmoduul för ICalendar instellen Name[nl]=Plugin voor exporteren van icalendar instellen Name[pl]=Ustawienia wtyczki eksportu icalendar Name[pt]=Configurar o 'plugin' de exportação para iCalendar Name[pt_BR]=Configurar o plugin de exportação para iCalendar Name[ro]=Configurează modulul de exportare în calendar Name[ru]=Настройка экспорта в формат icalendar Name[sk]=Nastaviť exportný plugin icalendar Name[sv]=Anpassa iCalendar-exportinsticksprogram Name[tr]=iCalendar dışa aktarma eklentisini yapılandır Name[uk]=Налаштувати додаток експорту до icalendar Name[x-test]=xxConfigure icalendar export pluginxx Name[zh_TW]=設定 icalendar 匯出外掛程式 Icon=x-office-calendar Type=Service ServiceTypes=KCModule -Exec=kcmshell4 kmm_icalendarexport +Exec=kcmshell5 icalendarexporter Categories=Qt;KDE;kmymoney; -Keywords=icalendar;kmymoney; +Keywords=icalendarexporter;kmymoney; Keywords[bs]=icalendar;kmymoney; Keywords[ca]=icalendar;kmymoney; Keywords[ca@valencia]=icalendar;kmymoney; Keywords[cs]=icalendar;kmymoney; Keywords[da]=icalendar;kmymoney; Keywords[de]=icalendar;kmymoney; Keywords[el]=icalendar;kmymoney; Keywords[en_GB]=icalendar;kmymoney; Keywords[es]=icalendar;kmymoney; Keywords[et]=icalendar;kmymoney; Keywords[eu]=icalendar;kmymoney; Keywords[fi]=icalendar;kmymoney; Keywords[fr]=iCalendar;kmymoney; Keywords[gl]=icalendar;kmymoney; Keywords[hu]=icalendar;kmymoney; Keywords[it]=icalendar;kmymoney; Keywords[kk]=icalendar;kmymoney; Keywords[lt]=icalendar;kmymoney; Keywords[nl]=icalendar;kmymoney; Keywords[pl]=icalendar;kmymoney; Keywords[pt]=icalendar;kmymoney; Keywords[pt_BR]=icalendar;kmymoney; Keywords[ro]=icalendar;kmymoney; Keywords[ru]=icalendar;kmymoney; Keywords[sk]=icalendar;kmymoney; Keywords[sv]=icalendar;kmymoney; Keywords[tr]=icalendar;kmymoney; Keywords[uk]=icalendar;kmymoney; Keywords[x-test]=xxicalendarxx;xxkmymoneyxx; Keywords[zh_TW]=icalendar;kmymoney; -X-KDE-Library=kcm_kmm_icalendarexport -X-KDE-ParentApp=iCalendar -X-KDE-ParentComponents=iCalendar +X-KDE-Library=kcm_icalendarexporter +X-KDE-ParentApp=icalendarexporter +X-KDE-ParentComponents=icalendarexporter Comment=Permits the configuration of the iCalendar plugin's options Comment[bs]=Dopušta konfiguraciju opcija iCalendar dodatka Comment[ca]=Permet la configuració de les opcions del connector iCalendar Comment[ca@valencia]=Permet la configuració de les opcions del connector iCalendar Comment[da]=Muliggør konfiguration af indstillingerne til iCalendar-pluginet Comment[de]=Erlaubt die Konfiguration des iCalendar-Moduls Comment[el]=Επιτρέπει την ρύθμιση των επιλογών του πρόσθετου iCalendar Comment[en_GB]=Permits the configuration of the iCalendar plugin's options Comment[es]=Permite la configuración de las opciones del complemento de iCalendar Comment[et]=iCalendari plugina valikute seadistamise lubamine Comment[eu]=iCalendar-en pluginak konfiguratzeko aukera ematen du Comment[fi]=Sallii iCalendar-asetusten liitännäisen valitsimet Comment[fr]=Permet la configuration des options du module externe iCalendar Comment[gl]=Permite a configuración das opcións do complemento para iCalendar Comment[hu]=Engedélyezi az iCalendar-bővítmény beállításainak módosítását Comment[it]=Consente la configurazione delle opzioni dell'estensione iCalendar Comment[kk]=iCalendar плагинін параметрлерін баптауын рұқсат ету Comment[nds]=Optschonen för dat iCalendarmoduul instellen Comment[nl]=Stelt in staat om opties voor plugins van iCalender in te stellen Comment[pl]=Pozwala na ustawienie wtyczki iCalendar Comment[pt]=Permite a configuração das opções do 'plugin' de iCalendar Comment[pt_BR]=Permite a configuração das opções do plugin de iCalendar Comment[ro]=Permite configurarea opțiunilor modulului de exportare în calendar Comment[ru]=Настройка модуля iCalendar Comment[sk]=Umožní nastavenie možností pluginu iCalendar Comment[sv]=Möjliggör inställning av alternativ för iCalendar-insticksprogrammet Comment[tr]=iCalendar eklentisinin seçeneklerinin yapılandırılmasına izin verir Comment[uk]=Надає змогу налаштовувати параметри додатка iCalendar Comment[x-test]=xxPermits the configuration of the iCalendar plugin's optionsxx Comment[zh_TW]=允許 iCalendar 外掛程式的設定選項 diff --git a/kmymoney/plugins/icalendarexport/kcm_icalendarexport.h b/kmymoney/plugins/icalendar/export/kcm_icalendarexporter.h similarity index 87% rename from kmymoney/plugins/icalendarexport/kcm_icalendarexport.h rename to kmymoney/plugins/icalendar/export/kcm_icalendarexporter.h index eda78f5a1..83e5d1870 100644 --- a/kmymoney/plugins/icalendarexport/kcm_icalendarexport.h +++ b/kmymoney/plugins/icalendar/export/kcm_icalendarexporter.h @@ -1,37 +1,37 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 * ***************************************************************************/ -#ifndef KCM_ICALENDAREXPORT_H -#define KCM_ICALENDAREXPORT_H +#ifndef KCM_ICALENDAREXPORTER_H +#define KCM_ICALENDAREXPORTER_H #include class QStringList; class KPluginInfo; -class KCMiCalendarExport : public KCModule +class KCMiCalendarExporter : public KCModule { public: - explicit KCMiCalendarExport(QWidget *parent = 0, const QVariantList& args = QVariantList()); - ~KCMiCalendarExport(); + explicit KCMiCalendarExporter(QWidget *parent = 0, const QVariantList& args = QVariantList()); + ~KCMiCalendarExporter(); }; #endif // KCM_ICALENDAREXPORT_H diff --git a/kmymoney/plugins/icalendarexport/pluginsettings.kcfg b/kmymoney/plugins/icalendar/export/pluginsettings.kcfg similarity index 100% rename from kmymoney/plugins/icalendarexport/pluginsettings.kcfg rename to kmymoney/plugins/icalendar/export/pluginsettings.kcfg diff --git a/kmymoney/plugins/icalendarexport/pluginsettings.kcfgc b/kmymoney/plugins/icalendar/export/pluginsettings.kcfgc similarity index 100% rename from kmymoney/plugins/icalendarexport/pluginsettings.kcfgc rename to kmymoney/plugins/icalendar/export/pluginsettings.kcfgc diff --git a/kmymoney/plugins/icalendarexport/pluginsettingsdecl.ui b/kmymoney/plugins/icalendar/export/pluginsettingsdecl.ui similarity index 100% rename from kmymoney/plugins/icalendarexport/pluginsettingsdecl.ui rename to kmymoney/plugins/icalendar/export/pluginsettingsdecl.ui diff --git a/kmymoney/plugins/icalendarexport/schedulestoicalendar.cpp b/kmymoney/plugins/icalendar/export/schedulestoicalendar.cpp similarity index 100% rename from kmymoney/plugins/icalendarexport/schedulestoicalendar.cpp rename to kmymoney/plugins/icalendar/export/schedulestoicalendar.cpp diff --git a/kmymoney/plugins/icalendarexport/schedulestoicalendar.h b/kmymoney/plugins/icalendar/export/schedulestoicalendar.h similarity index 100% rename from kmymoney/plugins/icalendarexport/schedulestoicalendar.h rename to kmymoney/plugins/icalendar/export/schedulestoicalendar.h diff --git a/kmymoney/plugins/icalendarexport/CMakeLists.txt b/kmymoney/plugins/icalendarexport/CMakeLists.txt deleted file mode 100644 index e30245f4e..000000000 --- a/kmymoney/plugins/icalendarexport/CMakeLists.txt +++ /dev/null @@ -1,54 +0,0 @@ -# patch the version with the version defined in the build system -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmm_icalendarexport.json.in - ${CMAKE_CURRENT_BINARY_DIR}/kmm_icalendarexport.json - @ONLY -) - -set(kmm_icalendarexport_PART_SRCS - schedulestoicalendar.cpp - icalendarexport.cpp -) - -kconfig_add_kcfg_files(kmm_icalendarexport_PART_SRCS pluginsettings.kcfgc) -add_library(kmm_icalendarexport MODULE ${kmm_icalendarexport_PART_SRCS}) - -target_include_directories(kmm_icalendarexport PRIVATE ${LIBICAL_INCLUDE_DIRS}) -target_link_libraries(kmm_icalendarexport - KF5::KIOWidgets - kmm_mymoney - kmm_plugin - ${LIBICAL_LIBRARIES} -) - -install(FILES kmm_icalendarexport.rc - DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_icalendarexport") - -install(TARGETS kmm_icalendarexport - DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") - -# the KCM module - -set(kcm_kmm_icalendarexport_PART_SRCS - kcm_icalendarexport.cpp - ) - -kconfig_add_kcfg_files(kcm_kmm_icalendarexport_PART_SRCS pluginsettings.kcfgc) -ki18n_wrap_ui(kcm_kmm_icalendarexport_PART_SRCS pluginsettingsdecl.ui) - -add_library(kcm_kmm_icalendarexport MODULE ${kcm_kmm_icalendarexport_PART_SRCS}) -kcoreaddons_desktop_to_json(kcm_kmm_icalendarexport kcm_kmm_icalendarexport.desktop) - -target_link_libraries(kcm_kmm_icalendarexport - KF5::I18n - KF5::ConfigWidgets - KF5::KIOWidgets - KF5::Completion - Qt5::Widgets - Qt5::Gui -) - -install(TARGETS kcm_kmm_icalendarexport - DESTINATION ${KDE_INSTALL_PLUGINDIR}) - -install(FILES kcm_kmm_icalendarexport.desktop - DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/kmymoney/plugins/kbanking/CMakeLists.txt b/kmymoney/plugins/kbanking/CMakeLists.txt index 827e946ef..045c6076e 100644 --- a/kmymoney/plugins/kbanking/CMakeLists.txt +++ b/kmymoney/plugins/kbanking/CMakeLists.txt @@ -1,61 +1,61 @@ # patch the version with the version defined in the build system configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kbanking.json.in ${CMAKE_CURRENT_BINARY_DIR}/kbanking.json @ONLY) add_subdirectory( dialogs ) add_subdirectory( widgets ) add_subdirectory( qml ) add_subdirectory( tasksettings ) -set(kmm_kbanking_PART_SRCS - mymoneybanking.cpp +set(kbanking_PART_SRCS + kbanking.cpp ../../mymoney/onlinejobmessage.cpp banking.cpp aqbankingkmmoperators.cpp gwenhywfarqtoperators.cpp gwenkdegui.cpp ) -kconfig_add_kcfg_files(kmm_kbanking_PART_SRCS kbankingsettings.kcfgc) +kconfig_add_kcfg_files(kbanking_PART_SRCS kbankingsettings.kcfgc) -add_library(kmm_kbanking MODULE ${kmm_kbanking_PART_SRCS}) +add_library(kbanking MODULE ${kbanking_PART_SRCS}) -target_link_libraries(kmm_kbanking - kmm_kbanking_dialogs - kmm_kbanking_widgets - kmm_kbanking_tasksettings +target_link_libraries(kbanking + kbanking_dialogs + kbanking_widgets + kbanking_tasksettings kmm_mymoney kmm_widgets kmm_plugin payeeidentifier_nationalAccount KF5::ConfigCore KF5::ConfigGui Qt5::Core Alkimia::alkimia gwenhywfar::core gwenhywfar::gui-cpp gwenhywfar::gui-qt5 aqbanking::aqbanking ) # List might not be complete -target_compile_features(kmm_kbanking PRIVATE cxx_generalized_initializers cxx_nullptr) +target_compile_features(kbanking PRIVATE cxx_generalized_initializers cxx_nullptr) # Add new menu entries if debugging is enabled set(KMM_BANKING_DEBUG_OPTIONS) # preprocessor definitions in case this is a debug build string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER) if("${CMAKE_BUILD_TYPE_TOLOWER}" MATCHES "debug") - file(READ kmm_kbanking_debug.rc KMM_BANKING_DEBUG_OPTIONS) + file(READ kbanking_debug.rc KMM_BANKING_DEBUG_OPTIONS) endif() -configure_file(kmm_kbanking.rc "${CMAKE_CURRENT_BINARY_DIR}/kmm_kbanking.rc" @ONLY) +configure_file(kbanking.rc "${CMAKE_CURRENT_BINARY_DIR}/kbanking.rc" @ONLY) ########### install files ############### -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kmm_kbanking.rc - DESTINATION ${KXMLGUI_INSTALL_DIR}/kmm_kbanking) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kbanking.rc + DESTINATION ${KXMLGUI_INSTALL_DIR}/kbanking) -install(TARGETS kmm_kbanking +install(TARGETS kbanking DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") install(FILES kbanking.kcfg DESTINATION ${KCFG_INSTALL_DIR}) diff --git a/kmymoney/plugins/kbanking/dialogs/CMakeLists.txt b/kmymoney/plugins/kbanking/dialogs/CMakeLists.txt index 45690c231..ca9f10379 100644 --- a/kmymoney/plugins/kbanking/dialogs/CMakeLists.txt +++ b/kmymoney/plugins/kbanking/dialogs/CMakeLists.txt @@ -1,24 +1,24 @@ ########### next target ############### -set(kmm_kbanking_dialogs_la_SOURCES +set(kbanking_dialogs_la_SOURCES kbaccountsettings.cpp kbpickstartdate.cpp kbmapaccount.cpp ) -set(kmm_kbanking_dialogs_UI +set(kbanking_dialogs_UI kbaccountsettings.ui kbpickstartdate.ui kbmapaccount.ui ) -ki18n_wrap_ui(kmm_kbanking_dialogs_la_SOURCES ${kmm_kbanking_dialogs_UI}) +ki18n_wrap_ui(kbanking_dialogs_la_SOURCES ${kbanking_dialogs_UI}) -add_library(kmm_kbanking_dialogs STATIC ${kmm_kbanking_dialogs_la_SOURCES}) -target_link_libraries(kmm_kbanking_dialogs +add_library(kbanking_dialogs STATIC ${kbanking_dialogs_la_SOURCES}) +target_link_libraries(kbanking_dialogs widgets dialogs aqbanking::aqbanking ) diff --git a/kmymoney/plugins/kbanking/dialogs/kbmapaccount.cpp b/kmymoney/plugins/kbanking/dialogs/kbmapaccount.cpp index 193287659..d265380cb 100644 --- a/kmymoney/plugins/kbanking/dialogs/kbmapaccount.cpp +++ b/kmymoney/plugins/kbanking/dialogs/kbmapaccount.cpp @@ -1,111 +1,111 @@ /*************************************************************************** begin : Mon Mar 01 2004 copyright : (C) 2004 by Martin Preuss email : martin@libchipcard.de *************************************************************************** * * * 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. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif // QBanking includes #include "kbmapaccount.h" #include "../widgets/kbaccountlist.h" -#include "../mymoneybanking.h" +#include "../kbanking.h" // QT includes #include #include #include #include #include #include "ui_kbmapaccount.h" struct KBMapAccount::Private { Ui::KBMapAccount ui; - KMyMoneyBanking *banking; + KBankingExt *banking; AB_ACCOUNT *account; }; -KBMapAccount::KBMapAccount(KMyMoneyBanking *kb, +KBMapAccount::KBMapAccount(KBankingExt *kb, const char *bankCode, const char *accountId, QWidget* parent, Qt::WindowFlags fl) : QDialog(parent, fl), d(new Private) { d->banking = kb; d->account = 0; d->ui.setupUi(this); d->ui.accountList->setSelectionMode(QAbstractItemView::SingleSelection); if (bankCode) d->ui.bankCodeEdit->setText(QString::fromUtf8(bankCode)); else d->ui.bankCodeEdit->setEnabled(false); if (accountId) d->ui.accountIdEdit->setText(QString::fromUtf8(accountId)); else d->ui.accountIdEdit->setEnabled(false); QObject::connect(d->ui.accountList, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged())); QObject::connect(d->ui.helpButton, SIGNAL(clicked()), this, SLOT(slotHelpClicked())); d->ui.accountList->addAccounts(d->banking->getAccounts()); } KBMapAccount::~KBMapAccount() { delete d; } AB_ACCOUNT *KBMapAccount::getAccount() { return d->account; } void KBMapAccount::accept() { if (d->account) QDialog::accept(); } void KBMapAccount::slotSelectionChanged() { std::list al; AB_ACCOUNT *a; al = d->ui.accountList->getSelectedAccounts(); if (al.empty()) { d->ui.assignButton->setEnabled(false); d->account = 0; return; } a = al.front(); if (AB_Account_GetUniqueId(a) != 0) { d->account = a; d->ui.assignButton->setEnabled(true); } else d->ui.assignButton->setEnabled(false); } void KBMapAccount::slotHelpClicked() { } diff --git a/kmymoney/plugins/kbanking/dialogs/kbmapaccount.h b/kmymoney/plugins/kbanking/dialogs/kbmapaccount.h index c215667d4..cc8fcac62 100644 --- a/kmymoney/plugins/kbanking/dialogs/kbmapaccount.h +++ b/kmymoney/plugins/kbanking/dialogs/kbmapaccount.h @@ -1,60 +1,60 @@ /*************************************************************************** begin : Mon Mar 01 2004 copyright : (C) 2004 by Martin Preuss email : martin@libchipcard.de *************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KBMAPACCOUNT_H #define KBMAPACCOUNT_H #include #include -class KMyMoneyBanking; +class KBankingExt; class KBMapAccount: public QDialog { Q_OBJECT public: - KBMapAccount(KMyMoneyBanking *kb, + KBMapAccount(KBankingExt *kb, const char *bankCode, const char *accountId, QWidget* parent = 0, Qt::WindowFlags fl = 0); ~KBMapAccount(); AB_ACCOUNT *getAccount(); void accept(); protected Q_SLOTS: void slotSelectionChanged(); void slotHelpClicked(); private: /// \internal d-pointer class. struct Private; /// \internal d-pointer instance. Private* const d; /* KMyMoneyBanking *_banking; AB_ACCOUNT *_account; KBAccountListView *_accountList; */ }; #endif /* QBANKING_MAPACCOUNT_H */ diff --git a/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.cpp b/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.cpp index 0542d19f0..335f5b3cd 100644 --- a/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.cpp +++ b/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.cpp @@ -1,147 +1,147 @@ /*************************************************************************** * Copyright 2009 Thomas Baumgart ipwizard@users.sourceforge.net * * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * Copyright 2004 Martin Preuss aquamaniac@users.sourceforge.net * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 * ***************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif // QBanking includes #include "kbpickstartdate.h" // Gwenhywfar includes #include // QT includes #include #include #include #include // KDE includes #include // KMyMoney includes #include "kmymoneydateinput.h" #include "ui_kbpickstartdate.h" struct KBPickStartDate::Private { Ui::KBPickStartDate ui; - KMyMoneyBanking *banking; + KBankingExt *banking; QDate firstPossible; QDate lastUpdate; }; -KBPickStartDate::KBPickStartDate(KMyMoneyBanking* qb, +KBPickStartDate::KBPickStartDate(KBankingExt* qb, const QDate &firstPossible, const QDate &lastUpdate, const QString& accountName, int defaultChoice, QWidget* parent, bool modal) : QDialog(parent), d(new Private) { d->ui.setupUi(this); d->firstPossible = firstPossible; d->lastUpdate = lastUpdate; setModal(modal); d->banking = qb; QObject::connect(d->ui.buttonBox, SIGNAL(helpRequested()), this, SLOT(slotHelpClicked())); d->ui.label->setText(i18n("

Please select the first date for which transactions are to be retrieved from %1.

If you specify no date then the bank will choose one.

", accountName)); if (lastUpdate.isValid()) { d->ui.lastUpdateLabel->setText(lastUpdate.toString()); d->ui.lastUpdateButton->setEnabled(true); d->ui.lastUpdateLabel->setEnabled(true); } else { d->ui.lastUpdateButton->setEnabled(false); d->ui.lastUpdateLabel->setEnabled(false); if (defaultChoice == 2) defaultChoice = 1; } if (firstPossible.isValid()) { d->ui.firstDateLabel->setText(firstPossible.toString()); d->ui.firstDateButton->setEnabled(true); d->ui.firstDateLabel->setEnabled(true); // As long as we use the KDateWidget we don't have // a chance to control the range. Once we are able // to use a KMyMoneyDateInput widget, we can make use // of the setRange() method again. // d->ui.pickDateEdit->setRange(firstPossible, QDate()); } else { d->ui.firstDateButton->setEnabled(false); d->ui.firstDateLabel->setEnabled(false); if (defaultChoice == 3) defaultChoice = 1; } switch (defaultChoice) { case 2: d->ui.lastUpdateButton->setChecked(true); break; case 3: d->ui.firstDateButton->setChecked(true); break; default: d->ui.noDateButton->setChecked(true); break; } d->ui.pickDateEdit->setDate(QDate::currentDate()); d->ui.buttonGroup->setFocus(); } KBPickStartDate::~KBPickStartDate() { delete d; } QDate KBPickStartDate::date() { if (d->ui.noDateButton->isChecked()) return QDate(); else if (d->ui.firstDateButton->isChecked()) return d->firstPossible; else if (d->ui.pickDateButton->isChecked()) return d->ui.pickDateEdit->date(); else if (d->ui.lastUpdateButton->isChecked()) return d->lastUpdate; else { DBG_ERROR(0, "Unknown date state"); return QDate(); } } void KBPickStartDate::slotHelpClicked() { } diff --git a/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.h b/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.h index 33363185a..b88491fa1 100644 --- a/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.h +++ b/kmymoney/plugins/kbanking/dialogs/kbpickstartdate.h @@ -1,61 +1,61 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * Copyright 2004 Martin Preuss aquamaniac@users.sourceforge.net * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 * ***************************************************************************/ #ifndef KBPICKSTARTDATE_H #define KBPICKSTARTDATE_H #include #include -class KMyMoneyBanking; +class KBankingExt; /** * Class derived from QBPickStartDate and modified to * be based on KDE widgets * * @author Martin Preuss * @author Thomas Baumgart */ class KBPickStartDate : public QDialog { Q_OBJECT public: - KBPickStartDate(KMyMoneyBanking* qb, + KBPickStartDate(KBankingExt* qb, const QDate &firstPossible, const QDate &lastUpdate, const QString& accountName, int defaultChoice, QWidget* parent = 0, bool modal = false); ~KBPickStartDate(); QDate date(); public Q_SLOTS: void slotHelpClicked(); private: /// \internal d-pointer class. struct Private; /// \internal d-pointer instance. Private* const d; }; #endif diff --git a/kmymoney/plugins/kbanking/mymoneybanking.cpp b/kmymoney/plugins/kbanking/kbanking.cpp similarity index 92% rename from kmymoney/plugins/kbanking/mymoneybanking.cpp rename to kmymoney/plugins/kbanking/kbanking.cpp index f5a4ea1c0..7882a49ec 100644 --- a/kmymoney/plugins/kbanking/mymoneybanking.cpp +++ b/kmymoney/plugins/kbanking/kbanking.cpp @@ -1,1515 +1,1522 @@ /*************************************************************************** * Copyright 2004 Martin Preuss aquamaniac@users.sourceforge.net * * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * Copyright 2010 Thomas Baumgart ipwizard@users.sourceforge.net * * Copyright 2015 Christian David christian-david@web.de * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 "mymoneybanking.h" +#include "kbanking.h" #include // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include //! @todo remove @c #include // ---------------------------------------------------------------------------- // KDE Includes +#include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Library Includes #include #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoney/onlinejob.h" #include "kbaccountsettings.h" #include "kbmapaccount.h" #include "mymoneyfile.h" #include "onlinejobadministration.h" #include "kmymoneyview.h" #include "kbpickstartdate.h" #include "mymoneyinstitution.h" #include "gwenkdegui.h" #include "gwenhywfarqtoperators.h" #include "aqbankingkmmoperators.h" #include "mymoneystatement.h" #include "statementinterface.h" #include "viewinterface.h" #ifdef KMM_DEBUG // Added an option to open the chipTanDialog from the menu for debugging purposes #include "chiptandialog.h" #endif -class KBankingPlugin::Private +class KBanking::Private { public: Private() : passwordCacheTimer(nullptr), jobList(), fileId() { QString gwenProxy = QString::fromLocal8Bit(qgetenv("GWEN_PROXY")); if (gwenProxy.isEmpty()) { std::unique_ptr cfg = std::unique_ptr(new KConfig("kioslaverc")); QRegExp exp("(\\w+://)?([^/]{2}.+:\\d+)"); QString proxy; KConfigGroup grp = cfg->group("Proxy Settings"); int type = grp.readEntry("ProxyType", 0); switch (type) { case 0: // no proxy break; case 1: // manual specified proxy = grp.readEntry("httpsProxy"); qDebug("KDE https proxy setting is '%s'", qPrintable(proxy)); if (exp.exactMatch(proxy)) { proxy = exp.cap(2); qDebug("Setting GWEN_PROXY to '%s'", qPrintable(proxy)); if (setenv("GWEN_PROXY", qPrintable(proxy), 1) == -1) { qDebug("Unable to setup GWEN_PROXY"); } } break; default: // other currently not supported qDebug("KDE proxy setting of type %d not supported", type); break; } } } /** * KMyMoney asks for accounts over and over again which causes a lot of "Job not supported with this account" error messages. * This function filters messages with that string. */ static int gwenLogHook(GWEN_GUI* gui, const char* domain, GWEN_LOGGER_LEVEL level, const char* message) { Q_UNUSED(gui); Q_UNUSED(domain); Q_UNUSED(level); const char* messageToFilter = "Job not supported with this account"; if (strstr(message, messageToFilter) != 0) return 1; return 0; } QTimer *passwordCacheTimer; QMap jobList; QString fileId; }; -KBankingPlugin::KBankingPlugin() - : KMyMoneyPlugin::OnlinePluginExtended(nullptr, "KBanking"/*must be the same as X-KDE-PluginInfo-Name*/) +KBanking::KBanking(QObject *parent, const QVariantList &args) : + OnlinePluginExtended(parent, "kbanking") , d(new Private) , m_configAction(nullptr) , m_importAction(nullptr) , m_kbanking(nullptr) , m_accountSettings(nullptr) { + Q_UNUSED(args) + qDebug("Plugins: kbanking loaded"); } -KBankingPlugin::~KBankingPlugin() +KBanking::~KBanking() { delete d; + qDebug("Plugins: kbanking unloaded"); } -void KBankingPlugin::plug() +void KBanking::plug() { - m_kbanking = new KMyMoneyBanking(this, "KMyMoney"); + m_kbanking = new KBankingExt(this, "KMyMoney"); d->passwordCacheTimer = new QTimer(this); d->passwordCacheTimer->setSingleShot(true); d->passwordCacheTimer->setInterval(60000); - connect(d->passwordCacheTimer, &QTimer::timeout, this, &KBankingPlugin::slotClearPasswordCache); + connect(d->passwordCacheTimer, &QTimer::timeout, this, &KBanking::slotClearPasswordCache); if (m_kbanking) { if (AB_Banking_HasConf4(m_kbanking->getCInterface())) { qDebug("KBankingPlugin: No AqB4 config found."); if (AB_Banking_HasConf3(m_kbanking->getCInterface())) { qDebug("KBankingPlugin: No AqB3 config found."); if (!AB_Banking_HasConf2(m_kbanking->getCInterface())) { qDebug("KBankingPlugin: AqB2 config found - converting."); AB_Banking_ImportConf2(m_kbanking->getCInterface()); } } else { qDebug("KBankingPlugin: AqB3 config found - converting."); AB_Banking_ImportConf3(m_kbanking->getCInterface()); } } //! @todo when is gwenKdeGui deleted? gwenKdeGui *gui = new gwenKdeGui(); GWEN_Gui_SetGui(gui->getCInterface()); GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning); if (m_kbanking->init() == 0) { // Tell the host application to load my GUI component - setComponentName("kmm_kbanking", "KBanking"); - setXMLFile("kmm_kbanking.rc"); - qDebug("KMyMoney kbanking plugin loaded"); + setComponentName("kbanking", "KBanking"); + setXMLFile("kbanking.rc"); + qDebug("Plugins: kbanking pluged"); // get certificate handling and dialog settings management AB_Gui_Extend(gui->getCInterface(), m_kbanking->getCInterface()); // create actions createActions(); // load protocol conversion list loadProtocolConversion(); GWEN_Logger_SetLevel(AQBANKING_LOGDOMAIN, GWEN_LoggerLevel_Warning); - GWEN_Gui_SetLogHookFn(GWEN_Gui_GetGui(), &KBankingPlugin::Private::gwenLogHook); + GWEN_Gui_SetLogHookFn(GWEN_Gui_GetGui(), &KBanking::Private::gwenLogHook); } else { qWarning("Could not initialize KBanking online banking interface"); delete m_kbanking; m_kbanking = 0; } } } -void KBankingPlugin::unplug() +void KBanking::unplug() { d->passwordCacheTimer->deleteLater(); if (m_kbanking) { m_kbanking->fini(); delete m_kbanking; + qDebug("Plugins: kbanking unpluged"); } } -void KBankingPlugin::loadProtocolConversion() +void KBanking::loadProtocolConversion() { if (m_kbanking) { m_protocolConversionMap = { {"aqhbci", "HBCI"}, {"aqofxconnect", "OFX"}, {"aqyellownet", "YellowNet"}, {"aqgeldkarte", "Geldkarte"}, {"aqdtaus", "DTAUS"} }; } } -void KBankingPlugin::protocols(QStringList& protocolList) const +void KBanking::protocols(QStringList& protocolList) const { if (m_kbanking) { std::list list = m_kbanking->getActiveProviders(); std::list::iterator it; for (it = list.begin(); it != list.end(); ++it) { // skip the dummy if (*it == "aqnone") continue; QMap::const_iterator it_m; it_m = m_protocolConversionMap.find((*it).c_str()); if (it_m != m_protocolConversionMap.end()) protocolList << (*it_m); else protocolList << (*it).c_str(); } } } -QWidget* KBankingPlugin::accountConfigTab(const MyMoneyAccount& acc, QString& name) +QWidget* KBanking::accountConfigTab(const MyMoneyAccount& acc, QString& name) { const MyMoneyKeyValueContainer& kvp = acc.onlineBankingSettings(); name = i18n("Online settings"); if (m_kbanking) { m_accountSettings = new KBAccountSettings(acc, 0); m_accountSettings->loadUi(kvp); return m_accountSettings; } QLabel* label = new QLabel(i18n("KBanking module not correctly initialized"), 0); label->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); return label; } -MyMoneyKeyValueContainer KBankingPlugin::onlineBankingSettings(const MyMoneyKeyValueContainer& current) +MyMoneyKeyValueContainer KBanking::onlineBankingSettings(const MyMoneyKeyValueContainer& current) { MyMoneyKeyValueContainer kvp(current); kvp["provider"] = objectName(); if (m_accountSettings) { m_accountSettings->loadKvp(kvp); } return kvp; } -void KBankingPlugin::createActions() +void KBanking::createActions() { QAction *settings_aqbanking = actionCollection()->addAction("settings_aqbanking"); settings_aqbanking->setText(i18n("Configure Aq&Banking...")); - connect(settings_aqbanking, &QAction::triggered, this, &KBankingPlugin::slotSettings); + connect(settings_aqbanking, &QAction::triggered, this, &KBanking::slotSettings); QAction *file_import_aqbanking = actionCollection()->addAction("file_import_aqbanking"); file_import_aqbanking->setText(i18n("AqBanking importer...")); - connect(file_import_aqbanking, &QAction::triggered, this, &KBankingPlugin::slotImport); + connect(file_import_aqbanking, &QAction::triggered, this, &KBanking::slotImport); Q_CHECK_PTR(viewInterface()); connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::viewStateChanged, action("file_import_aqbanking"), &QAction::setEnabled); #ifdef KMM_DEBUG QAction *openChipTanDialog = actionCollection()->addAction("open_chiptan_dialog"); openChipTanDialog->setText("Open ChipTan Dialog"); connect(openChipTanDialog, &QAction::triggered, [&](){ auto dlg = new chipTanDialog(); dlg->setHhdCode("0F04871100030333555414312C32331D"); dlg->setInfoText("

Test Graphic for debugging

The encoded data is

Account Number: 335554
Amount: 1,23

"); connect(dlg, &QDialog::accepted, dlg, &chipTanDialog::deleteLater); connect(dlg, &QDialog::rejected, dlg, &chipTanDialog::deleteLater); dlg->show(); }); #endif } -void KBankingPlugin::slotSettings() +void KBanking::slotSettings() { if (m_kbanking) { GWEN_DIALOG* dlg = AB_SetupDialog_new(m_kbanking->getCInterface()); if (dlg == NULL) { DBG_ERROR(0, "Could not create setup dialog."); return; } if (GWEN_Gui_ExecDialog(dlg, 0) == 0) { DBG_ERROR(0, "Aborted by user"); GWEN_Dialog_free(dlg); return; } GWEN_Dialog_free(dlg); } } -bool KBankingPlugin::mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& settings) +bool KBanking::mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& settings) { bool rc = false; if (m_kbanking && !acc.id().isEmpty()) { m_kbanking->askMapAccount(acc); // at this point, the account should be mapped // so we search it and setup the account reference in the KMyMoney object AB_ACCOUNT* ab_acc; ab_acc = aqbAccount(acc); if (ab_acc) { MyMoneyAccount a(acc); setupAccountReference(a, ab_acc); settings = a.onlineBankingSettings(); rc = true; } } return rc; } -AB_ACCOUNT* KBankingPlugin::aqbAccount(const MyMoneyAccount& acc) const +AB_ACCOUNT* KBanking::aqbAccount(const MyMoneyAccount& acc) const { if (m_kbanking == 0) { return 0; } // certainly looking for an expense or income account does not make sense at this point // so we better get out right away if (acc.isIncomeExpense()) { return 0; } AB_ACCOUNT *ab_acc = AB_Banking_GetAccountByAlias(m_kbanking->getCInterface(), m_kbanking->mappingId(acc).toUtf8().data()); // if the account is not found, we temporarily scan for the 'old' mapping (the one w/o the file id) // in case we find it, we setup the new mapping in addition on the fly. if (!ab_acc && acc.isAssetLiability()) { ab_acc = AB_Banking_GetAccountByAlias(m_kbanking->getCInterface(), acc.id().toUtf8().data()); if (ab_acc) { qDebug("Found old mapping for '%s' but not new. Setup new mapping", qPrintable(acc.name())); m_kbanking->setAccountAlias(ab_acc, m_kbanking->mappingId(acc).toUtf8().constData()); // TODO at some point in time, we should remove the old mapping } } return ab_acc; } -AB_ACCOUNT* KBankingPlugin::aqbAccount(const QString& accountId) const +AB_ACCOUNT* KBanking::aqbAccount(const QString& accountId) const { MyMoneyAccount account = MyMoneyFile::instance()->account(accountId); return aqbAccount(account); } -QString KBankingPlugin::stripLeadingZeroes(const QString& s) const +QString KBanking::stripLeadingZeroes(const QString& s) const { QString rc(s); QRegExp exp("^(0*)([^0].*)"); if (exp.exactMatch(s)) { rc = exp.cap(2); } return rc; } -void KBankingPlugin::setupAccountReference(const MyMoneyAccount& acc, AB_ACCOUNT* ab_acc) +void KBanking::setupAccountReference(const MyMoneyAccount& acc, AB_ACCOUNT* ab_acc) { MyMoneyKeyValueContainer kvp; if (ab_acc) { QString accountNumber = stripLeadingZeroes(AB_Account_GetAccountNumber(ab_acc)); QString routingNumber = stripLeadingZeroes(AB_Account_GetBankCode(ab_acc)); QString val = QString("%1-%2").arg(routingNumber, accountNumber); if (val != acc.onlineBankingSettings().value("kbanking-acc-ref")) { MyMoneyKeyValueContainer kvp; // make sure to keep our own previous settings const QMap& vals = acc.onlineBankingSettings().pairs(); QMap::const_iterator it_p; for (it_p = vals.begin(); it_p != vals.end(); ++it_p) { if (QString(it_p.key()).startsWith("kbanking-")) { kvp.setValue(it_p.key(), *it_p); } } kvp.setValue("kbanking-acc-ref", val); kvp.setValue("provider", objectName()); setAccountOnlineParameters(acc, kvp); } } else { // clear the connection setAccountOnlineParameters(acc, kvp); } } -bool KBankingPlugin::accountIsMapped(const MyMoneyAccount& acc) +bool KBanking::accountIsMapped(const MyMoneyAccount& acc) { return aqbAccount(acc) != 0; } -bool KBankingPlugin::updateAccount(const MyMoneyAccount& acc) +bool KBanking::updateAccount(const MyMoneyAccount& acc) { return updateAccount(acc, false); } -bool KBankingPlugin::updateAccount(const MyMoneyAccount& acc, bool moreAccounts) +bool KBanking::updateAccount(const MyMoneyAccount& acc, bool moreAccounts) { if (!m_kbanking) return false; bool rc = false; if (!acc.id().isEmpty()) { AB_JOB *job = 0; int rv; /* get AqBanking account */ AB_ACCOUNT *ba = aqbAccount(acc); // Update the connection between the KMyMoney account and the AqBanking equivalent. // If the account is not found anymore ba == 0 and the connection is removed. setupAccountReference(acc, ba); if (!ba) { KMessageBox::error(0, i18n("" "The given application account %1 " "has not been mapped to an online " "account." "", acc.name()), i18n("Account Not Mapped")); } else { bool enqueJob = true; if (acc.onlineBankingSettings().value("kbanking-txn-download") != "no") { /* create getTransactions job */ job = AB_JobGetTransactions_new(ba); rv = AB_Job_CheckAvailability(job); if (rv) { DBG_ERROR(0, "Job \"GetTransactions\" is not available (%d)", rv); KMessageBox::error(0, i18n("" "The update job is not supported by the " "bank/account/backend.\n" ""), i18n("Job not Available")); AB_Job_free(job); job = 0; } if (job) { int days = AB_JobGetTransactions_GetMaxStoreDays(job); QDate qd; if (days > 0) { GWEN_TIME *ti1; GWEN_TIME *ti2; ti1 = GWEN_CurrentTime(); ti2 = GWEN_Time_fromSeconds(GWEN_Time_Seconds(ti1) - (60 * 60 * 24 * days)); GWEN_Time_free(ti1); ti1 = ti2; int year, month, day; if (GWEN_Time_GetBrokenDownDate(ti1, &day, &month, &year)) { DBG_ERROR(0, "Bad date"); qd = QDate(); } else qd = QDate(year, month + 1, day); GWEN_Time_free(ti1); } // get last statement request date from application account object // and start from a few days before if the date is valid QDate lastUpdate = QDate::fromString(acc.value("lastImportedTransactionDate"), Qt::ISODate); if (lastUpdate.isValid()) lastUpdate = lastUpdate.addDays(-3); int dateOption = acc.onlineBankingSettings().value("kbanking-statementDate").toInt(); switch (dateOption) { case 0: // Ask user break; case 1: // No date qd = QDate(); break; case 2: // Last download qd = lastUpdate; break; case 3: // First possible // qd is already setup break; } // the pick start date option dialog is needed in // case the dateOption is 0 or the date option is > 1 // and the qd is invalid if (dateOption == 0 || (dateOption > 1 && !qd.isValid())) { QPointer psd = new KBPickStartDate(m_kbanking, qd, lastUpdate, acc.name(), lastUpdate.isValid() ? 2 : 3, 0, true); if (psd->exec() == QDialog::Accepted) { qd = psd->date(); } else { enqueJob = false; } delete psd; } if (enqueJob) { if (qd.isValid()) { GWEN_TIME *ti1; ti1 = GWEN_Time_new(qd.year(), qd.month() - 1, qd.day(), 0, 0, 0, 0); AB_JobGetTransactions_SetFromTime(job, ti1); GWEN_Time_free(ti1); } rv = m_kbanking->enqueueJob(job); if (rv) { DBG_ERROR(0, "Error %d", rv); KMessageBox::error(0, i18n("" "Could not enqueue the job.\n" ""), i18n("Error")); } } AB_Job_free(job); } } if (enqueJob) { /* create getBalance job */ job = AB_JobGetBalance_new(ba); rv = AB_Job_CheckAvailability(job); if (!rv) rv = m_kbanking->enqueueJob(job); else rv = 0; AB_Job_free(job); if (rv) { DBG_ERROR(0, "Error %d", rv); KMessageBox::error(0, i18n("" "Could not enqueue the job.\n" ""), i18n("Error")); } else { rc = true; emit queueChanged(); } } } } // make sure we have at least one job in the queue before sending it if (!moreAccounts && m_kbanking->getEnqueuedJobs().size() > 0) executeQueue(); return rc; } -void KBankingPlugin::executeQueue() +void KBanking::executeQueue() { if (m_kbanking && m_kbanking->getEnqueuedJobs().size() > 0) { AB_IMEXPORTER_CONTEXT *ctx; ctx = AB_ImExporterContext_new(); int rv = m_kbanking->executeQueue(ctx); if (!rv) { m_kbanking->importContext(ctx, 0); } else { DBG_ERROR(0, "Error: %d", rv); } AB_ImExporterContext_free(ctx); } } /** @todo improve error handling, e.g. by adding a .isValid to nationalTransfer * @todo use new onlineJob system */ -void KBankingPlugin::sendOnlineJob(QList& jobs) +void KBanking::sendOnlineJob(QList& jobs) { Q_CHECK_PTR(m_kbanking); m_onlineJobQueue.clear(); QList unhandledJobs; if (!jobs.isEmpty()) { foreach (onlineJob job, jobs) { if (sepaOnlineTransfer::name() == job.task()->taskName()) { onlineJobTyped typedJob(job); enqueTransaction(typedJob); job = typedJob; } else { job.addJobMessage(onlineJobMessage(eMyMoney::OnlineJob::MessageType::Error, "KBanking", "Cannot handle this request")); unhandledJobs.append(job); } m_onlineJobQueue.insert(m_kbanking->mappingId(job), job); } executeQueue(); } jobs = m_onlineJobQueue.values() + unhandledJobs; m_onlineJobQueue.clear(); } -QStringList KBankingPlugin::availableJobs(QString accountId) +QStringList KBanking::availableJobs(QString accountId) { try { MyMoneyAccount acc = MyMoneyFile::instance()->account(accountId); QString id = MyMoneyFile::instance()->value("kmm-id"); if(id != d->fileId) { d->jobList.clear(); d->fileId = id; } } catch (const MyMoneyException&) { // Exception usually means account was not found return QStringList(); } if(d->jobList.contains(accountId)) { return d->jobList[accountId]; } QStringList list; AB_ACCOUNT* abAccount = aqbAccount(accountId); if (!abAccount) { return list; } // Check availableJobs // sepa transfer AB_JOB* abJob = AB_JobSepaTransfer_new(abAccount); if (AB_Job_CheckAvailability(abJob) == 0) list.append(sepaOnlineTransfer::name()); AB_Job_free(abJob); d->jobList[accountId] = list; return list; } /** @brief experimenting with QScopedPointer and aqBanking pointers */ class QScopedPointerAbJobDeleter { public: static void cleanup(AB_JOB* job) { AB_Job_free(job); } }; /** @brief experimenting with QScopedPointer and aqBanking pointers */ class QScopedPointerAbAccountDeleter { public: static void cleanup(AB_ACCOUNT* account) { AB_Account_free(account); } }; -IonlineTaskSettings::ptr KBankingPlugin::settings(QString accountId, QString taskName) +IonlineTaskSettings::ptr KBanking::settings(QString accountId, QString taskName) { AB_ACCOUNT* abAcc = aqbAccount(accountId); if (abAcc == 0) return IonlineTaskSettings::ptr(); if (sepaOnlineTransfer::name() == taskName) { // Get limits for sepaonlinetransfer QScopedPointer abJob(AB_JobSepaTransfer_new(abAcc)); if (AB_Job_CheckAvailability(abJob.data()) != 0) return IonlineTaskSettings::ptr(); const AB_TRANSACTION_LIMITS* limits = AB_Job_GetFieldLimits(abJob.data()); return AB_TransactionLimits_toSepaOnlineTaskSettings(limits).dynamicCast(); } return IonlineTaskSettings::ptr(); } -bool KBankingPlugin::enqueTransaction(onlineJobTyped& job) +bool KBanking::enqueTransaction(onlineJobTyped& job) { /* get AqBanking account */ const QString accId = job.constTask()->responsibleAccount(); AB_ACCOUNT *abAccount = aqbAccount(accId); if (!abAccount) { job.addJobMessage(onlineJobMessage(eMyMoney::OnlineJob::MessageType::Warning, "KBanking", i18n("" "The given application account %1 " "has not been mapped to an online " "account." "", MyMoneyFile::instance()->account(accId).name()))); return false; } //setupAccountReference(acc, ba); // needed? AB_JOB *abJob = AB_JobSepaTransfer_new(abAccount); int rv = AB_Job_CheckAvailability(abJob); if (rv) { qDebug("AB_ERROR_OFFSET is %i", AB_ERROR_OFFSET); job.addJobMessage(onlineJobMessage(eMyMoney::OnlineJob::MessageType::Error, "AqBanking", QString("Sepa credit transfers for account \"%1\" are not available, error code %2.").arg(MyMoneyFile::instance()->account(accId).name(), rv) ) ); return false; } AB_TRANSACTION *AbTransaction = AB_Transaction_new(); // Recipient payeeIdentifiers::ibanBic beneficiaryAcc = job.constTask()->beneficiaryTyped(); AB_Transaction_SetRemoteName(AbTransaction, GWEN_StringList_fromQString(beneficiaryAcc.ownerName())); AB_Transaction_SetRemoteIban(AbTransaction, beneficiaryAcc.electronicIban().toUtf8().constData()); AB_Transaction_SetRemoteBic(AbTransaction, beneficiaryAcc.fullStoredBic().toUtf8().constData()); // Origin Account AB_Transaction_SetLocalAccount(AbTransaction, abAccount); // Purpose QStringList qPurpose = job.constTask()->purpose().split('\n'); GWEN_STRINGLIST *purpose = GWEN_StringList_fromQStringList(qPurpose); AB_Transaction_SetPurpose(AbTransaction, purpose); GWEN_StringList_free(purpose); // Reference // AqBanking duplicates the string. This should be safe. AB_Transaction_SetEndToEndReference(AbTransaction, job.constTask()->endToEndReference().toUtf8().constData()); // Other Fields AB_Transaction_SetTextKey(AbTransaction, job.constTask()->textKey()); AB_Transaction_SetValue(AbTransaction, AB_Value_fromMyMoneyMoney(job.constTask()->value())); /** @todo LOW remove Debug info */ qDebug() << "SetTransaction: " << AB_Job_SetTransaction(abJob, AbTransaction); GWEN_DB_NODE *gwenNode = AB_Job_GetAppData(abJob); GWEN_DB_SetCharValue(gwenNode, GWEN_DB_FLAGS_DEFAULT, "kmmOnlineJobId", m_kbanking->mappingId(job).toLatin1().constData()); qDebug() << "Enqueue: " << m_kbanking->enqueueJob(abJob); //delete localAcc; return true; } -void KBankingPlugin::startPasswordTimer() +void KBanking::startPasswordTimer() { if (d->passwordCacheTimer->isActive()) d->passwordCacheTimer->stop(); d->passwordCacheTimer->start(); } -void KBankingPlugin::slotClearPasswordCache() +void KBanking::slotClearPasswordCache() { m_kbanking->clearPasswordCache(); } -void KBankingPlugin::slotImport() +void KBanking::slotImport() { if (!m_kbanking->interactiveImport()) qWarning("Error on import dialog"); } -bool KBankingPlugin::importStatement(const MyMoneyStatement& s) +bool KBanking::importStatement(const MyMoneyStatement& s) { return statementInterface()->import(s); } -MyMoneyAccount KBankingPlugin::account(const QString& key, const QString& value) const +MyMoneyAccount KBanking::account(const QString& key, const QString& value) const { return statementInterface()->account(key, value); } -void KBankingPlugin::setAccountOnlineParameters(const MyMoneyAccount& acc, const MyMoneyKeyValueContainer& kvps) const +void KBanking::setAccountOnlineParameters(const MyMoneyAccount& acc, const MyMoneyKeyValueContainer& kvps) const { return statementInterface()->setAccountOnlineParameters(acc, kvps); } -KMyMoneyBanking::KMyMoneyBanking(KBankingPlugin* parent, const char* appname, const char* fname) +KBankingExt::KBankingExt(KBanking* parent, const char* appname, const char* fname) : AB_Banking(appname, fname) , m_parent(parent) , _jobQueue(0) { m_sepaKeywords = {QString::fromUtf8("SEPA-BASISLASTSCHRIFT"), QString::fromUtf8("SEPA-ÜBERWEISUNG")}; } -int KMyMoneyBanking::init() +int KBankingExt::init() { int rv = AB_Banking::init(); if (rv < 0) return rv; rv = onlineInit(); if (rv) { fprintf(stderr, "Error on online init (%d).\n", rv); AB_Banking::fini(); return rv; } _jobQueue = AB_Job_List2_new(); return 0; } -int KMyMoneyBanking::fini() +int KBankingExt::fini() { if (_jobQueue) { AB_Job_List2_FreeAll(_jobQueue); _jobQueue = 0; } const int rv = onlineFini(); if (rv) { AB_Banking::fini(); return rv; } return AB_Banking::fini(); } -int KMyMoneyBanking::executeQueue(AB_IMEXPORTER_CONTEXT *ctx) +int KBankingExt::executeQueue(AB_IMEXPORTER_CONTEXT *ctx) { m_parent->startPasswordTimer(); int rv = AB_Banking::executeJobs(_jobQueue, ctx); if (rv != 0) { qDebug() << "Sending queue by aqbanking got error no " << rv; } /** check result of each job */ AB_JOB_LIST2_ITERATOR* jobIter = AB_Job_List2_First(_jobQueue); if (jobIter) { AB_JOB* abJob = AB_Job_List2Iterator_Data(jobIter); while (abJob) { GWEN_DB_NODE* gwenNode = AB_Job_GetAppData(abJob); if (gwenNode == 0) { qWarning("Executed AB_Job without KMyMoney id"); abJob = AB_Job_List2Iterator_Next(jobIter); break; } QString jobIdent = QString::fromUtf8(GWEN_DB_GetCharValue(gwenNode, "kmmOnlineJobId", 0, "")); onlineJob job = m_parent->m_onlineJobQueue.value(jobIdent); if (job.isNull()) { // It should not be possiblie that this will happen (only if AqBanking fails heavily). //! @todo correct exception text qWarning("Executed a job which was not in queue. Please inform the KMyMoney developers."); abJob = AB_Job_List2Iterator_Next(jobIter); continue; } AB_JOB_STATUS abStatus = AB_Job_GetStatus(abJob); if (abStatus == AB_Job_StatusSent || abStatus == AB_Job_StatusPending || abStatus == AB_Job_StatusFinished || abStatus == AB_Job_StatusError || abStatus == AB_Job_StatusUnknown) job.setJobSend(); if (abStatus == AB_Job_StatusFinished) job.setBankAnswer(onlineJob::acceptedByBank); else if (abStatus == AB_Job_StatusError || abStatus == AB_Job_StatusUnknown) job.setBankAnswer(onlineJob::sendingError); job.addJobMessage(onlineJobMessage(eMyMoney::OnlineJob::MessageType::Debug, "KBanking", "Job was processed")); m_parent->m_onlineJobQueue.insert(jobIdent, job); abJob = AB_Job_List2Iterator_Next(jobIter); } AB_Job_List2Iterator_free(jobIter); } AB_JOB_LIST2 *oldQ = _jobQueue; _jobQueue = AB_Job_List2_new(); AB_Job_List2_FreeAll(oldQ); emit m_parent->queueChanged(); m_parent->startPasswordTimer(); return rv; } -void KMyMoneyBanking::clearPasswordCache() +void KBankingExt::clearPasswordCache() { /* clear password DB */ GWEN_Gui_SetPasswordStatus(NULL, NULL, GWEN_Gui_PasswordStatus_Remove, 0); } -std::list KMyMoneyBanking::getEnqueuedJobs() +std::list KBankingExt::getEnqueuedJobs() { AB_JOB_LIST2 *ll; std::list rl; ll = _jobQueue; if (ll && AB_Job_List2_GetSize(ll)) { AB_JOB *j; AB_JOB_LIST2_ITERATOR *it; it = AB_Job_List2_First(ll); assert(it); j = AB_Job_List2Iterator_Data(it); assert(j); while (j) { rl.push_back(j); j = AB_Job_List2Iterator_Next(it); } AB_Job_List2Iterator_free(it); } return rl; } -int KMyMoneyBanking::enqueueJob(AB_JOB *j) +int KBankingExt::enqueueJob(AB_JOB *j) { assert(_jobQueue); assert(j); AB_Job_Attach(j); AB_Job_List2_PushBack(_jobQueue, j); return 0; } -int KMyMoneyBanking::dequeueJob(AB_JOB *j) +int KBankingExt::dequeueJob(AB_JOB *j) { assert(_jobQueue); AB_Job_List2_Remove(_jobQueue, j); AB_Job_free(j); emit m_parent->queueChanged(); return 0; } -void KMyMoneyBanking::transfer() +void KBankingExt::transfer() { //m_parent->transfer(); } -bool KMyMoneyBanking::askMapAccount(const MyMoneyAccount& acc) +bool KBankingExt::askMapAccount(const MyMoneyAccount& acc) { MyMoneyFile* file = MyMoneyFile::instance(); QString bankId; QString accountId; // extract some information about the bank. if we have a sortcode // (BLZ) we display it, otherwise the name is enough. try { const MyMoneyInstitution &bank = file->institution(acc.institutionId()); bankId = bank.name(); if (!bank.sortcode().isEmpty()) bankId = bank.sortcode(); } catch (const MyMoneyException &e) { // no bank assigned, we just leave the field emtpy } // extract account information. if we have an account number // we show it, otherwise the name will be displayed accountId = acc.number(); if (accountId.isEmpty()) accountId = acc.name(); // do the mapping. the return value of this method is either // true, when the user mapped the account or false, if he // decided to quit the dialog. So not really a great thing // to present some more information. KBMapAccount *w; w = new KBMapAccount(this, bankId.toUtf8().constData(), accountId.toUtf8().constData()); if (w->exec() == QDialog::Accepted) { AB_ACCOUNT *a; a = w->getAccount(); assert(a); DBG_NOTICE(0, "Mapping application account \"%s\" to " "online account \"%s/%s\"", qPrintable(acc.name()), AB_Account_GetBankCode(a), AB_Account_GetAccountNumber(a)); // TODO remove the following line once we don't need backward compatibility setAccountAlias(a, acc.id().toUtf8().constData()); qDebug("Setup mapping to '%s'", acc.id().toUtf8().constData()); setAccountAlias(a, mappingId(acc).toUtf8().constData()); qDebug("Setup mapping to '%s'", mappingId(acc).toUtf8().constData()); delete w; return true; } delete w; return false; } -QString KMyMoneyBanking::mappingId(const MyMoneyObject& object) const +QString KBankingExt::mappingId(const MyMoneyObject& object) const { QString id = MyMoneyFile::instance()->storageId() + QLatin1Char('-') + object.id(); // AqBanking does not handle the enclosing parens, so we remove it id.remove('{'); id.remove('}'); return id; } -bool KMyMoneyBanking::interactiveImport() +bool KBankingExt::interactiveImport() { AB_IMEXPORTER_CONTEXT *ctx; GWEN_DIALOG *dlg; int rv; ctx = AB_ImExporterContext_new(); dlg = AB_ImporterDialog_new(getCInterface(), ctx, NULL); if (dlg == NULL) { DBG_ERROR(0, "Could not create importer dialog."); AB_ImExporterContext_free(ctx); return false; } rv = GWEN_Gui_ExecDialog(dlg, 0); if (rv == 0) { DBG_ERROR(0, "Aborted by user"); GWEN_Dialog_free(dlg); AB_ImExporterContext_free(ctx); return false; } if (!importContext(ctx, 0)) { DBG_ERROR(0, "Error on importContext"); GWEN_Dialog_free(dlg); AB_ImExporterContext_free(ctx); return false; } GWEN_Dialog_free(dlg); AB_ImExporterContext_free(ctx); return true; } -const AB_ACCOUNT_STATUS* KMyMoneyBanking::_getAccountStatus(AB_IMEXPORTER_ACCOUNTINFO *ai) +const AB_ACCOUNT_STATUS* KBankingExt::_getAccountStatus(AB_IMEXPORTER_ACCOUNTINFO *ai) { const AB_ACCOUNT_STATUS *ast; const AB_ACCOUNT_STATUS *best; best = 0; ast = AB_ImExporterAccountInfo_GetFirstAccountStatus(ai); while (ast) { if (!best) best = ast; else { const GWEN_TIME *tiBest; const GWEN_TIME *ti; tiBest = AB_AccountStatus_GetTime(best); ti = AB_AccountStatus_GetTime(ast); if (!tiBest) { best = ast; } else { if (ti) { double d; /* we have two times, compare them */ d = GWEN_Time_Diff(ti, tiBest); if (d > 0) /* newer */ best = ast; } } } ast = AB_ImExporterAccountInfo_GetNextAccountStatus(ai); } /* while */ return best; } -void KMyMoneyBanking::_xaToStatement(MyMoneyStatement &ks, +void KBankingExt::_xaToStatement(MyMoneyStatement &ks, const MyMoneyAccount& acc, const AB_TRANSACTION *t) { const GWEN_STRINGLIST *sl; QString s; QString memo; const char *p; const AB_VALUE *val; const GWEN_TIME *ti; const GWEN_TIME *startTime = 0; MyMoneyStatement::Transaction kt; unsigned long h; kt.m_fees = MyMoneyMoney(); // bank's transaction id p = AB_Transaction_GetFiId(t); if (p) kt.m_strBankID = QString("ID ") + QString::fromUtf8(p); // payee s.truncate(0); sl = AB_Transaction_GetRemoteName(t); if (sl) { GWEN_STRINGLISTENTRY *se; se = GWEN_StringList_FirstEntry(sl); while (se) { p = GWEN_StringListEntry_Data(se); assert(p); s += QString::fromUtf8(p); se = GWEN_StringListEntry_Next(se); } // while } kt.m_strPayee = s; // memo // The variable 's' contains the old method of extracting // the memo which added a linefeed after each part received // from AqBanking. The new variable 'memo' does not have // this inserted linefeed. We keep the variable 's' to // construct the hash-value to retrieve the reference s.truncate(0); sl = AB_Transaction_GetPurpose(t); if (sl) { GWEN_STRINGLISTENTRY *se; bool insertLineSep = false; se = GWEN_StringList_FirstEntry(sl); while (se) { p = GWEN_StringListEntry_Data(se); assert(p); if (insertLineSep) s += '\n'; insertLineSep = true; s += QString::fromUtf8(p).trimmed(); memo += QString::fromUtf8(p).trimmed(); se = GWEN_StringListEntry_Next(se); } // while // Sparda / Netbank hack: the software these banks use stores // parts of the payee name in the beginning of the purpose field // in case the payee name exceeds the 27 character limit. This is // the case, when one of the strings listed in m_sepaKeywords is part // of the purpose fields but does not start at the beginning. In this // case, the part leading up to the keyword is to be treated as the // tail of the payee. Also, a blank is inserted after the keyword. QSet::const_iterator itk; for (itk = m_sepaKeywords.constBegin(); itk != m_sepaKeywords.constEnd(); ++itk) { int idx = s.indexOf(*itk); if (idx >= 0) { if (idx > 0) { // re-add a possibly removed blank to name if (kt.m_strPayee.length() < 27) kt.m_strPayee += ' '; kt.m_strPayee += s.left(idx); s = s.mid(idx); } s = QString("%1 %2").arg(*itk).arg(s.mid((*itk).length())); // now do the same for 'memo' except for updating the payee idx = memo.indexOf(*itk); if (idx >= 0) { if (idx > 0) { memo = memo.mid(idx); } } memo = QString("%1 %2").arg(*itk).arg(memo.mid((*itk).length())); break; } } // in case we have some SEPA fields filled with information // we add them to the memo field p = AB_Transaction_GetEndToEndReference(t); if (p) { s += QString(", EREF: %1").arg(p); if(memo.length()) memo.append('\n'); memo.append(QString("EREF: %1").arg(p)); } p = AB_Transaction_GetCustomerReference(t); if (p) { s += QString(", CREF: %1").arg(p); if(memo.length()) memo.append('\n'); memo.append(QString("CREF: %1").arg(p)); } p = AB_Transaction_GetMandateId(t); if (p) { s += QString(", MREF: %1").arg(p); if(memo.length()) memo.append('\n'); memo.append(QString("MREF: %1").arg(p)); } p = AB_Transaction_GetCreditorSchemeId(t); if (p) { s += QString(", CRED: %1").arg(p); if(memo.length()) memo.append('\n'); memo.append(QString("CRED: %1").arg(p)); } p = AB_Transaction_GetOriginatorIdentifier(t); if (p) { s += QString(", DEBT: %1").arg(p); if(memo.length()) memo.append('\n'); memo.append(QString("DEBT: %1").arg(p)); } } kt.m_strMemo = memo; // calculate the hash code and start with the payee info // and append the memo field h = MyMoneyTransaction::hash(kt.m_strPayee.trimmed()); h = MyMoneyTransaction::hash(s, h); // see, if we need to extract the payee from the memo field const MyMoneyKeyValueContainer& kvp = acc.onlineBankingSettings(); QString rePayee = kvp.value("kbanking-payee-regexp"); if (!rePayee.isEmpty() && kt.m_strPayee.isEmpty()) { QString reMemo = kvp.value("kbanking-memo-regexp"); QStringList exceptions = kvp.value("kbanking-payee-exceptions").split(';', QString::SkipEmptyParts); bool needExtract = true; QStringList::const_iterator it_s; for (it_s = exceptions.constBegin(); needExtract && it_s != exceptions.constEnd(); ++it_s) { QRegExp exp(*it_s, Qt::CaseInsensitive); if (exp.indexIn(kt.m_strMemo) != -1) { needExtract = false; } } if (needExtract) { QRegExp expPayee(rePayee, Qt::CaseInsensitive); QRegExp expMemo(reMemo, Qt::CaseInsensitive); if (expPayee.indexIn(kt.m_strMemo) != -1) { kt.m_strPayee = expPayee.cap(1); if (expMemo.indexIn(kt.m_strMemo) != -1) { kt.m_strMemo = expMemo.cap(1); } } } } kt.m_strPayee = kt.m_strPayee.trimmed(); // date ti = AB_Transaction_GetDate(t); if (!ti) ti = AB_Transaction_GetValutaDate(t); if (ti) { int year, month, day; if (!startTime) startTime = ti; else { if (GWEN_Time_Diff(ti, startTime) < 0) startTime = ti; } if (!GWEN_Time_GetBrokenDownDate(ti, &day, &month, &year)) { kt.m_datePosted = QDate(year, month + 1, day); } } else { DBG_WARN(0, "No date for transaction"); } // value val = AB_Transaction_GetValue(t); if (val) { if (ks.m_strCurrency.isEmpty()) { p = AB_Value_GetCurrency(val); if (p) ks.m_strCurrency = p; } else { p = AB_Value_GetCurrency(val); if (p) s = p; if (ks.m_strCurrency.toLower() != s.toLower()) { // TODO: handle currency difference DBG_ERROR(0, "Mixed currencies currently not allowed"); } } kt.m_amount = MyMoneyMoney(AB_Value_GetValueAsDouble(val)); // The initial implementation of this feature was based on // a denominator of 100. Since the denominator might be // different nowadays, we make sure to use 100 for the // duplicate detection QString tmpVal = kt.m_amount.formatMoney(100, false); tmpVal.remove(QRegExp("[,\\.]")); tmpVal += QLatin1String("/100"); h = MyMoneyTransaction::hash(tmpVal, h); } else { DBG_WARN(0, "No value for transaction"); } if (startTime) { int year, month, day; if (!GWEN_Time_GetBrokenDownDate(startTime, &day, &month, &year)) { QDate d(year, month + 1, day); if (!ks.m_dateBegin.isValid()) ks.m_dateBegin = d; else if (d < ks.m_dateBegin) ks.m_dateBegin = d; if (!ks.m_dateEnd.isValid()) ks.m_dateEnd = d; else if (d > ks.m_dateEnd) ks.m_dateEnd = d; } } else { DBG_WARN(0, "No date in current transaction"); } // add information about remote account to memo in case we have something const char *remoteAcc = AB_Transaction_GetRemoteAccountNumber(t); const char *remoteBankCode = AB_Transaction_GetRemoteBankCode(t); if (remoteAcc && remoteBankCode) { kt.m_strMemo += QString("\n%1/%2").arg(remoteBankCode, remoteAcc); } // make hash value unique in case we don't have one already if (kt.m_strBankID.isEmpty()) { QString hashBase; hashBase.sprintf("%s-%07lx", qPrintable(kt.m_datePosted.toString(Qt::ISODate)), h); int idx = 1; QString hash; for (;;) { hash = QString("%1-%2").arg(hashBase).arg(idx); QMap::const_iterator it; it = m_hashMap.constFind(hash); if (it == m_hashMap.constEnd()) { m_hashMap[hash] = true; break; } ++idx; } kt.m_strBankID = QString("%1-%2").arg(acc.id()).arg(hash); } // store transaction ks.m_listTransactions += kt; } -bool KMyMoneyBanking::importAccountInfo(AB_IMEXPORTER_ACCOUNTINFO *ai, +bool KBankingExt::importAccountInfo(AB_IMEXPORTER_ACCOUNTINFO *ai, uint32_t /*flags*/) { const char *p; DBG_INFO(0, "Importing account..."); // account number MyMoneyStatement ks; p = AB_ImExporterAccountInfo_GetAccountNumber(ai); if (p) { ks.m_strAccountNumber = m_parent->stripLeadingZeroes(p); } p = AB_ImExporterAccountInfo_GetBankCode(ai); if (p) { ks.m_strRoutingNumber = m_parent->stripLeadingZeroes(p); } MyMoneyAccount kacc = m_parent->account("kbanking-acc-ref", QString("%1-%2").arg(ks.m_strRoutingNumber, ks.m_strAccountNumber)); ks.m_accountId = kacc.id(); // account name p = AB_ImExporterAccountInfo_GetAccountName(ai); if (p) ks.m_strAccountName = p; // account type switch (AB_ImExporterAccountInfo_GetType(ai)) { case AB_AccountType_Bank: ks.m_eType = eMyMoney::Statement::Type::Savings; break; case AB_AccountType_CreditCard: ks.m_eType = eMyMoney::Statement::Type::CreditCard; break; case AB_AccountType_Checking: ks.m_eType = eMyMoney::Statement::Type::Checkings; break; case AB_AccountType_Savings: ks.m_eType = eMyMoney::Statement::Type::Savings; break; case AB_AccountType_Investment: ks.m_eType = eMyMoney::Statement::Type::Investment; break; case AB_AccountType_Cash: ks.m_eType = eMyMoney::Statement::Type::None; break; default: ks.m_eType = eMyMoney::Statement::Type::None; } // account status const AB_ACCOUNT_STATUS* ast = _getAccountStatus(ai); if (ast) { const AB_BALANCE *bal; bal = AB_AccountStatus_GetBookedBalance(ast); if (!bal) bal = AB_AccountStatus_GetNotedBalance(ast); if (bal) { const AB_VALUE* val = AB_Balance_GetValue(bal); if (val) { DBG_INFO(0, "Importing balance"); ks.m_closingBalance = AB_Value_toMyMoneyMoney(val); p = AB_Value_GetCurrency(val); if (p) ks.m_strCurrency = p; } const GWEN_TIME* ti = AB_Balance_GetTime(bal); if (ti) { int year, month, day; if (!GWEN_Time_GetBrokenDownDate(ti, &day, &month, &year)) ks.m_dateEnd = QDate(year, month + 1, day); } else { DBG_WARN(0, "No time for balance"); } } else { DBG_WARN(0, "No account balance"); } } else { DBG_WARN(0, "No account status"); } // clear hash map m_hashMap.clear(); // get all transactions const AB_TRANSACTION* t = AB_ImExporterAccountInfo_GetFirstTransaction(ai); while (t) { _xaToStatement(ks, kacc, t); t = AB_ImExporterAccountInfo_GetNextTransaction(ai); } // import them if (!m_parent->importStatement(ks)) { if (KMessageBox::warningYesNo(0, i18n("Error importing statement. Do you want to continue?"), i18n("Critical Error")) == KMessageBox::No) { DBG_ERROR(0, "User aborted"); return false; } } return true; } -#include "mymoneybanking.moc" +K_PLUGIN_FACTORY_WITH_JSON(KBankingFactory, "kbanking.json", registerPlugin();) + +#include "kbanking.moc" diff --git a/kmymoney/plugins/kbanking/mymoneybanking.h b/kmymoney/plugins/kbanking/kbanking.h similarity index 93% rename from kmymoney/plugins/kbanking/mymoneybanking.h rename to kmymoney/plugins/kbanking/kbanking.h index 86f36f224..1ec416b39 100644 --- a/kmymoney/plugins/kbanking/mymoneybanking.h +++ b/kmymoney/plugins/kbanking/kbanking.h @@ -1,252 +1,253 @@ /*************************************************************************** * Copyright 2004 Martin Preuss aquamaniac@users.sourceforge.net * * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * Copyright 2010 Thomas Baumgart ipwizard@users.sourceforge.net * * Copyright 2016 Christian David christian-david@web.de * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 * ***************************************************************************/ #ifndef MYMONEYBANKING_H #define MYMONEYBANKING_H #ifdef HAVE_CONFIG_H #include #endif // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE & Library Includes class KAction; class QBanking; -class KMyMoneyBanking; +class KBankingExt; class KBAccountSettings; #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyplugin.h" #include "onlinepluginextended.h" #include "mymoneyaccount.h" #include "mymoneykeyvaluecontainer.h" #include "mymoney/onlinejobtyped.h" #include "onlinetasks/sepa/tasks/sepaonlinetransfer.h" #include "banking.hpp" /** * This class represents the KBanking plugin towards KMymoney. * All GUI related issues are handled in this object. */ class MyMoneyStatement; -class KBankingPlugin : public KMyMoneyPlugin::OnlinePluginExtended +class KBanking : public KMyMoneyPlugin::OnlinePluginExtended { - friend class KMyMoneyBanking; + friend class KBankingExt; Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.kbanking" FILE "kbanking.json") + Q_INTERFACES(KMyMoneyPlugin::OnlinePluginExtended + KMyMoneyPlugin::OnlinePlugin) public: - KBankingPlugin(); - virtual ~KBankingPlugin(); + explicit KBanking(QObject *parent, const QVariantList &args); + ~KBanking() override; bool importStatement(const MyMoneyStatement& s); MyMoneyAccount account(const QString& key, const QString& value) const; void setAccountOnlineParameters(const MyMoneyAccount& acc, const MyMoneyKeyValueContainer& kvps) const; void protocols(QStringList& protocolList) const override; QStringList availableJobs(QString accountId) override; IonlineTaskSettings::ptr settings(QString accountId, QString taskName) override; void sendOnlineJob(QList& jobs) override; - virtual void plug() override; - virtual void unplug() override; + void plug() override; + void unplug() override; private: /** * creates the action objects available through the application menus */ void createActions(); /** * creates the context menu */ void createContextMenu(); /** * checks whether a given KMyMoney account with id @p id is * already mapped or not. * * @param acc reference to KMyMoney account object * @retval false account is not mapped to an AqBanking account * @retval true account is mapped to an AqBanking account */ bool accountIsMapped(const MyMoneyAccount& acc); /** * sets up the reference string consisting out of BLZ and account number * in the KMyMoney object so that we can find it later on when importing data. */ void setupAccountReference(const MyMoneyAccount& acc, AB_ACCOUNT* ab_acc); /** * Returns the value of the parameter @a s with all leading 0's stripped. */ QString stripLeadingZeroes(const QString& s) const; /** * Prefills the protocol conversion list to allow mapping * of AqBanking internal names to external names */ void loadProtocolConversion(); /** * Creates an additional tab widget for the account edit dialog * to represent the necessary parameters for online banking * through AqBanking. */ QWidget* accountConfigTab(const MyMoneyAccount& acc, QString& name) override; /** * Stores the configuration data kept in the widgets created * in accountConfigTab() and returns them in a key value container * The current settings are accessible through the reference to * @a current. */ MyMoneyKeyValueContainer onlineBankingSettings(const MyMoneyKeyValueContainer& current) override; /** * Called by the application to map the KMyMoney account @a acc * to an AqBanking account. Calls KBanking to set up AqBanking mappings. * Returns the necessary settings for the plugin in @a settings and * @a true if the mapping was successful. */ bool mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& settings) override; /** * This method translates a MyMoneyAccount to the corresponding AB_ACCOUNT object pointer. * If no mapped account can be detected, it returns 0. */ AB_ACCOUNT* aqbAccount(const MyMoneyAccount& acc) const; /** * This is a convenient method for aqbAccount if you have KMyMoney's account id only. */ AB_ACCOUNT* aqbAccount(const QString& accountId) const; /** * Called by the application framework to update the * KMyMoney account @a acc with data from the online source. * Store the jobs in the outbox in case @a moreAccounts is true */ bool updateAccount(const MyMoneyAccount& acc, bool moreAccounts) override; /** * Kept for backward compatibility. Use * updateAccount(const MyMoneyAccount& acc, bool moreAccounts) instead. * * @deprecated */ bool updateAccount(const MyMoneyAccount& acc) DEPRECATED; /** * Trigger the password cache timer */ void startPasswordTimer(); bool enqueTransaction(onlineJobTyped& job); protected Q_SLOTS: void slotSettings(); void slotImport(); void slotClearPasswordCache(); void executeQueue(); Q_SIGNALS: void queueChanged(); private: class Private; Private* const d; KAction* m_configAction; KAction* m_importAction; - KMyMoneyBanking* m_kbanking; + KBankingExt* m_kbanking; QMap m_protocolConversionMap; KBAccountSettings* m_accountSettings; /** * @brief @ref onlineJob "onlineJobs" which are executed at the moment * Key is onlineJob->id(). This container is used during execution of jobs. */ QMap m_onlineJobQueue; }; /** * This class is the special implementation to glue the AB_Banking class * with the KMyMoneyPlugin structure. */ -class KMyMoneyBanking : public AB_Banking +class KBankingExt : public AB_Banking { - friend class KBankingPlugin; + friend class KBanking; public: - KMyMoneyBanking(KBankingPlugin* parent, const char* appname, const char* fname = 0); - virtual ~KMyMoneyBanking() {}; + explicit KBankingExt(KBanking* parent, const char* appname, const char* fname = 0); + virtual ~KBankingExt() {} int executeQueue(AB_IMEXPORTER_CONTEXT *ctx); int enqueueJob(AB_JOB *j); int dequeueJob(AB_JOB *j); std::list getEnqueuedJobs(); void transfer(); virtual bool interactiveImport(); protected: int init(); int fini(); bool askMapAccount(const MyMoneyAccount& acc); QString mappingId(const MyMoneyObject& object) const; bool importAccountInfo(AB_IMEXPORTER_ACCOUNTINFO *ai, uint32_t flags); const AB_ACCOUNT_STATUS* _getAccountStatus(AB_IMEXPORTER_ACCOUNTINFO *ai); void _xaToStatement(MyMoneyStatement &ks, const MyMoneyAccount&, const AB_TRANSACTION *t); void clearPasswordCache(); private: - KBankingPlugin* m_parent; + KBanking* m_parent; QMap m_hashMap; AB_JOB_LIST2 *_jobQueue; QSet m_sepaKeywords; }; #endif diff --git a/kmymoney/plugins/kbanking/kbanking.json.in b/kmymoney/plugins/kbanking/kbanking.json.in index 13542ccfa..311ac99cf 100644 --- a/kmymoney/plugins/kbanking/kbanking.json.in +++ b/kmymoney/plugins/kbanking/kbanking.json.in @@ -1,84 +1,84 @@ { "KPlugin": { "Authors": [ { "Name": "Martin Preuss,Thomas Baumgart,Christian Dávid", "Email": "aquamaniac@users.sourceforge.net,ipwizard@users.sourceforge.net,christian-david@web.de" } ], "Description": "Add HBCI/FinTs online banking to KMyMoney", "Description[ar]": "أضف صيرفة على الإنترنت إلى أموالك", "Description[bs]": "Dodaje mrežno bankarstvo u KMyMoney", "Description[ca@valencia]": "Afig la banca en línia al KMyMoney", "Description[ca]": "Afegeix la banca en línia al KMyMoney", "Description[da]": "Føj netbank til KMyMoney", "Description[de]": "Fügt Onlinebanking zu KMyMoney hinzu", "Description[el]": "Προσθήκη τραπεζικών διαδικτυακών υπηρεσιών στο KMyMoney", "Description[en_GB]": "Add online banking to KMyMoney", "Description[es]": "Añade banca en línea a KMyMoney", "Description[et]": "Internetipanganduse lisamine KMyMoneysse", "Description[eu]": "Erantsi lerroko bankua KMyMoney-ri", "Description[fi]": "Liitä verkkopankki KMyMoney-ohjelmaan", "Description[fr]": "Ajoute des capacités de banque en ligne à KMyMoney", "Description[gl]": "Engadir banca en liña ao KMyMoney", "Description[hu]": "Online bank hozzáadása a KMyMoney-hoz", "Description[it]": "Aggiungi l'online banking a KMyMoney", "Description[kk]": "KMyMoney-ге онлайн банкингті қосу", "Description[nds]": "Internett-Bankeree för KMyMoney", "Description[nl]": "Voegt online bankieren toe aan KMyMoney", "Description[pl]": "Dodaje bankowość internetową do KMyMoney", "Description[pt]": "Adiciona a banca 'online' ao KMyMoney", "Description[pt_BR]": "Adiciona banco online ao KMyMoney", "Description[ro]": "Interfață de online banking (hbci) pentru KMyMoney", "Description[ru]": "Система банк-клиент для KMyMoney", "Description[sk]": "Pridať online banking do KMyMoney", "Description[sv]": "Lägg till Internetbank till KMyMoney", "Description[tr]": "KMyMoney içerisine çevrimiçi bankacılık ekle", "Description[uk]": "Додає можливість роботи з банками за допомогою мережі до KMyMoney", "Description[x-test]": "xxAdd online banking to KMyMoneyxx", "Description[zh_TW]": "新增線上銀行到 KMyMoney", "EnabledByDefault": true, "Icon": "network-connect", - "Id": "KBanking", + "Id": "kbanking", "License": "GPL", "Name": "KBanking", "Name[ar]": "مصرفك", "Name[bs]": "KBanking", "Name[ca@valencia]": "KBanking", "Name[ca]": "KBanking", "Name[cs]": "KBanking", "Name[da]": "KBanking", "Name[de]": "KBanking", "Name[el]": "KBanking", "Name[en_GB]": "KBanking", "Name[es]": "KBanking", "Name[et]": "KBanking", "Name[eu]": "KBanking", "Name[fi]": "KBanking", "Name[fr]": "KBanking", "Name[ga]": "KBanking", "Name[gl]": "KBanking", "Name[hu]": "KBanking", "Name[it]": "KBanking", "Name[kk]": "KBanking", "Name[nds]": "KBanking", "Name[nl]": "KBanking", "Name[pl]": "KBanking", "Name[pt]": "KBanking", "Name[pt_BR]": "KBanking", "Name[ro]": "KBanking", "Name[ru]": "KBanking", "Name[sk]": "KBanking", "Name[sv]": "Kbank", "Name[tr]": "KBanking", "Name[uk]": "KBanking", "Name[x-test]": "xxKBankingxx", "Name[zh_CN]": "KBanking", "Name[zh_TW]": "KBanking", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/kbanking/kmm_kbanking.rc b/kmymoney/plugins/kbanking/kbanking.rc similarity index 100% rename from kmymoney/plugins/kbanking/kmm_kbanking.rc rename to kmymoney/plugins/kbanking/kbanking.rc diff --git a/kmymoney/plugins/kbanking/kmm_kbanking_debug.rc b/kmymoney/plugins/kbanking/kbanking_debug.rc similarity index 100% rename from kmymoney/plugins/kbanking/kmm_kbanking_debug.rc rename to kmymoney/plugins/kbanking/kbanking_debug.rc diff --git a/kmymoney/plugins/kbanking/qml/chipTan/CMakeLists.txt b/kmymoney/plugins/kbanking/qml/chipTan/CMakeLists.txt index a6215c2b3..1fd28b112 100644 --- a/kmymoney/plugins/kbanking/qml/chipTan/CMakeLists.txt +++ b/kmymoney/plugins/kbanking/qml/chipTan/CMakeLists.txt @@ -1,8 +1,8 @@ set( kbanking_qml_files chipTan.js ChipTan.qml ChipTanFlickerField.qml positionmarker.svg ) -install(FILES ${kbanking_qml_files} DESTINATION ${DATA_INSTALL_DIR}/kmm_kbanking/qml/chipTan/) +install(FILES ${kbanking_qml_files} DESTINATION ${DATA_INSTALL_DIR}/kbanking/qml/chipTan/) diff --git a/kmymoney/plugins/kbanking/tasksettings/CMakeLists.txt b/kmymoney/plugins/kbanking/tasksettings/CMakeLists.txt index 1595f95a2..ea3100055 100644 --- a/kmymoney/plugins/kbanking/tasksettings/CMakeLists.txt +++ b/kmymoney/plugins/kbanking/tasksettings/CMakeLists.txt @@ -1,14 +1,14 @@ set ( kbanking_tasksettings_SRCS credittransfersettingsbase.cpp ) -add_library( kmm_kbanking_tasksettings STATIC ${kbanking_tasksettings_SRCS} ) +add_library( kbanking_tasksettings STATIC ${kbanking_tasksettings_SRCS} ) -target_link_libraries( kmm_kbanking_tasksettings +target_link_libraries( kbanking_tasksettings kmm_utils_validators Qt5::Gui Qt5::Xml Qt5::Sql KF5::I18n konlinetasks_sepa_interface ) diff --git a/kmymoney/plugins/kbanking/views/CMakeLists.txt b/kmymoney/plugins/kbanking/views/CMakeLists.txt index 1afa9da80..5d98301a1 100644 --- a/kmymoney/plugins/kbanking/views/CMakeLists.txt +++ b/kmymoney/plugins/kbanking/views/CMakeLists.txt @@ -1,19 +1,19 @@ ########### next target ############### -set(kmm_kbanking_views_la_SOURCES +set(kbanking_views_la_SOURCES kbjobview.cpp ) -set(kmm_kbanking_views_UI +set(kbanking_views_UI kbjobview.ui ) -ki18n_wrap_ui(kmm_kbanking_views_la_SOURCES - ${kmm_kbanking_views_UI}) +ki18n_wrap_ui(kbanking_views_la_SOURCES + ${kbanking_views_UI}) -add_library(kmm_kbanking_views STATIC ${kmm_kbanking_views_la_SOURCES}) +add_library(kbanking_views STATIC ${kbanking_views_la_SOURCES}) -target_link_libraries(kmm_kbanking_views +target_link_libraries(kbanking_views Qt5::Widgets ) diff --git a/kmymoney/plugins/kbanking/widgets/CMakeLists.txt b/kmymoney/plugins/kbanking/widgets/CMakeLists.txt index 756ccf978..b11ac4fe1 100644 --- a/kmymoney/plugins/kbanking/widgets/CMakeLists.txt +++ b/kmymoney/plugins/kbanking/widgets/CMakeLists.txt @@ -1,27 +1,27 @@ ########### next target ############### -set(kmm_kbanking_widgets_la_SOURCES +set(kbanking_widgets_la_SOURCES chiptandialog.cpp kbaccountlist.cpp kbjoblist.cpp ) -set(kmm_kbanking_widgets_UI +set(kbanking_widgets_UI chiptandialog.ui ) -kconfig_add_kcfg_files(kmm_kbanking_widgets_la_SOURCES ../kbankingsettings.kcfgc) +kconfig_add_kcfg_files(kbanking_widgets_la_SOURCES ../kbankingsettings.kcfgc) -ki18n_wrap_ui(kmm_kbanking_widgets_la_SOURCES ${kmm_kbanking_widgets_UI}) +ki18n_wrap_ui(kbanking_widgets_la_SOURCES ${kbanking_widgets_UI}) -add_library(kmm_kbanking_widgets STATIC ${kmm_kbanking_widgets_la_SOURCES}) +add_library(kbanking_widgets STATIC ${kbanking_widgets_la_SOURCES}) -target_link_libraries(kmm_kbanking_widgets +target_link_libraries(kbanking_widgets Qt5::Widgets Qt5::QuickWidgets aqbanking::aqbanking KF5::I18n KF5::ConfigCore KF5::ConfigGui ) diff --git a/kmymoney/plugins/ofx/CMakeLists.txt b/kmymoney/plugins/ofx/CMakeLists.txt new file mode 100644 index 000000000..e5fd34999 --- /dev/null +++ b/kmymoney/plugins/ofx/CMakeLists.txt @@ -0,0 +1,3 @@ +if(ENABLE_OFXIMPORTER) + add_subdirectory( import ) +endif(ENABLE_OFXIMPORTER) diff --git a/kmymoney/plugins/ofximport/CMakeLists.txt b/kmymoney/plugins/ofx/import/CMakeLists.txt similarity index 54% rename from kmymoney/plugins/ofximport/CMakeLists.txt rename to kmymoney/plugins/ofx/import/CMakeLists.txt index bcffa4fb9..17003758e 100644 --- a/kmymoney/plugins/ofximport/CMakeLists.txt +++ b/kmymoney/plugins/ofx/import/CMakeLists.txt @@ -1,49 +1,49 @@ # patch the version with the version defined in the build system -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ofximport.json.in ${CMAKE_CURRENT_BINARY_DIR}/ofximport.json @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ofximporter.json.in ${CMAKE_CURRENT_BINARY_DIR}/ofximporter.json @ONLY) if(${LIBOFX_HAVE_CLIENTUID}) add_definitions(-DLIBOFX_HAVE_CLIENTUID) endif(${LIBOFX_HAVE_CLIENTUID}) add_subdirectory( dialogs ) ########### next target ############### -set(kmm_ofximport_PART_SRCS - ofximporterplugin.cpp +set(ofximporter_PART_SRCS + ofximporter.cpp ofxpartner.cpp ) -set(kmm_ofximport_PART_UI +set(ofximporter_PART_UI importoption.ui ) -ki18n_wrap_ui(kmm_ofximport_PART_SRCS ${kmm_ofximport_PART_UI}) +ki18n_wrap_ui(ofximporter_PART_SRCS ${ofximporter_PART_UI}) -add_library(kmm_ofximport MODULE ${kmm_ofximport_PART_SRCS}) +add_library(ofximporter MODULE ${ofximporter_PART_SRCS}) -target_link_libraries(kmm_ofximport - kmm_ofximport_dialogs +target_link_libraries(ofximporter + ofximporter_dialogs kmm_mymoney kmm_widgets kmm_plugin KF5::Wallet Qt5::Xml Alkimia::alkimia ${LIBOFX_LIBRARIES} ) -target_include_directories(kmm_ofximport +target_include_directories(ofximporter PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/dialogs ${CMAKE_CURRENT_BINARY_DIR}/dialogs ${LIBOFX_INCLUDE_DIR} ) ########### install files ############### -install(FILES kmm_ofximport.rc - DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_ofximport") +install(FILES ofximporter.rc + DESTINATION "${KXMLGUI_INSTALL_DIR}/ofximporter") -install(TARGETS kmm_ofximport +install(TARGETS ofximporter DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") diff --git a/kmymoney/plugins/ofx/import/dialogs/CMakeLists.txt b/kmymoney/plugins/ofx/import/dialogs/CMakeLists.txt new file mode 100644 index 000000000..151459d83 --- /dev/null +++ b/kmymoney/plugins/ofx/import/dialogs/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories(${LIBOFX_INCLUDE_DIR}) + +########### next target ############### + +set(ofximporter_dialogs_la_SOURCES + kofxdirectconnectdlg.cpp konlinebankingsetupwizard.cpp + konlinebankingstatus.cpp mymoneyofxconnector.cpp + ) + +set(ofximporter_dialogs_UI + kofxdirectconnectdlgdecl.ui konlinebankingsetupwizard.ui + konlinebankingstatusdecl.ui + ) + +ki18n_wrap_ui(ofximporter_dialogs_la_SOURCES + ${ofximporter_dialogs_UI}) +add_library(ofximporter_dialogs STATIC ${ofximporter_dialogs_la_SOURCES}) +target_link_libraries(ofximporter_dialogs kmm_widgets KF5::WidgetsAddons KF5::I18n KF5::CoreAddons KF5::KIOCore KF5::KIOWidgets KF5::Wallet Qt5::Widgets Qt5::Sql Qt5::Xml) diff --git a/kmymoney/plugins/ofximport/dialogs/kofxdirectconnectdlg.cpp b/kmymoney/plugins/ofx/import/dialogs/kofxdirectconnectdlg.cpp similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/kofxdirectconnectdlg.cpp rename to kmymoney/plugins/ofx/import/dialogs/kofxdirectconnectdlg.cpp diff --git a/kmymoney/plugins/ofximport/dialogs/kofxdirectconnectdlg.h b/kmymoney/plugins/ofx/import/dialogs/kofxdirectconnectdlg.h similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/kofxdirectconnectdlg.h rename to kmymoney/plugins/ofx/import/dialogs/kofxdirectconnectdlg.h diff --git a/kmymoney/plugins/ofximport/dialogs/kofxdirectconnectdlgdecl.ui b/kmymoney/plugins/ofx/import/dialogs/kofxdirectconnectdlgdecl.ui similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/kofxdirectconnectdlgdecl.ui rename to kmymoney/plugins/ofx/import/dialogs/kofxdirectconnectdlgdecl.ui diff --git a/kmymoney/plugins/ofximport/dialogs/konlinebankingsetupwizard.cpp b/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.cpp similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/konlinebankingsetupwizard.cpp rename to kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.cpp diff --git a/kmymoney/plugins/ofximport/dialogs/konlinebankingsetupwizard.h b/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.h similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/konlinebankingsetupwizard.h rename to kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.h diff --git a/kmymoney/plugins/ofximport/dialogs/konlinebankingsetupwizard.ui b/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.ui similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/konlinebankingsetupwizard.ui rename to kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.ui diff --git a/kmymoney/plugins/ofximport/dialogs/konlinebankingstatus.cpp b/kmymoney/plugins/ofx/import/dialogs/konlinebankingstatus.cpp similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/konlinebankingstatus.cpp rename to kmymoney/plugins/ofx/import/dialogs/konlinebankingstatus.cpp diff --git a/kmymoney/plugins/ofximport/dialogs/konlinebankingstatus.h b/kmymoney/plugins/ofx/import/dialogs/konlinebankingstatus.h similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/konlinebankingstatus.h rename to kmymoney/plugins/ofx/import/dialogs/konlinebankingstatus.h diff --git a/kmymoney/plugins/ofximport/dialogs/konlinebankingstatusdecl.ui b/kmymoney/plugins/ofx/import/dialogs/konlinebankingstatusdecl.ui similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/konlinebankingstatusdecl.ui rename to kmymoney/plugins/ofx/import/dialogs/konlinebankingstatusdecl.ui diff --git a/kmymoney/plugins/ofximport/dialogs/mymoneyofxconnector.cpp b/kmymoney/plugins/ofx/import/dialogs/mymoneyofxconnector.cpp similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/mymoneyofxconnector.cpp rename to kmymoney/plugins/ofx/import/dialogs/mymoneyofxconnector.cpp diff --git a/kmymoney/plugins/ofximport/dialogs/mymoneyofxconnector.h b/kmymoney/plugins/ofx/import/dialogs/mymoneyofxconnector.h similarity index 100% rename from kmymoney/plugins/ofximport/dialogs/mymoneyofxconnector.h rename to kmymoney/plugins/ofx/import/dialogs/mymoneyofxconnector.h diff --git a/kmymoney/plugins/ofximport/importoption.ui b/kmymoney/plugins/ofx/import/importoption.ui similarity index 100% rename from kmymoney/plugins/ofximport/importoption.ui rename to kmymoney/plugins/ofx/import/importoption.ui diff --git a/kmymoney/plugins/ofximport/nodeparser.cpp b/kmymoney/plugins/ofx/import/nodeparser.cpp similarity index 100% rename from kmymoney/plugins/ofximport/nodeparser.cpp rename to kmymoney/plugins/ofx/import/nodeparser.cpp diff --git a/kmymoney/plugins/ofximport/nodeparser.h b/kmymoney/plugins/ofx/import/nodeparser.h similarity index 100% rename from kmymoney/plugins/ofximport/nodeparser.h rename to kmymoney/plugins/ofx/import/nodeparser.h diff --git a/kmymoney/plugins/ofximport/ofximporterplugin.cpp b/kmymoney/plugins/ofx/import/ofximporter.cpp similarity index 87% rename from kmymoney/plugins/ofximport/ofximporterplugin.cpp rename to kmymoney/plugins/ofx/import/ofximporter.cpp index d91ffc129..cc734e03b 100644 --- a/kmymoney/plugins/ofximport/ofximporterplugin.cpp +++ b/kmymoney/plugins/ofx/import/ofximporter.cpp @@ -1,844 +1,851 @@ /*************************************************************************** - ofximporterplugin.cpp + ofximporter.cpp ------------------- begin : Sat Jan 01 2005 copyright : (C) 2005 by Ace Jones email : Ace Jones ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ -#include "ofximporterplugin.h" +#include "ofximporter.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes +#include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include #include "konlinebankingstatus.h" #include "konlinebankingsetupwizard.h" #include "kofxdirectconnectdlg.h" #include "mymoneyaccount.h" #include "mymoneyexception.h" #include "mymoneystatement.h" #include "statementinterface.h" #include "importinterface.h" #include "ui_importoption.h" //#define DEBUG_LIBOFX using KWallet::Wallet; -class OfxImporterPlugin::Private +class OFXImporter::Private { public: Private() : m_valid(false), m_preferName(PreferId), m_walletIsOpen(false), m_statusDlg(0), m_wallet(0), m_updateStartDate(QDate(1900,1,1)) {} bool m_valid; enum NamePreference { PreferId = 0, PreferName, PreferMemo } m_preferName; bool m_walletIsOpen; QList m_statementlist; QList m_securitylist; QString m_fatalerror; QStringList m_infos; QStringList m_warnings; QStringList m_errors; KOnlineBankingStatus* m_statusDlg; Wallet *m_wallet; QDate m_updateStartDate; }; -OfxImporterPlugin::OfxImporterPlugin() : - KMyMoneyPlugin::Plugin(nullptr, "KMyMoney OFX"), +OFXImporter::OFXImporter(QObject *parent, const QVariantList &args) : + KMyMoneyPlugin::Plugin(parent, "ofximporter"), /* * the string in the line above must be the same as * X-KDE-PluginInfo-Name and the provider name assigned in * OfxImporterPlugin::onlineBankingSettings() */ KMyMoneyPlugin::ImporterPlugin(), d(new Private) { - setComponentName("kmm_ofximport", i18n("KMyMoney OFX")); - setXMLFile("kmm_ofximport.rc"); + Q_UNUSED(args) + setComponentName("ofximporter", i18n("OFX Importer")); + setXMLFile("ofximporter.rc"); createActions(); // For ease announce that we have been loaded. - qDebug("KMyMoney ofximport plugin loaded"); + qDebug("Plugins: ofximporter loaded"); } -OfxImporterPlugin::~OfxImporterPlugin() +OFXImporter::~OFXImporter() { delete d; + qDebug("Plugins: ofximporter unloaded"); } -void OfxImporterPlugin::createActions() +void OFXImporter::createActions() { QAction *action = actionCollection()->addAction("file_import_ofx"); action->setText(i18n("OFX...")); connect(action, SIGNAL(triggered(bool)), this, SLOT(slotImportFile())); } -void OfxImporterPlugin::slotImportFile() +void OFXImporter::slotImportFile() { QWidget * widget = new QWidget; Ui_ImportOption* option = new Ui_ImportOption; option->setupUi(widget); QUrl url = importInterface()->selectFile(i18n("OFX import file selection"), "", "*.ofx *.qfx *.ofc|OFX files (*.ofx, *.qfx, *.ofc)\n*|All files", QFileDialog::ExistingFile, widget); - d->m_preferName = static_cast(option->m_preferName->currentIndex()); + d->m_preferName = static_cast(option->m_preferName->currentIndex()); if (url.isValid()) { if (isMyFormat(url.path())) { slotImportFile(url.path()); } else { KMessageBox::error(0, i18n("Unable to import %1 using the OFX importer plugin. This file is not the correct format.", url.toDisplayString()), i18n("Incorrect format")); } } delete widget; } -QString OfxImporterPlugin::formatName() const +QString OFXImporter::formatName() const { return "OFX"; } -QString OfxImporterPlugin::formatFilenameFilter() const +QString OFXImporter::formatFilenameFilter() const { return "*.ofx *.qfx *.ofc"; } -bool OfxImporterPlugin::isMyFormat(const QString& filename) const +bool OFXImporter::isMyFormat(const QString& filename) const { // filename is considered an Ofx file if it contains // the tag "" or "" in the first 20 lines. // which contain some data bool result = false; QFile f(filename); if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream ts(&f); int lineCount = 20; while (!ts.atEnd() && !result && lineCount != 0) { // get a line of data and remove all unnecessary whitepace chars QString line = ts.readLine().simplified(); if (line.contains("", Qt::CaseInsensitive) || line.contains("", Qt::CaseInsensitive)) result = true; // count only lines that contain some non white space chars if (!line.isEmpty()) lineCount--; } f.close(); } return result; } -bool OfxImporterPlugin::import(const QString& filename) +bool OFXImporter::import(const QString& filename) { d->m_fatalerror = i18n("Unable to parse file"); d->m_valid = false; d->m_errors.clear(); d->m_warnings.clear(); d->m_infos.clear(); d->m_statementlist.clear(); d->m_securitylist.clear(); QByteArray filename_deep = QFile::encodeName(filename); ofx_STATUS_msg = true; ofx_INFO_msg = true; ofx_WARNING_msg = true; ofx_ERROR_msg = true; #ifdef DEBUG_LIBOFX ofx_PARSER_msg = true; ofx_DEBUG_msg = true; ofx_DEBUG1_msg = true; ofx_DEBUG2_msg = true; ofx_DEBUG3_msg = true; ofx_DEBUG4_msg = true; ofx_DEBUG5_msg = true; #endif LibofxContextPtr ctx = libofx_get_new_context(); Q_CHECK_PTR(ctx); qDebug("setup callback routines"); ofx_set_transaction_cb(ctx, ofxTransactionCallback, this); ofx_set_statement_cb(ctx, ofxStatementCallback, this); ofx_set_account_cb(ctx, ofxAccountCallback, this); ofx_set_security_cb(ctx, ofxSecurityCallback, this); ofx_set_status_cb(ctx, ofxStatusCallback, this); qDebug("process data"); libofx_proc_file(ctx, filename_deep, AUTODETECT); libofx_free_context(ctx); if (d->m_valid) { d->m_fatalerror.clear(); d->m_valid = storeStatements(d->m_statementlist); } return d->m_valid; } -QString OfxImporterPlugin::lastError() const +QString OFXImporter::lastError() const { if (d->m_errors.count() == 0) return d->m_fatalerror; return d->m_errors.join("

"); } /* __________________________________________________________________________ * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA * * Static callbacks for LibOFX * * YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY */ -int OfxImporterPlugin::ofxTransactionCallback(struct OfxTransactionData data, void * pv) +int OFXImporter::ofxTransactionCallback(struct OfxTransactionData data, void * pv) { // kDebug(2) << Q_FUNC_INFO; - OfxImporterPlugin* pofx = reinterpret_cast(pv); + OFXImporter* pofx = reinterpret_cast(pv); MyMoneyStatement& s = pofx->back(); MyMoneyStatement::Transaction t; if (data.date_posted_valid) { QDateTime dt; dt.setTime_t(data.date_posted); t.m_datePosted = dt.date(); } else if (data.date_initiated_valid) { QDateTime dt; dt.setTime_t(data.date_initiated); t.m_datePosted = dt.date(); } if (t.m_datePosted.isValid()) { // verify the transaction date is one we want if (t.m_datePosted < pofx->d->m_updateStartDate) { //kDebug(0) << "discarding transaction dated" << qPrintable(t.m_datePosted.toString(Qt::ISODate)); return 0; } } if (data.amount_valid) { t.m_amount = MyMoneyMoney(data.amount, 1000); } if (data.check_number_valid) { t.m_strNumber = QString::fromUtf8(data.check_number); } if (data.fi_id_valid) { t.m_strBankID = QString("ID ") + QString::fromUtf8(data.fi_id); } else if (data.reference_number_valid) { t.m_strBankID = QString("REF ") + QString::fromUtf8(data.reference_number); } // Decide whether to use NAME, PAYEEID or MEMO to construct the payee bool validity[3] = {false, false, false}; QStringList values; switch (pofx->d->m_preferName) { - case OfxImporterPlugin::Private::PreferId: // PAYEEID + case OFXImporter::Private::PreferId: // PAYEEID default: validity[0] = data.payee_id_valid; validity[1] = data.name_valid; validity[2] = data.memo_valid; values += QString::fromUtf8(data.payee_id); values += QString::fromUtf8(data.name); values += QString::fromUtf8(data.memo); break; - case OfxImporterPlugin::Private::PreferName: // NAME + case OFXImporter::Private::PreferName: // NAME validity[0] = data.name_valid; validity[1] = data.payee_id_valid; validity[2] = data.memo_valid; values += QString::fromUtf8(data.name); values += QString::fromUtf8(data.payee_id); values += QString::fromUtf8(data.memo); break; - case OfxImporterPlugin::Private::PreferMemo: // MEMO + case OFXImporter::Private::PreferMemo: // MEMO validity[0] = data.memo_valid; validity[1] = data.payee_id_valid; validity[2] = data.name_valid; values += QString::fromUtf8(data.memo); values += QString::fromUtf8(data.payee_id); values += QString::fromUtf8(data.name); break; } for (int idx = 0; idx < 3; ++idx) { if (validity[idx]) { t.m_strPayee = values[idx]; break; } } // extract memo field if we haven't used it as payee if ((data.memo_valid) - && (pofx->d->m_preferName != OfxImporterPlugin::Private::PreferMemo)) { + && (pofx->d->m_preferName != OFXImporter::Private::PreferMemo)) { t.m_strMemo = QString::fromUtf8(data.memo); } // If the payee or memo fields are blank, set them to // the other one which is NOT blank. (acejones) if (t.m_strPayee.isEmpty()) { // But we only create a payee for non-investment transactions (ipwizard) if (! t.m_strMemo.isEmpty() && data.invtransactiontype_valid == false) t.m_strPayee = t.m_strMemo; } else { if (t.m_strMemo.isEmpty()) t.m_strMemo = t.m_strPayee; } if (data.security_data_valid) { struct OfxSecurityData* secdata = data.security_data_ptr; if (secdata->ticker_valid) { t.m_strSymbol = QString::fromUtf8(secdata->ticker); } if (secdata->secname_valid) { t.m_strSecurity = QString::fromUtf8(secdata->secname); } } t.m_shares = MyMoneyMoney(); if (data.units_valid) { t.m_shares = MyMoneyMoney(data.units, 100000).reduce(); } t.m_price = MyMoneyMoney(); if (data.unitprice_valid) { t.m_price = MyMoneyMoney(data.unitprice, 100000).reduce(); } t.m_fees = MyMoneyMoney(); if (data.fees_valid) { t.m_fees += MyMoneyMoney(data.fees, 1000).reduce(); } if (data.commission_valid) { t.m_fees += MyMoneyMoney(data.commission, 1000).reduce(); } bool unhandledtype = false; QString type; if (data.invtransactiontype_valid) { switch (data.invtransactiontype) { case OFX_BUYDEBT: case OFX_BUYMF: case OFX_BUYOPT: case OFX_BUYOTHER: case OFX_BUYSTOCK: t.m_eAction = eMyMoney::Transaction::Action::Buy; break; case OFX_REINVEST: t.m_eAction = eMyMoney::Transaction::Action::ReinvestDividend; break; case OFX_SELLDEBT: case OFX_SELLMF: case OFX_SELLOPT: case OFX_SELLOTHER: case OFX_SELLSTOCK: t.m_eAction = eMyMoney::Transaction::Action::Sell; break; case OFX_INCOME: t.m_eAction = eMyMoney::Transaction::Action::CashDividend; // NOTE: With CashDividend, the amount of the dividend should // be in data.amount. Since I've never seen an OFX file with // cash dividends, this is an assumption on my part. (acejones) break; // // These types are all not handled. We will generate a warning for them. // case OFX_CLOSUREOPT: unhandledtype = true; type = "CLOSUREOPT (Close a position for an option)"; break; case OFX_INVEXPENSE: unhandledtype = true; type = "INVEXPENSE (Misc investment expense that is associated with a specific security)"; break; case OFX_JRNLFUND: unhandledtype = true; type = "JRNLFUND (Journaling cash holdings between subaccounts within the same investment account)"; break; case OFX_MARGININTEREST: unhandledtype = true; type = "MARGININTEREST (Margin interest expense)"; break; case OFX_RETOFCAP: unhandledtype = true; type = "RETOFCAP (Return of capital)"; break; case OFX_SPLIT: unhandledtype = true; type = "SPLIT (Stock or mutial fund split)"; break; case OFX_TRANSFER: unhandledtype = true; type = "TRANSFER (Transfer holdings in and out of the investment account)"; break; default: unhandledtype = true; type = QString("UNKNOWN %1").arg(data.invtransactiontype); break; } } else t.m_eAction = eMyMoney::Transaction::Action::None; // In the case of investment transactions, the 'total' is supposed to the total amount // of the transaction. units * unitprice +/- commission. Easy, right? Sadly, it seems // some ofx creators do not follow this in all circumstances. Therefore, we have to double- // check the total here and adjust it if it's wrong. #if 0 // Even more sadly, this logic is BROKEN. It consistently results in bogus total // values, because of rounding errors in the price. A more through solution would // be to test if the comission alone is causing a discrepency, and adjust in that case. if (data.invtransactiontype_valid && data.unitprice_valid) { double proper_total = t.m_dShares * data.unitprice + t.m_moneyFees; if (proper_total != t.m_moneyAmount) { pofx->addWarning(QString("Transaction %1 has an incorrect total of %2. Using calculated total of %3 instead.").arg(t.m_strBankID).arg(t.m_moneyAmount).arg(proper_total)); t.m_moneyAmount = proper_total; } } #endif if (unhandledtype) pofx->addWarning(QString("Transaction %1 has an unsupported type (%2).").arg(t.m_strBankID, type)); else s.m_listTransactions += t; // kDebug(2) << Q_FUNC_INFO << "return 0 "; return 0; } -int OfxImporterPlugin::ofxStatementCallback(struct OfxStatementData data, void* pv) +int OFXImporter::ofxStatementCallback(struct OfxStatementData data, void* pv) { // kDebug(2) << Q_FUNC_INFO; - OfxImporterPlugin* pofx = reinterpret_cast(pv); + OFXImporter* pofx = reinterpret_cast(pv); MyMoneyStatement& s = pofx->back(); pofx->setValid(); if (data.currency_valid) { s.m_strCurrency = QString::fromUtf8(data.currency); } if (data.account_id_valid) { s.m_strAccountNumber = QString::fromUtf8(data.account_id); } if (data.date_start_valid) { QDateTime dt; dt.setTime_t(data.date_start); s.m_dateBegin = dt.date(); } if (data.date_end_valid) { QDateTime dt; dt.setTime_t(data.date_end); s.m_dateEnd = dt.date(); } if (data.ledger_balance_valid && data.ledger_balance_date_valid) { s.m_closingBalance = MyMoneyMoney(data.ledger_balance); QDateTime dt; dt.setTime_t(data.ledger_balance_date); s.m_dateEnd = dt.date(); } // kDebug(2) << Q_FUNC_INFO << " return 0"; return 0; } -int OfxImporterPlugin::ofxAccountCallback(struct OfxAccountData data, void * pv) +int OFXImporter::ofxAccountCallback(struct OfxAccountData data, void * pv) { // kDebug(2) << Q_FUNC_INFO; - OfxImporterPlugin* pofx = reinterpret_cast(pv); + OFXImporter* pofx = reinterpret_cast(pv); pofx->addnew(); MyMoneyStatement& s = pofx->back(); // Having any account at all makes an ofx statement valid pofx->d->m_valid = true; if (data.account_id_valid) { s.m_strAccountName = QString::fromUtf8(data.account_name); s.m_strAccountNumber = QString::fromUtf8(data.account_id); } if (data.bank_id_valid) { s.m_strRoutingNumber = QString::fromUtf8(data.bank_id); } if (data.broker_id_valid) { s.m_strRoutingNumber = QString::fromUtf8(data.broker_id); } if (data.currency_valid) { s.m_strCurrency = QString::fromUtf8(data.currency); } if (data.account_type_valid) { switch (data.account_type) { case OfxAccountData::OFX_CHECKING : s.m_eType = eMyMoney::Statement::Type::Checkings; break; case OfxAccountData::OFX_SAVINGS : s.m_eType = eMyMoney::Statement::Type::Savings; break; case OfxAccountData::OFX_MONEYMRKT : s.m_eType = eMyMoney::Statement::Type::Investment; break; case OfxAccountData::OFX_CREDITLINE : s.m_eType = eMyMoney::Statement::Type::CreditCard; break; case OfxAccountData::OFX_CMA : s.m_eType = eMyMoney::Statement::Type::CreditCard; break; case OfxAccountData::OFX_CREDITCARD : s.m_eType = eMyMoney::Statement::Type::CreditCard; break; case OfxAccountData::OFX_INVESTMENT : s.m_eType = eMyMoney::Statement::Type::Investment; break; } } // ask KMyMoney for an account id s.m_accountId = pofx->account("kmmofx-acc-ref", QString("%1-%2").arg(s.m_strRoutingNumber, s.m_strAccountNumber)).id(); // copy over the securities s.m_listSecurities = pofx->d->m_securitylist; // kDebug(2) << Q_FUNC_INFO << " return 0"; return 0; } -int OfxImporterPlugin::ofxSecurityCallback(struct OfxSecurityData data, void* pv) +int OFXImporter::ofxSecurityCallback(struct OfxSecurityData data, void* pv) { // kDebug(2) << Q_FUNC_INFO; - OfxImporterPlugin* pofx = reinterpret_cast(pv); + OFXImporter* pofx = reinterpret_cast(pv); MyMoneyStatement::Security sec; if (data.unique_id_valid) { sec.m_strId = QString::fromUtf8(data.unique_id); } if (data.secname_valid) { sec.m_strName = QString::fromUtf8(data.secname); } if (data.ticker_valid) { sec.m_strSymbol = QString::fromUtf8(data.ticker); } pofx->d->m_securitylist += sec; return 0; } -int OfxImporterPlugin::ofxStatusCallback(struct OfxStatusData data, void * pv) +int OFXImporter::ofxStatusCallback(struct OfxStatusData data, void * pv) { // kDebug(2) << Q_FUNC_INFO; - OfxImporterPlugin* pofx = reinterpret_cast(pv); + OFXImporter* pofx = reinterpret_cast(pv); QString message; // if we got this far, we know we were able to parse the file. // so if it fails after here it can only because there were no actual // accounts in the file! pofx->d->m_fatalerror = i18n("No accounts found."); if (data.ofx_element_name_valid) message.prepend(QString("%1: ").arg(QString::fromUtf8(data.ofx_element_name))); if (data.code_valid) message += QString("%1 (Code %2): %3").arg(QString::fromUtf8(data.name)).arg(data.code).arg(QString::fromUtf8(data.description)); if (data.server_message_valid) message += QString(" (%1)").arg(QString::fromUtf8(data.server_message)); if (data.severity_valid) { switch (data.severity) { case OfxStatusData::INFO: pofx->addInfo(message); break; case OfxStatusData::ERROR: pofx->addError(message); break; case OfxStatusData::WARN: pofx->addWarning(message); break; default: pofx->addWarning(message); pofx->addWarning("Previous message was an unknown type. 'WARNING' was assumed."); break; } } // kDebug(2) << Q_FUNC_INFO << " return 0 "; return 0; } -bool OfxImporterPlugin::importStatement(const MyMoneyStatement& s) +bool OFXImporter::importStatement(const MyMoneyStatement& s) { qDebug("OfxImporterPlugin::importStatement start"); return statementInterface()->import(s); } -MyMoneyAccount OfxImporterPlugin::account(const QString& key, const QString& value) const +MyMoneyAccount OFXImporter::account(const QString& key, const QString& value) const { return statementInterface()->account(key, value); } -void OfxImporterPlugin::protocols(QStringList& protocolList) const +void OFXImporter::protocols(QStringList& protocolList) const { protocolList.clear(); protocolList << "OFX"; } -QWidget* OfxImporterPlugin::accountConfigTab(const MyMoneyAccount& acc, QString& name) +QWidget* OFXImporter::accountConfigTab(const MyMoneyAccount& acc, QString& name) { name = i18n("Online settings"); d->m_statusDlg = new KOnlineBankingStatus(acc, 0); return d->m_statusDlg; } -MyMoneyKeyValueContainer OfxImporterPlugin::onlineBankingSettings(const MyMoneyKeyValueContainer& current) +MyMoneyKeyValueContainer OFXImporter::onlineBankingSettings(const MyMoneyKeyValueContainer& current) { MyMoneyKeyValueContainer kvp(current); // keep the provider name in sync with the one found in kmm_ofximport.desktop - kvp["provider"] = "KMyMoney OFX"; + kvp["provider"] = QLatin1String("OFX Importer"); if (d->m_statusDlg) { kvp.deletePair("appId"); kvp.deletePair("kmmofx-headerVersion"); kvp.deletePair("password"); d->m_wallet = openSynchronousWallet(); if (d->m_wallet && (d->m_wallet->hasFolder(KWallet::Wallet::PasswordFolder()) || d->m_wallet->createFolder(KWallet::Wallet::PasswordFolder())) && d->m_wallet->setFolder(KWallet::Wallet::PasswordFolder())) { QString key = OFX_PASSWORD_KEY(kvp.value("url"), kvp.value("uniqueId")); if (d->m_statusDlg->m_storePassword->isChecked()) { d->m_wallet->writePassword(key, d->m_statusDlg->m_password->text()); } else { if (d->m_wallet->hasEntry(key)) { d->m_wallet->removeEntry(key); } } } else { if (d->m_statusDlg->m_storePassword->isChecked()) { kvp.setValue("password", d->m_statusDlg->m_password->text()); } } if (!d->m_statusDlg->appId().isEmpty()) kvp.setValue("appId", d->m_statusDlg->appId()); kvp.setValue("kmmofx-headerVersion", d->m_statusDlg->headerVersion()); kvp.setValue("kmmofx-numRequestDays", QString::number(d->m_statusDlg->m_numdaysSpin->value())); kvp.setValue("kmmofx-todayMinus", QString::number(d->m_statusDlg->m_todayRB->isChecked())); kvp.setValue("kmmofx-lastUpdate", QString::number(d->m_statusDlg->m_lastUpdateRB->isChecked())); kvp.setValue("kmmofx-pickDate", QString::number(d->m_statusDlg->m_pickDateRB->isChecked())); kvp.setValue("kmmofx-specificDate", d->m_statusDlg->m_specificDate->date().toString()); kvp.setValue("kmmofx-preferName", QString::number(d->m_statusDlg->m_preferredPayee->currentIndex())); if (!d->m_statusDlg->m_clientUidEdit->text().isEmpty()) kvp.setValue("clientUid", d->m_statusDlg->m_clientUidEdit->text()); else kvp.deletePair("clientUid"); // get rid of pre 4.6 values kvp.deletePair("kmmofx-preferPayeeid"); } return kvp; } -bool OfxImporterPlugin::mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& settings) +bool OFXImporter::mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& settings) { Q_UNUSED(acc); bool rc = false; QPointer wiz = new KOnlineBankingSetupWizard(0); if (wiz->isInit()) { if (wiz->exec() == QDialog::Accepted) { rc = wiz->chosenSettings(settings); } } delete wiz; return rc; } -bool OfxImporterPlugin::updateAccount(const MyMoneyAccount& acc, bool moreAccounts) +bool OFXImporter::updateAccount(const MyMoneyAccount& acc, bool moreAccounts) { Q_UNUSED(moreAccounts); qDebug("OfxImporterPlugin::updateAccount"); try { if (!acc.id().isEmpty()) { // Save the value of preferName to be used by ofxTransactionCallback - d->m_preferName = static_cast(acc.onlineBankingSettings().value("kmmofx-preferName").toInt()); + d->m_preferName = static_cast(acc.onlineBankingSettings().value("kmmofx-preferName").toInt()); QPointer dlg = new KOfxDirectConnectDlg(acc); connect(dlg, SIGNAL(statementReady(QString)), this, SLOT(slotImportFile(QString))); // get the date of the earliest transaction that we are interested in // from the settings for this account MyMoneyKeyValueContainer settings = acc.onlineBankingSettings(); if (!settings.value("provider").isEmpty()) { if ((settings.value("kmmofx-todayMinus").toInt() != 0) && !settings.value("kmmofx-numRequestDays").isEmpty()) { //kDebug(0) << "start date = today minus"; d->m_updateStartDate = QDate::currentDate().addDays(-settings.value("kmmofx-numRequestDays").toInt()); } else if ((settings.value("kmmofx-lastUpdate").toInt() != 0) && !acc.value("lastImportedTransactionDate").isEmpty()) { //kDebug(0) << "start date = last update"; d->m_updateStartDate = QDate::fromString(acc.value("lastImportedTransactionDate"), Qt::ISODate); } else if ((settings.value("kmmofx-pickDate").toInt() != 0) && !settings.value("kmmofx-specificDate").isEmpty()) { //kDebug(0) << "start date = pick date"; d->m_updateStartDate = QDate::fromString(settings.value("kmmofx-specificDate")); } else { //kDebug(0) << "start date = today - 2 months"; d->m_updateStartDate = QDate::currentDate().addMonths(-2); } } //kDebug(0) << "ofx plugin: account" << acc.name() << "earliest transaction date to process =" << qPrintable(d->m_updateStartDate.toString(Qt::ISODate)); if (dlg->init()) dlg->exec(); delete dlg; // reset the earliest-interesting-transaction date to the non-specific account setting d->m_updateStartDate = QDate(1900,1,1); } } catch (const MyMoneyException &e) { KMessageBox::information(0 , i18n("Error connecting to bank: %1", e.what())); } return false; } -void OfxImporterPlugin::slotImportFile(const QString& url) +void OFXImporter::slotImportFile(const QString& url) { qDebug("OfxImporterPlugin::slotImportFile"); if (!import(url)) { KMessageBox::error(0, QString("%1").arg(i18n("

Unable to import '%1' using the OFX importer plugin. The plugin returned the following error:

%2

", url, lastError())), i18n("Importing error")); } } -bool OfxImporterPlugin::storeStatements(QList& statements) +bool OFXImporter::storeStatements(QList& statements) { bool hasstatements = (statements.count() > 0); bool ok = true; bool abort = false; // FIXME Deal with warnings/errors coming back from plugins /*if ( ofx.errors().count() ) { if ( KMessageBox::warningContinueCancelList(this,i18n("The following errors were returned from your bank"),ofx.errors(),i18n("OFX Errors")) == KMessageBox::Cancel ) abort = true; } if ( ofx.warnings().count() ) { if ( KMessageBox::warningContinueCancelList(this,i18n("The following warnings were returned from your bank"),ofx.warnings(),i18n("OFX Warnings"),KStandardGuiItem::cont(),"ofxwarnings") == KMessageBox::Cancel ) abort = true; }*/ qDebug("OfxImporterPlugin::storeStatements() with %d statements called", static_cast(statements.count())); QList::const_iterator it_s = statements.constBegin(); while (it_s != statements.constEnd() && !abort) { ok = ok && importStatement((*it_s)); ++it_s; } if (hasstatements && !ok) { KMessageBox::error(0, i18n("Importing process terminated unexpectedly."), i18n("Failed to import all statements.")); } return (!hasstatements || ok); } -void OfxImporterPlugin::addnew() +void OFXImporter::addnew() { d->m_statementlist.push_back(MyMoneyStatement()); } -MyMoneyStatement& OfxImporterPlugin::back() +MyMoneyStatement& OFXImporter::back() { return d->m_statementlist.back(); } -bool OfxImporterPlugin::isValid() const +bool OFXImporter::isValid() const { return d->m_valid; } -void OfxImporterPlugin::setValid() +void OFXImporter::setValid() { d->m_valid = true; } -void OfxImporterPlugin::addInfo(const QString& _msg) +void OFXImporter::addInfo(const QString& _msg) { d->m_infos += _msg; } -void OfxImporterPlugin::addWarning(const QString& _msg) +void OFXImporter::addWarning(const QString& _msg) { d->m_warnings += _msg; } -void OfxImporterPlugin::addError(const QString& _msg) +void OFXImporter::addError(const QString& _msg) { d->m_errors += _msg; } -const QStringList& OfxImporterPlugin::infos() const // krazy:exclude=spelling +const QStringList& OFXImporter::infos() const // krazy:exclude=spelling { return d->m_infos; } -const QStringList& OfxImporterPlugin::warnings() const +const QStringList& OFXImporter::warnings() const { return d->m_warnings; } -const QStringList& OfxImporterPlugin::errors() const +const QStringList& OFXImporter::errors() const { return d->m_errors; } + +K_PLUGIN_FACTORY_WITH_JSON(OFXImporterFactory, "ofximporter.json", registerPlugin();) + +#include "ofximporter.moc" diff --git a/kmymoney/plugins/ofximport/ofximporterplugin.h b/kmymoney/plugins/ofx/import/ofximporter.h similarity index 86% rename from kmymoney/plugins/ofximport/ofximporterplugin.h rename to kmymoney/plugins/ofx/import/ofximporter.h index 24de72355..9c85d8402 100644 --- a/kmymoney/plugins/ofximport/ofximporterplugin.h +++ b/kmymoney/plugins/ofx/import/ofximporter.h @@ -1,153 +1,152 @@ /*************************************************************************** - ofximporterplugin.h + ofximporter.h ------------------- begin : Sat Jan 01 2005 copyright : (C) 2005 by Ace Jones email : Ace Jones ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ -#ifndef OFXIMPORTERPLUGIN_H -#define OFXIMPORTERPLUGIN_H +#ifndef OFXIMPORTER_H +#define OFXIMPORTER_H // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // Library Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyplugin.h" #include "mymoneykeyvaluecontainer.h" /** @author Ace Jones */ class MyMoneyAccount; class MyMoneyStatement; -class OfxImporterPlugin : public KMyMoneyPlugin::Plugin, public KMyMoneyPlugin::ImporterPlugin, public KMyMoneyPlugin::OnlinePlugin +class OFXImporter : public KMyMoneyPlugin::Plugin, public KMyMoneyPlugin::ImporterPlugin, public KMyMoneyPlugin::OnlinePlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.ofximport" FILE "ofximport.json") Q_INTERFACES(KMyMoneyPlugin::ImporterPlugin) Q_INTERFACES(KMyMoneyPlugin::OnlinePlugin) public: - explicit OfxImporterPlugin(); - ~OfxImporterPlugin(); + explicit OFXImporter(QObject *parent, const QVariantList &args); + ~OFXImporter() override; /** * This method returns the english-language name of the format * this plugin imports, e.g. "OFX" * * @return QString Name of the format */ - virtual QString formatName() const; + QString formatName() const override; /** * This method returns the filename filter suitable for passing to * KFileDialog::setFilter(), e.g. "*.ofx *.qfx" which describes how * files of this format are likely to be named in the file system * * @return QString Filename filter string */ - virtual QString formatFilenameFilter() const; + QString formatFilenameFilter() const override; /** * This method returns whether this plugin is able to import * a particular file. * * @param filename Fully-qualified pathname to a file * * @return bool Whether the indicated file is importable by this plugin */ - virtual bool isMyFormat(const QString& filename) const; + bool isMyFormat(const QString& filename) const override; /** * Import a file * * @param filename File to import * * @return bool Whether the import was successful. */ - virtual bool import(const QString& filename); + bool import(const QString& filename) override; /** * Returns the error result of the last import * * @return QString English-language name of the error encountered in the * last import, or QString() if it was successful. * */ - virtual QString lastError() const; + QString lastError() const override; /** * Returns a pointer to a widget that will be added as tab to * the account edit dialog. @sa KNewAccountDlg. The data of the * current account is passed as const reference @a acc. @a name references * a QString that will receive the name of the tab to be shown in the dialog. */ - QWidget* accountConfigTab(const MyMoneyAccount& acc, QString& name); + QWidget* accountConfigTab(const MyMoneyAccount& acc, QString& name) override; /** * Retrieves the online banking settings and updates the password in the KDE wallet. * The caller has the choice to pass a MyMoneyKeyValueContainer with the @a current * settings. Only those are modified that are used by the plugin. */ - MyMoneyKeyValueContainer onlineBankingSettings(const MyMoneyKeyValueContainer& current); + MyMoneyKeyValueContainer onlineBankingSettings(const MyMoneyKeyValueContainer& current) override; MyMoneyAccount account(const QString& key, const QString& value) const; - void protocols(QStringList& protocolList) const; + void protocols(QStringList& protocolList) const override; - bool mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& settings); - bool updateAccount(const MyMoneyAccount& acc, bool moreAccounts); + bool mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& settings) override; + bool updateAccount(const MyMoneyAccount& acc, bool moreAccounts) override; protected Q_SLOTS: void slotImportFile(); void slotImportFile(const QString& url); protected: void createActions(); void addnew(); MyMoneyStatement& back(); bool isValid() const; void setValid(); void addInfo(const QString& _msg); void addWarning(const QString& _msg); void addError(const QString& _msg); const QStringList& infos() const; // krazy:exclude=spelling const QStringList& warnings() const; const QStringList& errors() const; bool storeStatements(QList& statements); bool importStatement(const MyMoneyStatement& s); static int ofxTransactionCallback(struct OfxTransactionData, void*); static int ofxStatementCallback(struct OfxStatementData, void*); static int ofxAccountCallback(struct OfxAccountData, void*); static int ofxStatusCallback(struct OfxStatusData, void*); static int ofxSecurityCallback(struct OfxSecurityData, void*); private: /// \internal d-pointer class. class Private; /// \internal d-pointer instance. Private* const d; }; #endif diff --git a/kmymoney/plugins/ofximport/ofximport.json.in b/kmymoney/plugins/ofx/import/ofximporter.json.in similarity index 98% rename from kmymoney/plugins/ofximport/ofximport.json.in rename to kmymoney/plugins/ofx/import/ofximporter.json.in index ac8615185..70c30d9bd 100644 --- a/kmymoney/plugins/ofximport/ofximport.json.in +++ b/kmymoney/plugins/ofx/import/ofximporter.json.in @@ -1,84 +1,84 @@ { "KPlugin": { "Authors": [ { "Email": "acejones@users.sourceforge.net,ipwizard@users.sourceforge.net", "Name": "Ace Jones,Thomas Baumgart" } ], "Description": "Add OFX importing to KMyMoney", "Description[ar]": "أضف استيراد OFX إلى أموالك", "Description[bs]": "Dodaj OFX uvoz za KMyMoney", "Description[ca@valencia]": "Afig la importació OFX al KMyMoney", "Description[ca]": "Afegeix la importació OFX al KMyMoney", "Description[da]": "Føj OFX-import til KMyMoney", "Description[de]": "Fügt OFX-Import zu KMyMoney hinzu", "Description[el]": "Προσθήκη του εργαλείου εισαγωγής αρχείων OFX στο KMyMoney", "Description[en_GB]": "Add OFX importing to KMyMoney", "Description[es]": "Añade la importación de OFX a KMyMoney", "Description[et]": "OFX import KMyMoneysse", "Description[eu]": "Erantsi OFX-ren inportazioa KMyMoney-ri", "Description[fi]": "Lisää OFX-tuonti KMyMoney-ohjelmaan", "Description[fr]": "Ajoute l'importation « OFX » à KMyMoney", "Description[gl]": "Engadir importación de OFX ao KMyMoney", "Description[hu]": "OFX hozzáadása a KMyMoney-ba importáláshoz", "Description[it]": "Aggiungi l'importazione OFX a KMyMoney", "Description[kk]": "KMyMoney-ге OFX импорттауын қосу", "Description[nds]": "OFX-Import för KMyMoney", "Description[nl]": "Voeg OFX importeren toe aan KMyMoney", "Description[pl]": "Dodaje importowanie OFX do KMyMoney", "Description[pt]": "Adiciona a importação de OFX ao KMyMoney", "Description[pt_BR]": "Adiciona a importação de OFX ao KMyMoney", "Description[ro]": "Importă fișiere OFX în KMyMoney", "Description[ru]": "Импорт из OFX в KMyMoney", "Description[sk]": "Pridať OFX import do KMyMoney", "Description[sv]": "Lägg till OFX-import till KMyMoney", "Description[tr]": "KMyMoney'e OFX aktarımı ekle", "Description[uk]": "Додає можливість імпортування OFX до KMyMoney", "Description[x-test]": "xxAdd OFX importing to KMyMoneyxx", "Description[zh_TW]": "匯入 OFX 檔到 KMyMoney", "EnabledByDefault": true, "Icon": "network-connect", - "Id": "KMyMoney OFX", + "Id": "ofximporter", "License": "GPL", - "Name": "KMyMoney OFX", + "Name": "OFX Importer", "Name[ar]": "OFX لأموالك", "Name[bs]": "KMyMoney OFX", "Name[ca@valencia]": "KMyMoney OFX", "Name[ca]": "KMyMoney OFX", "Name[cs]": "KMyMoney OFX", "Name[da]": "KMyMoney OFX", "Name[de]": "KMyMoney OFX", "Name[el]": "KMyMoney OFX", "Name[en_GB]": "KMyMoney OFX", "Name[es]": "KMyMoney OFX", "Name[et]": "KMyMoney OFX", "Name[eu]": "KMyMoney OFX", "Name[fi]": "KMyMoney OFX", "Name[fr]": "OFX KMyMoney", "Name[gl]": "KMyMoney OFX", "Name[hu]": "KMyMoney OFX", "Name[it]": "KMyMoney OFX", "Name[kk]": "KMyMoney OFX", "Name[mr]": "के-माय-मनी ओएफएक्स", "Name[nds]": "KMyMoney-OFX", "Name[nl]": "KMyMoney OFX", "Name[pl]": "KMyMoney OFX", "Name[pt]": "OFX do KMyMoney", "Name[pt_BR]": "OFX do KMyMoney", "Name[ro]": "KMyMoney OFX", "Name[ru]": "Модуль OFX для KMyMoney", "Name[sk]": "KMyMoney OFX", "Name[sv]": "KMyMoney OFX", "Name[tr]": "KMyMoney OFX", "Name[uk]": "OFX KMyMoney", "Name[x-test]": "xxKMyMoney OFXxx", "Name[zh_CN]": "KMyMoney OFX", "Name[zh_TW]": "KMyMoney OFX", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/ofximport/kmm_ofximport.rc b/kmymoney/plugins/ofx/import/ofximporter.rc similarity index 100% rename from kmymoney/plugins/ofximport/kmm_ofximport.rc rename to kmymoney/plugins/ofx/import/ofximporter.rc diff --git a/kmymoney/plugins/ofximport/ofxpartner.cpp b/kmymoney/plugins/ofx/import/ofxpartner.cpp similarity index 100% rename from kmymoney/plugins/ofximport/ofxpartner.cpp rename to kmymoney/plugins/ofx/import/ofxpartner.cpp diff --git a/kmymoney/plugins/ofximport/ofxpartner.h b/kmymoney/plugins/ofx/import/ofxpartner.h similarity index 100% rename from kmymoney/plugins/ofximport/ofxpartner.h rename to kmymoney/plugins/ofx/import/ofxpartner.h diff --git a/kmymoney/plugins/ofximport/dialogs/CMakeLists.txt b/kmymoney/plugins/ofximport/dialogs/CMakeLists.txt deleted file mode 100644 index b913db829..000000000 --- a/kmymoney/plugins/ofximport/dialogs/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -include_directories(${LIBOFX_INCLUDE_DIR}) - -########### next target ############### - -set(kmm_ofximport_dialogs_la_SOURCES - kofxdirectconnectdlg.cpp konlinebankingsetupwizard.cpp - konlinebankingstatus.cpp mymoneyofxconnector.cpp - ) - -set(kmm_ofximport_dialogs_UI - kofxdirectconnectdlgdecl.ui konlinebankingsetupwizard.ui - konlinebankingstatusdecl.ui - ) - -ki18n_wrap_ui(kmm_ofximport_dialogs_la_SOURCES - ${kmm_ofximport_dialogs_UI}) -add_library(kmm_ofximport_dialogs STATIC ${kmm_ofximport_dialogs_la_SOURCES}) -target_link_libraries(kmm_ofximport_dialogs kmm_widgets KF5::WidgetsAddons KF5::I18n KF5::CoreAddons KF5::KIOCore KF5::KIOWidgets KF5::Wallet Qt5::Widgets Qt5::Sql Qt5::Xml) diff --git a/kmymoney/plugins/onlinejobpluginmockup/CMakeLists.txt b/kmymoney/plugins/onlinejobpluginmockup/CMakeLists.txt index adf5e2c94..175f32df0 100644 --- a/kmymoney/plugins/onlinejobpluginmockup/CMakeLists.txt +++ b/kmymoney/plugins/onlinejobpluginmockup/CMakeLists.txt @@ -1,20 +1,20 @@ ########### next target ############### -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmm_onlinejobpluginmockup.json.in ${CMAKE_CURRENT_BINARY_DIR}/kmm_onlinejobpluginmockup.json @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/onlinejobpluginmockup.json.in ${CMAKE_CURRENT_BINARY_DIR}/onlinejobpluginmockup.json @ONLY) -set(kmm_onlinejobpluginmockup_PART_SRCS +set(onlinejobpluginmockup_PART_SRCS onlinejobpluginmockup.cpp ) -add_library(kmm_onlinejobpluginmockup MODULE ${kmm_onlinejobpluginmockup_PART_SRCS}) +add_library(onlinejobpluginmockup MODULE ${onlinejobpluginmockup_PART_SRCS}) -target_link_libraries(kmm_onlinejobpluginmockup +target_link_libraries(onlinejobpluginmockup kmm_mymoney kmm_plugin ) ########### install files ############### -install(TARGETS kmm_onlinejobpluginmockup +install(TARGETS onlinejobpluginmockup DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") diff --git a/kmymoney/plugins/onlinejobpluginmockup/onlinejobpluginmockup.cpp b/kmymoney/plugins/onlinejobpluginmockup/onlinejobpluginmockup.cpp index 88707915a..85d20eb36 100644 --- a/kmymoney/plugins/onlinejobpluginmockup/onlinejobpluginmockup.cpp +++ b/kmymoney/plugins/onlinejobpluginmockup/onlinejobpluginmockup.cpp @@ -1,94 +1,105 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014-2015 Christian Dávid * * 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 "onlinejobpluginmockup.h" #include +#include + #include "mymoneyfile.h" #include "onlinejobadministration.h" #include "plugins/onlinetasks/sepa/tasks/sepaonlinetransfer.h" #include "sepacredittransfersettingsmockup.h" -onlineJobPluginMockup::onlineJobPluginMockup() - : OnlinePluginExtended(nullptr, "onlinejobpluginmockup") +onlineJobPluginMockup::onlineJobPluginMockup(QObject *parent, const QVariantList &args) : + OnlinePluginExtended(parent, "onlinejobpluginmockup") { + Q_UNUSED(args); qDebug("onlineJobPluginMockup should be used during development only!"); } +onlineJobPluginMockup::~onlineJobPluginMockup() +{ +} + void onlineJobPluginMockup::protocols(QStringList& protocolList) const { protocolList << QLatin1String("Imaginary debugging protocol"); } QWidget* onlineJobPluginMockup::accountConfigTab(const MyMoneyAccount&, QString&) { return 0; } bool onlineJobPluginMockup::mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& onlineBankingSettings) { Q_UNUSED(acc); onlineBankingSettings.setValue("provider", objectName()); return true; } MyMoneyKeyValueContainer onlineJobPluginMockup::onlineBankingSettings(const MyMoneyKeyValueContainer& current) { MyMoneyKeyValueContainer nextKvp(current); nextKvp.setValue("provider", objectName()); return nextKvp; } bool onlineJobPluginMockup::updateAccount(const MyMoneyAccount& acc, bool moreAccounts) { Q_UNUSED(moreAccounts); - if (acc.onlineBankingSettings().value("provider") == objectName()) + if (acc.onlineBankingSettings().value("provider").toLower() == objectName()) return true; return false; } QStringList onlineJobPluginMockup::availableJobs(QString accountId) { try { - if (MyMoneyFile::instance()->account(accountId).onlineBankingSettings().value("provider") == objectName()) + if (MyMoneyFile::instance()->account(accountId).onlineBankingSettings().value("provider").toLower() == objectName()) return onlineJobAdministration::instance()->availableOnlineTasks(); } catch (MyMoneyException&) { } return QStringList(); } IonlineTaskSettings::ptr onlineJobPluginMockup::settings(QString accountId, QString taskName) { try { - if (taskName == sepaOnlineTransfer::name() && MyMoneyFile::instance()->account(accountId).onlineBankingSettings().value("provider") == objectName()) + if (taskName == sepaOnlineTransfer::name() && MyMoneyFile::instance()->account(accountId).onlineBankingSettings().value("provider").toLower() == objectName()) return IonlineTaskSettings::ptr(new sepaCreditTransferSettingsMockup); } catch (MyMoneyException&) { } return IonlineTaskSettings::ptr(); } void onlineJobPluginMockup::sendOnlineJob(QList< onlineJob >& jobs) { foreach (const onlineJob& job, jobs) { qDebug() << "Pretend to send: " << job.taskIid() << job.id(); } } + +K_PLUGIN_FACTORY_WITH_JSON(onlineJobPluginMockupFactory, "onlinejobpluginmockup.json", registerPlugin();) + +#include "onlinejobpluginmockup.moc" diff --git a/kmymoney/plugins/onlinejobpluginmockup/onlinejobpluginmockup.h b/kmymoney/plugins/onlinejobpluginmockup/onlinejobpluginmockup.h index 29ce015c3..4a5d8975a 100644 --- a/kmymoney/plugins/onlinejobpluginmockup/onlinejobpluginmockup.h +++ b/kmymoney/plugins/onlinejobpluginmockup/onlinejobpluginmockup.h @@ -1,65 +1,66 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 Christian Dávid * * 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 . */ #ifndef ONLINEJOBPLUGINMOCKUP_H #define ONLINEJOBPLUGINMOCKUP_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyplugin.h" #include "onlinepluginextended.h" #include "mymoneyaccount.h" #include "mymoneykeyvaluecontainer.h" #include "mymoney/onlinejob.h" /** * @short Mockup plugin which offers all online tasks */ class onlineJobPluginMockup : public KMyMoneyPlugin::OnlinePluginExtended { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.onlineJobPluginMockup" FILE "kmm_onlinejobpluginmockup.json") + Q_INTERFACES(KMyMoneyPlugin::OnlinePluginExtended + KMyMoneyPlugin::OnlinePlugin) public: + explicit onlineJobPluginMockup(QObject *parent, const QVariantList &args); + ~onlineJobPluginMockup() override; - onlineJobPluginMockup(); + void protocols(QStringList& protocolList) const override; + QWidget* accountConfigTab(const MyMoneyAccount& account, QString& tabName) override; - void protocols(QStringList& protocolList) const; - QWidget* accountConfigTab(const MyMoneyAccount& account, QString& tabName); + MyMoneyKeyValueContainer onlineBankingSettings(const MyMoneyKeyValueContainer& current) override; + bool mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& onlineBankingSettings) override; - MyMoneyKeyValueContainer onlineBankingSettings(const MyMoneyKeyValueContainer& current); - bool mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& onlineBankingSettings); + bool updateAccount(const MyMoneyAccount& acc, bool moreAccounts = false) override; - bool updateAccount(const MyMoneyAccount& acc, bool moreAccounts = false); - - QStringList availableJobs(QString accountId); - IonlineTaskSettings::ptr settings(QString accountId, QString taskName); - void sendOnlineJob(QList< onlineJob >& jobs); - void plug() override {}; - void unplug() override {}; + QStringList availableJobs(QString accountId) override; + IonlineTaskSettings::ptr settings(QString accountId, QString taskName) override; + void sendOnlineJob(QList< onlineJob >& jobs) override; + void plug() override {} + void unplug() override {} }; #endif // ONLINEJOBPLUGINMOCKUP_H diff --git a/kmymoney/plugins/onlinejobpluginmockup/kmm_onlinejobpluginmockup.json.in b/kmymoney/plugins/onlinejobpluginmockup/onlinejobpluginmockup.json.in similarity index 100% rename from kmymoney/plugins/onlinejobpluginmockup/kmm_onlinejobpluginmockup.json.in rename to kmymoney/plugins/onlinejobpluginmockup/onlinejobpluginmockup.json.in diff --git a/kmymoney/plugins/printcheck/CMakeLists.txt b/kmymoney/plugins/printcheck/CMakeLists.txt index a2965aece..e48dc77d4 100644 --- a/kmymoney/plugins/printcheck/CMakeLists.txt +++ b/kmymoney/plugins/printcheck/CMakeLists.txt @@ -1,72 +1,72 @@ # patch the version with the version defined in the build system -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmm_printcheck.json.in ${CMAKE_CURRENT_BINARY_DIR}/kmm_printcheck.json @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/printcheck.json.in ${CMAKE_CURRENT_BINARY_DIR}/printcheck.json @ONLY) -set(kmm_printcheck_PART_SRCS +set(printcheck_PART_SRCS numbertowords.cpp printcheck.cpp ../../widgets/selectedtransaction.cpp ) -kconfig_add_kcfg_files(kmm_printcheck_PART_SRCS pluginsettings.kcfgc) +kconfig_add_kcfg_files(printcheck_PART_SRCS pluginsettings.kcfgc) -add_library(kmm_printcheck MODULE ${kmm_printcheck_PART_SRCS}) +add_library(printcheck MODULE ${printcheck_PART_SRCS}) -target_link_libraries(kmm_printcheck +target_link_libraries(printcheck Qt5::PrintSupport KF5::I18n kmm_mymoney kmm_plugin ) if(ENABLE_WEBENGINE) - target_link_libraries(kmm_printcheck Qt5::WebEngineWidgets) + target_link_libraries(printcheck Qt5::WebEngineWidgets) else(ENABLE_WEBENGINE) - target_link_libraries(kmm_printcheck KF5::WebKit) + target_link_libraries(printcheck KF5::WebKit) endif(ENABLE_WEBENGINE) -install(TARGETS kmm_printcheck +install(TARGETS printcheck DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") -install(FILES kmm_printcheck.rc - DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_printcheck") +install(FILES printcheck.rc + DESTINATION "${KXMLGUI_INSTALL_DIR}/printcheck") install(FILES check_template.html - DESTINATION "${DATA_INSTALL_DIR}/kmm_printcheck") + DESTINATION "${DATA_INSTALL_DIR}/printcheck") install(FILES check_template_green_linen.html - DESTINATION "${DATA_INSTALL_DIR}/kmm_printcheck") + DESTINATION "${DATA_INSTALL_DIR}/printcheck") # the KCM module -set(kcm_kmm_printcheck_PART_SRCS +set(kcm_printcheck_PART_SRCS kcm_printcheck.cpp ) -kconfig_add_kcfg_files(kcm_kmm_printcheck_PART_SRCS pluginsettings.kcfgc) +kconfig_add_kcfg_files(kcm_printcheck_PART_SRCS pluginsettings.kcfgc) -ki18n_wrap_ui(kcm_kmm_printcheck_PART_SRCS pluginsettingsdecl.ui) +ki18n_wrap_ui(kcm_printcheck_PART_SRCS pluginsettingsdecl.ui) -add_library(kcm_kmm_printcheck MODULE ${kcm_kmm_printcheck_PART_SRCS}) +add_library(kcm_printcheck MODULE ${kcm_printcheck_PART_SRCS}) -kcoreaddons_desktop_to_json(kcm_kmm_printcheck kcm_kmm_printcheck.desktop) +kcoreaddons_desktop_to_json(kcm_printcheck kcm_printcheck.desktop) -target_link_libraries(kcm_kmm_printcheck +target_link_libraries(kcm_printcheck Qt5::PrintSupport KF5::I18n KF5::ConfigWidgets KF5::Completion KF5::KIOWidgets KF5::CoreAddons ) if(ENABLE_WEBENGINE) - target_link_libraries(kcm_kmm_printcheck Qt5::WebEngineWidgets) + target_link_libraries(kcm_printcheck Qt5::WebEngineWidgets) else(ENABLE_WEBENGINE) - target_link_libraries(kcm_kmm_printcheck KF5::WebKit) + target_link_libraries(kcm_printcheck KF5::WebKit) endif(ENABLE_WEBENGINE) -install(TARGETS kcm_kmm_printcheck +install(TARGETS kcm_printcheck DESTINATION "${KDE_INSTALL_PLUGINDIR}") -install(FILES kcm_kmm_printcheck.desktop +install(FILES kcm_printcheck.desktop DESTINATION "${SERVICES_INSTALL_DIR}") diff --git a/kmymoney/plugins/printcheck/kcm_printcheck.cpp b/kmymoney/plugins/printcheck/kcm_printcheck.cpp index fe0b02c4c..16895a00b 100644 --- a/kmymoney/plugins/printcheck/kcm_printcheck.cpp +++ b/kmymoney/plugins/printcheck/kcm_printcheck.cpp @@ -1,97 +1,88 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 "kcm_printcheck.h" #include // Qt includes #include #ifdef ENABLE_WEBENGINE #include #else #include #endif // KDE includes #include #include #include "pluginsettings.h" PluginSettingsWidget::PluginSettingsWidget(QWidget* parent) : QWidget(parent) { setupUi(this); #ifdef ENABLE_WEBENGINE m_checkTemplatePreviewHTMLPart = new QWebEngineView(m_previewFrame); #else m_checkTemplatePreviewHTMLPart = new KWebView(m_previewFrame); #endif QVBoxLayout *layout = new QVBoxLayout; m_previewFrame->setLayout(layout); layout->addWidget(m_checkTemplatePreviewHTMLPart); connect(kcfg_checkTemplateFile, SIGNAL(urlSelected(QUrl)), this, SLOT(urlSelected(QUrl))); connect(kcfg_checkTemplateFile, SIGNAL(returnPressed(QString)), this, SLOT(returnPressed(QString))); } void PluginSettingsWidget::urlSelected(const QUrl &url) { if (!url.isEmpty()) m_checkTemplatePreviewHTMLPart->load(url); } void PluginSettingsWidget::returnPressed(const QString& url) { if (!url.isEmpty()) m_checkTemplatePreviewHTMLPart->load(QUrl::fromUserInput(url)); } -K_PLUGIN_FACTORY_WITH_JSON(KCMPrintCheckFactory, "kcm_kmm_printcheck.json", registerPlugin();) - KCMPrintCheck::KCMPrintCheck(QWidget *parent, const QVariantList& args) : KCModule(parent, args) { - KAboutData *about = new KAboutData(QStringLiteral("kmm_printcheck"), - i18n("KMyMoney print check"), - QStringLiteral(VERSION), QString(), - KAboutLicense::GPL, - i18n("Copyright 2009" ) ); - about->addAuthor( QString::fromUtf8("Cristian Oneț") ); - - setAboutData( about ); - PluginSettingsWidget* w = new PluginSettingsWidget(this); addConfig(PluginSettings::self(), w); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); layout->addWidget(w); load(); w->urlSelected(QUrl::fromUserInput(PluginSettings::checkTemplateFile())); } KCMPrintCheck::~KCMPrintCheck() { } +K_PLUGIN_FACTORY_WITH_JSON(KCMPrintCheckFactory, "kcm_printcheck.json", registerPlugin();) + #include "kcm_printcheck.moc" diff --git a/kmymoney/plugins/printcheck/kcm_kmm_printcheck.desktop b/kmymoney/plugins/printcheck/kcm_printcheck.desktop similarity index 97% rename from kmymoney/plugins/printcheck/kcm_kmm_printcheck.desktop rename to kmymoney/plugins/printcheck/kcm_printcheck.desktop index 21483c02d..7adaba62b 100644 --- a/kmymoney/plugins/printcheck/kcm_kmm_printcheck.desktop +++ b/kmymoney/plugins/printcheck/kcm_printcheck.desktop @@ -1,100 +1,100 @@ [Desktop Entry] Name=Configure the print check plugin Name[ar]=اضبط ملحقة طبع الشّيكات Name[bs]=Konfiguriši dodatak za provjeru štampe Name[ca]=Configura el connector d'impressió de xecs Name[ca@valencia]=Configura el connector d'impressió de xecs Name[da]=Indstil plugin til udskrift af checks Name[de]=Einrichtung des Moduls zum Drucken von Schecks Name[el]=Ρύθμιση του πρόσθετου εκτύπωσης επιταγών Name[en_GB]=Configure the print cheque plugin Name[es]=Configurar el complemento de impresión de cheques Name[et]=Tšeki trükkimise plugina seadistamine Name[eu]=Konfiguratu txekeak inprimatzeko plugina Name[fi]=Aseta tulosta shekki -liitännäinen Name[fr]=Configurer le module externe pour l'impression de chèques Name[gl]=Complemento para configurar a impresión de cheques Name[hu]=A nyomtatás-ellenőrző bővítmény beállítása Name[it]=Configura l'estensione di stampa assegni Name[kk]=Чекті басып шығару плагинін баптау Name[nds]=Scheckdruck-Moduul instellen Name[nl]=De plugin voor printen van een cheque instellen Name[pl]=Ustawienia wtyczki wydruku czeku Name[pt]=Configura o 'plugin' de impressão de cheques Name[pt_BR]=Configura o plugin de impressão de cheques Name[ro]=Configurază modulul pentru tipărirea cecurilor Name[ru]=Настройка модуля печати чеков Name[sk]=Nastaviť plugin tlače šekov Name[sv]=Anpassa insticksprogrammet för utskrift av checkar Name[tr]=Çek yazdırma eklentisini yapılandır Name[uk]=Налаштувати додаток друку чеків Name[x-test]=xxConfigure the print check pluginxx Name[zh_TW]=設定列印支票外掛程式 Icon=document-print Type=Service ServiceTypes=KCModule -Exec=kcmshell4 kmm_printcheck +Exec=kcmshell5 printcheck Categories=Qt;KDE;kmymoney; Keywords=printcheck;kmymoney; Keywords[bs]=printcheck;kmymoney; Keywords[ca]=impressió de xec;kmymoney; Keywords[ca@valencia]=impressió de xec;kmymoney; Keywords[cs]=printcheck;kmymoney; Keywords[da]=printcheck;kmymoney; Keywords[de]=printcheck;kmymoney; Keywords[el]=printcheck;kmymoney; Keywords[en_GB]=printcheque;kmymoney; Keywords[es]=printcheck;kmymoney; Keywords[et]=tšeki trükkimine,kmymoney; Keywords[eu]=printcheck;kmymoney; Keywords[fi]=printcheck;kmymoney; Keywords[fr]=printcheck;kmymoney; Keywords[gl]=printcheck;kmymoney;cheque; Keywords[hu]=printcheck;kmymoney; Keywords[it]=printcheck;kmymoney; Keywords[kk]=printcheck;kmymoney; Keywords[nl]=printcheck;kmymoney; Keywords[pl]=czek;kmymoney; Keywords[pt]=impressão;cheques;kmymoney; Keywords[pt_BR]=impressão de cheques;kmymoney; Keywords[ro]=tipărește;cec;kmymoney; Keywords[ru]=printcheck;kmymoney;чеки; Keywords[sk]=printcheck;kmymoney; Keywords[sv]=checkutskrift;kmymoney; Keywords[tr]=printcheck;kmymoney; Keywords[uk]=printcheck;kmymoney;надрукувати;чек; Keywords[x-test]=xxprintcheckxx;xxkmymoneyxx; Keywords[zh_TW]=printcheck;kmymoney; -X-KDE-Library=kcm_kmm_printcheck -X-KDE-ParentApp=Print check -X-KDE-ParentComponents=Print check +X-KDE-Library=kcm_printcheck +X-KDE-ParentApp=printcheck +X-KDE-ParentComponents=printcheck Comment=Permits the configuration of the print check plugin's options Comment[bs]=Dopušta konfiguraciju opcija dodatka za štampanje čekva Comment[ca]=Permet la configuració de les opcions del connector d'impressió de xecs Comment[ca@valencia]=Permet la configuració de les opcions del connector d'impressió de xecs Comment[da]=Muliggør konfiguration af indstillingerne til pluginet til udskrift af checks Comment[de]=Erlaubt die Konfiguration des Moduls zum Drucken von Schecks Comment[el]=Επιτρέπει την ρύθμιση επιλογών του πρόσθετου εκτύπωσης επιταγών Comment[en_GB]=Permits the configuration of the print cheque plugin's options Comment[es]=Permite la configuración de las opciones del complemento de impresión de cheques Comment[et]=Tšeki trükkimise plugina valikute seadistamise lubamine Comment[eu]=Aukera ematen du txekeak inprimatzeko pluginaren aukerak konfiguratzeko Comment[fi]=Sallii tulosta shekki - asetuksen liitännäisen valitsimet Comment[fr]=Permet la configuration des options du module externe pour l'impression de chèques Comment[gl]=Permite a configuración das opcións do complemento para imprimir cheques Comment[hu]=Engedélyezi a nyomtatás-ellenőrző bővítmény beállításainak módosítását Comment[it]=Consente la configurazione delle opzioni dell'estensione di stampa assegni Comment[kk]=Чек басып шығару плагинін параметрлерін баптауын рұқсат ету Comment[nds]=Optschonen för dat Scheckdruckmoduul instellen Comment[nl]=Stelt in staat om opties voor de plugin voor het afdrukken van een check in te stellen Comment[pl]=Umożliwia ustawienie wtyczki wydruku czeku Comment[pt]=Permite a configuração das opções do 'plugin' de impressão de cheques Comment[pt_BR]=Permite a configuração das opções do plugin de impressão de cheques Comment[ro]=Permite configurarea modulului pentru tipărirea cecurilor Comment[ru]=Настройка модуля печати чеков Comment[sk]=Umožní nastavenie možností pluginu tlače šekov Comment[sv]=Möjliggör inställning av alternativ för insticksprogrammet för utskrift av checkar Comment[tr]=Çek yazdırma eklentisinin seçeneklerinin yapılandırılmasına izin verir Comment[uk]=Надає змогу налаштовувати параметри додатка друку чеків Comment[x-test]=xxPermits the configuration of the print check plugin's optionsxx Comment[zh_TW]=允許列印支票外掛程式的設定選項 diff --git a/kmymoney/plugins/printcheck/printcheck.cpp b/kmymoney/plugins/printcheck/printcheck.cpp index ded164f0d..966876ec0 100644 --- a/kmymoney/plugins/printcheck/printcheck.cpp +++ b/kmymoney/plugins/printcheck/printcheck.cpp @@ -1,240 +1,246 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 "printcheck.h" #include // QT includes #include #include #include #ifdef ENABLE_WEBENGINE #include #else #include #endif #include #include // KDE includes +#include #include -#include #include #include // KMyMoney includes #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneyinstitution.h" #include "mymoneymoney.h" #include "mymoneypayee.h" #include "mymoneysecurity.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "mymoneyutils.h" #include "viewinterface.h" #include "selectedtransactions.h" #include "numbertowords.h" #include "pluginsettings.h" #include "mymoneyenums.h" -struct KMMPrintCheckPlugin::Private { +struct PrintCheck::Private { QAction* m_action; QString m_checkTemplateHTML; QStringList m_printedTransactionIdList; KMyMoneyRegister::SelectedTransactions m_transactions; }; -KMMPrintCheckPlugin::KMMPrintCheckPlugin() - : KMyMoneyPlugin::Plugin(nullptr, "Print check"/*must be the same as X-KDE-PluginInfo-Name*/) +PrintCheck::PrintCheck(QObject *parent, const QVariantList &args) : + KMyMoneyPlugin::Plugin(parent, "printcheck"/*must be the same as X-KDE-PluginInfo-Name*/) { + Q_UNUSED(args); // Tell the host application to load my GUI component - setComponentName("kmm_printcheck", i18n("Print check")); - setXMLFile("kmm_printcheck.rc"); + setComponentName("printcheck", i18n("Print check")); + setXMLFile("printcheck.rc"); // For ease announce that we have been loaded. - qDebug("KMyMoney printcheck plugin loaded"); + qDebug("Plugins: printcheck loaded"); d = std::unique_ptr(new Private); // Create the actions of this plugin QString actionName = i18n("Print check"); d->m_action = actionCollection()->addAction("transaction_printcheck", this, SLOT(slotPrintCheck())); d->m_action->setText(actionName); // wait until a transaction is selected before enableing the action d->m_action->setEnabled(false); d->m_printedTransactionIdList = PluginSettings::printedChecks(); readCheckTemplate(); //! @todo Christian: Replace #if 0 - connect(KMyMoneyPlugin::PluginLoader::instance(), SIGNAL(plug(KPluginInfo*)), this, SLOT(slotPlug(KPluginInfo*))); connect(KMyMoneyPlugin::PluginLoader::instance(), SIGNAL(configChanged(Plugin*)), this, SLOT(slotUpdateConfig())); #endif } /** * @internal Destructor is needed because destructor call of unique_ptr must be in this compile unit */ -KMMPrintCheckPlugin::~KMMPrintCheckPlugin() +PrintCheck::~PrintCheck() { + qDebug("Plugins: printcheck unloaded"); } -void KMMPrintCheckPlugin::readCheckTemplate() +void PrintCheck::plug() { - QString checkTemplateHTMLPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kmm_printcheck/check_template.html"); + connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::transactionsSelected, this, &PrintCheck::slotTransactionsSelected); +} + +void PrintCheck::unplug() +{ + disconnect(viewInterface(), &KMyMoneyPlugin::ViewInterface::transactionsSelected, this, &PrintCheck::slotTransactionsSelected); +} + +void PrintCheck::readCheckTemplate() +{ + QString checkTemplateHTMLPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "printcheck/check_template.html"); if (PluginSettings::checkTemplateFile().isEmpty()) { PluginSettings::setCheckTemplateFile(checkTemplateHTMLPath); PluginSettings::self()->save(); } QFile checkTemplateHTMLFile(PluginSettings::checkTemplateFile()); checkTemplateHTMLFile.open(QIODevice::ReadOnly); QTextStream stream(&checkTemplateHTMLFile); d->m_checkTemplateHTML = stream.readAll(); checkTemplateHTMLFile.close(); } -bool KMMPrintCheckPlugin::canBePrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction) const +bool PrintCheck::canBePrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction) const { MyMoneyFile* file = MyMoneyFile::instance(); bool isACheck = file->account(selectedTransaction.split().accountId()).accountType() == eMyMoney::Account::Type::Checkings && selectedTransaction.split().shares().isNegative(); return isACheck && d->m_printedTransactionIdList.contains(selectedTransaction.transaction().id()) == 0; } -void KMMPrintCheckPlugin::markAsPrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction) +void PrintCheck::markAsPrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction) { d->m_printedTransactionIdList.append(selectedTransaction.transaction().id()); } -void KMMPrintCheckPlugin::slotPrintCheck() +void PrintCheck::slotPrintCheck() { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyMoneyToWordsConverter converter; #ifdef ENABLE_WEBENGINE auto htmlPart = new QWebEngineView(); #else auto htmlPart = new KWebView(); #endif KMyMoneyRegister::SelectedTransactions::const_iterator it; for (it = d->m_transactions.constBegin(); it != d->m_transactions.constEnd(); ++it) { if (!canBePrinted(*it)) continue; // skip this check since it was already printed QString checkHTML = d->m_checkTemplateHTML; const MyMoneyAccount account = file->account((*it).split().accountId()); const MyMoneySecurity currency = file->currency(account.currencyId()); const MyMoneyInstitution institution = file->institution(file->account((*it).split().accountId()).institutionId()); // replace the predefined tokens // data about the user checkHTML.replace("$OWNER_NAME", file->user().name()); checkHTML.replace("$OWNER_ADDRESS", file->user().address()); checkHTML.replace("$OWNER_CITY", file->user().city()); checkHTML.replace("$OWNER_STATE", file->user().state()); // data about the account institution checkHTML.replace("$INSTITUTION_NAME", institution.name()); checkHTML.replace("$INSTITUTION_STREET", institution.street()); checkHTML.replace("$INSTITUTION_TELEPHONE", institution.telephone()); checkHTML.replace("$INSTITUTION_TOWN", institution.town()); checkHTML.replace("$INSTITUTION_CITY", institution.city()); checkHTML.replace("$INSTITUTION_POSTCODE", institution.postcode()); checkHTML.replace("$INSTITUTION_MANAGER", institution.manager()); // data about the transaction checkHTML.replace("$DATE", QLocale().toString((*it).transaction().postDate(), QLocale::ShortFormat)); checkHTML.replace("$CHECK_NUMBER", (*it).split().number()); checkHTML.replace("$PAYEE_NAME", file->payee((*it).split().payeeId()).name()); checkHTML.replace("$PAYEE_ADDRESS", file->payee((*it).split().payeeId()).address()); checkHTML.replace("$PAYEE_CITY", file->payee((*it).split().payeeId()).city()); checkHTML.replace("$PAYEE_POSTCODE", file->payee((*it).split().payeeId()).postcode()); checkHTML.replace("$PAYEE_STATE", file->payee((*it).split().payeeId()).state()); checkHTML.replace("$AMOUNT_STRING", converter.convert((*it).split().shares().abs(), currency.smallestAccountFraction())); checkHTML.replace("$AMOUNT_DECIMAL", MyMoneyUtils::formatMoney((*it).split().shares().abs(), currency)); checkHTML.replace("$MEMO", (*it).split().memo()); // print the check htmlPart->setHtml(checkHTML, QUrl("file://")); m_currentPrinter = new QPrinter(); QPointer dialog = new QPrintDialog(m_currentPrinter); dialog->setWindowTitle(QString()); if (dialog->exec() != QDialog::Accepted) { delete m_currentPrinter; m_currentPrinter = nullptr; continue; } else { #ifdef ENABLE_WEBENGINE htmlPart->page()->print(m_currentPrinter, [=] (bool) {delete m_currentPrinter; m_currentPrinter = nullptr;}); #else htmlPart->print(m_currentPrinter); #endif } delete dialog; // mark the transaction as printed markAsPrinted(*it); } PluginSettings::setPrintedChecks(d->m_printedTransactionIdList); delete htmlPart; } -void KMMPrintCheckPlugin::slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions) +void PrintCheck::slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions) { d->m_transactions = transactions; bool actionEnabled = false; // enable/disable the action depending if there are transactions selected or not // and whether they can be printed or not KMyMoneyRegister::SelectedTransactions::const_iterator it; for (it = d->m_transactions.constBegin(); it != d->m_transactions.constEnd(); ++it) { if (canBePrinted(*it)) { actionEnabled = true; break; } } d->m_action->setEnabled(actionEnabled); } -// the plugin loader plugs in a plugin -void KMMPrintCheckPlugin::slotPlug(KPluginInfo *info) -{ - if (info->name() == objectName()) { - connect(viewInterface(), SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions)), - this, SLOT(slotTransactionsSelected(KMyMoneyRegister::SelectedTransactions))); - } -} - // the plugin's configurations has changed -void KMMPrintCheckPlugin::slotUpdateConfig() +void PrintCheck::configurationChanged() { PluginSettings::self()->load(); // re-read the data because the configuration has changed readCheckTemplate(); d->m_printedTransactionIdList = PluginSettings::printedChecks(); } + +K_PLUGIN_FACTORY_WITH_JSON(PrintCheckFactory, "printcheck.json", registerPlugin();) + +#include "printcheck.moc" diff --git a/kmymoney/plugins/printcheck/printcheck.h b/kmymoney/plugins/printcheck/printcheck.h index 9f859f3d4..78949d0c0 100644 --- a/kmymoney/plugins/printcheck/printcheck.h +++ b/kmymoney/plugins/printcheck/printcheck.h @@ -1,61 +1,61 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 * ***************************************************************************/ #ifndef PRINTCHECK_H #define PRINTCHECK_H #include #include "kmymoneyplugin.h" #include "selectedtransactions.h" class KPluginInfo; class QPrinter; class QObject; -class KMMPrintCheckPlugin: public KMyMoneyPlugin::Plugin +class PrintCheck : public KMyMoneyPlugin::Plugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.printcheck" FILE "kmm_printcheck.json") public: - KMMPrintCheckPlugin(); - ~KMMPrintCheckPlugin(); + explicit PrintCheck(QObject *parent, const QVariantList &args); + ~PrintCheck() override; + +public Q_SLOTS: + void plug() override; + void unplug() override; + void configurationChanged() override; private: void readCheckTemplate(); bool canBePrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction) const; void markAsPrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction); protected Q_SLOTS: void slotPrintCheck(); void slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions); - // the plugin loader plugs in a plugin - void slotPlug(KPluginInfo*); - // the plugin's configurations has changed - void slotUpdateConfig(); private: struct Private; std::unique_ptr d; QPrinter *m_currentPrinter; }; #endif // PRINTCHECK_H diff --git a/kmymoney/plugins/printcheck/kmm_printcheck.json.in b/kmymoney/plugins/printcheck/printcheck.json.in similarity index 99% rename from kmymoney/plugins/printcheck/kmm_printcheck.json.in rename to kmymoney/plugins/printcheck/printcheck.json.in index 78bf5b03e..a9e37852d 100644 --- a/kmymoney/plugins/printcheck/kmm_printcheck.json.in +++ b/kmymoney/plugins/printcheck/printcheck.json.in @@ -1,85 +1,85 @@ { "KPlugin": { "Authors": [ { "Email": "onet.cristian@gmail.com", "Name": "Cristian Oneț" } ], "Description": "Provides the capability to print checks", "Description[ar]": "توفّر إمكانيّة طباعة الشّيكات", "Description[bs]": "Daje mogućnost štampanja čekova", "Description[ca@valencia]": "Aporta la capacitat d'imprimir xecs", "Description[ca]": "Aporta la capacitat d'imprimir xecs", "Description[da]": "Giver mulighed for at udskrive checks", "Description[de]": "Stellt die Möglichkeit bereit, Schecks zu drucken", "Description[el]": "Παρέχει την δυνατότητα εκτύπωσης επιταγών", "Description[en_GB]": "Provides the capability to print cheques", "Description[es]": "Proporciona la capacidad de imprimir cheques", "Description[et]": "Tšekkide trükkimise võimaldamine", "Description[eu]": "Txekeak inprimatzeko gaitasuna ematen du", "Description[fi]": "Tarjoaa kyvyn tulostaa shekkejä", "Description[fr]": "Fournit la possibilité d'imprimer des chèques", "Description[gl]": "Fornece a capacidade de imprimir cheques", "Description[hu]": "Biztosítja a nyomtatás-ellenőrzés képességet", "Description[it]": "Fornisce la capacità di stampare assegni", "Description[kk]": "Чекті басып шығару мүмкіндігін қосу", "Description[nds]": "Stellt Scheckdruck praat", "Description[nl]": "Levert de mogelijkheid checks af te drukken", "Description[pl]": "Dostarcza możliwość drukowania czeków", "Description[pt]": "Oferece a capacidade de impressão de cheques", "Description[pt_BR]": "Fornece a capacidade de impressão de cheques", "Description[ro]": "Permite tipărirea cecurilor din KMyMoney", "Description[ru]": "Печать чеков", "Description[sk]": "Poskytuje možnosť tlačiť účty", "Description[sv]": "Tillhandahåller möjlighet att skriva ut checkar", "Description[tr]": "Çek yazdırmak yeteneği sağlar", "Description[uk]": "Надає змогу друкувати чеки", "Description[x-test]": "xxProvides the capability to print checksxx", "Description[zh_TW]": "提供列印支票的功能", "EnabledByDefault": true, "Icon": "document-print", - "Id": "Print check", + "Id": "printcheck", "License": "GPL", "Name": "Print check", "Name[ar]": "اطبع شيكًا", "Name[bs]": "Provjera štampe", "Name[ca@valencia]": "Imprimeix el xec", "Name[ca]": "Imprimeix el xec", "Name[cs]": "Vytisknout šek", "Name[da]": "Udskriv check", "Name[de]": "Schecks drucken", "Name[el]": "Εκτύπωση επιταγής", "Name[en_GB]": "Print cheque", "Name[es]": "Imprimir cheque", "Name[et]": "Tšeki trükkimine", "Name[eu]": "Inprimatu txekea", "Name[fi]": "Tulosta shekki", "Name[fr]": "Imprimer un chèque", "Name[gl]": "Imprimir cheque", "Name[hu]": "Nyomtatás-ellenőrzés", "Name[it]": "Stampa assegno", "Name[kk]": "Чекті басып шығару", "Name[lt]": "Spausdinti čekį", "Name[mr]": "चेक छापा", "Name[nds]": "Scheck drucken", "Name[nl]": "Cheque afdrukken", "Name[pl]": "Drukowanie czeków", "Name[pt]": "Imprimir um cheque", "Name[pt_BR]": "Impressão de cheques", "Name[ro]": "Tipărește un cec", "Name[ru]": "Печать чеков", "Name[sk]": "Tlačiť šek", "Name[sv]": "Skriv ut check", "Name[tr]": "Çek yazdır", "Name[uk]": "Надрукувати чек", "Name[x-test]": "xxPrint checkxx", "Name[zh_CN]": "打印支票", "Name[zh_TW]": "列印支票", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/printcheck/kmm_printcheck.rc b/kmymoney/plugins/printcheck/printcheck.rc similarity index 100% rename from kmymoney/plugins/printcheck/kmm_printcheck.rc rename to kmymoney/plugins/printcheck/printcheck.rc diff --git a/kmymoney/plugins/qif/config/CMakeLists.txt b/kmymoney/plugins/qif/config/CMakeLists.txt index e14b26cfa..3709d7933 100644 --- a/kmymoney/plugins/qif/config/CMakeLists.txt +++ b/kmymoney/plugins/qif/config/CMakeLists.txt @@ -1,41 +1,41 @@ # the KCM module -set(kcm_kmm_qif_PART_SRCS +set(kcm_qif_PART_SRCS mymoneyqifprofileeditor.cpp mymoneyqifprofile.cpp kcm_qif.cpp ) -ki18n_wrap_ui(kcm_kmm_qif_PART_SRCS mymoneyqifprofileeditor.ui) +ki18n_wrap_ui(kcm_qif_PART_SRCS mymoneyqifprofileeditor.ui) -add_library(kcm_kmm_qif MODULE ${kcm_kmm_qif_PART_SRCS}) +add_library(kcm_qif MODULE ${kcm_qif_PART_SRCS}) if(ENABLE_QIFIMPORTER) - kcoreaddons_desktop_to_json(kcm_kmm_qif kcm_kmm_qifimport.desktop) + kcoreaddons_desktop_to_json(kcm_qif kcm_qifimporter.desktop) endif() if(ENABLE_QIFEXPORTER) - kcoreaddons_desktop_to_json(kcm_kmm_qif kcm_kmm_qifexport.desktop) + kcoreaddons_desktop_to_json(kcm_qif kcm_qifexporter.desktop) endif() -target_link_libraries(kcm_kmm_qif +target_link_libraries(kcm_qif kmm_mymoney KF5::I18n KF5::ConfigWidgets KF5::Completion KF5::KIOWidgets ) -install(TARGETS kcm_kmm_qif +install(TARGETS kcm_qif DESTINATION ${KDE_INSTALL_PLUGINDIR}) if(ENABLE_QIFIMPORTER) - install(FILES kcm_kmm_qifimport.desktop + install(FILES kcm_qifimporter.desktop DESTINATION ${SERVICES_INSTALL_DIR}) endif() if(ENABLE_QIFEXPORTER) - install(FILES kcm_kmm_qifexport.desktop + install(FILES kcm_qifexporter.desktop DESTINATION ${SERVICES_INSTALL_DIR}) endif() diff --git a/kmymoney/plugins/qif/config/kcm_qif.cpp b/kmymoney/plugins/qif/config/kcm_qif.cpp index a37c88d16..9c2bb53ac 100644 --- a/kmymoney/plugins/qif/config/kcm_qif.cpp +++ b/kmymoney/plugins/qif/config/kcm_qif.cpp @@ -1,51 +1,43 @@ /*************************************************************************** * Copyright 2017 Łukasz Wojniłowicz lukasz.wojnilowicz@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 "kcm_qif.h" #include // KDE includes + #include #include #include "mymoneyqifprofileeditor.h" -K_PLUGIN_FACTORY(KCMqifFactory, registerPlugin();) - KCMqif::KCMqif(QWidget *parent, const QVariantList& args) : KCModule(parent, args) { - KAboutData *about = new KAboutData(QStringLiteral("kmm_csvimport"), - i18n("KMyMoney QIF importer"), - QStringLiteral(VERSION), QString(), - KAboutLicense::GPL, - i18n("Copyright 2002-2017" ) ); - about->addAuthor( QLatin1String("Thomas Baumgart") ); - - setAboutData( about ); - auto editor = new MyMoneyQifProfileEditor(true, this); auto layout = new QVBoxLayout; setLayout(layout); layout->addWidget(editor); setButtons(NoAdditionalButton); load(); } +K_PLUGIN_FACTORY(KCMqifFactory, registerPlugin();) + #include "kcm_qif.moc" diff --git a/kmymoney/plugins/qif/config/kcm_kmm_qifexport.desktop b/kmymoney/plugins/qif/config/kcm_qifexporter.desktop similarity index 92% rename from kmymoney/plugins/qif/config/kcm_kmm_qifexport.desktop rename to kmymoney/plugins/qif/config/kcm_qifexporter.desktop index 4ee729eee..516d38697 100644 --- a/kmymoney/plugins/qif/config/kcm_kmm_qifexport.desktop +++ b/kmymoney/plugins/qif/config/kcm_qifexporter.desktop @@ -1,54 +1,54 @@ [Desktop Entry] Name=QIF Importer configuration Name[ca]=Configuració de l'importador de QIF Name[ca@valencia]=Configuració de l'importador de QIF Name[cs]=Nastavení importu QIF Name[de]=QIF-Importeinrichtung Name[en_GB]=QIF Importer configuration Name[es]=Configuración del importador QIF Name[it]=Configurazione dello strumento di importazione QIF Name[nl]=Instellingen van QIF-importeren Name[pl]=Ustawienia importera QIF Name[pt]=Configuração da Importação de QIF Name[sv]=QIF-importinställning Name[tr]=QIF İçe Aktarıcı yapılandırması Name[uk]=Налаштування засобу імпортування QIF Name[x-test]=xxQIF Importer configurationxx Icon=document-import Type=Service ServiceTypes=KCModule -Exec=kcmshell5 kmm_qifexport +Exec=kcmshell5 qifexporter Categories=Qt;KDE;kmymoney; -Keywords=qifexport;kmymoney; -Keywords[ca]=qifexport;kmymoney; +Keywords=qifexporter;kmymoney; +Keywords[ca]=qifexporter;kmymoney; Keywords[ca@valencia]=qifexport;kmymoney; Keywords[cs]=qifexport;kmymoney; Keywords[de]=qifexport;kmymoney; Keywords[en_GB]=qifexport;kmymoney; Keywords[es]=qifexport;kmymoney; Keywords[it]=qifexport;kmymoney; Keywords[nl]=qifexport;kmymoney; Keywords[pl]=qifexport;kmymoney; Keywords[pt]=exportação qif;kmymoney; Keywords[sv]=qif-export;kmymoney; Keywords[tr]=qifexport;kmymoney; Keywords[uk]=qifexport;kmymoney;експорт;кома;кмаймані;кіф; Keywords[x-test]=xxqifexportxx;xxkmymoneyxx; -X-KDE-Library=kcm_kmm_qif -X-KDE-ParentApp=qifexport -X-KDE-ParentComponents=qifexport +X-KDE-Library=kcm_qif +X-KDE-ParentApp=qifexporter +X-KDE-ParentComponents=qifexporter Comment=Configuration for QIF Importer plugin Comment[ca]=Configuració del connector d'importació de QIF Comment[ca@valencia]=Configuració del connector d'importació de QIF Comment[cs]=Nastavení modulu pro import QIF Comment[de]=Einrichtung des QIF-Importmoduls Comment[en_GB]=Configuration for QIF Importer plugin Comment[es]=Configuración para el complemento de importación QIF Comment[it]=Configurazione dell'estensione di importazione QIF Comment[nl]=Instellingen voor plug-in voor QIF importeren Comment[pl]=Ustawienia dla wtyczki Importera QIF Comment[pt]=Configuração do 'plugin' de Importação de QIF Comment[sv]=Anpassa QIF-importinsticksprogram Comment[tr]=QIF İçe Aktarıcı eklentisi için yapılandırma Comment[uk]=Налаштування додатка імпортування у форматі QIF Comment[x-test]=xxConfiguration for QIF Importer pluginxx diff --git a/kmymoney/plugins/qif/config/kcm_kmm_qifimport.desktop b/kmymoney/plugins/qif/config/kcm_qifimporter.desktop similarity index 93% rename from kmymoney/plugins/qif/config/kcm_kmm_qifimport.desktop rename to kmymoney/plugins/qif/config/kcm_qifimporter.desktop index c752f0395..c931017e9 100644 --- a/kmymoney/plugins/qif/config/kcm_kmm_qifimport.desktop +++ b/kmymoney/plugins/qif/config/kcm_qifimporter.desktop @@ -1,54 +1,54 @@ [Desktop Entry] Name=QIF Importer configuration Name[ca]=Configuració de l'importador de QIF Name[ca@valencia]=Configuració de l'importador de QIF Name[cs]=Nastavení importu QIF Name[de]=QIF-Importeinrichtung Name[en_GB]=QIF Importer configuration Name[es]=Configuración del importador QIF Name[it]=Configurazione dello strumento di importazione QIF Name[nl]=Instellingen van QIF-importeren Name[pl]=Ustawienia importera QIF Name[pt]=Configuração da Importação de QIF Name[sv]=QIF-importinställning Name[tr]=QIF İçe Aktarıcı yapılandırması Name[uk]=Налаштування засобу імпортування QIF Name[x-test]=xxQIF Importer configurationxx Icon=document-import Type=Service ServiceTypes=KCModule -Exec=kcmshell5 kmm_qifimport +Exec=kcmshell5 qifimporter Categories=Qt;KDE;kmymoney; -Keywords=qifimport;kmymoney; +Keywords=qifimporter;kmymoney; Keywords[ca]=qifimport;kmymoney; Keywords[ca@valencia]=qifimport;kmymoney; Keywords[cs]=qifimport;kmymoney; Keywords[de]=qifimport;kmymoney; Keywords[en_GB]=qifimport;kmymoney; Keywords[es]=qifimport;kmymoney; Keywords[it]=qifimport;kmymoney; Keywords[nl]=qifimport;kmymoney; Keywords[pl]=qifimport;kmymoney; Keywords[pt]=importação qif;kmymoney; Keywords[sv]=qif-import;kmymoney; Keywords[tr]=qifimport;kmymoney; Keywords[uk]=qifimport;kmymoney;імпортування;кіф;кмаймані; Keywords[x-test]=xxqifimportxx;xxkmymoneyxx; -X-KDE-Library=kcm_kmm_qif -X-KDE-ParentApp=qifimport -X-KDE-ParentComponents=qifimport +X-KDE-Library=kcm_qif +X-KDE-ParentApp=qifimporter +X-KDE-ParentComponents=qifimporter Comment=Configuration for QIF Importer plugin Comment[ca]=Configuració del connector d'importació de QIF Comment[ca@valencia]=Configuració del connector d'importació de QIF Comment[cs]=Nastavení modulu pro import QIF Comment[de]=Einrichtung des QIF-Importmoduls Comment[en_GB]=Configuration for QIF Importer plugin Comment[es]=Configuración para el complemento de importación QIF Comment[it]=Configurazione dell'estensione di importazione QIF Comment[nl]=Instellingen voor plug-in voor QIF importeren Comment[pl]=Ustawienia dla wtyczki Importera QIF Comment[pt]=Configuração do 'plugin' de Importação de QIF Comment[sv]=Anpassa QIF-importinsticksprogram Comment[tr]=QIF İçe Aktarıcı eklentisi için yapılandırma Comment[uk]=Налаштування додатка імпортування у форматі QIF Comment[x-test]=xxConfiguration for QIF Importer pluginxx diff --git a/kmymoney/plugins/qif/export/CMakeLists.txt b/kmymoney/plugins/qif/export/CMakeLists.txt index 43bb900c3..ee3c2725b 100644 --- a/kmymoney/plugins/qif/export/CMakeLists.txt +++ b/kmymoney/plugins/qif/export/CMakeLists.txt @@ -1,33 +1,33 @@ # patch the version with the version defined in the build system -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qifexport.json.in ${CMAKE_CURRENT_BINARY_DIR}/qifexport.json @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qifexporter.json.in ${CMAKE_CURRENT_BINARY_DIR}/qifexporter.json @ONLY) ########### next target ############### -set(kmm_qifexport_PART_SRCS - qifexporterplugin.cpp +set(qifexporter_PART_SRCS + qifexporter.cpp ../config/mymoneyqifprofile.cpp mymoneyqifwriter.cpp kexportdlg.cpp ${CMAKE_SOURCE_DIR}/kmymoney/widgets/kmymoneyaccountcombo.cpp ) -set(kmm_qifexport_PART_UI +set(qifexporter_PART_UI kexportdlgdecl.ui ) -ki18n_wrap_ui(kmm_qifexport_PART_SRCS ${kmm_qifexport_PART_UI}) +ki18n_wrap_ui(qifexporter_PART_SRCS ${qifexporter_PART_UI}) -add_library(kmm_qifexport MODULE ${kmm_qifexport_PART_SRCS}) +add_library(qifexporter MODULE ${qifexporter_PART_SRCS}) -target_link_libraries(kmm_qifexport +target_link_libraries(qifexporter kmm_plugin models #TODO: Get rid of this big dependency ) ########### install files ############### -install(FILES kmm_qifexport.rc - DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_qifexport") +install(FILES qifexporter.rc + DESTINATION "${KXMLGUI_INSTALL_DIR}/qifexporter") -install(TARGETS kmm_qifexport +install(TARGETS qifexporter DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") diff --git a/kmymoney/plugins/qif/export/kcm_kmm_qifexport.desktop b/kmymoney/plugins/qif/export/kcm_kmm_qifexport.desktop deleted file mode 100644 index 456d4b828..000000000 --- a/kmymoney/plugins/qif/export/kcm_kmm_qifexport.desktop +++ /dev/null @@ -1,54 +0,0 @@ -[Desktop Entry] -Name=QIF Importer configuration -Name[ca]=Configuració de l'importador de QIF -Name[ca@valencia]=Configuració de l'importador de QIF -Name[cs]=Nastavení importu QIF -Name[de]=QIF-Importeinrichtung -Name[en_GB]=QIF Importer configuration -Name[es]=Configuración del importador QIF -Name[it]=Configurazione dello strumento di importazione QIF -Name[nl]=Instellingen van QIF-importeren -Name[pl]=Ustawienia importera QIF -Name[pt]=Configuração da Importação de QIF -Name[sv]=QIF-importinställning -Name[tr]=QIF İçe Aktarıcı yapılandırması -Name[uk]=Налаштування засобу імпортування QIF -Name[x-test]=xxQIF Importer configurationxx -Icon=document-import -Type=Service -ServiceTypes=KCModule -Exec=kcmshell5 kmm_qifimport -Categories=Qt;KDE;kmymoney; -Keywords=qifexport;kmymoney; -Keywords[ca]=qifexport;kmymoney; -Keywords[ca@valencia]=qifexport;kmymoney; -Keywords[cs]=qifexport;kmymoney; -Keywords[de]=qifexport;kmymoney; -Keywords[en_GB]=qifexport;kmymoney; -Keywords[es]=qifexport;kmymoney; -Keywords[it]=qifexport;kmymoney; -Keywords[nl]=qifexport;kmymoney; -Keywords[pl]=qifexport;kmymoney; -Keywords[pt]=exportação qif;kmymoney; -Keywords[sv]=qif-export;kmymoney; -Keywords[tr]=qifexport;kmymoney; -Keywords[uk]=qifexport;kmymoney;експорт;кома;кмаймані;кіф; -Keywords[x-test]=xxqifexportxx;xxkmymoneyxx; -X-KDE-Library=kcm_kmm_qifimport -X-KDE-ParentApp=qifexport -X-KDE-ParentComponents=qifexport -Comment=Configuration for QIF Importer plugin -Comment[ca]=Configuració del connector d'importació de QIF -Comment[ca@valencia]=Configuració del connector d'importació de QIF -Comment[cs]=Nastavení modulu pro import QIF -Comment[de]=Einrichtung des QIF-Importmoduls -Comment[en_GB]=Configuration for QIF Importer plugin -Comment[es]=Configuración para el complemento de importación QIF -Comment[it]=Configurazione dell'estensione di importazione QIF -Comment[nl]=Instellingen voor plug-in voor QIF importeren -Comment[pl]=Ustawienia dla wtyczki Importera QIF -Comment[pt]=Configuração do 'plugin' de Importação de QIF -Comment[sv]=Anpassa QIF-importinsticksprogram -Comment[tr]=QIF İçe Aktarıcı eklentisi için yapılandırma -Comment[uk]=Налаштування додатка імпортування у форматі QIF -Comment[x-test]=xxConfiguration for QIF Importer pluginxx diff --git a/kmymoney/plugins/qif/export/qifexporterplugin.cpp b/kmymoney/plugins/qif/export/qifexporter.cpp similarity index 74% rename from kmymoney/plugins/qif/export/qifexporterplugin.cpp rename to kmymoney/plugins/qif/export/qifexporter.cpp index 0f6064447..2798b545b 100644 --- a/kmymoney/plugins/qif/export/qifexporterplugin.cpp +++ b/kmymoney/plugins/qif/export/qifexporter.cpp @@ -1,78 +1,81 @@ /*************************************************************************** - qifimporterplugin.cpp - (based on qifimporterplugin.cpp) + qifexporter.cpp ------------------- copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "qifexporterplugin.h" +#include "qifexporter.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes +#include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kexportdlg.h" #include "mymoneyqifwriter.h" - -QIFExporterPlugin::QIFExporterPlugin() : - KMyMoneyPlugin::Plugin(nullptr, "qifexport"/*must be the same as X-KDE-PluginInfo-Name*/) +QIFExporter::QIFExporter(QObject *parent, const QVariantList &args) : + KMyMoneyPlugin::Plugin(parent, "qifexporter"/*must be the same as X-KDE-PluginInfo-Name*/) { - setComponentName("kmm_qifexport", i18n("QIF exporter")); - setXMLFile("kmm_qifexport.rc"); + Q_UNUSED(args); + setComponentName("qifexporter", i18n("QIF exporter")); + setXMLFile("qifexporter.rc"); createActions(); // For information, announce that we have been loaded. - qDebug("KMyMoney qifexport plugin loaded"); + qDebug("Plugins: qifexporter loaded"); } -QIFExporterPlugin::~QIFExporterPlugin() +QIFExporter::~QIFExporter() { + qDebug("Plugins: qifexporter unloaded"); } -void QIFExporterPlugin::createActions() +void QIFExporter::createActions() { m_action = actionCollection()->addAction("file_export_qif"); m_action->setText(i18n("QIF...")); - connect(m_action, &QAction::triggered, this, &QIFExporterPlugin::slotQifExport); + connect(m_action, &QAction::triggered, this, &QIFExporter::slotQifExport); } -void QIFExporterPlugin::slotQifExport() +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); } -//#include "qifexporterplugin.moc" +K_PLUGIN_FACTORY_WITH_JSON(QIFExporterFactory, "qifexporter.json", registerPlugin();) + +#include "qifexporter.moc" diff --git a/kmymoney/plugins/qif/export/qifexporterplugin.h b/kmymoney/plugins/qif/export/qifexporter.h similarity index 84% rename from kmymoney/plugins/qif/export/qifexporterplugin.h rename to kmymoney/plugins/qif/export/qifexporter.h index 394efff99..7b64c23b2 100644 --- a/kmymoney/plugins/qif/export/qifexporterplugin.h +++ b/kmymoney/plugins/qif/export/qifexporter.h @@ -1,57 +1,56 @@ /*************************************************************************** - qifexporterplugin.h + qifexporter.h ------------------- copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ -#ifndef QIFEXPORTERPLUGIN_H -#define QIFEXPORTERPLUGIN_H +#ifndef QIFEXPORTER_H +#define QIFEXPORTER_H // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // QT Includes // Project Includes #include "kmymoneyplugin.h" class MyMoneyQifReader; -class QIFExporterPlugin : public KMyMoneyPlugin::Plugin +class QIFExporter : public KMyMoneyPlugin::Plugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.qifexport" FILE "qifexport.json") public: - explicit QIFExporterPlugin(); - ~QIFExporterPlugin(); + explicit QIFExporter(QObject *parent, const QVariantList &args); + ~QIFExporter() override; QAction *m_action; MyMoneyQifReader *m_qifReader; public Q_SLOTS: /** * Called when the user wishes to export some transaction to a * QIF formatted file. An account must be open for this to work. * Uses MyMoneyQifWriter() for the actual output. */ void slotQifExport(); protected: void createActions(); }; #endif diff --git a/kmymoney/plugins/qif/export/qifexport.json.in b/kmymoney/plugins/qif/export/qifexporter.json.in similarity index 95% rename from kmymoney/plugins/qif/export/qifexport.json.in rename to kmymoney/plugins/qif/export/qifexporter.json.in index 22f155a31..39294d44a 100644 --- a/kmymoney/plugins/qif/export/qifexport.json.in +++ b/kmymoney/plugins/qif/export/qifexporter.json.in @@ -1,21 +1,21 @@ { "KPlugin": { "Authors": [ { "Email": "mte@users.sourceforge.net", "Name": "Michael Edwardes" } ], "Description": "Add QIF exporting to KMyMoney", "EnabledByDefault": true, "Icon": "document-export", - "Id": "qifexport", + "Id": "qifexporter", "License": "GPL", "Name": "QIF Exporter", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/qif/export/kmm_qifexport.rc b/kmymoney/plugins/qif/export/qifexporter.rc similarity index 100% rename from kmymoney/plugins/qif/export/kmm_qifexport.rc rename to kmymoney/plugins/qif/export/qifexporter.rc diff --git a/kmymoney/plugins/qif/import/CMakeLists.txt b/kmymoney/plugins/qif/import/CMakeLists.txt index f583c3fdd..9a5b6fff3 100644 --- a/kmymoney/plugins/qif/import/CMakeLists.txt +++ b/kmymoney/plugins/qif/import/CMakeLists.txt @@ -1,32 +1,32 @@ # patch the version with the version defined in the build system -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qifimport.json.in ${CMAKE_CURRENT_BINARY_DIR}/qifimport.json @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qifimporter.json.in ${CMAKE_CURRENT_BINARY_DIR}/qifimporter.json @ONLY) ########### next target ############### -set(kmm_qifimport_PART_SRCS - qifimporterplugin.cpp +set(qifimporter_PART_SRCS + qifimporter.cpp ../config/mymoneyqifprofile.cpp mymoneyqifreader.cpp kimportdlg.cpp ) -set(kmm_qifimport_PART_UI +set(qifimporter_PART_UI kimportdlgdecl.ui ) -ki18n_wrap_ui(kmm_qifimport_PART_SRCS ${kmm_qifimport_PART_UI}) +ki18n_wrap_ui(qifimporter_PART_SRCS ${qifimporter_PART_UI}) -add_library(kmm_qifimport MODULE ${kmm_qifimport_PART_SRCS}) +add_library(qifimporter MODULE ${qifimporter_PART_SRCS}) -target_link_libraries(kmm_qifimport +target_link_libraries(qifimporter kmm_plugin Alkimia::alkimia ) ########### install files ############### -install(FILES kmm_qifimport.rc - DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_qifimport") +install(FILES qifimporter.rc + DESTINATION "${KXMLGUI_INSTALL_DIR}/qifimporter") -install(TARGETS kmm_qifimport +install(TARGETS qifimporter DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") diff --git a/kmymoney/plugins/qif/import/qifimporterplugin.cpp b/kmymoney/plugins/qif/import/qifimporter.cpp similarity index 72% rename from kmymoney/plugins/qif/import/qifimporterplugin.cpp rename to kmymoney/plugins/qif/import/qifimporter.cpp index 122c66695..c8db5dcb2 100644 --- a/kmymoney/plugins/qif/import/qifimporterplugin.cpp +++ b/kmymoney/plugins/qif/import/qifimporter.cpp @@ -1,90 +1,94 @@ /*************************************************************************** - qifimporterplugin.cpp - (based on qifimporterplugin.cpp) + qifimporter.cpp ------------------- copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "qifimporterplugin.h" +#include "qifimporter.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes +#include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kimportdlg.h" #include "mymoneyqifreader.h" #include "statementinterface.h" class MyMoneyStatement; -QIFImporterPlugin::QIFImporterPlugin() : - KMyMoneyPlugin::Plugin(nullptr, "qifimport"/*must be the same as X-KDE-PluginInfo-Name*/) +QIFImporter::QIFImporter(QObject *parent, const QVariantList &args) : + KMyMoneyPlugin::Plugin(parent, "qifimporter"/*must be the same as X-KDE-PluginInfo-Name*/) { - setComponentName("kmm_qifimport", i18n("QIF importer")); - setXMLFile("kmm_qifimport.rc"); + Q_UNUSED(args); + setComponentName("qifimporter", i18n("QIF importer")); + setXMLFile("qifimporter.rc"); createActions(); // For information, announce that we have been loaded. - qDebug("KMyMoney qifimport plugin loaded"); + qDebug("Plugins: qifimporter loaded"); } -QIFImporterPlugin::~QIFImporterPlugin() +QIFImporter::~QIFImporter() { + qDebug("Plugins: qifimporter unloaded"); } -void QIFImporterPlugin::createActions() +void QIFImporter::createActions() { m_action = actionCollection()->addAction("file_import_qif"); m_action->setText(i18n("QIF...")); - connect(m_action, &QAction::triggered, this, &QIFImporterPlugin::slotQifImport); + connect(m_action, &QAction::triggered, this, &QIFImporter::slotQifImport); } -void QIFImporterPlugin::slotQifImport() +void QIFImporter::slotQifImport() { m_action->setEnabled(false); QPointer dlg = new KImportDlg(nullptr); if (dlg->exec() == QDialog::Accepted && dlg != nullptr) { m_qifReader = new MyMoneyQifReader; - connect(m_qifReader, &MyMoneyQifReader::statementsReady, this, &QIFImporterPlugin::slotGetStatements); + 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; } delete dlg; m_action->setEnabled(true); } -bool QIFImporterPlugin::slotGetStatements(QList &statements) +bool QIFImporter::slotGetStatements(QList &statements) { auto ret = true; foreach (const auto statement, statements) ret &= statementInterface()->import(statement); delete m_qifReader; return ret; } -//#include "qifimporterplugin.moc" +K_PLUGIN_FACTORY_WITH_JSON(QIFImporterFactory, "qifimporter.json", registerPlugin();) + +#include "qifimporter.moc" diff --git a/kmymoney/plugins/qif/import/qifimporterplugin.h b/kmymoney/plugins/qif/import/qifimporter.h similarity index 85% rename from kmymoney/plugins/qif/import/qifimporterplugin.h rename to kmymoney/plugins/qif/import/qifimporter.h index 79e2e9787..568003a54 100644 --- a/kmymoney/plugins/qif/import/qifimporterplugin.h +++ b/kmymoney/plugins/qif/import/qifimporter.h @@ -1,64 +1,63 @@ /*************************************************************************** - qifimporterplugin.h + qifimporter.h ------------------- copyright : (C) 2017 by Łukasz Wojniłowicz email : lukasz.wojnilowicz@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ -#ifndef QIFIMPORTERPLUGIN_H -#define QIFIMPORTERPLUGIN_H +#ifndef QIFIMPORTER_H +#define QIFIMPORTER_H // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // QT Includes // Project Includes #include "kmymoneyplugin.h" #include "mymoneystatement.h" class MyMoneyQifReader; -class QIFImporterPlugin : public KMyMoneyPlugin::Plugin +class QIFImporter : public KMyMoneyPlugin::Plugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.qifimport" FILE "qifimport.json") public: - explicit QIFImporterPlugin(); - ~QIFImporterPlugin(); + explicit QIFImporter(QObject *parent, const QVariantList &args); + ~QIFImporter() override; QAction *m_action; private: MyMoneyQifReader *m_qifReader; private Q_SLOTS: /** * Called when the user wishes to import tab delimeted transactions * into the current account. An account must be open for this to * work. Calls KMyMoneyView::slotAccountImportAscii. * * @see MyMoneyAccount */ void slotQifImport(); bool slotGetStatements(QList &statements); protected: void createActions(); }; #endif diff --git a/kmymoney/plugins/qif/import/qifimport.json.in b/kmymoney/plugins/qif/import/qifimporter.json.in similarity index 95% rename from kmymoney/plugins/qif/import/qifimport.json.in rename to kmymoney/plugins/qif/import/qifimporter.json.in index c34a9605b..b01ae3d56 100644 --- a/kmymoney/plugins/qif/import/qifimport.json.in +++ b/kmymoney/plugins/qif/import/qifimporter.json.in @@ -1,21 +1,21 @@ { "KPlugin": { "Authors": [ { "Email": "mte@users.sourceforge.net", "Name": "Michael Edwardes" } ], "Description": "Add QIF importing to KMyMoney", "EnabledByDefault": true, "Icon": "document-import", - "Id": "qifimport", + "Id": "qifimporter", "License": "GPL", "Name": "QIF Importer", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/qif/import/kmm_qifimport.rc b/kmymoney/plugins/qif/import/qifimporter.rc similarity index 100% rename from kmymoney/plugins/qif/import/kmm_qifimport.rc rename to kmymoney/plugins/qif/import/qifimporter.rc diff --git a/kmymoney/plugins/reconciliationreport/CMakeLists.txt b/kmymoney/plugins/reconciliationreport/CMakeLists.txt index ed3f892ff..69f7fda39 100644 --- a/kmymoney/plugins/reconciliationreport/CMakeLists.txt +++ b/kmymoney/plugins/reconciliationreport/CMakeLists.txt @@ -1,29 +1,29 @@ # patch the version with the version defined in the build system -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmm_reconciliationreport.json.in - ${CMAKE_CURRENT_BINARY_DIR}/kmm_reconciliationreport.json +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/reconciliationreport.json.in + ${CMAKE_CURRENT_BINARY_DIR}/reconciliationreport.json @ONLY ) -set(kmm_reconciliationreport_PART_SRCS +set(reconciliationreport_PART_SRCS kreconciliationreportdlg.cpp reconciliationreport.cpp ) -ki18n_wrap_ui(kmm_reconciliationreport_PART_SRCS kreconciliationreportdlgdecl.ui ) +ki18n_wrap_ui(reconciliationreport_PART_SRCS kreconciliationreportdlgdecl.ui ) -add_library(kmm_reconciliationreport MODULE ${kmm_reconciliationreport_PART_SRCS}) +add_library(reconciliationreport MODULE ${reconciliationreport_PART_SRCS}) -target_link_libraries(kmm_reconciliationreport +target_link_libraries(reconciliationreport Qt5::PrintSupport kmm_mymoney kmm_plugin ) if(ENABLE_WEBENGINE) - target_link_libraries(kmm_reconciliationreport Qt5::WebEngineWidgets) + target_link_libraries(reconciliationreport Qt5::WebEngineWidgets) else(ENABLE_WEBENGINE) - target_link_libraries(kmm_reconciliationreport KF5::WebKit) + target_link_libraries(reconciliationreport KF5::WebKit) endif(ENABLE_WEBENGINE) -install(TARGETS kmm_reconciliationreport +install(TARGETS reconciliationreport DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") diff --git a/kmymoney/plugins/reconciliationreport/reconciliationreport.cpp b/kmymoney/plugins/reconciliationreport/reconciliationreport.cpp index 36c4bc317..c42c76c5f 100644 --- a/kmymoney/plugins/reconciliationreport/reconciliationreport.cpp +++ b/kmymoney/plugins/reconciliationreport/reconciliationreport.cpp @@ -1,324 +1,336 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 "reconciliationreport.h" //! @todo remove #include #include #include #include // KDE includes +#include #include #include // KMyMoney includes #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneypayee.h" #include "mymoneysecurity.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "mymoneytransactionfilter.h" #include "mymoneyutils.h" #include "viewinterface.h" #include "mymoneyenums.h" #include "kreconciliationreportdlg.h" -KMMReconciliationReportPlugin::KMMReconciliationReportPlugin() - : KMyMoneyPlugin::Plugin(nullptr, "Reconciliation report"/*must be the same as X-KDE-PluginInfo-Name*/) +ReconciliationReport::ReconciliationReport(QObject *parent, const QVariantList &args) : + KMyMoneyPlugin::Plugin(parent, "reconciliationreport"/*must be the same as X-KDE-PluginInfo-Name*/) { + Q_UNUSED(args); + qDebug("Plugins: reconciliation report loaded"); } -void KMMReconciliationReportPlugin::plug() +ReconciliationReport::~ReconciliationReport() { - connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::accountReconciled, this, &KMMReconciliationReportPlugin::slotGenerateReconciliationReport); - qDebug() << "Connect was done" << viewInterface(); + qDebug("Plugins: reconciliation report unloaded"); } -void KMMReconciliationReportPlugin::unplug() +void ReconciliationReport::plug() { - disconnect(viewInterface(), &KMyMoneyPlugin::ViewInterface::accountReconciled, this, &KMMReconciliationReportPlugin::slotGenerateReconciliationReport); + connect(viewInterface(), &KMyMoneyPlugin::ViewInterface::accountReconciled, this, &ReconciliationReport::slotGenerateReconciliationReport); +// qDebug() << "Connect was done" << viewInterface(); } -void KMMReconciliationReportPlugin::slotGenerateReconciliationReport(const MyMoneyAccount& account, const QDate& date, const MyMoneyMoney& startingBalance, const MyMoneyMoney& endingBalance, const QList >& transactionList) +void ReconciliationReport::unplug() +{ + disconnect(viewInterface(), &KMyMoneyPlugin::ViewInterface::accountReconciled, this, &ReconciliationReport::slotGenerateReconciliationReport); +} + +void ReconciliationReport::slotGenerateReconciliationReport(const MyMoneyAccount& account, const QDate& date, const MyMoneyMoney& startingBalance, const MyMoneyMoney& endingBalance, const QList >& transactionList) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneySecurity currency = file->currency(account.currencyId()); QString filename; if (!MyMoneyFile::instance()->value("reportstylesheet").isEmpty()) filename = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("html/%1").arg(MyMoneyFile::instance()->value("reportstylesheet"))); if (filename.isEmpty()) filename = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "html/kmymoney.css"); QString header = QString("\n") + QString("").arg(QUrl::fromLocalFile(filename).url()); header += ""; QColor tcolor = KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText).color(); QString css; css += "\n"; header += css; header += "\n"; QString footer = "\n"; MyMoneyMoney clearedBalance = startingBalance; MyMoneyMoney clearedDepositAmount, clearedPaymentAmount; int clearedDeposits = 0; int clearedPayments = 0; MyMoneyMoney outstandingDepositAmount, outstandingPaymentAmount; int outstandingDeposits = 0; int outstandingPayments = 0; QList >::const_iterator it; for (it = transactionList.begin(); it != transactionList.end(); ++it) { // if this split is a stock split, we can't just add the amount of shares if ((*it).second.reconcileFlag() == eMyMoney::Split::State::NotReconciled) { if ((*it).second.shares().isNegative()) { outstandingPayments++; outstandingPaymentAmount += (*it).second.shares(); } else { outstandingDeposits++; outstandingDepositAmount += (*it).second.shares(); } } else { if ((*it).second.shares().isNegative()) { clearedPayments++; clearedPaymentAmount += (*it).second.shares(); } else { clearedDeposits++; clearedDepositAmount += (*it).second.shares(); } clearedBalance += (*it).second.shares(); } } QString reportName = i18n("Reconciliation report of account %1", account.name()); QString report = QString("

%1

\n").arg(reportName); report += QString("
"); report += QString("%1").arg(QLocale().toString(date, QLocale::ShortFormat)); report += QString("
\n"); report += QString("
 
\n"); report += QString("
"); report += i18n("All values shown in %1", file->baseCurrency().name()); report += QString("
\n"); report += QString("
 
\n"); report += "\n"; report += ""; report += "\n"; // row 1 report += ""; // row 2 report += ""; // row 3 report += ""; // row 4 report += ""; // separator report += ""; // row 5 report += ""; // row 6 report += ""; // row 7 report += ""; // row 8 report += ""; // retrieve list of all transactions after the reconciliation date that are not reconciled or cleared QList > afterTransactionList; MyMoneyTransactionFilter filter(account.id()); filter.addState((int)eMyMoney::TransactionFilter::State::Cleared); filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled); filter.setDateFilter(date.addDays(1), QDate()); filter.setConsiderCategory(false); filter.setReportAllSplits(true); file->transactionList(afterTransactionList, filter); MyMoneyMoney afterDepositAmount, afterPaymentAmount; int afterDeposits = 0; int afterPayments = 0; for (it = afterTransactionList.constBegin(); it != afterTransactionList.constEnd(); ++it) { // if this split is a stock split, we can't just add the amount of shares if ((*it).second.reconcileFlag() == eMyMoney::Split::State::NotReconciled) { if ((*it).second.shares().isNegative()) { afterPayments++; afterPaymentAmount += (*it).second.shares(); } else { afterDeposits++; afterDepositAmount += (*it).second.shares(); } } } // row 9 report += ""; // row 10 report += ""; // row 11 report += ""; // end of the table report += "
" + i18n("Summary") + "
"; report += i18n("Starting balance on bank statement"); report += ""; report += MyMoneyUtils::formatMoney(startingBalance, currency); report += "
"; report += i18np("%1 cleared payment", "%1 cleared payments in total", clearedPayments); report += ""; report += MyMoneyUtils::formatMoney(clearedPaymentAmount, currency); report += "
"; report += i18np("%1 cleared deposit", "%1 cleared deposits in total", clearedDeposits); report += ""; report += MyMoneyUtils::formatMoney(clearedDepositAmount, currency); report += "
"; report += i18n("Ending balance on bank statement"); report += ""; report += MyMoneyUtils::formatMoney(endingBalance, currency); report += "
"; report += i18n("Cleared balance"); report += ""; report += MyMoneyUtils::formatMoney(clearedBalance, currency); report += "
"; report += i18np("%1 outstanding payment", "%1 outstanding payments in total", outstandingPayments); report += ""; report += MyMoneyUtils::formatMoney(outstandingPaymentAmount, currency); report += "
"; report += i18np("%1 outstanding deposit", "%1 outstanding deposits in total", outstandingDeposits); report += ""; report += MyMoneyUtils::formatMoney(outstandingDepositAmount, currency); report += "
"; report += i18n("Register balance as of %1", QLocale().toString(date, QLocale::ShortFormat)); report += ""; report += MyMoneyUtils::formatMoney(MyMoneyFile::instance()->balance(account.id(), date), currency); report += "
"; report += i18np("%1 payment after %2", "%1 payments after %2", afterPayments, QLocale().toString(date, QLocale::ShortFormat)); report += ""; report += MyMoneyUtils::formatMoney(afterPaymentAmount, currency); report += "
"; report += i18np("%1 deposit after %2", "%1 deposits after %2", afterDeposits, QLocale().toString(date, QLocale::ShortFormat)); report += ""; report += MyMoneyUtils::formatMoney(afterDepositAmount, currency); report += "
"; report += i18n("Register ending balance"); report += ""; report += MyMoneyUtils::formatMoney(MyMoneyFile::instance()->balance(account.id()), currency); report += "
\n"; QString detailsTableHeader; detailsTableHeader += "\n"; detailsTableHeader += ""; detailsTableHeader += ""; detailsTableHeader += ""; detailsTableHeader += ""; detailsTableHeader += ""; detailsTableHeader += ""; detailsTableHeader += "\n"; QString detailsReport = QString("

%1

\n").arg(i18n("Outstanding payments")); detailsReport += detailsTableHeader; auto index = 0; foreach (const auto transaction, transactionList) { if (transaction.second.reconcileFlag() == eMyMoney::Split::State::NotReconciled && transaction.second.shares().isNegative()) { QString category; foreach (const auto split, transaction.first.splits()) { if (split.accountId() != account.id()) { if (!category.isEmpty()) category += ", "; // this is a split transaction category = file->account(split.accountId()).name(); } } detailsReport += QString(""; } } detailsReport += ""; detailsReport += QString("").arg(i18np("One outstanding payment of", "Total of %1 outstanding payments amounting to", outstandingPayments)).arg(MyMoneyUtils::formatMoney(outstandingPaymentAmount, currency)); detailsReport += "
" + i18n("Date") + "" + i18n("Number") + "" + i18n("Payee") + "" + i18n("Memo") + "" + i18n("Category") + "" + i18n("Amount") + "
").arg((index++ % 2 == 1) ? "row-odd" : "row-even"); detailsReport += QString("%1").arg(QLocale().toString(transaction.first.entryDate(), QLocale::ShortFormat)); detailsReport += ""; detailsReport += QString("%1").arg(transaction.second.number()); detailsReport += ""; detailsReport += QString("%1").arg(file->payee(transaction.second.payeeId()).name()); detailsReport += ""; detailsReport += QString("%1").arg(transaction.first.memo()); detailsReport += ""; detailsReport += QString("%1").arg(category); detailsReport += ""; detailsReport += QString("%1").arg(MyMoneyUtils::formatMoney(transaction.second.shares(), file->currency(account.currencyId()))); detailsReport += "
%1%2
\n"; detailsReport += QString("

%1

\n").arg(i18n("Outstanding deposits")); detailsReport += detailsTableHeader; index = 0; for (it = transactionList.begin(); it != transactionList.end(); ++it) { if ((*it).second.reconcileFlag() == eMyMoney::Split::State::NotReconciled && !(*it).second.shares().isNegative()) { QString category; foreach (const auto split, (*it).first.splits()) { if (split.accountId() != account.id()) { if (!category.isEmpty()) category += ", "; // this is a split transaction category = file->account(split.accountId()).name(); } } detailsReport += QString("").arg((index++ % 2 == 1) ? "row-odd" : "row-even") + QString("%1").arg(QLocale().toString((*it).first.entryDate(), QLocale::ShortFormat)) + "" + QString("%1").arg((*it).second.number()) + "" + QString("%1").arg(file->payee((*it).second.payeeId()).name()) + "" + QString("%1").arg((*it).first.memo()) + "" + QString("%1").arg(category) + "" + QString("%1").arg(MyMoneyUtils::formatMoney((*it).second.shares(), file->currency(account.currencyId()))) + ""; } } detailsReport += "" + QString("%1%2").arg(i18np("One outstanding deposit of", "Total of %1 outstanding deposits amounting to", outstandingDeposits)).arg(MyMoneyUtils::formatMoney(outstandingDepositAmount, currency)) // end of the table + "\n"; QPointer dlg = new KReportDlg(0, header + report + footer, header + detailsReport + footer); dlg->exec(); delete dlg; } + +K_PLUGIN_FACTORY_WITH_JSON(ReconciliationReportFactory, "reconciliationreport.json", registerPlugin();) + +#include "reconciliationreport.moc" diff --git a/kmymoney/plugins/reconciliationreport/reconciliationreport.h b/kmymoney/plugins/reconciliationreport/reconciliationreport.h index f41883ce2..cb9e3427c 100644 --- a/kmymoney/plugins/reconciliationreport/reconciliationreport.h +++ b/kmymoney/plugins/reconciliationreport/reconciliationreport.h @@ -1,49 +1,49 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 * ***************************************************************************/ #ifndef RECONCILIATIONREPORT_H #define RECONCILIATIONREPORT_H #include "kmymoneyplugin.h" #include "mymoneymoney.h" class MyMoneyAccount; class MyMoneySplit; class MyMoneyTransaction; -class KMMReconciliationReportPlugin: public KMyMoneyPlugin::Plugin +class ReconciliationReport: public KMyMoneyPlugin::Plugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.reconcilationreport" FILE "kmm_reconciliationreport.json") public: - KMMReconciliationReportPlugin(); + explicit ReconciliationReport(QObject *parent, const QVariantList &args); + ~ReconciliationReport() override; public Q_SLOTS: void plug() override; void unplug() override; protected Q_SLOTS: // reconciliation of an account has finished void slotGenerateReconciliationReport(const MyMoneyAccount& account, const QDate& date, const MyMoneyMoney& startingBalance, const MyMoneyMoney& endingBalance, const QList >& transactionList); }; #endif // RECONCILIATIONREPORT_H diff --git a/kmymoney/plugins/reconciliationreport/kmm_reconciliationreport.json.in b/kmymoney/plugins/reconciliationreport/reconciliationreport.json.in similarity index 99% rename from kmymoney/plugins/reconciliationreport/kmm_reconciliationreport.json.in rename to kmymoney/plugins/reconciliationreport/reconciliationreport.json.in index de36e7735..2e2755c80 100644 --- a/kmymoney/plugins/reconciliationreport/kmm_reconciliationreport.json.in +++ b/kmymoney/plugins/reconciliationreport/reconciliationreport.json.in @@ -1,82 +1,82 @@ { "KPlugin": { "Authors": [ { "Email": "onet.cristian@gmail.com", "Name": "Cristian Oneț" } ], "Description": "Creates a report after each reconciliation containing data about the reconciliation process", "Description[bs]": "Kreira izvještaj nakon svakog usaglašavanja s podacima o procesu usaglašavanja", "Description[ca@valencia]": "Crea un informe després de cada conciliació amb les dades sobre el procés de conciliació", "Description[ca]": "Crea un informe després de cada conciliació amb les dades sobre el procés de conciliació", "Description[da]": "Opretter en rapport efter hver afstemning som indeholder data om afstemningsprocessen", "Description[de]": "Erstellt nach jedem Abgleich einen Bericht, der Daten über den Abgleich-Prozess enthält", "Description[el]": "Δημιουργεί μια αναφορά έπειτα από κάθε συμφωνία η οποία περιέχει δεδομένα σχετικά με την διεργασία της συμφωνίας", "Description[en_GB]": "Creates a report after each reconciliation containing data about the reconciliation process", "Description[es]": "Crea un informe tras cada conciliación que contiene datos sobre el proceso de conciliación.", "Description[et]": "Konsolideerimisandmeid sisaldava aruande loomine pärast iga konsolideerimist", "Description[eu]": "Txosten bat sortzen du berdinkatze bakoitzaren ondoren, berdinkatze-prozesuari buruzko datuakin", "Description[fi]": "Luo raportin jokaisen täsmäytyksen jälkeen sisältäen tietoja täsmäytysprosessista", "Description[fr]": "Crée un rapport après chaque consolidation contenant des données sur le processus de consolidation", "Description[gl]": "Crea despois de cada conciliación un informe que contén os datos relativos a esta", "Description[hu]": "Jelentést készít, miután minden egyeztetés tartalmaz adatot az egyeztetési folyamatról", "Description[it]": "Crea un report dopo ogni riconciliazione che contiene dati relativi al processo di riconciliazione", "Description[kk]": "Әрбір салыстырып тексеруден кейін ол туралы баяндау", "Description[nds]": "Stellt na elk Afgliek en Bericht över mit de Afgliekdaten op", "Description[nl]": "Maakt een rapport aan na elke transactiecontrole met gegevens over het controleproces", "Description[pl]": "Tworzy sprawozdanie, po każdym uzgodnieniu, zawierające dane o procesie uzgadniania", "Description[pt]": "Cria um relatório, a seguir a cada reconciliação, que contém os dados do processo de reconciliação", "Description[pt_BR]": "Cria um relatório que contém os dados de cada processo de reconciliação", "Description[ro]": "Creează un raport după fiecare reconciliere conținând date relevante despre procesul de reconciliere.", "Description[ru]": "Создание отчёта выполнения каждой сверки", "Description[sk]": "Vytvorí report po každom zrovnaní obsahujúcom dáta o procese zrovnania", "Description[sv]": "Skapar en rapport efter varje avstämning som innehåller information om avstämningsprocessen", "Description[tr]": "Uzlaşma süreci hakkında bilgileri içeren her uzlaşma sonrası bir rapor oluşturur", "Description[uk]": "Створює звіт після кожної звірки з даними щодо процесу звірки", "Description[x-test]": "xxCreates a report after each reconciliation containing data about the reconciliation processxx", "Description[zh_TW]": "在對帳後產生報告,包含對帳程序的資料", "EnabledByDefault": true, "Icon": "application-vnd.oasis.opendocument.spreadsheet", - "Id": "Reconciliation report", + "Id": "reconciliationreport", "License": "GPL", "Name": "Reconciliation report", "Name[bs]": "Izvještaj o usaglašavanju", "Name[ca@valencia]": "Informe de conciliació", "Name[ca]": "Informe de conciliació", "Name[cs]": "Hlášení rozvahy", "Name[da]": "Afstemningsrapport", "Name[de]": "Abgleichbericht", "Name[el]": "Αναφορά διευθέτησης", "Name[en_GB]": "Reconciliation report", "Name[es]": "Informe de conciliación", "Name[et]": "Konsolideerimisaruanne", "Name[eu]": "Berdinkatze-txostena", "Name[fi]": "Täsmäytysraportti", "Name[fr]": "Rapport de consolidation", "Name[gl]": "Informe de conciliación", "Name[hu]": "Egyeztetés jelentés", "Name[it]": "Report di riconciliazione", "Name[kk]": "Салыстырып тексеру", "Name[lt]": "Sutaikinimo vediklis", "Name[nds]": "Bericht", "Name[nl]": "Rapport over transactiecontrole", "Name[pl]": "Sprawozdanie uzgadniania", "Name[pt]": "Relatório de reconciliação", "Name[pt_BR]": "Relatório de reconciliação", "Name[ro]": "Raport de reconciliere", "Name[ru]": "Отчёт по сверке", "Name[sk]": "Zrovnávacia správa", "Name[sv]": "Avstämningsrapport", "Name[tr]": "Uzlaşma raporu", "Name[uk]": "Звіт щодо звірки", "Name[x-test]": "xxReconciliation reportxx", "Name[zh_CN]": "对帐报告", "Name[zh_TW]": "對帳報告", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/weboob/CMakeLists.txt b/kmymoney/plugins/weboob/CMakeLists.txt index 7a2e6163e..e9eb54a4c 100644 --- a/kmymoney/plugins/weboob/CMakeLists.txt +++ b/kmymoney/plugins/weboob/CMakeLists.txt @@ -1,38 +1,38 @@ find_package(Qt5 COMPONENTS REQUIRED Concurrent) # Use KDE compiler settings to enable C++11 include(KDECompilerSettings NO_POLICY_SCOPE) # patch the version with the version defined in the build system -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmm_weboob.json.in ${CMAKE_CURRENT_BINARY_DIR}/kmm_weboob.json @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/weboob.json.in ${CMAKE_CURRENT_BINARY_DIR}/weboob.json @ONLY) add_subdirectory( dialogs ) ########### next target ############### -set(kmm_weboob_PART_SRCS +set(weboob_PART_SRCS + weboobext.cpp weboob.cpp - plugin.cpp ) -add_library(kmm_weboob MODULE ${kmm_weboob_PART_SRCS}) +add_library(weboob MODULE ${weboob_PART_SRCS}) -target_link_libraries(kmm_weboob - kmm_weboob_dialogs +target_link_libraries(weboob + weboob_dialogs kmm_mymoney kmm_widgets kmm_plugin KF5::KrossCore ) ########### install files ############### install(FILES weboob.py - DESTINATION "${DATA_INSTALL_DIR}/kmm_weboob") + DESTINATION "${DATA_INSTALL_DIR}/weboob") -install(FILES kmm_weboob.rc - DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_weboob") +install(FILES weboob.rc + DESTINATION "${KXMLGUI_INSTALL_DIR}/weboob") -install(TARGETS kmm_weboob +install(TARGETS weboob DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") diff --git a/kmymoney/plugins/weboob/dialogs/CMakeLists.txt b/kmymoney/plugins/weboob/dialogs/CMakeLists.txt index c8ec9e854..93be710fd 100644 --- a/kmymoney/plugins/weboob/dialogs/CMakeLists.txt +++ b/kmymoney/plugins/weboob/dialogs/CMakeLists.txt @@ -1,17 +1,17 @@ ########### next target ############### -set(kmm_weboob_dialogs_la_SOURCES +set(weboob_dialogs_la_SOURCES mapaccount.cpp webaccount.cpp ) -set(kmm_weboob_dialogs_UI +set(weboob_dialogs_UI mapaccount.ui webaccount.ui ) -ki18n_wrap_ui(kmm_weboob_dialogs_la_SOURCES - ${kmm_weboob_dialogs_UI}) -add_library(kmm_weboob_dialogs STATIC ${kmm_weboob_dialogs_la_SOURCES}) -target_link_libraries(kmm_weboob_dialogs Qt5::Concurrent KF5::KrossCore widgets dialogs) +ki18n_wrap_ui(weboob_dialogs_la_SOURCES + ${weboob_dialogs_UI}) +add_library(weboob_dialogs STATIC ${weboob_dialogs_la_SOURCES}) +target_link_libraries(weboob_dialogs Qt5::Concurrent KF5::KrossCore widgets dialogs) diff --git a/kmymoney/plugins/weboob/dialogs/mapaccount.cpp b/kmymoney/plugins/weboob/dialogs/mapaccount.cpp index 2522d9793..3207a8cde 100644 --- a/kmymoney/plugins/weboob/dialogs/mapaccount.cpp +++ b/kmymoney/plugins/weboob/dialogs/mapaccount.cpp @@ -1,156 +1,156 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014-2015 Romain Bignon * Copyright (C) 2014-2015 Florent Fourcot * * 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 "mapaccount.h" #include #include #include #include #include #include "../weboob.h" struct WbMapAccountDialog::Private { - QFutureWatcher > watcher; - QFutureWatcher > watcher2; + QFutureWatcher > watcher; + QFutureWatcher > watcher2; std::unique_ptr progress; }; WbMapAccountDialog::WbMapAccountDialog(QWidget *parent): QWizard(parent), d(new Private), d2(new Private) { setupUi(this); checkNextButton(); connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(checkNextButton())); connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(newPage(int))); connect(backendsList, SIGNAL(itemSelectionChanged()), this, SLOT(checkNextButton())); connect(accountsList, SIGNAL(itemSelectionChanged()), this, SLOT(checkNextButton())); connect(&d->watcher, SIGNAL(finished()), this, SLOT(gotAccounts())); connect(&d2->watcher2, SIGNAL(finished()), this, SLOT(gotBackends())); // 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()); } /** * @internal Deconstructer stub needed to delete unique_ptrs with type Private */ WbMapAccountDialog::~WbMapAccountDialog() { } void WbMapAccountDialog::checkNextButton(void) { bool enableButton = false; switch (currentId()) { case BACKENDS_PAGE: enableButton = backendsList->currentItem() != 0 && backendsList->currentItem()->isSelected(); break; case ACCOUNTS_PAGE: enableButton = accountsList->currentItem() != 0 && accountsList->currentItem()->isSelected(); break; } button(QWizard::NextButton)->setEnabled(enableButton); } void WbMapAccountDialog::newPage(int id) { //! @Todo C++14: this should be make_unique d2->progress = std::unique_ptr(new QProgressDialog(this)); d2->progress->setModal(true); d2->progress->setCancelButton(nullptr); d2->progress->setMinimum(0); d2->progress->setMaximum(0); d2->progress->setMinimumDuration(0); switch (id) { case BACKENDS_PAGE: { backendsList->clear(); d2->progress->setWindowTitle(i18n("Loading Weboob backend...")); d2->progress->setLabelText(i18n("Getting list of backends.")); QCoreApplication::processEvents(); - QFuture > future = QtConcurrent::run(weboob, &Weboob::getBackends); + QFuture > future = QtConcurrent::run(weboob, &WeboobExt::getBackends); d2->watcher2.setFuture(future); break; } case ACCOUNTS_PAGE: { accountsList->clear(); d2->progress->setWindowTitle(i18n("Connecting to bank...")); d2->progress->setLabelText(i18n("Getting list of accounts from your bank.")); QCoreApplication::processEvents(); - QFuture > future = QtConcurrent::run(weboob, &Weboob::getAccounts, backendsList->currentItem()->text(0)); + QFuture > future = QtConcurrent::run(weboob, &WeboobExt::getAccounts, backendsList->currentItem()->text(0)); d->watcher.setFuture(future); button(QWizard::BackButton)->setEnabled(false); accountsList->setEnabled(false); break; } default: // I do not know if this can actually happen. But to be safe: d2->progress.reset(); } } void WbMapAccountDialog::gotBackends() { - QList backends = d2->watcher2.result(); + QList backends = d2->watcher2.result(); - for (QListIterator it(backends); it.hasNext();) { - Weboob::Backend backend = it.next(); + for (QListIterator it(backends); it.hasNext();) { + WeboobExt::Backend backend = it.next(); QStringList headers; headers << backend.name << backend.module; backendsList->addTopLevelItem(new QTreeWidgetItem(headers)); } d2->progress.reset(); } void WbMapAccountDialog::gotAccounts() { - QList accounts = d->watcher.result(); + QList accounts = d->watcher.result(); - for (QListIterator it(accounts); it.hasNext();) { - Weboob::Account account = it.next(); + for (QListIterator it(accounts); it.hasNext();) { + WeboobExt::Account account = it.next(); QStringList headers; headers << account.id << account.name << account.balance.formatMoney(QString(), 2); accountsList->addTopLevelItem(new QTreeWidgetItem(headers)); } d->progress.reset(); button(QWizard::BackButton)->setEnabled(true); accountsList->setEnabled(true); } diff --git a/kmymoney/plugins/weboob/dialogs/mapaccount.h b/kmymoney/plugins/weboob/dialogs/mapaccount.h index e964edd87..51d64be75 100644 --- a/kmymoney/plugins/weboob/dialogs/mapaccount.h +++ b/kmymoney/plugins/weboob/dialogs/mapaccount.h @@ -1,64 +1,64 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014-2015 Romain Bignon * Copyright (C) 2014-2015 Florent Fourcot * * 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 . */ #ifndef WEBOOB_MAPACCOUNT_HPP #define WEBOOB_MAPACCOUNT_HPP #include #include #include "../weboob.h" #include "ui_mapaccount.h" class WbMapAccountDialog : public QWizard, public Ui::WbMapAccountDialog { Q_OBJECT public: - Weboob *weboob; + WeboobExt *weboob; explicit WbMapAccountDialog(QWidget *parent = 0); virtual ~WbMapAccountDialog(); protected Q_SLOTS: void checkNextButton(void); void newPage(int id); void gotAccounts(); void gotBackends(); protected: bool finishAccountPage(void); bool finishLoginPage(void); bool finishFiPage(void); private: enum { BACKENDS_PAGE = 0, ACCOUNTS_PAGE }; struct Private; /// \internal d-pointer instance. const std::unique_ptr d; const std::unique_ptr d2; }; #endif /* WEBOOB_MAPACCOUNT_HPP */ diff --git a/kmymoney/plugins/weboob/plugin.cpp b/kmymoney/plugins/weboob/plugin.cpp deleted file mode 100644 index 3cd6a3da7..000000000 --- a/kmymoney/plugins/weboob/plugin.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - * This file is part of KMyMoney, A Personal Finance Manager by KDE - * Copyright (C) 2014-2015 Romain Bignon - * Copyright (C) 2014-2015 Florent Fourcot - * Copyright (C) 2016 Christian David - * - * 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 "plugin.h" -#include "weboob.h" - -#include -#include -#include - -#include -#include - -#include "dialogs/mapaccount.h" -#include "dialogs/webaccount.h" - -#include "mymoneystatement.h" -#include "statementinterface.h" - -struct WeboobPlugin::Private -{ - QFutureWatcher watcher; - std::unique_ptr progress; - WebAccountSettings* accountSettings; -}; - -WeboobPlugin::WeboobPlugin() : - KMyMoneyPlugin::Plugin(), - KMyMoneyPlugin::OnlinePlugin(), - d(new Private()) -{ - setComponentName("kmm_weboob", i18n("Weboob")); - setXMLFile("kmm_weboob.rc"); - - connect(&d->watcher, &QFutureWatcher::finished, this, &WeboobPlugin::gotAccount); -} - -WeboobPlugin::~WeboobPlugin() -{ -} - -void WeboobPlugin::protocols(QStringList& protocolList) const -{ - protocolList << "weboob"; -} - -QWidget* WeboobPlugin::accountConfigTab(const MyMoneyAccount& account, QString& tabName) -{ - const MyMoneyKeyValueContainer& kvp = account.onlineBankingSettings(); - tabName = i18n("Weboob configuration"); - - d->accountSettings = new WebAccountSettings(account, 0); - d->accountSettings->loadUi(kvp); - - return d->accountSettings; -} - -MyMoneyKeyValueContainer WeboobPlugin::onlineBankingSettings(const MyMoneyKeyValueContainer& current) -{ - MyMoneyKeyValueContainer kvp(current); - kvp["provider"] = objectName(); - if (d->accountSettings) { - d->accountSettings->loadKvp(kvp); - } - return kvp; -} - -bool WeboobPlugin::mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& onlineBankingSettings) -{ - Q_UNUSED(acc); - - WbMapAccountDialog w; - w.weboob = &weboob; - if (w.exec() == QDialog::Accepted) { - onlineBankingSettings.setValue("wb-backend", w.backendsList->currentItem()->text(0)); - onlineBankingSettings.setValue("wb-id", w.accountsList->currentItem()->text(0)); - onlineBankingSettings.setValue("wb-max", "0"); - return true; - } - return false; -} - -bool WeboobPlugin::updateAccount(const MyMoneyAccount& kacc, bool moreAccounts) -{ - Q_UNUSED(moreAccounts); - - QString bname = kacc.onlineBankingSettings().value("wb-backend"); - QString id = kacc.onlineBankingSettings().value("wb-id"); - QString max = kacc.onlineBankingSettings().value("wb-max"); - - //! @todo C++14 use make_unique() - d->progress = std::unique_ptr(new QProgressDialog()); - d->progress->setWindowTitle(i18n("Connecting to bank...")); - d->progress->setLabelText(i18n("Retrieving transactions...")); - d->progress->setModal(true); - d->progress->setCancelButton(nullptr); - d->progress->setMinimum(0); - d->progress->setMaximum(0); - d->progress->setMinimumDuration(0); - - QFuture future = QtConcurrent::run(&weboob, &Weboob::getAccount, bname, id, max); - d->watcher.setFuture(future); - - d->progress->exec(); - d->progress.reset(); - - return true; -} - -void WeboobPlugin::gotAccount() -{ - Weboob::Account acc = d->watcher.result(); - - MyMoneyAccount kacc = statementInterface()->account("wb-id", acc.id); - MyMoneyStatement ks; - - ks.m_accountId = kacc.id(); - ks.m_strAccountName = acc.name; - ks.m_closingBalance = acc.balance; - if (acc.transactions.length() > 0) - ks.m_dateEnd = acc.transactions.front().date; - -#if 0 - switch (acc.type) { - case Weboob::Account::TYPE_CHECKING: - ks.m_eType = MyMoneyStatement::etCheckings; - break; - case Weboob::Account::TYPE_SAVINGS: - ks.m_eType = MyMoneyStatement::etSavings; - break; - case Weboob::Account::TYPE_MARKET: - ks.m_eType = MyMoneyStatement::etInvestment; - break; - case Weboob::Account::TYPE_DEPOSIT: - case Weboob::Account::TYPE_LOAN: - case Weboob::Account::TYPE_JOINT: - case Weboob::Account::TYPE_UNKNOWN: - break; - } -#endif - - for (QListIterator it(acc.transactions); it.hasNext();) { - Weboob::Transaction tr = it.next(); - MyMoneyStatement::Transaction kt; - - kt.m_strBankID = QLatin1String("ID ") + tr.id; - kt.m_datePosted = tr.rdate; - kt.m_amount = tr.amount; - kt.m_strMemo = tr.raw; - kt.m_strPayee = tr.label; - - ks.m_listTransactions += kt; - } - - statementInterface()->import(ks); - - d->progress->hide(); -} - diff --git a/kmymoney/plugins/weboob/plugin.h b/kmymoney/plugins/weboob/plugin.h deleted file mode 100644 index 567c40aea..000000000 --- a/kmymoney/plugins/weboob/plugin.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of KMyMoney, A Personal Finance Manager by KDE - * Copyright (C) 2014-2015 Romain Bignon - * Copyright (C) 2014-2015 Florent Fourcot - * - * 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 . - */ - -#ifndef WEBOOB_PLUGIN_HPP -#define WEBOOB_PLUGIN_HPP - -#include - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "kmymoneyplugin.h" -#include "mymoneyaccount.h" -#include "mymoneykeyvaluecontainer.h" -#include "weboob.h" - -class WeboobPlugin : public KMyMoneyPlugin::Plugin, public KMyMoneyPlugin::OnlinePlugin -{ - Q_OBJECT - Q_INTERFACES(KMyMoneyPlugin::OnlinePlugin) - Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.weboob" FILE "kmm_weboob.json") - -public: - Weboob weboob; - WeboobPlugin(); - virtual ~WeboobPlugin(); - - void protocols(QStringList& protocolList) const; - - QWidget* accountConfigTab(const MyMoneyAccount& account, QString& tabName); - - MyMoneyKeyValueContainer onlineBankingSettings(const MyMoneyKeyValueContainer& current); - - bool mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& onlineBankingSettings); - - bool updateAccount(const MyMoneyAccount& acc, bool moreAccounts = false); - -protected Q_SLOTS: - - void gotAccount(); - -private: - - struct Private; - /// \internal d-pointer instance. - const std::unique_ptr d; -}; - -#endif /* WEBOOB_PLUGIN_HPP */ diff --git a/kmymoney/plugins/weboob/weboob.cpp b/kmymoney/plugins/weboob/weboob.cpp index 63c8414a3..b98b6a212 100644 --- a/kmymoney/plugins/weboob/weboob.cpp +++ b/kmymoney/plugins/weboob/weboob.cpp @@ -1,136 +1,190 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014-2015 Romain Bignon * Copyright (C) 2014-2015 Florent Fourcot + * Copyright (C) 2016 Christian David * * 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 "weboob.h" +#include "weboobext.h" -#include -#include -#include +#include +#include +#include -Weboob::Weboob(QObject* parent) - : QObject(parent) +#include +#include + +#include "dialogs/mapaccount.h" +#include "dialogs/webaccount.h" + +#include "mymoneystatement.h" +#include "statementinterface.h" + +struct Weboob::Private +{ + QFutureWatcher watcher; + std::unique_ptr progress; + WebAccountSettings* accountSettings; +}; + +Weboob::Weboob(QObject *parent, const QVariantList &args) : + KMyMoneyPlugin::Plugin(parent, "weboob"), + d(new Private()) { - mutex = new QMutex(); - path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kmm_weboob/weboob.py"); - action = new Kross::Action(0, path); - action->setFile(path); + Q_UNUSED(args) + setComponentName("weboob", i18n("Weboob")); + setXMLFile("weboob.rc"); + + qDebug("Plugins: weboob loaded"); } Weboob::~Weboob() { - delete mutex; - action->finalize(); - delete action; + qDebug("Plugins: weboob unloaded"); } -QVariant Weboob::execute(QString method, QVariantList args) +void Weboob::plug() { - QVariant result; - - mutex->lock(); - result = action->callFunction(method, args); - mutex->unlock(); - - return result; + connect(&d->watcher, &QFutureWatcher::finished, this, &Weboob::gotAccount); } -QList Weboob::getBackends() +void Weboob::unplug() { - QList backendsList; - - QVariantList args; + disconnect(&d->watcher, &QFutureWatcher::finished, this, &Weboob::gotAccount); +} - QVariant result = this->execute("get_backends", args); +void Weboob::protocols(QStringList& protocolList) const +{ + protocolList << "weboob"; +} - QMap list = result.toMap(); - QMapIterator it(list); +QWidget* Weboob::accountConfigTab(const MyMoneyAccount& account, QString& tabName) +{ + const MyMoneyKeyValueContainer& kvp = account.onlineBankingSettings(); + tabName = i18n("Weboob configuration"); - while (it.hasNext()) { - it.next(); - QMap params = it.value().toMap(); + d->accountSettings = new WebAccountSettings(account, 0); + d->accountSettings->loadUi(kvp); - Weboob::Backend backend; - backend.name = it.key(); - backend.module = params["module"].toString(); + return d->accountSettings; +} - backendsList.append(backend); +MyMoneyKeyValueContainer Weboob::onlineBankingSettings(const MyMoneyKeyValueContainer& current) +{ + MyMoneyKeyValueContainer kvp(current); + kvp["provider"] = objectName(); + if (d->accountSettings) { + d->accountSettings->loadKvp(kvp); } - - return backendsList; + return kvp; } +bool Weboob::mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& onlineBankingSettings) +{ + Q_UNUSED(acc); + + WbMapAccountDialog w; + w.weboob = &weboob; + if (w.exec() == QDialog::Accepted) { + onlineBankingSettings.setValue("wb-backend", w.backendsList->currentItem()->text(0)); + onlineBankingSettings.setValue("wb-id", w.accountsList->currentItem()->text(0)); + onlineBankingSettings.setValue("wb-max", "0"); + return true; + } + return false; +} -QList Weboob::getAccounts(QString backend) +bool Weboob::updateAccount(const MyMoneyAccount& kacc, bool moreAccounts) { - QList accountsList; + Q_UNUSED(moreAccounts); - QVariantList args; - args << backend; - QVariant result = this->execute("get_accounts", args); + QString bname = kacc.onlineBankingSettings().value("wb-backend"); + QString id = kacc.onlineBankingSettings().value("wb-id"); + QString max = kacc.onlineBankingSettings().value("wb-max"); - QMap list = result.toMap(); - for (QMapIterator it(list); it.hasNext();) { - it.next(); - QMap params = it.value().toMap(); + //! @todo C++14 use make_unique() + d->progress = std::unique_ptr(new QProgressDialog()); + d->progress->setWindowTitle(i18n("Connecting to bank...")); + d->progress->setLabelText(i18n("Retrieving transactions...")); + d->progress->setModal(true); + d->progress->setCancelButton(nullptr); + d->progress->setMinimum(0); + d->progress->setMaximum(0); + d->progress->setMinimumDuration(0); - Weboob::Account account; - account.id = it.key(); - account.name = params["name"].toString(); - account.balance = MyMoneyMoney(params["balance"].toInt(), 100); - account.type = (Weboob::Account::type_t)params["type"].toInt(); + QFuture future = QtConcurrent::run(&weboob, &WeboobExt::getAccount, bname, id, max); + d->watcher.setFuture(future); - accountsList.append(account); - } + d->progress->exec(); + d->progress.reset(); - return accountsList; + return true; } -Weboob::Account Weboob::getAccount(QString backend, QString accid, QString max) +void Weboob::gotAccount() { - Weboob::Account acc; - - QVariantList args; - args << backend; - args << accid; - args << max; - QMap result = this->execute("get_transactions", args).toMap(); - - acc.id = result["id"].toString(); - acc.name = result["name"].toString(); - acc.balance = MyMoneyMoney(result["balance"].toInt(), 100); - acc.type = (Weboob::Account::type_t)result["type"].toInt(); - - QList list = result["transactions"].toList(); - for (QListIterator it(list); it.hasNext();) { - QMap params = it.next().toMap(); - Weboob::Transaction tr; - - tr.id = params["id"].toString(); - tr.date = QDate::fromString(params["date"].toString(), "yyyy-MM-dd"); - tr.rdate = QDate::fromString(params["rdate"].toString(), "yyyy-MM-dd"); - tr.type = (Weboob::Transaction::type_t)params["type"].toInt(); - tr.raw = params["raw"].toString(); - tr.category = params["category"].toString(); - tr.label = params["label"].toString(); - tr.amount = MyMoneyMoney(params["amount"].toInt(), 100); - - acc.transactions.append(tr); + WeboobExt::Account acc = d->watcher.result(); + + MyMoneyAccount kacc = statementInterface()->account("wb-id", acc.id); + MyMoneyStatement ks; + + ks.m_accountId = kacc.id(); + ks.m_strAccountName = acc.name; + ks.m_closingBalance = acc.balance; + if (acc.transactions.length() > 0) + ks.m_dateEnd = acc.transactions.front().date; + +#if 0 + switch (acc.type) { + case Weboob::Account::TYPE_CHECKING: + ks.m_eType = MyMoneyStatement::etCheckings; + break; + case Weboob::Account::TYPE_SAVINGS: + ks.m_eType = MyMoneyStatement::etSavings; + break; + case Weboob::Account::TYPE_MARKET: + ks.m_eType = MyMoneyStatement::etInvestment; + break; + case Weboob::Account::TYPE_DEPOSIT: + case Weboob::Account::TYPE_LOAN: + case Weboob::Account::TYPE_JOINT: + case Weboob::Account::TYPE_UNKNOWN: + break; } - return acc; +#endif + + for (QListIterator it(acc.transactions); it.hasNext();) { + WeboobExt::Transaction tr = it.next(); + MyMoneyStatement::Transaction kt; + + kt.m_strBankID = QLatin1String("ID ") + tr.id; + kt.m_datePosted = tr.rdate; + kt.m_amount = tr.amount; + kt.m_strMemo = tr.raw; + kt.m_strPayee = tr.label; + + ks.m_listTransactions += kt; + } + + statementInterface()->import(ks); + + d->progress->hide(); } + +K_PLUGIN_FACTORY_WITH_JSON(WeboobFactory, "weboob.json", registerPlugin();) + +#include "weboob.moc" diff --git a/kmymoney/plugins/weboob/weboob.h b/kmymoney/plugins/weboob/weboob.h index d75ec8fad..65e14be0b 100644 --- a/kmymoney/plugins/weboob/weboob.h +++ b/kmymoney/plugins/weboob/weboob.h @@ -1,100 +1,68 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014-2015 Romain Bignon * Copyright (C) 2014-2015 Florent Fourcot * * 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 . */ #ifndef WEBOOB_HPP #define WEBOOB_HPP -#include -#include -#include -#include +#include -#include "mymoneymoney.h" +#ifdef HAVE_CONFIG_H +#include +#endif -class Weboob : public QObject +#include "kmymoneyplugin.h" +#include "mymoneyaccount.h" +#include "mymoneykeyvaluecontainer.h" +#include "weboobext.h" + +class Weboob : public KMyMoneyPlugin::Plugin, public KMyMoneyPlugin::OnlinePlugin { Q_OBJECT - - Kross::Action* action; - QMutex *mutex; - QString path; + Q_INTERFACES(KMyMoneyPlugin::OnlinePlugin) public: + WeboobExt weboob; + explicit Weboob(QObject *parent, const QVariantList &args); + ~Weboob() override; + + void plug() override; + void unplug() override; + + void protocols(QStringList& protocolList) const override; + + QWidget* accountConfigTab(const MyMoneyAccount& account, QString& tabName) override; + + MyMoneyKeyValueContainer onlineBankingSettings(const MyMoneyKeyValueContainer& current) override; + + bool mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& onlineBankingSettings) override; + + bool updateAccount(const MyMoneyAccount& acc, bool moreAccounts = false) override; + +protected Q_SLOTS: + + void gotAccount(); - struct Backend { - QString name; - QString module; - }; - - struct Transaction { - QString id; - QDate date; - QDate rdate; - enum type_t { - TYPE_UNKNOWN = 0, - TYPE_TRANSFER, - TYPE_ORDER, - TYPE_CHECK, - TYPE_DEPOSIT, - TYPE_PAYBACK, - TYPE_WITHDRAWAL, - TYPE_CARD, - TYPE_LOAN_PAYMENT, - TYPE_BANK - } type; - QString raw; - QString category; - QString label; - MyMoneyMoney amount; - }; - - struct Account { - QString id; - QString name; - enum type_t { - TYPE_UNKNOWN = 0, - TYPE_CHECKING, - TYPE_SAVINGS, - TYPE_DEPOSIT, - TYPE_LOAN, - TYPE_MARKET, - TYPE_JOINT - } type; - MyMoneyMoney balance; - - QList transactions; - }; - - explicit Weboob(QObject* parent = 0); - - ~Weboob(); - - QStringList getProtocols(); - - QList getBackends(); - - QList getAccounts(QString backend); - - Account getAccount(QString backend, QString account, QString max); - - QVariant execute(QString method, QVariantList args); +private: + struct Private; + /// \internal d-pointer instance. + const std::unique_ptr d; }; -#endif /* WEBOOB_HPP */ +#endif /* WEBOOB_PLUGIN_HPP */ diff --git a/kmymoney/plugins/weboob/kmm_weboob.json.in b/kmymoney/plugins/weboob/weboob.json.in similarity index 98% rename from kmymoney/plugins/weboob/kmm_weboob.json.in rename to kmymoney/plugins/weboob/weboob.json.in index 2a2806e4e..43734148e 100644 --- a/kmymoney/plugins/weboob/kmm_weboob.json.in +++ b/kmymoney/plugins/weboob/weboob.json.in @@ -1,47 +1,47 @@ { "KPlugin": { "Authors": [ { "Email": "romain@weboob.org", "Name": "Romain Bignon" } ], "Description": "Plugin for weboob", "Description[ca@valencia]": "Connector pel Weboob", "Description[ca]": "Connector pel Weboob", "Description[en_GB]": "Plugin for weboob", "Description[es]": "Complemento para weboob", "Description[fi]": "Weboob-liitännäinen", "Description[gl]": "Complemento para Weboob", "Description[nl]": "Plug-in voor weboob", "Description[pl]": "Wtyczka do weboob", "Description[pt]": "'Plugin' para o Weboob", "Description[pt_BR]": "Plugin para o Weboob", "Description[sv]": "Insticksprogram för weboob", "Description[uk]": "Додаток для роботи з weboob", "Description[x-test]": "xxPlugin for weboobxx", "EnabledByDefault": true, "Icon": "network-connect", - "Id": "Weboob", + "Id": "weboob", "License": "GPL", "Name": "Weboob", "Name[ca@valencia]": "Weboob", "Name[ca]": "Weboob", "Name[en_GB]": "Weboob", "Name[es]": "Weboob", "Name[fi]": "Weboob", "Name[gl]": "Weboob", "Name[nl]": "Weboob", "Name[pl]": "Weboob", "Name[pt]": "Weboob", "Name[pt_BR]": "Weboob", "Name[sv]": "Weboob", "Name[uk]": "Weboob", "Name[x-test]": "xxWeboobxx", "ServiceTypes": [ "KMyMoney/Plugin" ], "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "Website": "https://kmymoney.org/plugins.html" } } diff --git a/kmymoney/plugins/weboob/kmm_weboob.rc b/kmymoney/plugins/weboob/weboob.rc similarity index 100% rename from kmymoney/plugins/weboob/kmm_weboob.rc rename to kmymoney/plugins/weboob/weboob.rc diff --git a/kmymoney/plugins/weboob/weboob.cpp b/kmymoney/plugins/weboob/weboobext.cpp similarity index 80% copy from kmymoney/plugins/weboob/weboob.cpp copy to kmymoney/plugins/weboob/weboobext.cpp index 63c8414a3..fbde8d875 100644 --- a/kmymoney/plugins/weboob/weboob.cpp +++ b/kmymoney/plugins/weboob/weboobext.cpp @@ -1,136 +1,136 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014-2015 Romain Bignon * Copyright (C) 2014-2015 Florent Fourcot * * 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 "weboob.h" +#include "weboobext.h" #include #include #include -Weboob::Weboob(QObject* parent) +WeboobExt::WeboobExt(QObject* parent) : QObject(parent) { mutex = new QMutex(); path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kmm_weboob/weboob.py"); action = new Kross::Action(0, path); action->setFile(path); } -Weboob::~Weboob() +WeboobExt::~WeboobExt() { delete mutex; action->finalize(); delete action; } -QVariant Weboob::execute(QString method, QVariantList args) +QVariant WeboobExt::execute(QString method, QVariantList args) { QVariant result; mutex->lock(); result = action->callFunction(method, args); mutex->unlock(); return result; } -QList Weboob::getBackends() +QList WeboobExt::getBackends() { - QList backendsList; + QList backendsList; QVariantList args; QVariant result = this->execute("get_backends", args); QMap list = result.toMap(); QMapIterator it(list); while (it.hasNext()) { it.next(); QMap params = it.value().toMap(); - Weboob::Backend backend; + WeboobExt::Backend backend; backend.name = it.key(); backend.module = params["module"].toString(); backendsList.append(backend); } return backendsList; } -QList Weboob::getAccounts(QString backend) +QList WeboobExt::getAccounts(QString backend) { - QList accountsList; + QList accountsList; QVariantList args; args << backend; QVariant result = this->execute("get_accounts", args); QMap list = result.toMap(); for (QMapIterator it(list); it.hasNext();) { it.next(); QMap params = it.value().toMap(); - Weboob::Account account; + WeboobExt::Account account; account.id = it.key(); account.name = params["name"].toString(); account.balance = MyMoneyMoney(params["balance"].toInt(), 100); - account.type = (Weboob::Account::type_t)params["type"].toInt(); + account.type = (WeboobExt::Account::type_t)params["type"].toInt(); accountsList.append(account); } return accountsList; } -Weboob::Account Weboob::getAccount(QString backend, QString accid, QString max) +WeboobExt::Account WeboobExt::getAccount(QString backend, QString accid, QString max) { - Weboob::Account acc; + WeboobExt::Account acc; QVariantList args; args << backend; args << accid; args << max; QMap result = this->execute("get_transactions", args).toMap(); acc.id = result["id"].toString(); acc.name = result["name"].toString(); acc.balance = MyMoneyMoney(result["balance"].toInt(), 100); - acc.type = (Weboob::Account::type_t)result["type"].toInt(); + acc.type = (WeboobExt::Account::type_t)result["type"].toInt(); QList list = result["transactions"].toList(); for (QListIterator it(list); it.hasNext();) { QMap params = it.next().toMap(); - Weboob::Transaction tr; + WeboobExt::Transaction tr; tr.id = params["id"].toString(); tr.date = QDate::fromString(params["date"].toString(), "yyyy-MM-dd"); tr.rdate = QDate::fromString(params["rdate"].toString(), "yyyy-MM-dd"); - tr.type = (Weboob::Transaction::type_t)params["type"].toInt(); + tr.type = (WeboobExt::Transaction::type_t)params["type"].toInt(); tr.raw = params["raw"].toString(); tr.category = params["category"].toString(); tr.label = params["label"].toString(); tr.amount = MyMoneyMoney(params["amount"].toInt(), 100); acc.transactions.append(tr); } return acc; } diff --git a/kmymoney/plugins/weboob/weboob.h b/kmymoney/plugins/weboob/weboobext.h similarity index 92% copy from kmymoney/plugins/weboob/weboob.h copy to kmymoney/plugins/weboob/weboobext.h index d75ec8fad..e678c51cd 100644 --- a/kmymoney/plugins/weboob/weboob.h +++ b/kmymoney/plugins/weboob/weboobext.h @@ -1,100 +1,100 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014-2015 Romain Bignon * Copyright (C) 2014-2015 Florent Fourcot * * 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 . */ -#ifndef WEBOOB_HPP -#define WEBOOB_HPP +#ifndef WEBOOBEXT_HPP +#define WEBOOBEXT_HPP #include #include #include #include #include "mymoneymoney.h" -class Weboob : public QObject +class WeboobExt : public QObject { Q_OBJECT Kross::Action* action; QMutex *mutex; QString path; public: struct Backend { QString name; QString module; }; struct Transaction { QString id; QDate date; QDate rdate; enum type_t { TYPE_UNKNOWN = 0, TYPE_TRANSFER, TYPE_ORDER, TYPE_CHECK, TYPE_DEPOSIT, TYPE_PAYBACK, TYPE_WITHDRAWAL, TYPE_CARD, TYPE_LOAN_PAYMENT, TYPE_BANK } type; QString raw; QString category; QString label; MyMoneyMoney amount; }; struct Account { QString id; QString name; enum type_t { TYPE_UNKNOWN = 0, TYPE_CHECKING, TYPE_SAVINGS, TYPE_DEPOSIT, TYPE_LOAN, TYPE_MARKET, TYPE_JOINT } type; MyMoneyMoney balance; QList transactions; }; - explicit Weboob(QObject* parent = 0); + explicit WeboobExt(QObject* parent = 0); - ~Weboob(); + ~WeboobExt(); QStringList getProtocols(); QList getBackends(); QList getAccounts(QString backend); Account getAccount(QString backend, QString account, QString max); QVariant execute(QString method, QVariantList args); }; -#endif /* WEBOOB_HPP */ +#endif /* WEBOOBEXT_HPP */ diff --git a/kmymoney/views/konlinejoboutbox.cpp b/kmymoney/views/konlinejoboutbox.cpp index d5218ce4c..7d38f7092 100644 --- a/kmymoney/views/konlinejoboutbox.cpp +++ b/kmymoney/views/konlinejoboutbox.cpp @@ -1,477 +1,477 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2013-2014 Christian Dávid * * 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 "konlinejoboutbox.h" #include "ui_konlinejoboutbox.h" #include "kmymoneyviewbase_p.h" #include "konlinetransferform.h" #include "kmymoneyplugin.h" #include #include #include #include #include #include #include #include #include #include #include "models.h" #include "onlinejobmodel.h" #include "onlinejobadministration.h" #include "onlinejobtyped.h" #include "onlinejobmessagesview.h" #include "onlinejobmessagesmodel.h" #include "onlinepluginextended.h" #include "mymoneyaccount.h" #include "mymoneyfile.h" #include "menuenums.h" #include "mymoneyenums.h" #include class KOnlineJobOutboxPrivate : public KMyMoneyViewBasePrivate { Q_DECLARE_PUBLIC(KOnlineJobOutbox) public: explicit KOnlineJobOutboxPrivate(KOnlineJobOutbox *qq) : KMyMoneyViewBasePrivate(), q_ptr(qq), ui(new Ui::KOnlineJobOutbox), m_needLoad(true) { } ~KOnlineJobOutboxPrivate() { if (!m_needLoad) { // Save column state KConfigGroup configGroup = KSharedConfig::openConfig()->group("KOnlineJobOutbox"); configGroup.writeEntry("HeaderState", ui->m_onlineJobView->header()->saveState()); } } void init() { Q_Q(KOnlineJobOutbox); m_needLoad = false; ui->setupUi(q); // Restore column state KConfigGroup configGroup = KSharedConfig::openConfig()->group("KOnlineJobOutbox"); QByteArray columns; columns = configGroup.readEntry("HeaderState", columns); ui->m_onlineJobView->header()->restoreState(columns); ui->m_onlineJobView->setModel(Models::instance()->onlineJobsModel()); q->connect(ui->m_buttonSend, &QAbstractButton::clicked, q, &KOnlineJobOutbox::slotSendJobs); q->connect(ui->m_buttonRemove, &QAbstractButton::clicked, q, &KOnlineJobOutbox::slotRemoveJob); q->connect(ui->m_buttonEdit, &QAbstractButton::clicked, q, static_cast(&KOnlineJobOutbox::slotEditJob)); q->connect(ui->m_onlineJobView, &QAbstractItemView::doubleClicked, q, static_cast(&KOnlineJobOutbox::slotEditJob)); q->connect(ui->m_onlineJobView->selectionModel(), &QItemSelectionModel::selectionChanged, q, &KOnlineJobOutbox::updateButtonState); // Set new credit transfer button q->connect(pActions[eMenu::Action::AccountCreditTransfer], &QAction::changed, q, &KOnlineJobOutbox::updateNewCreditTransferButton); q->connect(ui->m_buttonNewCreditTransfer, &QAbstractButton::clicked, q, &KOnlineJobOutbox::slotNewCreditTransfer); q->updateNewCreditTransferButton(); } void editJob(const QString jobId) { try { const onlineJob constJob = MyMoneyFile::instance()->getOnlineJob(jobId); editJob(constJob); } catch (MyMoneyException&) { // Prevent a crash in very rare cases } } void editJob(onlineJob job) { try { editJob(onlineJobTyped(job)); } catch (MyMoneyException&) { } } void editJob(const onlineJobTyped job) { Q_Q(KOnlineJobOutbox); auto transferForm = new kOnlineTransferForm(q); transferForm->setOnlineJob(job); q->connect(transferForm, &QDialog::rejected, transferForm, &QObject::deleteLater); q->connect(transferForm, &kOnlineTransferForm::acceptedForSave, q, &KOnlineJobOutbox::slotOnlineJobSave); q->connect(transferForm, &kOnlineTransferForm::acceptedForSend, q, static_cast(&KOnlineJobOutbox::slotOnlineJobSend)); q->connect(transferForm, &QDialog::accepted, transferForm, &QObject::deleteLater); transferForm->show(); } KOnlineJobOutbox *q_ptr; std::unique_ptr ui; /** * This member holds the load state of page */ bool m_needLoad; QMap* m_onlinePlugins; MyMoneyAccount m_currentAccount; }; KOnlineJobOutbox::KOnlineJobOutbox(QWidget *parent) : KMyMoneyViewBase(*new KOnlineJobOutboxPrivate(this), parent) { connect(pActions[eMenu::Action::LogOnlineJob], &QAction::triggered, this, static_cast(&KOnlineJobOutbox::slotOnlineJobLog)); connect(pActions[eMenu::Action::AccountCreditTransfer], &QAction::triggered, this, &KOnlineJobOutbox::slotNewCreditTransfer); } KOnlineJobOutbox::~KOnlineJobOutbox() { } void KOnlineJobOutbox::setDefaultFocus() { Q_D(KOnlineJobOutbox); QTimer::singleShot(0, d->ui->m_onlineJobView, SLOT(setFocus())); } void KOnlineJobOutbox::updateButtonState() const { Q_D(const KOnlineJobOutbox); const QModelIndexList indexes = d->ui->m_onlineJobView->selectionModel()->selectedRows(); const int selectedItems = indexes.count(); // Send button //! @todo Enable button if it is useful //ui->m_buttonSend->setEnabled(selectedItems > 0); // Edit button/action bool editable = true; QString tooltip; if (selectedItems == 1) { const onlineJob job = d->ui->m_onlineJobView->model()->data(indexes.first(), onlineJobModel::OnlineJobRole).value(); if (!job.isEditable()) { editable = false; if (job.sendDate().isValid()) tooltip = i18n("This job cannot be edited anymore because is was sent already."); else if (job.isLocked()) tooltip = i18n("Job is being processed at the moment."); else Q_ASSERT(false); } else if (!onlineJobAdministration::instance()->canEditOnlineJob(job)) { editable = false; tooltip = i18n("The plugin to edit this job is not available."); } } else { editable = false; tooltip = i18n("You must select a single job for editing."); } QAction *const onlinejob_edit = pActions[eMenu::Action::EditOnlineJob]; Q_CHECK_PTR(onlinejob_edit); onlinejob_edit->setEnabled(editable); onlinejob_edit->setToolTip(tooltip); d->ui->m_buttonEdit->setEnabled(editable); d->ui->m_buttonEdit->setToolTip(tooltip); // Delete button/action QAction *const onlinejob_delete = pActions[eMenu::Action::DeleteOnlineJob]; Q_CHECK_PTR(onlinejob_delete); onlinejob_delete->setEnabled(selectedItems > 0); d->ui->m_buttonRemove->setEnabled(onlinejob_delete->isEnabled()); } void KOnlineJobOutbox::updateNewCreditTransferButton() { Q_D(KOnlineJobOutbox); auto action = pActions[eMenu::Action::AccountCreditTransfer]; Q_CHECK_PTR(action); d->ui->m_buttonNewCreditTransfer->setEnabled(action->isEnabled()); } void KOnlineJobOutbox::slotRemoveJob() { Q_D(KOnlineJobOutbox); QAbstractItemModel* model = d->ui->m_onlineJobView->model(); QModelIndexList indexes = d->ui->m_onlineJobView->selectionModel()->selectedRows(); while (!indexes.isEmpty()) { model->removeRow(indexes.at(0).row()); indexes = d->ui->m_onlineJobView->selectionModel()->selectedRows(); } } QStringList KOnlineJobOutbox::selectedOnlineJobs() const { Q_D(const KOnlineJobOutbox); QModelIndexList indexes = d->ui->m_onlineJobView->selectionModel()->selectedRows(); if (indexes.isEmpty()) return QStringList(); QStringList list; list.reserve(indexes.count()); const QAbstractItemModel *const model = d->ui->m_onlineJobView->model(); Q_FOREACH(const QModelIndex& index, indexes) { list.append(model->data(index, onlineJobModel::OnlineJobId).toString()); } return list; } void KOnlineJobOutbox::setOnlinePlugins(QMap& plugins) { Q_D(KOnlineJobOutbox); d->m_onlinePlugins = &plugins; } void KOnlineJobOutbox::slotSendJobs() { Q_D(KOnlineJobOutbox); if (d->ui->m_onlineJobView->selectionModel()->hasSelection()) slotSendSelectedJobs(); else slotSendAllSendableJobs(); } void KOnlineJobOutbox::slotSendAllSendableJobs() { QList validJobs; foreach (const onlineJob& job, MyMoneyFile::instance()->onlineJobList()) { if (job.isValid() && job.isEditable()) validJobs.append(job); } qDebug() << "I shall send " << validJobs.count() << "/" << MyMoneyFile::instance()->onlineJobList().count() << " onlineJobs"; if (!validJobs.isEmpty()) slotOnlineJobSend(validJobs); // emit sendJobs(validJobs); } void KOnlineJobOutbox::slotSendSelectedJobs() { Q_D(KOnlineJobOutbox); QModelIndexList indexes = d->ui->m_onlineJobView->selectionModel()->selectedRows(); if (indexes.isEmpty()) return; // Valid jobs to send QList validJobs; validJobs.reserve(indexes.count()); // Get valid jobs const QAbstractItemModel *const model = d->ui->m_onlineJobView->model(); foreach (const QModelIndex& index, indexes) { onlineJob job = model->data(index, onlineJobModel::OnlineJobRole).value(); if (job.isValid() && job.isEditable()) validJobs.append(job); } // Abort if not all jobs can be sent if (validJobs.count() != indexes.count()) { KMessageBox::information(this, i18nc("The user selected credit transfers to send. But they cannot be sent.", "Cannot send selection"), i18n("Not all selected credit transfers can be sent because some of them are invalid or were already sent.")); return; } slotOnlineJobSend(validJobs); // emit sendJobs(validJobs); } void KOnlineJobOutbox::slotEditJob() { Q_D(KOnlineJobOutbox); QModelIndexList indexes = d->ui->m_onlineJobView->selectionModel()->selectedIndexes(); if (!indexes.isEmpty()) { QString jobId = d->ui->m_onlineJobView->model()->data(indexes.first(), onlineJobModel::OnlineJobId).toString(); Q_ASSERT(!jobId.isEmpty()); d->editJob(jobId); // emit editJob(jobId); } } void KOnlineJobOutbox::slotEditJob(const QModelIndex &index) { Q_D(KOnlineJobOutbox); auto jobId = d->ui->m_onlineJobView->model()->data(index, onlineJobModel::OnlineJobId).toString(); d->editJob(jobId); // emit editJob(jobId); } void KOnlineJobOutbox::contextMenuEvent(QContextMenuEvent*) { Q_D(KOnlineJobOutbox); QModelIndexList indexes = d->ui->m_onlineJobView->selectionModel()->selectedIndexes(); if (!indexes.isEmpty()) { // onlineJob job = d->ui->m_onlineJobView->model()->data(indexes.first(), onlineJobModel::OnlineJobRole).value(); pMenus[eMenu::Menu::OnlineJob]->exec(); } } /** * Do not know why this is needed, but all other views in KMyMoney have it. */ void KOnlineJobOutbox::showEvent(QShowEvent* event) { Q_D(KOnlineJobOutbox); if (d->m_needLoad) d->init(); emit aboutToShow(View::OnlineJobOutbox); // don't forget base class implementation QWidget::showEvent(event); } void KOnlineJobOutbox::updateActions(const MyMoneyObject& obj) { Q_D(KOnlineJobOutbox); 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); d->m_currentAccount = acc; } void KOnlineJobOutbox::slotOnlineJobSave(onlineJob job) { MyMoneyFileTransaction fileTransaction; if (job.id().isEmpty()) MyMoneyFile::instance()->addOnlineJob(job); else MyMoneyFile::instance()->modifyOnlineJob(job); fileTransaction.commit(); } /** @todo when onlineJob queue is used, continue here */ void KOnlineJobOutbox::slotOnlineJobSend(onlineJob job) { MyMoneyFileTransaction fileTransaction; if (job.id().isEmpty()) MyMoneyFile::instance()->addOnlineJob(job); else MyMoneyFile::instance()->modifyOnlineJob(job); fileTransaction.commit(); QList jobList; jobList.append(job); slotOnlineJobSend(jobList); } void KOnlineJobOutbox::slotOnlineJobSend(QList jobs) { Q_D(KOnlineJobOutbox); MyMoneyFile *const kmmFile = MyMoneyFile::instance(); QMultiMap jobsByPlugin; // Sort jobs by online plugin & lock them foreach (onlineJob job, jobs) { Q_ASSERT(!job.id().isEmpty()); // find the provider const MyMoneyAccount originAcc = job.responsibleMyMoneyAccount(); job.setLock(); - job.addJobMessage(onlineJobMessage(eMyMoney::OnlineJob::MessageType::Debug, "KMyMoneyApp::slotOnlineJobSend", "Added to queue for plugin '" + originAcc.onlineBankingSettings().value("provider") + '\'')); + job.addJobMessage(onlineJobMessage(eMyMoney::OnlineJob::MessageType::Debug, "KMyMoneyApp::slotOnlineJobSend", "Added to queue for plugin '" + originAcc.onlineBankingSettings().value("provider").toLower() + '\'')); MyMoneyFileTransaction fileTransaction; kmmFile->modifyOnlineJob(job); fileTransaction.commit(); - jobsByPlugin.insert(originAcc.onlineBankingSettings().value("provider"), job); + jobsByPlugin.insert(originAcc.onlineBankingSettings().value("provider").toLower(), job); } // Send onlineJobs to plugins QList usedPlugins = jobsByPlugin.keys(); std::sort(usedPlugins.begin(), usedPlugins.end()); const QList::iterator newEnd = std::unique(usedPlugins.begin(), usedPlugins.end()); usedPlugins.erase(newEnd, usedPlugins.end()); foreach (const QString& pluginKey, usedPlugins) { QMap::const_iterator it_p = d->m_onlinePlugins->constFind(pluginKey); if (it_p != d->m_onlinePlugins->constEnd()) { // plugin found, call it KMyMoneyPlugin::OnlinePluginExtended *pluginExt = dynamic_cast< KMyMoneyPlugin::OnlinePluginExtended* >(*it_p); if (pluginExt == 0) { qWarning("Job given for plugin which is not an extended plugin"); continue; } //! @fixme remove debug message qDebug() << "Sending " << jobsByPlugin.count(pluginKey) << " job(s) to online plugin " << pluginKey; QList jobsToExecute = jobsByPlugin.values(pluginKey); QList executedJobs = jobsToExecute; pluginExt->sendOnlineJob(executedJobs); // Save possible changes of the online job and remove lock MyMoneyFileTransaction fileTransaction; foreach (onlineJob job, executedJobs) { fileTransaction.restart(); job.setLock(false); kmmFile->modifyOnlineJob(job); fileTransaction.commit(); } if (Q_UNLIKELY(executedJobs.size() != jobsToExecute.size())) { // OnlinePlugin did not return all jobs qWarning() << "Error saving send online tasks. After restart you should see at minimum all successfully executed jobs marked send. Imperfect plugin: " << pluginExt->objectName(); } } else { qWarning() << "Error, got onlineJob for an account without online plugin."; /** @FIXME can this actually happen? */ } } } void KOnlineJobOutbox::slotOnlineJobLog() { QStringList jobIds = this->selectedOnlineJobs(); slotOnlineJobLog(jobIds); } void KOnlineJobOutbox::slotOnlineJobLog(const QStringList& onlineJobIds) { onlineJobMessagesView *const dialog = new onlineJobMessagesView(); onlineJobMessagesModel *const model = new onlineJobMessagesModel(dialog); model->setOnlineJob(MyMoneyFile::instance()->getOnlineJob(onlineJobIds.first())); dialog->setModel(model); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); // Note: Objects are not deleted here, Qt's parent-child system has to do that. } void KOnlineJobOutbox::slotNewCreditTransfer() { Q_D(KOnlineJobOutbox); auto *transferForm = new kOnlineTransferForm(this); if (!d->m_currentAccount.id().isEmpty()) { transferForm->setCurrentAccount(d->m_currentAccount.id()); } connect(transferForm, &QDialog::rejected, transferForm, &QObject::deleteLater); connect(transferForm, &kOnlineTransferForm::acceptedForSave, this, &KOnlineJobOutbox::slotOnlineJobSave); connect(transferForm, &kOnlineTransferForm::acceptedForSend, this, static_cast(&KOnlineJobOutbox::slotOnlineJobSend)); connect(transferForm, &QDialog::accepted, transferForm, &QObject::deleteLater); transferForm->show(); }