diff --git a/plugins/generic/skg_debug/skgdebugpluginwidget.cpp b/plugins/generic/skg_debug/skgdebugpluginwidget.cpp index 2dcfadd6c..9c6b1bd43 100644 --- a/plugins/generic/skg_debug/skgdebugpluginwidget.cpp +++ b/plugins/generic/skg_debug/skgdebugpluginwidget.cpp @@ -1,224 +1,224 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file is a plugin for debug. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgdebugpluginwidget.h" #include #include #include #include "skgdocument.h" #include "skgmainpanel.h" #include "skgservices.h" #include "skgtraces.h" #include "skgtransactionmng.h" SKGDebugPluginWidget::SKGDebugPluginWidget(QWidget* iParent, SKGDocument* iDocument) : SKGTabPage(iParent, iDocument) { SKGTRACEINFUNC(10) if (iDocument == nullptr) { return; } ui.setupUi(this); // Set icons ui.kSQLPushButton->setIcon(SKGServices::fromTheme(QStringLiteral("system-run"))); ui.kSQLTransactionPushButton->setIcon(SKGServices::fromTheme(QStringLiteral("system-run"))); ui.kRefreshViewsAndIndexes->setIcon(SKGServices::fromTheme(QStringLiteral("view-refresh"))); // Fill combo box ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("system-run")), i18nc("Execute an SQL query", "Execute")); ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("help-hint")), i18nc("Explain an SQL query", "Explain")); ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("games-hint")), i18nc("Explain the SQL query plan", "Explain query plan")); ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("media-playback-start")), i18nc("Execute script", "Execute script [%1]", "javascript")); ui.kInput->setVisible(false); // Set level trace ui.kTraceLevel->setValue(SKGTraces::SKGLevelTrace); // Set profiling mode ui.kEnableProfilingChk->setCheckState(SKGTraces::SKGPerfo ? Qt::Checked : Qt::Unchecked); // Init debug page QStringList tables; ui.kSQLInput->addItem(QStringLiteral("SELECT * FROM sqlite_master;")); iDocument->getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), QStringLiteral("type in ('table', 'view')"), tables); int nb = tables.count(); for (int i = 0; i < nb; ++i) { ui.kSQLInput->addItem("SELECT * FROM " % tables.at(i) % ';'); } ui.kSQLInput->addItem(QStringLiteral("ANALYZE;")); ui.kSQLInput->addItem(QStringLiteral("PRAGMA integrity_check;")); for (int i = 0; i < nb; ++i) { ui.kSQLInput->addItem("PRAGMA table_info(" % tables.at(i) % ");"); ui.kSQLInput->addItem("PRAGMA index_list(" % tables.at(i) % ");"); } iDocument->getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), QStringLiteral("type='index'"), tables); nb = tables.count(); for (int i = 0; i < nb; ++i) { ui.kSQLInput->addItem("PRAGMA index_info(" % tables.at(i) % ");"); } connect(ui.kTraceLevel, &QSlider::valueChanged, this, &SKGDebugPluginWidget::onTraceLevelModified); connect(ui.kEnableProfilingChk, &QCheckBox::stateChanged, this, &SKGDebugPluginWidget::onProfilingModeChanged); connect(ui.kExplainCmb, static_cast(&SKGComboBox::currentIndexChanged), this, &SKGDebugPluginWidget::onModeChanged); connect(ui.kSQLPushButton, &QPushButton::clicked, this, &SKGDebugPluginWidget::onExecuteSqlOrder); connect(ui.kSQLTransactionPushButton, &QPushButton::clicked, this, &SKGDebugPluginWidget::onExecuteSqlOrderInTransaction); connect(ui.kRefreshViewsAndIndexes, &QPushButton::clicked, this, &SKGDebugPluginWidget::onRefreshViewsAndIndexes); } SKGDebugPluginWidget::~SKGDebugPluginWidget() { SKGTRACEINFUNC(10) } QString SKGDebugPluginWidget::getState() { SKGTRACEINFUNC(10) QDomDocument doc(QStringLiteral("SKGML")); QDomElement root = doc.createElement(QStringLiteral("parameters")); doc.appendChild(root); root.setAttribute(QStringLiteral("explain"), ui.kExplainCmb->currentIndex()); root.setAttribute(QStringLiteral("enableProfiling"), ui.kEnableProfilingChk->checkState() == Qt::Checked ? QStringLiteral("Y") : QStringLiteral("N")); root.setAttribute(QStringLiteral("levelTraces"), ui.kTraceLevel->value()); root.setAttribute(QStringLiteral("sqlOrder"), ui.kSQLInput->currentText()); return doc.toString(); } void SKGDebugPluginWidget::setState(const QString& iState) { SKGTRACEINFUNC(10) QDomDocument doc(QStringLiteral("SKGML")); doc.setContent(iState); QDomElement root = doc.documentElement(); QString explain = root.attribute(QStringLiteral("explain")); QString enableProfiling = root.attribute(QStringLiteral("enableProfiling")); QString levelTraces = root.attribute(QStringLiteral("levelTraces")); QString sqlOrder = root.attribute(QStringLiteral("sqlOrder")); QString sqlResult = root.attribute(QStringLiteral("sqlResult")); if (!explain.isEmpty()) { ui.kExplainCmb->setCurrentIndex(SKGServices::stringToInt(explain == QStringLiteral("Y") ? QStringLiteral("1") : explain)); } if (!enableProfiling.isEmpty()) { ui.kEnableProfilingChk->setCheckState(enableProfiling == QStringLiteral("Y") ? Qt::Checked : Qt::Unchecked); } if (!levelTraces.isEmpty()) { ui.kTraceLevel->setValue(SKGServices::stringToInt(levelTraces)); } ui.kSQLInput->setText(sqlOrder); ui.kSQLResult->setPlainText(sqlResult); } void SKGDebugPluginWidget::onExecuteSqlOrderInTransaction() { onExecuteSqlOrder(true); } void SKGDebugPluginWidget::onExecuteSqlOrder(bool iInTransaction) { SKGTRACEINFUNC(10) SKGError err; int exp = ui.kExplainCmb->currentIndex(); if (exp > 2) { // Script execution ui.kSQLResult->clear(); QJSEngine myEngine; // skgresult.setText(skgdocument.getUniqueIdentifier()) // skgerror=skgdocument.sendMessage(QStringLiteral("Hello")) // skgerror=skgdocument.sendMessage(QStringLiteral("Hello")) // skgmainpanel.closeAllOtherPages(skgmainpanel.currentPage()) auto t = myEngine.globalObject(); t.setProperty(QStringLiteral("skgresult"), myEngine.newQObject(ui.kSQLResult)); t.setProperty(QStringLiteral("skgdocument"), myEngine.newQObject(getDocument())); - //t.setProperty(QStringLiteral("skgerror"), myEngine.newQObject(&err)); + // t.setProperty(QStringLiteral("skgerror"), myEngine.newQObject(&err)); t.setProperty(QStringLiteral("skgmainpanel"), myEngine.newQObject(SKGMainPanel::getMainPanel())); // Finally execute the scripting code. myEngine.evaluate(ui.kInput->toPlainText()); } else { // SQL execution QString text = ui.kSQLInput->currentText(); if (exp == 1) { text = "EXPLAIN " % text; } else if (exp == 2) { text = "EXPLAIN QUERY PLAN " % text; } QString oResult; double time = SKGServices::getMicroTime(); if (iInTransaction) { SKGBEGINTRANSACTION(*getDocument(), i18nc("Display an SQL command from the debug plugin", "SQL command from debug plugin"), err) IFOKDO(err, getDocument()->dumpSelectSqliteOrder(text, oResult)) } else { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); err = getDocument()->dumpSelectSqliteOrder(text, oResult); QApplication::restoreOverrideCursor(); } time = SKGServices::getMicroTime() - time; oResult += i18nc("Display the execution time needed by an SQL query", "\nExecution time: %1 ms", SKGServices::doubleToString(time)); IFOK(err) { ui.kSQLResult->setPlainText(oResult); } else { ui.kSQLResult->setPlainText(err.getFullMessageWithHistorical()); } } } void SKGDebugPluginWidget::onTraceLevelModified() { SKGTRACEINFUNC(10) SKGTraces::SKGLevelTrace = ui.kTraceLevel->value(); } void SKGDebugPluginWidget::onModeChanged() { SKGTRACEINFUNC(10) int exp = ui.kExplainCmb->currentIndex(); ui.kInput->setVisible(exp > 2); ui.kSQLInput->setVisible(exp < 3); } void SKGDebugPluginWidget::onProfilingModeChanged() { SKGTRACEINFUNC(10) SKGTraces::SKGPerfo = (ui.kEnableProfilingChk->checkState() == Qt::Checked); } void SKGDebugPluginWidget::onRefreshViewsAndIndexes() { SKGTRACEINFUNC(10) QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); SKGError err; err = getDocument()->refreshViewsIndexesAndTriggers(); IFKO(err) { ui.kSQLResult->setPlainText(err.getFullMessageWithHistorical()); } QApplication::restoreOverrideCursor(); } diff --git a/plugins/generic/skg_properties/skgpropertiesplugin.cpp b/plugins/generic/skg_properties/skgpropertiesplugin.cpp index ee1c46921..adbc5cd78 100644 --- a/plugins/generic/skg_properties/skgpropertiesplugin.cpp +++ b/plugins/generic/skg_properties/skgpropertiesplugin.cpp @@ -1,355 +1,359 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * A plugin to manage properties on objects * * @author Stephane MANKOWSKI */ #include "skgpropertiesplugin.h" #include #include #include #include #include #include #include #include "skgmainpanel.h" #include "skgpropertiesplugindockwidget.h" #include "skgtraces.h" #include "skgtransactionmng.h" /** * This plugin factory. */ K_PLUGIN_FACTORY(SKGPropertiesPluginFactory, registerPlugin();) SKGPropertiesPlugin::SKGPropertiesPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) : SKGInterfacePlugin(iParent), m_currentDocument(nullptr), m_dockWidget(nullptr), m_dockContent(nullptr), m_addPropertyMenu(nullptr) { Q_UNUSED(iWidget) SKGTRACEINFUNC(10) // Get list of bills m_billsProcess.setStandardOutputFile(QDir::tempPath() % "/skg_bills.csv"); m_billsProcess.start(QStringLiteral("boobill bills -q -f csv -v")); connect(&m_billsProcess, QOverload::of(&QProcess::finished), this, &SKGPropertiesPlugin::onBillsRetreived); connect(&m_billsProcess, static_cast(&QProcess::errorOccurred), this, &SKGPropertiesPlugin::onBillsRetreived); } SKGPropertiesPlugin::~SKGPropertiesPlugin() { SKGTRACEINFUNC(10) m_currentDocument = nullptr; m_dockWidget = nullptr; m_dockContent = nullptr; m_addPropertyMenu = nullptr; if (m_billsProcess.state() == QProcess::Running) { m_billsProcess.kill(); } + if (m_billsProcess.state() == QProcess::Running) { + m_billsProcess.kill(); + m_billsProcess.waitForFinished(-1); + } } void SKGPropertiesPlugin::onBillsRetreived() { QFile file(QDir::tempPath() % "/skg_bills.csv"); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream stream(&file); stream.readLine(); // To avoid header id;date;format;label;idparent;price;currency;deadline;startdate;finishdate while (!stream.atEnd()) { // Read line QString line = stream.readLine().trimmed(); m_bills.push_back(line); } // close file file.close(); } file.remove(); } bool SKGPropertiesPlugin::setupActions(SKGDocument* iDocument) { SKGTRACEINFUNC(10) m_currentDocument = iDocument; setComponentName(QStringLiteral("skg_properties"), title()); setXMLFile(QStringLiteral("skg_properties.rc")); m_dockContent = new SKGPropertiesPluginDockWidget(SKGMainPanel::getMainPanel(), m_currentDocument); if (m_dockContent != nullptr) { connect(m_dockContent, &SKGPropertiesPluginDockWidget::selectionChanged, SKGMainPanel::getMainPanel(), &SKGMainPanel::refresh); m_dockWidget = new QDockWidget(SKGMainPanel::getMainPanel()); if (m_dockWidget != nullptr) { m_dockWidget->setObjectName(QStringLiteral("skg_properties_docwidget")); m_dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); m_dockWidget->setWindowTitle(title()); m_dockWidget->setWidget(m_dockContent); // add action to control hide / display of Bookmarks QAction* toggle = m_dockWidget->toggleViewAction(); QAction* panelAction = actionCollection()->addAction(QStringLiteral("view_properties")); registerGlobalAction(QStringLiteral("view_properties"), panelAction); panelAction->setCheckable(true); panelAction->setChecked(toggle->isChecked()); panelAction->setText(toggle->text()); actionCollection()->setDefaultShortcut(panelAction, Qt::SHIFT + Qt::Key_F12); connect(panelAction, &QAction::triggered, toggle, &QAction::trigger); connect(toggle, &QAction::toggled, panelAction, &QAction::setChecked); } } // Menu auto actAddProperty = new KToolBarPopupAction(SKGServices::fromTheme(icon()), i18nc("Allows user to add a user defined property on an object", "Add property"), this); m_addPropertyMenu = actAddProperty->menu(); connect(m_addPropertyMenu, &QMenu::aboutToShow, this, &SKGPropertiesPlugin::onShowAddPropertyMenu); actAddProperty->setStickyMenu(false); actAddProperty->setDelayed(false); registerGlobalAction(QStringLiteral("add_property"), actAddProperty, QStringList() << QStringLiteral("query:type='table' AND name NOT LIKE 'doctransaction%'"), 1, -1, 450); return true; } void SKGPropertiesPlugin::onAddProperty() { SKGTRACEINFUNC(10) SKGError err; // Scope for the transaction auto* act = qobject_cast(sender()); if ((act != nullptr) && (m_currentDocument != nullptr)) { // Get parameters QStringList list = act->data().toStringList(); const QString& name = list.at(0); const QString& value = list.at(1); // Create properties IFOK(err) { SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); int nb = selection.count(); SKGBEGINPROGRESSTRANSACTION(*m_currentDocument, i18nc("Create a user defined property", "Property creation"), err, nb) for (int i = 0; !err && i < nb; ++i) { err = selection.at(i).setProperty(name, value); IFOKDO(err, m_currentDocument->stepForward(i + 1)) } } } // status bar IFOK(err) { err = SKGError(0, i18nc("The user defined property was successfully created", "Property created")); } SKGMainPanel::displayErrorMessage(err); } void SKGPropertiesPlugin::onDownloadAndAddBills() { SKGTRACEINFUNC(10) SKGError err; // Scope for the transaction auto* act = qobject_cast(sender()); if ((act != nullptr) && (m_currentDocument != nullptr)) { // Get parameters QStringList list = act->data().toStringList(); const QString& id = list.at(0); QString fileName = QDir::tempPath() % '/' % list.at(3) % '.' % list.at(2); // Create properties IFOK(err) { SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); int nb = selection.count(); SKGBEGINPROGRESSTRANSACTION(*m_currentDocument, i18nc("Create a user defined property", "Property creation"), err, 2 * nb) for (int i = 0; !err && i < nb; ++i) { // Download the files QFile::remove(fileName); QString cmd = "boobill download " % id % " \"" % fileName % '"'; QProcess p; p.start(cmd); if (!p.waitForFinished(60000) || p.exitCode() != 0) { err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message", "The following command line failed with code %2:\n'%1'", cmd, p.exitCode())); } else { IFOKDO(err, m_currentDocument->stepForward(2 * i)) IFOKDO(err, selection.at(i).setProperty(i18nc("Noun", "Bill"), id, fileName)) QStringList importedBills = SKGServices::splitCSVLine(m_currentDocument->getParameter(QStringLiteral("SKG_IMPORTED_BILLS"))); importedBills.push_back(id); IFOKDO(err, m_currentDocument->setParameter(QStringLiteral("SKG_IMPORTED_BILLS"), SKGServices::stringsToCsv(importedBills))) IFOKDO(err, m_currentDocument->stepForward(2 * i + 1)) QFile::remove(fileName); } } } } // status bar IFOK(err) { err = SKGError(0, i18nc("The user defined property was successfully created", "Property created")); } SKGMainPanel::displayErrorMessage(err); } void SKGPropertiesPlugin::onShowAddPropertyMenu() { if ((m_addPropertyMenu != nullptr) && (m_currentDocument != nullptr)) { m_addPropertyMenu->clear(); // Get selection SKGObjectBase::SKGListSKGObjectBase sels = SKGMainPanel::getMainPanel()->getSelectedObjects(); if (!sels.isEmpty()) { // Get the table of the selection QString table = sels.at(0).getRealTable(); // Get list of more used properties for this table SKGStringListList listTmp; m_currentDocument->executeSelectSqliteOrder( "SELECT t_name, t_value FROM (SELECT t_name, t_value, COUNT(1) AS nb FROM parameters WHERE (t_uuid_parent like '%-" % table % "' OR t_uuid_parent like '%-sub" % table % "') AND t_name NOT LIKE 'SKG_%' AND b_blob IS NULL GROUP BY t_name, t_value) ORDER BY nb DESC LIMIT 7", listTmp); // Create actions int nb = listTmp.count(); QIcon iconp = SKGServices::fromTheme(icon()); if (nb > 1) { for (int i = 1; i < nb; ++i) { // Should the string below be translated ??? It contains no word... QAction* act = m_addPropertyMenu->addAction(iconp, i18nc("Add a property (attribute=value)", "Add %1=%2", listTmp.at(i).at(0), listTmp.at(i).at(1))); if (act != nullptr) { act->setData(listTmp.at(i)); connect(act, &QAction::triggered, this, &SKGPropertiesPlugin::onAddProperty); } } } else { QAction* act = m_addPropertyMenu->addAction(iconp, i18nc("Help", "No property found. You must create a property from the dock first.")); act->setEnabled(false); } // Check if the sub process is still running if (m_billsProcess.state() == QProcess::Running) { // Create separator { QAction* act = m_addPropertyMenu->addAction(QLatin1String("")); act->setSeparator(true); } // Add download on going QAction* act = m_addPropertyMenu->addAction(i18nc("Message", "Download list of available bills on going...")); if (act != nullptr) { act->setEnabled(false); } } else { // Check if some bills can be downloaded int nb2 = m_bills.count(); if (nb2 != 0) { // Create separator { QAction* act = m_addPropertyMenu->addAction(QLatin1String("")); act->setSeparator(true); } // Create action QStringList importedBills = SKGServices::splitCSVLine(m_currentDocument->getParameter(QStringLiteral("SKG_IMPORTED_BILLS"))); QMenu* menuMore = nullptr; QIcon icond = SKGServices::fromTheme(icon(), QStringList() << QStringLiteral("download")); QSet backendDone; for (int j = 1; j < nb2; ++j) { // id;date;format;label;idparent;price;currency;deadline;startdate;finishdate QStringList fields = SKGServices::splitCSVLine(m_bills.at(j)); if (fields.count() > 3 && !importedBills.contains(fields.at(0))) { QStringList ids = SKGServices::splitCSVLine(fields.at(0), '@'); if (ids.count() == 2) { const QString& backend = ids.at(1); // Selection of the menu where the item must be added QMenu* menu; if (!backendDone.contains(backend)) { // This item must be added in root menu menu = m_addPropertyMenu; backendDone.insert(backend); } else { // This item must be added in "More..." menu if (menuMore == nullptr) { menuMore = new QMenu(i18nc("Noun", "More..."), m_addPropertyMenu); } menu = menuMore; } // Should the string below be translated ??? It contains no word... QAction* act = menu->addAction(icond, i18nc("Add a property (attribute=value)", "Download and add %1 (%2)", fields[3] % '.' % fields[2], fields[0])); if (act != nullptr) { act->setToolTip(fields[0]); act->setData(fields); connect(act, &QAction::triggered, this, &SKGPropertiesPlugin::onDownloadAndAddBills); } } } } // Add "More..." menu if (menuMore != nullptr) { m_addPropertyMenu->addMenu(menuMore); } } } } } } void SKGPropertiesPlugin::refresh() { SKGTRACEINFUNC(10) if (m_dockContent != nullptr) { m_dockContent->refresh(); } } QDockWidget* SKGPropertiesPlugin::getDockWidget() { return m_dockWidget; } QString SKGPropertiesPlugin::title() const { return i18nc("Noun, an item's properties", "Properties"); } QString SKGPropertiesPlugin::icon() const { return QStringLiteral("tag"); } int SKGPropertiesPlugin::getOrder() const { return 6; } QStringList SKGPropertiesPlugin::tips() const { QStringList output; output.push_back(i18nc("Description of a tip", "

... you can manage properties on all objects.

")); output.push_back(i18nc("Description of a tip", "

... you can add files or Internet links as property.

")); output.push_back(i18nc("Description of a tip", "

... you can automatically download and add bills as properties by using %1.

", "weboob")); return output; } #include diff --git a/plugins/skrooge/skrooge_unit/skgunitpluginwidget_pref.ui b/plugins/skrooge/skrooge_unit/skgunitpluginwidget_pref.ui index 34e9d544b..191350478 100644 --- a/plugins/skrooge/skrooge_unit/skgunitpluginwidget_pref.ui +++ b/plugins/skrooge/skrooge_unit/skgunitpluginwidget_pref.ui @@ -1,236 +1,236 @@ skgunitplugin_pref 0 0 596 542 2 Download on open false Once a day Once a week Once a month Qt::Horizontal 40 20 Download mode 2 Last &value found only &Monthly values since last download Wee&kly values since last download Dail&y values since last download Monthly values for all dates Weekly values for all dates Daily values for all dates 2 Maximum num&ber of imported values: kcfg_nb_loaded_values 50 Qt::Horizontal 40 20 API Keys - <html><head/><body><p>Some download sources (e.g. coinmarketcap, cryptocompare) may require an API key. So you need to request one from the source site and enter it here:</p></body></html> + <html><head/><body><p>The following download sources need an API key. So you need to request one from the source site web and enter it here:</p></body></html> true 2 For security reasons, these keys are not saved in setting file but in your document. Save in document true Qt::Vertical 20 44 KComboBox QComboBox
kcombobox.h
kcfg_download_on_open kcfg_download_frequency kcfg_last kcfg_last_monthly kcfg_last_weekly kcfg_last_daily kcfg_all_monthly kcfg_all_weekly kcfg_all_daily kcfg_nb_loaded_values
diff --git a/skgbankmodeler/skgaccountobject.cpp b/skgbankmodeler/skgaccountobject.cpp index 0e45d9ba5..e4b86199c 100644 --- a/skgbankmodeler/skgaccountobject.cpp +++ b/skgbankmodeler/skgaccountobject.cpp @@ -1,1076 +1,1082 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file implements classes SKGAccountObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgaccountobject.h" #include #include "skgbankobject.h" #include "skgdocumentbank.h" #include "skginterestobject.h" #include "skgoperationobject.h" #include "skgpayeeobject.h" #include "skgsuboperationobject.h" #include "skgtraces.h" #include "skgunitobject.h" int factorial(int n) { return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n; } SKGAccountObject::SKGAccountObject() : SKGAccountObject(nullptr, 0) {} SKGAccountObject::SKGAccountObject(SKGDocument* iDocument, int iID) : SKGNamedObject(iDocument, QStringLiteral("v_account"), iID) {} SKGAccountObject::~SKGAccountObject() = default; SKGAccountObject::SKGAccountObject(const SKGAccountObject& iObject) = default; SKGAccountObject::SKGAccountObject(const SKGNamedObject& iObject) : SKGNamedObject(iObject.getDocument(), QStringLiteral("v_account"), iObject.getID()) { if (iObject.getRealTable() == QStringLiteral("account")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_account"), iObject.getID()); } } SKGAccountObject::SKGAccountObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("account")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_account"), iObject.getID()); } } SKGAccountObject& SKGAccountObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGAccountObject& SKGAccountObject::operator= (const SKGAccountObject& iObject) +{ + copyFrom(iObject); + return *this; +} + SKGError SKGAccountObject::setInitialBalance(double iBalance, const SKGUnitObject& iUnit) { SKGError err; SKGTRACEINFUNCRC(10, err) if (getDocument() != nullptr) { // Delete previous initial balance for this account err = getDocument()->executeSqliteOrder("DELETE FROM operation WHERE d_date='0000-00-00' AND rd_account_id=" % SKGServices::intToString(getID())); // Creation of new initial balance IFOK(err) { SKGOperationObject initialBalanceOp; err = addOperation(initialBalanceOp, true); IFOKDO(err, initialBalanceOp.setAttribute(QStringLiteral("d_date"), QStringLiteral("0000-00-00"))) IFOKDO(err, initialBalanceOp.setUnit(iUnit)) IFOKDO(err, initialBalanceOp.setStatus(SKGOperationObject::CHECKED)) IFOKDO(err, initialBalanceOp.save()) SKGSubOperationObject initialBalanceSubOp; IFOKDO(err, initialBalanceOp.addSubOperation(initialBalanceSubOp)) IFOKDO(err, initialBalanceSubOp.setAttribute(QStringLiteral("d_date"), QStringLiteral("0000-00-00"))) IFOKDO(err, initialBalanceSubOp.setQuantity(iBalance)) IFOKDO(err, initialBalanceSubOp.save()) } } return err; } SKGError SKGAccountObject::getInitialBalance(double& oBalance, SKGUnitObject& oUnit) { SKGError err; SKGTRACEINFUNCRC(10, err) // Initialisation oBalance = 0; oUnit = SKGUnitObject(); QString unitName = qobject_cast(getDocument())->getPrimaryUnit().Symbol; // Get initial balance SKGStringListList listTmp; err = getDocument()->executeSelectSqliteOrder("SELECT f_QUANTITY, t_UNIT FROM v_operation_tmp1 WHERE d_date='0000-00-00' AND rd_account_id=" % SKGServices::intToString(getID()), listTmp); if (!err && listTmp.count() > 1) { oBalance = SKGServices::stringToDouble(listTmp.at(1).at(0)); unitName = listTmp.at(1).at(1); oUnit = SKGUnitObject(getDocument()); err = oUnit.setSymbol(unitName); IFOKDO(err, oUnit.load()) } return err; } SKGError SKGAccountObject::setBank(const SKGBankObject& iBank) { return setAttribute(QStringLiteral("rd_bank_id"), SKGServices::intToString(iBank.getID())); } SKGError SKGAccountObject::getBank(SKGBankObject& oBank) const { SKGError err = getDocument()->getObject(QStringLiteral("v_bank"), "id=" % getAttribute(QStringLiteral("rd_bank_id")), oBank); return err; } SKGError SKGAccountObject::setLinkedAccount(const SKGAccountObject& iAccount) { return setAttribute(QStringLiteral("r_account_id"), SKGServices::intToString(iAccount.getID())); } SKGError SKGAccountObject::getLinkedAccount(SKGAccountObject& oAccount) const { SKGError err = getDocument()->getObject(QStringLiteral("v_account"), "id=" % getAttribute(QStringLiteral("r_account_id")), oAccount); return err; } SKGError SKGAccountObject::getLinkedByAccounts(SKGListSKGObjectBase& oAccounts) const { SKGError err; if (getDocument() != nullptr) { err = getDocument()->getObjects(QStringLiteral("v_account"), "r_account_id=" % SKGServices::intToString(getID()), oAccounts); } return err; } SKGError SKGAccountObject::setNumber(const QString& iNumber) { return setAttribute(QStringLiteral("t_number"), iNumber); } QString SKGAccountObject::getNumber() const { return getAttribute(QStringLiteral("t_number")); } SKGError SKGAccountObject::setComment(const QString& iComment) { return setAttribute(QStringLiteral("t_comment"), iComment); } QString SKGAccountObject::getComment() const { return getAttribute(QStringLiteral("t_comment")); } SKGError SKGAccountObject::setAgencyNumber(const QString& iNumber) { return setAttribute(QStringLiteral("t_agency_number"), iNumber); } QString SKGAccountObject::getAgencyNumber() const { return getAttribute(QStringLiteral("t_agency_number")); } SKGError SKGAccountObject::setAgencyAddress(const QString& iAddress) { return setAttribute(QStringLiteral("t_agency_address"), iAddress); } QString SKGAccountObject::getAgencyAddress() const { return getAttribute(QStringLiteral("t_agency_address")); } SKGError SKGAccountObject::addOperation(SKGOperationObject& oOperation, bool iForce) { SKGError err; if (getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGAccountObject::addOperation"))); } else { oOperation = SKGOperationObject(getDocument()); err = oOperation.setParentAccount(*this, iForce); } return err; } int SKGAccountObject::getNbOperation() const { int nb = 0; if (getDocument() != nullptr) { getDocument()->getNbObjects(QStringLiteral("operation"), "rd_account_id=" % SKGServices::intToString(getID()), nb); } return nb; } SKGError SKGAccountObject::getOperations(SKGListSKGObjectBase& oOperations) const { SKGError err; if (getDocument() != nullptr) { err = getDocument()->getObjects(QStringLiteral("v_operation"), "rd_account_id=" % SKGServices::intToString(getID()), oOperations); } return err; } double SKGAccountObject::getCurrentAmount() const { return SKGServices::stringToDouble(getAttributeFromView(QStringLiteral("v_account_amount"), QStringLiteral("f_CURRENTAMOUNT"))); } double SKGAccountObject::getAmount(QDate iDate, bool iOnlyCurrencies) const { SKGTRACEINFUNC(10) double output = 0; if (getDocument() != nullptr) { // Search result in cache QString ids = SKGServices::intToString(getID()); QString dates = SKGServices::dateToSqlString(QDateTime(iDate)); QString key = "getamount-" % ids % '-' % dates; QString val = getDocument()->getCachedValue(key); if (val.isEmpty()) { SKGStringListList listTmp; SKGError err = getDocument()->executeSelectSqliteOrder("SELECT TOTAL(f_QUANTITY), rc_unit_id FROM v_operation_tmp1 WHERE " "d_date<='" % dates % "' AND t_template='N' AND rd_account_id=" % ids % (iOnlyCurrencies ? " AND t_TYPEUNIT IN ('1', '2', 'C')" : "") % " GROUP BY rc_unit_id", listTmp); int nb = listTmp.count(); for (int i = 1; !err && i < nb ; ++i) { QString quantity = listTmp.at(i).at(0); QString unitid = listTmp.at(i).at(1); double coef = 1; QString val2 = getDocument()->getCachedValue("unitvalue-" % unitid); if (!val2.isEmpty()) { // Yes coef = SKGServices::stringToDouble(val2); } else { // No SKGUnitObject unit(getDocument(), SKGServices::stringToInt(unitid)); if (unit.getType() != SKGUnitObject::PRIMARY) { coef = unit.getAmount(iDate); } } output += coef * SKGServices::stringToDouble(quantity); } getDocument()->addValueInCache(key, SKGServices::doubleToString(output)); } else { output = SKGServices::stringToDouble(val); } } return output; } SKGError SKGAccountObject::setType(SKGAccountObject::AccountType iType) { return setAttribute(QStringLiteral("t_type"), (iType == CURRENT ? QStringLiteral("C") : (iType == CREDITCARD ? QStringLiteral("D") : (iType == ASSETS ? QStringLiteral("A") : (iType == INVESTMENT ? QStringLiteral("I") : (iType == WALLET ? QStringLiteral("W") : (iType == PENSION ? QStringLiteral("P") : (iType == LOAN ? QStringLiteral("L") : (iType == SAVING ? QStringLiteral("S") : QStringLiteral("O")))))))))); } SKGAccountObject::AccountType SKGAccountObject::getType() const { QString typeString = getAttribute(QStringLiteral("t_type")); return (typeString == QStringLiteral("C") ? CURRENT : (typeString == QStringLiteral("D") ? CREDITCARD : (typeString == QStringLiteral("A") ? ASSETS : (typeString == QStringLiteral("I") ? INVESTMENT : (typeString == QStringLiteral("W") ? WALLET : (typeString == QStringLiteral("P") ? PENSION : (typeString == QStringLiteral("L") ? LOAN : (typeString == QStringLiteral("S") ? SAVING : OTHER)))))))); } SKGError SKGAccountObject::setClosed(bool iClosed) { return setAttribute(QStringLiteral("t_close"), iClosed ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGAccountObject::isClosed() const { return (getAttribute(QStringLiteral("t_close")) == QStringLiteral("Y")); } SKGError SKGAccountObject::bookmark(bool iBookmark) { return setAttribute(QStringLiteral("t_bookmarked"), iBookmark ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGAccountObject::isBookmarked() const { return (getAttribute(QStringLiteral("t_bookmarked")) == QStringLiteral("Y")); } SKGError SKGAccountObject::maxLimitAmountEnabled(bool iEnabled) { return setAttribute(QStringLiteral("t_maxamount_enabled"), iEnabled ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGAccountObject::isMaxLimitAmountEnabled() const { return (getAttribute(QStringLiteral("t_maxamount_enabled")) == QStringLiteral("Y")); } SKGError SKGAccountObject::setMaxLimitAmount(double iAmount) { SKGError err = setAttribute(QStringLiteral("f_maxamount"), SKGServices::doubleToString(iAmount)); if (!err && getMinLimitAmount() > iAmount) { err = setMinLimitAmount(iAmount); } return err; } double SKGAccountObject::getMaxLimitAmount() const { return SKGServices::stringToDouble(getAttribute(QStringLiteral("f_maxamount"))); } SKGError SKGAccountObject::minLimitAmountEnabled(bool iEnabled) { return setAttribute(QStringLiteral("t_minamount_enabled"), iEnabled ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGAccountObject::isMinLimitAmountEnabled() const { return (getAttribute(QStringLiteral("t_minamount_enabled")) == QStringLiteral("Y")); } SKGError SKGAccountObject::setMinLimitAmount(double iAmount) { SKGError err = setAttribute(QStringLiteral("f_minamount"), SKGServices::doubleToString(iAmount)); if (!err && getMaxLimitAmount() < iAmount) { err = setMaxLimitAmount(iAmount); } return err; } double SKGAccountObject::getMinLimitAmount() const { return SKGServices::stringToDouble(getAttribute(QStringLiteral("f_minamount"))); } SKGError SKGAccountObject::setReconciliationDate(QDate iDate) { return setAttribute(QStringLiteral("d_reconciliationdate"), SKGServices::dateToSqlString(QDateTime(iDate))); } QDate SKGAccountObject::getReconciliationDate() const { return SKGServices::stringToTime(getAttribute(QStringLiteral("d_reconciliationdate"))).date(); } SKGError SKGAccountObject::setReconciliationBalance(double iAmount) { return setAttribute(QStringLiteral("f_reconciliationbalance"), SKGServices::doubleToString(iAmount)); } double SKGAccountObject::getReconciliationBalance() const { return SKGServices::stringToDouble(getAttribute(QStringLiteral("f_reconciliationbalance"))); } SKGError SKGAccountObject::getUnit(SKGUnitObject& oUnit) const { // Get initial amount SKGStringListList listTmp; SKGError err = getDocument()->executeSelectSqliteOrder("SELECT t_UNIT FROM v_suboperation_consolidated WHERE d_date='0000-00-00' AND rd_account_id=" % SKGServices::intToString(getID()), listTmp); IFOK(err) { // Is initial amount existing ? if (listTmp.count() > 1) { // Yes ==> then the amount is the amount of the initial value oUnit = SKGUnitObject(getDocument()); err = oUnit.setSymbol(listTmp.at(1).at(0)); IFOKDO(err, oUnit.load()) } else { // No ==> we get the preferred unit SKGObjectBase::SKGListSKGObjectBase units; err = getDocument()->getObjects(QStringLiteral("v_unit"), "t_type IN ('1', '2', 'C') AND EXISTS(SELECT 1 FROM operation WHERE rc_unit_id=v_unit.id AND rd_account_id=" % SKGServices::intToString(getID()) % ") ORDER BY t_type", units); int nb = units.count(); if (nb != 0) { oUnit = units.at(0); } } } return err; } SKGError SKGAccountObject::addInterest(SKGInterestObject& oInterest) { SKGError err; if (getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGAccountObject::addInterest"))); } else { oInterest = SKGInterestObject(qobject_cast(getDocument())); err = oInterest.setAccount(*this); } return err; } SKGError SKGAccountObject::getInterests(SKGListSKGObjectBase& oInterestList) const { SKGError err = getDocument()->getObjects(QStringLiteral("v_interest"), "rd_account_id=" % SKGServices::intToString(getID()), oInterestList); return err; } SKGError SKGAccountObject::getInterest(QDate iDate, SKGInterestObject& oInterest) const { QString ids = SKGServices::intToString(getID()); QString dates = SKGServices::dateToSqlString(QDateTime(iDate)); SKGError err = SKGObjectBase::getDocument()->getObject(QStringLiteral("v_interest"), "rd_account_id=" % ids % " AND d_date<='" % dates % "' AND ABS(strftime('%s','" % dates % "')-strftime('%s',d_date))=(SELECT MIN(ABS(strftime('%s','" % dates % "')-strftime('%s',u2.d_date))) FROM interest u2 WHERE u2.rd_account_id=" % ids % " AND u2.d_date<='" % dates % "')", oInterest); // If not found then get first IFKO(err) err = SKGObjectBase::getDocument()->getObject(QStringLiteral("v_interest"), "rd_account_id=" % SKGServices::intToString(getID()) % " AND d_date=(SELECT MIN(d_date) FROM interest WHERE rd_account_id=" % SKGServices::intToString(getID()) % ')', oInterest); return err; } SKGError SKGAccountObject::getInterestItems(SKGAccountObject::SKGInterestItemList& oInterestList, double& oInterests, int iYear) const { oInterestList.clear(); SKGError err; // Initial date int y = iYear; if (y == 0) { y = QDate::currentDate().year(); } QDate initialDate = QDate(y, 1, 1); QDate lastDate = QDate(y, 12, 31); oInterests = 0; bool computationNeeded = false; // Add operations SKGObjectBase::SKGListSKGObjectBase items; err = getDocument()->getObjects(QStringLiteral("v_operation"), "rd_account_id=" % SKGServices::intToString(getID()) % " AND t_template='N' AND t_TYPEUNIT IN ('1', '2', 'C')" " AND d_date>='" % SKGServices::dateToSqlString(QDateTime(initialDate)) % "' " " AND d_date<='" % SKGServices::dateToSqlString(QDateTime(lastDate)) % "' ORDER BY d_date", items); int nb = items.count(); for (int i = 0; !err && i < nb; ++i) { SKGOperationObject ob(items.at(i)); SKGInterestItem itemI; itemI.object = ob; itemI.date = ob.getDate(); itemI.valueDate = itemI.date; itemI.rate = 0; itemI.base = 0; itemI.coef = 0; itemI.annualInterest = 0; itemI.accruedInterest = 0; itemI.amount = ob.getCurrentAmount(); oInterestList.push_back(itemI); } // Add interest IFOK(err) { err = getDocument()->getObjects(QStringLiteral("v_interest"), "rd_account_id=" % SKGServices::intToString(getID()) % " AND d_date>='" % SKGServices::dateToSqlString(QDateTime(initialDate)) % "' " " AND d_date<='" % SKGServices::dateToSqlString(QDateTime(lastDate)) % "' ORDER BY d_date", items); int pos = 0; int nb2 = items.count(); for (int i = 0; !err && i < nb2; ++i) { SKGInterestObject ob(items.at(i)); SKGInterestItem itemI; itemI.object = ob; itemI.date = ob.getDate(); itemI.valueDate = itemI.date; itemI.rate = ob.getRate(); itemI.base = SKGServices::stringToInt(ob.getAttribute(QStringLiteral("t_base"))); itemI.coef = 0; itemI.annualInterest = 0; itemI.accruedInterest = 0; itemI.amount = 0; int nb3 = oInterestList.count(); for (int j = pos; !err && j < nb3; ++j) { if (itemI.date <= oInterestList.at(j).date) { break; } ++pos; } oInterestList.insert(pos, itemI); computationNeeded = true; } } // Get first interest IFOK(err) { SKGInterestObject firstInterest; if (getInterest(initialDate, firstInterest).isSucceeded()) { if (firstInterest.getDate() < initialDate) { SKGInterestItem itemI; itemI.object = firstInterest; itemI.date = initialDate; itemI.valueDate = initialDate; itemI.rate = firstInterest.getRate(); itemI.base = 0; itemI.coef = 0; itemI.annualInterest = 0; itemI.accruedInterest = 0; itemI.amount = 0; oInterestList.insert(0, itemI); computationNeeded = true; } } } // Launch computation IFOK(err) { if (computationNeeded) { err = computeInterestItems(oInterestList, oInterests, y); } else { // Drop temporary table IFOKDO(err, getDocument()->executeSqliteOrder(QStringLiteral("DROP TABLE IF EXISTS interest_result"))) // Create fake table IFOKDO(err, getDocument()->executeSqliteOrder(QStringLiteral("CREATE TEMP TABLE interest_result(a)"))) } } return err; } SKGError SKGAccountObject::computeInterestItems(SKGAccountObject::SKGInterestItemList& ioInterestList, double& oInterests, int iYear) const { SKGError err; // Sum annual interest oInterests = 0; // Initial date int y = iYear; if (y == 0) { y = QDate::currentDate().year(); } QDate initialDate = QDate(y, 1, 1); // Default interest item SKGInterestItem currentInterest; currentInterest.date = initialDate; currentInterest.valueDate = currentInterest.date; currentInterest.rate = 0; currentInterest.coef = 0; currentInterest.annualInterest = 0; currentInterest.accruedInterest = 0; int nb = ioInterestList.count(); for (int i = 0; !err && i < nb; ++i) { SKGInterestItem tmp = ioInterestList.at(i); SKGObjectBase object = tmp.object; if (object.getRealTable() == QStringLiteral("operation")) { // Get operations SKGOperationObject op(object); // Get current amount tmp.amount = op.getCurrentAmount(); // Get value date computation mode SKGInterestObject::ValueDateMode valueMode = SKGInterestObject::FIFTEEN; SKGInterestObject::InterestMode baseMode = SKGInterestObject::FIFTEEN24; if (currentInterest.object.getRealTable() == QStringLiteral("interest")) { SKGInterestObject interestObj(currentInterest.object); valueMode = (tmp.amount >= 0 ? interestObj.getIncomeValueDateMode() : interestObj.getExpenditueValueDateMode()); baseMode = interestObj.getInterestComputationMode(); tmp.rate = interestObj.getRate(); } // Compute value date if (object.getRealTable() == QStringLiteral("operation")) { if (valueMode == SKGInterestObject::FIFTEEN) { if (tmp.amount >= 0) { if (tmp.date.day() <= 15) { tmp.valueDate = tmp.date.addDays(16 - tmp.date.day()); } else { tmp.valueDate = tmp.date.addMonths(1).addDays(1 - tmp.date.day()); } } else { if (tmp.date.day() <= 15) { tmp.valueDate = tmp.date.addDays(1 - tmp.date.day()); } else { tmp.valueDate = tmp.date.addDays(16 - tmp.date.day()); } } } else { tmp.valueDate = tmp.date.addDays(tmp.amount >= 0 ? (static_cast(valueMode)) - 1 : - (static_cast(valueMode)) + 1); } } // Compute coef if (baseMode == SKGInterestObject::DAYS365) { QDate last(tmp.date.year(), 12, 31); tmp.coef = tmp.valueDate.daysTo(last) + 1; tmp.coef /= 365; } else if (baseMode == SKGInterestObject::DAYS360) { QDate last(tmp.date.year(), 12, 31); tmp.coef = 360 * (last.year() - tmp.valueDate.year()) + 30 * (last.month() - tmp.valueDate.month()) + (last.day() - tmp.valueDate.day()); tmp.coef /= 360; } else { tmp.coef = 2 * (12 - tmp.valueDate.month()) + (tmp.valueDate.day() <= 15 ? 2 : 1); tmp.coef /= 24; } if (tmp.valueDate.year() != iYear) { tmp.coef = 0; } // Compute annual interest tmp.annualInterest = tmp.amount * tmp.coef * tmp.rate / 100; } else if (object.getRealTable() == QStringLiteral("interest")) { // Compute coef if (tmp.base == 365) { QDate last(tmp.date.year(), 12, 31); tmp.coef = tmp.valueDate.daysTo(last) + 1; tmp.coef /= 365; } else if (tmp.base == 360) { QDate last(tmp.date.year(), 12, 31); tmp.coef = 360 * (last.year() - tmp.valueDate.year()) + 30 * (last.month() - tmp.valueDate.month()) + (last.day() - tmp.valueDate.day()); tmp.coef /= 360; } else { tmp.coef = 2 * (12 - tmp.valueDate.month()) + (tmp.valueDate.day() <= 15 ? 2 : 1); tmp.coef /= 24; } if (tmp.valueDate.year() != iYear) { tmp.coef = 0; } // Compute annual interest // BUG 329568: We must ignore operations of the day tmp.amount = getAmount(tmp.valueDate.addDays(-1), true); tmp.annualInterest = tmp.amount * tmp.coef * (tmp.rate - currentInterest.rate) / 100; currentInterest = tmp; } // Compute sum oInterests += tmp.annualInterest; // Compute accrued interest tmp.accruedInterest = oInterests - getAmount(tmp.date, true) * tmp.coef * tmp.rate / 100; ioInterestList[i] = tmp; } // Create temporary table IFOK(err) { QStringList sqlOrders; sqlOrders << QStringLiteral("DROP TABLE IF EXISTS interest_result") << QStringLiteral("CREATE TEMP TABLE interest_result(" "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," "d_date DATE NOT NULL," "d_valuedate DATE NOT NULL," "t_comment TEXT NOT NULL DEFAULT ''," "f_currentamount FLOAT NOT NULL DEFAULT 0," "f_coef FLOAT NOT NULL DEFAULT 0," "f_rate FLOAT NOT NULL DEFAULT 0," "f_annual_interest FLOAT NOT NULL DEFAULT 0," "f_accrued_interest FLOAT NOT NULL DEFAULT 0" ")"); err = getDocument()->executeSqliteOrders(sqlOrders); // Fill table int nb2 = ioInterestList.count(); for (int i = 0; !err && i < nb2; ++i) { SKGInterestItem interest = ioInterestList.at(i); SKGObjectBase object = interest.object; QString sqlinsert = "INSERT INTO interest_result (d_date,d_valuedate,t_comment,f_currentamount,f_coef,f_rate,f_annual_interest,f_accrued_interest) " " VALUES ('" % SKGServices::dateToSqlString(QDateTime(interest.date)) % "','" % SKGServices::dateToSqlString(QDateTime(interest.valueDate)) % "','" % SKGServices::stringToSqlString(object.getRealTable() == QStringLiteral("operation") ? i18nc("Noun", "Relative to operation '%1'", SKGOperationObject(object).getDisplayName()) : i18nc("Noun", "Rate change")) % "'," % SKGServices::doubleToString(interest.amount) % ',' % SKGServices::doubleToString(interest.coef) % ',' % SKGServices::doubleToString(interest.rate) % ',' % SKGServices::doubleToString(interest.annualInterest) % ',' % SKGServices::doubleToString(interest.accruedInterest) % ")"; err = getDocument()->executeSqliteOrder(sqlinsert); } } return err; } SKGError SKGAccountObject::transferDeferredOperations(const SKGAccountObject& iTargetAccount, QDate iDate) { SKGError err; SKGTRACEINFUNCRC(10, err) // auto* doc = qobject_cast(getDocument()); if (doc != nullptr) { // Get pointed operations SKGObjectBase::SKGListSKGObjectBase operations; IFOKDO(err, getDocument()->getObjects(QStringLiteral("v_operation"), "rd_account_id=" % SKGServices::intToString(getID()) % " AND t_status='P'", operations)) int nb = operations.count(); if (nb != 0) { SKGOperationObject mergedOperations; SKGOperationObject balancedOperations; for (int i = 0; !err && i < nb; ++i) { SKGOperationObject op(operations.at(i)); // Create the balance operation SKGOperationObject opdup; IFOKDO(err, op.duplicate(opdup, iDate)) SKGListSKGObjectBase subops; IFOKDO(err, opdup.getSubOperations(subops)) int nbsupops = subops.count(); for (int j = 0; !err && j < nbsupops; ++j) { SKGSubOperationObject subop(subops.at(j)); IFOKDO(err, subop.setDate(op.getDate())) IFOKDO(err, subop.setQuantity(-subop.getQuantity())) IFOKDO(err, subop.save()) } if (i == 0) { mergedOperations = opdup; } else { IFOKDO(err, mergedOperations.mergeSuboperations(opdup)) } // Create the duplicate in target account SKGOperationObject opduptarget; IFOKDO(err, op.duplicate(opduptarget)) IFOKDO(err, opduptarget.setDate(op.getDate())) IFOKDO(err, opduptarget.setParentAccount(iTargetAccount)) IFOKDO(err, opduptarget.setImported(op.isImported())) IFOKDO(err, opduptarget.setImportID(op.getImportID())) IFOKDO(err, opduptarget.setGroupOperation(mergedOperations)) IFOKDO(err, opduptarget.setStatus(SKGOperationObject::POINTED)) IFOKDO(err, opduptarget.save()) IFOKDO(err, mergedOperations.load()) // To reload the modif done by the setGroupOperation // Check the operation IFOKDO(err, op.setStatus(SKGOperationObject::CHECKED)) IFOKDO(err, op.save()) } // Check the balance operation IFOKDO(err, mergedOperations.setPayee(SKGPayeeObject())) IFOKDO(err, mergedOperations.setStatus(SKGOperationObject::CHECKED)) IFOKDO(err, mergedOperations.save()) } } return err; } QVector< QVector > SKGAccountObject::getPossibleReconciliations(double iTargetBalance, bool iSearchAllPossibleReconciliation) const { SKGTRACEINFUNC(5) QVector< QVector > output; auto* doc = qobject_cast(getDocument()); if (doc != nullptr) { // Get unit SKGServices::SKGUnitInfo unit1 = doc->getPrimaryUnit(); SKGUnitObject unitAccount; if (getUnit(unitAccount).isSucceeded()) { if (!unitAccount.getSymbol().isEmpty()) { unit1.Symbol = unitAccount.getSymbol(); unit1.Value = SKGServices::stringToDouble(unitAccount.getAttribute(QStringLiteral("f_CURRENTAMOUNT"))); } } SKGTRACEL(5) << "iTargetBalance=" << doc->formatMoney(iTargetBalance, unit1, false) << endl; // Get balance of checked operations QString balanceString; getDocument()->executeSingleSelectSqliteOrder("SELECT f_CHECKED from v_account_display WHERE id=" % SKGServices::intToString(getID()), balanceString); double balance = SKGServices::stringToDouble(balanceString); SKGTRACEL(5) << "balance=" << doc->formatMoney(balance, unit1, false) << endl; QString zero = doc->formatMoney(0, unit1, false); QString negativezero = doc->formatMoney(-EPSILON, unit1, false); QString sdiff = doc->formatMoney(balance - iTargetBalance * unit1.Value, unit1, false); if (sdiff == zero || sdiff == negativezero) { // This is an empty soluce output.push_back(QVector()); SKGTRACEL(5) << "empty solution found !!!" << endl; } else { // Get all imported operation SKGObjectBase::SKGListSKGObjectBase operations; getDocument()->getObjects(QStringLiteral("v_operation"), "rd_account_id=" % SKGServices::intToString(getID()) % " AND t_status!='Y' AND t_template='N' AND t_imported IN ('Y','P') ORDER BY d_date, id", operations); int nb = operations.count(); if (!iSearchAllPossibleReconciliation) { // Check if all operations are a solution double amount = 0.0; QVector list; list.reserve(nb); for (int i = 0; i < nb; ++i) { SKGOperationObject op(operations.at(i)); amount += op.getCurrentAmount(); list.push_back(op); } QString sdiff = doc->formatMoney(amount + balance - iTargetBalance * unit1.Value, unit1, false); if (sdiff == zero || sdiff == negativezero) { SKGTRACEL(5) << "all operations are a solution !!!" << endl; output.push_back(list); return output; } } // Search int nbmax = 500; SKGTRACEL(5) << "Nb operations:" << nb << endl; if (nb > nbmax) { SKGTRACEL(5) << "Too many operations (" << nb << ") ==> Reducing the size of the computation" << endl; for (int i = 0; i < nb - nbmax; ++i) { SKGOperationObject op(operations.at(0)); auto amount = op.getCurrentAmount(); balance += amount; operations.removeFirst(); } } output = getPossibleReconciliations(operations, balance, iTargetBalance, unit1, iSearchAllPossibleReconciliation); } } return output; } QVector< QVector > SKGAccountObject::getPossibleReconciliations(const SKGObjectBase::SKGListSKGObjectBase& iOperations, double iBalance, double iTargetBalance, const SKGServices::SKGUnitInfo& iUnit, bool iSearchAllPossibleReconciliation) const { SKGTRACEINFUNC(5) QVector< QVector > output; output.reserve(5); auto* doc = qobject_cast(getDocument()); if (doc != nullptr) { SKGTRACEL(5) << "iTargetBalance=" << doc->formatMoney(iTargetBalance, iUnit, false) << endl; // Comparison QString zero = doc->formatMoney(0, iUnit, false); QString negativezero = doc->formatMoney(-EPSILON, iUnit, false); // Check operations list int nb = iOperations.count(); if (nb > 0) { // Get all operations of the next date QVector nextOperations; nextOperations.reserve(iOperations.count()); QString date = iOperations.at(0).getAttribute(QStringLiteral("d_date")); for (int i = 0; i < nb; ++i) { SKGOperationObject op(iOperations.at(i)); if (op.getAttribute(QStringLiteral("d_date")) == date) { nextOperations.push_back(op); } else { break; } } // Get all combination of operations int nbNext = nextOperations.count(); SKGTRACEL(5) << date << ":" << nbNext << " operations found" << endl; std::vector v(nbNext); for (int i = 0; i < nbNext; ++i) { v[i] = i; } double nextBalance = iBalance; int index = 0; if (nbNext > 7) { SKGTRACEL(5) << "Too many combination: " << factorial(nbNext) << " ==> limited to 5040" << endl; nbNext = 7; } nb = factorial(nbNext); bool stopTests = false; QVector combi; QVector combiAmount; combi.reserve(nbNext); combiAmount.reserve(nbNext); do { // Build the next combination combi.resize(0); combiAmount.resize(0); double sumOperationPositives = 0.0; double sumOperationsNegatives = 0.0; for (int i = 0; i < nbNext; ++i) { const SKGOperationObject& op = nextOperations.at(v[i]); combi.push_back(op); auto amount = op.getCurrentAmount(); combiAmount.push_back(amount); if (Q_LIKELY(amount < 0)) { sumOperationsNegatives += amount; } else if (amount > 0) { sumOperationPositives += amount; } } // Test the combination double diff = iBalance - iTargetBalance * iUnit.Value; double previousDiff = diff; SKGTRACEL(5) << "Check combination " << (index + 1) << "/" << nb << ": Diff=" << doc->formatMoney(diff, iUnit, false) << endl; // Try to find an immediate soluce int nbop = combi.count(); for (int j = 0; j < nbop; ++j) { auto amount = combiAmount.at(j); diff += amount; if (Q_UNLIKELY(index == 0)) { nextBalance += amount; } QString sdiff = doc->formatMoney(diff, iUnit, false); SKGTRACEL(5) << (j + 1) << "/" << nbop << ": Amount=" << amount << " / New diff=" << sdiff << endl; if (sdiff == zero || sdiff == negativezero) { // This is a soluce auto s = combi.mid(0, j + 1); if (output.contains(s)) { SKGTRACEL(5) << "found but already existing !!!" << endl; } else { output.push_back(s); SKGTRACEL(5) << "found !!!" << endl; if (j == nbop - 1 || iSearchAllPossibleReconciliation) { // No need to test all combinations SKGTRACEL(5) << "No need to test all combinations" << endl; stopTests = true; } } } } // Check if tests of all combinations can be cancelled if ((previousDiff > 0 && previousDiff + sumOperationsNegatives > 0) || (previousDiff < 0 && previousDiff + sumOperationPositives < 0)) { SKGTRACEL(5) << "No need to test all combinations due to signs of operations and diffs" << endl; stopTests = true; } ++index; } while (index < nb && std::next_permutation(v.begin(), v.end()) && !stopTests); // Try to find next solutions auto reconciliations = getPossibleReconciliations(iOperations.mid(nbNext), nextBalance, iTargetBalance, iUnit, iSearchAllPossibleReconciliation); int nbReconciliations = reconciliations.count(); output.reserve(nbReconciliations + 5); for (int i = 0; i < nbReconciliations; ++i) { QVector output2 = nextOperations; output2 = output2 << reconciliations.at(i); output.push_back(output2); } } } SKGTRACEL(5) << output.count() << " soluces found" << endl; if (!output.isEmpty()) { SKGTRACEL(5) << "Size of the first soluce: " << output.at(0).count() << endl; } return output; } SKGError SKGAccountObject::autoReconcile(double iBalance) { SKGError err; SKGTRACEINFUNCRC(5, err) // Soluces auto soluces = getPossibleReconciliations(iBalance); int nbSoluces = soluces.count(); if (nbSoluces > 0) { if (nbSoluces > 1) { err = getDocument()->sendMessage(i18nc("An information message", "More than one solution is possible for this auto reconciliation.")); } // Choose the longest solution QVector soluce; int length = 0; for (int i = 0; i < nbSoluces; ++i) { const auto& s = soluces.at(i); int l = s.count(); if (l > length) { soluce = s; length = l; } } // Check all SKGTRACEL(5) << length << " operations pointed" << endl; for (int i = 0; i < length; ++i) { SKGOperationObject op(soluce.at(i)); err = op.setStatus(SKGOperationObject::POINTED); IFOKDO(err, op.save(true, false)) } } else { err = SKGError(ERR_FAIL, i18nc("Error message", "Can not find the imported operations for obtaining the expected final balance"), QString("skg://skrooge_operation_plugin/?title_icon=quickopen&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Operations of account \"%1\" used for auto reconciliation", getDisplayName())) % "&operationWhereClause=" % SKGServices::encodeForUrl("rd_account_id=" + SKGServices::intToString(getID()) + " AND t_template='N' AND ((t_status='N' AND t_imported IN ('Y','P')) OR t_status='Y')"))); } return err; } SKGError SKGAccountObject::merge(const SKGAccountObject& iAccount, bool iMergeInitalBalance) { SKGError err; SKGTRACEINFUNCRC(10, err) // Get initial balances double balance1 = 0.0; SKGUnitObject unit1; err = getInitialBalance(balance1, unit1); double balance2 = 0.0; SKGUnitObject unit2; if (iMergeInitalBalance) { IFOKDO(err, const_cast(&iAccount)->getInitialBalance(balance2, unit2)) } // Transfer operations SKGObjectBase::SKGListSKGObjectBase ops; IFOKDO(err, iAccount.getOperations(ops)) int nb = ops.count(); for (int i = 0; !err && i < nb; ++i) { SKGOperationObject op(ops.at(i)); err = op.setParentAccount(*this); IFOKDO(err, op.save(true, false)) } // Set initial balance SKGUnitObject unit = unit1; if (!unit1.exist()) { unit = unit2; } if (unit.exist() && balance2 != 0.0) { double balance = balance1 + SKGUnitObject::convert(balance2, unit2, unit); IFOKDO(err, setInitialBalance(balance, unit)) } // Remove account IFOKDO(err, iAccount.remove(false)) return err; } diff --git a/skgbankmodeler/skgaccountobject.h b/skgbankmodeler/skgaccountobject.h index 83fc424f6..aa716e1f0 100644 --- a/skgbankmodeler/skgaccountobject.h +++ b/skgbankmodeler/skgaccountobject.h @@ -1,504 +1,510 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGACCOUNTOBJECT_H #define SKGACCOUNTOBJECT_H /** @file * This file defines classes SKGAccountObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankmodeler_export.h" #include "skgnamedobject.h" class SKGBankObject; class SKGOperationObject; class SKGUnitObject; class SKGInterestObject; /** * This class manages account object */ class SKGBANKMODELER_EXPORT SKGAccountObject : public SKGNamedObject { public: /** * Describe a interest item */ struct SKGInterestItem { SKGObjectBase object; /**< The object (an operation, an interest or nothing for initial balance) */ QDate date; /**< The date */ QDate valueDate; /**< The valueDate */ int base{}; /**< Base */ double amount{}; /**< Amount */ double coef{}; /**< Coef */ double rate{}; /**< Rate */ double annualInterest{}; /**< Annual Interest */ double accruedInterest{}; /**< Accrued Interest */ }; using SKGInterestItemList = QVector; /** * This enumerate defines type of account */ enum AccountType {CURRENT, /**< to define a bank account*/ CREDITCARD, /**< to define a credit card account*/ INVESTMENT, /**< to define an account for investment */ ASSETS, /**< to define a assets account */ OTHER, /**< other kind of account */ WALLET, /**< to define a wallet account */ LOAN, /**< to define a loan account */ SAVING, /**< to define a saving account */ PENSION /**< to define a pension account */ }; /** * This enumerate defines type of account */ Q_ENUM(AccountType) /** * Default constructor */ explicit SKGAccountObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier in @p iTable of the object */ explicit SKGAccountObject(SKGDocument* iDocument, int iID = 0); /** * Copy constructor * @param iObject the object to copy */ SKGAccountObject(const SKGAccountObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGAccountObject(const SKGNamedObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGAccountObject(const SKGObjectBase& iObject); /** * Operator affectation * @param iObject the object to copy */ SKGAccountObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGAccountObject& operator= (const SKGAccountObject& iObject); + /** * Destructor */ virtual ~SKGAccountObject(); /** * Get the parent bank * @param oBank the parent bank * @return an object managing the error * @see SKGError */ virtual SKGError getBank(SKGBankObject& oBank) const; /** * Set the parent bank * @param iBank the parent bank * @return an object managing the error * @see SKGError */ virtual SKGError setBank(const SKGBankObject& iBank); /** * Get the linked account * @param oAccount the linked account * @return an object managing the error * @see SKGError */ virtual SKGError getLinkedAccount(SKGAccountObject& oAccount) const; /** * Get the list of accounts linked to this one * @param oAccounts the list of accounts linked * @return an object managing the error * @see SKGError */ virtual SKGError getLinkedByAccounts(SKGListSKGObjectBase& oAccounts) const; /** * Set the linked account * @param iAccount the linked account * @return an object managing the error * @see SKGError */ virtual SKGError setLinkedAccount(const SKGAccountObject& iAccount); /** * Set the initial balance of this account * @param iBalance the balance * @param iUnit the unit * @return an object managing the error * @see SKGError */ virtual SKGError setInitialBalance(double iBalance, const SKGUnitObject& iUnit); /** * Get the initial balance of this account * @param oBalance the balance * @param oUnit the unit * @return an object managing the error * @see SKGError */ virtual SKGError getInitialBalance(double& oBalance, SKGUnitObject& oUnit); /** * Set the number of the account * @param iNumber the number * @return an object managing the error * @see SKGError */ virtual SKGError setNumber(const QString& iNumber); /** * Get the number of the account * @return the number */ virtual QString getNumber() const; /** * Set the agency number of the account * @param iNumber the agency number * @return an object managing the error * @see SKGError */ virtual SKGError setAgencyNumber(const QString& iNumber); /** * Get the agency number of the account * @return the number */ virtual QString getAgencyNumber() const; /** * Set the agency address of the account * @param iAddress the agency address * @return an object managing the error * @see SKGError */ virtual SKGError setAgencyAddress(const QString& iAddress); /** * Get the agency address of the account * @return the number */ virtual QString getAgencyAddress() const; /** * Set the comment of account * @param iComment the comment * @return an object managing the error * @see SKGError */ virtual SKGError setComment(const QString& iComment); /** * Get the comment of this account * @return the comment */ virtual QString getComment() const; /** * Add a new operation to this account * @param oOperation the created operation * @param iForce force the creation even if the account is closed * @return an object managing the error * @see SKGError */ virtual SKGError addOperation(SKGOperationObject& oOperation, bool iForce = false); /** * Get number of operations * @return the number of operations */ virtual int getNbOperation() const; /** * Get all operations of this account * @param oOperations all operations of this account * @return an object managing the error * @see SKGError */ virtual SKGError getOperations(SKGListSKGObjectBase& oOperations) const; /** * Get the current amount * @return the current amount */ virtual double getCurrentAmount() const; /** * Get amount of the account at a date * @param iDate date * @param iOnlyCurrencies only the operations based on currencies are taken into account, not shares * @return amount of the account */ // cppcheck-suppress passedByValue virtual double getAmount(QDate iDate, bool iOnlyCurrencies = false) const; /** * Set the type of this account * @param iType the type * @return an object managing the error * @see SKGError */ virtual SKGError setType(SKGAccountObject::AccountType iType); /** * Get the type of this account * @return the type */ virtual SKGAccountObject::AccountType getType() const; /** * To set the closed attribute of an account * @param iClosed the closed attribute: true or false * @return an object managing the error * @see SKGError */ virtual SKGError setClosed(bool iClosed); /** * To know if the account has been closed or not * @return an object managing the error * @see SKGError */ virtual bool isClosed() const; /** * To bookmark or not on an account * @param iBookmark the bookmark: true or false * @return an object managing the error * @see SKGError */ virtual SKGError bookmark(bool iBookmark); /** * To know if the account is bookmarked * @return an object managing the error * @see SKGError */ virtual bool isBookmarked() const; /** * To enable the max amount limit or not on an account * @param iEnabled the state: true or false * @return an object managing the error * @see SKGError */ virtual SKGError maxLimitAmountEnabled(bool iEnabled); /** * To know if the account has a max amount limit * @return an object managing the error * @see SKGError */ virtual bool isMaxLimitAmountEnabled() const; /** * To set the max amount limit * @param iAmount the amount (in account's unit) * @return an object managing the error * @see SKGError */ virtual SKGError setMaxLimitAmount(double iAmount); /** * To get the max amount limit (in account's unit) * @return an object managing the error * @see SKGError */ virtual double getMaxLimitAmount() const; /** * To enable the min amount limit or not on an account * @param iEnabled the state: true or false * @return an object managing the error * @see SKGError */ virtual SKGError minLimitAmountEnabled(bool iEnabled); /** * To know if the account has a min amount limit * @return an object managing the error * @see SKGError */ virtual bool isMinLimitAmountEnabled() const; /** * To set the min amount limit * @param iAmount the amount (in account's unit) * @return an object managing the error * @see SKGError */ virtual SKGError setMinLimitAmount(double iAmount); /** * To get the min amount limit (in account's unit) * @return an object managing the error * @see SKGError */ virtual double getMinLimitAmount() const; /** * Set reconciliation date of this account * @param iDate the date * @return an object managing the error * @see SKGError */ // cppcheck-suppress passedByValue virtual SKGError setReconciliationDate(QDate iDate); /** * Get reconciliation date of this account * @return the date */ virtual QDate getReconciliationDate() const; /** * Set reconciliation balance of this account * @param iAmount the balance * @return an object managing the error * @see SKGError */ virtual SKGError setReconciliationBalance(double iAmount); /** * Get reconciliation balance of this account * @return the balance */ virtual double getReconciliationBalance() const; /** * Get the unit * @param oUnit the unit * @return an object managing the error * @see SKGError */ virtual SKGError getUnit(SKGUnitObject& oUnit) const; /** * Add an interest * @param oInterest the created interest * @return an object managing the error. * @see SKGError */ virtual SKGError addInterest(SKGInterestObject& oInterest); /** * Get interests * @param oInterestList the list of interest in this account * @return an object managing the error * @see SKGError */ virtual SKGError getInterests(SKGListSKGObjectBase& oInterestList) const; /** * Get interest object at a given date * @param iDate the date * @param oInterest the interest object * @return an object managing the error * @see SKGError */ // cppcheck-suppress passedByValue virtual SKGError getInterest(QDate iDate, SKGInterestObject& oInterest) const; /** * Get and compute interest items * @param oInterestList interest items * @param oInterests sum of annual interests * @param iYear the year of computation (default=current year) * @return an object managing the error * @see SKGError */ virtual SKGError getInterestItems(SKGAccountObject::SKGInterestItemList& oInterestList, double& oInterests, int iYear = 0) const; /** * Compute all items * @param ioInterestList interest items * @param oInterests sum of annual interests * @param iYear the year of computation (default=current year) * @return an object managing the error * @see SKGError */ virtual SKGError computeInterestItems(SKGAccountObject::SKGInterestItemList& ioInterestList, double& oInterests, int iYear = 0) const; /** * Try to point all imported operations to obtain the expected balance * @param iBalance the expected balance in the account unit * @return an object managing the error * @see SKGError */ virtual SKGError autoReconcile(double iBalance); /** * Merge iAccount in current account * @param iAccount the account. All operations will be transferred into this account. The account will be removed * @param iMergeInitalBalance to merge the initial balances too * @return an object managing the error * @see SKGError */ virtual SKGError merge(const SKGAccountObject& iAccount, bool iMergeInitalBalance); /** * Create a Transfer operation to balance a credit card account (based on pointed operations) * @param iTargetAccount the target account (should be the bank account) * @param iDate the date of the balance operation * @return an object managing the error * @see SKGError */ // cppcheck-suppress passedByValue virtual SKGError transferDeferredOperations(const SKGAccountObject& iTargetAccount, QDate iDate = QDate::currentDate()); /** * Get all list of operations that can be checked to reconcile with target balance * @param iTargetBalance the target balance (in the unit of the account) * @param iSearchAllPossibleReconciliation to search all possible reconciliation * @return the list of possible solutions */ virtual QVector< QVector > getPossibleReconciliations(double iTargetBalance, bool iSearchAllPossibleReconciliation = true) const; private: /** * Get all list of operations that can be checked to reconcile with target balance * @param iOperations the operations to take into account * @param iBalance the current balance (in the unit of the account) * @param iTargetBalance the target balance (in the unit of the account) * @param iUnit the unit of the account (for better performances) * @param iSearchAllPossibleReconciliation to search all possible reconciliation * @return the list of possible solutions */ QVector< QVector > getPossibleReconciliations(const SKGObjectBase::SKGListSKGObjectBase& iOperations, double iBalance, double iTargetBalance, const SKGServices::SKGUnitInfo& iUnit, bool iSearchAllPossibleReconciliation) const; }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGAccountObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbankmodeler/skgbankobject.cpp b/skgbankmodeler/skgbankobject.cpp index 6a828704f..55d7d8947 100644 --- a/skgbankmodeler/skgbankobject.cpp +++ b/skgbankmodeler/skgbankobject.cpp @@ -1,109 +1,115 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file implements classes SKGBankObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankobject.h" #include #include "skgaccountobject.h" #include "skgdocumentbank.h" SKGBankObject::SKGBankObject(): SKGBankObject(nullptr) {} SKGBankObject::SKGBankObject(SKGDocument* iDocument, int iID): SKGNamedObject(iDocument, QStringLiteral("v_bank"), iID) {} SKGBankObject::~SKGBankObject() = default; SKGBankObject::SKGBankObject(const SKGBankObject& iObject) = default; SKGBankObject::SKGBankObject(const SKGNamedObject& iObject) { if (iObject.getRealTable() == QStringLiteral("bank")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_bank"), iObject.getID()); } } SKGBankObject::SKGBankObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("bank")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_bank"), iObject.getID()); } } SKGBankObject& SKGBankObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGBankObject& SKGBankObject::operator= (const SKGBankObject& iObject) +{ + copyFrom(iObject); + return *this; +} + SKGError SKGBankObject::addAccount(SKGAccountObject& oAccount) { SKGError err; if (getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGBankObject::addAccount"))); } else { oAccount = SKGAccountObject(qobject_cast(getDocument())); err = oAccount.setAttribute(QStringLiteral("rd_bank_id"), SKGServices::intToString(getID())); } return err; } SKGError SKGBankObject::getAccounts(SKGListSKGObjectBase& oAccountList) const { SKGError err = getDocument()->getObjects(QStringLiteral("v_account"), "rd_bank_id=" % SKGServices::intToString(getID()), oAccountList); return err; } SKGError SKGBankObject::setNumber(const QString& iNumber) { return setAttribute(QStringLiteral("t_bank_number"), iNumber); } QString SKGBankObject::getNumber() const { return getAttribute(QStringLiteral("t_bank_number")); } SKGError SKGBankObject::setIcon(const QString& iIcon) { return setAttribute(QStringLiteral("t_icon"), iIcon); } QString SKGBankObject::getIcon() const { return getAttribute(QStringLiteral("t_icon")); } double SKGBankObject::getCurrentAmount() const { return SKGServices::stringToDouble(getAttributeFromView(QStringLiteral("v_bank_amount"), QStringLiteral("f_CURRENTAMOUNT"))); } diff --git a/skgbankmodeler/skgbankobject.h b/skgbankmodeler/skgbankobject.h index 66898586c..f5a9cb912 100644 --- a/skgbankmodeler/skgbankobject.h +++ b/skgbankmodeler/skgbankobject.h @@ -1,131 +1,137 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGBANKOBJECT_H #define SKGBANKOBJECT_H /** @file * This file defines classes SKGBankObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankmodeler_export.h" #include "skgnamedobject.h" class SKGAccountObject; /** * This class manages bank object */ class SKGBANKMODELER_EXPORT SKGBankObject final : public SKGNamedObject { public: /** * Default constructor */ explicit SKGBankObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier in @p iTable of the object */ explicit SKGBankObject(SKGDocument* iDocument, int iID = 0); /** * Copy constructor * @param iObject the object to copy */ SKGBankObject(const SKGBankObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGBankObject(const SKGNamedObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGBankObject(const SKGObjectBase& iObject); /** * Operator affectation * @param iObject the object to copy */ SKGBankObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGBankObject& operator= (const SKGBankObject& iObject); + /** * Destructor */ virtual ~SKGBankObject(); /** * Add an account * @param oAccount the created account * @return an object managing the error. * @see SKGError */ SKGError addAccount(SKGAccountObject& oAccount); /** * Get accounts * @param oAccountList the list of accounts in this bank * @return an object managing the error * @see SKGError */ SKGError getAccounts(SKGListSKGObjectBase& oAccountList) const; /** * Set the number of the bank * @param iNumber the number * @return an object managing the error * @see SKGError */ SKGError setNumber(const QString& iNumber); /** * Get the number of the bank * @return the number */ QString getNumber() const; /** * Set the icon of the bank * @param iIcon the icon * @return an object managing the error * @see SKGError */ SKGError setIcon(const QString& iIcon); /** * Get the icon of the bank * @return the number */ QString getIcon() const; /** * Get the current amount * @return the current amount */ double getCurrentAmount() const; }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGBankObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbankmodeler/skgcategoryobject.cpp b/skgbankmodeler/skgcategoryobject.cpp index c119194e1..91c39a695 100644 --- a/skgbankmodeler/skgcategoryobject.cpp +++ b/skgbankmodeler/skgcategoryobject.cpp @@ -1,310 +1,316 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file implements classes SKGCategoryObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgcategoryobject.h" #include #include "skgdocumentbank.h" #include "skgsuboperationobject.h" #include "skgtraces.h" SKGCategoryObject::SKGCategoryObject() : SKGCategoryObject(nullptr) {} SKGCategoryObject::SKGCategoryObject(SKGDocument* iDocument, int iID) : SKGNamedObject(iDocument, QStringLiteral("v_category"), iID) {} SKGCategoryObject::~SKGCategoryObject() = default; SKGCategoryObject::SKGCategoryObject(const SKGCategoryObject& iObject) = default; SKGCategoryObject::SKGCategoryObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("category")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_category"), iObject.getID()); } } SKGCategoryObject& SKGCategoryObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGCategoryObject& SKGCategoryObject::operator= (const SKGCategoryObject& iObject) +{ + copyFrom(iObject); + return *this; +} + SKGError SKGCategoryObject::setName(const QString& iName) { SKGError err; if (iName.contains(OBJECTSEPARATOR)) { err = SKGError(ERR_FAIL, i18nc("Error message", "Invalid name '%1' because of the name cannot contain '%2'", iName, QString(OBJECTSEPARATOR))); } else { err = SKGNamedObject::setName(iName); } return err; } QString SKGCategoryObject::getWhereclauseId() const { // Could we use the id QString output = SKGObjectBase::getWhereclauseId(); // clazy:exclude=skipped-base-method if (output.isEmpty()) { if (!(getAttribute(QStringLiteral("t_name")).isEmpty())) { output = "t_name='" % SKGServices::stringToSqlString(getAttribute(QStringLiteral("t_name"))) % '\''; } QString rd_category_id = getAttribute(QStringLiteral("rd_category_id")); if (!output.isEmpty()) { output += QStringLiteral(" AND "); } if (rd_category_id.isEmpty()) { output += QStringLiteral("(rd_category_id=0 OR rd_category_id IS NULL OR rd_category_id='')"); } else { output += "rd_category_id=" % rd_category_id; } } return output; } QString SKGCategoryObject::getFullName() const { return getAttribute(QStringLiteral("t_fullname")); } SKGError SKGCategoryObject::createPathCategory(SKGDocumentBank* iDocument, const QString& iFullPath, SKGCategoryObject& oCategory, bool iSendPopupMessageOnCreation, bool iRenameIfAlreadyExist) { SKGError err; SKGTRACEINFUNCRC(10, err) // Check if category is already existing if (iFullPath.isEmpty()) { oCategory = SKGCategoryObject(nullptr, 0); } else if (iDocument != nullptr) { if (!iRenameIfAlreadyExist) { iDocument->getObject(QStringLiteral("v_category"), "t_fullname='" % SKGServices::stringToSqlString(iFullPath) % '\'', oCategory); } else { oCategory.resetID(); } if (oCategory.getID() == 0) { // No, we have to create it // Search category separator int posSeparator = iFullPath.lastIndexOf(OBJECTSEPARATOR); if (posSeparator == -1) { oCategory = SKGCategoryObject(iDocument); err = oCategory.setName(iFullPath); // Check if already existing if (!err && iRenameIfAlreadyExist) { int index = 1; while (!err && oCategory.exist()) { index++; err = oCategory.setName(iFullPath % " (" % SKGServices::intToString(index) % ')'); } } IFOKDO(err, oCategory.save()) } else { // Get first and second parts of the branch QString first = iFullPath.mid(0, posSeparator); QString second = iFullPath.mid(posSeparator + QString(OBJECTSEPARATOR).length(), iFullPath.length() - posSeparator - QString(OBJECTSEPARATOR).length()); // Get first category SKGCategoryObject FirstCategory; err = SKGCategoryObject::createPathCategory(iDocument, first, FirstCategory); IFOK(err) { // Get second category err = FirstCategory.addCategory(oCategory); // Add second under first IFOKDO(err, oCategory.setName(second)) // Check if already existing if (!err && iRenameIfAlreadyExist) { int index = 2; while (!err && oCategory.exist()) { err = oCategory.setName(second % " (" % SKGServices::intToString(index) % ')'); ++index; } } // save IFOKDO(err, oCategory.save()) } } if (!err && iSendPopupMessageOnCreation) { iDocument->sendMessage(i18nc("Information message", "The category '%1' has been created", iFullPath), SKGDocument::Positive); } } } return err; } SKGError SKGCategoryObject::addCategory(SKGCategoryObject& oCategory) { SKGError err; SKGTRACEINFUNCRC(10, err) if (getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGCategoryObject::addCategory"))); } else { oCategory = SKGCategoryObject(qobject_cast(getDocument())); err = oCategory.setAttribute(QStringLiteral("rd_category_id"), SKGServices::intToString(getID())); } return err; } SKGError SKGCategoryObject::setParentCategory(const SKGCategoryObject& iCategory) { SKGError err; SKGTRACEINFUNCRC(10, err) if (iCategory.getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGCategoryObject::setParentCategory"))); } else { // Check if it is a loop SKGCategoryObject current = iCategory; do { if (current == *this) { err = SKGError(ERR_FAIL, i18nc("Error message", "You cannot create a loop.")); } else { SKGCategoryObject parent2; current.getParentCategory(parent2); current = parent2; } } while (!err && current.getID() != 0); IFOKDO(err, setAttribute(QStringLiteral("rd_category_id"), SKGServices::intToString(iCategory.getID()))) } return err; } SKGError SKGCategoryObject::removeParentCategory() { return setAttribute(QStringLiteral("rd_category_id"), QStringLiteral("0")); } SKGError SKGCategoryObject::getParentCategory(SKGCategoryObject& oCategory) const { SKGError err; QString parent_id = getAttribute(QStringLiteral("rd_category_id")); if (!parent_id.isEmpty() && parent_id != QStringLiteral("0")) { err = getDocument()->getObject(QStringLiteral("v_category"), "id=" % parent_id, oCategory); } return err; } SKGError SKGCategoryObject::getRootCategory(SKGCategoryObject& oCategory) const { SKGError err; SKGCategoryObject parent2; err = getParentCategory(parent2); IFOK(err) { if (!parent2.exist()) { // No parent oCategory = *this; } else { // Parent exist err = parent2.getRootCategory(oCategory); } } return err; } SKGError SKGCategoryObject::getCategories(SKGListSKGObjectBase& oCategoryList) const { return getDocument()->getObjects(QStringLiteral("v_category"), "rd_category_id=" % SKGServices::intToString(getID()), oCategoryList); } double SKGCategoryObject::getCurrentAmount() const { QString v = getAttribute(QStringLiteral("f_SUMCURRENTAMOUNT")); if (v.isEmpty()) { SKGNamedObject cat(getDocument(), QStringLiteral("v_category_display"), getID()); v = cat.getAttribute(QStringLiteral("f_SUMCURRENTAMOUNT")); } return SKGServices::stringToDouble(v); } SKGError SKGCategoryObject::getSubOperations(SKGListSKGObjectBase& oSubOperations) const { SKGError err = getDocument()->getObjects(QStringLiteral("v_suboperation"), "r_category_id=" % SKGServices::intToString(getID()), oSubOperations); return err; } SKGError SKGCategoryObject::bookmark(bool iBookmark) { return setAttribute(QStringLiteral("t_bookmarked"), iBookmark ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGCategoryObject::isBookmarked() const { return (getAttribute(QStringLiteral("t_bookmarked")) == QStringLiteral("Y")); } SKGError SKGCategoryObject::setClosed(bool iClosed) { return setAttribute(QStringLiteral("t_close"), iClosed ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGCategoryObject::isClosed() const { return (getAttribute(QStringLiteral("t_close")) == QStringLiteral("Y")); } SKGError SKGCategoryObject::merge(const SKGCategoryObject& iCategory) { SKGError err; SKGObjectBase::SKGListSKGObjectBase ops; IFOKDO(err, iCategory.getSubOperations(ops)) int nb = ops.count(); for (int i = 0; !err && i < nb; ++i) { SKGSubOperationObject op(ops.at(i)); err = op.setCategory(*this); IFOKDO(err, op.save(true, false)) } SKGObjectBase::SKGListSKGObjectBase cats; IFOKDO(err, iCategory.getCategories(cats)) nb = cats.count(); for (int i = 0; !err && i < nb; ++i) { SKGCategoryObject cat(cats.at(i)); err = cat.setParentCategory(*this); IFOKDO(err, cat.save(true, false)) } IFOKDO(err, iCategory.remove(false)) return err; } diff --git a/skgbankmodeler/skgcategoryobject.h b/skgbankmodeler/skgcategoryobject.h index 591eb519c..52c311faf 100644 --- a/skgbankmodeler/skgcategoryobject.h +++ b/skgbankmodeler/skgcategoryobject.h @@ -1,213 +1,219 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGCATEGORYOBJECT_H #define SKGCATEGORYOBJECT_H /** @file * This file defines classes SKGCategoryObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankmodeler_export.h" #include "skgnamedobject.h" class SKGDocumentBank; /** * This class manages category object */ class SKGBANKMODELER_EXPORT SKGCategoryObject final : public SKGNamedObject { public: /** * Default constructor */ explicit SKGCategoryObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier in @p iTable of the object */ explicit SKGCategoryObject(SKGDocument* iDocument, int iID = 0); /** * Copy constructor * @param iObject the object to copy */ SKGCategoryObject(const SKGCategoryObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGCategoryObject(const SKGObjectBase& iObject); /** * Operator affectation * @param iObject the object to copy */ SKGCategoryObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGCategoryObject& operator= (const SKGCategoryObject& iObject); + /** * Destructor */ virtual ~SKGCategoryObject(); /** * Create a category branch if needed and return the leaf of the branch * @param iDocument the document where to create * @param iFullPath the full path. Example: cat1 > cat2 > cat3 * @param oCategory the leaf of the branch * @param iSendPopupMessageOnCreation to send a creation message if the category is created * @param iRenameIfAlreadyExist if a leaf with the expected name already exist than the leaf will be renamed and created * @return an object managing the error. * @see SKGError */ static SKGError createPathCategory(SKGDocumentBank* iDocument, const QString& iFullPath, SKGCategoryObject& oCategory, bool iSendPopupMessageOnCreation = false, bool iRenameIfAlreadyExist = false); /** * Set the name of this object * @param iName the name * @return an object managing the error * @see SKGError */ SKGError setName(const QString& iName) override; /** * To bookmark or not a category * @param iBookmark the bookmark: true or false * @return an object managing the error * @see SKGError */ SKGError bookmark(bool iBookmark); /** * To know if the category is bookmarked * @return an object managing the error * @see SKGError */ bool isBookmarked() const; /** * To set the closed attribute of a payee * @param iClosed the closed attribute: true or false * @return an object managing the error * @see SKGError */ virtual SKGError setClosed(bool iClosed); /** * To know if the payee has been closed or not * @return an object managing the error * @see SKGError */ virtual bool isClosed() const; /** * Get the full name of this category. * The full name is the unique name of the category. * It is computed by the concatenation of names for all * the fathers of this category. * @return the full name */ QString getFullName() const; /** * Add a category * @param oCategory the created category * @return an object managing the error. * @see SKGError */ SKGError addCategory(SKGCategoryObject& oCategory); /** * Move the category by changing the parent * @param iCategory the parent category * @return an object managing the error. * @see SKGError */ SKGError setParentCategory(const SKGCategoryObject& iCategory); /** * Remove the parent category. The category will be a root. * @return an object managing the error. * @see SKGError */ SKGError removeParentCategory(); /** * Get the parent category * @param oCategory the parent category * @return an object managing the error. * @see SKGError */ SKGError getParentCategory(SKGCategoryObject& oCategory) const; /** * Get the root category * @param oCategory the root category * @return an object managing the error. * @see SKGError */ SKGError getRootCategory(SKGCategoryObject& oCategory) const; /** * Get categories * @param oCategoryList the list of categories under the current one * @return an object managing the error * @see SKGError */ SKGError getCategories(SKGListSKGObjectBase& oCategoryList) const; /** * Get the current amount * @return the current amount */ double getCurrentAmount() const; /** * Get all sub operations of this category * @param oSubOperations all sub operations of this category * @return an object managing the error * @see SKGError */ SKGError getSubOperations(SKGListSKGObjectBase& oSubOperations) const; /** * Merge iCategory in current category * @param iCategory the category. All sub operations will be transferred into this category. The category will be removed * @return an object managing the error * @see SKGError */ SKGError merge(const SKGCategoryObject& iCategory); protected: /** * Get where clause needed to identify objects. * For this class, the whereclause is based on name + rd_category_id * @return the where clause */ QString getWhereclauseId() const override; }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGCategoryObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbankmodeler/skgoperationobject.cpp b/skgbankmodeler/skgoperationobject.cpp index 67c4ec3c2..e3e23ad0a 100644 --- a/skgbankmodeler/skgoperationobject.cpp +++ b/skgbankmodeler/skgoperationobject.cpp @@ -1,579 +1,585 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file implements classes SKGOperationObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgoperationobject.h" #include #include "skgaccountobject.h" #include "skgdocument.h" #include "skgpayeeobject.h" #include "skgrecurrentoperationobject.h" #include "skgservices.h" #include "skgsuboperationobject.h" #include "skgtraces.h" #include "skgunitobject.h" SKGOperationObject::SKGOperationObject() : SKGOperationObject(nullptr) {} SKGOperationObject::SKGOperationObject(SKGDocument* iDocument, int iID) : SKGObjectBase(iDocument, QStringLiteral("v_operation"), iID) {} SKGOperationObject::~SKGOperationObject() = default; SKGOperationObject::SKGOperationObject(const SKGOperationObject& iObject) = default; SKGOperationObject::SKGOperationObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("operation")) { copyFrom(iObject); } else { *this = SKGObjectBase(iObject.getDocument(), QStringLiteral("v_operation"), iObject.getID()); } } SKGOperationObject& SKGOperationObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGOperationObject& SKGOperationObject::operator= (const SKGOperationObject& iObject) +{ + copyFrom(iObject); + return *this; +} + SKGError SKGOperationObject::duplicate(SKGOperationObject& oOperation, QDate iDate, bool iTemplateMode) const { SKGError err; SKGTRACEINFUNCRC(20, err) QDate previousDate = getDate(); // Create the duplicated operation oOperation = SKGOperationObject(getDocument(), this->getID()); // To be sure the object is on v_operation IFOKDO(err, oOperation.load()) IFOKDO(err, oOperation.resetID()) IFOKDO(err, oOperation.setDate(iDate)) IFOKDO(err, oOperation.setStatus(SKGOperationObject::NONE)) IFOKDO(err, oOperation.setImported(false)) IFOKDO(err, oOperation.setTemplate(iTemplateMode)) IFOKDO(err, oOperation.setImportID(QLatin1String(""))) IFOKDO(err, oOperation.bookmark(false)) IFOKDO(err, oOperation.setNumber(QLatin1String(""))) IFOKDO(err, oOperation.setGroupOperation(oOperation)) IFOKDO(err, oOperation.setAttribute(QStringLiteral("d_createdate"), SKGServices::dateToSqlString(QDateTime::currentDateTime()))) IFOKDO(err, oOperation.save(false, false)) // Duplicate subop IFOK(err) { SKGListSKGObjectBase subops; err = getSubOperations(subops); int nbsupops = subops.count(); for (int i = 0; !err && i < nbsupops; ++i) { SKGSubOperationObject subop(subops.at(i)); err = subop.resetID(); IFOKDO(err, subop.setParentOperation(oOperation)) IFOKDO(err, subop.setDate(subop.getDate().addDays(previousDate.daysTo(iDate)))) IFOKDO(err, subop.save(false)) } } // Duplicate grouped operation to support recurrent transfers IFOK(err) { SKGListSKGObjectBase goupops; err = getGroupedOperations(goupops); int nbgoupops = goupops.count(); for (int i = 0; !err && i < nbgoupops; ++i) { SKGOperationObject groupop(goupops.at(i)); if (groupop != *this) { // Create the duplicated operation SKGOperationObject newgroupop = groupop; err = newgroupop.resetID(); IFOKDO(err, newgroupop.setDate(iDate)) IFOKDO(err, newgroupop.setStatus(SKGOperationObject::NONE)) IFOKDO(err, newgroupop.setImported(false)) IFOKDO(err, newgroupop.setTemplate(iTemplateMode)) IFOKDO(err, newgroupop.setImportID(QLatin1String(""))) IFOKDO(err, newgroupop.bookmark(false)) IFOKDO(err, newgroupop.setNumber(QLatin1String(""))) IFOKDO(err, newgroupop.setGroupOperation(newgroupop)) IFOKDO(err, newgroupop.setGroupOperation(oOperation)) IFOKDO(err, newgroupop.save(false)) // Duplicate subop IFOK(err) { SKGListSKGObjectBase subops; err = groupop.getSubOperations(subops); int nbsupops = subops.count(); for (int j = 0; !err && j < nbsupops; ++j) { SKGSubOperationObject subop(subops.at(j)); err = subop.resetID(); IFOKDO(err, subop.setParentOperation(newgroupop)) IFOKDO(err, subop.setDate(subop.getDate().addDays(previousDate.daysTo(iDate)))) IFOKDO(err, subop.save(false)) } } } } } IFOKDO(err, oOperation.load()) return err; } SKGError SKGOperationObject::getParentAccount(SKGAccountObject& oAccount) const { SKGObjectBase objTmp; SKGError err = getDocument()->getObject(QStringLiteral("v_account"), "id=" % getAttribute(QStringLiteral("rd_account_id")), objTmp); oAccount = objTmp; return err; } SKGError SKGOperationObject::setParentAccount(const SKGAccountObject& iAccount, bool iForce) { SKGError err; QString currentAccount = getAttribute(QStringLiteral("rd_account_id")); QString newAccount = SKGServices::intToString(iAccount.getID()); if (newAccount == QStringLiteral("0")) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGOperationObject::setParentAccount"))); } else { if (newAccount != currentAccount) { if (iAccount.isClosed() && !iForce) { err = SKGError(ERR_FAIL, i18nc("Error message", "Impossible to add an operation in a closed account")); } else { err = setAttribute(QStringLiteral("rd_account_id"), newAccount); } } } return err; } SKGError SKGOperationObject::setMode(const QString& iMode) { return setAttribute(QStringLiteral("t_mode"), iMode); } QString SKGOperationObject::getMode() const { return getAttribute(QStringLiteral("t_mode")); } SKGError SKGOperationObject::setPayee(const SKGPayeeObject& iPayee) { return setAttribute(QStringLiteral("r_payee_id"), SKGServices::intToString(iPayee.getID())); } SKGError SKGOperationObject::getPayee(SKGPayeeObject& oPayee) const { SKGError err = getDocument()->getObject(QStringLiteral("v_payee"), "id=" % SKGServices::intToString(SKGServices::stringToInt(getAttribute(QStringLiteral("r_payee_id")))), oPayee); return err; } SKGError SKGOperationObject::setComment(const QString& iComment) { return setAttribute(QStringLiteral("t_comment"), iComment); } QString SKGOperationObject::getComment() const { return getAttribute(QStringLiteral("t_comment")); } SKGError SKGOperationObject::setNumber(const QString& iNumber) { return setAttribute(QStringLiteral("t_number"), iNumber); } QString SKGOperationObject::getNumber() const { return getAttribute(QStringLiteral("t_number")); } SKGOperationObject::OperationStatus SKGOperationObject::getStatus() const { QString t_status = getAttribute(QStringLiteral("t_status")); if (t_status == QStringLiteral("Y")) { return SKGOperationObject::CHECKED; } if (t_status == QStringLiteral("P")) { return SKGOperationObject::POINTED; } return SKGOperationObject::NONE; } SKGError SKGOperationObject::setStatus(SKGOperationObject::OperationStatus iStatus) { return setAttribute(QStringLiteral("t_status"), (iStatus == SKGOperationObject::CHECKED ? QStringLiteral("Y") : (iStatus == SKGOperationObject::POINTED ? QStringLiteral("P") : QStringLiteral("N")))); } SKGError SKGOperationObject::setDate(QDate iDate, bool iRefreshSubOperations) { SKGError err; // Compute delta of the change of date QDate previousDate = getDate(); if (iRefreshSubOperations) { // Apply the delta on sub operations SKGObjectBase::SKGListSKGObjectBase listSubOperations; getSubOperations(listSubOperations); // Error is not manage to avoid error in case of first creation int nbSubOperations = listSubOperations.count(); for (int i = 0; !err && i < nbSubOperations; ++i) { SKGSubOperationObject sop(listSubOperations.at(i)); QDate previousSubDate = sop.getDate(); if (previousSubDate.isValid()) { if (previousDate.isValid()) { int delta = previousDate.daysTo(iDate); err = sop.setDate(previousSubDate.addDays(delta)); IFOKDO(err, sop.save(true, false)) } } else { err = sop.setDate(iDate); IFOKDO(err, sop.save(true, false)) } } } IFOKDO(err, setAttribute(QStringLiteral("d_date"), SKGServices::dateToSqlString(QDateTime(iDate)))) return err; } QDate SKGOperationObject::getDate() const { return SKGServices::stringToTime(getAttribute(QStringLiteral("d_date"))).date(); } SKGError SKGOperationObject::getUnit(SKGUnitObject& oUnit) const { SKGError err = (getDocument() == nullptr ? SKGError(ERR_POINTER, i18nc("Error message", "Operation impossible because the document is missing")) : getDocument()->getObject(QStringLiteral("v_unit"), "id=" % getAttribute(QStringLiteral("rc_unit_id")), oUnit)); // SKGError err = getDocument()->getObject(QStringLiteral("v_unit"), "id=" % getAttribute(QStringLiteral("rc_unit_id")), oUnit); return err; } SKGError SKGOperationObject::setUnit(const SKGUnitObject& iUnit) { return setAttribute(QStringLiteral("rc_unit_id"), SKGServices::intToString(iUnit.getID())); } bool SKGOperationObject::isInGroup() const { return (getAttribute(QStringLiteral("i_group_id")) != QStringLiteral("0")); } bool SKGOperationObject::isTransfer(SKGOperationObject& oOperation) const { SKGTRACEINFUNC(10) SKGObjectBase::SKGListSKGObjectBase ops; getGroupedOperations(ops); if (ops.count() == 2) { oOperation = (*this == SKGOperationObject(ops.at(0)) ? ops.at(1) : ops.at(0)); } return (getAttribute(QStringLiteral("t_TRANSFER")) == QStringLiteral("Y")); } SKGError SKGOperationObject::getGroupedOperations(SKGListSKGObjectBase& oGroupedOperations) const { SKGError err; QString gpId1 = getAttribute(QStringLiteral("i_group_id")); if (gpId1 == QStringLiteral("0") || gpId1.isEmpty()) { oGroupedOperations.clear(); } else { err = getDocument()->getObjects(QStringLiteral("v_operation"), "i_group_id=" % gpId1, oGroupedOperations); } return err; } SKGError SKGOperationObject::getGroupOperation(SKGOperationObject& oOperation) const { SKGError err = getDocument()->getObject(QStringLiteral("v_operation"), "id=" % getAttribute(QStringLiteral("i_group_id")), oOperation); return err; } SKGError SKGOperationObject::setGroupOperation(const SKGOperationObject& iOperation) { SKGError err; SKGTRACEINFUNCRC(20, err) // Is it a remove group ? if (iOperation == *this) { // Yes err = setAttribute(QStringLiteral("i_group_id"), QStringLiteral("0")); } else { // Get previous groups QString group1 = getAttribute(QStringLiteral("i_group_id")); QString group2 = iOperation.getAttribute(QStringLiteral("i_group_id")); // Create a new group SKGStringListList result; err = getDocument()->executeSelectSqliteOrder(QStringLiteral("SELECT max(i_group_id) from operation"), result); IFOK(err) { // Compute new group id QString newIdGroup('1'); if (result.count() == 2) { newIdGroup = SKGServices::intToString(SKGServices::stringToInt(result.at(1).at(0)) + 1); } // Set group id SKGOperationObject op1 = SKGOperationObject(iOperation.getDocument(), iOperation.getID()); err = op1.setAttribute(QStringLiteral("i_group_id"), newIdGroup); IFOKDO(err, op1.save(true, false)) IFOKDO(err, setAttribute(QStringLiteral("i_group_id"), newIdGroup)) // Update all objects of group2 if (!err && !group1.isEmpty() && group1 != QStringLiteral("0")) { err = getDocument()->executeSqliteOrder("UPDATE operation SET i_group_id=" % newIdGroup % " WHERE i_group_id=" % group1); } // Update all objects of group2 if (!err && !group2.isEmpty() && group2 != QStringLiteral("0")) { err = getDocument()->executeSqliteOrder("UPDATE operation SET i_group_id=" % newIdGroup % " WHERE i_group_id=" % group2); } } } return err; } SKGError SKGOperationObject::bookmark(bool iBookmark) { return setAttribute(QStringLiteral("t_bookmarked"), iBookmark ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGOperationObject::isBookmarked() const { return (getAttribute(QStringLiteral("t_bookmarked")) == QStringLiteral("Y")); } SKGError SKGOperationObject::setImported(bool iImported) { return setAttribute(QStringLiteral("t_imported"), iImported ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGOperationObject::isImported() const { return (getAttribute(QStringLiteral("t_imported")) != QStringLiteral("N")); } SKGError SKGOperationObject::setImportID(const QString& iImportID) { SKGError err = setAttribute(QStringLiteral("t_import_id"), iImportID); if (!err && !iImportID.isEmpty()) { err = setAttribute(QStringLiteral("t_imported"), QStringLiteral("T")); } return err; } QString SKGOperationObject::getImportID() const { return getAttribute(QStringLiteral("t_import_id")); } SKGError SKGOperationObject::setTemplate(bool iTemplate) { return setAttribute(QStringLiteral("t_template"), iTemplate ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGOperationObject::isTemplate() const { return (getAttribute(QStringLiteral("t_template")) != QStringLiteral("N")); } int SKGOperationObject::getNbSubOperations() const { return SKGServices::stringToInt(getAttribute(QStringLiteral("i_NBSUBOPERATIONS"))); } SKGError SKGOperationObject::addSubOperation(SKGSubOperationObject& oSubOperation) { SKGError err; if (getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGOperationObject::addSubOperation"))); } else { oSubOperation = SKGSubOperationObject(getDocument()); err = oSubOperation.setParentOperation(*this); IFOKDO(err, oSubOperation.setDate(getDate())) } return err; } SKGError SKGOperationObject::getSubOperations(SKGListSKGObjectBase& oSubOperations) const { SKGError err; if (getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGOperationObject::getSubOperations"))); } else { err = getDocument()->getObjects(QStringLiteral("v_suboperation"), "rd_operation_id=" % SKGServices::intToString(getID()) % " ORDER BY i_order", oSubOperations); } return err; } double SKGOperationObject::getCurrentAmount() const { return SKGServices::stringToDouble(getAttribute(QStringLiteral("f_CURRENTAMOUNT"))); } double SKGOperationObject::getBalance() const { double output = 0.0; SKGStringListList result; SKGError err = getDocument()->executeSelectSqliteOrder("SELECT TOTAL(f_CURRENTAMOUNT) FROM v_operation WHERE t_template='N' AND " "rd_account_id=" % getAttribute(QStringLiteral("rd_account_id")) % " AND (d_date<'" % getAttribute(QStringLiteral("d_date")) % "' OR " "(d_date='" % getAttribute(QStringLiteral("d_date")) % "' AND id<=" % SKGServices::intToString(getID()) % "))", result); IFOK(err) { output = SKGServices::stringToDouble(result.at(1).at(0)); } return output; } double SKGOperationObject::getAmount(QDate iDate) const { // Get quantity double quantity = SKGServices::stringToDouble(getAttribute(QStringLiteral("f_QUANTITY"))); // Is the unit value already in cache ? double coef = 1; QString val = getDocument()->getCachedValue("unitvalue-" % getAttribute(QStringLiteral("rc_unit_id"))); if (!val.isEmpty()) { // Yes coef = SKGServices::stringToDouble(val); } else { // No SKGUnitObject unit; if (getUnit(unit).isSucceeded()) { coef = unit.getAmount(iDate); } } return coef * quantity; } SKGError SKGOperationObject::addRecurrentOperation(SKGRecurrentOperationObject& oRecurrentOperation) const { SKGError err; if (getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGOperationObject::addRecurrentOperation"))); } else { oRecurrentOperation = SKGRecurrentOperationObject(getDocument()); err = oRecurrentOperation.setParentOperation(*this); IFOK(err) oRecurrentOperation.setDate(getDate()); } return err; } SKGError SKGOperationObject::getRecurrentOperations(SKGListSKGObjectBase& oRecurrentOperation) const { SKGError err; if (getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGOperationObject::getRecurrentOperation"))); } else { err = getDocument()->getObjects(QStringLiteral("v_recurrentoperation"), "rd_operation_id=" % SKGServices::intToString(getID()), oRecurrentOperation); } return err; } SKGError SKGOperationObject::mergeAttribute(const SKGOperationObject& iDeletedOne, SKGOperationObject::AmountAlignmentMode iMode, bool iSendMessage) { // Merge operation SKGError err = setDate(iDeletedOne.getDate()); IFOKDO(err, setImportID(iDeletedOne.getImportID())) IFOKDO(err, setAttribute(QStringLiteral("t_imported"), iDeletedOne.getAttribute(QStringLiteral("t_imported")))) if (!err && getComment().isEmpty()) { err = setComment(iDeletedOne.getComment()); } SKGPayeeObject payee; getPayee(payee); IFOKDO(err, setPayee(payee)) if (!err && getMode().isEmpty()) { err = setMode(iDeletedOne.getMode()); } if (!err && !isBookmarked()) { err = bookmark(iDeletedOne.isBookmarked()); } if (!err && getNumber().isEmpty()) { err = setNumber(iDeletedOne.getNumber()); } IFOKDO(err, save()) // Merge suboperations double currentAmount = getCurrentAmount(); double targettAmount = iDeletedOne.getCurrentAmount(); if (qAbs(currentAmount - targettAmount) > 0.0001) { SKGObjectBase::SKGListSKGObjectBase subOps1; IFOKDO(err, getSubOperations(subOps1)) SKGObjectBase::SKGListSKGObjectBase subOps2; IFOKDO(err, iDeletedOne.getSubOperations(subOps2)) // Align amounts SKGOperationObject::AmountAlignmentMode mode = iMode; if (mode == DEFAULT) { if (subOps2.count() == 1 && subOps1.count() == 1) { mode = PROPORTIONAL; } else if (subOps2.count() >= 1 && subOps1.count() >= 1) { mode = ADDSUBOPERATION; } } if (mode == SKGOperationObject::ADDSUBOPERATION) { // Add sub operation to align amount SKGSubOperationObject so1; IFOKDO(err, addSubOperation(so1)) IFOKDO(err, so1.setQuantity(targettAmount - currentAmount)) IFOKDO(err, so1.save()) } else { // Keep ratio for (const auto& sopbase : qAsConst(subOps1)) { SKGSubOperationObject sop(sopbase); IFOKDO(err, sop.setQuantity(targettAmount * sop.getQuantity() / currentAmount)) IFOKDO(err, sop.save()) } } IFOKDO(err, load()) if (iSendMessage) { IFOK(err) getDocument()->sendMessage(i18nc("An information message", "Amount has been changed to be aligned with the imported operation"), SKGDocument::Positive); } } // transfers properties IFOKDO(err, getDocument()->executeSqliteOrder(QStringLiteral("UPDATE parameters SET t_uuid_parent='") % getUniqueID() % QStringLiteral("' WHERE t_uuid_parent='") % iDeletedOne.getUniqueID() % QStringLiteral("'"))) // Delete useless operation IFOKDO(err, iDeletedOne.remove(false, true)) return err; } SKGError SKGOperationObject::mergeSuboperations(const SKGOperationObject& iDeletedOne) { SKGError err; SKGObjectBase::SKGListSKGObjectBase subops; err = iDeletedOne.getSubOperations(subops); int nb = subops.count(); for (int i = 0; !err && i < nb; ++i) { SKGSubOperationObject subop(subops.at(i)); err = subop.setParentOperation(*this); IFOKDO(err, subop.save()) } IFOKDO(err, iDeletedOne.remove(false)) return err; } diff --git a/skgbankmodeler/skgoperationobject.h b/skgbankmodeler/skgoperationobject.h index 9320a7a6a..956d6735e 100644 --- a/skgbankmodeler/skgoperationobject.h +++ b/skgbankmodeler/skgoperationobject.h @@ -1,408 +1,414 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGOPERATIONOBJECT_H #define SKGOPERATIONOBJECT_H /** @file * This file defines classes SKGOperationObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankmodeler_export.h" #include "skgobjectbase.h" class SKGAccountObject; class SKGUnitObject; class SKGSubOperationObject; class SKGRecurrentOperationObject; class SKGPayeeObject; /** * This class manages operation object */ class SKGBANKMODELER_EXPORT SKGOperationObject final : public SKGObjectBase { public: /** * This enumerate defines status for operations */ enum OperationStatus {NONE, /**< no status */ POINTED, /**< pointed */ CHECKED /**< checked */ }; /** * This enumerate defines status for operations */ Q_ENUM(OperationStatus) /** * This enumerate defines the alignment amount mode */ enum AmountAlignmentMode { DEFAULT, /**< Default */ PROPORTIONAL, /**< Proportional */ ADDSUBOPERATION /**< Add sub operation */ }; /** * This enumerate defines the alignment amount mode */ Q_ENUM(AmountAlignmentMode) /** * Default constructor */ explicit SKGOperationObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier in @p iTable of the object */ explicit SKGOperationObject(SKGDocument* iDocument, int iID = 0); /** * Copy constructor * @param iObject the object to copy */ explicit SKGOperationObject(const SKGObjectBase& iObject); /** * Copy constructor * @param iObject the object to copy */ SKGOperationObject(const SKGOperationObject& iObject); /** * Operator affectation * @param iObject the object to copy */ SKGOperationObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGOperationObject& operator= (const SKGOperationObject& iObject); + /** * Destructor */ virtual ~SKGOperationObject(); /** * Duplicate current operation including suboperations and grouped operations * @param oOperation the created operation * @param iDate date(s) for new operation(s) * @param iTemplateMode the template mode for new operation(s) * @return an object managing the error. * @see SKGError */ // cppcheck-suppress passedByValue SKGError duplicate(SKGOperationObject& oOperation, QDate iDate = QDate::currentDate(), bool iTemplateMode = false) const; /** * Get the parent account * @param oAccount the parent account * @return an object managing the error. * @see SKGError */ SKGError getParentAccount(SKGAccountObject& oAccount) const; /** * Set the parent account * @param iAccount the parent account * @param iForce force the creation even if the account is closed * @return an object managing the error. * @see SKGError */ SKGError setParentAccount(const SKGAccountObject& iAccount, bool iForce = false); /** * Set the mode of operation * @param iNumber the number * @return an object managing the error * @see SKGError */ SKGError setNumber(const QString& iNumber); /** * Get the number of this operation * @return the number */ QString getNumber() const; /** * Set the mode of operation * @param iMode the mode * @return an object managing the error * @see SKGError */ SKGError setMode(const QString& iMode); /** * Get the mode of this operation * @return the mode */ QString getMode() const; /** * Set the payee of operation * @param iPayee the payee * @return an object managing the error * @see SKGError */ SKGError setPayee(const SKGPayeeObject& iPayee); /** * Get the payee of this operation * @param oPayee the payee * @return an object managing the error * @see SKGError */ SKGError getPayee(SKGPayeeObject& oPayee) const; /** * Set the comment of operation * @param iComment the comment * @return an object managing the error * @see SKGError */ SKGError setComment(const QString& iComment); /** * Get the comment of this operation * @return the comment */ QString getComment() const; /** * Get the status of this operation * @return the status */ SKGOperationObject::OperationStatus getStatus() const; /** * Set the status of operation * @param iStatus the status * @return an object managing the error * @see SKGError */ SKGError setStatus(SKGOperationObject::OperationStatus iStatus); /** * Set date of this operation * @param iDate the date * @param iRefreshSubOperations to refresh the sub operations * @return an object managing the error * @see SKGError */ // cppcheck-suppress passedByValue SKGError setDate(QDate iDate, bool iRefreshSubOperations = true); /** * Get date of this operation * @return the date */ QDate getDate() const; /** * Set the unit * @param iUnit the unit * @return an object managing the error * @see SKGError */ SKGError setUnit(const SKGUnitObject& iUnit); /** * Get the unit * @param oUnit the unit * @return an object managing the error * @see SKGError */ SKGError getUnit(SKGUnitObject& oUnit) const; /** * To know if an operation is grouped * @return true or false */ bool isInGroup() const; /** * To know if the current operation is a transfer of the other one * @param oOperation the other operation * @return true or false */ bool isTransfer(SKGOperationObject& oOperation) const; /** * Set the group operation * @param iOperation the operation (itself to remove from group) * @return an object managing the error * @see SKGError */ SKGError setGroupOperation(const SKGOperationObject& iOperation); /** * Get the group operation * @param oOperation the operation * @return an object managing the error * @see SKGError */ SKGError getGroupOperation(SKGOperationObject& oOperation) const; /** * Get all operations in the same group * @param oGroupedOperations all operation in the same group * @return an object managing the error * @see SKGError */ SKGError getGroupedOperations(SKGListSKGObjectBase& oGroupedOperations) const; /** * To bookmark or not an operation * @param iBookmark the bookmark: true or false * @return an object managing the error * @see SKGError */ SKGError bookmark(bool iBookmark); /** * To know if the operation is bookmarked * @return an object managing the error * @see SKGError */ bool isBookmarked() const; /** * To set the imported attribute of an operation * @param iImported the imported status: true or false * @return an object managing the error * @see SKGError */ SKGError setImported(bool iImported); /** * To know if the operation has been imported or not * @return an object managing the error * @see SKGError */ bool isImported() const; /** * Set the import identifier of operation, t_imported is set to 'T' * @param iImportID the import identifier (it is used to check if the operation is already imported) * @return an object managing the error * @see SKGError */ SKGError setImportID(const QString& iImportID); /** * Get the import identifier of operation * @return the comment */ QString getImportID() const; /** * To set the template attribute of an operation * @param iTemplate the template status: true or false * @return an object managing the error * @see SKGError */ SKGError setTemplate(bool iTemplate); /** * To know if the operation is a template or not * @return an object managing the error * @see SKGError */ bool isTemplate() const; /** * Add a new suboperation to this operation * @param oSubOperation the created suboperation * @return an object managing the error * @see SKGError */ SKGError addSubOperation(SKGSubOperationObject& oSubOperation); /** * Get the number of sub operations * @return number of sub operations */ int getNbSubOperations() const; /** * Get all suboperations of this operation * @param oSubOperations all suboperations of this operation * @return an object managing the error * @see SKGError */ SKGError getSubOperations(SKGListSKGObjectBase& oSubOperations) const; /** * Get the current amount * @return the current amount */ double getCurrentAmount() const; /** * Get the account balance for this operation * @return the account balance */ double getBalance() const; /** * Get amount of the operation at a date * @param iDate date * @return amount of the operation */ // cppcheck-suppress passedByValue double getAmount(QDate iDate) const; /** * Create a recurrent operation based on this one * @param oRecurrentOperation the created recurrent operation * @return an object managing the error * @see SKGError */ SKGError addRecurrentOperation(SKGRecurrentOperationObject& oRecurrentOperation) const; /** * Get the recurrent operations based on this one * @param oRecurrentOperation the recurrent operations * @return an object managing the error * @see SKGError */ SKGError getRecurrentOperations(SKGListSKGObjectBase& oRecurrentOperation) const; /** * Merge current operation with another one * @param iDeletedOne after merge this operation will be deleted * @param iMode the alignment mode * @param iSendMessage send warning message * @return an object managing the error * @see SKGError */ SKGError mergeAttribute(const SKGOperationObject& iDeletedOne, SKGOperationObject::AmountAlignmentMode iMode = SKGOperationObject::DEFAULT, bool iSendMessage = true); /** * Merge current operation with another one * @param iDeletedOne after merge this operation will be deleted * @return an object managing the error * @see SKGError */ SKGError mergeSuboperations(const SKGOperationObject& iDeletedOne); }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGOperationObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbankmodeler/skgpayeeobject.cpp b/skgbankmodeler/skgpayeeobject.cpp index 6dbec05dd..66f1ded71 100644 --- a/skgbankmodeler/skgpayeeobject.cpp +++ b/skgbankmodeler/skgpayeeobject.cpp @@ -1,152 +1,158 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file implements classes SKGPayeeObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgpayeeobject.h" #include #include "skgcategoryobject.h" #include "skgdocumentbank.h" #include "skgoperationobject.h" #include "skgtraces.h" SKGPayeeObject::SKGPayeeObject(): SKGPayeeObject(nullptr) {} SKGPayeeObject::SKGPayeeObject(SKGDocument* iDocument, int iID): SKGNamedObject(iDocument, QStringLiteral("v_payee"), iID) {} SKGPayeeObject::~SKGPayeeObject() = default; SKGPayeeObject::SKGPayeeObject(const SKGPayeeObject& iObject) = default; SKGPayeeObject::SKGPayeeObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("payee")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_payee"), iObject.getID()); } } SKGPayeeObject& SKGPayeeObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGPayeeObject& SKGPayeeObject::operator= (const SKGPayeeObject& iObject) +{ + copyFrom(iObject); + return *this; +} + SKGError SKGPayeeObject::createPayee(SKGDocumentBank* iDocument, const QString& iName, SKGPayeeObject& oPayee, bool iSendPopupMessageOnCreation) { SKGError err; SKGTRACEINFUNCRC(10, err) // Check if refund is already existing if (iName.isEmpty()) { oPayee = SKGPayeeObject(nullptr, 0); } else if (iDocument != nullptr) { iDocument->getObject(QStringLiteral("v_payee"), "t_name='" % SKGServices::stringToSqlString(iName) % '\'', oPayee); if (oPayee.getID() == 0) { // No, we have to create it oPayee = SKGPayeeObject(iDocument); err = oPayee.setName(iName); IFOKDO(err, oPayee.save()) if (!err && iSendPopupMessageOnCreation) { err = iDocument->sendMessage(i18nc("Information message", "Payee '%1' has been created", iName), SKGDocument::Positive); } } } return err; } SKGError SKGPayeeObject::getOperations(SKGListSKGObjectBase& oOperations) const { SKGError err = getDocument()->getObjects(QStringLiteral("v_operation"), "r_payee_id=" % SKGServices::intToString(getID()), oOperations); return err; } SKGError SKGPayeeObject::setAddress(const QString& iAddress) { return setAttribute(QStringLiteral("t_address"), iAddress); } QString SKGPayeeObject::getAddress() const { return getAttribute(QStringLiteral("t_address")); } SKGError SKGPayeeObject::setClosed(bool iClosed) { return setAttribute(QStringLiteral("t_close"), iClosed ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGPayeeObject::isClosed() const { return (getAttribute(QStringLiteral("t_close")) == QStringLiteral("Y")); } SKGError SKGPayeeObject::bookmark(bool iBookmark) { return setAttribute(QStringLiteral("t_bookmarked"), iBookmark ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGPayeeObject::isBookmarked() const { return (getAttribute(QStringLiteral("t_bookmarked")) == QStringLiteral("Y")); } SKGError SKGPayeeObject::getCategory(SKGCategoryObject& oCategory) const { SKGError err = getDocument()->getObject(QStringLiteral("v_category"), "id=" % getAttribute(QStringLiteral("r_category_id")), oCategory); return err; } SKGError SKGPayeeObject::setCategory(const SKGCategoryObject& iCategory) { return setAttribute(QStringLiteral("r_category_id"), SKGServices::intToString(iCategory.getID())); } SKGError SKGPayeeObject::merge(const SKGPayeeObject& iPayee) { SKGError err; SKGObjectBase::SKGListSKGObjectBase ops; IFOKDO(err, iPayee.getOperations(ops)) int nb = ops.count(); for (int i = 0; !err && i < nb; ++i) { SKGOperationObject op(ops.at(i)); err = op.setPayee(*this); IFOKDO(err, op.save(true, false)) } IFOKDO(err, iPayee.remove(false)) return err; } diff --git a/skgbankmodeler/skgpayeeobject.h b/skgbankmodeler/skgpayeeobject.h index 4744690ad..8c182e6b1 100644 --- a/skgbankmodeler/skgpayeeobject.h +++ b/skgbankmodeler/skgpayeeobject.h @@ -1,165 +1,171 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGPAYEEOBJECT_H #define SKGPAYEEOBJECT_H /** @file * This file defines classes SKGPayeeObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankmodeler_export.h" #include "skgnamedobject.h" class SKGDocumentBank; class SKGCategoryObject; /** * This class manages payee object */ class SKGBANKMODELER_EXPORT SKGPayeeObject final : public SKGNamedObject { public: /** * Default constructor */ explicit SKGPayeeObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier in @p iTable of the object */ explicit SKGPayeeObject(SKGDocument* iDocument, int iID = 0); /** * Copy constructor * @param iObject the object to copy */ SKGPayeeObject(const SKGPayeeObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGPayeeObject(const SKGObjectBase& iObject); /** * Operator affectation * @param iObject the object to copy */ SKGPayeeObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGPayeeObject& operator= (const SKGPayeeObject& iObject); + /** * Destructor */ virtual ~SKGPayeeObject(); /** * Create a payee if needed and return it * @param iDocument the document where to create * @param iName the name * @param oPayee the payee * @param iSendPopupMessageOnCreation to send a creation message if the payee is created * @return an object managing the error. * @see SKGError */ static SKGError createPayee(SKGDocumentBank* iDocument, const QString& iName, SKGPayeeObject& oPayee, bool iSendPopupMessageOnCreation = false); /** * Get all operations of this payee * @param oOperations all operations of this payee * @return an object managing the error * @see SKGError */ SKGError getOperations(SKGListSKGObjectBase& oOperations) const; /** * Set the address of payee * @param iAddress the address * @return an object managing the error * @see SKGError */ SKGError setAddress(const QString& iAddress); /** * Get the address of this payee * @return the address */ QString getAddress() const; /** * To set the closed attribute of a payee * @param iClosed the closed attribute: true or false * @return an object managing the error * @see SKGError */ virtual SKGError setClosed(bool iClosed); /** * To know if the payee has been closed or not * @return an object managing the error * @see SKGError */ virtual bool isClosed() const; /** * To bookmark or not a payee * @param iBookmark the bookmark: true or false * @return an object managing the error * @see SKGError */ SKGError bookmark(bool iBookmark); /** * To know if the payee is bookmarked * @return an object managing the error * @see SKGError */ bool isBookmarked() const; /** * Set the category * @param iCategory the category * @return an object managing the error * @see SKGError */ SKGError setCategory(const SKGCategoryObject& iCategory); /** * Get the category * @param oCategory the category * @return an object managing the error * @see SKGError */ SKGError getCategory(SKGCategoryObject& oCategory) const; /** * Merge iPayee in current payee * @param iPayee the payee. All operations will be transferred into this payee. The payee will be removed * @return an object managing the error * @see SKGError */ SKGError merge(const SKGPayeeObject& iPayee); }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGPayeeObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbankmodeler/skgrecurrentoperationobject.cpp b/skgbankmodeler/skgrecurrentoperationobject.cpp index 4a493a8d5..c67245055 100644 --- a/skgbankmodeler/skgrecurrentoperationobject.cpp +++ b/skgbankmodeler/skgrecurrentoperationobject.cpp @@ -1,312 +1,318 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file implements classes SKGRecurrentOperationObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgrecurrentoperationobject.h" #include #include "skgdocumentbank.h" #include "skgoperationobject.h" #include "skgservices.h" #include "skgsuboperationobject.h" #include "skgtraces.h" SKGRecurrentOperationObject::SKGRecurrentOperationObject(): SKGRecurrentOperationObject(nullptr) {} SKGRecurrentOperationObject::SKGRecurrentOperationObject(SKGDocument* iDocument, int iID): SKGObjectBase(iDocument, QStringLiteral("v_recurrentoperation"), iID) {} SKGRecurrentOperationObject::~SKGRecurrentOperationObject() = default; SKGRecurrentOperationObject::SKGRecurrentOperationObject(const SKGRecurrentOperationObject& iObject) = default; SKGRecurrentOperationObject::SKGRecurrentOperationObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("recurrentoperation")) { copyFrom(iObject); } else { *this = SKGObjectBase(iObject.getDocument(), QStringLiteral("v_recurrentoperation"), iObject.getID()); } } SKGRecurrentOperationObject& SKGRecurrentOperationObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGRecurrentOperationObject& SKGRecurrentOperationObject::operator= (const SKGRecurrentOperationObject& iObject) +{ + copyFrom(iObject); + return *this; +} + SKGError SKGRecurrentOperationObject::getParentOperation(SKGOperationObject& oOperation) const { SKGObjectBase objTmp; SKGError err = getDocument()->getObject(QStringLiteral("v_operation"), "id=" % getAttribute(QStringLiteral("rd_operation_id")), objTmp); oOperation = objTmp; return err; } SKGError SKGRecurrentOperationObject::setParentOperation(const SKGOperationObject& iOperation) { return setAttribute(QStringLiteral("rd_operation_id"), SKGServices::intToString(iOperation.getID())); } SKGError SKGRecurrentOperationObject::setPeriodIncrement(int iIncrement) { return setAttribute(QStringLiteral("i_period_increment"), SKGServices::intToString(iIncrement)); } int SKGRecurrentOperationObject::getPeriodIncrement() const { return SKGServices::stringToInt(getAttribute(QStringLiteral("i_period_increment"))); } SKGRecurrentOperationObject::PeriodUnit SKGRecurrentOperationObject::getPeriodUnit() const { QString t_period_unit = getAttribute(QStringLiteral("t_period_unit")); if (t_period_unit == QStringLiteral("D")) { return SKGRecurrentOperationObject::DAY; } if (t_period_unit == QStringLiteral("W")) { return SKGRecurrentOperationObject::WEEK; } if (t_period_unit == QStringLiteral("M")) { return SKGRecurrentOperationObject::MONTH; } return SKGRecurrentOperationObject::YEAR; } SKGError SKGRecurrentOperationObject::setPeriodUnit(SKGRecurrentOperationObject::PeriodUnit iPeriod) { return setAttribute(QStringLiteral("t_period_unit"), (iPeriod == SKGRecurrentOperationObject::DAY ? QStringLiteral("D") : (iPeriod == SKGRecurrentOperationObject::WEEK ? QStringLiteral("W") : (iPeriod == SKGRecurrentOperationObject::MONTH ? QStringLiteral("M") : QStringLiteral("Y"))))); } SKGError SKGRecurrentOperationObject::setAutoWriteDays(int iDays) { return setAttribute(QStringLiteral("i_auto_write_days"), SKGServices::intToString(iDays)); } int SKGRecurrentOperationObject::getAutoWriteDays() const { return SKGServices::stringToInt(getAttribute(QStringLiteral("i_auto_write_days"))); } SKGError SKGRecurrentOperationObject::setWarnDays(int iDays) { return setAttribute(QStringLiteral("i_warn_days"), SKGServices::intToString(iDays)); } int SKGRecurrentOperationObject::getWarnDays() const { return SKGServices::stringToInt(getAttribute(QStringLiteral("i_warn_days"))); } bool SKGRecurrentOperationObject::hasTimeLimit() const { return (getAttribute(QStringLiteral("t_times")) == QStringLiteral("Y")); } SKGError SKGRecurrentOperationObject::timeLimit(bool iTimeLimit) { return setAttribute(QStringLiteral("t_times"), iTimeLimit ? QStringLiteral("Y") : QStringLiteral("N")); } SKGError SKGRecurrentOperationObject::setTimeLimit(QDate iLastDate) { // Get parameters QDate firstDate = this->getDate(); if (iLastDate < firstDate) { return setTimeLimit(0); } SKGRecurrentOperationObject::PeriodUnit period = this->getPeriodUnit(); int occu = qMax(this->getPeriodIncrement(), 1); // Compute nb time int nbd = firstDate.daysTo(iLastDate); if (period == SKGRecurrentOperationObject::DAY) { nbd = nbd / occu; } else if (period == SKGRecurrentOperationObject::WEEK) { nbd = nbd / (7 * occu); } else if (period == SKGRecurrentOperationObject::MONTH) { nbd = (iLastDate.day() >= firstDate.day() ? 0 : -1) + (iLastDate.year() - firstDate.year()) * 12 + (iLastDate.month() - firstDate.month()); } else if (period == SKGRecurrentOperationObject::YEAR) { nbd = nbd / (365 * occu); } if (nbd < -1) { nbd = -1; } return setTimeLimit(nbd + 1); } SKGError SKGRecurrentOperationObject::setTimeLimit(int iTimeLimit) { return setAttribute(QStringLiteral("i_nb_times"), SKGServices::intToString(iTimeLimit)); } int SKGRecurrentOperationObject::getTimeLimit() const { return SKGServices::stringToInt(getAttribute(QStringLiteral("i_nb_times"))); } SKGError SKGRecurrentOperationObject::setDate(QDate iDate) { return setAttribute(QStringLiteral("d_date"), SKGServices::dateToSqlString(QDateTime(iDate))); } QDate SKGRecurrentOperationObject::getNextDate() const { QDate nextDate = getDate(); SKGRecurrentOperationObject::PeriodUnit punit = getPeriodUnit(); int p = getPeriodIncrement(); if (punit == SKGRecurrentOperationObject::DAY) { nextDate = nextDate.addDays(p); } else if (punit == SKGRecurrentOperationObject::WEEK) { nextDate = nextDate.addDays(7 * p); } else if (punit == SKGRecurrentOperationObject::MONTH) { nextDate = nextDate.addMonths(p); } else if (punit == SKGRecurrentOperationObject::YEAR) { nextDate = nextDate.addYears(p); } return nextDate; } QDate SKGRecurrentOperationObject::getDate() const { return SKGServices::stringToTime(getAttribute(QStringLiteral("d_date"))).date(); } SKGError SKGRecurrentOperationObject::warnEnabled(bool iWarn) { return setAttribute(QStringLiteral("t_warn"), iWarn ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGRecurrentOperationObject::isWarnEnabled() const { return (getAttribute(QStringLiteral("t_warn")) == QStringLiteral("Y")); } SKGError SKGRecurrentOperationObject::autoWriteEnabled(bool iAutoWrite) { return setAttribute(QStringLiteral("t_auto_write"), iAutoWrite ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGRecurrentOperationObject::isAutoWriteEnabled() const { return (getAttribute(QStringLiteral("t_auto_write")) == QStringLiteral("Y")); } SKGError SKGRecurrentOperationObject::getRecurredOperations(SKGListSKGObjectBase& oOperations) const { return getDocument()->getObjects(QStringLiteral("v_operation"), "r_recurrentoperation_id=" % SKGServices::intToString(getID()), oOperations); } SKGError SKGRecurrentOperationObject::process(int& oNbInserted, bool iForce, QDate iDate) { SKGError err; SKGTRACEINFUNCRC(10, err) oNbInserted = 0; if (!hasTimeLimit() || getTimeLimit() > 0) { if (isAutoWriteEnabled() || iForce) { QDate nextDate = getDate(); if (nextDate.isValid() && iDate >= nextDate.addDays(-getAutoWriteDays())) { SKGOperationObject op; err = getParentOperation(op); IFOK(err) { // Create the duplicated operation SKGOperationObject newOp; err = op.duplicate(newOp, nextDate); if (!op.isTemplate()) { // Set old op as recurrent IFOKDO(err, op.setAttribute(QStringLiteral("r_recurrentoperation_id"), SKGServices::intToString(getID()))) IFOKDO(err, op.save()) // Set new operation as reference IFOKDO(err, setParentOperation(newOp)) } else { // Set new op as recurrent IFOKDO(err, newOp.setAttribute(QStringLiteral("r_recurrentoperation_id"), SKGServices::intToString(getID()))) IFOKDO(err, newOp.save()) } IFOKDO(err, setDate(getNextDate())) if (!err && hasTimeLimit()) { err = setTimeLimit(getTimeLimit() - 1); } IFOKDO(err, save()) IFOKDO(err, load()) // Process again in case of multi insert needed int nbi = 0; IFOKDO(err, process(nbi, iForce, iDate)) oNbInserted = oNbInserted + 1 + nbi; // Send message IFOKDO(err, newOp.load()) IFOK(err) { err = getDocument()->sendMessage(i18nc("An information message", "Operation '%1' has been inserted", newOp.getDisplayName()), SKGDocument::Positive); } } } } if (isWarnEnabled() && !err) { QDate nextDate = getDate(); if (QDate::currentDate() >= nextDate.addDays(-getWarnDays())) { SKGOperationObject op; err = getParentOperation(op); IFOK(err) { int nbdays = QDate::currentDate().daysTo(nextDate); if (nbdays > 0) { err = getDocument()->sendMessage(i18np("Operation '%2' will be inserted in one day", "Operation '%2' will be inserted in %1 days", nbdays, getDisplayName())); } } } } } return err; } SKGError SKGRecurrentOperationObject::process(SKGDocumentBank* iDocument, int& oNbInserted, bool iForce, QDate iDate) { SKGError err; oNbInserted = 0; // Get all operation with auto_write SKGListSKGObjectBase recuOps; if (iDocument != nullptr) { err = iDocument->getObjects(QStringLiteral("v_recurrentoperation"), QLatin1String(""), recuOps); } int nb = recuOps.count(); for (int i = 0; !err && i < nb; ++i) { SKGRecurrentOperationObject recu(recuOps.at(i)); int nbi = 0; err = recu.process(nbi, iForce, iDate); oNbInserted += nbi; } return err; } diff --git a/skgbankmodeler/skgrecurrentoperationobject.h b/skgbankmodeler/skgrecurrentoperationobject.h index d0b2e7f7a..79f459ea2 100644 --- a/skgbankmodeler/skgrecurrentoperationobject.h +++ b/skgbankmodeler/skgrecurrentoperationobject.h @@ -1,280 +1,286 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGRECURRENTOPERATIONOBJECT_H #define SKGRECURRENTOPERATIONOBJECT_H /** @file * This file defines classes SKGRecurrentOperationObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankmodeler_export.h" #include "skgobjectbase.h" class SKGOperationObject; class SKGDocumentBank; /** * This class manages recurrent operation object */ class SKGBANKMODELER_EXPORT SKGRecurrentOperationObject final : public SKGObjectBase { public: /** * This enumerate defines the period unit */ enum PeriodUnit {DAY = 0, /**< day */ WEEK = 1, /**< week */ MONTH = 2, /**< month */ YEAR = 3 /**< year */ }; /** * This enumerate defines the period unit */ Q_ENUM(PeriodUnit) /** * Default constructor */ explicit SKGRecurrentOperationObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier in @p iTable of the object */ explicit SKGRecurrentOperationObject(SKGDocument* iDocument, int iID = 0); /** * Copy constructor * @param iObject the object to copy */ explicit SKGRecurrentOperationObject(const SKGObjectBase& iObject); /** * Copy constructor * @param iObject the object to copy */ SKGRecurrentOperationObject(const SKGRecurrentOperationObject& iObject); /** * Operator affectation * @param iObject the object to copy */ SKGRecurrentOperationObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGRecurrentOperationObject& operator= (const SKGRecurrentOperationObject& iObject); + /** * Destructor */ virtual ~SKGRecurrentOperationObject(); /** * Get the parent operation * @param oOperation the parent operation * @return an object managing the error. * @see SKGError */ SKGError getParentOperation(SKGOperationObject& oOperation) const; /** * Set the parent operation * @param iOperation the parent operation * @return an object managing the error. * @see SKGError */ SKGError setParentOperation(const SKGOperationObject& iOperation); /** * Set the increment * @param iIncrement the number of @see setPeriodUnit * @return an object managing the error * @see SKGError */ SKGError setPeriodIncrement(int iIncrement); /** * Get the increment * @return the number */ int getPeriodIncrement() const; /** * Get the period unit of this recurrent operation * @return the status */ SKGRecurrentOperationObject::PeriodUnit getPeriodUnit() const; /** * Set the period unit of this recurrent operation * @param iPeriod the period unit * @return an object managing the error * @see SKGError */ SKGError setPeriodUnit(SKGRecurrentOperationObject::PeriodUnit iPeriod); /** * Set the number of days before term to create operation * @param iDays the number of days * @return an object managing the error * @see SKGError */ SKGError setAutoWriteDays(int iDays); /** * Get the number of days before term to create operation * @return the number of days */ int getAutoWriteDays() const; /** * Set the number of days before term to warn user * @param iDays the number of days * @return an object managing the error * @see SKGError */ SKGError setWarnDays(int iDays); /** * Get the number of days before term to warn user * @return the number of days */ int getWarnDays() const; /** * Set date of this recurrent operation * @param iDate the date * @return an object managing the error * @see SKGError */ // cppcheck-suppress passedByValue SKGError setDate(QDate iDate); /** * Get date of this recurrent operation * @return the date */ QDate getDate() const; /** * Get next date of this recurrent operation * @return the date */ QDate getNextDate() const; /** * Get all operations created by this recurrent operation * @param oOperations all operations * @return an object managing the error * @see SKGError */ SKGError getRecurredOperations(SKGListSKGObjectBase& oOperations) const; /** * To warn or not the end user * @param iWarn the warn: true or false * @return an object managing the error * @see SKGError */ SKGError warnEnabled(bool iWarn); /** * To know if the end user is warned or not * @return an object managing the error * @see SKGError */ bool isWarnEnabled() const; /** * To activate or not the auto write mode * @param iAutoWrite auto write mode: true or false * @return an object managing the error * @see SKGError */ SKGError autoWriteEnabled(bool iAutoWrite); /** * To know if auto write mode is enabled or not * @return an object managing the error * @see SKGError */ bool isAutoWriteEnabled() const; /** * To know if a time limit is enabled or not * @return an object managing the error * @see SKGError */ bool hasTimeLimit() const; /** * To enable / disable a time limit * @param iTimeLimit the time limit: true or false * @return an object managing the error * @see SKGError */ SKGError timeLimit(bool iTimeLimit); /** * Set the time limit * @param iTimeLimit the number of times operation will be inserted * @return an object managing the error * @see SKGError */ SKGError setTimeLimit(int iTimeLimit); /** * Set the time limit * @param iLastDate the last date of the operation will be inserted. setDate, setPeriodIncrement and setPeriodUnit must be used before. * @return an object managing the error * @see SKGError */ // cppcheck-suppress passedByValue SKGError setTimeLimit(QDate iLastDate); /** * Get the number of times operation will be inserted * @return the number of times */ int getTimeLimit() const; /** * Warn and/or create operations for this recurrent operation * @param oNbInserted number of operations inserted * @param iForce to force the insertion even if autowrite is not enable * @param iDate date limit for insertion * @return an object managing the error * @see SKGError */ // cppcheck-suppress passedByValue SKGError process(int& oNbInserted, bool iForce = false, QDate iDate = QDate::currentDate()); /** * Warn and/or create operations for all recurrent operations of the document * @param iDocument the document containing the object * @param oNbInserted number of operations inserted * @param iForce to force the insertion even if autowrite is not enable* * @param iDate date limit for insertion * @return an object managing the error * @see SKGError */ // cppcheck-suppress passedByValue static SKGError process(SKGDocumentBank* iDocument, int& oNbInserted, bool iForce = false, QDate iDate = QDate::currentDate()); }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGRecurrentOperationObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbankmodeler/skgruleobject.cpp b/skgbankmodeler/skgruleobject.cpp index 94b38dcf7..a9d301b0f 100644 --- a/skgbankmodeler/skgruleobject.cpp +++ b/skgbankmodeler/skgruleobject.cpp @@ -1,866 +1,872 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file defines classes SKGRuleObject. * * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgruleobject.h" #include #include #include "skgdocumentbank.h" #include "skgoperationobject.h" #include "skgtraces.h" #include "skgtransactionmng.h" #include "skgunitvalueobject.h" SKGRuleObject::SKGRuleObject() : SKGRuleObject(nullptr) {} SKGRuleObject::SKGRuleObject(SKGDocument* iDocument, int iID) : SKGObjectBase(iDocument, QStringLiteral("v_rule"), iID) {} SKGRuleObject::SKGRuleObject(const SKGRuleObject& iObject) = default; SKGRuleObject::SKGRuleObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("rule")) { copyFrom(iObject); } else { *this = SKGObjectBase(iObject.getDocument(), QStringLiteral("v_rule"), iObject.getID()); } } SKGRuleObject& SKGRuleObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGRuleObject& SKGRuleObject::operator= (const SKGRuleObject& iObject) +{ + copyFrom(iObject); + return *this; +} + SKGRuleObject::~SKGRuleObject() = default; QString SKGRuleObject::getDisplayName() const { return getSearchDescription(); } SKGError SKGRuleObject::bookmark(bool iBookmark) { return setAttribute(QStringLiteral("t_bookmarked"), iBookmark ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGRuleObject::isBookmarked() const { return getAttribute(QStringLiteral("t_bookmarked")) == QStringLiteral("Y"); } SKGError SKGRuleObject::save(bool iInsertOrUpdate, bool iReloadAfterSave) { // Do the save SKGError err = SKGObjectBase::save(iInsertOrUpdate, iReloadAfterSave); // Raise alarm if (!err && getActionType() == ALARM) { err = execute(); } return err; } SKGError SKGRuleObject::setXMLSearchDefinition(const QString& iXml) { setSearchDescription(SKGRuleObject::getDescriptionFromXML(getDocument(), iXml, false, SEARCH)); return setAttribute(QStringLiteral("t_definition"), iXml); } QString SKGRuleObject::getXMLSearchDefinition() const { return getAttribute(QStringLiteral("t_definition")); } SKGError SKGRuleObject::setXMLActionDefinition(const QString& iXml) { setActionDescription(SKGRuleObject::getDescriptionFromXML(getDocument(), iXml, false, getActionType())); return setAttribute(QStringLiteral("t_action_definition"), iXml); } QString SKGRuleObject::getXMLActionDefinition() const { return getAttribute(QStringLiteral("t_action_definition")); } SKGError SKGRuleObject::setSearchDescription(const QString& iDescription) { return setAttribute(QStringLiteral("t_description"), iDescription); } QString SKGRuleObject::getSearchDescription() const { return getAttribute(QStringLiteral("t_description")); } SKGError SKGRuleObject::setActionDescription(const QString& iDescription) { return setAttribute(QStringLiteral("t_action_description"), iDescription); } QString SKGRuleObject::getActionDescription() const { return getAttribute(QStringLiteral("t_action_description")); } SKGError SKGRuleObject::setActionType(SKGRuleObject::ActionType iType) { SKGError err = setAttribute(QStringLiteral("t_action_type"), (iType == SEARCH ? QStringLiteral("S") : (iType == UPDATE ? QStringLiteral("U") : (iType == APPLYTEMPLATE ? QStringLiteral("T") : QStringLiteral("A"))))); return err; } SKGRuleObject::ActionType SKGRuleObject::getActionType() const { QString typeString = getAttribute(QStringLiteral("t_action_type")); return (typeString == QStringLiteral("S") ? SEARCH : (typeString == QStringLiteral("U") ? UPDATE : (typeString == QStringLiteral("T") ? APPLYTEMPLATE : ALARM))); } SKGError SKGRuleObject::setOrder(double iOrder) { SKGError err; double order = iOrder; if (order == -1) { order = 1; SKGStringListList result; err = getDocument()->executeSelectSqliteOrder(QStringLiteral("SELECT max(f_sortorder) from rule"), result); if (!err && result.count() == 2) { order = SKGServices::stringToDouble(result.at(1).at(0)) + 1; } } IFOKDO(err, setAttribute(QStringLiteral("f_sortorder"), SKGServices::doubleToString(order))) return err; } QString SKGRuleObject::getSelectSqlOrder(const QString& iAdditionalCondition) const { QString wc = iAdditionalCondition; QString wc2 = SKGRuleObject::getDescriptionFromXML(getDocument(), getXMLSearchDefinition(), true, SEARCH); if (!wc2.isEmpty()) { if (wc.isEmpty()) { wc = wc2; } else { wc = '(' % wc % ") AND (" % wc2 % ')'; } } if (wc.isEmpty()) { wc = QStringLiteral("1=1"); } wc = "t_template='N' AND d_date!='0000-00-00' AND (" % wc % ')'; return wc; } SKGRuleObject::SKGAlarmInfo SKGRuleObject::getAlarmInfo() const { SKGTRACEINFUNC(10) SKGRuleObject::SKGAlarmInfo alarm; alarm.Raised = false; alarm.Message = QLatin1String(""); alarm.Amount = 0.0; alarm.Limit = 0.0; if (getActionType() == SKGRuleObject::ALARM) { // Alarm mode QString wc = getSelectSqlOrder(); if (wc.isEmpty()) { wc = QStringLiteral("1=1"); } SKGDocument* doc = getDocument(); QStringList list = SKGRuleObject::getFromXML(doc, getXMLActionDefinition(), true, ALARM, false); if (!list.isEmpty()) { QString sql = list.at(0); sql.replace(QStringLiteral("#WC#"), wc); SKGStringListList result; doc->executeSelectSqliteOrder(sql, result); if (result.count() == 2) { const auto& r = result.at(1); alarm.Raised = (r.at(0) == QStringLiteral("1")); alarm.Message = r.at(3); alarm.Amount = SKGServices::stringToDouble(r.at(1)); alarm.Limit = SKGServices::stringToDouble(r.at(2)); } } } return alarm; } SKGError SKGRuleObject::execute(ProcessMode iMode) { SKGError err; SKGTRACEINFUNCRC(10, err) if (getActionType() == SKGRuleObject::UPDATE) { // Update mode QString addSql; if (iMode == IMPORTED) { addSql = QStringLiteral("t_imported!='N'"); } else if (iMode == IMPORTEDNOTVALIDATE) { addSql = QStringLiteral("t_imported='P'"); } else if (iMode == IMPORTING) { addSql = QStringLiteral("t_imported='T'"); } else if (iMode == NOTCHECKED) { addSql = QStringLiteral("t_status!='Y'"); } QString wc = getSelectSqlOrder(addSql); SKGDocument* doc = getDocument(); if (doc != nullptr) { QStringList list = SKGRuleObject::getFromXML(doc, getXMLActionDefinition(), true, UPDATE, true); // SQL + SET clause int nb = list.count(); err = doc->beginTransaction("#INTERNAL#" % i18nc("Progression step", "Apply rule"), nb); IFOK(err) { // All sql orders must be executed to be sure than i_tmp is reset SKGError err2; for (int i = 0; i < nb; ++i) { QString sql = list.at(i); sql.replace(QStringLiteral("#WC#"), wc); err2 = doc->executeSqliteOrder(sql); if (!err2) { err2 = doc->stepForward(i + 1); } if (err2 && !err) { err = err2; } } } IFOK(err) { SKGStringListList result; err = doc->executeSelectSqliteOrder(QStringLiteral("SELECT changes()"), result); if (!err && result.count() == 2) { int nbChanges = SKGServices::stringToInt(result.at(1).at(0)); if (nbChanges != 0) { doc->sendMessage(i18np("1 operation modified by %2", "%1 operations modified by %2", nbChanges, getAttribute(QStringLiteral("i_ORDER")))); } } } SKGENDTRANSACTION(doc, err) } } else if (getActionType() == SKGRuleObject::ALARM) { // Alarm mode auto doc = qobject_cast(getDocument()); if (doc != nullptr) { SKGRuleObject::SKGAlarmInfo alarm = getAlarmInfo(); if (!alarm.Message.isEmpty()) { SKGServices::SKGUnitInfo unit = doc->getPrimaryUnit(); // Build the message if (alarm.Message.contains(QLatin1String("%3"))) { alarm.Message = alarm.Message.arg(doc->formatMoney(alarm.Amount, unit, false), doc->formatMoney(alarm.Limit, unit, false), doc->formatMoney(alarm.Amount - alarm.Limit, unit, false)); } else if (alarm.Message.contains(QLatin1String("%2"))) { alarm.Message = alarm.Message.arg(doc->formatMoney(alarm.Amount, unit, false), doc->formatMoney(alarm.Limit, unit, false)); } else if (alarm.Message.contains(QLatin1String("%1"))) { alarm.Message = alarm.Message.arg(doc->formatMoney(alarm.Amount, unit, false)); } getDocument()->sendMessage(alarm.Message); } } } else if (getActionType() == SKGRuleObject::APPLYTEMPLATE) { // Template mode QString addSql; if (iMode == IMPORTED) { addSql = QStringLiteral("t_imported!='N'"); } else if (iMode == IMPORTEDNOTVALIDATE) { addSql = QStringLiteral("t_imported='P'"); } else if (iMode == IMPORTING) { addSql = QStringLiteral("t_imported='T'"); } QString wc = getSelectSqlOrder(addSql); auto doc = qobject_cast(getDocument()); if (doc != nullptr) { QStringList list = SKGRuleObject::getFromXML(doc, getXMLActionDefinition(), true, APPLYTEMPLATE, false); if (!list.isEmpty()) { const QString& id = list.at(0); // Get template SKGOperationObject templateToApply(doc, SKGServices::stringToInt(id)); // Get operations to modify SKGObjectBase::SKGListSKGObjectBase objectsToModify; IFOKDO(err, doc->getObjects(QStringLiteral("v_operation_prop"), wc, objectsToModify)) int nb = objectsToModify.count(); for (int i = 0; !err && i < nb; ++i) { SKGOperationObject operationObj(doc, SKGServices::stringToInt(objectsToModify.at(i).getAttribute(QStringLiteral("i_OPID")))); SKGOperationObject op; IFOKDO(err, templateToApply.duplicate(op)) IFOKDO(err, op.mergeAttribute(operationObj, SKGOperationObject::PROPORTIONAL, false)) } } } } IFKO(err) err.addError(ERR_FAIL, i18nc("Error message", "Rule %1 failed", getAttribute(QStringLiteral("i_ORDER")))); return err; } double SKGRuleObject::getOrder() const { return SKGServices::stringToDouble(getAttribute(QStringLiteral("f_sortorder"))); } QString SKGRuleObject::getDisplayForOperator(const QString& iOperator, const QString& iParam1, const QString& iParam2, const QString& iAtt2) { QString output = iOperator; if (output == QStringLiteral("#ATT# LIKE '%#V1S#%'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "contains '#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT# NOT LIKE '%#V1S#%'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "does not contain '#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT# LIKE '#V1S#%'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "starts with '#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT# NOT LIKE '#V1S#%'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "does not start with '#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT# LIKE '%#V1S#'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "ends with '#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT# NOT LIKE '%#V1S#'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "does not end with '#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT#=''")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is empty"); } else if (output == QStringLiteral("#ATT#!=''")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is not empty"); } else if (output == QStringLiteral("#ATT#=REGEXPCAPTURE('#V1S#', #ATT2#, #V2#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=regexpcapture(#ATT2#, '#V1S#', #V2#)").replace(QStringLiteral("#V1S#"), iParam1).replace(QStringLiteral("#V2#"), iParam2).replace(QStringLiteral("#ATT2#"), iAtt2); } else if (output == QStringLiteral("REGEXP('#V1S#', #ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "regexp '#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("NOT(REGEXP('#V1S#', #ATT#))")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "not regexp '#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("WILDCARD('#V1S#', #ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "wildcard '#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("NOT(WILDCARD('#V1S#', #ATT#))")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "not wildcard '#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT#=#V1#")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=#V1#").replace(QStringLiteral("#V1#"), iParam1); } else if (output == QStringLiteral("#ATT#!=#V1#")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "!=#V1#").replace(QStringLiteral("#V1#"), iParam1); } else if (output == QStringLiteral("#ATT#>#V1#")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", ">#V1#").replace(QStringLiteral("#V1#"), iParam1); } else if (output == QStringLiteral("#ATT#<#V1#")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "<#V1#").replace(QStringLiteral("#V1#"), iParam1); } else if (output == QStringLiteral("#ATT#>=#V1#")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", ">=#V1#").replace(QStringLiteral("#V1#"), iParam1); } else if (output == QStringLiteral("#ATT#<=#V1#")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "<=#V1#").replace(QStringLiteral("#V1#"), iParam1); } else if (output == QStringLiteral("#ATT#='#V1S#'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "='#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT#!='#V1S#'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "!='#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT#>'#V1S#'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", ">'#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT#<'#V1S#'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "<'#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT#>='#V1S#'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", ">='#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT#<='#V1S#'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "<='#V1S#'").replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT#>=#V1# AND #ATT#<=#V2#")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is between #V1# and #V2#").replace(QStringLiteral("#V1#"), iParam1).replace(QStringLiteral("#V2#"), iParam2); } else if (output == QStringLiteral("((#ATT#>='#V1S#' AND #ATT#<='#V2S#') OR (#ATT#>='#V2S#' AND #ATT#<='#V1S#'))")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is between '#V1S#' and '#V2S#'").replace(QStringLiteral("#V1S#"), iParam1).replace(QStringLiteral("#V2S#"), iParam2); } else if (output == QStringLiteral("#ATT#=lower(#ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is set to lower"); } else if (output == QStringLiteral("#ATT#=upper(#ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is set to upper"); } else if (output == QStringLiteral("#ATT#=capitalize(#ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is set to capitalize"); } else if (output == QStringLiteral("#ATT#!=lower(#ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is not lower"); } else if (output == QStringLiteral("#ATT#!=upper(#ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is not upper"); } else if (output == QStringLiteral("#ATT#!=capitalize(#ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is not capitalize"); } else if (output == QStringLiteral("#ATT#= lower(#ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is lower"); } else if (output == QStringLiteral("#ATT#= upper(#ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is upper"); } else if (output == QStringLiteral("#ATT#= capitalize(#ATT#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is capitalize"); } else if (output == QStringLiteral("#ATT#=replace(#ATT2#,'#V1S#','#V2S#')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=#ATT2# with '#V1S#' replaced by '#V2S#'").replace(QStringLiteral("#V1S#"), iParam1).replace(QStringLiteral("#V2S#"), iParam2).replace(QStringLiteral("#ATT2#"), iAtt2); } else if (output == QStringLiteral("#ATT#=substr(#ATT2#,'#V1#','#V2#')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=substring of #ATT2# from #V1# to #V2#").replace(QStringLiteral("#V1#"), iParam1).replace(QStringLiteral("#V2#"), iParam2).replace(QStringLiteral("#ATT2#"), iAtt2); } else if (output == QStringLiteral("#ATT#=#ATT2#")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=#ATT2#").replace(QStringLiteral("#ATT2#"), iAtt2); } else if (output == QStringLiteral("#ATT#=WORD(#ATT2#,#V1S#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=word(#ATT2#,#V1S#)").replace(QStringLiteral("#ATT2#"), iAtt2).replace(QStringLiteral("#V1S#"), iParam1); } else if (output == QStringLiteral("#ATT#=todate(#ATT2#,'#DF#')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=#ATT2# as date with format #DF#").replace(QStringLiteral("#ATT2#"), iAtt2).replace(QStringLiteral("#DF#"), iParam2); } else if (output == QStringLiteral("#ATT#=todate(WORD(#ATT2#,#V1S#),'#DF#')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=word(#ATT2#,#V1S#) as date with format #DF#").replace(QStringLiteral("#ATT2#"), iAtt2).replace(QStringLiteral("#V1S#"), iParam1).replace(QStringLiteral("#DF#"), iParam2); } else if (output == QStringLiteral("STRFTIME('%Y-%m',#ATT#)=STRFTIME('%Y-%m',date('now'))")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in current month"); } else if (output == QStringLiteral("STRFTIME('%Y-%m',#ATT#)=STRFTIME('%Y-%m',date('now','start of month','-1 month'))")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in previous month"); } else if (output == QStringLiteral("STRFTIME('%Y',#ATT#)=STRFTIME('%Y',date('now'))")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in current year"); } else if (output == QStringLiteral("STRFTIME('%Y',#ATT#)=STRFTIME('%Y',date('now','start of month','-1 year'))")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in previous year"); } else if (output == QStringLiteral("#ATT#>date('now','-30 day') AND #ATT#<=date('now')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 30 days"); } else if (output == QStringLiteral("#ATT#>date('now','-3 month') AND #ATT#<=date('now')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 3 months"); } else if (output == QStringLiteral("#ATT#>date('now','-6 month') AND #ATT#<=date('now')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 6 months"); } else if (output == QStringLiteral("#ATT#>date('now','-12 month') AND #ATT#<=date('now')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 12 months"); } else if (output == QStringLiteral("#ATT#>date('now','-2 year') AND #ATT#<=date('now')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 2 years"); } else if (output == QStringLiteral("#ATT#>date('now','-3 year') AND #ATT#<=date('now')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 3 years"); } else if (output == QStringLiteral("#ATT#>date('now','-5 year') AND #ATT#<=date('now')")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 5 years"); } else if (output == QStringLiteral("ABS(TOTAL(#ATT#))#OP##V1#,ABS(TOTAL(#ATT#)), #V1#, '#V2S#'")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "If total(#ATT#)#OP##V1# then send '#V2S#'").replace(QStringLiteral("#V1#"), iParam1).replace(QStringLiteral("#V2S#"), iParam2).replace(QStringLiteral("#OP#"), iAtt2); } else if (output == QStringLiteral("APPLYTEMPLATE(#V1#)")) { output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "Apply the template '#V2S#'").replace(QStringLiteral("#V2S#"), iParam2); } return output; } QString SKGRuleObject::getToolTipForOperator(const QString& iOperator) { QString output = iOperator; if (output == QStringLiteral("#ATT# LIKE '%#V1S#%'")) { output = i18nc("Help for a condition", "To find out if the attribute contains a given string"); } else if (output == QStringLiteral("#ATT# NOT LIKE '%#V1S#%'")) { output = i18nc("Help for a condition", "To find out if the attribute doesn't contain a given string"); } else if (output == QStringLiteral("#ATT# LIKE '#V1S#%'")) { output = i18nc("Help for a condition", "To find out if the attribute is starting by a given string"); } else if (output == QStringLiteral("#ATT# NOT LIKE '#V1S#%'")) { output = i18nc("Help for a condition", "To find out if the attribute is not starting by a given string"); } else if (output == QStringLiteral("#ATT# LIKE '%#V1S#'")) { output = i18nc("Help for a condition", "To find out if the attribute is ending by a given string"); } else if (output == QStringLiteral("#ATT# NOT LIKE '%#V1S#'")) { output = i18nc("Help for a condition", "To find out if the attribute is not ending by a given string"); } else if (output == QStringLiteral("#ATT#=''")) { output = i18nc("Help for a condition", "To find out if the attribute is empty"); } else if (output == QStringLiteral("#ATT#!=''")) { output = i18nc("Help for a condition", "To find out if the attribute is not empty"); } else if (output == QStringLiteral("#ATT#=REGEXPCAPTURE('#V1S#', #ATT2#, #V2#)")) { output = i18nc("Help for a condition", "To get a captured value by a given regular expression (eg. \"(\\d+)(\\s*)(cm|inch(es)?)\")"); } else if (output == QStringLiteral("REGEXP('#V1S#', #ATT#)")) { output = i18nc("Help for a condition", "To find out if the attribute is matching a given regular expression (eg. \"^[H,h]ello$\")"); } else if (output == QStringLiteral("NOT(REGEXP('#V1S#', #ATT#))")) { output = i18nc("Help for a condition", "To find out if the attribute is not matching a given regular expression (eg. \"^[H,h]ello$\")"); } else if (output == QStringLiteral("WILDCARD('#V1S#', #ATT#)")) { output = i18nc("Help for a condition", "To find out if the attribute is matching a given wildcard expression (eg. \"_ello\")"); } else if (output == QStringLiteral("NOT(WILDCARD('#V1S#', #ATT#))")) { output = i18nc("Help for a condition", "To find out if the attribute is not matching a given wildcard expression (eg. \"_ello\")"); } else if (output == QStringLiteral("#ATT#=#V1#")) { output = i18nc("Help for a condition", "To find out if the attribute is equal to a given value"); } else if (output == QStringLiteral("#ATT#!=#V1#")) { output = i18nc("Help for a condition", "To find out if the attribute is not equal to a given value"); } else if (output == QStringLiteral("#ATT#>#V1#")) { output = i18nc("Help for a condition", "To find out if the attribute is greater than a given value"); } else if (output == QStringLiteral("#ATT#<#V1#")) { output = i18nc("Help for a condition", "To find out if the attribute is smaller than a given value"); } else if (output == QStringLiteral("#ATT#>=#V1#")) { output = i18nc("Help for a condition", "To find out if the attribute is greater or equal to a given value"); } else if (output == QStringLiteral("#ATT#<=#V1#")) { output = i18nc("Help for a condition", "To set the attribute with a given value"); } else if (output == QStringLiteral("#ATT#='#V1S#'")) { output = i18nc("Help for a condition", "To find out if the attribute is equal to a given string"); } else if (output == QStringLiteral("#ATT#!='#V1S#'")) { output = i18nc("Help for a condition", "To find out if the attribute is not equal to a given string"); } else if (output == QStringLiteral("#ATT#>'#V1S#'")) { output = i18nc("Help for a condition", "To find out if the attribute is greater than a given string"); } else if (output == QStringLiteral("#ATT#<'#V1S#'")) { output = i18nc("Help for a condition", "To find out if the attribute is smaller than a given string"); } else if (output == QStringLiteral("#ATT#>='#V1S#'")) { output = i18nc("Help for a condition", "To find out if the attribute is greater or equal to a given string"); } else if (output == QStringLiteral("#ATT#<='#V1S#'")) { output = i18nc("Help for a condition", "To find out if the attribute is smaller or equal to a given string"); } else if (output == QStringLiteral("#ATT#>=#V1# AND #ATT#<=#V2#")) { output = i18nc("Help for a condition", "To find out if the attribute is between two given values"); } else if (output == QStringLiteral("((#ATT#>='#V1S#' AND #ATT#<='#V2S#') OR (#ATT#>='#V2S#' AND #ATT#<='#V1S#'))")) { output = i18nc("Help for a condition", "To find out if the attribute is between two given strings"); } else if (output == QStringLiteral("#ATT#=lower(#ATT#)")) { output = i18nc("Help for a condition", "To set the attribute in lower case (eg. hello)"); } else if (output == QStringLiteral("#ATT#=upper(#ATT#)")) { output = i18nc("Help for a condition", "To set the attribute in upper case (eg. HELLO)"); } else if (output == QStringLiteral("#ATT#=capitalize(#ATT#)")) { output = i18nc("Help for a condition", "To set the attribute in capitalized case (eg. Hello)"); } else if (output == QStringLiteral("#ATT#!=lower(#ATT#)")) { output = i18nc("Help for a condition", "To find out if the attribute is not in lower case (eg. hello)"); } else if (output == QStringLiteral("#ATT#!=upper(#ATT#)")) { output = i18nc("Help for a condition", "To find out if the attribute is not in upper case (eg. HELLO)"); } else if (output == QStringLiteral("#ATT#!=capitalize(#ATT#)")) { output = i18nc("Help for a condition", "To find out if the attribute is not in capitalized case (eg. Hello)"); } else if (output == QStringLiteral("#ATT#= lower(#ATT#)")) { output = i18nc("Help for a condition", "To find out if the attribute is in lower case (eg. hello)"); } else if (output == QStringLiteral("#ATT#= upper(#ATT#)")) { output = i18nc("Help for a condition", "To find out if the attribute is in upper case (eg. HELLO)"); } else if (output == QStringLiteral("#ATT#= capitalize(#ATT#)")) { output = i18nc("Help for a condition", "To find out if the attribute is in capitalized case (eg. Hello)"); } else if (output == QStringLiteral("#ATT#=replace(#ATT2#,'#V1S#','#V2S#')")) { output = i18nc("Help for a condition", "To set the attribute with the value of another attribute where a value is replaced by another one"); } else if (output == QStringLiteral("#ATT#=substr(#ATT2#,'#V1#','#V2#')")) { output = i18nc("Help for a condition", "To set the attribute with a part of the value of another attribute"); } else if (output == QStringLiteral("#ATT#=#ATT2#")) { output = i18nc("Help for a condition", "To set the attribute with the value of another attribute"); } else if (output == QStringLiteral("#ATT#=WORD(#ATT2#,#V1S#)")) { output = i18nc("Help for a condition", "To set the attribute with a word of the value of another attribute converted in date format"); } else if (output == QStringLiteral("#ATT#=todate(#ATT2#,'#DF#')")) { output = i18nc("Help for a condition", "To set the date attribute with the value of another attribute"); } else if (output == QStringLiteral("#ATT#=todate(WORD(#ATT2#,#V1S#),'#DF#')")) { output = i18nc("Help for a condition", "To set the date attribute with a word of another attribute converted in date format"); } else if (output == QStringLiteral("STRFTIME('%Y-%m',#ATT#)=STRFTIME('%Y-%m',date('now'))")) { output = i18nc("Help for a condition", "To find out if the date of the operation is today"); } else if (output == QStringLiteral("STRFTIME('%Y-%m',#ATT#)=STRFTIME('%Y-%m',date('now','start of month','-1 month'))")) { output = i18nc("Help for a condition", "To find out if the date of the operation is in previous month"); } else if (output == QStringLiteral("STRFTIME('%Y',#ATT#)=STRFTIME('%Y',date('now'))")) { output = i18nc("Help for a condition", "To find out if the date of the operation is in current year"); } else if (output == QStringLiteral("STRFTIME('%Y',#ATT#)=STRFTIME('%Y',date('now','start of month','-1 year'))")) { output = i18nc("Help for a condition", "To find out if the date of the operation is in previous year"); } else if (output == QStringLiteral("#ATT#>date('now','-30 day') AND #ATT#<=date('now')")) { output = i18nc("Help for a condition", "To find out if the date of the operation is in last 30 days"); } else if (output == QStringLiteral("#ATT#>date('now','-3 month') AND #ATT#<=date('now')")) { output = i18nc("Help for a condition", "To find out if the date of the operation is in last 3 months"); } else if (output == QStringLiteral("#ATT#>date('now','-6 month') AND #ATT#<=date('now')")) { output = i18nc("Help for a condition", "To find out if the date of the operation is in last 6 months"); } else if (output == QStringLiteral("#ATT#>date('now','-12 month') AND #ATT#<=date('now')")) { output = i18nc("Help for a condition", "To find out if the date of the operation is in last 12 months"); } else if (output == QStringLiteral("#ATT#>date('now','-2 year') AND #ATT#<=date('now')")) { output = i18nc("Help for a condition", "To find out if the date of the operation is in last 2 years"); } else if (output == QStringLiteral("#ATT#>date('now','-3 year') AND #ATT#<=date('now')")) { output = i18nc("Help for a condition", "To find out if the date of the operation is in last 3 years"); } else if (output == QStringLiteral("#ATT#>date('now','-5 year') AND #ATT#<=date('now')")) { output = i18nc("Help for a condition", "To find out if the date of the operation is in last 5 years"); } return output; } QStringList SKGRuleObject::getListOfOperators(SKGServices::AttributeType iAttributeType, SKGRuleObject::ActionType iType) { QStringList output; if (iType == UPDATE) { // Mode update if (iAttributeType == SKGServices::TEXT) { output.push_back(QStringLiteral("#ATT#='#V1S#'")); output.push_back(QStringLiteral("#ATT#=lower(#ATT#)")); output.push_back(QStringLiteral("#ATT#=upper(#ATT#)")); output.push_back(QStringLiteral("#ATT#=capitalize(#ATT#)")); output.push_back(QStringLiteral("#ATT#=replace(#ATT2#,'#V1S#','#V2S#')")); output.push_back(QStringLiteral("#ATT#=REGEXPCAPTURE('#V1S#', #ATT2#, #V2#)")); } else if (iAttributeType == SKGServices::INTEGER || iAttributeType == SKGServices::FLOAT) { output.push_back(QStringLiteral("#ATT#=#V1#")); } else if (iAttributeType == SKGServices::DATE || iAttributeType == SKGServices::BOOL || iAttributeType == SKGServices::TRISTATE) { output.push_back(QStringLiteral("#ATT#='#V1S#'")); } if (iAttributeType == SKGServices::DATE) { output.push_back(QStringLiteral("#ATT#=todate(#ATT2#,'#DF#')")); output.push_back(QStringLiteral("#ATT#=todate(WORD(#ATT2#,#V1S#),'#DF#')")); } else if (iAttributeType != SKGServices::BOOL && iAttributeType != SKGServices::TRISTATE) { output.push_back(QStringLiteral("#ATT#=substr(#ATT2#,'#V1#','#V2#')")); output.push_back(QStringLiteral("#ATT#=#ATT2#")); output.push_back(QStringLiteral("#ATT#=WORD(#ATT2#,#V1S#)")); } } else if (iType == SEARCH) { // Mode query if (iAttributeType == SKGServices::TEXT) { output.push_back(QStringLiteral("#ATT# LIKE '%#V1S#%'")); output.push_back(QStringLiteral("#ATT# NOT LIKE '%#V1S#%'")); output.push_back(QStringLiteral("#ATT# LIKE '#V1S#%'")); output.push_back(QStringLiteral("#ATT# NOT LIKE '#V1S#%'")); output.push_back(QStringLiteral("#ATT# LIKE '%#V1S#'")); output.push_back(QStringLiteral("#ATT# NOT LIKE '%#V1S#'")); output.push_back(QStringLiteral("#ATT#=''")); output.push_back(QStringLiteral("#ATT#!=''")); output.push_back(QStringLiteral("#ATT#= lower(#ATT#)")); output.push_back(QStringLiteral("#ATT#!=lower(#ATT#)")); output.push_back(QStringLiteral("#ATT#= upper(#ATT#)")); output.push_back(QStringLiteral("#ATT#!=upper(#ATT#)")); output.push_back(QStringLiteral("#ATT#= capitalize(#ATT#)")); output.push_back(QStringLiteral("#ATT#!=capitalize(#ATT#)")); output.push_back(QStringLiteral("REGEXP('#V1S#', #ATT#)")); output.push_back(QStringLiteral("NOT(REGEXP('#V1S#', #ATT#))")); output.push_back(QStringLiteral("WILDCARD('#V1S#', #ATT#)")); output.push_back(QStringLiteral("NOT(WILDCARD('#V1S#', #ATT#))")); } if (iAttributeType == SKGServices::INTEGER || iAttributeType == SKGServices::FLOAT) { output.push_back(QStringLiteral("#ATT#=#V1#")); output.push_back(QStringLiteral("#ATT#!=#V1#")); output.push_back(QStringLiteral("#ATT#>#V1#")); output.push_back(QStringLiteral("#ATT#<#V1#")); output.push_back(QStringLiteral("#ATT#>=#V1#")); output.push_back(QStringLiteral("#ATT#<=#V1#")); output.push_back(QStringLiteral("#ATT#>=#V1# AND #ATT#<=#V2#")); } if (iAttributeType == SKGServices::BOOL || iAttributeType == SKGServices::TRISTATE || iAttributeType == SKGServices::TEXT || iAttributeType == SKGServices::DATE) { output.push_back(QStringLiteral("#ATT#='#V1S#'")); } if (iAttributeType == SKGServices::TRISTATE || iAttributeType == SKGServices::TEXT || iAttributeType == SKGServices::DATE) { output.push_back(QStringLiteral("#ATT#!='#V1S#'")); } if (iAttributeType == SKGServices::TEXT || iAttributeType == SKGServices::DATE) { output.push_back(QStringLiteral("#ATT#>'#V1S#'")); output.push_back(QStringLiteral("#ATT#<'#V1S#'")); output.push_back(QStringLiteral("#ATT#>='#V1S#'")); output.push_back(QStringLiteral("#ATT#<='#V1S#'")); output.push_back(QStringLiteral("((#ATT#>='#V1S#' AND #ATT#<='#V2S#') OR (#ATT#>='#V2S#' AND #ATT#<='#V1S#'))")); } if (iAttributeType == SKGServices::DATE) { output.push_back(QStringLiteral("STRFTIME('%Y-%m',#ATT#)=STRFTIME('%Y-%m',date('now'))")); output.push_back(QStringLiteral("STRFTIME('%Y-%m',#ATT#)=STRFTIME('%Y-%m',date('now','start of month','-1 month'))")); output.push_back(QStringLiteral("STRFTIME('%Y',#ATT#)=STRFTIME('%Y',date('now'))")); output.push_back(QStringLiteral("STRFTIME('%Y',#ATT#)=STRFTIME('%Y',date('now','start of month','-1 year'))")); output.push_back(QStringLiteral("#ATT#>date('now','-30 day') AND #ATT#<=date('now')")); output.push_back(QStringLiteral("#ATT#>date('now','-3 month') AND #ATT#<=date('now')")); output.push_back(QStringLiteral("#ATT#>date('now','-6 month') AND #ATT#<=date('now')")); output.push_back(QStringLiteral("#ATT#>date('now','-12 month') AND #ATT#<=date('now')")); output.push_back(QStringLiteral("#ATT#>date('now','-2 year') AND #ATT#<=date('now')")); output.push_back(QStringLiteral("#ATT#>date('now','-3 year') AND #ATT#<=date('now')")); output.push_back(QStringLiteral("#ATT#>date('now','-5 year') AND #ATT#<=date('now')")); } } else if (iType == ALARM) { output.push_back(QStringLiteral("ABS(TOTAL(#ATT#))#OP##V1#,ABS(TOTAL(#ATT#)), #V1#, '#V2S#'")); } else if (iType == APPLYTEMPLATE) { output.push_back(QStringLiteral("APPLYTEMPLATE(#V1#)")); } return output; } QStringList SKGRuleObject::getFromXML(SKGDocument* iDocument, const QString& iXML, bool iSQL, SKGRuleObject::ActionType iType, bool iFullUpdate) { QStringList output; if (iFullUpdate) { // Add markers output.push_back(QStringLiteral("UPDATE v_operation_prop set i_tmp=1 WHERE #WC#")); } QDomDocument doc(QStringLiteral("SKGML")); doc.setContent(iXML); QDomElement root = doc.documentElement(); if (root.tagName() == QStringLiteral("element") || iType != SEARCH) { // Mode advanced QDomNode l = root.firstChild(); while (!l.isNull()) { QDomElement elementl = l.toElement(); if (!elementl.isNull()) { QString lineDescription; QDomNode n = elementl.firstChild(); bool parenthesisNeeded = false; while (!n.isNull()) { QDomElement element = n.toElement(); if (!element.isNull()) { // Build element description QString elementDescription; QString attribute = element.attribute(QStringLiteral("attribute")); QString propName; if (iSQL) { attribute = SKGServices::stringToSqlString(attribute); if (attribute.startsWith(QLatin1String("p_"))) { // Case property propName = attribute.right(attribute.length() - 2); if (iType == SEARCH) { attribute = "i_PROPPNAME='" % SKGServices::stringToSqlString(propName) % "' AND i_PROPVALUE"; } else { attribute = QStringLiteral("t_value"); } } QString part = element.attribute(QStringLiteral("operator")); part = part.replace(QStringLiteral("#V1#"), SKGServices::stringToSqlString(element.attribute(QStringLiteral("value")))); part = part.replace(QStringLiteral("#V1S#"), SKGServices::stringToSqlString(element.attribute(QStringLiteral("value")))); part = part.replace(QStringLiteral("#V2#"), SKGServices::stringToSqlString(element.attribute(QStringLiteral("value2")))); part = part.replace(QStringLiteral("#V2S#"), SKGServices::stringToSqlString(element.attribute(QStringLiteral("value2")))); part = part.replace(QStringLiteral("#DF#"), SKGServices::stringToSqlString(element.attribute(QStringLiteral("value2")))); part = part.replace(QStringLiteral("#OP#"), SKGServices::stringToSqlString(element.attribute(QStringLiteral("operator2")))); elementDescription += part; } else { attribute = "operation." % attribute; if (iDocument != nullptr) { attribute = iDocument->getDisplay(attribute); } if (iType != ALARM && iType != APPLYTEMPLATE) { elementDescription = attribute; elementDescription += ' '; } QString tmp = element.attribute(QStringLiteral("att2s")); if (tmp.isEmpty()) { tmp = element.attribute(QStringLiteral("operator2")); } QString part = SKGRuleObject::getDisplayForOperator(element.attribute(QStringLiteral("operator")), element.attribute(QStringLiteral("value")), element.attribute(QStringLiteral("value2")), tmp); elementDescription += part; } elementDescription = elementDescription.replace(QStringLiteral("#ATT#"), attribute); // Att2 QString attribute2 = element.attribute(QStringLiteral("att2")); if (iSQL) { attribute2 = SKGServices::stringToSqlString(attribute2); if (attribute2.startsWith(QLatin1String("p_"))) { QString propertyName = attribute2.right(attribute2.length() - 2); attribute2 = "IFNULL((SELECT op2.i_PROPVALUE FROM v_operation_prop op2 WHERE op2.id=v_operation_prop.id AND op2.i_PROPPNAME='" % SKGServices::stringToSqlString(propertyName) % "'),'')"; } if (element.attribute(QStringLiteral("attribute")).startsWith(QLatin1String("p_"))) { attribute2 = "(SELECT " % attribute2 % " FROM v_operation_prop WHERE i_PROPPID=parameters.id)"; } } elementDescription = elementDescription.replace(QStringLiteral("#ATT2#"), attribute2); // Add it to line description if (iSQL) { if (iType == UPDATE) { if (!iFullUpdate) { output.push_back(elementDescription); } else { if (attribute == QStringLiteral("t_REALCATEGORY")) { elementDescription.replace(attribute, QStringLiteral("t_fullname")); QString parentcat; QString cat = element.attribute(QStringLiteral("value")); bool stop = false; while (!stop) { int posSeparator = cat.indexOf(OBJECTSEPARATOR); if (posSeparator == -1) { if (parentcat.isEmpty()) { output.push_back(QStringLiteral("UPDATE category SET rd_category_id=0 WHERE rd_category_id IS NULL OR rd_category_id=''")); output.push_back("INSERT OR IGNORE INTO category (t_name, rd_category_id) VALUES ('" % SKGServices::stringToSqlString(cat) % "', 0)"); } else { output.push_back("INSERT OR IGNORE INTO category (t_name, rd_category_id) VALUES ('" % SKGServices::stringToSqlString(cat) % "',(SELECT id FROM category WHERE t_fullname='" % SKGServices::stringToSqlString(parentcat) % "'))"); } stop = true; } else { // Get first and second parts of the branch QString first = cat.mid(0, posSeparator); QString second = cat.mid(posSeparator + QString(OBJECTSEPARATOR).length(), cat.length() - posSeparator - QString(OBJECTSEPARATOR).length()); if (parentcat.isEmpty()) { output.push_back(QStringLiteral("UPDATE category SET rd_category_id=0 WHERE rd_category_id IS NULL OR rd_category_id=''")); output.push_back("INSERT OR IGNORE INTO category (t_name, rd_category_id) VALUES ('" % SKGServices::stringToSqlString(first) % "', 0)"); } else { output.push_back("INSERT OR IGNORE INTO category (t_name, rd_category_id) VALUES ('" % SKGServices::stringToSqlString(first) % "',(SELECT id FROM category WHERE t_fullname='" % SKGServices::stringToSqlString(parentcat) % "'))"); } if (parentcat.isEmpty()) { parentcat = first; } else { parentcat = parentcat % OBJECTSEPARATOR % first; } cat = second; } } output.push_back("UPDATE suboperation set r_category_id=(SELECT id FROM category WHERE " % elementDescription % ") WHERE i_tmp=1"); } else if (element.attribute(QStringLiteral("attribute")).startsWith(QLatin1String("p_"))) { output.push_back("INSERT OR IGNORE INTO parameters (t_uuid_parent, t_name, i_tmp) SELECT o.id||'-operation', '" % SKGServices::stringToSqlString(propName) % "', 1 FROM operation o WHERE o.i_tmp=1"); output.push_back("UPDATE parameters set " % elementDescription % " WHERE i_tmp=1 AND t_name='" % SKGServices::stringToSqlString(propName) % "' AND NOT(" % elementDescription % ')'); output.push_back("DELETE FROM parameters WHERE i_tmp=1 AND t_name='" % SKGServices::stringToSqlString(propName) % "' AND t_value=''"); } else { output.push_back("UPDATE v_operation_prop set " % elementDescription % " WHERE i_tmp=1 AND NOT(" % elementDescription % ')'); } } } else if (iType == ALARM) { output.push_back("SELECT " % elementDescription % " FROM v_operation_prop WHERE #WC#"); } else if (iType == APPLYTEMPLATE) { output.push_back(element.attribute(QStringLiteral("value"))); } } if (!lineDescription.isEmpty()) { lineDescription += (iType == UPDATE ? QStringLiteral(" , ") : (iSQL ? QStringLiteral(" AND ") : i18nc("logical operator in a search query", " and "))); parenthesisNeeded = true; } lineDescription += elementDescription; } n = n.nextSibling(); } if (!(iType != SEARCH && iSQL) && !lineDescription.isEmpty()) { if (iType == SEARCH && parenthesisNeeded) { lineDescription = '(' % lineDescription % ')'; } output.push_back(lineDescription); } } l = l.nextSibling(); } } else { output.push_back(root.attribute(iSQL ? QStringLiteral("sql") : QStringLiteral("query"))); } if (iFullUpdate) { // Remove markers output.push_back(QStringLiteral("UPDATE v_operation_prop set i_tmp=0 WHERE i_tmp=1")); } return output; } QString SKGRuleObject::getDescriptionFromXML(SKGDocument* iDocument, const QString& iXML, bool iSQL, SKGRuleObject::ActionType iType) { QString output; QStringList list = getFromXML(iDocument, iXML, iSQL, iType); int nb = list.count(); for (int i = 0; i < nb; ++i) { output.append(list.at(i)); if (i < nb - 1) { output.append(iType != SEARCH ? QStringLiteral(" , ") : (iSQL ? QStringLiteral(" OR ") : i18nc("logical operator in a search query", " or "))); } } return output; } SKGError SKGRuleObject::createPayeeCategoryRule(SKGDocument* iDocument, const QString& iPayee, const QString& iCategory, SKGRuleObject& oRule) { SKGError err; oRule = SKGRuleObject(iDocument); // TODO(Stephane MANKOWSKI): escape values IFOKDO(err, oRule.setActionType(SKGRuleObject::UPDATE)) IFOKDO(err, oRule.setSearchDescription(iDocument->getDisplay(QStringLiteral("t_PAYEE")) % ' ' % getDisplayForOperator(QStringLiteral("#ATT#='#V1S#'"), iPayee, QString(), QString()))) IFOKDO(err, oRule.setXMLSearchDefinition(" ")) IFOKDO(err, oRule.setActionDescription(iDocument->getDisplay(QStringLiteral("t_REALCATEGORY")) % ' ' % getDisplayForOperator(QStringLiteral("#ATT#='#V1S#'"), iCategory, QString(), QString()))) IFOKDO(err, oRule.setXMLActionDefinition(" ")) IFOKDO(err, oRule.save()) return err; } diff --git a/skgbankmodeler/skgruleobject.h b/skgbankmodeler/skgruleobject.h index a50197e76..65cff4061 100644 --- a/skgbankmodeler/skgruleobject.h +++ b/skgbankmodeler/skgruleobject.h @@ -1,336 +1,342 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGRULEOBJECT_H #define SKGRULEOBJECT_H /** @file * This file defines classes SKGRuleObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankmodeler_export.h" #include "skgdocument.h" #include "skgerror.h" #include "skgobjectbase.h" /** * This class allows to define rules */ class SKGBANKMODELER_EXPORT SKGRuleObject final : public SKGObjectBase { public: /** * Alarm */ struct SKGAlarmInfo { /** To know if the alarm is raised */ bool Raised{}; /** The message to display */ QString Message; /** The amount in absolute value */ double Amount{}; /** The limit */ double Limit{}; }; /** * Process mode */ enum ProcessMode {ALL, NOTCHECKED, IMPORTED, IMPORTEDNOTVALIDATE, IMPORTING }; /** * Process mode */ Q_ENUM(ProcessMode) /** * Action type */ enum ActionType {SEARCH, UPDATE, ALARM, APPLYTEMPLATE }; /** * Action type */ Q_ENUM(ActionType) /** * Default constructor */ explicit SKGRuleObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier of the object */ explicit SKGRuleObject(SKGDocument* iDocument, int iID = 0); /** * Copy constructor * @param iObject the object to copy */ SKGRuleObject(const SKGRuleObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGRuleObject(const SKGObjectBase& iObject); /** * Operator affectation * @param iObject the object to copy */ SKGRuleObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGRuleObject& operator= (const SKGRuleObject& iObject); + /** * Destructor */ virtual ~SKGRuleObject(); /** * Return the name of the object for the display * @return name of the object */ QString getDisplayName() const override; /** * Set the XML for the search definition * @param iXml the XML search definition * @code example: * * * * * * * @endcode * @return an object managing the error * @see SKGError */ SKGError setXMLSearchDefinition(const QString& iXml); /** * Get the XML for the search definition * @return the XML */ QString getXMLSearchDefinition() const; /** * Set the XML for the action definition * @param iXml the XML action definition * @code example for an update action: * * * * * * * @endcode * @code example for an alarm action: * * * * * * * @endcode * @code example to apply a template: * * * * * * * @endcode* * @return an object managing the error * @see SKGError */ SKGError setXMLActionDefinition(const QString& iXml); /** * Get the XML for the action definition * @return the XML */ QString getXMLActionDefinition() const; /** * Set the search description * @param iDescription the search description * @return an object managing the error * @see SKGError */ SKGError setSearchDescription(const QString& iDescription); /** * Get the search description * @return the description */ QString getSearchDescription() const; /** * Set the action description * @param iDescription the action description * @return an object managing the error * @see SKGError */ SKGError setActionDescription(const QString& iDescription); /** * Get the action description * @return the description */ QString getActionDescription() const; /** * Set the action type * @param iType the action type * @return an object managing the error * @see SKGError */ SKGError setActionType(SKGRuleObject::ActionType iType); /** * Get the action type * @return the type */ SKGRuleObject::ActionType getActionType() const; /** * Set the order for the rule * @param iOrder the order. (-1 means "at the end") * @return an object managing the error * @see SKGError */ SKGError setOrder(double iOrder); /** * Get the order for the rule * @return the order */ double getOrder() const; /** * To bookmark or not an account * @param iBookmark the bookmark: true or false * @return an object managing the error * @see SKGError */ SKGError bookmark(bool iBookmark); /** * To know if the account is bookmarked * @return an object managing the error * @see SKGError */ bool isBookmarked() const; /** * save the object into the database * @param iInsertOrUpdate the save mode. * true: try an insert, if the insert failed then try an update. * false: try an insert, if the insert failed then return an error. * @param iReloadAfterSave to reload the object after save. Set false if you are sure that you will not use this object after save * @return an object managing the error * @see SKGError */ SKGError save(bool iInsertOrUpdate = true, bool iReloadAfterSave = true) override; /** * Execute actions * @param iMode mode * @return an object managing the error * @see SKGError */ SKGError execute(ProcessMode iMode = SKGRuleObject::ALL); /** * Get where clause corresponding to search condition * @param iAdditionalCondition additional select condition * @return the where clause */ QString getSelectSqlOrder(const QString& iAdditionalCondition = QString()) const; /** * Get alarm info * @return alarm info */ SKGRuleObject::SKGAlarmInfo getAlarmInfo() const; /** * Create a rule to update a category on operations having a specific payee * @param iDocument the document containing the object * @param iPayee the payee * @param iCategory the category * @param oRule the created rule * @return an object managing the error * @see SKGError */ static SKGError createPayeeCategoryRule(SKGDocument* iDocument, const QString& iPayee, const QString& iCategory, SKGRuleObject& oRule); /** * Get the list of supported operators * @param iAttributeType type of attribute * @param iType mode * @return list of supported operators */ static QStringList getListOfOperators(SKGServices::AttributeType iAttributeType, SKGRuleObject::ActionType iType = SEARCH); /** * Get the NLS display of an operator * @param iOperator the operator (see @see getListOfOperators) * @param iParam1 parameter * @param iParam2 parameter * @param iAtt2 attribute number 2 * @return the NLS display */ static QString getDisplayForOperator(const QString& iOperator, const QString& iParam1, const QString& iParam2, const QString& iAtt2); /** * Get the NLS tooltip of an operator * @param iOperator the operator (see @see getListOfOperators) * @return the NLS display */ static QString getToolTipForOperator(const QString& iOperator); protected: /** * Get the description of an XML definition * @param iDocument the document * @param iXML the XML * @param iSQL to define if you want the TXT or SQL description * @param iType mode * @return the description */ static QString getDescriptionFromXML(SKGDocument* iDocument, const QString& iXML, bool iSQL = false, SKGRuleObject::ActionType iType = SEARCH); private: static QStringList getFromXML(SKGDocument* iDocument, const QString& iXML, bool iSQL = false, SKGRuleObject::ActionType iType = SEARCH, bool iFullUpdate = false); }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGRuleObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbankmodeler/skgsuboperationobject.cpp b/skgbankmodeler/skgsuboperationobject.cpp index 89749ef01..dd51fdf03 100644 --- a/skgbankmodeler/skgsuboperationobject.cpp +++ b/skgbankmodeler/skgsuboperationobject.cpp @@ -1,167 +1,173 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file implements classes SKGSubOperationObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgsuboperationobject.h" #include #include "skgcategoryobject.h" #include "skgdocument.h" #include "skgoperationobject.h" #include "skgservices.h" #include "skgtrackerobject.h" SKGSubOperationObject::SKGSubOperationObject(): SKGSubOperationObject(nullptr) {} SKGSubOperationObject::SKGSubOperationObject(SKGDocument* iDocument, int iID): SKGObjectBase(iDocument, QStringLiteral("v_suboperation"), iID) {} SKGSubOperationObject::~SKGSubOperationObject() = default; SKGSubOperationObject::SKGSubOperationObject(const SKGSubOperationObject& iObject) = default; SKGSubOperationObject::SKGSubOperationObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("suboperation")) { copyFrom(iObject); } else { *this = SKGObjectBase(iObject.getDocument(), QStringLiteral("v_suboperation"), iObject.getID()); } } SKGSubOperationObject& SKGSubOperationObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGSubOperationObject& SKGSubOperationObject::operator= (const SKGSubOperationObject& iObject) +{ + copyFrom(iObject); + return *this; +} + SKGError SKGSubOperationObject::setDate(QDate iDate) { return setAttribute(QStringLiteral("d_date"), iDate.isValid() ? SKGServices::dateToSqlString(QDateTime(iDate)) : QLatin1String("")); } QDate SKGSubOperationObject::getDate() const { return SKGServices::stringToTime(getAttribute(QStringLiteral("d_date"))).date(); } SKGError SKGSubOperationObject::setOrder(int iOrder) { return setAttribute(QStringLiteral("i_order"), SKGServices::intToString(iOrder)); } int SKGSubOperationObject::getOrder() const { return SKGServices::stringToInt(getAttribute(QStringLiteral("i_order"))); } SKGError SKGSubOperationObject::setComment(const QString& iComment) { return setAttribute(QStringLiteral("t_comment"), iComment); } QString SKGSubOperationObject::getComment() const { return getAttribute(QStringLiteral("t_comment")); } QString SKGSubOperationObject::getFormula() const { return getAttribute(QStringLiteral("t_formula")); } SKGError SKGSubOperationObject::setFormula(const QString& iFormula) { return setAttribute(QStringLiteral("t_formula"), iFormula); } SKGError SKGSubOperationObject::getParentOperation(SKGOperationObject& oOperation) const { SKGError err = getDocument()->getObject(QStringLiteral("v_operation"), "id=" % getAttribute(QStringLiteral("rd_operation_id")), oOperation); return err; } SKGError SKGSubOperationObject::setParentOperation(const SKGOperationObject& iOperation) { SKGError err; if (!getDate().isValid()) { err = setDate(iOperation.getDate()); } IFOKDO(err, setAttribute(QStringLiteral("rd_operation_id"), SKGServices::intToString(iOperation.getID()))) return err; } SKGError SKGSubOperationObject::getCategory(SKGCategoryObject& oCategory) const { SKGError err = getDocument()->getObject(QStringLiteral("v_category"), "id=" % getAttribute(QStringLiteral("r_category_id")), oCategory); return err; } SKGError SKGSubOperationObject::setCategory(const SKGCategoryObject& iCategory) { return setAttribute(QStringLiteral("r_category_id"), SKGServices::intToString(iCategory.getID())); } SKGError SKGSubOperationObject::setQuantity(double iValue) { return setAttribute(QStringLiteral("f_value"), SKGServices::doubleToString(iValue)); } double SKGSubOperationObject::getQuantity() const { return SKGServices::stringToDouble(getAttribute(QStringLiteral("f_value"))); } SKGError SKGSubOperationObject::setTracker(const SKGTrackerObject& iTracker, bool iForce) { SKGError err; SKGTrackerObject previous; getTracker(previous); if (iTracker != previous) { if (!iForce && previous.isClosed()) { err = SKGError(ERR_FAIL, i18nc("Error message", "Impossible to remove an operation from a closed tracker")); } else if (!iForce && iTracker.isClosed()) { err = SKGError(ERR_FAIL, i18nc("Error message", "Impossible to add an operation in a closed tracker")); } else { err = setAttribute(QStringLiteral("r_refund_id"), SKGServices::intToString(iTracker.getID())); } } return err; } SKGError SKGSubOperationObject::getTracker(SKGTrackerObject& oTracker) const { QString idS = getAttribute(QStringLiteral("r_refund_id")); if (idS.isEmpty()) { idS = '0'; } SKGError err; if ((getDocument() != nullptr) && idS != QStringLiteral("0")) { err = getDocument()->getObject(QStringLiteral("v_refund"), "id=" % idS, oTracker); } return err; } diff --git a/skgbankmodeler/skgsuboperationobject.h b/skgbankmodeler/skgsuboperationobject.h index 7d98c6f40..6a0b4e2d3 100644 --- a/skgbankmodeler/skgsuboperationobject.h +++ b/skgbankmodeler/skgsuboperationobject.h @@ -1,197 +1,203 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGSUBOPERATIONOBJECT_H #define SKGSUBOPERATIONOBJECT_H /** @file * This file defines classes SKGSubOperationObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankmodeler_export.h" #include "skgobjectbase.h" class SKGOperationObject; class SKGCategoryObject; class SKGTrackerObject; /** * This class manages suboperation object */ class SKGBANKMODELER_EXPORT SKGSubOperationObject final : public SKGObjectBase { public: /** * Default constructor */ explicit SKGSubOperationObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier in @p iTable of the object */ explicit SKGSubOperationObject(SKGDocument* iDocument, int iID = 0); /** * Copy constructor * @param iObject the object to copy */ SKGSubOperationObject(const SKGSubOperationObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGSubOperationObject(const SKGObjectBase& iObject); /** * Operator affectation * @param iObject the object to copy */ SKGSubOperationObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGSubOperationObject& operator= (const SKGSubOperationObject& iObject); + /** * Destructor */ virtual ~SKGSubOperationObject(); /** * Set date of this suboperation * @param iDate the date * @return an object managing the error * @see SKGError */ // cppcheck-suppress passedByValue SKGError setDate(QDate iDate); /** * Get date of this suboperation * @return the date */ QDate getDate() const; /** * Set the order of suboperation * @param iOrder the order * @return an object managing the error * @see SKGError */ SKGError setOrder(int iOrder); /** * Get order of this suboperation * @return the order */ int getOrder() const; /** * Set the comment of suboperation * @param iComment the comment * @return an object managing the error * @see SKGError */ SKGError setComment(const QString& iComment); /** * Get the comment of this suboperation * @return the comment */ QString getComment() const; /** * Get the parent operation * @param oOperation the parent operation * @return an object managing the error. * @see SKGError */ SKGError getParentOperation(SKGOperationObject& oOperation) const; /** * Set the parent operation * @param iOperation the parent operation * @return an object managing the error. * @see SKGError */ SKGError setParentOperation(const SKGOperationObject& iOperation); /** * Set the category * @param iCategory the category * @return an object managing the error * @see SKGError */ SKGError setCategory(const SKGCategoryObject& iCategory); /** * Get the category * @param oCategory the category * @return an object managing the error * @see SKGError */ SKGError getCategory(SKGCategoryObject& oCategory) const; /** * Set the tracker * @param iTracker the tracker * @param iForce force the change of the tracker even if closed * @return an object managing the error * @see SKGError */ SKGError setTracker(const SKGTrackerObject& iTracker, bool iForce = false); /** * Get the tracker * @param oTracker the tracker * @return an object managing the error * @see SKGError */ SKGError getTracker(SKGTrackerObject& oTracker) const; /** * Set the quantity of the suboperation * @param iValue the value * @return an object managing the error * @see SKGError */ SKGError setQuantity(double iValue); /** * Get the quantity of the suboperation * @return the value */ double getQuantity() const; /** * Set the formula * @param iFormula the formula * @return an object managing the error * @see SKGError */ SKGError setFormula(const QString& iFormula); /** * Get the formula * @return the formula */ QString getFormula() const; }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGSubOperationObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbankmodeler/skgtrackerobject.cpp b/skgbankmodeler/skgtrackerobject.cpp index d5f2a6bd2..ebbe2b315 100644 --- a/skgbankmodeler/skgtrackerobject.cpp +++ b/skgbankmodeler/skgtrackerobject.cpp @@ -1,135 +1,141 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file implements classes SKGTrackerObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgtrackerobject.h" #include #include "skgdocumentbank.h" #include "skgsuboperationobject.h" #include "skgtraces.h" SKGTrackerObject::SKGTrackerObject(): SKGTrackerObject(nullptr) {} SKGTrackerObject::SKGTrackerObject(SKGDocument* iDocument, int iID): SKGNamedObject(iDocument, QStringLiteral("v_refund"), iID) {} SKGTrackerObject::~SKGTrackerObject() = default; SKGTrackerObject::SKGTrackerObject(const SKGTrackerObject& iObject) = default; SKGTrackerObject::SKGTrackerObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("refund")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_refund"), iObject.getID()); } } SKGTrackerObject& SKGTrackerObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGTrackerObject& SKGTrackerObject::operator= (const SKGTrackerObject& iObject) +{ + copyFrom(iObject); + return *this; +} + SKGError SKGTrackerObject::createTracker(SKGDocumentBank* iDocument, const QString& iName, SKGTrackerObject& oTracker, bool iSendPopupMessageOnCreation) { SKGError err; SKGTRACEINFUNCRC(10, err) // Check if refund is already existing if (iName.isEmpty()) { oTracker = SKGTrackerObject(nullptr, 0); } else if (iDocument != nullptr) { iDocument->getObject(QStringLiteral("v_refund"), "t_name='" % SKGServices::stringToSqlString(iName) % '\'', oTracker); if (oTracker.getID() == 0) { // No, we have to create it oTracker = SKGTrackerObject(iDocument); err = oTracker.setName(iName); IFOKDO(err, oTracker.save()) if (!err && iSendPopupMessageOnCreation) { err = iDocument->sendMessage(i18nc("Information message", "Tracker '%1' has been created", iName), SKGDocument::Positive); } } } return err; } SKGError SKGTrackerObject::getSubOperations(SKGListSKGObjectBase& oSubOperations) const { SKGError err = getDocument()->getObjects(QStringLiteral("v_suboperation"), "r_refund_id=" % SKGServices::intToString(getID()), oSubOperations); return err; } SKGError SKGTrackerObject::setClosed(bool iClosed) { return setAttribute(QStringLiteral("t_close"), iClosed ? QStringLiteral("Y") : QStringLiteral("N")); } bool SKGTrackerObject::isClosed() const { return (getAttribute(QStringLiteral("t_close")) == QStringLiteral("Y")); } SKGError SKGTrackerObject::setComment(const QString& iComment) { return setAttribute(QStringLiteral("t_comment"), iComment); } QString SKGTrackerObject::getComment() const { return getAttribute(QStringLiteral("t_comment")); } double SKGTrackerObject::getCurrentAmount() const { return SKGServices::stringToDouble(getAttributeFromView(QStringLiteral("v_refund_amount"), QStringLiteral("f_CURRENTAMOUNT"))); } SKGError SKGTrackerObject::merge(const SKGTrackerObject& iTracker) { SKGError err; SKGObjectBase::SKGListSKGObjectBase ops; IFOKDO(err, iTracker.getSubOperations(ops)) int nb = ops.count(); for (int i = 0; !err && i < nb; ++i) { SKGSubOperationObject op(ops.at(i)); err = op.setTracker(*this); IFOKDO(err, op.save(true, false)) } IFOKDO(err, iTracker.remove(false)) return err; } diff --git a/skgbankmodeler/skgtrackerobject.h b/skgbankmodeler/skgtrackerobject.h index 6638e4898..d2373d2da 100644 --- a/skgbankmodeler/skgtrackerobject.h +++ b/skgbankmodeler/skgtrackerobject.h @@ -1,139 +1,145 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGTRACKEROBJECT_H #define SKGTRACKEROBJECT_H /** @file * This file defines classes SKGTrackerObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankmodeler_export.h" #include "skgnamedobject.h" class SKGDocumentBank; /** * This class manages tracker object */ class SKGBANKMODELER_EXPORT SKGTrackerObject final : public SKGNamedObject { public: /** * Default constructor */ explicit SKGTrackerObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier in @p iTable of the object */ explicit SKGTrackerObject(SKGDocument* iDocument, int iID = 0); /** * Copy constructor * @param iObject the object to copy */ SKGTrackerObject(const SKGTrackerObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGTrackerObject(const SKGObjectBase& iObject); /** * Operator affectation * @param iObject the object to copy */ SKGTrackerObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGTrackerObject& operator= (const SKGTrackerObject& iObject); + /** * Destructor */ virtual ~SKGTrackerObject(); /** * Create a tracker if needed and return it * @param iDocument the document where to create * @param iName the name * @param oTracker the tracker * @param iSendPopupMessageOnCreation to send a creation message if the tracker is created * @return an object managing the error. * @see SKGError */ static SKGError createTracker(SKGDocumentBank* iDocument, const QString& iName, SKGTrackerObject& oTracker, bool iSendPopupMessageOnCreation = false); /** * Get all suboperations of this tracker * @param oSubOperations all suboperations of this operation * @return an object managing the error * @see SKGError */ SKGError getSubOperations(SKGListSKGObjectBase& oSubOperations) const; /** * Set the comment of tracker * @param iComment the comment * @return an object managing the error * @see SKGError */ SKGError setComment(const QString& iComment); /** * Get the comment of this tracker * @return the comment */ QString getComment() const; /** * To set the closed attribute of this tracker * @param iClosed the closed attribute: true or false * @return an object managing the error * @see SKGError */ SKGError setClosed(bool iClosed); /** * To know if the tracker has been closed or not * @return an object managing the error * @see SKGError */ bool isClosed() const; /** * Get the current amount * @return the current amount */ double getCurrentAmount() const; /** * Merge iTracker in current tracker * @param iTracker the tracker. All operations will be transferred into this tracker. The tracker will be removed * @return an object managing the error * @see SKGError */ SKGError merge(const SKGTrackerObject& iTracker); }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGTrackerObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbankmodeler/skgunitobject.cpp b/skgbankmodeler/skgunitobject.cpp index 20d5b2444..8df739a29 100644 --- a/skgbankmodeler/skgunitobject.cpp +++ b/skgbankmodeler/skgunitobject.cpp @@ -1,2582 +1,2589 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file defines classes SKGUnitObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgunitobject.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "skgdocumentbank.h" #include "skgoperationobject.h" #include "skgtraces.h" #include "skgtransactionmng.h" #include "skgunitvalueobject.h" SKGUnitObject::SKGUnitObject() : SKGUnitObject(nullptr) {} SKGUnitObject::SKGUnitObject(SKGDocument* iDocument, int iID) : SKGNamedObject(iDocument, QStringLiteral("v_unit"), iID) {} SKGUnitObject::~SKGUnitObject() = default; SKGUnitObject::SKGUnitObject(const SKGUnitObject& iObject) = default; SKGUnitObject::SKGUnitObject(const SKGNamedObject& iObject) { if (iObject.getRealTable() == QStringLiteral("unit")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_unit"), iObject.getID()); } } SKGUnitObject::SKGUnitObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("unit")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_unit"), iObject.getID()); } } SKGUnitObject& SKGUnitObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGUnitObject& SKGUnitObject::operator= (const SKGUnitObject& iObject) +{ + copyFrom(iObject); + return *this; +} + + QString SKGUnitObject::getWhereclauseId() const { QString output = SKGObjectBase::getWhereclauseId(); // clazy:exclude=skipped-base-method if (output.isEmpty()) { QString name = getName(); if (!name.isEmpty()) { output = "t_name='" % SKGServices::stringToSqlString(name) % '\''; } QString symbol = getSymbol(); if (!symbol.isEmpty()) { if (!output.isEmpty()) { output += QStringLiteral(" OR "); } output += "t_symbol='" % SKGServices::stringToSqlString(symbol) % '\''; } if (!output.isEmpty()) { output = '(' % output % ')'; } } return output; } QList SKGUnitObject::currencies; QStringList SKGUnitObject::getListofKnownCurrencies(bool iIncludingObsolete) { SKGTRACEINFUNC(10) if (currencies.isEmpty()) { // Search currencies const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/currency/"), QStandardPaths::LocateDirectory); for (const auto& dir : dirs) { auto listDesktopFiles = QDir(dir).entryList(QStringList() << QStringLiteral("*.desktop")); for (const auto& path : qAsConst(listDesktopFiles)) { // Read the file QFileInfo file(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/currency/") + path)); KConfig cgFile(file.absoluteFilePath()); KConfigGroup cg(&cgFile, QStringLiteral("Currency Code")); SKGServices::SKGUnitInfo unit; unit.Name = cg.readEntry(QStringLiteral("Name"), QString()) + QStringLiteral(" (") + cg.readEntry(QStringLiteral("CurrencyCodeIsoAlpha3"), QString()) + QStringLiteral(")"); unit.Symbol = cg.readEntry(QStringLiteral("CurrencyUnitSymbolDefault"), QString()); if (unit.Symbol.isEmpty()) { unit.Symbol = cg.readEntry(QStringLiteral("CurrencyCodeIsoAlpha3"), file.baseName()); } unit.Value = 1; unit.NbDecimal = cg.readEntry(QStringLiteral("CurrencyDecimalPlacesDisplay"), 2); unit.Source = QStringLiteral("GrandTrunk"); unit.Date = cg.readEntry(QStringLiteral("CurrencyIntroducedDate"), QDate::currentDate()); unit.Obsolete = (cg.readEntry(QStringLiteral("CurrencySuspendedDate"), cg.readEntry(QStringLiteral("CurrencyWithdrawnDate"), QDate())) != QDate()); currencies.push_back(unit); } } // Add other units { // CAC40 SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "CAC 40"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "France"); info.Date = QDate(1987, 1, 1); info.Internet = QStringLiteral("^FCHI"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // NASDAQ SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "NASDAQ"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "United States"); info.Date = QDate(1971, 2, 5); info.Internet = QStringLiteral("^IXIC"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // Dow Jones SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "Dow Jones (DJIA)"); info.Symbol = QStringLiteral("DJIA"); info.Country = i18nc("Noun, a country", "United States"); info.Date = QDate(1884, 1, 1); info.Internet = QStringLiteral("^DJI"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // SBF 120 SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "SBF 120"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "France"); info.Date = QDate(1990, 12, 31); info.Internet = QStringLiteral("^SBF120"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // S&P 500 SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "S&P 500"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "United States"); info.Date = QDate(1920, 1, 1); info.Internet = QStringLiteral("^GSPC"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // FTSE 100 SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "FTSE 100"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "England"); info.Date = QDate(1984, 1, 3); info.Internet = QStringLiteral("^FTSE"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // DAX SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "DAX"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "Germany"); info.Date = QDate(1920, 1, 1); info.Internet = QStringLiteral("^GDAXI"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // NIKKEI 225 SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "NIKKEI 225"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "Japan"); info.Date = QDate(1920, 1, 1); info.Internet = QStringLiteral("^N225"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // HANG SENG SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "HANG SENG"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "China"); info.Date = QDate(1920, 1, 1); info.Internet = QStringLiteral("^HSI"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // STRAITS TIMES SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "STRAITS TIMES"); info.Symbol = info.Name; info.Date = QDate(1920, 1, 1); info.Country = i18nc("Noun, a country", "Singapore"); info.Internet = QStringLiteral("^STI"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // BITCOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "Bitcoin"); info.Symbol = QStringLiteral("BTC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BTC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ETHEREUM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ethereum"); info.Symbol = QStringLiteral("ETH"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ETH"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // RIPPLE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ripple"); info.Symbol = QStringLiteral("XRP"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XRP"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BITCOIN-CASH SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Bitcoin Cash"); info.Symbol = QStringLiteral("BCH"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BCH"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // CARDANO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Cardano"); info.Symbol = QStringLiteral("ADA"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ADA"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // NEM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "NEM"); info.Symbol = QStringLiteral("XEM"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XEM"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // LITECOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Litecoin"); info.Symbol = QStringLiteral("LTC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("LTC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // STELLAR SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Stellar"); info.Symbol = QStringLiteral("XLM"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XLM"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // IOTA SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "IOTA"); info.Symbol = QStringLiteral("MIOTA"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("MIOTA"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // TRON SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "TRON"); info.Symbol = QStringLiteral("TRX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("TRX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DASH SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Dash"); info.Symbol = QStringLiteral("DASH"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DASH"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // NEO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "NEO"); info.Symbol = QStringLiteral("NEO"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("NEO"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // MONERO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Monero"); info.Symbol = QStringLiteral("XMR"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XMR"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // EOS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "EOS"); info.Symbol = QStringLiteral("EOS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("EOS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // QTUM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Qtum"); info.Symbol = QStringLiteral("QTUM"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("QTUM"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ICON SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "ICON"); info.Symbol = QStringLiteral("ICX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ICX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BITCOIN-GOLD SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Bitcoin Gold"); info.Symbol = QStringLiteral("BTG"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BTG"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // LISK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Lisk"); info.Symbol = QStringLiteral("LSK"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("LSK"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // RAIBLOCKS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "RaiBlocks"); info.Symbol = QStringLiteral("XRB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XRB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ETHEREUM-CLASSIC SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ethereum Classic"); info.Symbol = QStringLiteral("ETC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ETC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // VERGE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Verge"); info.Symbol = QStringLiteral("XVG"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XVG"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // SIACOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Siacoin"); info.Symbol = QStringLiteral("SC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // OMISEGO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "OmiseGO"); info.Symbol = QStringLiteral("OMG"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("OMG"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BYTECOIN-BCN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Bytecoin"); info.Symbol = QStringLiteral("BCN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BCN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BITCONNECT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "BitConnect"); info.Symbol = QStringLiteral("BCC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BCC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // POPULOUS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Populous"); info.Symbol = QStringLiteral("PPT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("PPT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // STRATIS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Stratis"); info.Symbol = QStringLiteral("STRAT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("STRAT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ZCASH SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Zcash"); info.Symbol = QStringLiteral("ZEC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ZEC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DENTACOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Dentacoin"); info.Symbol = QStringLiteral("DCN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DCN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BITSHARES SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "BitShares"); info.Symbol = QStringLiteral("BTS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BTS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BINANCE-COIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Binance Coin"); info.Symbol = QStringLiteral("BNB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BNB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DOGECOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Dogecoin"); info.Symbol = QStringLiteral("DOGE"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DOGE"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // STATUS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Status"); info.Symbol = QStringLiteral("SNT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SNT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ARDOR SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ardor"); info.Symbol = QStringLiteral("ARDR"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ARDR"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // KUCOIN-SHARES SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "KuCoin Shares"); info.Symbol = QStringLiteral("KCS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("KCS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // TETHER SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Tether"); info.Symbol = QStringLiteral("USDT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("USDT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // STEEM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Steem"); info.Symbol = QStringLiteral("STEEM"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("STEEM"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // WAVES SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Waves"); info.Symbol = QStringLiteral("WAVES"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("WAVES"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // VECHAIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "VeChain"); info.Symbol = QStringLiteral("VEN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("VEN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DIGIBYTE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "DigiByte"); info.Symbol = QStringLiteral("DGB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DGB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // KOMODO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Komodo"); info.Symbol = QStringLiteral("KMD"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("KMD"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DRAGONCHAIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Dragonchain"); info.Symbol = QStringLiteral("DRGN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DRGN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // AUGUR SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Augur"); info.Symbol = QStringLiteral("REP"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("REP"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // GOLEM-NETWORK-TOKENS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Golem"); info.Symbol = QStringLiteral("GNT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("GNT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // VERITASEUM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Veritaseum"); info.Symbol = QStringLiteral("VERI"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("VERI"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // HSHARE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Hshare"); info.Symbol = QStringLiteral("HSR"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("HSR"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // KIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Kin"); info.Symbol = QStringLiteral("KIN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("KIN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // SALT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "SALT"); info.Symbol = QStringLiteral("SALT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SALT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ELECTRONEUM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Electroneum"); info.Symbol = QStringLiteral("ETN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ETN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ARK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ark"); info.Symbol = QStringLiteral("ARK"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ARK"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DENT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Dent"); info.Symbol = QStringLiteral("DENT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DENT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ETHOS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ethos"); info.Symbol = QStringLiteral("ETHOS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ETHOS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BASIC-ATTENTION-TOKEN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Basic Attention Token"); info.Symbol = QStringLiteral("BAT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BAT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // REDDCOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "ReddCoin"); info.Symbol = QStringLiteral("RDD"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("RDD"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // 0X SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "0x"); info.Symbol = QStringLiteral("ZRX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ZRX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DECRED SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Decred"); info.Symbol = QStringLiteral("DCR"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DCR"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // NEXUS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Nexus"); info.Symbol = QStringLiteral("NXS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("NXS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // EXPERIENCE-POINTS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Experience Points"); info.Symbol = QStringLiteral("XP"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XP"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // QASH SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "QASH"); info.Symbol = QStringLiteral("QASH"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("QASH"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // KYBER-NETWORK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Kyber Network"); info.Symbol = QStringLiteral("KNC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("KNC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // PIVX SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "PIVX"); info.Symbol = QStringLiteral("PIVX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("PIVX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // FUNFAIR SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "FunFair"); info.Symbol = QStringLiteral("FUN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("FUN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // FACTOM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Factom"); info.Symbol = QStringLiteral("FCT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("FCT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // NEBLIO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Neblio"); info.Symbol = QStringLiteral("NEBL"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("NEBL"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // REQUEST-NETWORK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Request Network"); info.Symbol = QStringLiteral("REQ"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("REQ"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // AETERNITY SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Aeternity"); info.Symbol = QStringLiteral("AE"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("AE"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // SUBSTRATUM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Substratum"); info.Symbol = QStringLiteral("SUB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SUB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // POWER-LEDGER SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Power Ledger"); info.Symbol = QStringLiteral("POWR"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("POWR"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // WAX SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "WAX"); info.Symbol = QStringLiteral("WAX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("WAX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // AELF SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "aelf"); info.Symbol = QStringLiteral("ELF"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ELF"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BYTOM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Bytom"); info.Symbol = QStringLiteral("BTM"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BTM"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // AION SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Aion"); info.Symbol = QStringLiteral("AION"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("AION"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // RCHAIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "RChain"); info.Symbol = QStringLiteral("RHOC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("RHOC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DIGITALNOTE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "DigitalNote"); info.Symbol = QStringLiteral("XDN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XDN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ENIGMA-PROJECT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Enigma"); info.Symbol = QStringLiteral("ENG"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ENG"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // NXT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Nxt"); info.Symbol = QStringLiteral("NXT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("NXT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // TIME-NEW-BANK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Time New Bank"); info.Symbol = QStringLiteral("TNB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("TNB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BITCOINDARK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "BitcoinDark"); info.Symbol = QStringLiteral("BTCD"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BTCD"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // MONACOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "MonaCoin"); info.Symbol = QStringLiteral("MONA"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("MONA"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // QUANTSTAMP SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Quantstamp"); info.Symbol = QStringLiteral("QSP"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("QSP"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // MAIDSAFECOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "MaidSafeCoin"); info.Symbol = QStringLiteral("MAID"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("MAID"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BYTEBALL SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Byteball Bytes"); info.Symbol = QStringLiteral("GBYTE"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("GBYTE"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // GAS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Gas"); info.Symbol = QStringLiteral("GAS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("GAS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // CHAINLINK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "ChainLink"); info.Symbol = QStringLiteral("LINK"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("LINK"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // SYSCOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Syscoin"); info.Symbol = QStringLiteral("SYS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SYS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // SANTIMENT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Santiment Network Token"); info.Symbol = QStringLiteral("SAN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SAN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // COBINHOOD SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Cobinhood"); info.Symbol = QStringLiteral("COB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("COB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // RED-PULSE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Red Pulse"); info.Symbol = QStringLiteral("RPX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("RPX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DIGIXDAO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "DigixDAO"); info.Symbol = QStringLiteral("DGD"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DGD"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // TENX SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "TenX"); info.Symbol = QStringLiteral("PAY"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("PAY"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ICONOMI SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Iconomi"); info.Symbol = QStringLiteral("ICN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ICN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // POET SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Po.et"); info.Symbol = QStringLiteral("POE"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("POE"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ZCOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "ZCoin"); info.Symbol = QStringLiteral("XZC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XZC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // GNOSIS-GNO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Gnosis"); info.Symbol = QStringLiteral("GNO"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("GNO"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BLOCKV SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "BLOCKv"); info.Symbol = QStringLiteral("VEE"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("VEE"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // WALTON SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Walton"); info.Symbol = QStringLiteral("WTC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("WTC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // PACCOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "PACcoin"); info.Symbol = QStringLiteral("PAC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("PAC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DEEPBRAIN-CHAIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "DeepBrain Chain"); info.Symbol = QStringLiteral("DBC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DBC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ETHLEND SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "ETHLend"); info.Symbol = QStringLiteral("LEND"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("LEND"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // CIVIC SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Civic"); info.Symbol = QStringLiteral("CVC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("CVC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } } QStringList output; output.reserve(currencies.count()); for (const auto& unit : qAsConst(currencies)) { if (iIncludingObsolete || !unit.Obsolete) { output.push_back(unit.Name); } } output.sort(); return output; } QString SKGUnitObject::getInternationalCode(const QString& iUnitName) { SKGTRACEINFUNC(10) QString output = iUnitName; QRegExp rx(QStringLiteral(".*\\(([^\\(\\)]+)\\)[^\\(\\)]*")); if (rx.indexIn(iUnitName) != -1) { output = rx.cap(1); } return output; } SKGServices::SKGUnitInfo SKGUnitObject::getUnitInfo() { SKGTRACEINFUNC(10) SKGServices::SKGUnitInfo info; info.Name = getName(); info.Value = getAmount(); info.NbDecimal = getNumberDecimal(); info.Symbol = getSymbol(); info.Country = getCountry(); info.Internet = getInternetCode(); info.Date = QDate::currentDate(); return info; } SKGServices::SKGUnitInfo SKGUnitObject::getUnitInfo(const QString& iUnitName) { SKGTRACEINFUNC(10) SKGServices::SKGUnitInfo info; if (currencies.isEmpty()) { getListofKnownCurrencies(false); } QString isoCode = getInternationalCode(iUnitName); for (const auto& unit : qAsConst(currencies)) { if (getInternationalCode(unit.Name) == isoCode) { info = unit; break; } } return info; } SKGError SKGUnitObject::createCurrencyUnit(SKGDocumentBank* iDocument, const QString& iUnitName, SKGUnitObject& oUnit) { SKGError err; SKGTRACEINFUNCRC(10, err) if (iDocument != nullptr) { SKGUnitObject parentUnit; oUnit = SKGUnitObject(iDocument); SKGUnitObject::UnitType type = SKGUnitObject::CURRENCY; SKGServices::SKGUnitInfo prim = iDocument->getPrimaryUnit(); SKGServices::SKGUnitInfo seco = iDocument->getSecondaryUnit(); // Get information on the unit SKGServices::SKGUnitInfo info = getUnitInfo(iUnitName); if (info.Name.isEmpty()) { err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Unknown unit '%1'", iUnitName)); } if (!err && !info.Parent.isEmpty()) { err = createCurrencyUnit(iDocument, info.Parent, parentUnit); } // Set the type if (info.Name == info.Symbol) { // This is an index type = SKGUnitObject::INDEX; } else if (!info.Parent.isEmpty()) { // This is a secondary unit type = (seco.Symbol.isEmpty() || seco.Symbol == info.Symbol ? SKGUnitObject::SECONDARY : SKGUnitObject::CURRENCY); } else { // As primary type = (prim.Symbol.isEmpty() || prim.Symbol == info.Symbol ? SKGUnitObject::PRIMARY : SKGUnitObject::CURRENCY); } // Point on primary unit if (info.Value == 1 && !err && (type == SKGUnitObject::CURRENCY || type == SKGUnitObject::SECONDARY)) { SKGUnitObject primunit(iDocument); err = primunit.setSymbol(prim.Symbol); IFOKDO(err, primunit.load()) IFOK(err) { QString codeprimunit = getInternationalCode(primunit.getName()); QString codeunit = getInternationalCode(info.Name); if (!codeprimunit.isEmpty()) { info.Internet = codeunit % '/' % codeprimunit; info.Value = -1; parentUnit = SKGUnitObject(iDocument); err = parentUnit.setSymbol(prim.Symbol); IFOKDO(err, parentUnit.load()) } } } IFOKDO(err, oUnit.setName(info.Name)) if (!err && oUnit.exist()) { err = oUnit.load(); } IFOKDO(err, oUnit.setType(type)) IFOKDO(err, oUnit.setSymbol(info.Symbol)) IFOKDO(err, oUnit.setInternetCode(info.Internet)) IFOKDO(err, oUnit.setDownloadSource(info.Source)) IFOKDO(err, oUnit.setCountry(info.Country)) IFOKDO(err, oUnit.setNumberDecimal(info.NbDecimal)) if (!err && parentUnit.exist()) { err = oUnit.setUnit(parentUnit); } IFOKDO(err, oUnit.save()) // Creation of the value if (info.Value > 0) { SKGUnitValueObject unitValue; IFOKDO(err, oUnit.addUnitValue(unitValue)) IFOKDO(err, unitValue.setDate(info.Date)) IFOKDO(err, unitValue.setQuantity(info.Value)) IFOKDO(err, unitValue.save()) } } return err; } double SKGUnitObject::convert(double iValue, const SKGUnitObject& iUnitFrom, const SKGUnitObject& iUnitTo, QDate iDate) { double output = iValue; if (iUnitFrom != iUnitTo) { double valFrom = iUnitFrom.getAmount(iDate); double valTo = iUnitTo.getAmount(iDate); output = iValue * valFrom / valTo; } return output; } SKGError SKGUnitObject::setSymbol(const QString& iSymbol) { return setAttribute(QStringLiteral("t_symbol"), iSymbol); } QString SKGUnitObject::getSymbol() const { return getAttribute(QStringLiteral("t_symbol")); } QString SKGUnitObject::getDownloadSource() const { return getAttribute(QStringLiteral("t_source")); } SKGError SKGUnitObject::setDownloadSource(const QString& iSource) { return setAttribute(QStringLiteral("t_source"), iSource); } SKGError SKGUnitObject::setNumberDecimal(int iNb) { return setAttribute(QStringLiteral("i_nbdecimal"), SKGServices::intToString(iNb)); } int SKGUnitObject::getNumberDecimal() const { return SKGServices::stringToInt(getAttribute(QStringLiteral("i_nbdecimal"))); } SKGError SKGUnitObject::setCountry(const QString& iCountry) { return setAttribute(QStringLiteral("t_country"), iCountry); } QString SKGUnitObject::getCountry() const { return getAttribute(QStringLiteral("t_country")); } SKGError SKGUnitObject::setInternetCode(const QString& iCode) { return setAttribute(QStringLiteral("t_internet_code"), iCode); } QString SKGUnitObject::getInternetCode() const { return getAttribute(QStringLiteral("t_internet_code")); } SKGError SKGUnitObject::setType(SKGUnitObject::UnitType iType) { SKGError err; if (getAttribute(QStringLiteral("t_type")).isEmpty() || this->getType() != iType) { // Guaranty that PRIMARY and SECONDARY is unique if (iType == PRIMARY || iType == SECONDARY) { // Set old SECONDARY as CURRENCY err = getDocument()->executeSqliteOrder(QStringLiteral("UPDATE unit SET t_type='C' WHERE t_type='2'")); // Set old PRIMARY as SECONDARY if (!err && iType == PRIMARY) { err = getDocument()->executeSqliteOrder(QStringLiteral("UPDATE unit SET t_type='2' WHERE t_type='1'")); } } } IFOKDO(err, setAttribute(QStringLiteral("t_type"), (iType == CURRENCY ? QStringLiteral("C") : (iType == PRIMARY ? QStringLiteral("1") : (iType == SECONDARY ? QStringLiteral("2") : (iType == SHARE ? QStringLiteral("S") : (iType == INDEX ? QStringLiteral("I") : QStringLiteral("O")))))))) return err; } SKGUnitObject::UnitType SKGUnitObject::getType() const { QString typeString = getAttribute(QStringLiteral("t_type")); return (typeString == QStringLiteral("C") ? CURRENCY : (typeString == QStringLiteral("S") ? SHARE : (typeString == QStringLiteral("1") ? PRIMARY : (typeString == QStringLiteral("2") ? SECONDARY : (typeString == QStringLiteral("I") ? INDEX : OBJECT))))); } SKGError SKGUnitObject::getUnit(SKGUnitObject& oUnit) const { SKGError err; if (getDocument() != nullptr) { err = getDocument()->getObject(QStringLiteral("v_unit"), "id=" % getAttribute(QStringLiteral("rd_unit_id")), oUnit); } return err; } SKGError SKGUnitObject::setUnit(const SKGUnitObject& iUnit) { SKGError err; if (*this == iUnit && iUnit.getID() != 0) { err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Reference unit of a unit cannot be itself.")); } else { err = setAttribute(QStringLiteral("rd_unit_id"), SKGServices::intToString(iUnit.getID())); } return err; } SKGError SKGUnitObject::removeUnit() { return setAttribute(QStringLiteral("rd_unit_id"), QStringLiteral("0")); } SKGError SKGUnitObject::addUnitValue(SKGUnitValueObject& oUnitValue) { SKGError err; if (getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGUnitObject::addUnitValue"))); } else { oUnitValue = SKGUnitValueObject(qobject_cast(getDocument())); err = oUnitValue.setAttribute(QStringLiteral("rd_unit_id"), SKGServices::intToString(getID())); } return err; } SKGError SKGUnitObject::simplify() { SKGListSKGObjectBase values; SKGError err = getUnitValues(values); int nb = values.count(); if (!err && (nb != 0)) { QHash groups; SKGBEGINPROGRESSTRANSACTION(*getDocument(), "#INTERNAL#" % i18nc("Progression step", "Simplify unit"), err, 2) // Build groups { SKGBEGINPROGRESSTRANSACTION(*getDocument(), "#INTERNAL#" % i18nc("Progression step", "Analyze unit"), err, nb) QDate limit1 = QDate::currentDate().addMonths(-3); QDate limit2 = QDate::currentDate().addYears(-1); QDate limit3 = QDate::currentDate().addYears(-3); for (int i = nb - 1; !err && i >= 0 ; --i) { SKGUnitValueObject v(values.at(i)); QDate date = v.getDate(); if (date >= limit1) { // No simplification ==> nothing to do } else if (date >= limit2) { // Simplification by group of 30 days QString key = limit1.addDays(30 * (limit1.daysTo(date) / 30)).toString(); SKGListSKGObjectBase group = groups[key]; group.push_back(v); groups[key] = group; } else if (date >= limit3) { // Simplification by group of 2 months QString key = limit2.addDays(60 * (limit2.daysTo(date) / 60)).toString(); SKGListSKGObjectBase group = groups[key]; group.push_back(v); groups[key] = group; } else { // Simplification by group of 6 months QString key = limit3.addDays(180 * (limit2.daysTo(date) / 180)).toString(); SKGListSKGObjectBase group = groups[key]; group.push_back(v); groups[key] = group; } IFOKDO(err, getDocument()->stepForward(nb - i)) } } IFOKDO(err, getDocument()->stepForward(1)) // Simplify { QList keys = groups.keys(); nb = keys.count(); SKGBEGINPROGRESSTRANSACTION(*getDocument(), "#INTERNAL#" % i18nc("Progression step", "Remove useless values"), err, nb) for (int i = 0; !err && i < nb ; ++i) { const QString& k = keys.at(i); SKGListSKGObjectBase group = groups[k]; // Simplify the group int nb2 = group.count(); // Compute min and max double min = 10e20; double max = 0; for (int j = 0; j < nb2; ++j) { SKGUnitValueObject v1(group.at(j)); double y1 = v1.getQuantity(); min = qMin(y1, min); max = qMax(y1, max); } // Simplify for (int j = 1; !err && j < nb2 - 1; ++j) { SKGUnitValueObject v1(group.at(j)); double y1 = v1.getQuantity(); if (y1 != min && y1 != max) { err = v1.remove(); } } IFOKDO(err, getDocument()->stepForward(i + 1)) } } IFOKDO(err, getDocument()->stepForward(2)) } return err; } SKGError SKGUnitObject::getUnitValues(SKGListSKGObjectBase& oUnitValueList) const { SKGError err = getDocument()->getObjects(QStringLiteral("v_unitvalue"), "rd_unit_id=" % SKGServices::intToString(getID()) % " ORDER BY d_date", oUnitValueList); return err; } SKGError SKGUnitObject::getLastUnitValue(SKGUnitValueObject& oUnitValue) const { return SKGObjectBase::getDocument()->getObject(QStringLiteral("v_unitvalue"), "rd_unit_id=" % SKGServices::intToString(getID()) % " AND d_date=(select MAX(u2.d_date) from unitvalue u2 where u2.rd_unit_id=" % SKGServices::intToString(getID()) % ')', oUnitValue); } SKGError SKGUnitObject::getUnitValue(QDate iDate, SKGUnitValueObject& oUnitValue) const { QString ids = SKGServices::intToString(getID()); QString dates = SKGServices::dateToSqlString(QDateTime(iDate)); SKGError err = SKGObjectBase::getDocument()->getObject(QStringLiteral("v_unitvalue"), "rd_unit_id=" % ids % " AND d_date<='" % dates % "' AND ABS(strftime('%s','" % dates % "')-strftime('%s',d_date))=(select MIN(ABS(strftime('%s','" % dates % "')-strftime('%s',u2.d_date))) from unitvalue u2 where u2.rd_unit_id=" % ids % " AND u2.d_date<='" % dates % "')", oUnitValue); // If not found then get first IFKO(err) err = SKGObjectBase::getDocument()->getObject(QStringLiteral("v_unitvalue"), "rd_unit_id=" % SKGServices::intToString(getID()) % " AND d_date=(select MIN(d_date) from unitvalue where rd_unit_id=" % SKGServices::intToString(getID()) % ')', oUnitValue); return err; } double SKGUnitObject::getAmount(QDate iDate) const { SKGTRACEINFUNC(10) double output = 0; if (getType() == SKGUnitObject::PRIMARY) { output = 1.0; } else if (getDocument() != nullptr) { // Search result in cache QString ids = SKGServices::intToString(getID()); QString dates = SKGServices::dateToSqlString(QDateTime(iDate)); QString key = "unitvalue-" % ids % '-' % dates; QString val = getDocument()->getCachedValue(key); if (val.isEmpty()) { // Get quantity double quantity = 1; SKGUnitValueObject uv; if (getUnitValue(iDate, uv).isSucceeded()) { quantity = uv.getQuantity(); } SKGUnitObject unit; double coef = 1; if (getUnit(unit).isSucceeded()) { if (unit != *this) { coef = unit.getAmount(iDate); } } output = coef * quantity; getDocument()->addValueInCache(key, SKGServices::doubleToString(output)); if (getAttribute(QStringLiteral("i_NBVALUES")) == QStringLiteral("1")) { // Store value for this symbol for all date getDocument()->addValueInCache("unitvalue-" % ids, SKGServices::doubleToString(output)); } } else { output = SKGServices::stringToDouble(val); } } return output; } double SKGUnitObject::getDailyChange(QDate iDate) const { double output = 0; SKGStringListList result; SKGError err = getDocument()->executeSelectSqliteOrder( "SELECT d_date, f_quantity from unitvalue where rd_unit_id=" % SKGServices::intToString(getID()) % " AND d_date<='" % SKGServices::dateToSqlString(QDateTime(iDate)) % "' ORDER BY d_date DESC LIMIT 2", result); if (!err && result.count() == 3) { double v2 = SKGServices::stringToDouble(result.at(1).at(1)); double v1 = SKGServices::stringToDouble(result.at(2).at(1)); QDate d2 = SKGServices::stringToTime(result.at(1).at(0)).date(); QDate d1 = SKGServices::stringToTime(result.at(2).at(0)).date(); output = 100 * (qExp(qLn(v2 / v1) / (SKGServices::nbWorkingDays(d1, d2))) - 1); } return output; } SKGError SKGUnitObject::split(double iRatio) const { SKGError err; if (iRatio > 0) { err = getDocument()->executeSqliteOrder("UPDATE unitvalue SET f_quantity=f_quantity/" % SKGServices::doubleToString(iRatio) % " WHERE rd_unit_id=" % SKGServices::intToString(getID())); IFOKDO(err, getDocument()->executeSqliteOrder("UPDATE suboperation SET f_value=f_value*" % SKGServices::doubleToString(iRatio) % " WHERE rd_operation_id IN (SELECT id FROM operation WHERE rc_unit_id=" % SKGServices::intToString(getID()) % ')')); } else { err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Invalid ratio. Ratio must be greater than 0.")); } return err; } QStringList SKGUnitObject::downloadSources() { QStringList sources; // Get sources from .txt file (old mode) TODO: Remove QString a = QStringLiteral("skrooge/quotes"); const auto list = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, a, QStandardPaths::LocateDirectory); for (const auto& dir : list) { QDirIterator it(dir, QStringList() << QStringLiteral("*.txt")); while (it.hasNext()) { QFileInfo f(it.next()); QString file2 = f.completeBaseName(); if (!sources.contains(file2)) { sources.push_back(file2); } } } // Get sources from.desktop file const auto list2 = KServiceTypeTrader::self()->query(QStringLiteral("skrooge/source")); for (const auto& service : list2) { auto name = service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString(); if (!sources.contains(name)) { sources.push_back(name); } } sources.sort(); return sources; } QString SKGUnitObject::getCommentFromSource(const QString& iSource) { QString output; const auto list2 = KServiceTypeTrader::self()->query(QStringLiteral("skrooge/source")); for (const auto& service : list2) { auto name = service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString(); if (name == iSource) { output = service->property(QStringLiteral("Comment"), QVariant::String).toString(); break; } } return output; } SKGError SKGUnitObject::addSource(const QString& iNewSource, bool iOpenSource) { SKGError err; QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); QDir(path).mkpath(QStringLiteral("skrooge/quotes/")); QString newfile = path + "/skrooge/quotes/" % iNewSource % ".txt"; // Create the new file QSaveFile file(newfile); // Check if file already existing if (!QFile(newfile).exists()) { // Creation of the template if (!file.open(QIODevice::WriteOnly)) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Save file '%1' failed", newfile)); } else { QTextStream out(&file); out << "#" << i18nc("Description test for a text file used to define a source of download", "The URL or the SCRIPT of the source. %1 will be replaced by the internet code of the unit", "%1") << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", "%1 will be replaced by the current day in format yyyy-MM-dd", "%2") << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", "%1 will be replaced by the previous date in format yyyy-MM-dd", "%3") << endl; out << "url=https://server/?s=%1" << endl; out << "or" << endl; out << "script=mydownloadscript %1" << endl << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", "The mode (HTML or CSV or CSVR). In HTML mode, only one value will be extracted from downloaded page. In CSV mode, a value per line will be extracted. CSVR means CSV in reverse mode.") << endl; out << "mode=CSV, CSVR or or HTML" << endl << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", "The regular expression for the price (see %1)", "https://doc.qt.io/qt-5/qregexp.html") << endl; out << "price=" << endl << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", "The regular expression for the date (see %1)", "https://doc.qt.io/qt-5/qregexp.html") << endl; out << "date=" << endl << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", "The format of the date (see %1) or \"UNIX\" for unix time", "https://doc.qt.io/qt-5/qdate.html#fromString-1") << endl; out << "dateformat=yyyy-MM-dd" << endl; // Close file file.commit(); } } // Open the created or already existing file if (iOpenSource) { QDesktopServices::openUrl(QUrl::fromLocalFile(newfile)); } return err; } SKGError SKGUnitObject::deleteSource(const QString& iSource) { SKGError err; QString fileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("skrooge/quotes/") % iSource % ".txt"; // Delete the file QFile file(fileName); if (!file.remove()) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Deletion of '%1' failed", fileName)); } return err; } bool SKGUnitObject::isWritable(const QString& iSource) { QString fileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("skrooge/quotes/") % iSource % ".txt"; return QFileInfo(fileName).isWritable(); } SKGError SKGUnitObject::downloadUnitValue(UnitDownloadMode iMode, int iNbMaxValues) { SKGError err; SKGTRACEINFUNCRC(10, err) QString unitname = getName(); QString code = getInternetCode(); bool invert = code.contains(QStringLiteral(" /")); code.remove(QStringLiteral(" /")); QString source = getDownloadSource(); auto* doc = qobject_cast(getDocument()); if (!code.trimmed().isEmpty() && !source.trimmed().isEmpty() && (doc != nullptr)) { // Get last date QDate firstDate; SKGStringListList result; doc->executeSelectSqliteOrder("SELECT MAX(d_date) FROM unitvalue where rd_unit_id=" % SKGServices::intToString(getID()), result); if (result.count() == 2) { firstDate = SKGServices::stringToTime(result.at(1).at(0)).date().addDays(-1); } if (code.startsWith(QLatin1String("="))) { // MODE FORMULAR // Set 1st january if date is not found if (!firstDate.isValid()) { firstDate.setDate(QDate::currentDate().year(), 1, 1); } // Compute yearly rate double rate = SKGServices::stringToDouble(code.right(code.length() - 1)); // Compute rate for step double step = (iMode == LAST_MONTHLY || iMode == ALL_MONTHLY ? 12.0 : (iMode == LAST_WEEKLY || iMode == ALL_WEEKLY ? 365.0 / 7.0 : 365)); double rate2 = 100.0 * (qExp(qLn(1 + rate / 100.0) / step) - 1.0); // Get last value SKGStringListList result2; double value = 100; doc->executeSelectSqliteOrder("SELECT f_quantity FROM unitvalue where rd_unit_id=(SELECT id from unit where t_name='" % SKGServices::stringToSqlString(unitname) % "') AND d_date='" % SKGServices::dateToSqlString(QDateTime(firstDate)) % '\'', result2); if (result2.count() == 2) { value = SKGServices::stringToDouble(result2.at(1).at(0)); } // Compute and add values while (!err && firstDate <= QDate::currentDate()) { // Compute next date and value if (iMode == LAST_MONTHLY || iMode == ALL_MONTHLY) { firstDate = firstDate.addMonths(1); } else if (iMode == LAST_WEEKLY || iMode == ALL_WEEKLY) { firstDate = firstDate.addDays(7); } else { firstDate = firstDate.addDays(1); } value *= (1 + rate2 / 100.0); // Create value SKGUnitValueObject val; err = addUnitValue(val); IFOKDO(err, val.setDate(firstDate)) IFOKDO(err, val.setQuantity(value)) IFOKDO(err, val.save()) } } else { // Quote download // Set 1st january 1970 if date is not found if (!firstDate.isValid()) { firstDate.setDate(1970, 1, 1); } QString interval = QStringLiteral("1d"); bool last = (iMode == LAST); if (last) { interval = QStringLiteral("1d"); } else if (iMode == LAST_MONTHLY) { interval = QStringLiteral("1mo"); } else if (iMode == LAST_WEEKLY) { interval = QStringLiteral("1wk"); } else if (iMode == LAST_DAILY) { interval = QStringLiteral("1d"); } else if (iMode == ALL_MONTHLY) { firstDate = QDate(1970, 01, 01); interval = QStringLiteral("1mo"); } else if (iMode == ALL_WEEKLY) { firstDate = QDate(1970, 01, 01); interval = QStringLiteral("1wk"); } else if (iMode == ALL_DAILY) { firstDate = QDate(1970, 01, 01); interval = QStringLiteral("1d"); } bool modeScript = false; QString path; QString mode; QString price; QString date; QString dateFormat; QString apiKey; QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/quotes/" % source % ".txt"); if (fileName.isEmpty()) { const auto list2 = KServiceTypeTrader::self()->query(QStringLiteral("skrooge/source")); for (const auto& service : list2) { auto name = service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString(); if (name == source) { path = service->property(QStringLiteral("X-SKROOGE-url"), QVariant::String).toString(); if (path.isEmpty()) { path = service->property(QStringLiteral("X-SKROOGE-script"), QVariant::String).toString(); modeScript = true; } mode = service->property(QStringLiteral("X-SKROOGE-mode"), QVariant::String).toString().toUpper(); price = service->property(QStringLiteral("X-SKROOGE-price"), QVariant::String).toString().replace(QStringLiteral("%1"), code); date = service->property(QStringLiteral("X-SKROOGE-date"), QVariant::String).toString().replace(QStringLiteral("%1"), code); dateFormat = service->property(QStringLiteral("X-SKROOGE-dateformat"), QVariant::String).toString(); if (service->property(QStringLiteral("X-SKROOGE-keyAPI"), QVariant::Bool).toBool()) { apiKey = getDocument()->getParameter("KEYAPI_" + source); } break; } } if (path.isEmpty()) { err = SKGError(ERR_FAIL, i18nc("Error message", "Source of download %1 is not installed.", source)); } } else { // Read source file from .txt file QHash< QString, QString > properties; err = SKGServices::readPropertyFile(fileName, properties); IFOK(err) { path = properties[QStringLiteral("url")]; if (path.isEmpty()) { path = properties[QStringLiteral("script")]; modeScript = true; } mode = properties[QStringLiteral("mode")].toUpper(); price = properties[QStringLiteral("price")].replace(QStringLiteral("%1"), code); date = properties[QStringLiteral("date")].replace(QStringLiteral("%1"), code); dateFormat = properties[QStringLiteral("dateformat")]; } else { doc->sendMessage(i18nc("An information message", "Open url '%1' failed", path), SKGDocument::Warning); } } IFOK(err) { path = path.replace(QStringLiteral("%1"), code); path = path.replace(QStringLiteral("%2"), QDate::currentDate().toString(QStringLiteral("yyyy-MM-dd"))); path = path.replace(QStringLiteral("%3"), firstDate.toString(QStringLiteral("yyyy-MM-dd"))); path = path.replace(QStringLiteral("%4"), interval); path = path.replace(QStringLiteral("%5"), apiKey); SKGTRACEL(1) << "path=[" << path << "]" << endl; SKGTRACEL(1) << "mode=[" << mode << "]" << endl; SKGTRACEL(1) << "price=[" << price << "]" << endl; SKGTRACEL(1) << "date=[" << date << "]" << endl; SKGTRACEL(1) << "dateFormat=[" << dateFormat << "]" << endl; // Download url QByteArray stream; if (modeScript) { path = SKGServices::getFullPathCommandLine(path); SKGTRACEL(1) << "full path=[" << path << "]" << endl; QProcess p; p.start(path); if (p.waitForFinished(1000 * 60 * 2) && p.exitCode() == 0) { stream = p.readAllStandardOutput(); } else { err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message", "The following command line failed with code %2:\n'%1'", path, p.exitCode())); } } else { SKGServices::downloadToStream(QUrl::fromUserInput(path), stream); } if (!err && !stream.isEmpty()) { SKGTRACEL(1) << "stream=[" << stream << "]" << endl; // Parse data QRegExp priceRegExp(price, Qt::CaseInsensitive); QRegExp dateRegExp(date, Qt::CaseInsensitive); QStringList lines = (mode.startsWith(QLatin1String("CSV")) ? SKGServices::splitCSVLine(stream, '\n') : QStringList() << QLatin1String("") << stream); int nb = lines.count(); int nbLoaded = 0; for (int i = 1; i < nb && !err && (i < iNbMaxValues || iNbMaxValues == 0); ++i) { QString data = lines.at(mode == QStringLiteral("CSVR") ? nb - i : i).trimmed(); if (!data.isEmpty()) { SKGTRACEL(1) << "Downloaded data from [" << path << "]=[" << data << "]" << endl; double val = 0.0; if (priceRegExp.indexIn(data) > -1) { val = SKGServices::stringToDouble(priceRegExp.cap(1)); } QString date2; if (dateRegExp.indexIn(data) > -1) { date2 = dateRegExp.cap(1); } SKGTRACEL(1) << "Price found=[" << val << "]" << endl; SKGTRACEL(1) << "Date found=[" << date2 << "]" << endl; // Set value if (val != 0.0) { QDate ds; if (dateFormat == QStringLiteral("UNIX")) { ds = QDateTime::fromTime_t(SKGServices::stringToInt(date2)).date(); } else { ds = QDate::fromString(date2, dateFormat); // Try with an english locale if (!ds.isValid()) { QLocale en(QStringLiteral("en_EN")); ds = en.toDate(date2, dateFormat); } } if (!ds.isValid()) { ds = QDate::currentDate(); SKGTRACE << "WARNING:" << date2 << " not well parsed with format " << dateFormat << endl; } if (!dateFormat.contains(QStringLiteral("yyyy")) && ds.year() < 2000) { ds = ds.addYears(100); } // Creation or update of the value SKGUnitValueObject value; IFOKDO(err, addUnitValue(value)) IFOKDO(err, value.setDate(ds)) IFOKDO(err, value.setQuantity(invert && val != 0 ? 1 / val : val)) IFOKDO(err, value.save()) if (last) { break; } nbLoaded++; } } if (nbLoaded == 0 && !data.isEmpty()) { err = doc->sendMessage(i18nc("Information message", "Price not found for '%1' with regular expression '%2' in line '%3'", getName(), SKGServices::stringToHtml(price), data), SKGDocument::Warning); // TODO(Stephane MANKOWSKI) does not work with html } } } } } } IFKO(err) { err.addError(ERR_FAIL, i18nc("Error message", "Impossible to download unit %1 with Internet code %2 on the source %3.", unitname, code, source)); } return err; } SKGError SKGUnitObject::getUrl(QUrl& oUrl) const { SKGError err; SKGTRACEINFUNCRC(10, err) QString url; QString code = getInternetCode(); code.remove(QStringLiteral(" /")); QString source = getDownloadSource(); if (!code.isEmpty()) { if (code.startsWith(QLatin1String("="))) { // MODE FORMULAR } else { // ALTERNATIVE MODE QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/quotes/" % source % ".txt"); if (fileName.isEmpty()) { const auto list2 = KServiceTypeTrader::self()->query(QStringLiteral("skrooge/source")); for (const auto& service : list2) { auto name = service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString(); if (name == source) { url = service->property(QStringLiteral("X-SKROOGE-url"), QVariant::String).toString().replace(QStringLiteral("%1"), code); url = url.replace(QStringLiteral("%2"), QDate::currentDate().toString(QStringLiteral("yyyy-MM-dd"))); url = url.replace(QStringLiteral("%3"), QDate::currentDate().addDays(-15).toString(QStringLiteral("yyyy-MM-dd"))); break; } } if (url.isEmpty()) { err = SKGError(ERR_FAIL, i18nc("Error message", "Source of download %1 is not installed.", source)); } } else { // Read source file QHash< QString, QString > properties; err = SKGServices::readPropertyFile(fileName, properties); IFOK(err) { url = properties[QStringLiteral("url")].replace(QStringLiteral("%1"), code); url = url.replace(QStringLiteral("%2"), QDate::currentDate().toString(QStringLiteral("yyyy-MM-dd"))); url = url.replace(QStringLiteral("%3"), QDate::currentDate().addDays(-15).toString(QStringLiteral("yyyy-MM-dd"))); } } } } IFOK(err) { oUrl = QUrl(url); } return err; } SKGError SKGUnitObject::openURL() const { QUrl url; SKGError err = getUrl(url); IFKO(err) { err.addError(ERR_FAIL, i18nc("Error message", "Impossible to open unit %1 with Internet code %2.", getName(), getInternetCode())); } else { QDesktopServices::openUrl(url); } return err; } SKGError SKGUnitObject::getOperations(SKGObjectBase::SKGListSKGObjectBase& oOperations) const { SKGError err = getDocument()->getObjects(QStringLiteral("v_operation"), "rc_unit_id=" % SKGServices::intToString(getID()), oOperations); return err; } SKGError SKGUnitObject::merge(const SKGUnitObject& iUnit) { SKGError err; SKGObjectBase::SKGListSKGObjectBase ops; IFOKDO(err, iUnit.getOperations(ops)) int nb = ops.count(); for (int i = 0; !err && i < nb; ++i) { SKGOperationObject op(ops.at(i)); err = op.setUnit(*this); IFOKDO(err, op.save(true, false)) } IFOKDO(err, iUnit.remove(false)) return err; } diff --git a/skgbankmodeler/skgunitobject.h b/skgbankmodeler/skgunitobject.h index fc9287ffc..4b0ca819d 100644 --- a/skgbankmodeler/skgunitobject.h +++ b/skgbankmodeler/skgunitobject.h @@ -1,431 +1,436 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGUNITOBJECT_H #define SKGUNITOBJECT_H /** @file * This file defines classes SKGUnitObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include #include "skgbankmodeler_export.h" #include "skgerror.h" #include "skgnamedobject.h" #include "skgservices.h" class SKGUnitValueObject; class SKGDocumentBank; /** * This class is a unit */ class SKGBANKMODELER_EXPORT SKGUnitObject final : public SKGNamedObject { public: /** * This enumerate defines type of units */ enum UnitType {PRIMARY, /**< to define the main currency (only one)*/ SECONDARY, /**< to define the secondary currency (only one)*/ CURRENCY, /**< for currency: dollar, euro, yen, ... */ SHARE, /**< for stock: Google, EDF, ... */ INDEX, /**< for index: CAC 40, ... */ OBJECT /**< for objects: cars, houses, ... */ }; /** * This enumerate defines type of units */ Q_ENUM(UnitType) /** * This enumerate defines the mode of download */ enum UnitDownloadMode {LAST, /**< last value only*/ LAST_MONTHLY, /**< one value par month since last download*/ LAST_WEEKLY, /**< one value par week since last download*/ LAST_DAILY, /**< one value par day since last download*/ ALL_MONTHLY, /**< one value par month*/ ALL_WEEKLY, /**< one value par week*/ ALL_DAILY /**< one value par day*/ }; /** * This enumerate defines the mode of download */ Q_ENUM(UnitDownloadMode) /** * Default constructor */ explicit SKGUnitObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier in @p iTable of the object */ explicit SKGUnitObject(SKGDocument* iDocument, int iID = 0); /** * Destructor */ virtual ~SKGUnitObject(); /** * Copy constructor * @param iObject the object to copy */ SKGUnitObject(const SKGUnitObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGUnitObject(const SKGNamedObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGUnitObject(const SKGObjectBase& iObject); - /** * Operator affectation * @param iObject the object to copy */ SKGUnitObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGUnitObject& operator= (const SKGUnitObject& iObject); + /** * Get unit information * @return the unit information * @see SKGUnitInfo */ SKGServices::SKGUnitInfo getUnitInfo(); /** * Get unit information * @param iUnitName the currency unit name (nls), or the international code, or the symbol (@see getListofKnownCurrencies) * @return the unit information * @see SKGUnitInfo */ static SKGServices::SKGUnitInfo getUnitInfo(const QString& iUnitName); /** * Create an existing currency unit * @param iDocument the document containing the object * @param iUnitName the currency unit name (nls), or the international code, or the symbol (@see getListofKnownCurrencies) * @param oUnit the created unit object * @return an object managing the error * @see SKGError */ static SKGError createCurrencyUnit(SKGDocumentBank* iDocument, const QString& iUnitName, SKGUnitObject& oUnit); /** * Return the list of known currencies. * @param iIncludingObsolete including obsolete currencies * @return the list of known currencies */ static QStringList getListofKnownCurrencies(bool iIncludingObsolete = false); /** * Return the International code of currencies. * @param iUnitName the currency unit name (nls) * @return the International code */ static QString getInternationalCode(const QString& iUnitName); /** * Convert a value from one unit to another one * @param iValue initial value * @param iUnitFrom source unit * @param iUnitTo target unit * @param iDate the date to use to compute the units values * @return Value in target unit */ // cppcheck-suppress passedByValue static double convert(double iValue, const SKGUnitObject& iUnitFrom, const SKGUnitObject& iUnitTo, QDate iDate = QDate::currentDate()); /** * Set the symbol of this unit * @param iSymbol the symbol * @return an object managing the error * @see SKGError */ SKGError setSymbol(const QString& iSymbol); /** * Get the symbol of this unit * @return the symbol */ QString getSymbol() const; /** * Set the download source of this unit * @param iSource the download source ("" means native source) * @return an object managing the error * @see SKGError */ SKGError setDownloadSource(const QString& iSource); /** * Get the download source of this unit * @return the download source ("" means native source) */ QString getDownloadSource() const; /** * Set the number of decimal of this unit * @param iNb number of decimal * @return an object managing the error * @see SKGError */ SKGError setNumberDecimal(int iNb); /** * Get the number of decimal of this unit * @return the number of decimal */ int getNumberDecimal() const; /** * Set the country of this unit * @param iCountry the country * @return an object managing the error * @see SKGError */ SKGError setCountry(const QString& iCountry); /** * Get the country of this unit * @return the country */ QString getCountry() const; /** * Set the Internet code of this unit * @param iCode the Internet code * @return an object managing the error * @see SKGError */ SKGError setInternetCode(const QString& iCode); /** * Get the Internet code of this unit * @return the Internet code */ QString getInternetCode() const; /** * Set the type of this unit * @param iType the type * @return an object managing the error * @see SKGError */ SKGError setType(SKGUnitObject::UnitType iType); /** * Get the type of this unit * @return the type */ SKGUnitObject::UnitType getType() const; /** * Add a unit value * @param oUnitValue the created unit value * @return an object managing the error. * @see SKGError */ SKGError addUnitValue(SKGUnitValueObject& oUnitValue); /** * Get unit values * @param oUnitValueList the list of unit values of this unit * @return an object managing the error * @see SKGError */ SKGError getUnitValues(SKGListSKGObjectBase& oUnitValueList) const; /** * Get last unit value * @param oUnitValue the last unit value * @return an object managing the error. * @see SKGError */ SKGError getLastUnitValue(SKGUnitValueObject& oUnitValue) const; /** * Get unit value at a date * @param iDate the date * @param oUnitValue the last unit value * @return an object managing the error. * @see SKGError */ // cppcheck-suppress passedByValue SKGError getUnitValue(QDate iDate, SKGUnitValueObject& oUnitValue) const; /** * Download values * @param iMode download mode (used only for native source) * @param iNbMaxValues nb max of values to download (used only for native source) * @return an object managing the error * @see SKGError */ SKGError downloadUnitValue(UnitDownloadMode iMode = LAST, int iNbMaxValues = 50); /** * Open the URL where the values are downloaded * @return an object managing the error * @see SKGError */ SKGError openURL() const; /** * Get the URL where the values are downloaded * @param oUrl the url * @return an object managing the error * @see SKGError */ SKGError getUrl(QUrl& oUrl) const; /** * Get the list of sources for the download * @return the list of sources */ static QStringList downloadSources(); /** * Get the source comment/help * @param iSource the source name (@see downloadSources) * @return the comment/help */ static QString getCommentFromSource(const QString& iSource); /** * Create a new source * @param iNewSource the name of the new source * @param iOpenSource to open the created source with the appropriate editor * @return an object managing the error * @see SKGError */ static SKGError addSource(const QString& iNewSource, bool iOpenSource = true); /** * To know if a source can be modified or deleted * @param iSource the name of the source to modified or deleted * @return true or false */ static bool isWritable(const QString& iSource); /** * Delete a new source * @param iSource the name of the source to delete * @return an object managing the error * @see SKGError */ static SKGError deleteSource(const QString& iSource); /** * Get amount of the unit at a date * @param iDate date * @return amount of the unit */ // cppcheck-suppress passedByValue double getAmount(QDate iDate = QDate::currentDate()) const; /** * Get daily change in percentage at a date. * @param iDate date * @return daily change */ // cppcheck-suppress passedByValue double getDailyChange(QDate iDate = QDate::currentDate()) const; /** * Set the unit * @param iUnit the unit * @return an object managing the error * @see SKGError */ SKGError setUnit(const SKGUnitObject& iUnit); /** * Remove the unit * @return an object managing the error * @see SKGError */ SKGError removeUnit(); /** * Get the unit * @param oUnit the unit * @return an object managing the error * @see SKGError */ SKGError getUnit(SKGUnitObject& oUnit) const; /** * Split unit. * All quantity in operations will be multiply by iRatio. * All unit values will be divise by iRatio. * @param iRatio the split ratio * @return an object managing the error * @see SKGError */ SKGError split(double iRatio) const; /** * Get all operations of this unit * @param oOperations all operations of this unit * @return an object managing the error * @see SKGError */ SKGError getOperations(SKGListSKGObjectBase& oOperations) const; /** * Merge iUnit in current unit * @param iUnit the unit. All operations will be transferred into this unit. The unit will be removed * @return an object managing the error * @see SKGError */ SKGError merge(const SKGUnitObject& iUnit); /** * Remove all unit values not relevant. * The curve is preserved but useless values are removed. * @return an object managing the error. * @see SKGError */ SKGError simplify(); protected: /** * Get where clause needed to identify objects. * For this class, the whereclause is based on name * @return the where clause */ QString getWhereclauseId() const override; private: static QList currencies; }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGUnitObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbankmodeler/skgunitvalueobject.cpp b/skgbankmodeler/skgunitvalueobject.cpp index ba83d080c..e171bf718 100644 --- a/skgbankmodeler/skgunitvalueobject.cpp +++ b/skgbankmodeler/skgunitvalueobject.cpp @@ -1,99 +1,105 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file defines classes SKGUnitValueObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgunitvalueobject.h" #include #include "skgdocument.h" #include "skgunitobject.h" SKGUnitValueObject::SKGUnitValueObject() : SKGUnitValueObject(nullptr) {} SKGUnitValueObject::SKGUnitValueObject(SKGDocument* iDocument, int iID) : SKGObjectBase(iDocument, QStringLiteral("v_unitvalue"), iID) {} SKGUnitValueObject::SKGUnitValueObject(const SKGUnitValueObject& iObject) = default; SKGUnitValueObject::SKGUnitValueObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("unitvalue")) { copyFrom(iObject); } else { *this = SKGObjectBase(iObject.getDocument(), QStringLiteral("v_unitvalue"), iObject.getID()); } } SKGUnitValueObject& SKGUnitValueObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } +SKGUnitValueObject& SKGUnitValueObject::operator= (const SKGUnitValueObject& iObject) +{ + copyFrom(iObject); + return *this; +} + SKGUnitValueObject::~SKGUnitValueObject() = default; SKGError SKGUnitValueObject::setQuantity(double iValue) { if (iValue < 0) { return SKGError(ERR_INVALIDARG, i18nc("Error message", "Value of a currency cannot be a negative value")); } return setAttribute(QStringLiteral("f_quantity"), SKGServices::doubleToString(iValue)); } double SKGUnitValueObject::getQuantity() const { return SKGServices::stringToDouble(getAttribute(QStringLiteral("f_quantity"))); } SKGError SKGUnitValueObject::setDate(QDate iDate) { return setAttribute(QStringLiteral("d_date"), SKGServices::dateToSqlString(QDateTime(iDate))); } QDate SKGUnitValueObject::getDate() const { return SKGServices::stringToTime(getAttribute(QStringLiteral("d_date"))).date(); } QString SKGUnitValueObject::getWhereclauseId() const { // Could we use the id QString output = SKGObjectBase::getWhereclauseId(); if (output.isEmpty()) { // No, so we use the date and parent if (!(getAttribute(QStringLiteral("d_date")).isEmpty()) && !(getAttribute(QStringLiteral("rd_unit_id")).isEmpty())) { output = "d_date='" % getAttribute(QStringLiteral("d_date")) % "' AND rd_unit_id=" % getAttribute(QStringLiteral("rd_unit_id")); } } return output; } SKGError SKGUnitValueObject::getUnit(SKGUnitObject& oUnit) const { SKGError err; if (getDocument() != nullptr) { err = getDocument()->getObject(QStringLiteral("v_unit"), "id=" % getAttribute(QStringLiteral("rd_unit_id")), oUnit); } return err; } diff --git a/skgbankmodeler/skgunitvalueobject.h b/skgbankmodeler/skgunitvalueobject.h index 57a957992..28b06fdae 100644 --- a/skgbankmodeler/skgunitvalueobject.h +++ b/skgbankmodeler/skgunitvalueobject.h @@ -1,121 +1,127 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 SKGUNITVALUEOBJECT_H #define SKGUNITVALUEOBJECT_H /** @file * This file defines classes SKGUnitValueObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgbankmodeler_export.h" #include "skgerror.h" #include "skgobjectbase.h" class SKGUnitObject; /** * This class is a value at a time for a unit */ class SKGBANKMODELER_EXPORT SKGUnitValueObject final : public SKGObjectBase { public: /** * Default constructor */ explicit SKGUnitValueObject(); /** * Constructor * @param iDocument the document containing the object * @param iID the identifier in @p iTable of the object */ explicit SKGUnitValueObject(SKGDocument* iDocument, int iID = 0); /** * Copy constructor * @param iObject the object to copy */ SKGUnitValueObject(const SKGUnitValueObject& iObject); /** * Copy constructor * @param iObject the object to copy */ explicit SKGUnitValueObject(const SKGObjectBase& iObject); /** * Operator affectation * @param iObject the object to copy */ SKGUnitValueObject& operator= (const SKGObjectBase& iObject); + /** + * Operator affectation + * @param iObject the object to copy + */ + SKGUnitValueObject& operator= (const SKGUnitValueObject& iObject); + /** * Destructor */ virtual ~SKGUnitValueObject(); /** * Set the quantity for the date of this unit * @param iValue the quantity * @return an object managing the error * @see SKGError */ SKGError setQuantity(double iValue); /** * Get the quantity for the date of this unit * @return the quantity */ double getQuantity() const; /** * Set date of this value * @param iDate the date * @return an object managing the error * @see SKGError */ // cppcheck-suppress passedByValue SKGError setDate(QDate iDate); /** * Get date of this value * @return the date */ QDate getDate() const; /** * Get the parent unit * @param oUnit the parent unit * @return an object managing the error * @see SKGError */ SKGError getUnit(SKGUnitObject& oUnit) const; protected: /** * Get where clause needed to identify objects. * For this class, the whereclause is based on date + unit * @return the where clause */ QString getWhereclauseId() const override; }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGUnitValueObject, Q_MOVABLE_TYPE); #endif diff --git a/skgbasemodeler/skgdocument.cpp b/skgbasemodeler/skgdocument.cpp index 72b89ebee..90d0ee665 100644 --- a/skgbasemodeler/skgdocument.cpp +++ b/skgbasemodeler/skgdocument.cpp @@ -1,3503 +1,3502 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file implements classes SKGDocument. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgdocument.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "skgdocumentprivate.h" #include "skgerror.h" #include "skgpropertyobject.h" #include "skgreport.h" #include "skgservices.h" #include "skgtraces.h" #include "skgtransactionmng.h" #define SQLDRIVERNAME QStringLiteral("SKGSQLCIPHER") /** * Custom sqlite function. */ static void sleepFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); if (Q_LIKELY(data1)) { auto s = SKGServices::stringToInt(QString(reinterpret_cast(data1), len1 / sizeof(QChar))); QThread::sleep(s); sqlite3_result_text(context, "OK", 2, SQLITE_TRANSIENT); } } /** * Custom sqlite function. */ static void periodFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); int len2 = sqlite3_value_bytes16(argv[ 1 ]); const void* data2 = sqlite3_value_text16(argv[ 1 ]); if (Q_LIKELY(data1 && data2)) { QDate date = SKGServices::stringToTime(QString(reinterpret_cast(data1), len1 / sizeof(QChar))).date(); QString format = QString::fromRawData(reinterpret_cast(data2), len2 / sizeof(QChar)).toUpper(); QString period = SKGServices::dateToPeriod(date, format); QByteArray output = period.toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } /** * Custom sqlite function. */ static void formattedDateFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); int len2 = sqlite3_value_bytes16(argv[ 1 ]); const void* data2 = sqlite3_value_text16(argv[ 1 ]); if (Q_LIKELY(data1 && data2)) { QString string(reinterpret_cast(data1), len1 / sizeof(QChar)); QString format = QString::fromRawData(reinterpret_cast(data2), len2 / sizeof(QChar)); QString date = QDate::fromString(string, QStringLiteral("yyyy-MM-dd")).toString(format); QByteArray output = date.toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } /** * Custom sqlite function. */ static void dateFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); int len2 = sqlite3_value_bytes16(argv[ 1 ]); const void* data2 = sqlite3_value_text16(argv[ 1 ]); if (Q_LIKELY(data1 && data2)) { QString string(reinterpret_cast(data1), len1 / sizeof(QChar)); QString format = QString::fromRawData(reinterpret_cast(data2), len2 / sizeof(QChar)); QString date = SKGServices::dateToSqlString(string, format).trimmed(); if (date.isEmpty()) { date = QDate::currentDate().toString(QStringLiteral("yyyy-MM-dd")); } QByteArray output = date.toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } /** * Custom sqlite function. */ static void currencyFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); int len2 = sqlite3_value_bytes16(argv[ 1 ]); const void* data2 = sqlite3_value_text16(argv[ 1 ]); if (Q_LIKELY(data1 && data2)) { double string = SKGServices::stringToDouble(QString::fromRawData(reinterpret_cast(data1), len1 / sizeof(QChar))); QString symbol = QString::fromRawData(reinterpret_cast(data2), len2 / sizeof(QChar)); QString currency = SKGServices::toCurrencyString(string, symbol); QByteArray output = currency.toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } /** * Custom sqlite function. */ static void xorFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); int len2 = sqlite3_value_bytes16(argv[ 1 ]); const void* data2 = sqlite3_value_text16(argv[ 1 ]); if (Q_LIKELY(data1 && data2)) { auto string = QString::fromRawData(reinterpret_cast(data1), len1 / sizeof(QChar)).toUtf8(); auto key = QString::fromRawData(reinterpret_cast(data2), len2 / sizeof(QChar)).toUtf8(); if (string.startsWith(QByteArray("# "))) { // Decrypt string = QByteArray::fromHex(string.right(string.length() - 2)); QByteArray estring; for (int i = 0; i < string.size(); ++i) { estring += static_cast(string[i] ^ key[i % key.size()]); } QByteArray output = estring; sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } else { // Encrypt QByteArray estring; for (int i = 0; i < string.size(); ++i) { estring += static_cast(string[i] ^ key[i % key.size()]); } QByteArray output = "# " + estring.toHex(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } } static void xordoubleFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); int len2 = sqlite3_value_bytes16(argv[ 1 ]); const void* data2 = sqlite3_value_text16(argv[ 1 ]); if (Q_LIKELY(data1 && data2)) { auto d = SKGServices::stringToDouble(QString::fromRawData(reinterpret_cast(data1), len1 / sizeof(QChar)).toUtf8()); auto key = QString::fromRawData(reinterpret_cast(data2), len2 / sizeof(QChar)).toUtf8(); int kk = 0; for (int i = 0; i < key.size(); ++i) { kk += key[i]; } QByteArray output = SKGServices::doubleToString(static_cast(kk) - d).toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } /** * Custom sqlite function. */ static void wordFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); int len2 = sqlite3_value_bytes16(argv[ 1 ]); const void* data2 = sqlite3_value_text16(argv[ 1 ]); if (Q_LIKELY(data1 && data2)) { QString string1(reinterpret_cast(data1), len1 / sizeof(QChar)); string1 = string1.simplified(); QRegularExpression re(QStringLiteral("(\\w+)")); QRegularExpressionMatchIterator i = re.globalMatch(string1); QStringList list; while (i.hasNext()) { QRegularExpressionMatch match = i.next(); QString word = match.captured(1); list << word; } int pos = SKGServices::stringToInt(QString::fromRawData(reinterpret_cast(data2), len2 / sizeof(QChar))); if (pos == 0) { pos = 1; } else if (pos > list.count()) { pos = list.count(); } else if (pos < -list.count()) { pos = 1; } else if (pos < 0) { pos = list.count() + pos + 1; } QByteArray output = list[pos - 1].toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } /** * Custom sqlite function. */ static void wildcardFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); int len2 = sqlite3_value_bytes16(argv[ 1 ]); const void* data2 = sqlite3_value_text16(argv[ 1 ]); if (Q_LIKELY(data1 && data2)) { QString string1(reinterpret_cast(data1), len1 / sizeof(QChar)); QString string2 = QString::fromRawData(reinterpret_cast(data2), len2 / sizeof(QChar)); QRegExp pattern(string1, Qt::CaseInsensitive, QRegExp::Wildcard); if (pattern.isValid()) { sqlite3_result_int(context, static_cast(pattern.exactMatch(string2))); } else { sqlite3_result_error(context, pattern.errorString().toUtf8().constData(), -1); } } } /** * Custom sqlite function. */ static void regexpFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); int len2 = sqlite3_value_bytes16(argv[ 1 ]); const void* data2 = sqlite3_value_text16(argv[ 1 ]); if (Q_LIKELY(data1 && data2)) { QString string1(reinterpret_cast(data1), len1 / sizeof(QChar)); QString string2 = QString::fromRawData(reinterpret_cast(data2), len2 / sizeof(QChar)); QRegExp pattern(string1, Qt::CaseInsensitive, QRegExp::RegExp2); if (pattern.isValid()) { sqlite3_result_int(context, static_cast(pattern.exactMatch(string2))); } else { sqlite3_result_error(context, pattern.errorString().toUtf8().constData(), -1); } } } /** * Custom sqlite function. */ static void regexpCaptureFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); int len2 = sqlite3_value_bytes16(argv[ 1 ]); const void* data2 = sqlite3_value_text16(argv[ 1 ]); int len3 = sqlite3_value_bytes16(argv[ 2 ]); const void* data3 = sqlite3_value_text16(argv[ 2 ]); if (Q_LIKELY(data1 && data2 && data3)) { - int pos = SKGServices::stringToInt(QString::fromRawData(reinterpret_cast(data3), len3 / sizeof(QChar))); QString string1(reinterpret_cast(data1), len1 / sizeof(QChar)); QString string2 = QString::fromRawData(reinterpret_cast(data2), len2 / sizeof(QChar)); QRegExp pattern(string1, Qt::CaseInsensitive, QRegExp::RegExp2); if (pattern.isValid()) { pattern.indexIn(string2); QByteArray output = pattern.capturedTexts().value(pos).toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } else { sqlite3_result_error(context, pattern.errorString().toUtf8().constData(), -1); } } } /** * Custom sqlite function. */ static void upperFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); if (Q_LIKELY(data1)) { QByteArray output = QString::fromRawData(reinterpret_cast(data1), len1 / sizeof(QChar)).toUpper().toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } /** * Custom sqlite function. */ static void nextFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); if (Q_LIKELY(data1)) { QByteArray output = SKGServices::getNextString(QString::fromRawData(reinterpret_cast(data1), len1 / sizeof(QChar))).toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } /** * Custom sqlite function. */ static void lowerFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); if (Q_LIKELY(data1)) { QByteArray output = QString::fromRawData(reinterpret_cast(data1), len1 / sizeof(QChar)).toLower().toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } /** * Custom sqlite function. */ static void capitalizeFunction(sqlite3_context* context, int /*argc*/, sqlite3_value** argv) { int len1 = sqlite3_value_bytes16(argv[ 0 ]); const void* data1 = sqlite3_value_text16(argv[ 0 ]); if (Q_LIKELY(data1)) { QString str = QString::fromRawData(reinterpret_cast(data1), len1 / sizeof(QChar)); QByteArray output = (str.at(0).toUpper() + str.mid(1).toLower()).toUtf8(); sqlite3_result_text(context, output.constData(), output.size(), SQLITE_TRANSIENT); } } static SKGError addSqliteAddon(QSqlDatabase* iDb) { SKGError err; auto* sqlite_handle = iDb->driver()->handle().value(); if (sqlite_handle != nullptr) { sqlite3_create_function(sqlite_handle, "REGEXP", 2, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, ®expFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "REGEXPCAPTURE", 3, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, ®expCaptureFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "WILDCARD", 2, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &wildcardFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "WORD", 2, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &wordFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "TODATE", 2, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &dateFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "TOFORMATTEDDATE", 2, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &formattedDateFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "PERIOD", 2, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &periodFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "SLEEP", 1, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &sleepFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "TOCURRENCY", 2, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, ¤cyFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "XOR", 2, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &xorFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "XORD", 2, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &xordoubleFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "UPPER", 1, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &upperFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "LOWER", 1, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &lowerFunction, nullptr, nullptr); sqlite3_create_function(sqlite_handle, "NEXT", 1, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &nextFunction, nullptr, nullptr); int rc = sqlite3_create_function(sqlite_handle, "CAPITALIZE", 1, SQLITE_UTF16 | SQLITE_DETERMINISTIC, nullptr, &capitalizeFunction, nullptr, nullptr); if (rc != SQLITE_OK) { err = SKGError(SQLLITEERROR + rc, QStringLiteral("sqlite3_create_function failed")); } } else { SKGTRACE << "WARNING: Custom sqlite functions not added" << endl; } return err; } SKGDocument::SKGDocument() : d(new SKGDocumentPrivate()) { SKGTRACEINFUNC(10) // Set the QThreadPool // QThreadPool::globalInstance()->setMaxThreadCount(3*QThread::idealThreadCount()); // DBUS registration QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/skg/skgdocument"), this, QDBusConnection::ExportAllContents); dbus.registerService(QStringLiteral("org.skg")); // Initialisation of undoable tables SKGListNotUndoable.push_back(QStringLiteral("T.doctransaction")); SKGListNotUndoable.push_back(QStringLiteral("T.doctransactionitem")); SKGListNotUndoable.push_back(QStringLiteral("T.doctransactionmsg")); // Database unique identifier ++SKGDocumentPrivate::m_databaseUniqueIdentifier; d->m_databaseIdentifier = "SKGDATABASE_" % SKGServices::intToString(SKGDocumentPrivate::m_databaseUniqueIdentifier); // Initialisation of backup file parameters setBackupParameters(QLatin1String(""), QStringLiteral(".old")); // 320157 vvvv // Disable OS lock sqlite3_vfs* vfs = sqlite3_vfs_find("unix-none"); if (Q_LIKELY(vfs)) { sqlite3_vfs_register(vfs, 1); } else { SKGTRACE << "WARNING: Impossible to use the 'unix-none' vfs mode of sqlite3. Use:'" << sqlite3_vfs_find(nullptr)->zName << "'" << endl; } // 320157 ^^^^ } SKGDocument::~SKGDocument() { SKGTRACEINFUNC(10) close(); d->m_progressFunction = nullptr; d->m_progressData = nullptr; d->m_checkFunctions.clear(); for (auto w : qAsConst(d->m_watchers)) { delete w; } d->m_watchers.clear(); delete d->m_cacheSql; d->m_cacheSql = nullptr; delete d; d = nullptr; } QString SKGDocument::getUniqueIdentifier() const { return d->m_uniqueIdentifier; } QString SKGDocument::getDatabaseIdentifier() const { return d->m_databaseIdentifier; } SKGError SKGDocument::setProgressCallback(FuncProgress iProgressFunction, void* iData) { d->m_progressFunction = iProgressFunction; d->m_progressData = iData; return SKGError(); } SKGError SKGDocument::addEndOfTransactionCheck(SKGError(*iCheckFunction)(SKGDocument*)) { d->m_checkFunctions.append(iCheckFunction); return SKGError(); } SKGError SKGDocument::stepForward(int iPosition, const QString& iText) { SKGError err; // Increase the step for the last transaction if (Q_LIKELY(getDepthTransaction())) { d->m_posStepForTransaction.pop_back(); d->m_posStepForTransaction.push_back(iPosition); } // Check if a callback function exists if (Q_LIKELY(d->m_progressFunction)) { // YES ==> compute double min = 0; double max = 100; bool emitevent = true; auto nbIt = d->m_nbStepForTransaction.constBegin(); auto posIt = d->m_posStepForTransaction.constBegin(); for (; emitevent && nbIt != d->m_nbStepForTransaction.constEnd(); ++nbIt) { int p = *posIt; int n = *nbIt; if (Q_UNLIKELY(p < 0 || p > n)) { p = n; } if (Q_LIKELY(n != 0)) { double pmin = min; double pmax = max; min = pmin + (pmax - pmin) * (static_cast(p) / static_cast(n)); max = pmin + (pmax - pmin) * (static_cast(p + 1) / static_cast(n)); if (Q_UNLIKELY(max > 100)) { max = 100; } } else { emitevent = false; } ++posIt; } int posPercent = rint(min); // Call the call back if (emitevent) { d->m_inProgress = true; QString text; qint64 time = QDateTime::currentMSecsSinceEpoch() - d->m_timeBeginTransaction; if (Q_UNLIKELY(time > 3000)) { text = iText; if (text.isEmpty()) { text = d->m_nameForTransaction.at(d->m_nameForTransaction.count() - 1); } } if (Q_LIKELY(d->m_progressFunction(posPercent, time, text, d->m_progressData) != 0)) { err.setReturnCode(ERR_ABORT).setMessage(i18nc("User interrupted something that Skrooge was performing", "The current operation has been interrupted")); // Remove all untransactionnal messaged m_unTransactionnalMessages.clear(); } d->m_inProgress = false; } } return err; } SKGError SKGDocument::beginTransaction(const QString& iName, int iNbStep, const QDateTime& iDate, bool iRefreshViews) { SKGError err; SKGTRACEINFUNCRC(5, err) SKGTRACEL(10) << "Input parameter [name]=[" << iName << "] [nb step]=[" << iNbStep << "] [refresh]=[" << (iRefreshViews ? QStringLiteral("Y") : QStringLiteral("N")) << ']' << endl; bool overrideCursor = false; if (d->m_nbStepForTransaction.isEmpty()) { // Open SQLtransaction err = executeSqliteOrder(QStringLiteral("BEGIN;")); IFOK(err) { overrideCursor = true; // Create undo redo transaction err = executeSqliteOrder(QStringLiteral("insert into doctransaction (d_date, t_name, i_parent") % (!iRefreshViews ? ", t_refreshviews" : "") % ") values " "('" % SKGServices::timeToString(iDate) % "','" % SKGServices::stringToSqlString(iName) % "', " % SKGServices::intToString(getTransactionToProcess(SKGDocument::UNDO)) % (!iRefreshViews ? ",'N'" : "") % ");"); addValueInCache(QStringLiteral("SKG_REFRESH_VIEW"), (iRefreshViews ? QStringLiteral("Y") : QStringLiteral("N"))); d->m_currentTransaction = getTransactionToProcess(SKGDocument::UNDO); d->m_timeBeginTransaction = QDateTime::currentMSecsSinceEpoch(); } } else { // A transaction already exists // Check if the child transaction is a opened in the progress callback if (d->m_inProgress) { err.setReturnCode(ERR_FAIL).setMessage(i18nc("Something went wrong with SQL transactions", "A transaction cannot be started during execution of another one")); } } IFOK(err) { d->m_nbStepForTransaction.push_back(iNbStep); d->m_posStepForTransaction.push_back(iNbStep); QString n = iName; n = n.remove(QStringLiteral("#INTERNAL#")); if (n.isEmpty() && !d->m_nameForTransaction.isEmpty()) { n = d->m_nameForTransaction.at(d->m_nameForTransaction.count() - 1); } d->m_nameForTransaction.push_back(n); if (iNbStep > 0) { err = stepForward(0); } } else { executeSqliteOrder(QStringLiteral("ROLLBACK;")); } if (Q_LIKELY(overrideCursor && !err && qobject_cast(qApp) != nullptr)) { // clazy:excludeall=unneeded-cast QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } return err; } SKGError SKGDocument::checkExistingTransaction() const { SKGError err; if (d->m_nbStepForTransaction.isEmpty()) { err.setReturnCode(ERR_ABORT).setMessage(i18nc("Something went wrong with SQL transactions", "A transaction must be opened to do this action")); } return err; } SKGError SKGDocument::endTransaction(bool succeedded) { SKGError err; SKGError errOverwritten; SKGTRACEINFUNCRC(5, err) if (Q_UNLIKELY(d->m_nbStepForTransaction.empty())) { err.setReturnCode(ERR_ABORT).setMessage(i18nc("Something went wrong with SQL transactions", "Closing transaction failed because too many transactions ended")); } else { stepForward(d->m_nbStepForTransaction.at(d->m_nbStepForTransaction.count() - 1)); // =100% if (Q_LIKELY(d->m_nbStepForTransaction.size())) { // This test is needed. It is a security in some cases. d->m_nbStepForTransaction.pop_back(); d->m_posStepForTransaction.pop_back(); d->m_nameForTransaction.pop_back(); } QString currentTransactionString = SKGServices::intToString(getCurrentTransaction()); if (d->m_nbStepForTransaction.empty()) { QStringList listModifiedTables; // Check if (succeedded) { auto cachepointer = d->m_cache; d->m_cache = QHash(); for (auto check : qAsConst(d->m_checkFunctions)) { errOverwritten = check(this); IFKO(errOverwritten) { succeedded = false; SKGTRACEL(5) << "Transaction cancelled by a check" << endl; break; } } d->m_cache = cachepointer; } if (succeedded) { // Link items on current transaction IFOK(err) { err = executeSqliteOrder("UPDATE doctransactionitem set rd_doctransaction_id=" % currentTransactionString % " WHERE rd_doctransaction_id=0;"); } // Optimization of the current transaction IFOK(err) { SKGStringListList listTmp; err = executeSelectSqliteOrder("SELECT count(1) FROM doctransactionitem where rd_doctransaction_id=" % currentTransactionString, listTmp); IFOK(err) { int nbItem = SKGServices::stringToInt(listTmp.at(1).at(0)); if (nbItem == 0) { // Optimization is needed // Get non hidden messages SKGMessageList messages; getMessages(getCurrentTransaction(), messages, false); // Delete current transaction err = executeSqliteOrder("DELETE FROM doctransaction WHERE id=" % currentTransactionString); int nb = messages.count(); for (int i = 0; i < nb; ++i) { m_unTransactionnalMessages.push_back(messages.at(i)); } } } } // Optimization 2: remove duplicate orders IFOK(err) { QString wc = "DELETE FROM doctransactionitem WHERE id IN " "(SELECT a.id FROM doctransactionitem a INDEXED BY idx_doctransactionitem_optimization, doctransactionitem b INDEXED BY idx_doctransactionitem_optimization " "WHERE a.rd_doctransaction_id=" % currentTransactionString % " AND b.rd_doctransaction_id=" % currentTransactionString % " AND a.i_object_id=b.i_object_id AND a.t_object_table=b.t_object_table AND b.t_action=a.t_action AND b.t_sqlorder=a.t_sqlorder AND a.id>b.id );"; err = executeSqliteOrder(wc); } // Get current transaction information to be able to emit envent in case of SKG_UNDO_MAX_DEPTH=0 IFOK(err) { err = this->getDistinctValues(QStringLiteral("doctransactionitem"), QStringLiteral("t_object_table"), "rd_doctransaction_id=" % currentTransactionString, listModifiedTables); } // Remove oldest transaction IFOK(err) { QString maxdepthstring = getParameter(QStringLiteral("SKG_UNDO_MAX_DEPTH")); if (maxdepthstring.isEmpty()) { maxdepthstring = QStringLiteral("-1"); } int maxdepth = SKGServices::stringToInt(maxdepthstring); if (maxdepth >= 0) { err = executeSqliteOrder("delete from doctransaction where id in (select id from doctransaction limit max(0,((select count(1) from doctransaction)-(" % maxdepthstring % "))))"); } } // Remove SKGDocument::REDO transactions if we are not in a undo / redo transaction if (d->m_inundoRedoTransaction == 0) { int i = 0; while (((i = getTransactionToProcess(SKGDocument::REDO)) != 0) && !err) { err = executeSqliteOrder("delete from doctransaction where id=" % SKGServices::intToString(i)); } } // Commit the transaction IFOK(err) { err = executeSqliteOrder(QStringLiteral("COMMIT;")); } } // clean cache sql (must be done before event emit) d->m_cacheSql->clear(); if (!succeedded || err) { // Rollback the transaction SKGError err2 = executeSqliteOrder(QStringLiteral("ROLLBACK;")); // delete the transaction IFOKDO(err2, executeSqliteOrder("delete from doctransaction where id=" % currentTransactionString)) if (err2) { err.addError(err2.getReturnCode(), err2.getMessage()); } } else { // For better performance, events are submitted only for the first recursive undo if (Q_UNLIKELY(d->m_inundoRedoTransaction <= 1)) { // Is it a light transaction? bool lightTransaction = (getCachedValue(QStringLiteral("SKG_REFRESH_VIEW")) != QStringLiteral("Y")); // Emit modification events QStringList tablesRefreshed; tablesRefreshed.reserve(listModifiedTables.count()); for (const auto& table : qAsConst(listModifiedTables)) { Q_EMIT tableModified(table, getCurrentTransaction(), lightTransaction); tablesRefreshed.push_back(table); } // Remove temporary transaction if needed IFOKDO(err, executeSqliteOrder(QStringLiteral("delete from doctransaction where t_name LIKE '#INTERNAL#%';"))) Q_EMIT tableModified(QStringLiteral("doctransaction"), getCurrentTransaction(), lightTransaction); Q_EMIT tableModified(QStringLiteral("doctransactionitem"), getCurrentTransaction(), lightTransaction); // WARNING: list is modified during treatement for (int i = 0; !err && i < listModifiedTables.count(); ++i) { QString table = listModifiedTables.at(i); QStringList toAdd = getImpactedViews(table); int nbToAdd = toAdd.count(); for (int j = 0; !err && j < nbToAdd; ++j) { const QString& toAddTable = toAdd.at(j); if (!listModifiedTables.contains(toAddTable)) { // Compute materialized view of modified table if (!lightTransaction) { err = computeMaterializedViews(toAddTable); } listModifiedTables.push_back(toAddTable); } } } // Emit events for (int i = tablesRefreshed.count(); i < listModifiedTables.count(); ++i) { Q_EMIT tableModified(listModifiedTables.at(i), 0, lightTransaction); } Q_EMIT transactionSuccessfullyEnded(getCurrentTransaction()); } } // clean cache d->m_cache.clear(); d->m_currentTransaction = 0; if (Q_LIKELY(qobject_cast(qApp) != nullptr)) { // clazy:excludeall=unneeded-cast QApplication::restoreOverrideCursor(); } } } IFOK(err) { err = errOverwritten; } return err; } SKGError SKGDocument::removeAllTransactions() { SKGError err; SKGTRACEINFUNCRC(10, err) // Check if a transaction is still opened err = checkExistingTransaction(); IFOK(err) err.setReturnCode(ERR_ABORT).setMessage(i18nc("Something went wrong with SQL transactions", "Remove of transactions is forbidden inside a transaction")); else { err = SKGDocument::beginTransaction(QStringLiteral("#INTERNAL#")); IFOKDO(err, executeSqliteOrder(QStringLiteral("delete from doctransaction"))) SKGENDTRANSACTION(this, err) // Force the save d->m_lastSavedTransaction = -1; } return err; } SKGError SKGDocument::computeMaterializedViews(const QString& iTable) const { SKGError err; SKGTRACEINFUNCRC(5, err) // Compute additional where clause QStringList tables; if (d->m_MaterializedViews.contains(iTable)) { tables = d->m_MaterializedViews[iTable]; } else { QString wc; if (!iTable.isEmpty()) { QString t = iTable; if (t.startsWith(QLatin1String("v_"))) { t.replace(0, 2, QStringLiteral("vm_")); } wc = " AND name='" % t % '\''; } // Get list of materialized table err = getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), "type='table' AND name LIKE 'vm_%' " % wc, tables); d->m_MaterializedViews[iTable] = tables; } // Refresh tables int nb = tables.count(); for (int i = 0; !err && i < nb; ++i) { const QString& table = tables.at(i); QString view = table; view.replace(0, 3, QStringLiteral("v_")); // Remove previous table { SKGTRACEINRC(5, "SKGDocument::computeMaterializedViews-drop-" % table, err) err = executeSqliteOrder("DROP TABLE IF EXISTS " % table); } { // Recreate table SKGTRACEINRC(5, "SKGDocument::computeMaterializedViews-create-" % table, err) IFOKDO(err, executeSqliteOrder("CREATE TABLE " % table % " AS SELECT * FROM " % view)) } } return err; } SKGError SKGDocument::sendMessage(const QString& iMessage, MessageType iMessageType, const QString& iAction) { SKGError err; SKGTRACEINFUNCRC(10, err) // Associate message with transaction if (!checkExistingTransaction()) { SKGObjectBase msg(this, QStringLiteral("doctransactionmsg")); err = msg.setAttribute(QStringLiteral("rd_doctransaction_id"), SKGServices::intToString(getCurrentTransaction())); IFOKDO(err, msg.setAttribute(QStringLiteral("t_message"), iMessage)) IFOKDO(err, msg.setAttribute(QStringLiteral("t_type"), iMessageType == SKGDocument::Positive ? QStringLiteral("P") : iMessageType == SKGDocument::Information ? QStringLiteral("I") : iMessageType == SKGDocument::Warning ? QStringLiteral("W") : iMessageType == SKGDocument::Error ? QStringLiteral("E") : QStringLiteral("H"))) IFOKDO(err, msg.save()) } if (checkExistingTransaction() || !iAction.isEmpty()) { // Addition message in global variable in case of no transaction opened bool found = false; for (const auto& m : qAsConst(m_unTransactionnalMessages)) { if (m.Text == iMessage) { found = true; } } if (iMessageType != SKGDocument::Hidden && !found) { SKGMessage m; m.Text = iMessage; m.Type = iMessageType; m.Action = iAction; m_unTransactionnalMessages.push_back(m); } } return err; } SKGError SKGDocument::removeMessages(int iIdTransaction) { SKGError err; SKGTRACEINFUNCRC(10, err) if (!checkExistingTransaction()) { err = executeSqliteOrder("DELETE FROM doctransactionmsg WHERE rd_doctransaction_id=" % SKGServices::intToString(iIdTransaction)); } m_unTransactionnalMessages.clear(); return err; } SKGError SKGDocument::getMessages(int iIdTransaction, SKGMessageList& oMessages, bool iAll) { SKGError err; SKGTRACEINFUNCRC(10, err) oMessages = m_unTransactionnalMessages; SKGStringListList listTmp; err = executeSelectSqliteOrder( QStringLiteral("SELECT t_message, t_type FROM doctransactionmsg WHERE ") % (iAll ? "" : "t_type<>'H' AND ") % "rd_doctransaction_id=" % SKGServices::intToString(iIdTransaction) % " ORDER BY id ASC", listTmp); int nb = listTmp.count(); for (int i = 1; !err && i < nb ; ++i) { QString msg = listTmp.at(i).at(0); QString type = listTmp.at(i).at(1); bool found = false; for (const auto& m : qAsConst(m_unTransactionnalMessages)) { if (m.Text == msg) { found = true; } } if (!found) { SKGMessage m; m.Text = msg; m.Type = type == QStringLiteral("P") ? SKGDocument::Positive : type == QStringLiteral("I") ? SKGDocument::Information : type == QStringLiteral("W") ? SKGDocument::Warning : type == QStringLiteral("E") ? SKGDocument::Error : SKGDocument::Hidden; oMessages.push_back(m); } } m_unTransactionnalMessages.clear(); return err; } SKGError SKGDocument::getModifications(int iIdTransaction, SKGObjectModificationList& oModifications) const { SKGError err; SKGTRACEINFUNCRC(10, err) oModifications.clear(); SKGStringListList listTmp; err = executeSelectSqliteOrder( "SELECT i_object_id,t_object_table,t_action FROM doctransactionitem WHERE rd_doctransaction_id=" % SKGServices::intToString(iIdTransaction) % " ORDER BY id ASC", listTmp); int nb = listTmp.count(); for (int i = 1; !err && i < nb ; ++i) { SKGObjectModification mod; mod.id = SKGServices::stringToInt(listTmp.at(i).at(0)); mod.table = listTmp.at(i).at(1); QString type = listTmp.at(i).at(2); mod.type = (type == QStringLiteral("D") ? I : (type == QStringLiteral("I") ? D : U)); // Normal because in database we have to sql order to go back. mod.uuid = listTmp.at(i).at(0) % '-' % mod.table; oModifications.push_back(mod); } return err; } QStringList SKGDocument::getImpactedViews(const QString& iTable) const { SKGTRACEINFUNC(10) if (Q_UNLIKELY(d->m_ImpactedViews.isEmpty())) { // Get list of tables and views QStringList tables; SKGStringListList result; executeSelectSqliteOrder(QStringLiteral("SELECT tbl_name FROM sqlite_master WHERE tbl_name NOT LIKE '%_delete' AND type IN ('table', 'view')"), result); int nb = result.count(); tables.reserve(nb); for (int i = 1; i < nb; ++i) { tables.push_back(result.at(i).at(0)); } // First computation executeSelectSqliteOrder(QStringLiteral("SELECT tbl_name, sql FROM sqlite_master WHERE tbl_name NOT LIKE '%_delete' AND type='view'"), result); nb = result.count(); for (int i = 1; i < nb; ++i) { const QStringList& line = result.at(i); const QString& name = line.at(0); const QString& sql = line.at(1); QStringList words = SKGServices::splitCSVLine(sql, ' ', false); words.push_back(QStringLiteral("parameters")); int nbWords = words.count(); for (int j = 0; j < nbWords; ++j) { QString word = words.at(j); word = word.remove(','); if (word.startsWith(QLatin1String("vm_"))) { word.replace(0, 3, QStringLiteral("v_")); } if (word != name && tables.contains(word, Qt::CaseInsensitive)) { QStringList l = d->m_ImpactedViews.value(word); if (!l.contains(name)) { l.push_back(name); } d->m_ImpactedViews[word] = l; } } } // Now, we have some thing like this // d->m_ImpactedViews[A]={ B, C, D} // d->m_ImpactedViews[B]={ E, F} // We must build d->m_ImpactedViews[A]={ B, C, D, E, F} QStringList keys = d->m_ImpactedViews.keys(); for (const auto& k : qAsConst(keys)) { QStringList l = d->m_ImpactedViews.value(k); for (int i = 0; i < l.count(); ++i) { // Warning: the size of l will change in the loop QString item = l.at(i); if (d->m_ImpactedViews.contains(item)) { // No qAsConst here, item is already const for (const auto& name : d->m_ImpactedViews.value(item)) { if (!l.contains(name)) { l.push_back(name); } } } } d->m_ImpactedViews[k] = l; } } return d->m_ImpactedViews.value(iTable); } SKGError SKGDocument::groupTransactions(int iFrom, int iTo) { SKGError err; SKGTRACEINFUNCRC(5, err) ++d->m_inundoRedoTransaction; // It is a kind of undo redo // Check if a transaction is still opened err = checkExistingTransaction(); IFOK(err) err.setReturnCode(ERR_ABORT).setMessage(i18nc("Something went wrong with SQL transactions", "Creation of a group of transactions is forbidden inside a transaction")); else { int iidMaster = qMax(iFrom, iTo); QString smin = SKGServices::intToString(qMin(iFrom, iTo)); QString smax = SKGServices::intToString(iidMaster); // Get transaction SKGStringListList transactions; err = executeSelectSqliteOrder( QStringLiteral("SELECT id, t_name, t_mode, i_parent FROM doctransaction WHERE id BETWEEN ") % smin % " AND " % smax % " ORDER BY id ASC", transactions); // Check and get main parameter for the group int nb = transactions.count(); QString transactionMode; QString communParent; QString name; for (int i = 1; !err && i < nb; ++i) { // We forget header const QStringList& transaction = transactions.at(i); const QString& mode = transaction.at(2); if (!name.isEmpty()) { name += ','; } name += transaction.at(1); if (!transactionMode.isEmpty() && mode != transactionMode) { err = SKGError(ERR_INVALIDARG, QStringLiteral("Undo and Redo transactions cannot be grouped")); } else { transactionMode = mode; } if (i == 1) { communParent = transaction.at(3); } } // Group IFOK(err) { err = SKGDocument::beginTransaction(QStringLiteral("#INTERNAL#")); // Group items IFOKDO(err, executeSqliteOrder( QStringLiteral("UPDATE doctransactionitem set rd_doctransaction_id=") % smax % " where rd_doctransaction_id BETWEEN " % smin % " AND " % smax)) IFOKDO(err, executeSqliteOrder( QStringLiteral("UPDATE doctransaction set i_parent=") % communParent % ", t_name='" % SKGServices::stringToSqlString(name) % "' where id=" % smax)) IFOKDO(err, executeSqliteOrder( QStringLiteral("DELETE FROM doctransaction WHERE id BETWEEN ") % smin % " AND " % SKGServices::intToString(qMax(iFrom, iTo) - 1))) SKGENDTRANSACTION(this, err) } } --d->m_inundoRedoTransaction; return err; } SKGError SKGDocument::undoRedoTransaction(UndoRedoMode iMode) { SKGError err; SKGTRACEINFUNCRC(5, err) // Check if a transaction is still opened err = checkExistingTransaction(); IFOK(err) err.setReturnCode(ERR_ABORT).setMessage(i18nc("Something went wrong with SQL transactions", "Undo / Redo is forbidden inside a transaction")); else { if (iMode == SKGDocument::UNDOLASTSAVE) { // Create group SKGStringListList transactions; err = executeSelectSqliteOrder( QStringLiteral("SELECT id, t_savestep FROM doctransaction WHERE t_mode='U' ORDER BY id DESC"), transactions); int nb = transactions.count(); int min = 0; int max = 0; for (int i = 1; !err && i < nb; ++i) { const QStringList& transaction = transactions.at(i); if (i == 1) { max = SKGServices::stringToInt(transaction.at(0)); } if (i != 1 && transaction.at(1) == QStringLiteral("Y")) { break; } min = SKGServices::stringToInt(transaction.at(0)); } if (min == 0) { min = max; } if (!err && min != max && min != 0) { err = groupTransactions(min, max); } } else { err = SKGError(); // To ignore error generated by checkExistingTransaction. } // Get ID of the transaction to undo IFOK(err) { QString name; bool saveStep = false; QDateTime date; bool refreshViews; int id = getTransactionToProcess(iMode, &name, &saveStep, &date, &refreshViews); if (id == 0) { // No transaction found ==> generate an error err = SKGError(ERR_INVALIDARG, QStringLiteral("No transaction found. Undo / Redo impossible.")); } else { // Undo transaction SKGTRACEL(5) << "Undoing transaction [" << id << "]- [" << name << "]..." << endl; SKGStringListList listSqlOrder; err = executeSelectSqliteOrder( "SELECT t_sqlorder FROM doctransactionitem WHERE rd_doctransaction_id=" % SKGServices::intToString(id) % " ORDER BY id DESC", listSqlOrder); IFOK(err) { int nb = listSqlOrder.count(); err = SKGDocument::beginTransaction(name, nb + 3, date, refreshViews); IFOK(err) { ++d->m_inundoRedoTransaction; // Because we will be in a undo/redo transaction // Normal the first element is ignored because it is the header for (int i = 1; !err && i < nb ; ++i) { err = executeSqliteOrder(listSqlOrder.at(i).at(0)); IFOKDO(err, stepForward(i)) } IFOK(err) { // Set the NEW transaction in redo mode int lastredo = getTransactionToProcess((iMode == SKGDocument::UNDO || iMode == SKGDocument::UNDOLASTSAVE ? SKGDocument::REDO : SKGDocument::UNDO)); int newredo = getTransactionToProcess(iMode); IFOKDO(err, executeSqliteOrder( QStringLiteral("UPDATE doctransaction set t_mode=") % (iMode == SKGDocument::UNDO || iMode == SKGDocument::UNDOLASTSAVE ? QStringLiteral("'R'") : QStringLiteral("'U'")) % ", i_parent=" % SKGServices::intToString(lastredo) % " where id=" % SKGServices::intToString(newredo))) IFOKDO(err, stepForward(nb)) // Move messages from previous transaction to new one IFOKDO(err, executeSqliteOrder( "UPDATE doctransactionmsg set rd_doctransaction_id=" % SKGServices::intToString(getCurrentTransaction()) % " where rd_doctransaction_id=" % SKGServices::intToString(id))) IFOKDO(err, stepForward(nb + 1)) // delete treated transaction IFOKDO(err, executeSqliteOrder( "DELETE from doctransaction where id=" % SKGServices::intToString(id))) IFOKDO(err, stepForward(nb + 2)) // Check that new transaction has exactly the same number of item /* IFOK (err) { SKGStringListList listSqlOrder; err=executeSelectSqliteOrder( "SELECT count(1) FROM doctransactionitem WHERE rd_doctransaction_id=" % SKGServices::intToString(getCurrentTransaction()), listSqlOrder); if (!err && SKGServices::stringToInt(listSqlOrder.at(1).at(0))!=nb-1) { err=SKGError(ERR_ABORT, i18nc("Error message", "Invalid number of item after undo/redo. Expected (%1) != Result (%2)",nb-1,listSqlOrder.at(1).at(0))); } }*/ IFOKDO(err, stepForward(nb + 3)) } SKGENDTRANSACTION(this, err) --d->m_inundoRedoTransaction; // We left the undo / redo transaction } } } } } return err; } int SKGDocument::getDepthTransaction() const { return d->m_nbStepForTransaction.size(); } int SKGDocument::getNbTransaction(UndoRedoMode iMode) const { SKGTRACEINFUNC(10) int output = 0; if (Q_LIKELY(getMainDatabase())) { QString sqlorder = QStringLiteral("select count(1) from doctransaction where t_mode='"); sqlorder += (iMode == SKGDocument::UNDO || iMode == SKGDocument::UNDOLASTSAVE ? QStringLiteral("U") : QStringLiteral("R")); sqlorder += '\''; QSqlQuery query = getMainDatabase()->exec(sqlorder); if (query.next()) { output = query.value(0).toInt(); } } return output; } int SKGDocument::getTransactionToProcess(UndoRedoMode iMode, QString* oName, bool* oSaveStep, QDateTime* oDate, bool* oRefreshViews) const { SKGTRACEINFUNC(10) // initialisation int output = 0; if (oName != nullptr) { *oName = QLatin1String(""); } if (Q_LIKELY(getMainDatabase())) { QString sqlorder = QStringLiteral("select A.id , A.t_name, A.t_savestep, A.d_date, A.t_refreshviews from doctransaction A where " "NOT EXISTS(select 1 from doctransaction B where B.i_parent=A.id) " "and A.t_mode='"); sqlorder += (iMode == SKGDocument::UNDO || iMode == SKGDocument::UNDOLASTSAVE ? QStringLiteral("U") : QStringLiteral("R")); sqlorder += '\''; QSqlQuery query = getMainDatabase()->exec(sqlorder); if (query.next()) { output = query.value(0).toInt(); if (oName != nullptr) { *oName = query.value(1).toString(); } if (oSaveStep != nullptr) { *oSaveStep = (query.value(2).toString() == QStringLiteral("Y")); } if (oDate != nullptr) { *oDate = SKGServices::stringToTime(query.value(3).toString()); } if (oRefreshViews != nullptr) { *oRefreshViews = (query.value(4).toString() == QStringLiteral("Y")); } } } return output; } int SKGDocument::getCurrentTransaction() const { SKGTRACEINFUNC(10) return d->m_currentTransaction; } QString SKGDocument::getPassword() const { if (!d->m_password_got) { d->m_password = getParameter(QStringLiteral("SKG_PASSWORD")); d->m_password_got = true; } return d->m_password; } SKGError SKGDocument::changePassword(const QString& iNewPassword) { SKGError err; SKGTRACEINFUNCRC(10, err) IFOK(checkExistingTransaction()) err.setReturnCode(ERR_ABORT).setMessage(i18nc("Something went wrong with SQL transactions", "Change password is forbidden inside a transaction")); else { IFOKDO(err, executeSqliteOrder("PRAGMA REKEY = '" % SKGServices::stringToSqlString(iNewPassword.isEmpty() ? QStringLiteral("DEFAULTPASSWORD") : iNewPassword) % "'")) IFOKDO(err, beginTransaction(QStringLiteral("#INTERNAL#"), 0, QDateTime::currentDateTime(), false)) IFOKDO(err, setParameter(QStringLiteral("SKG_PASSWORD"), iNewPassword)) IFOKDO(err, setParameter(QStringLiteral("SKG_PASSWORD_LASTUPDATE"), SKGServices::dateToSqlString(QDate::currentDate()))) IFOKDO(err, sendMessage(iNewPassword.isEmpty() ? i18nc("Inform the user that the password protection was removed", "The document password has been removed.") : i18nc("Inform the user that the password was successfully changed", "The document password has been modified."), SKGDocument::Positive)) SKGENDTRANSACTION(this, err) // Force the save IFOK(err) { d->m_lastSavedTransaction = -1; d->m_password = iNewPassword; d->m_password_got = true; // Close all thread connection auto conNameMainConnection = getMainDatabase()->connectionName(); const auto conNames = QSqlDatabase::connectionNames(); for (const auto& conName : conNames) { if (conName.startsWith(conNameMainConnection % "_")) { /* NO NEED { auto con = QSqlDatabase::database(conName, false); con.close(); }*/ QSqlDatabase::removeDatabase(conName); } } } } return err; } SKGError SKGDocument::setLanguage(const QString& iLanguage) { SKGError err; SKGTRACEINFUNCRC(5, err) QString previousLanguage = getParameter(QStringLiteral("SKG_LANGUAGE")); if (previousLanguage != iLanguage) { // Save language into the document IFOKDO(err, beginTransaction(QStringLiteral("#INTERNAL#"), 0, QDateTime::currentDateTime(), false)) IFOKDO(err, setParameter(QStringLiteral("SKG_LANGUAGE"), iLanguage)) // Migrate view for new language IFOKDO(err, refreshViewsIndexesAndTriggers()) // close temporary transaction SKGENDTRANSACTION(this, err) } return err; } SKGError SKGDocument::initialize() { SKGError err; SKGTRACEINFUNCRC(5, err) err = load(QLatin1String(""), QLatin1String("")); return err; } SKGError SKGDocument::recover(const QString& iName, const QString& iPassword, QString& oRecoveredFile) { SKGError err; SKGTRACEINFUNCRC(5, err) SKGTRACEL(10) << "Input parameter [name]=[" << iName << ']' << endl; QString sqliteFile = QString(iName % "_recovered.sqlite").replace(QStringLiteral(".skg_"), QStringLiteral("_")); oRecoveredFile = QString(iName % "_recovered.skg").replace(QStringLiteral(".skg_"), QStringLiteral("_")); bool mode; err = SKGServices::cryptFile(iName, sqliteFile, iPassword, false, getDocumentHeader(), mode); IFOK(err) { QFile(oRecoveredFile).remove(); QString cmd = "echo .dump | sqlite3 \"" % sqliteFile % "\" | sed -e 's/ROLLBACK; -- due to errors/COMMIT;/g' | sqlite3 \"" % oRecoveredFile % '"'; QProcess p; p.start(QStringLiteral("sh"), QStringList() << QStringLiteral("-c") << cmd); if (!p.waitForFinished(1000 * 60 * 2) || p.exitCode() != 0) { err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message", "The following command line failed with code %2:\n'%1'", cmd, p.exitCode())); } // Try to load the recovered file IFOKDO(err, load(oRecoveredFile, QLatin1String(""))) IFOK(err) { SKGBEGINTRANSACTION(*this, i18nc("Noun", "Recovery"), err) IFOKDO(err, refreshViewsIndexesAndTriggers(true)) } IFOKDO(err, save()) // Reset the current document initialize(); // Clean useless file IFOK(err) { // We keep only the recovered QFile(sqliteFile).remove(); } else { // We keep the sqlite file in case of QFile(oRecoveredFile).remove(); err.addError(ERR_FAIL, i18nc("Error message", "Impossible to recover this file")); } } return err; } SKGError SKGDocument::load(const QString& iName, const QString& iPassword, bool iRestoreTmpFile, bool iForceReadOnly) { // Close previous document SKGError err; SKGTRACEINFUNCRC(5, err) SKGTRACEL(10) << "Input parameter [name]=[" << iName << ']' << endl; SKGTRACEL(10) << "Input parameter [iRestoreTmpFile]=[" << (iRestoreTmpFile ? "TRUE" : "FALSE") << ']' << endl; SKGTRACEL(10) << "Input parameter [iForceReadOnly]=[" << (iForceReadOnly ? "TRUE" : "FALSE") << ']' << endl; d->m_lastSavedTransaction = -1; // To avoid double event emission d->m_modeSQLCipher = true; d->m_blockEmits = true; err = close(); d->m_blockEmits = false; IFOK(err) { if (!iName.isEmpty()) { // File exist QFileInfo fi(iName); d->m_modeReadOnly = iForceReadOnly || !fi.permission(QFile::WriteUser); // Temporary file d->m_temporaryFile = SKGDocument::getTemporaryFile(iName, d->m_modeReadOnly); bool temporaryFileExisting = QFile(d->m_temporaryFile).exists(); SKGTRACEL(10) << "Temporary file: [" << d->m_temporaryFile << ']' << endl; SKGTRACEL(10) << "Temporary file existing: [" << (temporaryFileExisting ? "TRUE" : "FALSE") << ']' << endl; if (!iRestoreTmpFile || !temporaryFileExisting) { SKGTRACEL(10) << "Create the temporary file" << endl; QFile::remove(d->m_temporaryFile); // Must remove it to be able to copy err = SKGServices::cryptFile(iName, d->m_temporaryFile, iPassword, false, getDocumentHeader(), d->m_modeSQLCipher); } else { SKGTRACEL(10) << "The temporary file is existing, try a restore but we must check if the file is password protected first" << endl; // 249955: Check if password protected vvv // Temporary file will be loaded but first we must check if original document is password protected QString temporaryFile2 = d->m_temporaryFile % '2'; err = SKGServices::cryptFile(iName, temporaryFile2, iPassword, false, getDocumentHeader(), d->m_modeSQLCipher); // Try an open to check if well descrypted IFOK(err) { QSqlDatabase tryOpen(QSqlDatabase::addDatabase(SQLDRIVERNAME, QStringLiteral("tryOpen"))); tryOpen.setDatabaseName(temporaryFile2); if (!tryOpen.open()) { // Set error message QSqlError sqlErr = tryOpen.lastError(); err = SKGError(SQLLITEERROR + sqlErr.nativeErrorCode().toInt(), sqlErr.text()); } if (d->m_modeSQLCipher) { IFOKDO(err, SKGServices::executeSqliteOrder(tryOpen, "PRAGMA KEY = '" % SKGServices::stringToSqlString(iPassword.isEmpty() ? QStringLiteral("DEFAULTPASSWORD") : iPassword) % "'")) IFKO(err) { SKGTRACEL(10) << "Wrong installation of sqlcipher (doesn't support encryption)" << endl; err = SKGError(ERR_ENCRYPTION, i18nc("Error message", "Wrong installation")); } // Migrate to the last version of SQLCipher IFOKDO(err, SKGServices::executeSqliteOrder(tryOpen, QStringLiteral("PRAGMA cipher_migrate"))) // Test the password IFOKDO(err, SKGServices::executeSqliteOrder(tryOpen, QStringLiteral("SELECT count(*) FROM sqlite_master"))) IFKO(err) { SKGTRACEL(10) << "Wrong password in restore mode" << endl; err = SKGError(ERR_ENCRYPTION, i18nc("Error message", "Wrong password")); } } IFOKDO(err, SKGServices::executeSqliteOrder(tryOpen, QStringLiteral("PRAGMA synchronous = OFF"))) } QSqlDatabase::removeDatabase(QStringLiteral("tryOpen")); QFile::remove(temporaryFile2); // To avoid deletion of temporary file during next try IFKO(err) d->m_temporaryFile = QLatin1String(""); // 249955: Check if password protected ^^^ } // Create file database IFOK(err) { d->m_currentDatabase = QSqlDatabase::addDatabase(SQLDRIVERNAME, d->m_databaseIdentifier); d->m_currentDatabase.setDatabaseName(d->m_temporaryFile); if (!d->m_currentDatabase.open()) { // Set error message QSqlError sqlErr = d->m_currentDatabase.lastError(); err = SKGError(SQLLITEERROR + sqlErr.nativeErrorCode().toInt(), sqlErr.text()); } d->m_directAccessDb = true; if (QUrl::fromUserInput(iName).isLocalFile()) { d->m_currentFileName = iName; } } } else { // Temporary file d->m_temporaryFile = QDir::tempPath() % "/skg_" % QUuid::createUuid().toString() % ".skg"; // Create memory database d->m_currentDatabase = QSqlDatabase::addDatabase(SQLDRIVERNAME, d->m_databaseIdentifier); d->m_currentDatabase.setConnectOptions(QStringLiteral("QSQLITE_OPEN_URI")); d->m_currentDatabase.setDatabaseName(QStringLiteral("file:") + d->m_databaseIdentifier + QStringLiteral("?mode=memory&cache=shared")); if (!d->m_currentDatabase.open()) { // Set error message QSqlError sqlErr = d->m_currentDatabase.lastError(); err = SKGError(SQLLITEERROR + sqlErr.nativeErrorCode().toInt(), sqlErr.text()); } d->m_directAccessDb = false; } if (d->m_modeSQLCipher) { // This is an encrypted data base IFOKDO(err, executeSqliteOrder("PRAGMA KEY = '" % SKGServices::stringToSqlString(iPassword.isEmpty() ? QStringLiteral("DEFAULTPASSWORD") : iPassword) % "'")) IFKO(err) { SKGTRACEL(10) << "Wrong installation of sqlcipher (doesn't support encryption)" << endl; err = SKGError(ERR_ENCRYPTION, i18nc("Error message", "Wrong installation")); } // Migrate to the last version of SQLCipher IFOKDO(err, executeSqliteOrder(QStringLiteral("PRAGMA cipher_migrate"))) // Test the password IFOKDO(err, executeSqliteOrder(QStringLiteral("SELECT count(*) FROM sqlite_master"))) IFKO(err) { SKGTRACEL(10) << "Wrong password on temporary file" << endl; err = SKGError(ERR_ENCRYPTION, i18nc("Error message", "Wrong password")); } } // Check if the database is correct IFOK(err) { IFOKDO(err, executeSqliteOrder(QStringLiteral("PRAGMA journal_mode=MEMORY"))) IFKO(err) { err.addError(ERR_CORRUPTION, i18nc("Error message", "Oups, this file seems to be corrupted")); } } // Optimization QStringList optimization; optimization << QStringLiteral("PRAGMA case_sensitive_like=true") << QStringLiteral("PRAGMA journal_mode=MEMORY") << QStringLiteral("PRAGMA temp_store=MEMORY") // << QStringLiteral("PRAGMA locking_mode=EXCLUSIVE") << QStringLiteral("PRAGMA synchronous = OFF") << QStringLiteral("PRAGMA legacy_alter_table=ON") // For migration on sqlite >=3.25 (see https://sqlite.org/lang_altertable.html) << QStringLiteral("PRAGMA recursive_triggers=true"); IFOKDO(err, executeSqliteOrders(optimization)) // Add custom sqlite functions IFOKDO(err, addSqliteAddon(getMainDatabase())) if (!d->m_directAccessDb) { // Create parameter and undo redo table /** * This constant is used to initialized the data model (table creation) */ QStringList InitialDataModel; // ================================================================== // Table parameters InitialDataModel << QStringLiteral("CREATE TABLE parameters " "(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," "t_uuid_parent TEXT NOT NULL DEFAULT ''," "t_name TEXT NOT NULL," "t_value TEXT NOT NULL DEFAULT ''," "b_blob BLOB," "d_lastmodifdate DATE NOT NULL DEFAULT CURRENT_TIMESTAMP," "i_tmp INTEGER NOT NULL DEFAULT 0" ")") // ================================================================== // Table node << "CREATE TABLE node (" "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," "t_name TEXT NOT NULL DEFAULT '' CHECK (t_name NOT LIKE '%" % OBJECTSEPARATOR % "%')," "t_fullname TEXT," "t_icon TEXT DEFAULT ''," "f_sortorder FLOAT," "t_autostart VARCHAR(1) DEFAULT 'N' CHECK (t_autostart IN ('Y', 'N'))," "t_data TEXT," "rd_node_id INT CONSTRAINT fk_id REFERENCES node(id) ON DELETE CASCADE)" // ================================================================== // Table doctransaction << QStringLiteral("CREATE TABLE doctransaction (" "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," "t_name TEXT NOT NULL," "t_mode VARCHAR(1) DEFAULT 'U' CHECK (t_mode IN ('U', 'R'))," "d_date DATE NOT NULL," "t_savestep VARCHAR(1) DEFAULT 'N' CHECK (t_savestep IN ('Y', 'N'))," "t_refreshviews VARCHAR(1) DEFAULT 'Y' CHECK (t_refreshviews IN ('Y', 'N'))," "i_parent INTEGER)") // ================================================================== // Table doctransactionitem << QStringLiteral("CREATE TABLE doctransactionitem (" "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " "rd_doctransaction_id INTEGER NOT NULL," "i_object_id INTEGER NOT NULL," "t_object_table TEXT NOT NULL," "t_action VARCHAR(1) DEFAULT 'I' CHECK (t_action IN ('I', 'U', 'D'))," "t_sqlorder TEXT NOT NULL DEFAULT '')") << QStringLiteral("CREATE TABLE doctransactionmsg (" "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " "rd_doctransaction_id INTEGER NOT NULL," "t_message TEXT NOT NULL DEFAULT ''," "t_type VARCHAR(1) DEFAULT 'I' CHECK (t_type IN ('P', 'I', 'W', 'E', 'H')))"); // Positive, Information, Warning, Error, Hidden IFOKDO(err, executeSqliteOrders(InitialDataModel)) IFOKDO(err, SKGDocument::refreshViewsIndexesAndTriggers()) } } // migrate IFOK(err) { bool mig = false; err = migrate(mig); if (!err && getParameter(QStringLiteral("SKG_DATABASE_TYPE")) != SQLDRIVERNAME && !getPassword().isEmpty()) { err = sendMessage(i18nc("Information message", "This document is protected by a password but the database is still in SQLite mode.\nDo you know that the SQLCipher mode is more secured because even the temporary file is encrypted?"), SKGDocument::Warning, QStringLiteral("skg://migrate_sqlcipher")); } if (!err && getParameter(QStringLiteral("SKG_PASSWORD_LASTUPDATE")) == QLatin1String("") && !getPassword().isEmpty()) { err = sendMessage(i18nc("Information message", "A security hole has been detected and corrected on this version of the application. We strongly encourage you to change your password."), SKGDocument::Warning, QStringLiteral("skg://file_change_password")); } // To authorize manual repair of document in case of error during migration // the error is not caught if traces are activated if (err && (SKGTraces::SKGLevelTrace != 0)) { err = sendMessage(i18nc("Popup message", "The migration failed but the document has been loaded without error because debug mode is activated"), SKGDocument::Warning); } if (!err && mig && !iName.isEmpty()) { err = sendMessage(i18nc("The document has been upgraded to the latest Skrooge version format", "The document has been migrated"), SKGDocument::Positive); } } // Optimization IFOK(err) { d->m_lastSavedTransaction = getTransactionToProcess(SKGDocument::UNDO); executeSqliteOrder(QStringLiteral("ANALYZE")); } // Creation undo/redo triggers IFOKDO(err, createUndoRedoTemporaryTriggers()) IFOK(err) { QString sqliteQtVersion = getParameter(QStringLiteral("SKG_SQLITE_LAST_VERSION")); QString sqliteSystemVersion(sqlite3_libversion()); QProcess sqlite3Process; QString mode; sqlite3Process.start(QStringLiteral("sqlcipher"), QStringList() << QStringLiteral("-version")); mode = QStringLiteral("SQLCipher"); if (sqlite3Process.waitForFinished()) { sqliteSystemVersion = SKGServices::splitCSVLine(sqlite3Process.readAll(), ' ').value(0); } SKGTRACEL(5) << "SQLite version of Qt :" << sqliteQtVersion << endl; SKGTRACEL(5) << "SQLite version of the system:" << sqliteSystemVersion << endl; if (!sqliteQtVersion.isEmpty() && !sqliteSystemVersion.isEmpty() && sqliteQtVersion != sqliteSystemVersion) { QString message = i18nc("Error message", "This application can not run correctly because the %3 version of the system (%1) is not aligned with the %4 version embedded in Qt (%2). You should rebuild Qt with the option -system-sqlite.", sqliteSystemVersion, sqliteQtVersion, mode, mode); err = sendMessage(message, Warning); SKGTRACE << "WARNING:" << message << endl; } } if (err && !iName.isEmpty()) { close(); } else { // Send event d->m_uniqueIdentifier = QUuid::createUuid().toString(); d->m_password = iPassword; d->m_password_got = true; Q_EMIT tableModified(QLatin1String(""), 0, false); Q_EMIT modified(); } return err; } bool SKGDocument::isReadOnly() const { return d->m_modeReadOnly; } bool SKGDocument::isFileModified() const { // Get last executed transaction int last = getTransactionToProcess(SKGDocument::UNDO); // if (nbStepForTransaction.size()) --last; return (d->m_lastSavedTransaction != last); } void SKGDocument::setFileNotModified() const { d->m_lastSavedTransaction = getTransactionToProcess(SKGDocument::UNDO); } QString SKGDocument::getCurrentFileName() const { return d->m_currentFileName; } SKGError SKGDocument::save() { SKGError err; SKGTRACEINFUNCRC(5, err) if (d->m_currentFileName.isEmpty()) { err = SKGError(ERR_INVALIDARG, i18nc("Error message: Can not save a file if it has no name yet", "Save not authorized because the file name is not yet defined")); } else { // save err = saveAs(d->m_currentFileName, true); } return err; } SKGError SKGDocument::saveAs(const QString& iName, bool iOverwrite) { SKGError err; SKGTRACEINFUNCRC(5, err) SKGTRACEL(10) << "Input parameter [name]=[" << iName << ']' << endl; bool simulateFileSystemFull = !SKGServices::getEnvVariable(QStringLiteral("SKGFILESYSTEMFULL")).isEmpty(); // Check if a transaction is still opened err = checkExistingTransaction(); IFOK(err) err.setReturnCode(ERR_ABORT).setMessage(i18nc("Cannot save the file while the application is still performing an SQL transaction", "Save is forbidden if a transaction is still opened")); else { err = SKGError(); if (getParameter(QStringLiteral("SKG_UNDO_CLEAN_AFTER_SAVE")) == QStringLiteral("Y")) { err = executeSqliteOrder(QStringLiteral("delete from doctransaction")); } // No transaction opened ==> it is ok // We mark the last transaction as a save point IFOKDO(err, executeSqliteOrder(QStringLiteral("update doctransaction set t_savestep='Y' where id in (select A.id from doctransaction A where " "NOT EXISTS(select 1 from doctransaction B where B.i_parent=A.id) " "and A.t_mode='U')"))) Q_EMIT tableModified(QStringLiteral("doctransaction"), 0, false); // Optimization IFOK(err) { err = executeSqliteOrder(QStringLiteral("VACUUM;")); IFOK(err) { // Check if file already exist if (!iOverwrite && QFile(iName).exists()) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("There is already a file with the same name", "File '%1' already exist", iName)); } else { // Get backup file name bool backupFileMustBeRemoved = false; QString backupFileName = getBackupFile(iName); if (backupFileName.isEmpty()) { backupFileName = iName % ".tmp"; backupFileMustBeRemoved = true; } // Create backup file QFile::remove(backupFileName % '~'); QFile::rename(backupFileName, backupFileName % '~'); if (QFile(iName).exists() && (simulateFileSystemFull || !QFile(iName).copy(backupFileName))) { this->sendMessage(i18nc("Error message: Could not create a backup file", "Creation of backup file %1 failed", backupFileName), Warning); } // Save database IFOK(err) { QFile::remove(iName % '~'); QFile::rename(iName, iName % '~'); // To be sure that db is flushed IFOKDO(err, executeSqliteOrder(QStringLiteral("PRAGMA synchronous = FULL"))) QString pwd = getPassword(); // Copy memory to tmp db if (!d->m_directAccessDb && !err) { QFile::remove(d->m_temporaryFile); auto fileDb = QSqlDatabase::addDatabase(SQLDRIVERNAME, d->m_databaseIdentifier % "_tmp"); fileDb.setDatabaseName(d->m_temporaryFile); if (!fileDb.open()) { // Set error message QSqlError sqlErr = fileDb.lastError(); err = SKGError(SQLLITEERROR + sqlErr.nativeErrorCode().toInt(), sqlErr.text()); } else { IFOKDO(err, SKGServices::executeSqliteOrder(fileDb, "PRAGMA KEY = '" % SKGServices::stringToSqlString(pwd.isEmpty() ? QStringLiteral("DEFAULTPASSWORD") : pwd) % "'")) addSqliteAddon(&fileDb); IFOKDO(err, SKGServices::copySqliteDatabase(fileDb, d->m_currentDatabase, false, pwd.isEmpty() ? QStringLiteral("DEFAULTPASSWORD") : pwd)) } fileDb.close(); QSqlDatabase::removeDatabase(d->m_databaseIdentifier % "_tmp"); } // To simulate a file system full if (!err && simulateFileSystemFull) { err = SKGError(ERR_WRITEACCESS, i18nc("Error message: writing a file failed", "Write file '%1' failed", iName)); } // Crypt the file if (!err) { bool mode; err = SKGServices::cryptFile(d->m_temporaryFile, iName, pwd, true, getDocumentHeader(), mode); } if (!d->m_directAccessDb && !err) { QFile(d->m_temporaryFile).remove(); } // For performances IFOKDO(err, executeSqliteOrder(QStringLiteral("PRAGMA synchronous = OFF"))) } if (backupFileMustBeRemoved) { QFile::remove(backupFileName); } IFOK(err) { // The document is not modified QString oldtemporaryFile = d->m_temporaryFile; d->m_currentFileName = iName; d->m_modeReadOnly = false; d->m_temporaryFile = getTemporaryFile(d->m_currentFileName); if (oldtemporaryFile != d->m_temporaryFile) { QFile(oldtemporaryFile).rename(d->m_temporaryFile); } d->m_lastSavedTransaction = getTransactionToProcess(SKGDocument::UNDO); // Commit save QFile::remove(backupFileName % '~'); QFile::remove(iName % '~'); } else { // Rollback file QFile::remove(backupFileName); QFile::rename(backupFileName % '~', backupFileName); QFile::remove(iName); QFile::rename(iName % '~', iName); } } } } Q_EMIT transactionSuccessfullyEnded(0); } return err; } SKGError SKGDocument::close() { SKGTRACEINFUNC(5) if (getMainDatabase() != nullptr) { QString conNameMainConnection = getMainDatabase()->connectionName(); const auto& conNames = QSqlDatabase::connectionNames(); for (const auto& conName : conNames) { if (conName.startsWith(conNameMainConnection % "_")) { /* NO NEED { auto con = QSqlDatabase::database(conName, false); con.close(); }*/ QSqlDatabase::removeDatabase(conName); } } getMainDatabase()->close(); d->m_currentDatabase = QSqlDatabase(); // To avoid warning on remove QSqlDatabase::removeDatabase(d->m_databaseIdentifier); } if (!d->m_temporaryFile.isEmpty()) { QFile(d->m_temporaryFile).remove(); d->m_temporaryFile = QLatin1String(""); } // Emit events ? bool emitEvent = (d->m_lastSavedTransaction != -1); // Init fields d->m_currentFileName = QLatin1String(""); d->m_lastSavedTransaction = 0; d->m_nbStepForTransaction.clear(); d->m_posStepForTransaction.clear(); d->m_nameForTransaction.clear(); d->m_password.clear(); d->m_password_got = false; // Send event if (!d->m_blockEmits && emitEvent && qApp && !qApp->closingDown()) { Q_EMIT tableModified(QLatin1String(""), 0, false); Q_EMIT transactionSuccessfullyEnded(0); Q_EMIT modified(); } return SKGError(); } SKGError SKGDocument::dropViewsAndIndexes(const QStringList& iTables) const { SKGError err; // Drop all views SKGStringListList list; err = executeSelectSqliteOrder(QStringLiteral("SELECT tbl_name, name, type FROM sqlite_master WHERE type IN ('view','index')"), list); int nb = list.count(); for (int i = 1; !err && i < nb; ++i) { QString name = list.at(i).at(1); QString table = SKGServices::getRealTable(list.at(i).at(0)); QString type = list.at(i).at(2); if (iTables.contains(table)) { QString sql = "DROP " % type % " IF EXISTS " % name; err = this->executeSqliteOrder(sql); } } return err; } #include "skgdocument2.cpp" SKGError SKGDocument::migrate(bool& oMigrationDone) { SKGError err; SKGTRACEINFUNCRC(5, err) oMigrationDone = false; { SKGBEGINPROGRESSTRANSACTION(*this, "#INTERNAL#" % i18nc("Progression step", "Migrate document"), err, 3) if (getParameter(QStringLiteral("SKG_UNDO_MAX_DEPTH")).isEmpty()) { IFOKDO(err, setParameter(QStringLiteral("SKG_UNDO_MAX_DEPTH"), SKGServices::intToString(SKG_UNDO_MAX_DEPTH))) } if (getParameter(QStringLiteral("SKG_UNDO_CLEAN_AFTER_SAVE")).isEmpty()) { IFOKDO(err, setParameter(QStringLiteral("SKG_UNDO_CLEAN_AFTER_SAVE"), QStringLiteral("N"))) } if (!err && getParameter(QStringLiteral("SKG_DATABASE_TYPE")) != (d->m_modeSQLCipher ? SQLDRIVERNAME : QStringLiteral("QSQLITE"))) { IFOKDO(err, setParameter(QStringLiteral("SKG_DATABASE_TYPE"), d->m_modeSQLCipher ? SQLDRIVERNAME : QStringLiteral("QSQLITE"))) } QString version = getParameter(QStringLiteral("SKG_DB_VERSION")); QString initialversion = version; QString lastversion = QStringLiteral("1.6"); if (!err && version.isEmpty()) { // First creation SKGTRACEL(10) << "Migration from 0 to " << lastversion << endl; // Set new version version = lastversion; IFOKDO(err, setParameter(QStringLiteral("SKG_DB_VERSION"), version)) // Set sqlite creation version SKGStringListList listTmp; IFOKDO(err, executeSelectSqliteOrder(QStringLiteral("select sqlite_version()"), listTmp)) if (!err && listTmp.count() == 2) { err = setParameter(QStringLiteral("SKG_SQLITE_CREATION_VERSION"), listTmp.at(1).at(0)); } oMigrationDone = true; } if (!err && SKGServices::stringToDouble(version) > SKGServices::stringToDouble(lastversion)) { err = SKGError(ERR_ABORT, i18nc("Error message", "Impossible to load a document generated by a more recent version")); } { // Migration steps if (!err && version == QStringLiteral("0.1")) { // Migration from version 0.1 to 0.2 SKGTRACEL(10) << "Migration from 0.1 to 0.2" << endl; // ================================================================== // Table doctransactionmsg QStringList sqlOrders; sqlOrders << QStringLiteral("DROP TABLE IF EXISTS doctransactionmsg") << QStringLiteral("CREATE TABLE doctransactionmsg (" "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " "rd_doctransaction_id INTEGER NOT NULL," "t_message TEXT NOT NULL DEFAULT '')"); err = executeSqliteOrders(sqlOrders); // Set new version version = QStringLiteral("0.2"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("0.2")) { // Migration from version 0.2 to 0.3 SKGTRACEL(10) << "Migration from 0.2 to 0.3" << endl; err = executeSqliteOrder(QStringLiteral("UPDATE node set f_sortorder=id")); // Set new version version = QStringLiteral("0.3"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("0.3")) { // Migration from version 0.3 to 0.4 SKGTRACEL(10) << "Migration from 0.3 to 0.4" << endl; err = executeSqliteOrder(QStringLiteral("ALTER TABLE node ADD COLUMN t_autostart VARCHAR(1) DEFAULT 'N' CHECK (t_autostart IN ('Y', 'N'))")); IFOKDO(err, executeSqliteOrder("UPDATE node set t_autostart='Y' where t_name='" % i18nc("Verb, automatically load when the application is started", "autostart") % '\'')) // Set new version version = QStringLiteral("0.4"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("0.4")) { // Migration from version 0.4 to 0.5 SKGTRACEL(10) << "Migration from 0.4 to 0.5" << endl; err = executeSqliteOrder(QStringLiteral("ALTER TABLE doctransactionmsg ADD COLUMN t_popup VARCHAR(1) DEFAULT 'Y' CHECK (t_popup IN ('Y', 'N'))")); // Set new version version = QStringLiteral("0.5"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("0.5")) { // Migration from version 0.5 to 0.6 SKGTRACEL(10) << "Migration from 0.5 to 0.6" << endl; err = executeSqliteOrder(QStringLiteral("UPDATE node set t_autostart='N' where t_autostart NOT IN ('Y', 'N')")); // Set new version version = QStringLiteral("0.6"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("0.6")) { // Migration from version 0.6 to 0.7 SKGTRACEL(10) << "Migration from 0.6 to 0.7" << endl; err = executeSqliteOrder(QStringLiteral("ALTER TABLE parameters ADD COLUMN b_blob BLOB")); // Set new version version = QStringLiteral("0.7"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("0.7")) { // Migration from version 0.7 to 0.8 SKGTRACEL(10) << "Migration from 0.7 to 0.8" << endl; err = executeSqliteOrder(QStringLiteral("UPDATE parameters set t_name='SKG_LANGUAGE' where t_name='SKGLANGUAGE'")); // Set new version version = QStringLiteral("0.8"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("0.8")) { SKGTRACEL(10) << "Migration from 0.8 to 0.9" << endl; QStringList sql; sql << QStringLiteral("ALTER TABLE parameters ADD COLUMN i_tmp INTEGER NOT NULL DEFAULT 0") << QStringLiteral("UPDATE parameters set i_tmp=0"); err = executeSqliteOrders(sql); // Set new version version = QStringLiteral("0.9"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("0.9")) { SKGTRACEL(10) << "Migration from 0.9 to 1.0" << endl; err = SKGDocument::setParameter(QStringLiteral("SKG_UNIQUE_ID"), QLatin1String("")); // Set new version version = QStringLiteral("1.0"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("1.0")) { // Migration from version 1.0 to 1.1 SKGTRACEL(10) << "Migration from 1.0 to 1.1" << endl; err = executeSqliteOrder(QStringLiteral("ALTER TABLE node ADD COLUMN t_icon TEXT DEFAULT ''")); IFOK(err) { SKGStringListList result; err = executeSelectSqliteOrder(QStringLiteral("SELECT id,t_data from node"), result); int nb = result.count(); for (int i = 1; !err && i < nb; ++i) { const QStringList& line = result.at(i); QString icon = QStringLiteral("folder-bookmark"); QStringList data = SKGServices::splitCSVLine(line.at(1)); if (data.count() > 2) { icon = data.at(2); } data.removeAt(2); err = executeSqliteOrder("UPDATE node set t_icon='" % SKGServices::stringToSqlString(icon) % "', t_data='" % SKGServices::stringToSqlString(SKGServices::stringsToCsv(data)) % "' where id=" % line.at(0)); } } // Set new version version = QStringLiteral("1.1"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("1.1")) { // Migration from version 1.1 to 1.2 SKGTRACEL(10) << "Migration from 1.1 to 1.2" << endl; QStringList sql; sql << QStringLiteral("ALTER TABLE doctransaction ADD COLUMN t_refreshviews VARCHAR(1) DEFAULT 'Y' CHECK (t_refreshviews IN ('Y', 'N'))") << QStringLiteral("UPDATE doctransaction set t_refreshviews='Y'"); err = executeSqliteOrders(sql); // Set new version version = QStringLiteral("1.2"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("1.2")) { // Migration from version 1.2 to 1.3 SKGTRACEL(10) << "Migration from 1.2 to 1.3" << endl; err = SKGDocument::refreshViewsIndexesAndTriggers(); QStringList sql; sql << QStringLiteral("DELETE FROM node WHERE (r_node_id IS NULL OR r_node_id='') AND EXISTS (SELECT 1 FROM node n WHERE n.t_name=node.t_name AND r_node_id=0)") << QStringLiteral("UPDATE node SET t_name=t_name"); IFOKDO(err, executeSqliteOrders(sql)) // Set new version version = QStringLiteral("1.3"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("1.3")) { // Migration from version 1.3 to 1.4 SKGTRACEL(10) << "Migration from 1.3 to 1.4" << endl; QStringList sql; sql << "CREATE TABLE node2 (" "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," "t_name TEXT NOT NULL DEFAULT '' CHECK (t_name NOT LIKE '%" % OBJECTSEPARATOR % "%')," "t_fullname TEXT," "t_icon TEXT DEFAULT ''," "f_sortorder FLOAT," "t_autostart VARCHAR(1) DEFAULT 'N' CHECK (t_autostart IN ('Y', 'N'))," "t_data TEXT," "rd_node_id INT CONSTRAINT fk_id REFERENCES node(id) ON DELETE CASCADE)" << QStringLiteral("INSERT INTO node2 (id, t_name, t_fullname, t_icon, f_sortorder, t_autostart, t_data, rd_node_id) " "SELECT id, t_name, t_fullname, t_icon, f_sortorder, t_autostart, t_data, r_node_id FROM node") << QStringLiteral("DROP TABLE IF EXISTS node") << QStringLiteral("ALTER TABLE node2 RENAME TO node"); err = executeSqliteOrders(sql); // Set new version version = QStringLiteral("1.4"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("1.4")) { // Migration from version 1.4 to 1.5 SKGTRACEL(10) << "Migration from 1.4 to 1.5" << endl; err = SKGDocument::refreshViewsIndexesAndTriggers(); QStringList sql; sql << QStringLiteral("UPDATE parameters SET t_uuid_parent='advice' WHERE t_uuid_parent='advices'"); IFOKDO(err, executeSqliteOrders(sql)) // Set new version version = QStringLiteral("1.5"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } if (!err && version == QStringLiteral("1.5")) { // Migration from version 1.5 to 1.6 SKGTRACEL(10) << "Migration from 1.5 to 1.6" << endl; err = SKGDocument::refreshViewsIndexesAndTriggers(); QStringList sql; sql << QStringLiteral("DROP TABLE IF EXISTS doctransactionmsg2") << QStringLiteral("CREATE TABLE doctransactionmsg2 (" "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " "rd_doctransaction_id INTEGER NOT NULL," "t_message TEXT NOT NULL DEFAULT ''," "t_type VARCHAR(1) DEFAULT 'I' CHECK (t_type IN ('P', 'I', 'W', 'E', 'H')))") // Positive, Information, Warning, Error, Hidden << QStringLiteral("INSERT INTO doctransactionmsg2 (id, rd_doctransaction_id, t_message, t_type) SELECT id, rd_doctransaction_id, t_message, (CASE WHEN t_popup='Y' THEN 'I' ELSE 'H' END) FROM doctransactionmsg") << QStringLiteral("DROP TABLE IF EXISTS doctransactionmsg") << QStringLiteral("ALTER TABLE doctransactionmsg2 RENAME TO doctransactionmsg"); IFOKDO(err, executeSqliteOrders(sql)) // Set new version version = QStringLiteral("1.6"); IFOKDO(err, SKGDocument::setParameter(QStringLiteral("SKG_DB_VERSION"), version)) oMigrationDone = true; } } IFOKDO(err, stepForward(1, i18nc("Progression step", "Refresh views"))) // Set sqlite last version SKGStringListList listTmp; IFOKDO(err, executeSelectSqliteOrder(QStringLiteral("select sqlite_version()"), listTmp)) if (!err && listTmp.count() == 2) { err = setParameter(QStringLiteral("SKG_SQLITE_LAST_VERSION"), listTmp.at(1).at(0)); } // Refresh views IFOKDO(err, refreshViewsIndexesAndTriggers()) IFOKDO(err, stepForward(2, i18nc("Progression step", "Update materialized views"))) // Refresh materialized views if (!err && oMigrationDone) { err = computeMaterializedViews(); } IFOKDO(err, stepForward(3)) IFKO(err) err.addError(ERR_FAIL, i18nc("Error message: Could not perform database migration", "Database migration from version %1 to version %2 failed", initialversion, version)); } return err; } SKGError SKGDocument::createUndoRedoTemporaryTriggers() const { SKGError err; SKGTRACEINFUNCRC(10, err) // Create triggers QStringList tables; err = this->getTablesList(tables); int nbTables = tables.count(); for (int i = 0; !err && i < nbTables; ++i) { // Get table name const QString& table = tables.at(i); // Do we have to treat this table if (!SKGListNotUndoable.contains("T." % table) && !table.startsWith(QLatin1String("vm_"))) { // YES // Get attributes name QStringList attributes; err = getAttributesList(table, attributes); // Build sqlorder for update and insert QString sqlorderForUpdate2; QString sqlorderForInsert1; QString sqlorderForInsert2; int nbAttributes = attributes.count(); for (int j = 0; !err && j < nbAttributes; ++j) { // Get attribute const QString& att = attributes.at(j); // Do we have to treat this attribute if (!SKGListNotUndoable.contains("A." % table % '.' % att)) { // Build for update if (!sqlorderForUpdate2.isEmpty()) { sqlorderForUpdate2 += ','; } sqlorderForUpdate2 += att % "='||quote(old." % att % ")||'"; // Build for insert part 1 if (!sqlorderForInsert1.isEmpty()) { sqlorderForInsert1 += ','; } sqlorderForInsert1 += att; // Build for insert part 2 if (!sqlorderForInsert2.isEmpty()) { sqlorderForInsert2 += ','; } sqlorderForInsert2 += "'||quote(old." % att % ")||'"; } } // Create specific triggers for the current transaction QStringList sqlOrders; // DROP DELETE trigger sqlOrders << "DROP TRIGGER IF EXISTS UR_" % table % "_IN" // Create DELETE trigger << "CREATE TEMP TRIGGER UR_" % table % "_IN " "AFTER INSERT ON " % table % " BEGIN " "INSERT INTO doctransactionitem (rd_doctransaction_id, t_sqlorder,i_object_id,t_object_table,t_action) VALUES(0,'DELETE FROM " % table % " WHERE id='||new.id,new.id,'" % table % "','D');END" // DROP UPDATE trigger << "DROP TRIGGER IF EXISTS UR_" % table % "_UP" // Create UPDATE trigger << "CREATE TEMP TRIGGER UR_" % table % "_UP " "AFTER UPDATE ON " % table % " BEGIN " "INSERT INTO doctransactionitem (rd_doctransaction_id, t_sqlorder,i_object_id,t_object_table,t_action) VALUES(0,'UPDATE " % table % " SET " % sqlorderForUpdate2 % " WHERE id='||new.id,new.id,'" % table % "','U');END" // DROP INSERT trigger << "DROP TRIGGER IF EXISTS UR_" % table % "_DE" // Create INSERT trigger << "CREATE TEMP TRIGGER UR_" % table % "_DE " "AFTER DELETE ON " % table % " BEGIN " "INSERT INTO doctransactionitem (rd_doctransaction_id, t_sqlorder,i_object_id,t_object_table,t_action) VALUES(0,'INSERT INTO " % table % '(' % sqlorderForInsert1 % ") VALUES(" % sqlorderForInsert2 % ")',old.id,'" % table % "','I'); END"; err = executeSqliteOrders(sqlOrders); } } return err; } QStringList SKGDocument::getParameters(const QString& iParentUUID, const QString& iWhereClause) { SKGTRACEINFUNC(10) QStringList output; QString wc = "t_uuid_parent='" % SKGServices::stringToSqlString(iParentUUID) % '\''; if (!iWhereClause.isEmpty()) { wc += " AND (" % iWhereClause % ')'; } this->getDistinctValues(QStringLiteral("parameters"), QStringLiteral("t_name"), wc, output); return output; } QString SKGDocument::getParameter(const QString& iName, const QString& iParentUUID) const { SKGTRACEINFUNC(10) SKGTRACEL(10) << "Input parameter [iName]=[" << iName << ']' << endl; QString output; // Get parameter SKGObjectBase param; SKGError err = getObject(QStringLiteral("parameters"), "t_name='" % SKGServices::stringToSqlString(iName) % "' AND t_uuid_parent='" % SKGServices::stringToSqlString(iParentUUID) % '\'', param); IFOK(err) { output = param.getAttribute(QStringLiteral("t_value")); } return output; } QVariant SKGDocument::getParameterBlob(const QString& iName, const QString& iParentUUID) const { SKGTRACEINFUNC(10) SKGTRACEL(10) << "Input parameter [iName]=[" << iName << ']' << endl; QVariant output; if (getMainDatabase() != nullptr) { QString sqlQuery = QStringLiteral("SELECT b_blob FROM parameters WHERE t_name=? AND t_uuid_parent=?"); QSqlQuery query(*getMainDatabase()); query.prepare(sqlQuery); query.addBindValue(iName); query.addBindValue(iParentUUID); if (Q_LIKELY(!query.exec())) { QSqlError sqlError = query.lastError(); SKGTRACE << "WARNING: " << sqlQuery << endl; SKGTRACE << " returns :" << sqlError.text() << endl; } else { if (query.next()) { output = query.value(0); } } } return output; } SKGError SKGDocument::setParameter(const QString& iName, const QString& iValue, const QString& iFileName, const QString& iParentUUID, SKGPropertyObject* oObjectCreated) const { SKGError err; SKGTRACEINFUNCRC(10, err) SKGTRACEL(10) << "Input parameter [iName] =[" << iName << ']' << endl; SKGTRACEL(10) << "Input parameter [iValue] =[" << iValue << ']' << endl; SKGTRACEL(10) << "Input parameter [iFileName]=[" << iFileName << ']' << endl; QVariant blob; QString value = iValue; QFile file(iFileName); if (file.exists()) { QFileInfo fileInfo(iFileName); if (fileInfo.isDir()) { value = "file://" % iFileName; } else { // Open file if (Q_UNLIKELY(!file.open(QIODevice::ReadOnly))) { err = SKGError(ERR_INVALIDARG, i18nc("Error message: Could not open a file", "Open file '%1' failed", iFileName)); } else { QByteArray blob_bytes = file.readAll(); if (blob_bytes.isEmpty()) { err = SKGError(ERR_INVALIDARG, i18nc("Error message: Could not open a file", "Open file '%1' failed", iFileName)); } else { blob = blob_bytes; value = fileInfo.fileName(); } // close file file.close(); } } } IFOKDO(err, setParameter(iName, value, blob, iParentUUID, oObjectCreated)) return err; } SKGError SKGDocument::setParameter(const QString& iName, const QString& iValue, const QVariant& iBlob, const QString& iParentUUID, SKGPropertyObject* oObjectCreated) const { SKGError err; SKGTRACEINFUNCRC(10, err) SKGTRACEL(10) << "Input parameter [iName] =[" << iName << ']' << endl; SKGTRACEL(10) << "Input parameter [iValue] =[" << iValue << ']' << endl; if (getMainDatabase() == nullptr) { err = SKGError(ERR_POINTER, i18nc("Error message", "No database defined")); } else { SKGPropertyObject param(const_cast(this)); IFOKDO(err, param.setName(iName)) IFOKDO(err, param.setValue(iValue)) IFOKDO(err, param.setParentId(iParentUUID)) IFOKDO(err, param.save(true, oObjectCreated != nullptr)) if (!err && !iBlob.isNull()) { err = param.load(); IFOK(err) { // Set blob QString sqlQuery = QStringLiteral("UPDATE parameters SET b_blob=? WHERE id=?"); QSqlQuery query(*getMainDatabase()); query.prepare(sqlQuery); query.addBindValue(iBlob); query.addBindValue(param.getID()); if (Q_LIKELY(!query.exec())) { QSqlError sqlError = query.lastError(); QString msg = sqlQuery % ':' % sqlError.text(); err = SKGError(SQLLITEERROR + sqlError.nativeErrorCode().toInt(), msg); } } } if (!err && oObjectCreated != nullptr) { *oObjectCreated = param; } } return err; } SKGError SKGDocument::dump(int iMode) const { SKGError err; // dump parameters SKGTRACE << "=== START DUMP ===" << endl; if ((iMode & DUMPSQLITE) != 0) { SKGTRACE << "=== DUMPSQLITE ===" << endl; err.addError(dumpSelectSqliteOrder(QStringLiteral("SELECT * FROM sqlite_master order by type"))); SKGTRACE << "=== DUMPSQLITE (TEMPORARY) ===" << endl; err.addError(dumpSelectSqliteOrder(QStringLiteral("SELECT * FROM sqlite_temp_master order by type"))); } if ((iMode & DUMPPARAMETERS) != 0) { SKGTRACE << "=== DUMPPARAMETERS ===" << endl; err.addError(dumpSelectSqliteOrder(QStringLiteral("SELECT * FROM parameters order by id"))); } if ((iMode & DUMPNODES) != 0) { SKGTRACE << "=== DUMPNODES ===" << endl; err.addError(dumpSelectSqliteOrder(QStringLiteral("SELECT * FROM node order by id"))); } if ((iMode & DUMPTRANSACTIONS) != 0) { // dump transaction SKGTRACE << "=== DUMPTRANSACTIONS ===" << endl; err.addError(dumpSelectSqliteOrder(QStringLiteral("SELECT * FROM doctransaction order by id"))); // dump transaction SKGTRACE << "=== DUMPTRANSACTIONS (ITEMS) ===" << endl; err.addError(dumpSelectSqliteOrder(QStringLiteral("SELECT * FROM doctransactionitem order by rd_doctransaction_id, id"))); } SKGTRACE << "=== END DUMP ===" << endl; return err; } QSqlDatabase* SKGDocument::getMainDatabase() const { if (!d->m_currentDatabase.isOpen()) { return nullptr; } return const_cast(&d->m_currentDatabase); } QSqlDatabase SKGDocument::getThreadDatabase() const { if (qApp->thread() != QThread::currentThread()) { d->m_mutex.lock(); QString pwd = getPassword(); QString dbName = getMainDatabase()->databaseName(); QString conName = getMainDatabase()->connectionName(); QString id = conName % "_" % QString::number((quint64)QThread::currentThread(), 16); d->m_mutex.unlock(); auto tmpDatabase = QSqlDatabase::database(id); if (!tmpDatabase.isValid()) { tmpDatabase = QSqlDatabase::addDatabase(SQLDRIVERNAME, id); } if (tmpDatabase.databaseName() != dbName) { tmpDatabase.setConnectOptions(QStringLiteral("QSQLITE_OPEN_URI")); tmpDatabase.setDatabaseName(dbName); if (tmpDatabase.open()) { addSqliteAddon(&tmpDatabase); if (d->m_modeSQLCipher) { SKGServices::executeSqliteOrder(tmpDatabase, "PRAGMA KEY = '" % SKGServices::stringToSqlString(pwd.isEmpty() ? QStringLiteral("DEFAULTPASSWORD") : pwd) % "'"); } } } return tmpDatabase; } return d->m_currentDatabase; } SKGError SKGDocument::getConsolidatedView(const QString& iTable, const QString& iAsColumn, const QString& iAsRow, const QString& iAttribute, const QString& iOpAtt, const QString& iWhereClause, SKGStringListList& oTable, const QString& iMissingValue) const { SKGError err; SKGTRACEINFUNCRC(10, err) SKGTRACEL(10) << "Input parameter [iTable]=[" << iTable << ']' << endl; SKGTRACEL(10) << "Input parameter [iAsColumn]=[" << iAsColumn << ']' << endl; SKGTRACEL(10) << "Input parameter [iAsRow]=[" << iAsRow << ']' << endl; SKGTRACEL(10) << "Input parameter [iAttribute]=[" << iAttribute << ']' << endl; SKGTRACEL(10) << "Input parameter [iOpAtt]=[" << iOpAtt << ']' << endl; SKGTRACEL(10) << "Input parameter [iWhereClause]=[" << iWhereClause << ']' << endl; SKGTRACEL(10) << "Input parameter [iMissingValue]=[" << iMissingValue << ']' << endl; // Mode int mode = 0; if (!iAsColumn.isEmpty()) { mode += 1; } if (!iAsRow.isEmpty()) { mode += 2; } oTable.clear(); oTable.push_back(QStringList()); QStringList titles = oTable.at(0); if (mode == 3) { titles.push_back(iAsRow % '/' % iAsColumn); } else { if (mode == 1) { titles.push_back(iAsColumn); QStringList sums; sums.push_back(i18nc("Noun, the numerical sum of a list of values", "Sum")); oTable.push_back(sums); } else { if (mode == 2) { titles.push_back(iAsRow); titles.push_back(i18nc("Noun, the numerical sum of a list of values", "Sum")); } } } oTable.removeAt(0); oTable.insert(0, titles); // Create sqlorder QString asColumn = iAsColumn; if (asColumn.startsWith(QLatin1String("p_"))) { QString propertyName = asColumn.right(asColumn.length() - 2); asColumn = "(SELECT t_value FROM parameters WHERE t_uuid_parent=" % iTable % ".id||'-" % SKGServices::getRealTable(iTable) % "' AND t_name='" % propertyName % "')"; } QString asRow = iAsRow; if (asRow.startsWith(QLatin1String("p_"))) { QString propertyName = asRow.right(asRow.length() - 2); asRow = "(SELECT t_value FROM parameters WHERE t_uuid_parent=" % iTable % ".id||'-" % SKGServices::getRealTable(iTable) % "' AND t_name='" % propertyName % "')"; } QString att = asColumn; if (!att.isEmpty() && !asRow.isEmpty()) { att += ','; } att += asRow; QString sort = asRow; if (!sort.isEmpty() && !asColumn.isEmpty()) { sort += ','; } sort += asColumn; if (!att.isEmpty()) { QString sql = "SELECT " % att % ',' % iOpAtt % '(' % iAttribute % ") FROM " % iTable; if (!iWhereClause.isEmpty()) { sql += " WHERE " % iWhereClause; } if (!iOpAtt.isEmpty()) { sql += " GROUP BY " % att; } sql += " ORDER BY " % sort; QHash cols; QHash rows; SKGTRACEL(10) << "sqlorder=[" << sql << ']' << endl; SKGStringListList listTmp; err = executeSelectSqliteOrder(sql, listTmp); int nb = listTmp.count(); for (int i = 1; !err && i < nb; ++i) { // Title is ignored const QStringList& line = listTmp.at(i); int rowindex = -1; int colindex = -1; if (mode >= 2) { const QString& rowname = line.at(mode == 3 ? 1 : 0); if (!rows.contains(rowname)) { QStringList r; int nbx = oTable.at(0).count(); r.reserve(nbx); r.push_back(rowname); for (int j = 1; j < nbx; ++j) { r.push_back(iMissingValue); } oTable.push_back(r); rowindex = oTable.count() - 1; rows.insert(rowname, rowindex); } else { rowindex = rows[rowname]; } } else { rowindex = 1; } if (mode == 1 || mode == 3) { const QString& colname = line.at(0); if (!cols.contains(colname)) { // Search better position of this column colindex = -1; { QHashIterator cols_i(cols); while (cols_i.hasNext()) { cols_i.next(); if (colname > cols_i.key() && cols_i.value() > colindex) { colindex = cols_i.value(); } } } if (colindex == -1) { colindex = 1; } else { ++colindex; } int nbx = oTable.count(); for (int j = 0; j < nbx; ++j) { if (j == 0) { oTable[j].insert(colindex, colname); } else { oTable[j].insert(colindex, iMissingValue); } } { QHash tmp; QHashIterator cols_i(cols); while (cols_i.hasNext()) { cols_i.next(); tmp.insert(cols_i.key(), cols_i.value() + (cols_i.value() >= colindex ? 1 : 0)); } cols = tmp; } cols.insert(colname, colindex); } else { colindex = cols[colname]; } } else { colindex = 1; } const QString& sum = line.at(mode == 3 ? 2 : 1); oTable[rowindex][colindex] = sum; } IFSKGTRACEL(10) { QStringList dump2 = SKGServices::tableToDump(oTable, SKGServices::DUMP_TEXT); int nbl = dump2.count(); for (int i = 0; i < nbl; ++i) { SKGTRACE << dump2.at(i) << endl; } } // Correction bug 205466 vvv // If some months or years are missing, we must add them. if (asColumn.startsWith(QLatin1String("d_"))) { for (int c = 1; c < oTable[0].count() - 1; ++c) { // Dynamic size bool forecast = false; QString title = oTable.at(0).at(c); if (title.isEmpty()) { title = QStringLiteral("0000"); } if (title.endsWith(QLatin1String("999"))) { title = title.left(title.count() - 3); forecast = true; } QString nextTitle = oTable.at(0).at(c + 1); if (nextTitle.endsWith(QLatin1String("999"))) { nextTitle = nextTitle.left(nextTitle.count() - 3); forecast = true; } QString dateFormat = (asColumn == QStringLiteral("d_date") ? QStringLiteral("yyyy-MM-dd") : (asColumn == QStringLiteral("d_DATEMONTH") ? QStringLiteral("yyyy-MM") : (asColumn == QStringLiteral("d_DATEQUARTER") ? QStringLiteral("yyyy-QM") : (asColumn == QStringLiteral("d_DATESEMESTER") ? QStringLiteral("yyyy-SM") : (asColumn == QStringLiteral("d_DATEWEEK") ? QStringLiteral("yyyy-WM") : QStringLiteral("yyyy")))))); QDate nextExpected = QDate::fromString(title, dateFormat); QString nextExpectedString; if (asColumn == QStringLiteral("d_DATEWEEK")) { /* TODO(Stephane MANKOWSKI) QStringList items=SKGServices::splitCSVLine(oTable.at(0).at(c),'-'); nextExpected=QDate(SKGServices::stringToInt(items.at(0)), 1, 1); QString w=items.at(1); w.remove('W'); nextExpected=nextExpected.addDays(7*SKGServices::stringToInt(w)); QString newW=SKGServices::intToString(nextExpected.weekNumber()); if(newW.count()==1) newW='0'+newW; */ nextExpectedString = nextTitle; } else if (asColumn == QStringLiteral("d_DATEMONTH")) { nextExpected = nextExpected.addMonths(1); nextExpectedString = nextExpected.toString(dateFormat); } else if (asColumn == QStringLiteral("d_DATEQUARTER")) { nextExpected = nextExpected.addMonths(nextExpected.month() * 3 - nextExpected.month()); // convert quarter in month nextExpected = nextExpected.addMonths(3); nextExpectedString = nextExpected.toString(QStringLiteral("yyyy-Q")) % (nextExpected.month() <= 3 ? '1' : (nextExpected.month() <= 6 ? '2' : (nextExpected.month() <= 9 ? '3' : '4'))); } else if (asColumn == QStringLiteral("d_DATESEMESTER")) { nextExpected = nextExpected.addMonths(nextExpected.month() * 6 - nextExpected.month()); // convert semester in month nextExpected = nextExpected.addMonths(6); nextExpectedString = nextExpected.toString(QStringLiteral("yyyy-S")) % (nextExpected.month() <= 6 ? '1' : '2'); } else if (asColumn == QStringLiteral("d_DATEYEAR")) { nextExpected = nextExpected.addYears(1); nextExpectedString = nextExpected.toString(dateFormat); } else { nextExpected = nextExpected.addDays(1); nextExpectedString = nextExpected.toString(dateFormat); } if (title != QStringLiteral("0000") && nextTitle != nextExpectedString && nextTitle != title) { int colindex = c + 1; if (forecast) { nextExpectedString += QStringLiteral("999"); } int nbx = oTable.count(); oTable[0].insert(colindex, nextExpectedString); for (int j = 1; j < nbx; ++j) { oTable[j].insert(colindex, iMissingValue); } } } } // Correction bug 205466 ^^^ } return err; } SKGDocument::SKGModelTemplateList SKGDocument::getDisplaySchemas(const QString& iRealTable) const { SKGDocument::SKGModelTemplateList listSchema; // Build schemas if (iRealTable == QStringLiteral("doctransaction")) { SKGModelTemplate def; def.id = QStringLiteral("default"); def.name = i18nc("Noun, the default value of an item", "Default"); def.icon = QStringLiteral("edit-undo"); def.schema = QStringLiteral("t_name;t_value;d_lastmodifdate;t_savestep"); listSchema.push_back(def); SKGModelTemplate minimum; minimum.id = QStringLiteral("minimum"); minimum.name = i18nc("Noun, the minimum value of an item", "Minimum"); minimum.icon = QLatin1String(""); minimum.schema = QStringLiteral("t_name;t_value;d_lastmodifdate|N;t_savestep|N"); listSchema.push_back(minimum); } else if (iRealTable == QStringLiteral("parameters")) { SKGModelTemplate def; def.id = QStringLiteral("default"); def.name = i18nc("Noun, the default value of an item", "Default"); def.icon = QStringLiteral("edit-undo"); def.schema = QStringLiteral("t_name;t_value"); listSchema.push_back(def); } else if (iRealTable == QStringLiteral("node")) { SKGModelTemplate def; def.id = QStringLiteral("default"); def.name = i18nc("Noun, the default value of an item", "Default"); def.icon = QStringLiteral("edit-undo"); def.schema = QStringLiteral("t_name"); listSchema.push_back(def); } else { SKGModelTemplate def; def.id = QStringLiteral("default"); def.name = i18nc("Noun, the default value of an item", "Default"); def.icon = QStringLiteral("edit-undo"); def.schema = QLatin1String(""); SKGStringListList lines; executeSelectSqliteOrder("PRAGMA table_info(" % iRealTable % ");", lines); for (const auto& line : qAsConst(lines)) { if (!def.schema.isEmpty()) { def.schema += ';'; } def.schema += line[1]; } listSchema.push_back(def); } return listSchema; } QString SKGDocument::getDisplay(const QString& iString) const { QString output = iString.toLower(); if (output.endsWith(QLatin1String("t_name"))) { output = i18nc("Noun, the name of an item", "Name"); } else if (output.endsWith(QLatin1String("d_date"))) { output = i18nc("Noun, the date of an item", "Date"); } else if (output.endsWith(QLatin1String("t_savestep"))) { output = i18nc("Verb, save a document", "Save"); } else if (output.endsWith(QLatin1String("t_value"))) { output = i18nc("Noun, the value of an item", "Value"); } else if (output.endsWith(QLatin1String("d_lastmodifdate"))) { output = i18nc("Noun, date of last modification", "Last modification"); } else if (output.startsWith(QLatin1String("p_"))) { output = iString; output = output.right(output.count() - 2); } else if (output.contains(QStringLiteral(".p_"))) { output = iString; output = output.replace(QStringLiteral(".p_"), QStringLiteral(".")); } else { output = iString; } return output; } QString SKGDocument::getIconName(const QString& iString) const { QString output = iString.toLower(); if (output.startsWith(QLatin1String("p_")) || output.contains(QStringLiteral("p_"))) { return QStringLiteral("tag"); } return QLatin1String(""); } QIcon SKGDocument::getIcon(const QString& iString) const { return SKGServices::fromTheme(getIconName(iString)); } QString SKGDocument::getRealAttribute(const QString& iString) const { if (iString == iString.toLower()) { return iString; } return QLatin1String(""); } SKGServices::AttributeType SKGDocument::getAttributeType(const QString& iAttributeName) const { SKGServices::AttributeType output = SKGServices::TEXT; if (iAttributeName.startsWith(QLatin1String("d_"))) { output = SKGServices::DATE; } else if (iAttributeName.startsWith(QLatin1String("i_"))) { output = SKGServices::INTEGER; } else if (iAttributeName.startsWith(QLatin1String("rd_")) || iAttributeName.startsWith(QLatin1String("rc_")) || iAttributeName.startsWith(QLatin1String("r_")) || iAttributeName.startsWith(QLatin1String("id_"))) { output = SKGServices::LINK; } else if (iAttributeName.startsWith(QLatin1String("f_"))) { output = SKGServices::FLOAT; } else if (iAttributeName.startsWith(QLatin1String("b_"))) { output = SKGServices::BLOB; } else if (iAttributeName == QStringLiteral("id")) { output = SKGServices::ID; } else if (iAttributeName == QStringLiteral("t_savestep") || iAttributeName == QStringLiteral("t_refreshviews")) { output = SKGServices::BOOL; } return output; } SKGServices::SKGUnitInfo SKGDocument::getUnit(const QString& iPrefixInCache) const { SKGServices::SKGUnitInfo output; output.Name = getCachedValue(iPrefixInCache % "UnitCache"); if (output.Name.isEmpty()) { refreshCache(QStringLiteral("unit")); output.Name = getCachedValue(iPrefixInCache % "UnitCache"); } output.Symbol = getCachedValue(iPrefixInCache % "UnitSymbolCache"); QString val = getCachedValue(iPrefixInCache % "UnitValueCache"); if (!val.isEmpty()) { output.Value = SKGServices::stringToDouble(val); } else { output.Value = 1; } val = getCachedValue(iPrefixInCache % "UnitDecimalCache"); if (!val.isEmpty()) { output.NbDecimal = SKGServices::stringToInt(val); } else { output.NbDecimal = 2; } return output; } QString SKGDocument::formatMoney(double iValue, const SKGServices::SKGUnitInfo& iUnit, bool iHtml) const { QString val = SKGServices::toCurrencyString(iValue / iUnit.Value, iUnit.Symbol, iUnit.NbDecimal); if (iHtml) { // Return value if (iValue < 0) { // Get std colors KColorScheme scheme(QPalette::Normal); return QStringLiteral("" % SKGServices::stringToHtml(val) % ""; } return SKGServices::stringToHtml(val); } return val; } QString SKGDocument::formatPrimaryMoney(double iValue) const { return SKGServices::doubleToString(iValue); } QString SKGDocument::formatSecondaryMoney(double iValue)const { return SKGServices::doubleToString(iValue); } QString SKGDocument::formatPercentage(double iValue, bool iInvertColors) const { // Get std colors KColorScheme scheme(QPalette::Normal); QString negative = scheme.foreground(KColorScheme::NegativeText).color().name(); QString positive = scheme.foreground(KColorScheme::PositiveText).color().name(); // Return value QString p = SKGServices::toPercentageString(iValue); if (iValue > 0) { p = '+' % p; } if (p.count() > 10 || std::isnan(iValue) || std::isinf(iValue)) { p = QChar(8734); } return "= 0 && iInvertColors) ? negative : positive) % "\">" % SKGServices::stringToHtml(p) % ""; } QString SKGDocument::getFileExtension() const { return QStringLiteral("skgc"); } QString SKGDocument::getDocumentHeader() const { return QStringLiteral("SKG"); } void SKGDocument::addValueInCache(const QString& iKey, const QString& iValue) const { d->m_cache[iKey] = iValue; } void SKGDocument::addSqlResultInCache(const QString& iKey, const SKGStringListList& iValue) const { d->m_mutex.lock(); (*d->m_cacheSql)[iKey] = iValue; d->m_mutex.unlock(); } QString SKGDocument::getCachedValue(const QString& iKey) const { return d->m_cache.value(iKey); } SKGStringListList SKGDocument::getCachedSqlResult(const QString& iKey) const { return d->m_cacheSql->value(iKey); } void SKGDocument::refreshCache(const QString& iTable) const { Q_UNUSED(iTable) } void SKGDocument::setBackupParameters(const QString& iPrefix, const QString& iSuffix) const { d->m_backupPrefix = iPrefix; d->m_backupSuffix = iSuffix; } QString SKGDocument::getCurrentTemporaryFile() const { return d->m_temporaryFile; } QString SKGDocument::getTemporaryFile(const QString& iFileName, bool iForceReadOnly) { QString output; QFileInfo fi(iFileName); QFileInfo di(fi.dir().path()); if (iForceReadOnly || !QUrl::fromUserInput(iFileName).isLocalFile() || !di.permission(QFile::WriteUser)) { output = QDir::tempPath(); } else { output = fi.absolutePath(); } return output += "/." % fi.fileName() % ".wrk"; } QString SKGDocument::getBackupFile(const QString& iFileName) const { QString output; if (!d->m_backupPrefix.isEmpty() || !d->m_backupSuffix.isEmpty()) { QFileInfo fi(iFileName); if (d->m_backupSuffix.endsWith(QLatin1String(".skg"))) { output = d->m_backupPrefix % fi.baseName() % d->m_backupSuffix; } else { output = d->m_backupPrefix % fi.fileName() % d->m_backupSuffix; } output = output.replace(QStringLiteral(""), SKGServices::dateToSqlString(QDateTime::currentDateTime().date())); output = output.replace(QStringLiteral("