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  * A skrooge plugin to manage scheduled operations
8  *
9  * @author Stephane MANKOWSKI
10  */
11 #include "skgscheduledplugin.h"
12 
13 #include <kaboutdata.h>
14 #include <kactioncollection.h>
15 #include <kpluginfactory.h>
16 #include <qstandardpaths.h>
17 
18 #include "skgdocumentbank.h"
19 #include "skgmainpanel.h"
20 #include "skgrecurrentoperationobject.h"
21 #include "skgscheduled_settings.h"
22 #include "skgscheduledboardwidget.h"
23 #include "skgscheduledpluginwidget.h"
24 #include "skgsuboperationobject.h"
25 #include "skgtraces.h"
26 #include "skgtransactionmng.h"
27 
28 /**
29  * This plugin factory.
30  */
K_PLUGIN_FACTORY(SKGScheduledPluginFactory,registerPlugin<SKGScheduledPlugin> ();)31 K_PLUGIN_FACTORY(SKGScheduledPluginFactory, registerPlugin<SKGScheduledPlugin>();)
32 
33 SKGScheduledPlugin::SKGScheduledPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) :
34     SKGInterfacePlugin(iParent), m_currentBankDocument(nullptr), m_counterAdvice(0)
35 {
36     Q_UNUSED(iWidget)
37     SKGTRACEINFUNC(10)
38 }
39 
~SKGScheduledPlugin()40 SKGScheduledPlugin::~SKGScheduledPlugin()
41 {
42     SKGTRACEINFUNC(10)
43     m_currentBankDocument = nullptr;
44 }
45 
setupActions(SKGDocument * iDocument)46 bool SKGScheduledPlugin::setupActions(SKGDocument* iDocument)
47 {
48     SKGTRACEINFUNC(10)
49 
50     m_currentBankDocument = qobject_cast<SKGDocumentBank*>(iDocument);
51     if (m_currentBankDocument == nullptr) {
52         return false;
53     }
54 
55     setComponentName(QStringLiteral("skrooge_scheduled"), title());
56     setXMLFile(QStringLiteral("skrooge_scheduled.rc"));
57 
58     // Create yours actions here
59     auto actScheduleOperation = new QAction(SKGServices::fromTheme(icon()), i18nc("Verb, create a scheduled operation", "Schedule"), this);
60     connect(actScheduleOperation, &QAction::triggered, this, &SKGScheduledPlugin::onScheduleOperation);
61     actionCollection()->setDefaultShortcut(actScheduleOperation, Qt::CTRL + Qt::Key_I);
62     registerGlobalAction(QStringLiteral("schedule_operation"), actScheduleOperation, QStringList() << QStringLiteral("operation"), 1, -1, 410);
63 
64     auto actSkipScheduledOperation = new QAction(SKGServices::fromTheme(QStringLiteral("nextuntranslated")), i18nc("Verb, skip scheduled operations", "Skip"), this);
65     connect(actSkipScheduledOperation, &QAction::triggered, this, &SKGScheduledPlugin::onSkipScheduledOperations);
66     registerGlobalAction(QStringLiteral("skip_scheduled_operations"), actSkipScheduledOperation, QStringList() << QStringLiteral("recurrentoperation"), 1, -1, 410);
67 
68     return true;
69 }
70 
refresh()71 void SKGScheduledPlugin::refresh()
72 {
73     SKGTRACEINFUNC(10)
74     // Automatic insert
75     if ((m_currentBankDocument != nullptr) && m_currentBankDocument->getMainDatabase() != nullptr) {
76         QString doc_id = m_currentBankDocument->getUniqueIdentifier();
77         if (m_docUniqueIdentifier != doc_id && m_currentBankDocument->getParameter(QStringLiteral("SKG_DB_BANK_VERSION")) >= QStringLiteral("0.5")) {
78             m_docUniqueIdentifier = doc_id;
79 
80             SKGError err;
81             // Read Setting
82             bool check_on_open = skgscheduled_settings::check_on_open();
83 
84             if (check_on_open) {
85                 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Insert recurrent operations"), err)
86                 int nbi = 0;
87                 err = SKGRecurrentOperationObject::process(m_currentBankDocument, nbi);
88             }
89             // Display error
90             SKGMainPanel::displayErrorMessage(err);
91         }
92     }
93 }
94 
getNbDashboardWidgets()95 int SKGScheduledPlugin::getNbDashboardWidgets()
96 {
97     return 1;
98 }
99 
getDashboardWidgetTitle(int iIndex)100 QString SKGScheduledPlugin::getDashboardWidgetTitle(int iIndex)
101 {
102     Q_UNUSED(iIndex)
103     return i18nc("Noun, the title of a section", "Scheduled operations");
104 }
105 
getDashboardWidget(int iIndex)106 SKGBoardWidget* SKGScheduledPlugin::getDashboardWidget(int iIndex)
107 {
108     Q_UNUSED(iIndex)
109     return new SKGScheduledBoardWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument);
110 }
111 
getWidget()112 SKGTabPage* SKGScheduledPlugin::getWidget()
113 {
114     SKGTRACEINFUNC(10)
115     return new SKGScheduledPluginWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument);
116 }
117 
getPreferenceWidget()118 QWidget* SKGScheduledPlugin::getPreferenceWidget()
119 {
120     SKGTRACEINFUNC(10)
121     auto w = new QWidget();
122     ui.setupUi(w);
123 
124     connect(ui.kcfg_remind_me, &QCheckBox::toggled, ui.kcfg_remind_me_days, &QSpinBox::setEnabled);
125     connect(ui.kcfg_remind_me, &QCheckBox::toggled, ui.label_3, &QSpinBox::setEnabled);
126     connect(ui.kcfg_nb_times, &QCheckBox::toggled, ui.kcfg_nb_times_val, &QSpinBox::setEnabled);
127     connect(ui.kcfg_auto_write, &QCheckBox::toggled, ui.kcfg_auto_write_days, &QSpinBox::setEnabled);
128     connect(ui.kcfg_auto_write, &QCheckBox::toggled, ui.label_4, &QSpinBox::setEnabled);
129     return w;
130 }
131 
getPreferenceSkeleton()132 KConfigSkeleton* SKGScheduledPlugin::getPreferenceSkeleton()
133 {
134     return skgscheduled_settings::self();
135 }
136 
title() const137 QString SKGScheduledPlugin::title() const
138 {
139     return i18nc("Noun", "Scheduled operations");
140 }
141 
icon() const142 QString SKGScheduledPlugin::icon() const
143 {
144     return QStringLiteral("chronometer");
145 }
146 
toolTip() const147 QString SKGScheduledPlugin::toolTip() const
148 {
149     return i18nc("Noun", "Operations scheduled management");
150 }
151 
getOrder() const152 int SKGScheduledPlugin::getOrder() const
153 {
154     return 20;
155 }
156 
tips() const157 QStringList SKGScheduledPlugin::tips() const
158 {
159     QStringList output;
160     output.push_back(i18nc("Description of a tips", "<p>... you can <a href=\"skg://skrooge_scheduled_plugin\">schedule</a> operations or templates.</p>"));
161     return output;
162 }
163 
isInPagesChooser() const164 bool SKGScheduledPlugin::isInPagesChooser() const
165 {
166     return true;
167 }
168 
savePreferences() const169 SKGError SKGScheduledPlugin::savePreferences() const
170 {
171     SKGError err;
172     if (m_currentBankDocument != nullptr) {
173         // Read Setting
174         if (skgscheduled_settings::create_template()) {
175             SKGObjectBase::SKGListSKGObjectBase recurrents;
176             err = m_currentBankDocument->getObjects(QStringLiteral("v_recurrentoperation"), QStringLiteral("(select count(1) from operation where operation.id=rd_operation_id and t_template='N')=1"), recurrents);
177             int nb = recurrents.count();
178             if (nb != 0) {
179                 SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Conversion schedule"), err, nb)
180                 for (int i = 0; !err && i < nb; ++i) {
181                     // Migration of existing schedule in template mode
182                     SKGRecurrentOperationObject recOp(recurrents.at(i));
183                     SKGOperationObject operationObj;
184 
185                     IFOK(err) recOp.getParentOperation(operationObj);
186 
187                     SKGOperationObject operationObjOrig = operationObj;
188                     IFOKDO(err, operationObjOrig.duplicate(operationObj, operationObjOrig.getDate(), true))
189 
190                     IFOKDO(err, recOp.setParentOperation(operationObj))
191                     IFOKDO(err, recOp.save())
192                     IFOKDO(err, recOp.load())
193 
194                     IFOKDO(err, operationObjOrig.setAttribute(QStringLiteral("r_recurrentoperation_id"), SKGServices::intToString(recOp.getID())))
195                     IFOKDO(err, operationObjOrig.save())
196 
197                     IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
198                 }
199                 IFOK(err) m_currentBankDocument->sendMessage(i18nc("An information message",  "All scheduled operations have been converted in template"));
200             }
201         }
202     }
203     return err;
204 }
205 
scheduleOperation(const SKGOperationObject & iOperation,SKGRecurrentOperationObject & oRecurrent) const206 SKGError SKGScheduledPlugin::scheduleOperation(const SKGOperationObject& iOperation, SKGRecurrentOperationObject& oRecurrent) const
207 {
208     SKGError err;
209     SKGOperationObject operationObjDuplicate = iOperation;
210     bool isTemplate = operationObjDuplicate.isTemplate();
211 
212     SKGOperationObject operationObjOrig;
213     if (!isTemplate && skgscheduled_settings::create_template()) {
214         // The selected operation is not a template and settings is set to create one
215         operationObjOrig = operationObjDuplicate;
216         IFOKDO(err, operationObjOrig.duplicate(operationObjDuplicate, operationObjOrig.getDate(), true))
217         IFOK(err) m_currentBankDocument->sendMessage(i18nc("An information message",  "A template has been created"), SKGDocument::Positive);
218     }
219 
220     SKGRecurrentOperationObject recOp;
221     err = operationObjDuplicate.addRecurrentOperation(recOp);
222     IFOKDO(err, recOp.warnEnabled(skgscheduled_settings::remind_me()))
223     IFOKDO(err, recOp.setWarnDays(skgscheduled_settings::remind_me_days()))
224     IFOKDO(err, recOp.autoWriteEnabled(skgscheduled_settings::auto_write()))
225     IFOKDO(err, recOp.setAutoWriteDays(skgscheduled_settings::auto_write_days()))
226     IFOKDO(err, recOp.timeLimit(skgscheduled_settings::nb_times()))
227     IFOKDO(err, recOp.setTimeLimit(skgscheduled_settings::nb_times_val()))
228     IFOKDO(err, recOp.setPeriodIncrement(skgscheduled_settings::once_every()))
229     IFOKDO(err, recOp.setPeriodUnit(static_cast<SKGRecurrentOperationObject::PeriodUnit>(SKGServices::stringToInt(skgscheduled_settings::once_every_unit()))))
230     if (!err && !isTemplate) {
231         err = recOp.setDate(recOp.getNextDate());
232     }
233     IFOKDO(err, recOp.save())
234     if (!isTemplate && skgscheduled_settings::create_template()) {
235         IFOKDO(err, recOp.load())
236         IFOKDO(err, operationObjOrig.setAttribute(QStringLiteral("r_recurrentoperation_id"), SKGServices::intToString(recOp.getID())))
237         IFOKDO(err, operationObjOrig.save())
238     }
239 
240     oRecurrent = recOp;
241 
242     return err;
243 }
244 
onScheduleOperation()245 void SKGScheduledPlugin::onScheduleOperation()
246 {
247     SKGError err;
248     SKGTRACEINFUNCRC(10, err)
249 
250     // Get Selection
251     if (SKGMainPanel::getMainPanel() != nullptr) {
252         SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
253         int nb = selection.count();
254         if ((nb != 0) && (m_currentBankDocument != nullptr)) {
255             QStringList list;
256             SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Operation schedule"), err, nb)
257             for (int i = 0; !err && i < nb; ++i) {
258                 SKGOperationObject operationObj(selection.at(i));
259                 SKGRecurrentOperationObject rop;
260                 err = scheduleOperation(operationObj, rop);
261 
262                 // Send message
263                 IFOKDO(err, m_currentBankDocument->sendMessage(i18nc("An information to the user", "The operation '%1' has been scheduled", operationObj.getDisplayName()), SKGDocument::Hidden))
264 
265                 IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
266                 list.push_back(operationObj.getUniqueID());
267             }
268 
269             IFOK(err) {
270                 // Open the scheduled operation
271                 SKGMainPanel::getMainPanel()->openPage("skg://skrooge_scheduled_plugin/?selection=" % SKGServices::encodeForUrl(SKGServices::stringsToCsv(list)));
272             }
273         }
274 
275         // status bar
276         IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Operation scheduled.")))
277         else {
278             err.addError(ERR_FAIL, i18nc("Error message",  "Operation schedule failed"));
279         }
280 
281         // Display error
282         SKGMainPanel::displayErrorMessage(err);
283     }
284 }
285 
onSkipScheduledOperations()286 void SKGScheduledPlugin::onSkipScheduledOperations()
287 {
288     SKGError err;
289     SKGTRACEINFUNCRC(10, err)
290 
291     // Get Selection
292     if (SKGMainPanel::getMainPanel() != nullptr) {
293         SKGObjectBase::SKGListSKGObjectBase selection;
294         auto selectionString = sender()->property("selection").toString();
295         if (!selectionString.isEmpty()) {
296             selection.append(SKGRecurrentOperationObject(m_currentBankDocument, SKGServices::stringToInt(SKGServices::splitCSVLine(selectionString, QLatin1Char('-'), false).at(0))));
297         } else {
298             selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
299         }
300         int nb = selection.count();
301         if ((nb != 0) && (m_currentBankDocument != nullptr)) {
302             QStringList list;
303             SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Skip scheduled operations"), err, nb)
304             for (int i = 0; !err && i < nb; ++i) {
305                 SKGRecurrentOperationObject rop(m_currentBankDocument, selection.at(i).getID());
306                 err = rop.setDate(rop.getNextDate());
307                 if (!err && rop.hasTimeLimit()) {
308                     err = rop.setTimeLimit(rop.getTimeLimit() - 1);
309                 }
310                 IFOKDO(err, rop.save())
311 
312                 IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
313                 list.push_back(rop.getUniqueID());
314             }
315         }
316 
317         // status bar
318         IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Scheduled operations skipped.")))
319         else {
320             err.addError(ERR_FAIL, i18nc("Error message",  "Skip of scheduled operation failed"));
321         }
322 
323         // Display error
324         SKGMainPanel::displayErrorMessage(err);
325     }
326 }
327 
328 
advice(const QStringList & iIgnoredAdvice)329 SKGAdviceList SKGScheduledPlugin::advice(const QStringList& iIgnoredAdvice)
330 {
331     SKGTRACEINFUNC(10)
332     SKGAdviceList output;
333     output.reserve(20);
334 
335     // Recurrent operation with the last inserted operation having a different amount
336     if (!iIgnoredAdvice.contains(QStringLiteral("skgscheduledplugin_notuptodate"))) {
337         SKGStringListList result;
338         m_currentBankDocument->executeSelectSqliteOrder("SELECT r.id, r.rd_operation_id, r.f_CURRENTAMOUNT, r2.f_CURRENTAMOUNT FROM v_recurrentoperation_display r INNER JOIN (SELECT MAX(d_date), f_CURRENTAMOUNT, r_recurrentoperation_id FROM v_operation_display GROUP BY r_recurrentoperation_id) r2 WHERE r2.r_recurrentoperation_id=r.id AND ABS(r.f_CURRENTAMOUNT-r2.f_CURRENTAMOUNT)>" % SKGServices::doubleToString(EPSILON), result);
339         int nb = result.count();
340         SKGAdvice::SKGAdviceActionList autoCorrections;
341         for (int i = 1; i < nb; ++i) {  // Ignore header
342             // Get parameters
343             const QStringList& line = result.at(i);
344             int idRecu = SKGServices::stringToInt(line.at(0));
345             const QString& idOper = line.at(1);
346             const QString& amountLastOperation = line.at(3);
347 
348             SKGRecurrentOperationObject recu(m_currentBankDocument, idRecu);
349             QString name = recu.getDisplayName();
350 
351             SKGAdvice ad;
352             ad.setUUID("skgscheduledplugin_notuptodate|" % idOper % ';' % amountLastOperation);
353             ad.setPriority(4);
354             ad.setShortMessage(i18nc("Advice on making the best (short)", "Scheduled operation '%1' not uptodate", name));
355             ad.setLongMessage(i18nc("Advice on making the best (long)", "The scheduled operation '%1' does not have the amount of the last inserted operation (%2)", name, amountLastOperation));
356             autoCorrections.resize(0);
357             {
358                 SKGAdvice::SKGAdviceAction a;
359                 a.Title = i18nc("Advice on making the best (action)", "Update the next scheduled operation amount (%1)", amountLastOperation);
360                 a.IconName = QStringLiteral("system-run");
361                 a.IsRecommended = true;
362                 autoCorrections.push_back(a);
363             }
364             ad.setAutoCorrections(autoCorrections);
365             output.push_back(ad);
366         }
367     }
368 
369     // Recurrent operation with the last inserted operation having a different date
370     if (!iIgnoredAdvice.contains(QStringLiteral("skgscheduledplugin_newdate"))) {
371         SKGStringListList result;
372         m_currentBankDocument->executeSelectSqliteOrder(QStringLiteral("SELECT r.id, r.d_date, "
373                 "date(r2.d_date, '+'||((CASE r.t_period_unit WHEN 'W' THEN 7  ELSE 1 END)*r.i_period_increment)||' '||(CASE r.t_period_unit WHEN 'M' THEN 'month' WHEN 'Y' THEN 'year' ELSE 'day' END)) "
374                 "FROM v_recurrentoperation_display r "
375                 "INNER JOIN (SELECT MAX(d_date) as d_date, r_recurrentoperation_id FROM v_operation_display GROUP BY r_recurrentoperation_id) r2 "
376                 "WHERE r2.r_recurrentoperation_id=r.id AND r.d_PREVIOUS!=r2.d_date"), result);
377         int nb = result.count();
378         SKGAdvice::SKGAdviceActionList autoCorrections;
379         for (int i = 1; i < nb; ++i) {  // Ignore header
380             // Get parameters
381             const QStringList& line = result.at(i);
382             int idRecu = SKGServices::stringToInt(line.at(0));
383             const QString& currentDate = line.at(1);
384             const QString& newDate = line.at(2);
385             if (SKGServices::stringToTime(newDate).date() > QDate::currentDate() && SKGServices::stringToTime(newDate).date() != SKGServices::stringToTime(currentDate).date()) {
386                 SKGRecurrentOperationObject recu(m_currentBankDocument, idRecu);
387                 QString name = recu.getDisplayName();
388 
389                 SKGAdvice ad;
390                 ad.setUUID("skgscheduledplugin_newdate|" % line.at(0) % ';' % newDate);
391                 ad.setPriority(4);
392                 ad.setShortMessage(i18nc("Advice on making the best (short)", "Scheduled operation '%1' not uptodate", name));
393                 ad.setLongMessage(i18nc("Advice on making the best (long)", "The scheduled operation '%1' does not have the date aligned with the last inserted operation (%2)", name, currentDate));
394                 autoCorrections.resize(0);
395                 {
396                     SKGAdvice::SKGAdviceAction a;
397                     a.Title = i18nc("Advice on making the best (action)", "Update the next scheduled operation date (%1)", newDate);
398                     a.IconName = QStringLiteral("system-run");
399                     a.IsRecommended = true;
400                     autoCorrections.push_back(a);
401                 }
402                 ad.setAutoCorrections(autoCorrections);
403                 output.push_back(ad);
404             }
405         }
406     }
407 
408     // Possible recurrent operations
409     if (!iIgnoredAdvice.contains(QStringLiteral("skgscheduledplugin_possibleschedule"))) {
410         SKGStringListList result;
411 
412         m_currentBankDocument->executeSelectSqliteOrder(QStringLiteral("SELECT op1.id, op1.t_displayname FROM v_operation_displayname op1 WHERE op1.id||'#'||op1.f_QUANTITY IN (SELECT op1.id||'#'||op2.f_QUANTITY FROM operation op1, v_operation_tmp1 op2 "
413                 "WHERE op1.rd_account_id=op2.rd_account_id AND op1.r_payee_id=op2.r_payee_id AND op1.rc_unit_id=op2.rc_unit_id "
414                 "AND op1.r_recurrentoperation_id=0 AND op1.d_date<>'0000-00-00' AND op1.d_date=date(op2.d_date, '+1 month') "
415                 "AND op1.d_date>(SELECT date('now', 'localtime','-2 month')) AND op2.d_date>(SELECT date('now', 'localtime','-3 month'))) "
416                 "AND op1.t_TRANSFER='N' "
417                 "AND NOT EXISTS (SELECT 1 FROM recurrentoperation ro, v_operation_tmp1 rop  WHERE ro.rd_operation_id=rop.id AND ro.i_period_increment=1 AND ro.t_period_unit='M' AND op1.rd_account_id=rop.rd_account_id AND op1.r_payee_id=rop.r_payee_id AND op1.f_QUANTITY=rop.f_QUANTITY AND op1.rc_unit_id=rop.rc_unit_id)"), result);
418         int nb = result.count();
419         SKGAdvice::SKGAdviceActionList autoCorrections;
420         autoCorrections.reserve(nb);
421         for (int i = 1; i < nb; ++i) {  // Ignore header
422             // Get parameters
423             const QStringList& line = result.at(i);
424             const QString& id = line.at(0);
425             const QString& name = line.at(1);
426 
427             SKGAdvice ad;
428             ad.setUUID("skgscheduledplugin_possibleschedule|" % id);
429             ad.setPriority(4);
430             ad.setShortMessage(i18nc("Advice on making the best (short)", "Possible schedule '%1'", name));
431             ad.setLongMessage(i18nc("Advice on making the best (long)", "The operation '%1' seems to be regularly created and could be scheduled", name));
432             autoCorrections.resize(0);
433             {
434                 SKGAdvice::SKGAdviceAction a;
435                 a.Title = i18nc("Advice on making the best (action)", "Monthly schedule the operation '%1'", name);
436                 a.IconName = icon();
437                 a.IsRecommended = false;
438                 autoCorrections.push_back(a);
439             }
440             ad.setAutoCorrections(autoCorrections);
441             output.push_back(ad);
442         }
443     }
444     m_counterAdvice++;
445     return output;
446 }
447 
executeAdviceCorrection(const QString & iAdviceIdentifier,int iSolution)448 SKGError SKGScheduledPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution)
449 {
450     SKGError err;
451     if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgscheduledplugin_notuptodate|"))) {
452         // Get parameters
453         QString parameters = iAdviceIdentifier.right(iAdviceIdentifier.length() - 31);
454         int pos = parameters.indexOf(';');
455         int idOper = SKGServices::stringToInt(parameters.left(pos));
456         double amount = SKGServices::stringToDouble(parameters.right(parameters.length() - 1 - pos));
457 
458         // Update the operation
459         {
460             SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Update scheduled operation"), err)
461             SKGOperationObject op(m_currentBankDocument, idOper);
462             SKGObjectBase::SKGListSKGObjectBase subOps;
463             IFOKDO(err, op.getSubOperations(subOps))
464 
465             if (subOps.count() == 1) {
466                 // Change the quantity of the sub operation
467                 SKGSubOperationObject so1(subOps.at(0));
468                 IFOKDO(err, so1.setQuantity(amount))
469                 IFOKDO(err, so1.save())
470             } else if (subOps.count() >= 1) {
471                 // Add a split
472                 SKGSubOperationObject so1;
473                 IFOKDO(err, op.addSubOperation(so1))
474                 IFOKDO(err, so1.setQuantity(amount - op.getCurrentAmount()))
475                 IFOKDO(err, so1.save())
476             }
477 
478             // Send message
479             IFOKDO(err, op.getDocument()->sendMessage(i18nc("An information to the user", "The amount of the scheduled operation of '%1' have been updated", op.getDisplayName()), SKGDocument::Hidden))
480         }
481 
482         // status bar
483         IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Scheduled operation updated.")))
484         else {
485             err.addError(ERR_FAIL, i18nc("Error message", "Update failed"));
486         }
487 
488         // Display error
489         SKGMainPanel::displayErrorMessage(err);
490 
491         return err;
492     }
493     if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgscheduledplugin_newdate|"))) {
494         // Get parameters
495         QString parameters = iAdviceIdentifier.right(iAdviceIdentifier.length() - 27);
496         int pos = parameters.indexOf(';');
497         int id = SKGServices::stringToInt(parameters.left(pos));
498         QString newDate = parameters.right(parameters.length() - 1 - pos);
499 
500         // Update the operation
501         {
502             SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Update scheduled operation"), err)
503             SKGRecurrentOperationObject rop(m_currentBankDocument, id);
504             IFOKDO(err, rop.setDate(SKGServices::stringToTime(newDate).date()))
505             IFOKDO(err, rop.save())
506 
507             // Send message
508             IFOKDO(err, rop.getDocument()->sendMessage(i18nc("An information to the user", "The date of the scheduled operation of '%1' have been updated", rop.getDisplayName()), SKGDocument::Hidden))
509         }
510 
511         // status bar
512         IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Scheduled operation updated.")))
513         else {
514             err.addError(ERR_FAIL, i18nc("Error message", "Update failed"));
515         }
516 
517         // Display error
518         SKGMainPanel::displayErrorMessage(err);
519 
520         return err;
521     }
522     if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgscheduledplugin_possibleschedule|"))) {
523         // Get parameters
524         int idOper = SKGServices::stringToInt(iAdviceIdentifier.right(iAdviceIdentifier.length() - 36));
525 
526         // Update the operation
527         {
528             SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Scheduled operation"), err)
529             SKGOperationObject op(m_currentBankDocument, idOper);
530             SKGRecurrentOperationObject rop;
531             err = scheduleOperation(op, rop);
532             IFOKDO(err, rop.setPeriodUnit(SKGRecurrentOperationObject::MONTH))
533             IFOKDO(err, rop.setPeriodIncrement(1))
534             IFOKDO(err, rop.setDate(op.getDate()))
535             IFOKDO(err, rop.setDate(rop.getNextDate()))
536             IFOKDO(err, rop.save())
537 
538             // Send message
539             IFOKDO(err, rop.getDocument()->sendMessage(i18nc("An information to the user", "The scheduled operation of '%1' have been added", rop.getDisplayName()), SKGDocument::Hidden))
540 
541             m_counterAdvice = 0;  // To force the update
542         }
543 
544         // status bar
545         IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Operation scheduled.")))
546         else {
547             err.addError(ERR_FAIL, i18nc("Error message", "Schedule failed"));
548         }
549 
550         // Display error
551         SKGMainPanel::displayErrorMessage(err);
552 
553         return err;
554     }
555 
556 
557     return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution);
558 }
559 
560 #include <skgscheduledplugin.moc>
561