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_pref005965422Download on openfalseOnce a dayOnce a weekOnce a monthQt::Horizontal4020Download mode2Last &value found only&Monthly values since last downloadWee&kly values since last downloadDail&y values since last downloadMonthly values for all datesWeekly values for all datesDaily values for all dates2Maximum num&ber of imported values:kcfg_nb_loaded_values50Qt::Horizontal4020API 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>true2For security reasons, these keys are not saved in setting file but in your document.Save in documenttrueQt::Vertical2044KComboBoxQComboBoxkcombobox.hkcfg_download_on_openkcfg_download_frequencykcfg_lastkcfg_last_monthlykcfg_last_weeklykcfg_last_dailykcfg_all_monthlykcfg_all_weeklykcfg_all_dailykcfg_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("