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

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  * A skrooge plugin to manage budgets
0008  *
0009  * @author Stephane MANKOWSKI
0010  */
0011 #include "skgbudgetplugin.h"
0012 
0013 #include <kaboutdata.h>
0014 #include <kactioncollection.h>
0015 #include <klocalizedstring.h>
0016 #include <kpluginfactory.h>
0017 #include <kstandardaction.h>
0018 
0019 #include "skgbudgetobject.h"
0020 #include "skgbudgetpluginwidget.h"
0021 #include "skgbudgetruleobject.h"
0022 #include "skgdocumentbank.h"
0023 #include "skgmainpanel.h"
0024 #include "skgtraces.h"
0025 #include "skgtransactionmng.h"
0026 
0027 /**
0028  * This plugin factory.
0029  */
0030 K_PLUGIN_CLASS_WITH_JSON(SKGBudgetPlugin, "metadata.json")
0031 
0032 SKGBudgetPlugin::SKGBudgetPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) : SKGInterfacePlugin(iParent), m_currentBankDocument(nullptr)
0033 {
0034     Q_UNUSED(iWidget)
0035     SKGTRACEINFUNC(10)
0036 }
0037 
0038 SKGBudgetPlugin::~SKGBudgetPlugin()
0039 {
0040     SKGTRACEINFUNC(10)
0041     m_currentBankDocument = nullptr;
0042 }
0043 
0044 bool SKGBudgetPlugin::setupActions(SKGDocument* iDocument)
0045 {
0046     SKGTRACEINFUNC(10)
0047 
0048     m_currentBankDocument = qobject_cast<SKGDocumentBank*>(iDocument);
0049     if (m_currentBankDocument == nullptr) {
0050         return false;
0051     }
0052 
0053     setComponentName(QStringLiteral("skrooge_budget"), title());
0054     setXMLFile(QStringLiteral("skrooge_budget.rc"));
0055 
0056     // Create yours actions here
0057     // -----------
0058     QStringList overlayrun;
0059     overlayrun.push_back(QStringLiteral("system-run"));
0060     auto act = new QAction(SKGServices::fromTheme(icon(), overlayrun), i18nc("Verb", "Process budget rules"), this);
0061     connect(act, &QAction::triggered, this, &SKGBudgetPlugin::onProcessRules);
0062     registerGlobalAction(QStringLiteral("tool_process_budget_rules"), act);
0063     return true;
0064 }
0065 
0066 SKGTabPage* SKGBudgetPlugin::getWidget()
0067 {
0068     SKGTRACEINFUNC(10)
0069     return new SKGBudgetPluginWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument);
0070 }
0071 
0072 QString SKGBudgetPlugin::title() const
0073 {
0074     return i18nc("The title", "Budget");
0075 }
0076 
0077 QString SKGBudgetPlugin::icon() const
0078 {
0079     return QStringLiteral("view-calendar-whatsnext");
0080 }
0081 
0082 QString SKGBudgetPlugin::toolTip() const
0083 {
0084     return i18nc("The tool tip", "Budget");
0085 }
0086 
0087 int SKGBudgetPlugin::getOrder() const
0088 {
0089     return 32;
0090 }
0091 
0092 QStringList SKGBudgetPlugin::tips() const
0093 {
0094     QStringList output;
0095     return output;
0096 }
0097 
0098 bool SKGBudgetPlugin::isInPagesChooser() const
0099 {
0100     return true;
0101 }
0102 
0103 SKGAdviceList SKGBudgetPlugin::advice(const QStringList& iIgnoredAdvice)
0104 {
0105     SKGTRACEINFUNC(10)
0106     SKGAdviceList output;
0107     QString month = QDate::currentDate().toString(QStringLiteral("yyyy-MM"));
0108     QString year = QDate::currentDate().toString(QStringLiteral("yyyy"));
0109 
0110     // Alarms
0111     if (!iIgnoredAdvice.contains(QStringLiteral("skgbudgetplugin_alarm"))) {
0112         SKGObjectBase::SKGListSKGObjectBase budgets;
0113         // Query is done on v_budget_display because v_budget_display is faster than v_budget
0114         SKGError err = m_currentBankDocument->getObjects(QStringLiteral("v_budget_display"), "(f_CURRENTAMOUNT/f_budgeted_modified>0.8 OR (f_CURRENTAMOUNT<0 AND f_budgeted_modified>0)) AND ABS(f_CURRENTAMOUNT-f_budgeted_modified)>" % SKGServices::doubleToString(EPSILON) % " AND f_budgeted<0 AND (t_PERIOD='" % month % "' OR t_PERIOD='" % year % "');", budgets);
0115         int nb = budgets.count();
0116         if (nb != 0) {
0117             SKGServices::SKGUnitInfo primary = m_currentBankDocument->getPrimaryUnit();
0118             SKGAdvice::SKGAdviceActionList autoCorrections;
0119             for (int i = 0; !err && i < nb; ++i) {
0120                 SKGBudgetObject budget(budgets.at(i));
0121                 double f_CURRENTAMOUNT = SKGServices::stringToDouble(budget.getAttribute(QStringLiteral("f_CURRENTAMOUNT")));
0122                 double f_budgeted_modified = SKGServices::stringToDouble(budget.getAttribute(QStringLiteral("f_budgeted_modified")));
0123 
0124                 SKGAdvice ad;
0125                 ad.setUUID("skgbudgetplugin_alarm|" % SKGServices::intToString(budget.getID()));
0126                 ad.setPriority((f_CURRENTAMOUNT < f_budgeted_modified ? 9 : 6));
0127                 ad.setShortMessage(i18nc("Advice on making the best (short)", "Budget alarm for '%1'", budget.getAttribute(QStringLiteral("t_CATEGORY"))));
0128                 ad.setLongMessage(i18nc("Advice on making the best (long)", "Take care to your budget.<br> %1.<br>%2 / %3",
0129                                         budget.getAttribute(QStringLiteral("t_CATEGORY")),
0130                                         m_currentBankDocument->formatMoney(f_CURRENTAMOUNT, primary),
0131                                         m_currentBankDocument->formatMoney(f_budgeted_modified, primary)));
0132                 autoCorrections.resize(0);
0133                 {
0134                     SKGAdvice::SKGAdviceAction a;
0135                     a.Title = i18nc("Advice on making the best (action)", "Open transactions corresponding to this budget");
0136                     a.IconName = QStringLiteral("quickopen");
0137                     a.IsRecommended = false;
0138                     autoCorrections.push_back(a);
0139                 }
0140                 ad.setAutoCorrections(autoCorrections);
0141                 output.push_back(ad);
0142             }
0143         }
0144     }
0145 
0146     if (!iIgnoredAdvice.contains(QStringLiteral("skgbudgetplugin_oldprocessing"))) {
0147         QString lastBudgetProcessingDate = m_currentBankDocument->getParameter(QStringLiteral("SKG_LAST_BUDGET_PROCESSING"));
0148         if (lastBudgetProcessingDate.isEmpty() ||  SKGServices::stringToTime(lastBudgetProcessingDate).date() < QDate::currentDate().addMonths(-1)) {  // Ignore header
0149             bool exist = false;
0150             m_currentBankDocument->existObjects(QStringLiteral("budgetrule"), QLatin1String(""), exist);
0151             if (exist) {
0152                 SKGAdvice ad;
0153                 ad.setUUID(QStringLiteral("skgbudgetplugin_oldprocessing"));
0154                 ad.setPriority(3);
0155                 ad.setShortMessage(i18nc("Advice on making the best (short)", "Old budget treatment"));
0156                 ad.setLongMessage(i18nc("Advice on making the best (long)", "The budget has not been treated for at least one month."));
0157                 SKGAdvice::SKGAdviceActionList autoCorrections;
0158                 {
0159                     SKGAdvice::SKGAdviceAction a;
0160                     a.Title = QStringLiteral("skg://tool_process_budget_rules");
0161                     a.IsRecommended = true;
0162                     autoCorrections.push_back(a);
0163                 }
0164                 ad.setAutoCorrections(autoCorrections);
0165                 output.push_back(ad);
0166             }
0167         }
0168     }
0169 
0170     return output;
0171 }
0172 SKGError SKGBudgetPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution)
0173 {
0174     if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgbudgetplugin_alarm|"))) {
0175         // Get parameters
0176         QString id = iAdviceIdentifier.right(iAdviceIdentifier.length() - 22);
0177         SKGBudgetObject budget(m_currentBankDocument, SKGServices::stringToInt(id));
0178         budget.load();
0179 
0180         QAction* act = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("open"));
0181         if (act != nullptr) {
0182             act->setData(budget.getUniqueID());
0183             act->trigger();
0184         }
0185 
0186         return SKGError();
0187     }
0188     return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution);
0189 }
0190 
0191 void SKGBudgetPlugin::onProcessRules()
0192 {
0193     SKGError err;
0194     _SKGTRACEINFUNCRC(10, err)
0195 
0196     {
0197         SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Process budget rules"), err)
0198         err = SKGBudgetRuleObject::processAllRules(m_currentBankDocument);
0199     }
0200     // status bar
0201     IFOK(err) {
0202         err = SKGError(0, i18nc("Successful message after an user action", "Budget rules processed"));
0203     } else {
0204         err.addError(ERR_FAIL, i18nc("Error message", "Budget rules failed"));
0205     }
0206 
0207     // Display error
0208     SKGMainPanel::displayErrorMessage(err);
0209 }
0210 
0211 #include <skgbudgetplugin.moc>