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>