diff --git a/skgbankmodeler/skgimportexportmanager.cpp b/skgbankmodeler/skgimportexportmanager.cpp index 6d6c7bbe6..d80bece31 100644 --- a/skgbankmodeler/skgimportexportmanager.cpp +++ b/skgbankmodeler/skgimportexportmanager.cpp @@ -1,940 +1,940 @@ /*************************************************************************** * 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 SKGImportExportManager. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgimportexportmanager.h" #include #include #include #include #include #include #include #include #include "skgbankincludes.h" #include "skgimportplugin.h" #include "skgobjectbase.h" #include "skgpayeeobject.h" #include "skgrecurrentoperationobject.h" #include "skgruleobject.h" #include "skgservices.h" #include "skgtraces.h" SKGImportExportManager::SKGImportExportManager(SKGDocumentBank* iDocument, QUrl iFileName) : m_document(iDocument), m_fileName(std::move(iFileName)), m_defaultAccount(nullptr), m_defaultUnit(nullptr), m_importPlugin(nullptr), m_exportPlugin(nullptr) { SKGTRACEINFUNC(10); setAutomaticValidation(true); setAutomaticApplyRules(false); m_since_last_import = true; m_codec = QStringLiteral("UTF-8"); } SKGImportExportManager::~SKGImportExportManager() { SKGTRACEINFUNC(10); setDefaultAccount(nullptr); setDefaultUnit(nullptr); m_document = nullptr; m_importPlugin = nullptr; m_exportPlugin = nullptr; if (!m_localFileName.isEmpty() && m_localFileName != getFileName().toLocalFile()) { QFile(m_localFileName).remove(); } } QString SKGImportExportManager::getFileNameExtension() const { return QFileInfo(getFileName().path()).suffix().toUpper(); } QUrl SKGImportExportManager::getFileName() const { return m_fileName; } QString SKGImportExportManager::getLocalFileName(bool iDownload) { if (m_localFileName.isEmpty()) { if (getFileName().isLocalFile()) { m_localFileName = getFileName().toLocalFile(); } else { if (iDownload) { SKGServices::download(QUrl(getFileName()), m_localFileName); } else { QTemporaryFile tmpFile; tmpFile.setAutoRemove(false); tmpFile.open(); m_localFileName = tmpFile.fileName(); } } } return m_localFileName; } SKGError SKGImportExportManager::setDefaultAccount(SKGAccountObject* iAccount) { SKGError err; SKGTRACEINFUNCRC(10, err); delete m_defaultAccount; m_defaultAccount = nullptr; if (iAccount != nullptr) { m_defaultAccount = new SKGAccountObject(*iAccount); } return err; } SKGError SKGImportExportManager::getDefaultAccount(SKGAccountObject& oAccount) { SKGError err; SKGTRACEINFUNCRC(10, err); if (m_defaultAccount == nullptr && (m_document != nullptr)) { QFileInfo fInfo(getLocalFileName()); QStringList items = fInfo.completeBaseName().split('@'); QString bankname; QString number; QString nameComplete; QString name = items.at(0); if (items.count() == 2) { bankname = items.at(1); QStringList items2 = name.split('-'); if (items2.count() == 2) { name = items2.at(0); number = items2.at(1); } else { number = name; } name.replace('_', ' '); name.replace('-', ' '); nameComplete = name; } else { bankname = name; name.replace('_', ' '); name.replace('-', ' '); nameComplete = name; QRegExp rx(QStringLiteral("(\\d{6,})")); if (rx.indexIn(name) != -1) { number = rx.cap(1); if (name != number) { name = name.remove(number).trimmed(); } } } // Searching if an account exist QString whereClause = "t_name='" % SKGServices::stringToSqlString(name) % '\''; whereClause += " OR t_number='" % SKGServices::stringToSqlString(name) % "'"; whereClause += " OR t_agency_number||t_number='" % SKGServices::stringToSqlString(name) % "'"; whereClause += " OR t_BANK_NUMBER||t_agency_number||t_number='" % SKGServices::stringToSqlString(name) % "'"; whereClause += " OR (t_number!='' AND '" % SKGServices::stringToSqlString(name) % "' LIKE t_BANK_NUMBER||t_agency_number||t_number||'__')"; if (!number.isEmpty()) { whereClause += " OR t_number='" % SKGServices::stringToSqlString(number) % "'"; whereClause += " OR t_agency_number||t_number='" % SKGServices::stringToSqlString(number) % "'"; whereClause += " OR t_BANK_NUMBER||t_agency_number||t_number='" % SKGServices::stringToSqlString(number) % "'"; whereClause += " OR (t_number!='' AND '" % SKGServices::stringToSqlString(number) % "' LIKE t_BANK_NUMBER||t_agency_number||t_number||'__')"; } const auto words = nameComplete.split(' '); for (const auto& val : words) { whereClause += " OR t_number='" % SKGServices::stringToSqlString(val) % '\''; } // Avoid to take account with another import ongoing whereClause = '(' % whereClause % ") AND NOT EXISTS(SELECT 1 FROM operation WHERE operation.rd_account_id=v_account.id AND operation.t_imported='T')"; SKGObjectBase::SKGListSKGObjectBase listAccount; err = m_document->getObjects(QStringLiteral("v_account"), whereClause % " ORDER BY t_type ASC", listAccount); IFOK(err) { if (!listAccount.isEmpty()) { // Yes ! Only one account found SKGAccountObject account(listAccount.at(0)); m_defaultAccount = new SKGAccountObject(account); err = m_document->sendMessage(i18nc("An information message", "Using account '%1' for import", account.getName())); } } // If better account not found, then we must create one if (m_defaultAccount == nullptr) { SKGAccountObject account; SKGBankObject bank(m_document); IFOKDO(err, bank.setName(bankname)) if (!err && bank.load().isFailed()) { err = bank.save(false); // Save only } IFOKDO(err, bank.addAccount(account)) IFOKDO(err, account.setNumber(number)) int index = 1; do { IFOKDO(err, account.setName(name % (index > 1 ? SKGServices::intToString(index) : QString()))) IFOK(err) { if (account.exist()) { ++index; } else { err = account.save(false); // Save only index = -1; } } } while (!err && index > 0); IFOK(err) { m_defaultAccount = new SKGAccountObject(account); } IFOKDO(err, m_document->sendMessage(i18nc("An information message", "Default account '%1' created for import", name))) } } if (m_defaultAccount != nullptr) { oAccount = *m_defaultAccount; } return err; } SKGError SKGImportExportManager::setDefaultUnit(SKGUnitObject* iUnit) { SKGError err; SKGTRACEINFUNCRC(10, err); delete m_defaultUnit; m_defaultUnit = nullptr; if (iUnit != nullptr) { m_defaultUnit = new SKGUnitObject(*iUnit); } return err; } SKGError SKGImportExportManager::getDefaultUnit(SKGUnitObject& oUnit, const QDate* iDate) { SKGError err; SKGTRACEINFUNCRC(10, err); if ((m_document != nullptr) && (m_defaultUnit == nullptr || (iDate != nullptr))) { if (m_defaultUnit != nullptr) { delete m_defaultUnit; m_defaultUnit = nullptr; } // Do we have to found the best unit for a date ? QString wc = QStringLiteral("t_type IN ('1', '2', 'C')"); if (iDate != nullptr) { // Yes wc += " AND (d_MINDATE<'" % SKGServices::dateToSqlString(QDateTime(*iDate)) % "' OR d_MINDATE IS NULL)"; } // Check if a unit exist SKGObjectBase::SKGListSKGObjectBase listUnits; err = m_document->getObjects(QStringLiteral("v_unit"), wc % " ORDER BY ABS(f_CURRENTAMOUNT-1) ASC", listUnits); IFOK(err) { if (listUnits.isEmpty()) { SKGUnitObject unit(m_document); QString name = i18nc("Noun", "Unit for import"); err = unit.setName(name); if (unit.load().isFailed()) { IFOKDO(err, unit.setSymbol(name)) IFOKDO(err, unit.save(false)) SKGUnitValueObject unitval; IFOKDO(err, unit.addUnitValue(unitval)) IFOKDO(err, unitval.setQuantity(1)) IFOKDO(err, unitval.setDate(QDate(1970, 1, 1))) IFOKDO(err, unitval.save(false, false)) IFOKDO(err, m_document->sendMessage(i18nc("An information message", "Default unit '%1' created for import", name))) } IFOK(err) m_defaultUnit = new SKGUnitObject(unit); } else { // Found, we can use it m_defaultUnit = new SKGUnitObject(listUnits.at(0)); } } } if (m_defaultUnit != nullptr) { oUnit = *m_defaultUnit; } return err; } void SKGImportExportManager::setAutomaticValidation(bool iValidation) { m_automaticValidationOfImportedOperation = iValidation; } bool SKGImportExportManager::automaticValidation() const { return m_automaticValidationOfImportedOperation; } void SKGImportExportManager::setAutomaticApplyRules(bool iApply) { m_automaticApplyRulesOfImportedOperation = iApply; } bool SKGImportExportManager::automaticApplyRules() const { return m_automaticApplyRulesOfImportedOperation; } void SKGImportExportManager::setSinceLastImportDate(bool iSinceLast) { m_since_last_import = iSinceLast; } bool SKGImportExportManager::sinceLastImportDate() const { return m_since_last_import; } void SKGImportExportManager::addAccountToCheck(const SKGAccountObject& iAccount, double iBalance) { m_AccountToCheck.append(QPair(iAccount, iBalance)); } QList > SKGImportExportManager::getAccountsToCheck() { return m_AccountToCheck; } SKGError SKGImportExportManager::finalizeImportation() { SKGError err; if (!getImportParameters().contains(QStringLiteral("donotfinalize"))) { SKGTRACEINFUNCRC(2, err); if (m_document != nullptr) { // Count the number of operations imported but already existing QString wc = "v_operation.t_imported='T' AND exists (SELECT 1 from v_operation op2 WHERE op2.t_imported!='T' AND op2.rd_account_id=v_operation.rd_account_id AND op2.t_import_id=v_operation.t_import_id AND ABS(op2.f_CURRENTAMOUNT-v_operation.f_CURRENTAMOUNT)<" % SKGServices::doubleToString(EPSILON) % ')'; SKGObjectBase::SKGListSKGObjectBase objects; err = m_document->getObjects(QStringLiteral("v_operation"), wc, objects); int nbOperations = objects.count(); if (!err && (nbOperations != 0)) { err = m_document->sendMessage(i18np("One operation not imported because it already exists", "%1 operations not imported because they already exist", nbOperations), SKGDocument::Warning); SKGTRACEL(1) << "### LIST OF OPERATIONS DELETED BY IMPORT_ID ###" << endl; for(int i=0; iexecuteSqliteOrder("DELETE from operation WHERE id IN (SELECT id from v_operation WHERE " % wc % ')')) } // Delete operations before the last import if (sinceLastImportDate() && !err) { SKGTRACEINRC(2, "SKGImportExportManager::finalizeImportation-since last date", err); // Count the number of operations before the last import QString wc2 = QStringLiteral("operation.t_imported='T' AND " "EXISTS(SELECT 1 FROM operation o INDEXED BY idx_operation_rd_account_id_t_imported WHERE o.rd_account_id=operation.rd_account_id AND o.t_imported IN ('Y', 'P') AND date(o.d_date,'-4 day')>operation.d_date)"); err = m_document->getObjects(QStringLiteral("operation"), wc2, objects); int nbOperations2 = objects.count(); if (!err && (nbOperations2 != 0)) { err = m_document->sendMessage(i18np("One operation was not imported because it was dated before the last imported one, you can uncheck the option to avoid this.", "%1 operations were not imported because they were dated before the last imported one, you can uncheck the option to avoid this.", nbOperations2), SKGDocument::Warning); SKGTRACEL(1) << "### LIST OF OPERATIONS DELETED BECAUSE AFTER THE LAST ONE ###" << endl; for(int i=0; iexecuteSqliteOrder("DELETE from operation WHERE " % wc2)) } } // For performances IFOKDO(err, m_document->executeSqliteOrder(QStringLiteral("ANALYZE"))) // Apply rules if (!err && m_automaticApplyRulesOfImportedOperation) { SKGTRACEINRC(2, "SKGImportExportManager::finalizeImportation-apply rules", err); // Get rules SKGObjectBase::SKGListSKGObjectBase rules; IFOKDO(err, m_document->getObjects(QStringLiteral("v_rule"), QStringLiteral("1=1 ORDER BY f_sortorder"), rules)) int nbRules = rules.count(); IFOKDO(err, m_document->beginTransaction("#INTERNAL#" % i18nc("Progression step", "Finalize import"), nbRules)) for (int i = 0; !err && i < nbRules; ++i) { SKGRuleObject rule(rules.at(i)); err = rule.execute(SKGRuleObject::IMPORTING); IFOKDO(err, m_document->stepForward(i + 1)) } SKGENDTRANSACTION(m_document, err); // A failure in rule will not block the transaction. // A warning message is sent IFKO(err) err = m_document->sendMessage(i18nc("Warning message", "Error during execution of rules:\n%1", err.getFullMessageWithHistorical()), SKGDocument::Error); } // Change imported status IFOK(err) { SKGTRACEINRC(2, "SKGImportExportManager::finalizeImportation-change imported status", err); err = m_document->executeSqliteOrder(QStringLiteral("UPDATE operation SET t_imported='") % (m_automaticValidationOfImportedOperation ? QStringLiteral("Y") : QStringLiteral("P")) % "' WHERE t_imported='T'"); } // Check balances of accounts int nb = m_AccountToCheck.count(); for (int i = 0; !err && i < nb; ++i) { // Get the account to check auto act = m_AccountToCheck.at(i).first; auto targetBalance = m_AccountToCheck.at(i).second; err = act.setAttribute(QStringLiteral("f_importbalance"), SKGServices::doubleToString(targetBalance)); IFOKDO(err, act.setAttribute(QStringLiteral("d_importdate"), SKGServices::dateToSqlString(QDate::currentDate()))); IFOKDO(err, act.save()); auto soluces = act.getPossibleReconciliations(targetBalance, false); if (soluces.isEmpty()) { IFOKDO(err, m_document->sendMessage(i18nc("Information message", "The balance of account '%1' is not aligned with import balance %2", act.getDisplayName(), m_document->formatMoney(targetBalance, m_document->getPrimaryUnit())), SKGDocument::Warning, 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", act.getDisplayName())) % "&operationWhereClause=" % SKGServices::encodeForUrl("rd_account_id=" + SKGServices::intToString(act.getID()) + " AND t_template='N' AND ((t_status='N' AND t_imported IN ('Y','P')) OR t_status='Y')")))); } else { IFOKDO(err, m_document->sendMessage(i18nc("Information message", "The balance of account '%1' is aligned with import balance %2", act.getDisplayName(), m_document->formatMoney(targetBalance, m_document->getPrimaryUnit())), SKGDocument::Positive)); } } } } return err; } void SKGImportExportManager::setExportParameters(const QMap< QString, QString >& iParameters) { SKGImportPlugin* plugin = getExportPlugin(); if (plugin != nullptr) { plugin->setExportParameters(iParameters); } } QMap< QString, QString > SKGImportExportManager::getExportParameters() { QMap< QString, QString > output; SKGImportPlugin* plugin = getExportPlugin(); if (plugin != nullptr) { output = plugin->getExportParameters(); } return output; } void SKGImportExportManager::setImportParameters(const QMap< QString, QString >& iParameters) { SKGImportPlugin* plugin = getImportPlugin(); if (plugin != nullptr) { plugin->setImportParameters(iParameters); } } QString SKGImportExportManager::getParameterDefaultValue(const QString& iParameter) { QString output; // Search the first plugin having a value for this parameter KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("SKG IMPORT/Plugin")); int nb = offers.count(); for (int i = 0; i < nb && output.isEmpty(); ++i) { KService::Ptr service = offers.at(i); KPluginLoader loader(service->library()); KPluginFactory* factory = loader.factory(); if (factory != nullptr) { auto* pluginInterface = factory->create (nullptr); if (pluginInterface != nullptr) { auto val = pluginInterface->getImportParameters().value(iParameter); if (!val.isEmpty()) { output = val; } else { output = pluginInterface->getExportParameters().value(iParameter); } delete pluginInterface; } } } return output; } QMap< QString, QString > SKGImportExportManager::getImportParameters() { QMap< QString, QString > output; SKGImportPlugin* plugin = getImportPlugin(); if (plugin != nullptr) { output = plugin->getImportParameters(); } return output; } QString SKGImportExportManager::getImportMimeTypeFilter(bool iIncludingAll) { QMap tmp; KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("SKG IMPORT/Plugin")); int nb = offers.count(); for (int i = 0; i < nb; ++i) { KService::Ptr service = offers.at(i); KPluginLoader loader(service->library()); KPluginFactory* factory = loader.factory(); if (factory != nullptr) { auto* pluginInterface = factory->create (nullptr); if (pluginInterface != nullptr) { if (pluginInterface->isImportPossible()) { QString mime = pluginInterface->getMimeTypeFilter(); if (!mime.isEmpty()) { QStringList lines = SKGServices::splitCSVLine(mime, '\n'); int nblines = lines.count(); for (int l = 0; l < nblines; ++l) { QStringList items = SKGServices::splitCSVLine(lines.at(l), '|'); tmp[items.at(1)] = items.at(0); } } } delete pluginInterface; } } } QStringList descriptions = tmp.keys(); std::sort(descriptions.begin(), descriptions.end()); QStringList regexps = tmp.values(); QString output; if (iIncludingAll) { output = regexps.join(QStringLiteral(" ")) % '|' % i18nc("A file format", "All supported formats"); } nb = descriptions.count(); for (int i = 0; i < nb; ++i) { if (!output.isEmpty()) { output += '\n'; } output += tmp[descriptions.at(i)] % '|' % descriptions.at(i); } return output; } QString SKGImportExportManager::getExportMimeTypeFilter(bool iIncludingAll) { QMap tmp; KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("SKG IMPORT/Plugin")); int nb = offers.count(); for (int i = 0; i < nb; ++i) { KService::Ptr service = offers.at(i); KPluginLoader loader(service->library()); KPluginFactory* factory = loader.factory(); if (factory != nullptr) { auto* pluginInterface = factory->create (nullptr); if (pluginInterface != nullptr) { if (pluginInterface->isExportPossible()) { QString mime = pluginInterface->getMimeTypeFilter(); if (!mime.isEmpty()) { QStringList lines = SKGServices::splitCSVLine(mime, '\n'); int nblines = lines.count(); for (int l = 0; l < nblines; ++l) { QStringList items = SKGServices::splitCSVLine(lines.at(l), '|'); tmp[items.at(1)] = items.at(0); } } } delete pluginInterface; } } } QStringList descriptions = tmp.keys(); std::sort(descriptions.begin(), descriptions.end()); QStringList regexps = tmp.values(); QString output; if (iIncludingAll) { output = regexps.join(QStringLiteral(" ")) % '|' % i18nc("A file format", "All supported formats"); } nb = descriptions.count(); for (int i = 0; i < nb; ++i) { if (!output.isEmpty()) { output += '\n'; } output += tmp[descriptions.at(i)] % '|' % descriptions.at(i); } return output; } SKGImportPlugin* SKGImportExportManager::getImportPlugin() { if (m_importPlugin == nullptr) { KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("SKG IMPORT/Plugin")); int nb = offers.count(); for (int i = 0; (m_importPlugin == nullptr) && i < nb; ++i) { KService::Ptr service = offers.at(i); QString id = service->property(QStringLiteral("X-Krunner-ID"), QVariant::String).toString(); KPluginLoader loader(service->library()); KPluginFactory* factory = loader.factory(); if (factory != nullptr) { auto* pluginInterface = factory->create (this); if (pluginInterface != nullptr) { if (pluginInterface->isImportPossible()) { // Import m_importPlugin = pluginInterface; } } } else if (m_document != nullptr) { m_document->sendMessage(i18nc("An information message", "Loading plugin %1 failed because the factory could not be found in %2", id, service->library()), SKGDocument::Error); } } } return m_importPlugin; } SKGImportPlugin* SKGImportExportManager::getExportPlugin() { if (m_exportPlugin == nullptr) { KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("SKG IMPORT/Plugin")); int nb = offers.count(); for (int i = 0; (m_exportPlugin == nullptr) && i < nb; ++i) { KService::Ptr service = offers.at(i); QString id = service->property(QStringLiteral("X-Krunner-ID"), QVariant::String).toString(); KPluginLoader loader(service->library()); KPluginFactory* factory = loader.factory(); if (factory != nullptr) { auto* pluginInterface = factory->create (this); if (pluginInterface != nullptr) { if (pluginInterface->isExportPossible()) { // Import m_exportPlugin = pluginInterface; } } } else if (m_document != nullptr) { m_document->sendMessage(i18nc("An information message", "Loading plugin %1 failed because the factory could not be found in %2", id, service->library()), SKGDocument::Error); } } } return m_exportPlugin; } SKGError SKGImportExportManager::importFile() { SKGError err; SKGTRACEINFUNCRC(2, err); if (m_document != nullptr) { SKGBEGINPROGRESSTRANSACTION(*m_document, i18nc("Noun, name of the user action", "Import with codec %1", m_codec), err, 3); err = m_document->executeSqliteOrder(QStringLiteral("ANALYZE")); IFOKDO(err, m_document->stepForward(1)) IFOK(err) { // Search plugins bool fileTreated = false; SKGImportPlugin* pluginInterface = getImportPlugin(); if (pluginInterface != nullptr) { // Import fileTreated = true; m_AccountToCheck.clear(); SKGTRACEL(2) << "Input filename=" << m_fileName.toDisplayString() << endl; SKGTRACEL(2) << "Input local filename=" << getLocalFileName() << endl; err = pluginInterface->importFile(); } if (!err && !fileTreated) { err.setReturnCode(ERR_NOTIMPL).setMessage(i18nc("Error message", "The import mode %1 is not yet implemented", getFileNameExtension())); } } IFOKDO(err, m_document->stepForward(2)) IFOKDO(err, finalizeImportation()) IFOKDO(err, m_document->stepForward(3)) } return err; } SKGError SKGImportExportManager::exportFile() { SKGError err; SKGTRACEINFUNCRC(2, err); if (m_document != nullptr) { err = m_document->executeSqliteOrder(QStringLiteral("ANALYZE")); IFOK(err) { // Search plugins bool fileTreated = false; SKGImportPlugin* pluginInterface = getExportPlugin(); if (pluginInterface != nullptr) { // Import fileTreated = true; SKGTRACEL(2) << "Input filename=" << m_fileName.toDisplayString() << endl; SKGTRACEL(2) << "Input local filename=" << getLocalFileName(false) << endl; err = pluginInterface->exportFile(); IFOKDO(err, SKGServices::upload(QUrl::fromLocalFile(getLocalFileName(false)), m_fileName)) } if (!err && !fileTreated) { err.setReturnCode(ERR_NOTIMPL).setMessage(i18nc("Error message", "This export mode is not yet implemented")); } } } return err; } SKGError SKGImportExportManager::cleanBankImport() { SKGError err; SKGTRACEINFUNCRC(2, err); // Begin transaction if (m_document != nullptr) { err = m_document->beginTransaction("#INTERNAL#" % i18nc("Progression step", "Clean import"), 3); IFOK(err) { // Step 1 Clean operations without mode and with comment with double space SKGObjectBase::SKGListSKGObjectBase operations; IFOKDO(err, m_document->getObjects(QStringLiteral("operation"), QStringLiteral("t_imported!='N' and t_mode='' and t_comment like '% %'"), operations)) int nb = operations.count(); for (int i = 0; !err && i < nb ; ++i) { SKGOperationObject op(operations.at(i)); // Comment is like this: // Example: RETRAIT DAB 20/01/08 11H44 013330 LCL GAILLAC 000497 QRegExp rx(QStringLiteral("(.+) {2,}(.+)")); QString comment = op.getComment(); if (rx.indexIn(comment) != -1) { // Get parameters QString mode = rx.cap(1); QString info = rx.cap(2); // Modify err = op.setComment(info.trimmed()); IFOKDO(err, op.setMode(mode.trimmed())) IFOKDO(err, op.save(true, false)) // No reload } } // Step 1 done IFOKDO(err, m_document->stepForward(1)) // Step 2 Clean operations without mode and with comment IFOKDO(err, m_document->getObjects(QStringLiteral("operation"), QStringLiteral("t_imported!='N' and t_mode='' and t_comment!=''"), operations)) nb = operations.count(); for (int i = 0; !err && i < nb ; ++i) { SKGOperationObject op(operations.at(i)); // Comment is like this: // Example: RETRAIT DAB 14-05-16607-482390 QRegExp rx(QStringLiteral("(\\S+) +(.+)")); QString comment = op.getComment(); if (rx.indexIn(comment) != -1) { // Get parameters QString mode = rx.cap(1); QString info = rx.cap(2); // Modify err = op.setComment(info.trimmed()); IFOKDO(err, op.setMode(mode.trimmed())) IFOKDO(err, op.save(true, false)) // No reload } } // Step 2 done IFOKDO(err, m_document->stepForward(2)) // Step 3 Clean cheque without number IFOKDO(err, m_document->getObjects(QStringLiteral("operation"), QStringLiteral("t_imported!='N' and t_number='' and lower(t_mode)='cheque'"), operations)) nb = operations.count(); for (int i = 0; !err && i < nb ; ++i) { SKGOperationObject op(operations.at(i)); // Comment is like this: // Example: RETRAIT DAB 20/01/08 11H44 013330 LCL GAILLAC 000497 QRegExp rx(QStringLiteral("(\\d+)")); QString comment = op.getComment(); if (rx.indexIn(comment) != -1) { // Get parameters auto number = rx.cap(1); // Modify err = op.setNumber(number); IFOKDO(err, op.save(true, false)) // No reload } } // Step 3 done IFOKDO(err, m_document->stepForward(3)) } SKGENDTRANSACTION(m_document, err); } return err; } SKGError SKGImportExportManager::anonymize(const QString& iKey) { SKGError err; SKGTRACEINFUNCRC(2, err); if (m_document != nullptr) { if (m_document->isFileModified()) { err = SKGError(ERR_ABORT, i18nc("An information message", "The document must be saved to be anonymized."), QStringLiteral("skg://file_save")); } else { { // Remove password m_document->changePassword(QLatin1String("")); // Anonymize data QStringList sqlOrders; if (iKey.isEmpty()) { // Not reversible sqlOrders << QStringLiteral("UPDATE bank SET t_bank_number='', t_name='bank_'||id") << QStringLiteral("UPDATE account SET t_number='', t_agency_number='', t_agency_address='', t_comment='', t_name='account_'||id") << QStringLiteral("UPDATE category SET t_name='category_'||id") << QStringLiteral("UPDATE payee SET t_address='', t_name='payee_'||id") << QStringLiteral("UPDATE refund SET t_comment='', t_name='tracker_'||id") << QStringLiteral("UPDATE operation SET t_comment=''") << QStringLiteral("UPDATE suboperation SET t_comment='', f_value=f_value%1234.56") << QStringLiteral("DELETE FROM parameters WHERE t_name NOT LIKE 'SKG_%' OR t_name='SKG_PASSWORD'"); } else { // Reversible sqlOrders << QStringLiteral("UPDATE bank SET t_bank_number=XOR(t_bank_number,'") + SKGServices::stringToSqlString(iKey) + "'), t_name=XOR(t_name,'" + SKGServices::stringToSqlString(iKey) + "')" << QStringLiteral("UPDATE account SET t_number=XOR(t_number,'") + SKGServices::stringToSqlString(iKey) + "'), t_agency_number=XOR(t_agency_number,'" + SKGServices::stringToSqlString(iKey) + "'), t_agency_address=XOR(t_agency_address,'" + SKGServices::stringToSqlString(iKey) + "'), t_comment=XOR(t_comment,'" + SKGServices::stringToSqlString(iKey) + "'), t_name=XOR(t_name,'" + SKGServices::stringToSqlString(iKey) + "')" << QStringLiteral("UPDATE category SET t_name=XOR(t_name,'") + SKGServices::stringToSqlString(iKey) + "')" << QStringLiteral("UPDATE payee SET t_address=XOR(t_address,'") + SKGServices::stringToSqlString(iKey) + "'), t_name=XOR(t_name,'" + SKGServices::stringToSqlString(iKey) + "')" << QStringLiteral("UPDATE refund SET t_comment=XOR(t_comment,'") + SKGServices::stringToSqlString(iKey) + "'), t_name=XOR(t_name,'" + SKGServices::stringToSqlString(iKey) + "')" << QStringLiteral("UPDATE operation SET t_comment=XOR(t_comment,'") + SKGServices::stringToSqlString(iKey) + "')" << QStringLiteral("UPDATE suboperation SET t_comment=XOR(t_comment,'") + SKGServices::stringToSqlString(iKey) + "'), f_value=XORD(f_value,'" + SKGServices::stringToSqlString(iKey) + "')" << QStringLiteral("UPDATE parameters SET t_name=XOR(t_name,'") + SKGServices::stringToSqlString(iKey) + "'), t_value=XOR(t_value,'" + SKGServices::stringToSqlString(iKey) + "'), b_blob=XOR(b_blob,'" + SKGServices::stringToSqlString(iKey) + "') WHERE t_name NOT LIKE 'SKG_%'" << QStringLiteral("DELETE FROM parameters WHERE t_name='SKG_PASSWORD'"); } int nb = sqlOrders.count(); SKGBEGINPROGRESSTRANSACTION(*m_document, "##INTERNAL##" % i18nc("Progression step", "Anonymize"), err, nb); for (int i = 0; !err && i < nb; ++i) { err = m_document->executeSqliteOrder(sqlOrders.at(i)); IFOKDO(err, m_document->stepForward(i + 1)) } if (iKey.isEmpty()) { m_document->sendMessage(i18nc("An information message", "The document has been made anonymous with an irreversible mode."), SKGDocument::Positive); } else { m_document->sendMessage(i18nc("An information message", "The document has been made anonymous with a reversible mode. Don't forget the key if you want to reverse it."), SKGDocument::Information); } } // Remove transactions IFOKDO(err, m_document->removeAllTransactions()) // Save new file QString newName = m_document->getCurrentFileName().replace(QStringLiteral(".skg"), QStringLiteral("-anonymized.skg")); if (newName == m_document->getCurrentFileName()) { newName = m_document->getCurrentFileName() % "-anonymized.skg"; } IFOKDO(err, m_document->saveAs(newName, true)) } } return err; } SKGError SKGImportExportManager::findAndGroupTransfers(int& oNbOperationsMerged, const QString& iAdditionnalCondition) { SKGError err; SKGTRACEINFUNCRC(2, err); oNbOperationsMerged = 0; // Begin transaction if (m_document != nullptr) { err = m_document->beginTransaction("#INTERNAL#" % i18nc("Progression step", "Find and group transfers"), 3); IFOK(err) { IFOKDO(err, m_document->executeSqliteOrder(QStringLiteral("ANALYZE"))) // Look for operations with // Same units // Same dates // Null i_group_id // Different accounts // Opposite amounts SKGStringListList listTmp; { SKGTRACEIN(2, "SKGImportExportManager::findAndGroupTransfers-step 1"); IFOKDO(err, m_document->executeSelectSqliteOrder( "SELECT ID1, ID2 FROM (SELECT A.id as ID1, B.id as ID2, (SELECT TOTAL(s.f_value) FROM suboperation s " "WHERE s.rd_operation_id=A.ID) AS quantity1, (SELECT TOTAL(s.f_value) FROM suboperation s " "WHERE s.rd_operation_id=B.ID) AS quantity2 FROM operation A, operation B " "WHERE +A.d_date=B.d_date AND A.rc_unit_id=B.rc_unit_id AND A.id<=B.id AND A.rd_account_id!=B.rd_account_id AND A.i_group_id=0 AND B.i_group_id=0" % (iAdditionnalCondition.isEmpty() ? QString() : QStringLiteral(" AND ") % iAdditionnalCondition) % ") " "WHERE ABS(quantity1+quantity2)<1e-5 AND quantity1!=0", listTmp)); // Step 1 done IFOKDO(err, m_document->stepForward(1)) } SKGStringListList listTmp2; { SKGTRACEIN(2, "SKGImportExportManager::findAndGroupTransfers-step 2"); // +A.i_group_id=0 AND +B.i_group_id=0 is for avoiding to use bad index IFOKDO(err, m_document->executeSelectSqliteOrder( "SELECT A.id, B.id FROM v_operation A, operation B, parameters P " "WHERE +P.t_name='SKG_OP_ORIGINAL_AMOUNT' AND +P.t_uuid_parent=B.id||'-operation' AND A.rc_unit_id!=B.rc_unit_id AND A.d_date=B.d_date AND A.rd_account_id!=B.rd_account_id AND ABS(A.f_CURRENTAMOUNT+CAST(P.t_value AS REAL))<" % SKGServices::doubleToString(EPSILON) % " AND +A.i_group_id=0 AND +B.i_group_id=0 AND A.f_CURRENTAMOUNT!=0" % (iAdditionnalCondition.isEmpty() ? QString() : QStringLiteral(" AND ") % iAdditionnalCondition), listTmp2)); // Step 2 done IFOKDO(err, m_document->stepForward(2)) listTmp2.removeAt(0); // Remove header listTmp += listTmp2; } // Group { SKGTRACEIN(2, "SKGImportExportManager::findAndGroupTransfers-group"); oNbOperationsMerged = listTmp.count(); IFOKDO(err, m_document->beginTransaction("#INTERNAL#" % i18nc("Progression step", "Find and group transfers"), oNbOperationsMerged - 1)) for (int i = 1; !err && i < oNbOperationsMerged ; ++i) { // First line ignored because of its header SKGOperationObject op1(m_document, SKGServices::stringToInt(listTmp.at(i).at(0))); SKGOperationObject op2(m_document, SKGServices::stringToInt(listTmp.at(i).at(1))); if (!op1.isInGroup() && !op2.isInGroup()) { err = op2.setGroupOperation(op1); IFOKDO(err, op2.save(true, false)) } IFOKDO(err, m_document->stepForward(i)) } SKGENDTRANSACTION(m_document, err); } oNbOperationsMerged = (oNbOperationsMerged - 1) * 2; // Step 3 done IFOKDO(err, m_document->stepForward(3)) } SKGENDTRANSACTION(m_document, err); } return err; } SKGError SKGImportExportManager::findAndGroupTransfers(int& oNbOperationsMerged, bool iOnCurrentlyImport) { return findAndGroupTransfers(oNbOperationsMerged, iOnCurrentlyImport ? QStringLiteral("A.t_imported='T' AND B.t_imported='T'") : QLatin1String("")); }