1 /***************************************************************************
2  * SPDX-FileCopyrightText: 2021 S. MANKOWSKI stephane@mankowski.fr
3  * SPDX-FileCopyrightText: 2021 G. DE BURE support@mankowski.fr
4  * SPDX-License-Identifier: GPL-3.0-or-later
5  ***************************************************************************/
6 /** @file
7  * This file is Skrooge plugin for unit management.
8  *
9  * @author Stephane MANKOWSKI / Guillaume DE BURE
10  */
11 #include "skgunitplugin.h"
12 
13 #include <kaboutdata.h>
14 #include <kactioncollection.h>
15 #include <kpluginfactory.h>
16 #include <kservicetypetrader.h>
17 
18 #include <qaction.h>
19 #include <qinputdialog.h>
20 #include <qprocess.h>
21 
22 #include "skgdocumentbank.h"
23 #include "skghtmlboardwidget.h"
24 #include "skgmainpanel.h"
25 #include "skgtraces.h"
26 #include "skgtransactionmng.h"
27 #include "skgunit_settings.h"
28 #include "skgunitboardwidget.h"
29 #include "skgunitpluginwidget.h"
30 #include "skgunitvalueobject.h"
31 
32 /**
33  * This plugin factory.
34  */
K_PLUGIN_FACTORY(SKGUnitPluginFactory,registerPlugin<SKGUnitPlugin> ();)35 K_PLUGIN_FACTORY(SKGUnitPluginFactory, registerPlugin<SKGUnitPlugin>();)
36 
37 SKGUnitPlugin::SKGUnitPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/)
38     : SKGInterfacePlugin(iParent), m_currentBankDocument(nullptr)
39 {
40     Q_UNUSED(iWidget)
41     SKGTRACEINFUNC(10)
42 }
43 
~SKGUnitPlugin()44 SKGUnitPlugin::~SKGUnitPlugin()
45 {
46     SKGTRACEINFUNC(10)
47     m_currentBankDocument = nullptr;
48 }
49 
setupActions(SKGDocument * iDocument)50 bool SKGUnitPlugin::setupActions(SKGDocument* iDocument)
51 {
52     SKGTRACEINFUNC(10)
53 
54     m_currentBankDocument = qobject_cast<SKGDocumentBank*>(iDocument);
55     if (m_currentBankDocument == nullptr) {
56         return false;
57     }
58 
59     setComponentName(QStringLiteral("skrooge_unit"), title());
60     setXMLFile(QStringLiteral("skrooge_unit.rc"));
61 
62     // Menu
63     auto actSplitShare = new QAction(SKGServices::fromTheme(QStringLiteral("format-text-strikethrough")), i18nc("Verb", "Split share..."), this);
64     connect(actSplitShare, &QAction::triggered, this, &SKGUnitPlugin::onSplitShare);
65     actionCollection()->setDefaultShortcut(actSplitShare, Qt::ALT + Qt::Key_Slash);
66     registerGlobalAction(QStringLiteral("edit_split_stock"), actSplitShare, QStringList() << QStringLiteral("unit"), 1, 1, 310);  // TODO(Stephane MANKOWSKI): must be a share
67 
68     // -----------
69     auto act = new QAction(SKGServices::fromTheme(icon()), i18nc("Verb", "Delete unused units"), this);
70     connect(act, &QAction::triggered, this, &SKGUnitPlugin::deleteUnusedUnits);
71     registerGlobalAction(QStringLiteral("clean_delete_unused_units"), act);
72     return true;
73 }
74 
getNbDashboardWidgets()75 int SKGUnitPlugin::getNbDashboardWidgets()
76 {
77     return 2;
78 }
79 
getDashboardWidgetTitle(int iIndex)80 QString SKGUnitPlugin::getDashboardWidgetTitle(int iIndex)
81 {
82     if (iIndex == 0) {
83         return i18nc("Noun, the title of a section", "Quotes");
84     }
85     return i18nc("Noun, the title of a section", "Stock portfolio");
86 }
87 
getDashboardWidget(int iIndex)88 SKGBoardWidget* SKGUnitPlugin::getDashboardWidget(int iIndex)
89 {
90     if (iIndex == 0) {
91         return new SKGUnitBoardWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument);
92     }
93     // Get QML mode for dashboard
94     KConfigSkeleton* skl = SKGMainPanel::getMainPanel()->getPluginByName(QStringLiteral("Dashboard plugin"))->getPreferenceSkeleton();
95     KConfigSkeletonItem* sklItem = skl->findItem(QStringLiteral("qmlmode"));
96     bool qml = sklItem->property().toBool();
97 
98     auto listForFilter = QStringList() << QStringLiteral("t_name")
99                          << QStringLiteral("t_symbol")
100                          << QStringLiteral("t_country")
101                          << QStringLiteral("t_type")
102                          << QStringLiteral("t_internet_code")
103                          << QStringLiteral("t_source")
104                          << QStringLiteral("t_bookmarked");
105 
106     return new SKGHtmlBoardWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument,
107                                   getDashboardWidgetTitle(iIndex),
108                                   QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/html/default/portfolio.") % (qml ?  QStringLiteral("qml") :  QStringLiteral("html"))),
109                                   QStringList() << QStringLiteral("v_operation_display"),
110                                   SKGSimplePeriodEdit::NONE,
111                                   listForFilter);
112 }
113 
refresh()114 void SKGUnitPlugin::refresh()
115 {
116     SKGTRACEINFUNC(10)
117     if ((SKGMainPanel::getMainPanel() != nullptr) && (m_currentBankDocument != nullptr)) {
118         // Automatic download
119         QString doc_id = m_currentBankDocument->getUniqueIdentifier();
120         if (m_docUniqueIdentifier != doc_id) {
121             m_docUniqueIdentifier = doc_id;
122             // Check if current unit is existing
123             bool exist = false;
124             SKGError err = m_currentBankDocument->existObjects(QStringLiteral("unit"), QLatin1String(""), exist);
125             IFOK(err) {
126                 if (!exist) {
127                     SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Create default unit"), err)
128                     IFOK(err) {
129                         // Create default unit
130                         SKGUnitObject unit;
131                         QString unitS = QLocale().currencySymbol(QLocale::CurrencyIsoCode);
132                         if (!unitS.isEmpty()) {
133                             err = SKGUnitObject::createCurrencyUnit(m_currentBankDocument, unitS, unit);
134                         }
135 
136                         // The file is considered has not modified
137                         m_currentBankDocument->setFileNotModified();
138                     }
139                 } else if (skgunit_settings::download_on_open()) {
140                     // Check frequency
141                     QString lastAutomaticDownload = m_currentBankDocument->getParameter(QStringLiteral("SKG_LAST_UNIT_AUTOMATIC_DOWNLOAD"));
142                     if (lastAutomaticDownload.isEmpty()) {
143                         lastAutomaticDownload = QStringLiteral("1970-01-01");
144                     }
145                     QDate lastAutomaticDownloadDate = QDate::fromString(lastAutomaticDownload, QStringLiteral("yyyy-MM-dd"));
146                     if ((lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 1 && skgunit_settings::download_frequency() == 0) ||
147                         (lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 7 && skgunit_settings::download_frequency() == 1) ||
148                         (lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 30 && skgunit_settings::download_frequency() == 2))
149 
150                     {
151                         // Download all units
152                         SKGObjectBase::SKGListSKGObjectBase selection;
153                         err = m_currentBankDocument->getObjects(QStringLiteral("unit"), QLatin1String(""), selection);
154                         int nb = selection.count();
155                         SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Automatic download of units"), err, nb)
156                         for (int i = 0; !err && i < nb; ++i) {
157                             SKGUnitObject unit(selection.at(i));
158                             err = SKGUnitPluginWidget::downloadUnitValue(unit, SKGUnitPluginWidget::getDownloadModeFromSettings());
159 
160                             // Send message
161                             IFOKDO(err, m_currentBankDocument->sendMessage(i18nc("An information to the user", "The unit '%1' has been downloaded", unit.getDisplayName()), SKGDocument::Hidden))
162 
163                             IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
164                         }
165 
166                         // Memorize the last download date
167                         IFOKDO(err, m_currentBankDocument->setParameter(QStringLiteral("SKG_LAST_UNIT_AUTOMATIC_DOWNLOAD"), QDate::currentDate().toString(QStringLiteral("yyyy-MM-dd"))))
168                     }
169                 }
170             }
171 
172             // Display error
173             SKGMainPanel::displayErrorMessage(err);
174         }
175     }
176 }
177 
getWidget()178 SKGTabPage* SKGUnitPlugin::getWidget()
179 {
180     SKGTRACEINFUNC(10)
181     return new SKGUnitPluginWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument);
182 }
183 
getPreferenceWidget()184 QWidget* SKGUnitPlugin::getPreferenceWidget()
185 {
186     SKGTRACEINFUNC(10)
187     auto w = new QWidget();
188     ui.setupUi(w);
189 
190     // Get sources from.desktop file
191     QStringList sources;
192     const auto list2 = KServiceTypeTrader::self()->query(QStringLiteral("skrooge/source"));
193     for (const auto& service : list2) {
194         auto name = service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString();
195         auto keyAPI = service->property(QStringLiteral("X-SKROOGE-keyAPI"), QVariant::Bool).toBool();
196         if (!sources.contains(name) && keyAPI) {
197             sources.push_back(name);
198         }
199     }
200     sources.sort();
201 
202     auto nb = sources.count();
203     auto lwidgets = new QHash<QString, QLineEdit*>();
204     for (auto i = 0; i < nb; ++i) {
205         // Get Current value
206         auto ln = new QLineEdit(w);
207         ln->setText(m_currentBankDocument->getParameter("KEYAPI_" + sources[i]));
208         lwidgets->insert(sources.value(i), ln);
209 
210         ui.kAPIKeyLayout->addWidget(new QLabel(sources[i] + ':', w), i, 0);
211         ui.kAPIKeyLayout->addWidget(ln, i, 1);
212     }
213 
214     connect(ui.kcfg_download_on_open, &QCheckBox::toggled, ui.kcfg_download_frequency, &KComboBox::setEnabled);
215     connect(ui.kSave, &QPushButton::clicked, this, [ = ]() {
216         SKGError err;
217         {
218             SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Save API keys"), err)
219             foreach (auto k,  lwidgets->keys()) {
220                 m_currentBankDocument->setParameter("KEYAPI_" + k, lwidgets->value(k)->text());
221             }
222         }
223         // Display error
224         SKGMainPanel::displayErrorMessage(err);
225     });
226 
227     return w;
228 }
229 
getPreferenceSkeleton()230 KConfigSkeleton* SKGUnitPlugin::getPreferenceSkeleton()
231 {
232     return skgunit_settings::self();
233 }
234 
title() const235 QString SKGUnitPlugin::title() const
236 {
237     return i18nc("Noun, units for operations, usually currencies or a shares", "Units");
238 }
239 
icon() const240 QString SKGUnitPlugin::icon() const
241 {
242     return QStringLiteral("taxes-finances");
243 }
244 
toolTip() const245 QString SKGUnitPlugin::toolTip() const
246 {
247     return i18nc("A tool tip", "Unit management");
248 }
249 
tips() const250 QStringList SKGUnitPlugin::tips() const
251 {
252     QStringList output;
253     output.push_back(i18nc("Description of a tips", "<p>... you can download <a href=\"skg://skrooge_unit_plugin\">units</a>.</p>"));
254     output.push_back(i18nc("Description of a tips", "<p>... <a href=\"skg://skrooge_unit_plugin\">units</a> can be downloaded <a href=\"skg://tab_configure?page=Skrooge Unit Plugin\">automatically</a> when a document is opened.</p>"));
255     output.push_back(i18nc("Description of a tips", "<p>... you can split a <a href=\"skg://skrooge_unit_plugin\">share</a>.</p>"));
256     output.push_back(i18nc("Description of a tips", "<p>... <a href=\"skg://skrooge_unit_plugin\">units</a> can be merged by drag & drop.</p>"));
257     output.push_back(i18nc("Description of a tips", "<p>... you can download more <a href=\"skg://skrooge_unit_plugin\">sources</a> of quote.</p>"));
258     output.push_back(i18nc("Description of a tips", "<p>... you can create and share your own source of quote.</p>"));
259 
260     return output;
261 }
262 
getOrder() const263 int SKGUnitPlugin::getOrder() const
264 {
265     return 60;
266 }
267 
isInPagesChooser() const268 bool SKGUnitPlugin::isInPagesChooser() const
269 {
270     return true;
271 }
272 
onSplitShare()273 void SKGUnitPlugin::onSplitShare()
274 {
275     SKGError err;
276     SKGTRACEINFUNCRC(10, err)
277 
278     // Get Selection
279     if (SKGMainPanel::getMainPanel() != nullptr) {
280         SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
281         int nb = selection.count();
282         if (nb == 1) {
283             bool ok = false;
284             double ratio = QInputDialog::getDouble(SKGMainPanel::getMainPanel(), i18nc("Question", "Split share"),
285                                                    i18nc("Question", "Ratio (2 means 2-for-1, 0.5 means 1-for-2):"), 2.0,
286                                                    0, std::numeric_limits<double>::max(), 8, &ok);
287             if (ok) {
288                 SKGUnitObject unit(selection.at(0));
289                 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Split stock '%1' by '%2'", unit.getName(), ratio), err)
290                 IFOKDO(err, unit.split(ratio))
291             }
292         }
293 
294         // status
295         IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Stock split.")))
296         else {
297             err.addError(ERR_FAIL, i18nc("Error message", "Splitting stock failed."));
298         }
299 
300         // Display error
301         SKGMainPanel::displayErrorMessage(err);
302     }
303 }
304 
advice(const QStringList & iIgnoredAdvice)305 SKGAdviceList SKGUnitPlugin::advice(const QStringList& iIgnoredAdvice)
306 {
307     SKGTRACEINFUNC(10)
308     SKGAdviceList output;
309     output.reserve(20);
310 
311     // Get all currencies
312     SKGStringListList result;
313     m_currentBankDocument->executeSelectSqliteOrder(QStringLiteral("SELECT (SELECT count(1) FROM operation WHERE operation.rc_unit_id=unit.id), unit.t_name FROM unit WHERE t_type='C' GROUP BY t_name ORDER BY count(1) DESC"), result);
314     int nb = result.count();
315 
316     // Check primary unit
317     if (!iIgnoredAdvice.contains(QStringLiteral("skgunitplugin_primaryunit"))) {
318         if (m_currentBankDocument->getPrimaryUnit().Name.isEmpty() && nb > 1) {
319             // Get unit
320             QString unit = result.at(1).at(1);
321 
322             SKGAdvice ad;
323             ad.setUUID("skgunitplugin_primaryunit|" % unit);
324             ad.setPriority(8);
325             ad.setShortMessage(i18nc("Advice on making the best (short)", "Define a primary currency"));
326             ad.setLongMessage(i18nc("Advice on making the best (long)", "To avoid misunderstanding and conflicts between units at conversion time, you should define a primary currency. It is the currency against which all other will be converted"));
327             SKGAdvice::SKGAdviceActionList autoCorrections;
328             {
329                 SKGAdvice::SKGAdviceAction a;
330                 a.Title = i18nc("Advice on making the best (action)", "Set '%1' as primary currency", unit);
331                 a.IconName = icon();
332                 a.IsRecommended = true;
333                 autoCorrections.push_back(a);
334             }
335             {
336                 SKGAdvice::SKGAdviceAction a;
337                 a.Title = i18nc("Advice on making the best (action)", "Edit units");
338                 a.IconName = icon();
339                 a.IsRecommended = false;
340                 autoCorrections.push_back(a);
341             }
342             ad.setAutoCorrections(autoCorrections);
343             output.push_back(ad);
344 
345             --nb;
346         }
347     }
348 
349     // Check secondary unit
350     if (!iIgnoredAdvice.contains(QStringLiteral("skgunitplugin_secondaryunit"))) {
351         if (m_currentBankDocument->getSecondaryUnit().Name.isEmpty() && nb > 1) {
352             // Get unit
353             QString unit = result.at(1).at(1);
354 
355             SKGAdvice ad;
356             ad.setUUID("skgunitplugin_secondaryunit|" % unit);
357             ad.setPriority(2);
358             ad.setShortMessage(i18nc("Advice on making the best (short)", "Define a secondary currency"));
359             ad.setLongMessage(i18nc("Advice on making the best (long)", "When a secondary unit is defined, Skrooge will display it as an additional amount information."));
360             SKGAdvice::SKGAdviceActionList autoCorrections;
361             {
362                 SKGAdvice::SKGAdviceAction a;
363                 a.Title = i18nc("Advice on making the best (action)", "Set '%1' as secondary currency", unit);
364                 a.IconName = icon();
365                 a.IsRecommended = true;
366                 autoCorrections.push_back(a);
367             }
368             {
369                 SKGAdvice::SKGAdviceAction a;
370                 a.Title = i18nc("Advice on making the best (action)", "Edit units");
371                 a.IconName = icon();
372                 a.IsRecommended = false;
373                 autoCorrections.push_back(a);
374             }
375             ad.setAutoCorrections(autoCorrections);
376             output.push_back(ad);
377         }
378     }
379 
380     // Shares not downloaded
381     if (!iIgnoredAdvice.contains(QStringLiteral("skgunitplugin_notdownloaded"))) {
382         m_currentBankDocument->executeSelectSqliteOrder(QStringLiteral("SELECT t_name, t_internet_code from unit WHERE t_internet_code<>'' AND (julianday('now', 'localtime')-(SELECT MAX(julianday(d_date)) FROM unitvalue WHERE rd_unit_id=unit.id ))>30 OR NOT EXISTS (SELECT 1 FROM unitvalue WHERE unitvalue.rd_unit_id=unit.id)"), result);
383         nb = result.count();
384         SKGAdvice::SKGAdviceActionList autoCorrections;
385         autoCorrections.reserve(nb);
386         for (int i = 1; i < nb; ++i) {  // Ignore header
387             // Get parameters
388             const QStringList& line = result.at(i);
389             const QString& unit = line.at(0);
390             const QString& internet_code = line.at(1);
391 
392             SKGAdvice ad;
393             ad.setUUID("skgunitplugin_notdownloaded|" % unit);
394             ad.setPriority(5);
395             ad.setShortMessage(i18nc("Advice on making the best (short)", "Unit '%1' has not been downloaded for more than a month", unit));
396             ad.setLongMessage(i18nc("Advice on making the best (long)", "Do not forget download units to have a better view of your accounts"));
397             autoCorrections.resize(0);
398             {
399                 SKGAdvice::SKGAdviceAction a;
400                 a.Title = i18nc("Advice on making the best (action)", "Edit units");
401                 a.IconName = icon();
402                 a.IsRecommended = false;
403                 autoCorrections.push_back(a);
404             }
405             if (!internet_code.isEmpty()) {
406                 SKGAdvice::SKGAdviceAction a;
407                 a.Title = i18nc("Advice on making the best (action)", "Download '%1'", unit);
408                 a.IconName = QStringLiteral("download");
409                 a.IsRecommended = true;
410                 autoCorrections.push_back(a);
411             }
412             ad.setAutoCorrections(autoCorrections);
413             output.push_back(ad);
414         }
415     }
416 
417     // Check unused units
418     if (!iIgnoredAdvice.contains(QStringLiteral("skgunitplugin_unused"))) {
419         bool exist = false;
420         m_currentBankDocument->existObjects(QStringLiteral("unit"), QStringLiteral("t_type NOT IN ('I', '1', '2') AND NOT EXISTS (SELECT 1 FROM operation WHERE operation.rc_unit_id=unit.id) AND NOT EXISTS (SELECT 1 FROM unit as unit2 WHERE unit2.rd_unit_id=unit.id)"), exist);
421         if (exist) {
422             SKGAdvice ad;
423             ad.setUUID(QStringLiteral("skgunitplugin_unused"));
424             ad.setPriority(5);
425             ad.setShortMessage(i18nc("Advice on making the best (short)", "Many unused units"));
426             ad.setLongMessage(i18nc("Advice on making the best (long)", "You can improve performances by removing units for which no operation is registered."));
427             QStringList autoCorrections;
428             autoCorrections.push_back(QStringLiteral("skg://clean_delete_unused_units"));
429             ad.setAutoCorrections(autoCorrections);
430             output.push_back(ad);
431         }
432     }
433 
434     // Check unit too complex
435     if (!iIgnoredAdvice.contains(QStringLiteral("skgunitplugin_amountnotdefined"))) {
436         m_currentBankDocument->executeSelectSqliteOrder(QStringLiteral("SELECT t_name FROM v_unit WHERE t_type IN ('2','C') AND f_CURRENTAMOUNT=1"), result);
437         nb = result.count();
438         SKGAdvice::SKGAdviceActionList autoCorrections;
439         autoCorrections.reserve(nb);
440         for (int i = 1; i < nb; ++i) {  // Ignore header
441             // Get parameters
442             const QStringList& line = result.at(i);
443             const QString& unit = line.at(0);
444 
445             SKGAdvice ad;
446             ad.setUUID("skgunitplugin_amountnotdefined|" % unit);
447             ad.setPriority(9);
448             ad.setShortMessage(i18nc("Advice on making the best (short)", "The amount of the unit '%1' is not defined", unit));
449             ad.setLongMessage(i18nc("Advice on making the best (long)", "'%1' has an amount defined at 1. Most of the time this is not normal and causes wrong computation. Check if this is normal.", unit));
450             autoCorrections.resize(0);
451             {
452                 SKGAdvice::SKGAdviceAction a;
453                 a.Title = i18nc("Advice on making the best (action)", "Edit units");
454                 a.IconName = icon();
455                 a.IsRecommended = false;
456                 autoCorrections.push_back(a);
457             }
458             ad.setAutoCorrections(autoCorrections);
459             output.push_back(ad);
460         }
461     }
462 
463     // Check unit too complex
464     if (!iIgnoredAdvice.contains(QStringLiteral("skgunitplugin_toocomplex"))) {
465         m_currentBankDocument->executeSelectSqliteOrder(QStringLiteral("SELECT A.t_name FROM unit A, unit B, unit C, unit D WHERE A.rd_unit_id=B.id AND B.rd_unit_id=C.id AND C.rd_unit_id=D.id"), result);
466         nb = result.count();
467         SKGAdvice::SKGAdviceActionList autoCorrections;
468         autoCorrections.reserve(nb);
469         for (int i = 1; i < nb; ++i) {  // Ignore header
470             // Get parameters
471             const QStringList& line = result.at(i);
472             const QString& unit = line.at(0);
473 
474             SKGAdvice ad;
475             ad.setUUID(QStringLiteral("skgunitplugin_toocomplex"));
476             ad.setPriority(9);
477             ad.setShortMessage(i18nc("Advice on making the best (short)", "The definition of the unit '%1' is too complex", unit));
478             ad.setLongMessage(i18nc("Advice on making the best (long)", "'%1' is defined relatively to another unit defined relatively to a third one. This is too complex and not supported by Skrooge.", unit));
479             autoCorrections.resize(0);
480             {
481                 SKGAdvice::SKGAdviceAction a;
482                 a.Title = i18nc("Advice on making the best (action)", "Edit units");
483                 a.IconName = icon();
484                 a.IsRecommended = false;
485                 autoCorrections.push_back(a);
486             }
487             ad.setAutoCorrections(autoCorrections);
488             output.push_back(ad);
489         }
490     }
491 
492     // Unit with very old values
493     if (!iIgnoredAdvice.contains(QStringLiteral("skgunitplugin_veryold"))) {
494         m_currentBankDocument->executeSelectSqliteOrder(QStringLiteral("SELECT t_name from unit WHERE (SELECT COUNT(*) FROM unitvalue WHERE unitvalue.rd_unit_id=unit.id)>1 AND EXISTS (SELECT 1 FROM unitvalue WHERE unitvalue.rd_unit_id=unit.id AND unitvalue.d_date<=(SELECT date('now', 'localtime', '-50 year')))"), result);
495         nb = result.count();
496         SKGAdvice::SKGAdviceActionList autoCorrections;
497         autoCorrections.reserve(nb);
498         for (int i = 1; i < nb; ++i) {  // Ignore header
499             // Get parameters
500             const QStringList& line = result.at(i);
501             const QString& unit = line.at(0);
502 
503             SKGAdvice ad;
504             ad.setUUID("skgunitplugin_veryold|" % unit);
505             ad.setPriority(3);
506             ad.setShortMessage(i18nc("Advice on making the best (short)", "Unit '%1' has very old values", unit));
507             ad.setLongMessage(i18nc("Advice on making the best (long)", "Unit '%1' has very old values. Check if this is normal.", unit));
508             autoCorrections.resize(0);
509             {
510                 SKGAdvice::SKGAdviceAction a;
511                 a.Title = i18nc("Advice on making the best (action)", "Edit units");
512                 a.IconName = icon();
513                 a.IsRecommended = false;
514                 autoCorrections.push_back(a);
515             }
516             ad.setAutoCorrections(autoCorrections);
517             output.push_back(ad);
518         }
519     }
520 
521     // No decimal settings
522     if (!iIgnoredAdvice.contains(QStringLiteral("skgunitplugin_decimalsymbol")) && QLocale().decimalPoint().isNull()) {
523         SKGAdvice ad;
524         ad.setUUID(QStringLiteral("skgunitplugin_decimalsymbol"));
525         ad.setPriority(9);
526         ad.setShortMessage(i18nc("Advice on making the best (short)", "No decimal symbol defined"));
527         ad.setLongMessage(i18nc("Advice on making the best (long)", "In KDE localization settings, there is no decimal symbol defined for currencies. This could be confusing."));
528         SKGAdvice::SKGAdviceActionList autoCorrections;
529         {
530             SKGAdvice::SKGAdviceAction a;
531             a.Title = i18nc("Advice on making the best (action)", "Edit KDE settings");
532             a.IconName = QStringLiteral("configure");
533             a.IsRecommended = false;
534             autoCorrections.push_back(a);
535         }
536         ad.setAutoCorrections(autoCorrections);
537         output.push_back(ad);
538     }
539 
540     return output;
541 }
542 
executeAdviceCorrection(const QString & iAdviceIdentifier,int iSolution)543 SKGError SKGUnitPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution)
544 {
545     if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_primaryunit|"))) {
546         if (iSolution == 1) {
547             SKGMainPanel::getMainPanel()->openPage(QStringLiteral("skg://skrooge_unit_plugin"));
548         } else {
549             // Get parameters
550             QString unit = iAdviceIdentifier.right(iAdviceIdentifier.length() - 26);
551 
552             SKGError err;
553             {
554                 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Define primary currency"), err)
555                 SKGUnitObject unitObj(m_currentBankDocument);
556                 err = unitObj.setName(unit);
557                 IFOKDO(err, unitObj.load())
558                 IFOKDO(err, unitObj.setType(SKGUnitObject::PRIMARY))
559                 IFOKDO(err, unitObj.save())
560 
561                 // Send message
562                 IFOKDO(err, unitObj.getDocument()->sendMessage(i18nc("An information to the user", "The unit '%1' is now the primary unit", unitObj.getDisplayName()), SKGDocument::Hidden))
563             }
564 
565             // status bar
566             IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Primary currency defined.")))
567             else {
568                 err.addError(ERR_FAIL, i18nc("Error message", "Primary currency definition failed"));
569             }
570 
571             // Display error
572             SKGMainPanel::displayErrorMessage(err);
573         }
574 
575         return SKGError();
576     }
577     if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_secondaryunit|"))) {
578         if (iSolution == 1) {
579             SKGMainPanel::getMainPanel()->openPage(QStringLiteral("skg://skrooge_unit_plugin"));
580         } else {
581             // Get parameters
582             QString unit = iAdviceIdentifier.right(iAdviceIdentifier.length() - 28);
583 
584             SKGError err;
585             {
586                 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Define secondary currency"), err)
587                 SKGUnitObject unitObj(m_currentBankDocument);
588                 err = unitObj.setName(unit);
589                 IFOKDO(err, unitObj.load())
590                 IFOKDO(err, unitObj.setType(SKGUnitObject::SECONDARY))
591                 IFOKDO(err, unitObj.save())
592 
593                 // Send message
594                 IFOKDO(err, unitObj.getDocument()->sendMessage(i18nc("An information to the user", "The unit '%1' is now the secondary unit", unitObj.getDisplayName()), SKGDocument::Hidden))
595             }
596 
597             // status bar
598             IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Secondary currency defined.")))
599             else {
600                 err.addError(ERR_FAIL, i18nc("Error message", "Secondary currency definition failed"));
601             }
602 
603             // Display error
604             SKGMainPanel::displayErrorMessage(err);
605         }
606 
607         return SKGError();
608     }
609     if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_notdownloaded|"))) {
610         if (iSolution == 0) {
611             SKGMainPanel::getMainPanel()->openPage(QStringLiteral("skg://skrooge_unit_plugin"));
612         } else {
613             // Get parameters
614             QString unit = iAdviceIdentifier.right(iAdviceIdentifier.length() - 28);
615 
616             SKGError err;
617             SKGUnitObject unitObj(m_currentBankDocument);
618             err = unitObj.setName(unit);
619             IFOKDO(err, unitObj.load())
620             IFOKDO(err, SKGUnitPluginWidget::downloadUnitValue(unitObj, SKGUnitPluginWidget::getDownloadModeFromSettings()))
621 
622             // Display error
623             SKGMainPanel::displayErrorMessage(err);
624         }
625 
626         return SKGError();
627     }
628     if ((m_currentBankDocument != nullptr) && (iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_veryold|")) ||
629             iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_toocomplex")) ||
630             iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_amountnotdefined|")))
631        ) {
632         SKGMainPanel::getMainPanel()->openPage(QStringLiteral("skg://skrooge_unit_plugin"));
633         return SKGError();
634     }
635     if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_decimalsymbol"))) {
636         QProcess::execute(QStringLiteral("kcmshell5"), QStringList() << QStringLiteral("formats"));
637         return SKGError();
638     }
639     return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution);
640 }
641 
deleteUnusedUnits() const642 void SKGUnitPlugin::deleteUnusedUnits() const
643 {
644     SKGError err;
645     _SKGTRACEINFUNCRC(10, err)
646     if (m_currentBankDocument != nullptr) {
647         SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Delete unused units"), err)
648 
649         // Modification of payee object
650         QString sql = QStringLiteral("DELETE FROM unit WHERE t_type NOT IN ('I', '1', '2') AND NOT EXISTS (SELECT 1 FROM operation WHERE operation.rc_unit_id=unit.id) AND NOT EXISTS (SELECT 1 FROM unit as unit2 WHERE unit2.rd_unit_id=unit.id)");
651         err = m_currentBankDocument->executeSqliteOrder(sql);
652     }
653 
654     // status bar
655     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Unused units deleted")))
656     else {
657         err.addError(ERR_FAIL, i18nc("Error message", "Unused units deletion failed"));
658     }
659 
660     // Display error
661     SKGMainPanel::displayErrorMessage(err);
662 }
663 
664 #include <skgunitplugin.moc>
665