File indexing completed on 2024-06-16 04:47:27

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007  * This file is Skrooge plugin for import and export operation.
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgimportexportplugin.h"
0012 
0013 #include <kaboutdata.h>
0014 #include <kactioncollection.h>
0015 #include <kactionmenu.h>
0016 #include <kencodingfiledialog.h>
0017 #include <kmessagewidget.h>
0018 #include <kpassworddialog.h>
0019 #include <kpluginfactory.h>
0020 #include <kservice.h>
0021 #include <kservicetypetrader.h>
0022 #include <kmessagebox.h>
0023 
0024 #include <qaction.h>
0025 #include <qdiriterator.h>
0026 #include <qdom.h>
0027 #include <qregularexpression.h>
0028 #include <qstandardpaths.h>
0029 #include <qsysinfo.h>
0030 #include <qtextcodec.h>
0031 #include <qvariant.h>
0032 
0033 #include "skgbankincludes.h"
0034 #include "skgerror.h"
0035 #include "skgimportexport_settings.h"
0036 #include "skgmainpanel.h"
0037 #include "skgoperationobject.h"
0038 #include "skgtraces.h"
0039 
0040 /**
0041  * This plugin factory.
0042  */
0043 K_PLUGIN_CLASS_WITH_JSON(SKGImportExportPlugin, "metadata.json")
0044 
0045 SKGImportExportPlugin::SKGImportExportPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) :
0046     SKGInterfacePlugin(iParent),
0047     m_currentBankDocument(nullptr), m_install(false)
0048 {
0049     Q_UNUSED(iWidget)
0050     SKGTRACEINFUNC(10)
0051 }
0052 
0053 SKGImportExportPlugin::~SKGImportExportPlugin()
0054 {
0055     SKGTRACEINFUNC(10)
0056     m_currentBankDocument = nullptr;
0057 }
0058 
0059 bool SKGImportExportPlugin::setupActions(SKGDocument* iDocument)
0060 {
0061     SKGTRACEINFUNC(10)
0062 
0063     m_currentBankDocument = qobject_cast<SKGDocumentBank*>(iDocument);
0064     if (m_currentBankDocument == nullptr) {
0065         return false;
0066     }
0067 
0068     // Tell the host application to load my GUI component
0069     setComponentName(QStringLiteral("skrooge_importexport"), title());
0070     setXMLFile(QStringLiteral("skrooge_importexport.rc"));
0071 
0072     // Imports
0073     auto imports = new  KActionMenu(SKGServices::fromTheme(QStringLiteral("document-import")), i18nc("Verb, action to import items from another format", "Import"), this);
0074     registerGlobalAction(QStringLiteral("import"), imports);
0075 
0076     // Import
0077     QStringList overlay;
0078     overlay.push_back(QStringLiteral("skrooge"));
0079 
0080     auto actImport = new QAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlay), i18nc("Verb, action to import items from another format", "Import…"), this);
0081     actionCollection()->setDefaultShortcut(actImport, Qt::CTRL + Qt::META + Qt::Key_I);
0082     connect(actImport, &QAction::triggered, this, [ = ]() {
0083         this->importFiles();
0084     });
0085     imports->addAction(actImport);
0086     registerGlobalAction(QStringLiteral("import_operation"), actImport);
0087 
0088     // Import backends
0089     QStringList overlay2;
0090     overlay.push_back(QStringLiteral("download"));
0091 
0092     auto actImportBackend = new QAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlay2), i18nc("Verb, action to import items from another format", "Import with backends"), this);
0093     actionCollection()->setDefaultShortcut(actImportBackend, Qt::CTRL + Qt::META + Qt::Key_W);
0094     connect(actImportBackend, &QAction::triggered, this, &SKGImportExportPlugin::importbackends);
0095     imports->addAction(actImportBackend);
0096     registerGlobalAction(QStringLiteral("import_backends"), actImportBackend);
0097 
0098     // Import CSV Unit
0099     QStringList overlaycsv;
0100     overlaycsv.push_back(QStringLiteral("text-csv"));
0101     auto actImportCsvUnit = new QAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlaycsv), i18nc("Verb, action to import", "Import currency values…"), this);
0102     connect(actImportCsvUnit, &QAction::triggered, this, [ = ]() {
0103         this->importFiles(QList<QUrl>(), 2);
0104     });
0105     imports->addAction(actImportCsvUnit);
0106     registerGlobalAction(QStringLiteral("import_csv_unit"), actImportCsvUnit);
0107 
0108     // Import CSV Rule
0109     auto actImportCsvRule = new QAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlaycsv), i18nc("Verb, action to import", "Import rules…"), this);
0110     connect(actImportCsvRule, &QAction::triggered, this, [ = ]() {
0111         this->importFiles(QList<QUrl>(), 3);
0112     });
0113     imports->addAction(actImportCsvRule);
0114     registerGlobalAction(QStringLiteral("import_csv_rule"), actImportCsvRule);
0115 
0116     // Exports
0117     auto exports = new  KActionMenu(SKGServices::fromTheme(QStringLiteral("document-export")), i18nc("Verb, action to export items in another format", "Export"), this);
0118     registerGlobalAction(QStringLiteral("export"), exports);
0119 
0120     // Export
0121     auto actExportFile = new QAction(SKGServices::fromTheme(QStringLiteral("document-export")), i18nc("Verb, action to export items to another format", "Export…"), this);
0122     connect(actExportFile, &QAction::triggered, this, &SKGImportExportPlugin::exportFile);
0123     exports->addAction(actExportFile);
0124     actionCollection()->setDefaultShortcut(actExportFile, Qt::CTRL + Qt::META + Qt::Key_E);
0125     registerGlobalAction(QStringLiteral("export_operation"), actExportFile);
0126 
0127     // Processing
0128     auto processing = new  KActionMenu(SKGServices::fromTheme(QStringLiteral("tools-wizard")), i18nc("Noun, apply some kind of transformation on an item", "Processing"), this);
0129     registerGlobalAction(QStringLiteral("processing"), processing);
0130 
0131     // Processing found and group
0132     QStringList overlaytransfers;
0133     overlaytransfers.push_back(QStringLiteral("exchange-positions"));
0134 
0135     auto actProcessingFoundTransfer = new QAction(SKGServices::fromTheme(QStringLiteral("tools-wizard"), overlaytransfers), i18nc("Verb, action to find and group transfers", "Find and group transfers"), this);
0136     connect(actProcessingFoundTransfer, &QAction::triggered, this, &SKGImportExportPlugin::findTransfers);
0137     processing->addAction(actProcessingFoundTransfer);
0138     actionCollection()->setDefaultShortcut(actProcessingFoundTransfer, Qt::CTRL + Qt::META + Qt::Key_G);
0139     registerGlobalAction(QStringLiteral("process_foundtransfer"), actProcessingFoundTransfer);
0140 
0141     auto actProcessingAnonymize = new QAction(SKGServices::fromTheme(QStringLiteral("tools-wizard"), overlaytransfers), i18nc("Verb, action to anonymize a document", "Anonymize"), this);
0142     connect(actProcessingAnonymize, &QAction::triggered, this, &SKGImportExportPlugin::anonymize);
0143     processing->addAction(actProcessingAnonymize);
0144     registerGlobalAction(QStringLiteral("process_anonymize"), actProcessingAnonymize);
0145 
0146     // Processing banks
0147     auto actProcessingBank = new QAction(SKGServices::fromTheme(QStringLiteral("tools-wizard")), i18nc("Verb, action to clean an import", "Clean bank's imports"), this);
0148     connect(actProcessingBank, &QAction::triggered, this, &SKGImportExportPlugin::cleanBanks);
0149     processing->addAction(actProcessingBank);
0150     registerGlobalAction(QStringLiteral("process_banks"), actProcessingBank);
0151 
0152     // Processing banks
0153     QStringList overlayValidate;
0154     overlayValidate.push_back(QStringLiteral("dialog-ok"));
0155     auto actSwithValidationImportedOperations = new QAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlayValidate), i18nc("Verb, action to validate imported transactions", "Switch validation of imported transactions"), this);
0156     connect(actSwithValidationImportedOperations, &QAction::triggered, this, &SKGImportExportPlugin::swithvalidationImportedOperations);
0157     actionCollection()->setDefaultShortcut(actSwithValidationImportedOperations, Qt::CTRL + Qt::SHIFT + Qt::Key_V);
0158     registerGlobalAction(QStringLiteral("switch_validation_imported_operation"), actSwithValidationImportedOperations, QStringList() << QStringLiteral("operation"), 1, -1, 318);
0159 
0160 
0161     auto act = new QAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlayValidate), i18nc("Verb, action to merge", "Validate transactions that do not require further action"), this);
0162     connect(act, &QAction::triggered, this, &SKGImportExportPlugin::validateAllOperations);
0163     registerGlobalAction(QStringLiteral("process_validate"), act, QStringList(), -2, -1);
0164 
0165     QStringList overlayopen;
0166     overlayopen.push_back(QStringLiteral("quickopen"));
0167     auto actOpenNotValidated = new QAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlayopen), i18nc("Verb, action to open", "Open imported transactions not yet validated…"), this);
0168     actOpenNotValidated->setData(QString("skg://skrooge_operation_plugin/?title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions imported and not yet validated")) %
0169                                          "&title_icon=" % SKGServices::encodeForUrl(icon()) %
0170                                          "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("t_imported='P'"))));
0171     connect(actOpenNotValidated, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() {
0172         SKGMainPanel::getMainPanel()->SKGMainPanel::openPage();
0173     });
0174     actionCollection()->setDefaultShortcut(actOpenNotValidated, Qt::META + Qt::Key_V);
0175     registerGlobalAction(QStringLiteral("view_open_not_validated"), actOpenNotValidated);
0176 
0177     auto actMergeImportedOperation = new QAction(SKGServices::fromTheme(QStringLiteral("merge")), i18nc("Verb, action to merge", "Merge imported transactions"), this);
0178     connect(actMergeImportedOperation, &QAction::triggered, this, &SKGImportExportPlugin::mergeImportedOperation);
0179     actionCollection()->setDefaultShortcut(actMergeImportedOperation, Qt::CTRL + Qt::ALT + Qt::Key_M);
0180     registerGlobalAction(QStringLiteral("merge_imported_operation"), actMergeImportedOperation, QStringList() << QStringLiteral("operation"), 1, -1, 319);
0181 
0182     auto force = new QAction(i18nc("Noun", "Force the merge"), this);
0183     force->setIcon(SKGServices::fromTheme(QStringLiteral("merge")));
0184     force->setData(1);
0185     connect(force, &QAction::triggered, this, &SKGImportExportPlugin::mergeImportedOperation);
0186     registerGlobalAction(QStringLiteral("merge_imported_operation_force"), force);
0187 
0188     // Get last argument
0189     connect(this, &SKGImportExportPlugin::importFileName, this, [ = ](const QString & iFileName) {
0190         this->importFile(iFileName, true);
0191     }, Qt::QueuedConnection);
0192 
0193     // Krunner transactions
0194     QString dirName = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) ;
0195     QStringList fileList = QDir(dirName).entryList(QStringList() << QStringLiteral("add_operation_*.txt"), QDir::Files);
0196     if (!fileList.isEmpty()) {
0197         m_currentBankDocument->sendMessage(i18nc("Information message", "You have some krunner's transactions to import"));
0198     }
0199 
0200     return true;
0201 }
0202 
0203 QStringList SKGImportExportPlugin::processArguments(const QStringList& iArgument)
0204 {
0205     SKGTRACEINFUNC(10)
0206     QStringList output = iArgument;
0207     int nbArg = output.count();
0208     if (nbArg != 0) {
0209         QString filename = output.at(nbArg - 1);
0210         QString extension = QFileInfo(filename).suffix().toUpper();
0211         QString extensionDocument = m_currentBankDocument->getFileExtension().toUpper();
0212         if (QFile(filename).exists() && extension != extensionDocument) {
0213 #ifdef SKG_KF_5102
0214             int rc = KMessageBox::questionTwoActions(SKGMainPanel::getMainPanel(), i18nc("Question",  "Do you really want to import %1 into the current document ?", filename),
0215                      i18nc("Question",  "Import confirmation"),
0216                      KStandardGuiItem::apply(), KStandardGuiItem::cancel(),
0217                      QStringLiteral("importconfirmation"));
0218             if (rc == KMessageBox::PrimaryAction) {
0219 #else
0220             int rc = KMessageBox::questionYesNo(SKGMainPanel::getMainPanel(), i18nc("Question",  "Do you really want to import %1 into the current document ?", filename),
0221                                                 i18nc("Question",  "Import confirmation"),
0222                                                 KStandardGuiItem::yes(), KStandardGuiItem::no(),
0223                                                 QStringLiteral("importconfirmation"));
0224             if (rc == KMessageBox::Yes) {
0225 #endif
0226                 Q_EMIT importFileName(filename);
0227                 output.pop_back();
0228             }
0229         }
0230     }
0231     return output;
0232 }
0233 
0234 void SKGImportExportPlugin::initPreferences()
0235 {
0236     // Read Setting
0237     if (m_currentBankDocument != nullptr) {
0238         KSharedConfigPtr config = KSharedConfig::openConfig();
0239         KConfigGroup pref = config->group("skrooge_importexport");
0240         auto backends = m_currentBankDocument->getParameter(QStringLiteral("SKG_BACKENDS"));
0241         pref.writeEntry("backends", backends);
0242 
0243         skgimportexport_settings::self()->read();
0244     }
0245 }
0246 
0247 QWidget* SKGImportExportPlugin::getPreferenceWidget()
0248 {
0249     SKGTRACEINFUNC(10)
0250     // Create widget
0251     auto w = new QWidget();
0252     ui.setupUi(w);
0253     connect(ui.kcfg_automatic_search_header, &QCheckBox::toggled, ui.kHeaderPositionFrm, &QFrame::setHidden);
0254     connect(ui.kcfg_automatic_search_columns, &QCheckBox::toggled, ui.kColumnsPositionsFrm, &QFrame::setHidden);
0255     connect(ui.kcfg_automatic_search_columns, &QCheckBox::clicked, ui.kCsvMappingFrm, &QFrame::hide);
0256     connect(ui.kcfg_automatic_search_columns, &QCheckBox::toggled, ui.More, &QPushButton::setEnabled);
0257     connect(ui.More, &QPushButton::toggled, ui.kCsvMappingFrm, &QFrame::setVisible);
0258     connect(ui.kcfg_automatic_search_columns, &QCheckBox::clicked, ui.More, &QPushButton::setChecked);
0259     connect(ui.kcfg_download_on_open, &QCheckBox::toggled, ui.kcfg_download_frequency, &KComboBox::setEnabled);
0260 
0261     ui.kHeaderPositionFrm->hide();
0262     ui.kColumnsPositionsFrm->hide();
0263     ui.kCsvMappingFrm->hide();
0264 
0265     // Build list of known backends
0266     QString doc;
0267     const auto services = KServiceTypeTrader::self()->query(QStringLiteral("Skrooge/Import/Backend"));
0268     for (const auto& service : services) {
0269 #ifdef SKG_KF_5102
0270         auto os_supported = SKGServices::splitCSVLine(service->property(QStringLiteral("X-SKROOGE-ossupported"), QMetaType::QString).toString().toLower(), QLatin1Char(','));
0271         if (os_supported.isEmpty() || os_supported.contains(QSysInfo::kernelType().toLower())) {
0272             doc += "<br/><b>" + service->property(QStringLiteral("X-Krunner-ID"), QMetaType::QString).toString().toLower() + "</b><br/>";
0273             doc += service->property(QStringLiteral("Name"), QMetaType::QString).toString() + "<br/>";
0274             doc += service->property(QStringLiteral("Comment"), QMetaType::QString).toString() + "<br/>";
0275         }
0276 #else
0277         auto os_supported = SKGServices::splitCSVLine(service->property(QStringLiteral("X-SKROOGE-ossupported"), QVariant::String).toString().toLower(), QLatin1Char(','));
0278         if (os_supported.isEmpty() || os_supported.contains(QSysInfo::kernelType().toLower())) {
0279             doc += "<br/><b>" + service->property(QStringLiteral("X-Krunner-ID"), QVariant::String).toString().toLower() + "</b><br/>";
0280             doc += service->property(QStringLiteral("Name"), QVariant::String).toString() + "<br/>";
0281             doc += service->property(QStringLiteral("Comment"), QVariant::String).toString() + "<br/>";
0282         }
0283 #endif
0284     }
0285     auto text = i18nc("Information", "You must enter the list of backends to use separated by a ';'.\nA backend can have parameters. You can pass the parameters in parenthesis separated by comma.\n\nExample: backendA;backendB(parameter1,parameter2).\n\nHere is the list of known backends: %1.", doc);
0286     text = text.replace(QStringLiteral("\n"), QStringLiteral("<br/>"));
0287     ui.kbackendText->setText(text);
0288 
0289     // Add date formats
0290     QStringList dateFormats;
0291     dateFormats << i18nc("Format date", "Automatic detection")
0292                 << QStringLiteral("YYYYMMDD")
0293                 << QStringLiteral("MMDDYYYY")
0294                 << QStringLiteral("DDMMYYYY")
0295                 << QStringLiteral("MM-DD-YY")
0296                 << QStringLiteral("DD-MM-YY")
0297                 << QStringLiteral("MM-DD-YYYY")
0298                 << QStringLiteral("DD-MM-YYYY")
0299                 << QStringLiteral("YYYY-MM-DD")
0300                 << QStringLiteral("DDMMMYYYY")
0301                 << QStringLiteral("DD-MMM-YY")
0302                 << QStringLiteral("DD-MMM-YYYY");
0303     ui.kcfg_qif_date_format->addItems(dateFormats);
0304     ui.kcfg_csv_date_format->addItems(dateFormats);
0305 
0306     return w;
0307 }
0308 
0309 KConfigSkeleton* SKGImportExportPlugin::getPreferenceSkeleton()
0310 {
0311     return skgimportexport_settings::self();
0312 }
0313 
0314 SKGError SKGImportExportPlugin::savePreferences() const
0315 {
0316     SKGError err;
0317     if (m_currentBankDocument != nullptr) {
0318         // Read Setting
0319         auto backends = skgimportexport_settings::backends();
0320 
0321         // Save setting in document
0322         if (backends != m_currentBankDocument->getParameter(QStringLiteral("SKG_BACKENDS"))) {
0323             err = m_currentBankDocument->setParameter(QStringLiteral("SKG_BACKENDS"), backends);
0324         }
0325     }
0326     return err;
0327 }
0328 
0329 QString SKGImportExportPlugin::title() const
0330 {
0331     return i18nc("Noun", "Import / Export");
0332 }
0333 
0334 QString SKGImportExportPlugin::icon() const
0335 {
0336     return QStringLiteral("utilities-file-archiver");
0337 }
0338 
0339 QString SKGImportExportPlugin::toolTip() const
0340 {
0341     return i18nc("Noun", "Import / Export management");
0342 }
0343 
0344 QStringList SKGImportExportPlugin::tips() const
0345 {
0346     QStringList output;
0347     output.push_back(i18nc("Description of a tips", "<p>… skrooge is able to detect <a href=\"skg://tab_configure?page=Skrooge import and export plugin\">automatically</a> transfers after an import.</p>"));
0348     output.push_back(i18nc("Description of a tips", "<p>… you can automatically import transaction with <a href=\"skg://tab_configure?page=Skrooge import and export plugin\">backend</a>.</p>"));
0349     output.push_back(i18nc("Description of a tips", "<p>… you can <a href=\"skg://import_operation\">import</a> many files in one shot.</p>"));
0350     output.push_back(i18nc("Description of a tips", "<p>… unit amounts can be imported through a CSV file.</p>"));
0351     output.push_back(i18nc("Description of a tips", "<p>… you can customize your CSV import with regular expressions defined in <a href=\"skg://tab_configure?page=Skrooge import and export plugin\">setting</a> panel.</p>"));
0352     output.push_back(i18nc("Description of a tips", "<p>… you can export the full content of your document into a XML file.</p>"));
0353     output.push_back(i18nc("Description of a tips", "<p>… you can export some accounts or transactions just be selecting them before to launch the <a href=\"skg://export\">export_operation</a>.</p>"));
0354     output.push_back(i18nc("Description of a tips", "<p>… you can apply <a href=\"skg://skrooge_search_plugin\">automatic rules</a> after an import to set the right categories.</p>"));
0355     output.push_back(i18nc("Description of a tips", "<p>… you can convert file by using the batch tool '%1'.</p>", "skroogeconvert"));
0356     output.push_back(i18nc("Description of a tips", "<p>… skrooge uses the name of the imported file to find the target account.</p>"));
0357     return output;
0358 }
0359 
0360 QStringList SKGImportExportPlugin::subPlugins() const
0361 {
0362     return QStringList() << QStringLiteral("skrooge/import") << QStringLiteral("Skrooge/Import/Backend");
0363 }
0364 
0365 int SKGImportExportPlugin::getOrder() const
0366 {
0367     return 70;
0368 }
0369 
0370 void SKGImportExportPlugin::refresh()
0371 {
0372     SKGTRACEINFUNC(10)
0373 
0374     if ((m_currentBankDocument != nullptr) && (SKGMainPanel::getMainPanel() != nullptr)) {
0375         bool test = (m_currentBankDocument->getMainDatabase() != nullptr);
0376 
0377         // Automatic download
0378         if (test) {
0379             QString doc_id = m_currentBankDocument->getUniqueIdentifier();
0380             if (m_docUniqueIdentifier != doc_id) {
0381                 m_docUniqueIdentifier = doc_id;
0382 
0383                 SKGError err;
0384 
0385                 if (skgimportexport_settings::download_on_open()) {
0386                     // Check frequency
0387                     QString lastAutomaticDownload = m_currentBankDocument->getParameter(QStringLiteral("SKG_LAST_BACKEND_AUTOMATIC_DOWNLOAD"));
0388                     if (!lastAutomaticDownload.isEmpty()) {
0389                         // The automatic import is not done if at least one manual import has not been done
0390                         QDate lastAutomaticDownloadDate = QDate::fromString(lastAutomaticDownload, QStringLiteral("yyyy-MM-dd"));
0391                         if ((lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 1 && skgimportexport_settings::download_frequency() == 0) ||
0392                             (lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 7 && skgimportexport_settings::download_frequency() == 1) ||
0393                             (lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 30 && skgimportexport_settings::download_frequency() == 2))
0394 
0395                         {
0396                             // Import
0397                             importbackends();
0398                         }
0399                     }
0400                 }
0401             }
0402         }
0403     }
0404 }
0405 
0406 SKGError SKGImportExportPlugin::importbackends()
0407 {
0408     SKGError err;
0409     SKGTRACEINFUNCRC(10, err)
0410     if (m_currentBankDocument != nullptr) {
0411         // Check if already in a transaction
0412         if (!m_currentBankDocument->checkExistingTransaction()) {
0413             // Repeat later
0414             QTimer::singleShot(300, Qt::CoarseTimer, this, &SKGImportExportPlugin::importbackends);
0415             return err;
0416         }
0417 
0418         // Get backends list to used
0419         QStringList backends = SKGServices::splitCSVLine(skgimportexport_settings::backends());
0420         int nbBackends = backends.count();
0421 
0422         // Import
0423         SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Import with backends"), err, nbBackends)
0424         for (int i = 0; !err && i < nbBackends; ++i) {
0425             QString backend = backends.at(i).trimmed();
0426             QString parameter;
0427 
0428             if (backend.contains(QStringLiteral("("))) {
0429                 QRegularExpression reg(QStringLiteral("^(.+)\\((.+)\\)$"));
0430                 auto match = reg.match(backend);
0431                 if (match.hasMatch()) {
0432                     backend = match.captured(1);
0433                     parameter = match.captured(2);
0434                 } else {
0435                     err = SKGError(ERR_FAIL, i18nc("Error message", "Syntax error in backend \"%1\"", backend));
0436                     break;
0437                 }
0438             }
0439 
0440             // Is password needed?
0441             QString pwd;
0442             QHash< QString, QString > properties;
0443             auto fileToLoad = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kservices5/org.kde.skrooge-import-backend-" % backend % ".desktop");
0444             if (fileToLoad.isEmpty()) {
0445                 fileToLoad = "org.kde.skrooge-import-backend-" % backend % ".desktop";
0446             }
0447             IFOKDO(err, SKGServices::readPropertyFile(fileToLoad, properties));
0448             if (!err && (properties[QStringLiteral("x-skrooge-getaccounts")].contains(QStringLiteral("%3")) || properties[QStringLiteral("x-skrooge-getoperations")].contains(QStringLiteral("%3")))) {
0449                 QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
0450                 QPointer<KPasswordDialog> dlg = new KPasswordDialog(SKGMainPanel::getMainPanel());
0451                 dlg->setPrompt(i18nc("Question", "The backend '%1' needs a password.\nPlease enter the password.", backend));
0452                 int rc = dlg->exec();
0453                 pwd = dlg->password();
0454                 delete dlg;
0455                 QApplication::restoreOverrideCursor();
0456 
0457                 if (rc != QDialog::Accepted) {
0458                     continue;
0459                 }
0460             }
0461 
0462             QString codec = m_currentBankDocument->getParameter(QStringLiteral("SKG_LAST_CODEC_USED_FOR_IMPORT"));
0463             if (codec.isEmpty()) {
0464                 codec = QTextCodec::codecForLocale()->name();
0465             }
0466             IFOKDO(err, m_currentBankDocument->setParameter(QStringLiteral("SKG_LAST_CODEC_USED_FOR_IMPORT"), codec))
0467 
0468             SKGImportExportManager imp1(m_currentBankDocument, QUrl("." % backend));
0469             QMap<QString, QString> parameters = imp1.getImportParameters();
0470             parameters[QStringLiteral("password")] = pwd;
0471             auto params = SKGServices::splitCSVLine(parameter, QLatin1Char(','));
0472             int nbp = params.count();
0473             for (int j = 0; j < nbp; ++j) {
0474                 parameters[QStringLiteral("parameter") % SKGServices::intToString(j + 1)] = params.at(j).trimmed();
0475             }
0476             imp1.setImportParameters(parameters);
0477             imp1.setAutomaticValidation(skgimportexport_settings::automatic_validation());
0478             imp1.setAutomaticApplyRules(skgimportexport_settings::apply_rules());
0479             // This option is not used with backend import
0480             imp1.setSinceLastImportDate(false);
0481             imp1.setCodec(codec);
0482             IFOKDO(err, imp1.importFile())
0483             IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
0484         }
0485 
0486         // Memorize the last download date
0487         IFOKDO(err, m_currentBankDocument->setParameter(QStringLiteral("SKG_LAST_BACKEND_AUTOMATIC_DOWNLOAD"), QDate::currentDate().toString(QStringLiteral("yyyy-MM-dd"))))
0488     }
0489 
0490     // Display error
0491     SKGMainPanel::displayErrorMessage(err);
0492 
0493     // Open last modified transactions if setting activated
0494     IFOK(err) openLastModifiedIfSetting();
0495 
0496     return err;
0497 }
0498 
0499 void SKGImportExportPlugin::importFile(const QString& iFile, bool iBlockOpenLastModified)
0500 {
0501     importFiles(QList<QUrl>() << QUrl::fromLocalFile(iFile), static_cast<int>(iBlockOpenLastModified));
0502 }
0503 
0504 void SKGImportExportPlugin::importFiles(const QList<QUrl>& iFiles, int mode, bool iBlockOpenLastModified)
0505 {
0506     SKGError err;
0507     SKGTRACEINFUNCRC(10, err)
0508     if (m_currentBankDocument != nullptr) {
0509         QList<QUrl> fileNames;
0510         QString lastCodecUsed = m_currentBankDocument->getParameter(QStringLiteral("SKG_LAST_CODEC_USED_FOR_IMPORT"));
0511         if (lastCodecUsed.isEmpty()) {
0512             lastCodecUsed = QTextCodec::codecForLocale()->name();
0513         }
0514         QString codec;
0515 
0516         if (iFiles.isEmpty()) {
0517             // Panel to ask files
0518             KEncodingFileDialog::Result result = KEncodingFileDialog::getOpenUrlsAndEncoding(lastCodecUsed, QUrl::fromLocalFile(QUrl(QStringLiteral("kfiledialog:///IMPEXP")).toLocalFile()),
0519                                                  mode == 2 || mode == 3 ? QString("*.csv|" % i18nc("A file format", "CSV Files")) :
0520                                                  SKGImportExportManager().getImportMimeTypeFilter(),
0521                                                  SKGMainPanel::getMainPanel());
0522             const auto urls = result.URLs;
0523             fileNames.reserve(urls.count());
0524             for (const auto& u : qAsConst(urls)) {
0525                 fileNames.append(QUrl(u.url()));
0526             }
0527             codec = result.encoding;
0528         } else {
0529             fileNames = iFiles;
0530             codec = lastCodecUsed;
0531         }
0532 
0533         int nbFiles = fileNames.count();
0534         if (nbFiles != 0) {
0535             {
0536                 SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Import with codec %1", codec), err, nbFiles)
0537 
0538                 // Read Setting
0539                 bool automatic_validation = skgimportexport_settings::automatic_validation();
0540                 bool automatic_rule = skgimportexport_settings::apply_rules();
0541                 bool since_last = skgimportexport_settings::since_last_import();
0542 
0543                 IFOKDO(err, m_currentBankDocument->setParameter(QStringLiteral("SKG_LAST_CODEC_USED_FOR_IMPORT"), codec))
0544 
0545                 for (int i = 0; !err && i < nbFiles; ++i) {
0546                     // Get Filename
0547                     const QUrl& fileName = fileNames.at(i);
0548 
0549                     // Import
0550                     SKGImportExportManager imp1(m_currentBankDocument, fileName);
0551                     imp1.setAutomaticValidation(automatic_validation);
0552                     imp1.setAutomaticApplyRules(automatic_rule);
0553                     imp1.setSinceLastImportDate(since_last);
0554                     imp1.setCodec(codec);
0555 
0556                     if (QFileInfo(fileName.path()).suffix().toUpper() == QStringLiteral("CSV")) {
0557                         QMap<QString, QString> parameters = imp1.getImportParameters();
0558 
0559                         parameters[QStringLiteral("automatic_search_header")] = (skgimportexport_settings::automatic_search_header() ? QStringLiteral("Y") : QStringLiteral("N"));
0560                         parameters[QStringLiteral("header_position")] = SKGServices::intToString(skgimportexport_settings::header_position());
0561 
0562                         parameters[QStringLiteral("mapping_date")] = skgimportexport_settings::mapping_date();
0563                         parameters[QStringLiteral("mapping_account")] = skgimportexport_settings::mapping_account();
0564                         parameters[QStringLiteral("mapping_number")] = skgimportexport_settings::mapping_number();
0565                         parameters[QStringLiteral("mapping_mode")] = skgimportexport_settings::mapping_mode();
0566                         parameters[QStringLiteral("mapping_payee")] = skgimportexport_settings::mapping_payee();
0567                         parameters[QStringLiteral("mapping_comment")] = skgimportexport_settings::mapping_comment();
0568                         parameters[QStringLiteral("mapping_status")] = skgimportexport_settings::mapping_status();
0569                         parameters[QStringLiteral("mapping_bookmarked")] = skgimportexport_settings::mapping_bookmarked();
0570                         parameters[QStringLiteral("mapping_category")] = skgimportexport_settings::mapping_category();
0571                         parameters[QStringLiteral("mapping_amount")] = skgimportexport_settings::mapping_amount();
0572                         parameters[QStringLiteral("mapping_quantity")] = skgimportexport_settings::mapping_quantity();
0573                         parameters[QStringLiteral("mapping_unit")] = skgimportexport_settings::mapping_unit();
0574                         parameters[QStringLiteral("mapping_idtransaction")] = skgimportexport_settings::mapping_idtransaction();
0575                         parameters[QStringLiteral("mapping_idgroup")] = skgimportexport_settings::mapping_idgroup();
0576                         parameters[QStringLiteral("mapping_sign")] = skgimportexport_settings::mapping_sign();
0577                         parameters[QStringLiteral("mapping_debit")] = skgimportexport_settings::mapping_debit();
0578                         parameters[QStringLiteral("mapping_property")] = skgimportexport_settings::mapping_property();
0579 
0580                         parameters[QStringLiteral("automatic_search_columns")] = (skgimportexport_settings::automatic_search_columns() ? QStringLiteral("Y") : QStringLiteral("N"));
0581                         parameters[QStringLiteral("columns_positions")] = skgimportexport_settings::columns_positions();
0582                         parameters[QStringLiteral("mode_csv_unit")] = (mode == 2 ? QStringLiteral("Y") : QStringLiteral("N"));
0583                         parameters[QStringLiteral("mode_csv_rule")] = (mode == 3 ? QStringLiteral("Y") : QStringLiteral("N"));
0584                         parameters[QStringLiteral("date_format")] = skgimportexport_settings::csv_date_format();
0585                         if (!parameters[QStringLiteral("date_format")].contains(QStringLiteral("YY"))) {
0586                             parameters[QStringLiteral("date_format")] = QString();
0587                         }
0588 
0589                         imp1.setImportParameters(parameters);
0590                     } else if (QFileInfo(fileName.path()).suffix().toUpper() == QStringLiteral("QIF")) {
0591                         QMap<QString, QString> parameters = imp1.getImportParameters();
0592 
0593                         parameters[QStringLiteral("date_format")] = skgimportexport_settings::qif_date_format();
0594                         if (!parameters[QStringLiteral("date_format")].contains(QStringLiteral("YY"))) {
0595                             parameters[QStringLiteral("date_format")] = QString();
0596                         }
0597 
0598                         imp1.setImportParameters(parameters);
0599                     } else if (QFileInfo(fileName.path()).suffix().toUpper() == QStringLiteral("LEDGER")) {
0600                         QMap<QString, QString> parameters = imp1.getImportParameters();
0601                         parameters[QStringLiteral("ledger_account_identification")] = skgimportexport_settings::ledger_account_identification();
0602                         imp1.setImportParameters(parameters);
0603                     }
0604 
0605                     // Optimization
0606                     if (i != nbFiles - 1) {
0607                         QMap< QString, QString > parameters = imp1.getImportParameters();
0608                         parameters[QStringLiteral("donotfinalize")] = 'Y';
0609                         imp1.setImportParameters(parameters);
0610                     }
0611 
0612                     if (m_install) {
0613                         QMap< QString, QString > parameters = imp1.getImportParameters();
0614                         parameters[QStringLiteral("install_sunriise")] = 'Y';
0615                         imp1.setImportParameters(parameters);
0616                     }
0617 
0618                     IFOKDO(err, imp1.importFile())
0619                     if (err && err.getReturnCode() == ERR_ENCRYPTION) {
0620                         QString pwd;
0621                         QString additionalMessage;
0622                         do {
0623                             // Reset error
0624                             err = SKGError(ERR_FAIL, i18nc("Error message", "Import of file named '%1' failed", fileName.toDisplayString()));
0625                             pwd = QString();
0626 
0627                             // Use password dialog
0628                             QApplication::restoreOverrideCursor();
0629                             QPointer<KPasswordDialog> dlg = new KPasswordDialog(SKGMainPanel::getMainPanel());
0630                             dlg->setPrompt(additionalMessage % i18nc("Question", "This file seems to be protected.\nPlease enter the password."));
0631                             if (dlg->exec() == QDialog::Accepted) {
0632                                 pwd = dlg->password();
0633                             }
0634                             delete dlg;
0635                             QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0636 
0637                             // Load file
0638                             if (!pwd.isEmpty()) {
0639                                 QMap<QString, QString> parameters = imp1.getImportParameters();
0640                                 parameters[QStringLiteral("password")] = pwd;
0641                                 imp1.setImportParameters(parameters);
0642                                 err = imp1.importFile();
0643                                 IFKO(err) {
0644                                     if (err.getReturnCode() == ERR_ENCRYPTION) {
0645                                         additionalMessage = i18nc("The user did not provide the correct password", "<b>Wrong password.</b>\n");
0646                                     } else {
0647                                         // Import error
0648                                         break;
0649                                     }
0650                                 }
0651                             } else {
0652                                 err = SKGError(ERR_FAIL, i18nc("Error message", "Import canceled by user"));
0653                                 break;
0654                             }
0655                         } while (err);
0656                     }
0657 
0658                     if (err && err.getReturnCode() != ERR_INSTALL) {
0659                         err.addError(ERR_FAIL, i18nc("Error message", "Import of file named '%1' failed", fileName.toDisplayString()));
0660                     }
0661 
0662                     IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
0663                 }
0664             }
0665 
0666             // status bar
0667             IFOK(err) {
0668                 err = SKGError(0, i18np("%1 file successfully imported.", "%1 files successfully imported.", nbFiles));
0669             }
0670 
0671             // Display error
0672             m_install = false;
0673             KMessageWidget* msg = SKGMainPanel::displayErrorMessage(err);
0674             if (err.getReturnCode() == ERR_INSTALL && (msg != nullptr)) {
0675                 QString application = err.getProperty();
0676                 auto install = new QAction(i18nc("Noun", "Install %1", application), msg);
0677                 install->setIcon(SKGServices::fromTheme(QStringLiteral("download")));
0678                 msg->addAction(install);
0679                 connect(install, &QAction::triggered, this, &SKGImportExportPlugin::onInstall);
0680                 connect(install, &QAction::triggered, msg, &KMessageWidget::deleteLater, Qt::QueuedConnection);
0681             }
0682 
0683             // Open last modified transactions if setting activated
0684             if (!iBlockOpenLastModified) {
0685                 IFOK(err) openLastModifiedIfSetting();
0686             }
0687         }
0688     }
0689 }
0690 
0691 void SKGImportExportPlugin::onInstall()
0692 {
0693     m_install = true;
0694     SKGMainPanel::getMainPanel()->displayMessage(i18nc("Information message", "The installation will be done during the next import"));
0695 }
0696 
0697 void SKGImportExportPlugin::exportFile()
0698 {
0699     SKGError err;
0700     SKGTRACEINFUNCRC(10, err)
0701     if (m_currentBankDocument != nullptr) {
0702         QString lastCodecUsed = m_currentBankDocument->getParameter(QStringLiteral("SKG_LAST_CODEC_USED_FOR_IMPORT"));
0703         if (lastCodecUsed.isEmpty()) {
0704             lastCodecUsed = QTextCodec::codecForLocale()->name();
0705         }
0706         QString fileName = SKGMainPanel::getSaveFileName(QStringLiteral("kfiledialog:///IMPEXP"), SKGImportExportManager().getExportMimeTypeFilter(),
0707                            SKGMainPanel::getMainPanel(), &lastCodecUsed);
0708         if (fileName.isEmpty() || (m_currentBankDocument == nullptr)) {
0709             return;
0710         }
0711 
0712         QString uuids;
0713         const auto objects = SKGMainPanel::getMainPanel()->getSelectedObjects();
0714         for (const auto& obj : objects) {
0715             if (!uuids.isEmpty()) {
0716                 uuids.append(";");
0717             }
0718             uuids.append(obj.getUniqueID());
0719         }
0720 
0721         {
0722             SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Export"), err)
0723             IFOK(err) {
0724                 SKGImportExportManager imp1(m_currentBankDocument, QUrl::fromLocalFile(fileName));
0725                 imp1.setCodec(lastCodecUsed);
0726                 QMap<QString, QString> params;
0727                 params[QStringLiteral("uuid_of_selected_accounts_or_operations")] = uuids;
0728                 imp1.setExportParameters(params);
0729                 err = imp1.exportFile();
0730             }
0731         }
0732 
0733         // status bar
0734         IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "File '%1' successfully exported.", fileName)))
0735         else {
0736             err.addError(ERR_FAIL, i18nc("Error message", "Export of '%1' failed", fileName));
0737         }
0738 
0739         // Display error
0740         SKGMainPanel::displayErrorMessage(err);
0741     }
0742 }
0743 
0744 void SKGImportExportPlugin::anonymize()
0745 {
0746     SKGError err;
0747     SKGTRACEINFUNCRC(10, err)
0748     if (m_currentBankDocument != nullptr) {
0749         QString pwd;
0750         QPointer<KPasswordDialog> dlg = new KPasswordDialog(SKGMainPanel::getMainPanel());
0751         dlg->setPrompt(i18nc("Question", "The file can be made anonymous in two ways.<br/><b>Reversibly:</b> enter a key and memorize it, it will be used to go back.<br/><b>Irreversibly (recommended):</b> do not enter a key.<br/><br/>To reverse an anonymous file, simply try to anonymize it with the same key."));
0752         if (dlg->exec() == QDialog::Accepted) {
0753             pwd = dlg->password();
0754         }
0755 
0756         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0757         SKGImportExportManager imp1(m_currentBankDocument);
0758         err = imp1.anonymize(pwd);
0759         QApplication::restoreOverrideCursor();
0760 
0761         // status bar
0762         IFOKDO(err, SKGError(0, i18nc("An anonymized document is a document where all private data has been removed", "Document anonymized.")))
0763 
0764         // Display error
0765         SKGMainPanel::displayErrorMessage(err);
0766     }
0767 }
0768 
0769 void SKGImportExportPlugin::findTransfers()
0770 {
0771     SKGError err;
0772     SKGTRACEINFUNCRC(10, err)
0773     if (m_currentBankDocument != nullptr) {
0774         int NbOperationsMerged = 0;
0775         {
0776             SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Find and group transfers"), err)
0777             IFOK(err) {
0778                 SKGImportExportManager imp1(m_currentBankDocument);
0779                 err = imp1.findAndGroupTransfers(NbOperationsMerged);
0780             }
0781         }
0782 
0783         // status bar
0784         IFOK(err) {
0785             if (NbOperationsMerged != 0) {
0786                 err = SKGError(0, i18np("Document successfully processed. %1 transfer created.",
0787                                         "Document successfully processed. %1 transfers created.", NbOperationsMerged));
0788             } else {
0789                 err = m_currentBankDocument->sendMessage(i18nc("Information message", "No transfers found"));
0790             }
0791         } else {
0792             err.addError(ERR_FAIL, i18nc("Error message", "Processing failed."));
0793         }
0794 
0795         // Display error
0796         SKGMainPanel::displayErrorMessage(err);
0797 
0798         // Open last modified transactions if setting activated
0799         if (!err && (NbOperationsMerged != 0)) {
0800             openLastModifiedIfSetting();
0801         }
0802     }
0803 }
0804 
0805 void SKGImportExportPlugin::cleanBanks()
0806 {
0807     SKGError err;
0808     SKGTRACEINFUNCRC(10, err) {
0809         SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Clean bank's imports"), err)
0810         IFOK(err) {
0811             SKGImportExportManager imp1(m_currentBankDocument);
0812             err = imp1.cleanBankImport();
0813         }
0814     }
0815 
0816     // status bar
0817     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Document successfully cleaned.")))
0818     else {
0819         err.addError(ERR_FAIL, i18nc("Error message", "Clean failed."));
0820     }
0821 
0822     // Display error
0823     SKGMainPanel::displayErrorMessage(err);
0824 
0825     // Open last modified transactions if setting activated
0826     IFOK(err) openLastModifiedIfSetting();
0827 }
0828 
0829 void SKGImportExportPlugin::swithvalidationImportedOperations()
0830 {
0831     SKGError err;
0832     SKGTRACEINFUNCRC(10, err)
0833 
0834     if ((SKGMainPanel::getMainPanel() != nullptr) && (m_currentBankDocument != nullptr)) {
0835         SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
0836         int nb = selection.count();
0837         {
0838             SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Switch validation of imported transactions"), err, nb)
0839             for (int i = 0; !err && i < nb; ++i) {
0840                 SKGOperationObject op(selection.at(i));
0841                 if (op.getAttribute(QStringLiteral("t_imported")) == QStringLiteral("P")) {
0842                     err = op.setImported(true);
0843                     IFOKDO(err, op.save())
0844                 } else if (op.getAttribute(QStringLiteral("t_imported")) == QStringLiteral("Y")) {
0845                     err = op.setAttribute(QStringLiteral("t_imported"), QStringLiteral("P"));
0846                     IFOKDO(err, op.save())
0847                 }
0848                 IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
0849             }
0850         }
0851     }
0852 
0853     // status bar
0854     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Imported transactions validated.")))
0855     else {
0856         err.addError(ERR_FAIL, i18nc("Error message", "Validation failed"));
0857     }
0858 
0859     // Display error
0860     SKGMainPanel::displayErrorMessage(err);
0861 }
0862 
0863 void SKGImportExportPlugin::mergeImportedOperation()
0864 {
0865     SKGError err;
0866     SKGTRACEINFUNCRC(10, err)
0867 
0868     if ((SKGMainPanel::getMainPanel() != nullptr) && (m_currentBankDocument != nullptr)) {
0869         SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
0870         int nb = selection.count();
0871         err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Invalid selection, you must select one imported transaction and one manual transaction with same amounts"));
0872         if (nb == 2) {
0873             SKGOperationObject opImported(selection.at(0));
0874             SKGOperationObject opManual(selection.at(1));
0875             if (opImported.isImported() || opManual.isImported()) {
0876                 if (opImported.isImported() && opManual.isImported()) {
0877                     // Both are imports, so the "imported" on is the last one
0878                     if (opImported.getID() < opManual.getID()) {
0879                         qSwap(opImported, opManual);
0880                     }
0881                 } else if (!opImported.isImported()) {
0882                     qSwap(opImported, opManual);
0883                 }
0884 
0885                 // Mode force?
0886                 bool modeForce = false;
0887                 auto* act = qobject_cast< QAction* >(sender());
0888                 if (act != nullptr) {
0889                     modeForce = (act->data().toInt() == 1);
0890                 }
0891 
0892                 if (!modeForce && m_currentBankDocument->formatMoney(opImported.getCurrentAmount(), m_currentBankDocument->getPrimaryUnit()) != m_currentBankDocument->formatMoney(opManual.getCurrentAmount(), m_currentBankDocument->getPrimaryUnit())) {
0893                     SKGMainPanel::getMainPanel()->displayMessage(i18nc("Question",  "Amounts are not equals. Do you want to force the merge ?"), SKGDocument::Error, QStringLiteral("skg://merge_imported_operation_force"));
0894                     err = SKGError();
0895                 } else {
0896                     SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Merge imported transactions"), err)
0897                     err = opManual.mergeAttribute(opImported);
0898                     IFKO(err) err.addError(ERR_FAIL, i18nc("Error message", "Merge failed"));
0899                 }
0900             }
0901         }
0902     }
0903 
0904     // status bar
0905     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Imported transactions merged.")))
0906 
0907     // Display error
0908     SKGMainPanel::displayErrorMessage(err);
0909 }
0910 
0911 void SKGImportExportPlugin::openLastModifiedIfSetting()
0912 {
0913     // Read Setting
0914     bool open_after_import_or_processing = skgimportexport_settings::open_after_import_or_processing();
0915     if (open_after_import_or_processing) {
0916         // Open last transactions
0917         QAction* act = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("view_open_last_modified"));
0918         if (act != nullptr) {
0919             act->trigger();
0920         }
0921     }
0922 }
0923 
0924 void SKGImportExportPlugin::validateAllOperations()
0925 {
0926     SKGError err;
0927     SKGTRACEINFUNCRC(10, err) {
0928         SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Validate all transactions"), err)
0929         err = m_currentBankDocument->executeSqliteOrder(QStringLiteral("UPDATE operation SET t_imported='Y' WHERE t_imported='P'"));
0930     }
0931 
0932     // status bar
0933     IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Transactions validated.")))
0934     else {
0935         err.addError(ERR_FAIL, i18nc("Error message", "Validation failed"));
0936     }
0937 
0938     // Display error
0939     SKGMainPanel::displayErrorMessage(err);
0940 }
0941 
0942 SKGAdviceList SKGImportExportPlugin::advice(const QStringList& iIgnoredAdvice)
0943 {
0944     SKGTRACEINFUNC(10)
0945     SKGAdviceList output;
0946 
0947     // Check transactions not validated
0948     if (!iIgnoredAdvice.contains(QStringLiteral("skgimportexportplugin_notvalidated"))) {
0949         bool exist = false;
0950         m_currentBankDocument->existObjects(QStringLiteral("operation"), QStringLiteral("t_imported='P'"), exist);
0951         if (exist) {
0952             SKGAdvice ad;
0953             ad.setUUID(QStringLiteral("skgimportexportplugin_notvalidated"));
0954             ad.setPriority(4);
0955             ad.setShortMessage(i18nc("Advice on making the best (short)", "Many transactions imported and not yet validated"));
0956             ad.setLongMessage(i18nc("Advice on making the best (long)", "After importing transactions, you should review them, make corrections on, for instance, category, payee. Once done, you should mark the imported transaction as validated, so that you know the transaction has been fully processed."));
0957             SKGAdvice::SKGAdviceActionList autoCorrections;
0958             {
0959                 SKGAdvice::SKGAdviceAction a;
0960                 a.Title = QStringLiteral("skg://view_open_not_validated");
0961                 a.IsRecommended = false;
0962                 autoCorrections.push_back(a);
0963             }
0964             {
0965                 SKGAdvice::SKGAdviceAction a;
0966                 a.Title = QStringLiteral("skg://process_validate");
0967                 a.IsRecommended = false;
0968                 autoCorrections.push_back(a);
0969             }
0970             ad.setAutoCorrections(autoCorrections);
0971             output.push_back(ad);
0972         }
0973     }
0974 
0975     // Krunner transactions
0976     QString dirName = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
0977     QStringList fileList = QDir(dirName).entryList(QStringList() << QStringLiteral("add_operation_*.txt"), QDir::Files);
0978     int nb = fileList.count();
0979     if (nb != 0) {
0980         QStringList listAccounts;
0981         m_currentBankDocument->getDistinctValues(QStringLiteral("account"), QStringLiteral("t_name"), QStringLiteral("t_type IN ('C', 'D', 'W') and t_close='N'"), listAccounts);
0982         int nbAccounts = listAccounts.count();
0983         SKGAdvice::SKGAdviceActionList autoCorrections;
0984         autoCorrections.reserve(nbAccounts + 1);
0985         for (int i = 0; i < nb; ++i) {
0986             QString fileName = dirName % fileList.at(i);
0987             QFile file(fileName);
0988             if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
0989                 QTextStream stream(&file);
0990                 stream.readLine();  // action not used yet
0991                 QString date = QLocale().toString(SKGServices::stringToTime(stream.readLine().trimmed()).date(), QLocale::ShortFormat);
0992                 QString amount = m_currentBankDocument->formatMoney(SKGServices::stringToDouble(stream.readLine().trimmed()), m_currentBankDocument->getPrimaryUnit());
0993                 QString payee = stream.readLine().trimmed();
0994                 SKGAdvice ad;
0995                 ad.setUUID("skgimportexportplugin_krunner_" % fileName);
0996                 ad.setPriority(8);
0997                 ad.setShortMessage(i18nc("Advice on making the best (short)", "Krunner's transaction ongoing [%1 %2 %3]", date, amount, payee));
0998                 ad.setLongMessage(i18nc("Advice on making the best (long)", "Transactions created through krunner have to be fully created in skrooge."));
0999                 autoCorrections.resize(0);
1000                 for (int j = 0; j < nbAccounts; ++j) {
1001                     SKGAdvice::SKGAdviceAction a;
1002                     a.Title = i18nc("Advice on making the best (action)", "Import transaction in %1", listAccounts.at(j));
1003                     a.IconName = icon();
1004                     a.IsRecommended = false;
1005                     autoCorrections.push_back(a);
1006                 }
1007                 {
1008                     SKGAdvice::SKGAdviceAction a;
1009                     a.Title = i18nc("Advice on making the best (action)", "Remove transaction");
1010                     a.IconName = QStringLiteral("edit-delete");
1011                     a.IsRecommended = false;
1012                     autoCorrections.push_back(a);
1013                 }
1014                 ad.setAutoCorrections(autoCorrections);
1015                 output.push_back(ad);
1016 
1017                 // Close file
1018                 file.close();
1019             }
1020         }
1021     }
1022     return output;
1023 }
1024 
1025 SKGError SKGImportExportPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution)
1026 {
1027     if (iAdviceIdentifier.startsWith(QLatin1String("skgimportexportplugin_krunner_")) && (m_currentBankDocument != nullptr)) {
1028         SKGError err;
1029         // Get file name
1030         QString fileName = iAdviceIdentifier.right(iAdviceIdentifier.length() - 30);
1031         QFile file(fileName);
1032 
1033         // Get accounts
1034         QStringList listAccounts;
1035         m_currentBankDocument->getDistinctValues(QStringLiteral("account"), QStringLiteral("t_name"), QStringLiteral("t_type IN ('C', 'D', 'W') and t_close='N'"), listAccounts);
1036         if (iSolution < listAccounts.count()) {
1037             // Addition in an account
1038 
1039             if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
1040                 err = SKGError(ERR_FAIL, i18nc("An erro message", "Open file '%1' failed", fileName));
1041             } else {
1042                 QTextStream stream(&file);
1043                 stream.readLine();  // action is not used yet
1044                 QDate date = SKGServices::stringToTime(stream.readLine().trimmed()).date();
1045                 double amount = SKGServices::stringToDouble(stream.readLine().trimmed());
1046                 QString payee = stream.readLine().trimmed();
1047 
1048                 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Import krunner's transaction"), err)
1049 
1050                 // Get account
1051                 SKGAccountObject act(m_currentBankDocument);
1052                 err = act.setName(listAccounts.at(iSolution));
1053                 IFOKDO(err, act.load())
1054 
1055                 // Get unit
1056                 SKGUnitObject unit(m_currentBankDocument);
1057                 IFOKDO(err, unit.setName(m_currentBankDocument->getPrimaryUnit().Name))
1058                 IFOKDO(err, unit.load())
1059 
1060                 // Add transaction
1061                 SKGOperationObject op;
1062                 IFOKDO(err, act.addOperation(op))
1063                 IFOKDO(err, op.setDate(date))
1064                 IFOKDO(err, op.setUnit(unit))
1065 
1066                 if (!payee.isEmpty()) {
1067                     // Get payee
1068                     SKGPayeeObject pa;
1069                     IFOKDO(err, SKGPayeeObject::createPayee(m_currentBankDocument, payee, pa, true))
1070                     IFOKDO(err, op.setPayee(pa))
1071                 }
1072                 IFOK(err) {
1073                     int pos1 = fileName.indexOf(QStringLiteral("{"));
1074                     int pos2 = fileName.indexOf(QStringLiteral("}"));
1075                     if (pos1 != -1 && pos2 > pos1) {
1076                         err = op.setImportID("KR-" % fileName.mid(pos1 + 1, pos2 - pos1 - 1));
1077                     }
1078                 }
1079                 IFOKDO(err, op.save())
1080 
1081                 // Add suboperation
1082                 SKGSubOperationObject sop;
1083                 IFOKDO(err, op.addSubOperation(sop))
1084                 IFOKDO(err, sop.setQuantity(-amount))
1085                 IFOKDO(err, sop.save())
1086 
1087                 // Finalize the importation
1088                 IFOK(err) {
1089                     bool automatic_validation = skgimportexport_settings::automatic_validation();
1090                     bool automatic_rule = skgimportexport_settings::apply_rules();
1091                     bool since_last = skgimportexport_settings::since_last_import();
1092 
1093                     SKGImportExportManager imp1(m_currentBankDocument);
1094                     imp1.setAutomaticValidation(automatic_validation);
1095                     imp1.setAutomaticApplyRules(automatic_rule);
1096                     imp1.setSinceLastImportDate(since_last);
1097                     err = imp1.finalizeImportation();
1098                 }
1099 
1100                 // Send message
1101                 IFOKDO(err, op.getDocument()->sendMessage(i18nc("An information to the user", "The transaction '%1' has been added", op.getDisplayName()), SKGDocument::Hidden))
1102 
1103                 // Close file
1104                 file.close();
1105             }
1106 
1107             // status bar
1108             IFOK(err) {
1109                 err = SKGError(0, i18nc("Message for successful user action", "Transactions imported."));
1110                 QFile::remove(fileName);
1111             } else {
1112                 err.addError(ERR_FAIL, i18nc("Error message", "Import failed"));
1113             }
1114         } else {
1115             err = SKGError(0, i18nc("Message for successful user action", "Transactions removed."));
1116             QFile::remove(fileName);
1117         }
1118 
1119         // Display error
1120         SKGMainPanel::displayErrorMessage(err);
1121         return SKGError();
1122     }
1123     return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution);
1124 }
1125 
1126 #include <skgimportexportplugin.moc>