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 "skgbudgetpluginwidget.h"
0012 
0013 #include <qdom.h>
0014 #include <qevent.h>
0015 
0016 #include "skgbudgetdelegate.h"
0017 #include "skgbudgetobject.h"
0018 #include "skgbudgetruleobject.h"
0019 #include "skgcategoryobject.h"
0020 #include "skgdocument.h"
0021 #include "skgmainpanel.h"
0022 #include "skgobjectmodel.h"
0023 #include "skgtraces.h"
0024 #include "skgtransactionmng.h"
0025 
0026 SKGBudgetPluginWidget::SKGBudgetPluginWidget(QWidget* iParent, SKGDocument* iDocument)
0027     : SKGTabPage(iParent, iDocument), m_objectModel(nullptr)
0028 {
0029     SKGTRACEINFUNC(1)
0030     if (iDocument == nullptr) {
0031         return;
0032     }
0033 
0034     ui.setupUi(this);
0035 
0036     ui.kUseScheduledOperation->hide();
0037 
0038     ui.kTopBtn->setIcon(SKGServices::fromTheme(QStringLiteral("arrow-up-double")));
0039     ui.kUpBtn->setIcon(SKGServices::fromTheme(QStringLiteral("arrow-up")));
0040     ui.kDownBtn->setIcon(SKGServices::fromTheme(QStringLiteral("arrow-down")));
0041     ui.kBottomBtn->setIcon(SKGServices::fromTheme(QStringLiteral("arrow-down-double")));
0042 
0043     ui.kPeriodLbl->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_period"))));
0044     ui.kYearLbl->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("i_year"))));
0045     ui.kMonthLbl->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("i_month"))));
0046     ui.kAmountLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("f_value"))));
0047     ui.kCategoryLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_category"))));
0048     ui.kYearCheck->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("i_year"))));
0049     ui.kMonthCheck->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("i_month"))));
0050     ui.kCategoryCheck->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_category"))));
0051     ui.kPeriodLbl2->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_period"))));
0052     ui.kAmountLabel2->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("f_value"))));
0053     ui.kCategoryTransferCheck->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_category"))));
0054 
0055     ui.kYear->setValue(QDate::currentDate().year());
0056     ui.kYearAutoBase->setValue(QDate::currentDate().year());
0057     ui.kMonth->setValue(QDate::currentDate().month());
0058     ui.kPeriod->addItem(i18nc("Noun, how to define a budget period", "Monthly"));
0059     ui.kPeriod->addItem(i18nc("Noun, how to define a budget period", "Yearly"));
0060     ui.kPeriod->addItem(i18nc("Noun, how to define a budget period", "Individual"));
0061 
0062 
0063     ui.kView->getShowWidget()->addItem(QStringLiteral("all"), i18nc("Noun, budget items to display", "All"), QLatin1String(""),
0064                                        QLatin1String(""),
0065                                        QStringLiteral("current;currentYear;currentMonth;previousYear;previousMonth"),
0066                                        QStringLiteral("none"), QLatin1String(""), QLatin1String(""),
0067                                        Qt::META + Qt::Key_A);
0068     ui.kView->getShowWidget()->addItem(QStringLiteral("none"), i18nc("Noun, budget items to display", "None"), QLatin1String(""),
0069                                        QStringLiteral("1=0"),
0070                                        QLatin1String(""),  // Check when checked
0071                                        QStringLiteral("all;current;currentYear;currentMonth;previousYear;previousMonth"),       // Uncheck when checked
0072                                        QLatin1String(""),       // Check when unchecked
0073                                        QStringLiteral("all"),    // Uncheck when unchecked
0074                                        Qt::META + Qt::Key_0);
0075     ui.kView->getShowWidget()->addSeparator();
0076     ui.kView->getShowWidget()->addItem(QStringLiteral("current"), i18nc("Noun, budget items to display", "Current"), QStringLiteral("view-calendar-whatsnext"),
0077                                        QStringLiteral("t_PERIOD>=STRFTIME('%Y-%m', date('now', 'localtime')) OR t_PERIOD=STRFTIME('%Y', date('now', 'localtime'))"),
0078                                        QStringLiteral("currentMonth"),  // Check when checked
0079                                        QStringLiteral("none"),       // Uncheck when checked
0080                                        QLatin1String(""),       // Check when unchecked
0081                                        QStringLiteral("all"),    // Uncheck when unchecked
0082                                        Qt::META + Qt::Key_1);
0083     ui.kView->getShowWidget()->addSeparator();
0084     ui.kView->getShowWidget()->addItem(QStringLiteral("currentYear"), i18nc("Noun, budget items to display", "Current year"), QStringLiteral("view-calendar-month"),
0085                                        QStringLiteral("t_PERIOD LIKE STRFTIME('%Y', date('now', 'localtime'))||'%'"),
0086                                        QLatin1String(""),
0087                                        QStringLiteral("none"),
0088                                        QLatin1String(""),
0089                                        QStringLiteral("all"),
0090                                        Qt::META + Qt::Key_3);
0091     ui.kView->getShowWidget()->addItem(QStringLiteral("currentMonth"), i18nc("Noun, budget items to display", "Current month"), QStringLiteral("view-calendar-week"),
0092                                        QStringLiteral("t_PERIOD=STRFTIME('%Y-%m', date('now', 'localtime'))"),
0093                                        QLatin1String(""),
0094                                        QStringLiteral("none"),
0095                                        QLatin1String(""),
0096                                        QStringLiteral("all;current"),
0097                                        Qt::META + Qt::Key_2);
0098     ui.kView->getShowWidget()->addSeparator();
0099     ui.kView->getShowWidget()->addItem(QStringLiteral("previousYear"), i18nc("Noun, budget items to display", "Previous year"), QStringLiteral("view-calendar-month"),
0100                                        QStringLiteral("t_PERIOD LIKE STRFTIME('%Y', date('now', 'localtime','start of year','-1 day'))||'%'"),
0101                                        QLatin1String(""),
0102                                        QStringLiteral("none"),
0103                                        QLatin1String(""),
0104                                        QStringLiteral("all"),
0105                                        Qt::META + Qt::Key_5);
0106     ui.kView->getShowWidget()->addItem(QStringLiteral("previousMonth"), i18nc("Noun, budget items to display", "Previous month"), QStringLiteral("view-calendar-week"),
0107                                        QStringLiteral("t_PERIOD=STRFTIME('%Y-%m', date('now', 'localtime','start of month','-1 day'))"),
0108                                        QLatin1String(""),
0109                                        QStringLiteral("none"),
0110                                        QLatin1String(""),
0111                                        QStringLiteral("all"),
0112                                        Qt::META + Qt::Key_4);
0113     ui.kView->getShowWidget()->setDefaultState(QStringLiteral("current;currentMonth"));
0114 
0115     m_timer.setSingleShot(true);
0116     connect(&m_timer, &QTimer::timeout, this, &SKGBudgetPluginWidget::refreshInfoZone, Qt::QueuedConnection);
0117 
0118     ui.kConditionCmb->addItem(i18nc("Noun, condition item to a apply a transfer of budget", "All"), static_cast<int>(SKGBudgetRuleObject::ALL));
0119     ui.kConditionCmb->addItem(i18nc("Noun, condition item to a apply a transfer of budget", "Negative"), static_cast<int>(SKGBudgetRuleObject::NEGATIVE));
0120     ui.kConditionCmb->addItem(i18nc("Noun, condition item to a apply a transfer of budget", "Positive"), static_cast<int>(SKGBudgetRuleObject::POSITIVE));
0121 
0122     ui.kModeCmb->addItem(i18nc("Noun, mode item to a apply a transfer of budget", "Next"), static_cast<int>(SKGBudgetRuleObject::NEXT));
0123     ui.kModeCmb->addItem(i18nc("Noun, mode item to a apply a transfer of budget", "Current"), static_cast<int>(SKGBudgetRuleObject::CURRENT));
0124     ui.kModeCmb->addItem(i18nc("Noun, mode item to a apply a transfer of budget", "Current year"), static_cast<int>(SKGBudgetRuleObject::YEAR));
0125 
0126     ui.kView->getView()->setItemDelegate(new SKGBudgetDelegate(ui.kView->getView(), getDocument()));
0127 
0128     auto* doc = qobject_cast<SKGDocumentBank*>(getDocument());
0129     if (doc != nullptr) {
0130         ui.kUnitCmb->addItem(QStringLiteral("%"));
0131         ui.kUnitCmb->addItem(doc->getPrimaryUnit().Symbol);
0132         ui.kUnit->setText(doc->getPrimaryUnit().Symbol);
0133 
0134         // Bind transaction view
0135         m_objectModel = new SKGObjectModel(doc, QStringLiteral("v_budget_display"), QStringLiteral("1=0"), this, QLatin1String(""), false);
0136         ui.kView->setModel(m_objectModel);
0137         ui.kSortButton->setVisible(false);
0138     }
0139 
0140     connect(ui.kView->getView(), &SKGTreeView::doubleClicked, SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("open")).data(), &QAction::trigger);
0141     connect(ui.kView->getView(), &SKGTreeView::selectionChangedDelayed, this, [ = ] {this->onSelectionChanged();});
0142 
0143     // Add Standard KDE Icons to buttons to Transactions
0144     ui.kModifyBtn->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok")));
0145     ui.kAddBtn->setIcon(SKGServices::fromTheme(QStringLiteral("list-add")));
0146     QAction* processAction = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("tool_process_budget_rules"));
0147     if (processAction != nullptr) {
0148         ui.kProcessBtn->setIcon(processAction->icon());
0149         connect(ui.kProcessBtn, &QPushButton::clicked, processAction, &QAction::trigger);
0150     }
0151 
0152     {
0153         SKGWidgetSelector::SKGListQWidget list;
0154         list.push_back(ui.SKGManualSection);
0155         list.push_back(ui.SKGEditionButtonsSection);
0156         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("user-properties")), i18n("Manual"), i18n("Display the edit panel for standard budget"), list);
0157     }
0158     {
0159         SKGWidgetSelector::SKGListQWidget list;
0160         list.push_back(ui.SKGAutoSection);
0161         list.push_back(ui.SKGEditionButtonsSection);
0162         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("games-solve")), i18n("Auto"), i18n("Display the edit panel for automatic budgets"), list);
0163     }
0164     {
0165         SKGWidgetSelector::SKGListQWidget list;
0166         list.push_back(ui.SKGRuleSection);
0167         list.push_back(ui.SKGEditionButtonsSection);
0168         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("run-build")), i18n("Rules"), i18n("Display the edit panel for rules"), list);
0169     }
0170     connect(ui.kWidgetSelector, &SKGWidgetSelector::selectedModeChanged, this, &SKGBudgetPluginWidget::onBtnModeClicked, Qt::QueuedConnection);
0171     connect(ui.kAddBtn, &QPushButton::clicked, this, &SKGBudgetPluginWidget::onAddClicked);
0172     connect(ui.kModifyBtn, &QPushButton::clicked, this, &SKGBudgetPluginWidget::onUpdateClicked);
0173     connect(ui.kAmountEdit, &SKGCalculatorEdit::textChanged, this, &SKGBudgetPluginWidget::onCreatorModified);
0174     connect(ui.kPeriod, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGBudgetPluginWidget::onCreatorModified);
0175     connect(ui.kYearCheck, &QCheckBox::toggled, ui.kYearRule, &QSpinBox::setEnabled);
0176     connect(ui.kMonthCheck, &QCheckBox::toggled, ui.kMonthRule, &QSpinBox::setEnabled);
0177     connect(ui.kCategoryCheck, &QCheckBox::toggled, ui.kCategoryRule, &SKGComboBox::setEnabled);
0178     connect(ui.kCategoryTransferCheck, &QCheckBox::toggled, ui.kCategoryTransfer, &SKGComboBox::setEnabled);
0179     connect(ui.kModeCmb, static_cast<void (SKGComboBox::*)(int)>(&SKGComboBox::currentIndexChanged), this, &SKGBudgetPluginWidget::onCreatorModified);
0180     connect(ui.kAutoBudgetCheck, &QCheckBox::toggled, ui.kYearAutoBase, &QSpinBox::setEnabled);
0181     connect(ui.kAutoBudgetCheck, &QCheckBox::toggled, ui.kRemovePrevious, &QCheckBox::setEnabled);
0182     connect(ui.kAutoBudgetCheck, &QCheckBox::toggled, ui.kUseScheduledOperation, &QCheckBox::setEnabled);
0183 
0184     connect(ui.kTopBtn, &QToolButton::clicked, this, &SKGBudgetPluginWidget::onTop);
0185     connect(ui.kUpBtn, &QToolButton::clicked, this, &SKGBudgetPluginWidget::onUp);
0186     connect(ui.kDownBtn, &QToolButton::clicked, this, &SKGBudgetPluginWidget::onDown);
0187     connect(ui.kBottomBtn, &QToolButton::clicked, this, &SKGBudgetPluginWidget::onBottom);
0188 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
0189     connect(ui.kYearAuto, static_cast<void (QSpinBox::*)(const QString&)>(&QSpinBox::valueChanged), this, [ = ](const QString & text) {
0190         ui.kRemovePrevious->setText(i18nc("Option", "Remove existing budgets for %1", text));
0191     });
0192 #else
0193     connect(ui.kYearAuto, &QSpinBox::textChanged, this, [ = ](const QString & text) {
0194         ui.kRemovePrevious->setText(i18nc("Option", "Remove existing budgets for %1", text));
0195     });
0196 #endif
0197     ui.kYearAuto->setValue(QDate::currentDate().year());
0198 
0199     ui.kWidgetSelector->setSelectedMode(0);
0200 
0201     // Set Event filters to catch CTRL+ENTER or SHIFT+ENTER
0202     this->installEventFilter(this);
0203 
0204     // Refresh
0205     connect(getDocument(), &SKGDocument::tableModified, this, &SKGBudgetPluginWidget::dataModified, Qt::QueuedConnection);
0206     dataModified(QLatin1String(""), 0);
0207 }
0208 
0209 SKGBudgetPluginWidget::~SKGBudgetPluginWidget()
0210 {
0211     SKGTRACEINFUNC(1)
0212     m_objectModel = nullptr;
0213 }
0214 
0215 bool SKGBudgetPluginWidget::eventFilter(QObject* iObject, QEvent* iEvent)
0216 {
0217     if ((iEvent != nullptr) && iEvent->type() == QEvent::KeyPress) {
0218         auto* keyEvent = dynamic_cast<QKeyEvent*>(iEvent);
0219         if (keyEvent && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && iObject == this) {
0220             if ((QApplication::keyboardModifiers() & Qt::ControlModifier) != 0u && ui.kAddBtn->isEnabled()) {
0221                 ui.kAddBtn->click();
0222             } else if ((QApplication::keyboardModifiers() &Qt::ShiftModifier) != 0u && ui.kModifyBtn->isEnabled()) {
0223                 ui.kModifyBtn->click();
0224             }
0225         }
0226     }
0227 
0228     return SKGTabPage::eventFilter(iObject, iEvent);
0229 }
0230 
0231 QString SKGBudgetPluginWidget::getState()
0232 {
0233     SKGTRACEINFUNC(10)
0234     QDomDocument doc(QStringLiteral("SKGML"));
0235     QDomElement root = doc.createElement(QStringLiteral("parameters"));
0236     doc.appendChild(root);
0237 
0238     root.setAttribute(QStringLiteral("currentPage"), SKGServices::intToString(ui.kWidgetSelector->getSelectedMode()));
0239     if ((m_objectModel != nullptr) && m_objectModel->getRealTable() == QStringLiteral("budget")) {
0240         root.setAttribute(QStringLiteral("view"), ui.kView->getState());
0241         root.setAttribute(QStringLiteral("viewRule"), m_viewRule);
0242     } else {
0243         root.setAttribute(QStringLiteral("view"), m_viewBudget);
0244         root.setAttribute(QStringLiteral("viewRule"), ui.kView->getState());
0245     }
0246 
0247     return doc.toString();
0248 }
0249 
0250 void SKGBudgetPluginWidget::setState(const QString& iState)
0251 {
0252     SKGTRACEINFUNC(10)
0253     QDomDocument doc(QStringLiteral("SKGML"));
0254     doc.setContent(iState);
0255     QDomElement root = doc.documentElement();
0256 
0257     QString currentPage = root.attribute(QStringLiteral("currentPage"));
0258     if (currentPage.isEmpty()) {
0259         currentPage = '0';
0260     }
0261 
0262     ui.kWidgetSelector->setSelectedMode(SKGServices::stringToInt(currentPage));
0263 
0264     m_viewBudget = root.attribute(QStringLiteral("view"));
0265     m_viewRule = root.attribute(QStringLiteral("viewRule"));
0266     if ((m_objectModel != nullptr) && m_objectModel->getRealTable() == QStringLiteral("budget")) {
0267         ui.kView->setState(m_viewBudget);
0268     } else {
0269         ui.kView->setState(m_viewRule);
0270     }
0271 }
0272 
0273 QString SKGBudgetPluginWidget::getDefaultStateAttribute()
0274 {
0275     return QStringLiteral("SKGBUDGET_DEFAULT_PARAMETERS");
0276 }
0277 
0278 QWidget* SKGBudgetPluginWidget::mainWidget()
0279 {
0280     return ui.kView->getView();
0281 }
0282 
0283 void SKGBudgetPluginWidget::refresh()
0284 {
0285     SKGTRACEINFUNC(1)
0286 
0287     QSqlDatabase* db = getDocument()->getMainDatabase();
0288     setEnabled(db != nullptr);
0289     if (db != nullptr) {
0290         // Refresh yours widgets here
0291     }
0292 }
0293 
0294 void SKGBudgetPluginWidget::dataModified(const QString& iTableName, int iIdTransaction, bool iLightTransaction)
0295 {
0296     SKGTRACEINFUNC(10)
0297     Q_UNUSED(iIdTransaction)
0298 
0299     // Refresh widgets
0300     if (iTableName == QStringLiteral("budget") || iTableName.isEmpty()) {
0301         // Refresh info area
0302         m_timer.start(300);
0303     }
0304 
0305     if (!iLightTransaction) {
0306         if (iTableName == QStringLiteral("category") || iTableName.isEmpty()) {
0307             // Set type category
0308             SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kCategoryEdit << ui.kCategoryRule << ui.kCategoryTransfer, getDocument(), QStringLiteral("category"), QStringLiteral("t_fullname"), QLatin1String(""));
0309         }
0310     }
0311 }
0312 
0313 void SKGBudgetPluginWidget::onBtnModeClicked(int mode)
0314 {
0315     SKGTRACEINFUNC(10)
0316     if (m_objectModel == nullptr) {
0317         return;
0318     }
0319 
0320     if (mode == 2 && m_objectModel->getTable() != QStringLiteral("v_budgetrule_display")) {
0321         ui.kView->getShowWidget()->setEnabled(false);
0322         m_viewBudget = ui.kView->getState();
0323         m_objectModel->setFilter(QLatin1String(""));
0324         m_objectModel->setTable(QStringLiteral("v_budgetrule_display"));
0325         ui.kSortButton->setVisible(true);
0326         ui.kView->setState(m_viewRule);
0327     } else if (mode != 2 && m_objectModel->getTable() != QStringLiteral("v_budget_display")) {
0328         ui.kView->getShowWidget()->setEnabled(true);
0329         m_viewRule = ui.kView->getState();
0330         m_objectModel->setTable(QStringLiteral("v_budget_display"));
0331         ui.kSortButton->setVisible(false);
0332         ui.kView->setState(m_viewBudget);
0333     }
0334 
0335     onCreatorModified();
0336 }
0337 
0338 void SKGBudgetPluginWidget::onAddClicked()
0339 {
0340     SKGError err;
0341     _SKGTRACEINFUNCRC(10, err)
0342 
0343     if (ui.kWidgetSelector->getSelectedMode() == 2) {
0344         // Creation of a rule
0345         QStringList uniqueIDs;
0346         {
0347             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget rule creation"), err)
0348             SKGBudgetRuleObject budgetRule(getDocument());
0349             IFOKDO(err, updateBudgetRule(budgetRule))
0350             IFOKDO(err, budgetRule.setOrder(-1))
0351             IFOKDO(err, budgetRule.save())
0352             uniqueIDs.push_back(budgetRule.getUniqueID());
0353 
0354             // Send message
0355             IFOKDO(err, budgetRule.getDocument()->sendMessage(i18nc("An information to the user", "The budget rule '%1' has been added", budgetRule.getDisplayName()), SKGDocument::Hidden))
0356         }
0357         // status bar
0358         IFOK(err) {
0359             err = SKGError(0, i18nc("Successful message after an user action", "Budget rule created"));
0360             ui.kView->getView()->selectObjects(uniqueIDs);
0361         } else {
0362             err.addError(ERR_FAIL, i18nc("Error message", "Budget rule creation failed"));
0363         }
0364     } else {
0365         // Creation of a budget
0366         QStringList uniqueIDs;
0367         {
0368             SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget creation"), err, 2)
0369             if (ui.kWidgetSelector->getSelectedMode() == 0) {
0370                 // Manual creation
0371                 int mode = ui.kPeriod->currentIndex();
0372                 if (mode == 0) {  // Monthly
0373                     for (int m = 1; !err && m <= 12; ++m) {
0374                         SKGBudgetObject budget(getDocument());
0375                         IFOKDO(err, updateBudget(budget, m))
0376 
0377                         // Send message
0378                         IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget '%1' has been added", budget.getDisplayName()), SKGDocument::Hidden))
0379 
0380                         uniqueIDs.push_back(budget.getUniqueID());
0381                     }
0382                 } else if (mode == 1) {  // Yearly
0383                     SKGBudgetObject budget(getDocument());
0384 
0385                     IFOKDO(err, updateBudget(budget, 0))
0386 
0387                     // Send message
0388                     IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget '%1' has been added", budget.getDisplayName()), SKGDocument::Hidden))
0389 
0390                     uniqueIDs.push_back(budget.getUniqueID());
0391                 } else {  // Individual
0392                     SKGBudgetObject budget(getDocument());
0393                     IFOKDO(err, updateBudget(budget))
0394 
0395                     // Send message
0396                     IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget '%1' has been added", budget.getDisplayName()), SKGDocument::Hidden))
0397 
0398                     uniqueIDs.push_back(budget.getUniqueID());
0399                 }
0400             } else {
0401                 // Automatic creation
0402                 if (ui.kAutoBudgetCheck->isChecked()) {
0403                     err = SKGBudgetObject::createAutomaticBudget(qobject_cast<SKGDocumentBank*>(getDocument()),
0404                             ui.kYearAuto->value(),
0405                             ui.kYearAutoBase->value(),
0406                             ui.kUseScheduledOperation->isChecked(),
0407                             ui.kRemovePrevious->isChecked());
0408                 }
0409                 IFOKDO(err, getDocument()->stepForward(1))
0410 
0411                 IFOKDO(err, SKGBudgetObject::balanceBudget(qobject_cast<SKGDocumentBank*>(getDocument()),
0412                         ui.kYearAuto->value(), (ui.kBalancingMonthly->isChecked() ? 0 : -1),
0413                         ui.kBalancingAnnual->isChecked()));
0414                 IFOKDO(err, getDocument()->stepForward(2))
0415             }
0416         }
0417 
0418         // status bar
0419         IFOK(err) {
0420             err = SKGError(0, i18nc("Successful message after an user action", "Budget created"));
0421             ui.kView->getView()->selectObjects(uniqueIDs);
0422         } else {
0423             err.addError(ERR_FAIL, i18nc("Error message", "Budget creation failed"));
0424         }
0425     }
0426 
0427     // Display error
0428     SKGMainPanel::displayErrorMessage(err, true);
0429 }
0430 
0431 void SKGBudgetPluginWidget::onUpdateClicked()
0432 {
0433     SKGError err;
0434     SKGTRACEINFUNCRC(10, err)
0435     // Get Selection
0436     SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects();
0437     if (ui.kWidgetSelector->getSelectedMode() == 2) {
0438         {
0439             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget rule update"), err)
0440             SKGBudgetRuleObject rule(selection.at(0));
0441             IFOKDO(err, updateBudgetRule(rule))
0442 
0443             // Send message
0444             IFOKDO(err, rule.getDocument()->sendMessage(i18nc("An information to the user", "The budget rule '%1' has been updated", rule.getDisplayName()), SKGDocument::Hidden))
0445         }
0446 
0447         // status bar
0448         IFOK(err) {
0449             err = SKGError(0, i18nc("Successful message after an user action", "Budget rule updated"));
0450         } else {
0451             err.addError(ERR_FAIL, i18nc("Error message",  "Budget rule update failed"));
0452         }
0453     } else {
0454         {
0455             int nb = selection.count();
0456             SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget update"), err, nb)
0457             for (int i = 0; !err && i < nb; ++i) {
0458                 SKGBudgetObject budget(selection.at(i));
0459                 int mode = ui.kPeriod->currentIndex();
0460                 if (mode == 1) {  // Yearly
0461                     err = updateBudget(budget, 0);
0462                 } else {  // Individual
0463                     err = updateBudget(budget);
0464                 }
0465 
0466                 IFOKDO(err, getDocument()->stepForward(i + 1))
0467             }
0468         }
0469 
0470         // status bar
0471         IFOK(err) {
0472             err = SKGError(0, i18nc("Successful message after an user action", "Budget updated"));
0473         } else {
0474             err.addError(ERR_FAIL, i18nc("Error message",  "Budget update failed"));
0475         }
0476     }
0477 
0478 
0479     // Display error
0480     SKGMainPanel::displayErrorMessage(err, true);
0481 
0482     // Set focus on table
0483     ui.kView->getView()->setFocus();
0484 }
0485 
0486 SKGError SKGBudgetPluginWidget::updateBudget(SKGBudgetObject& iBudget, int iMonth)
0487 {
0488     SKGError err;
0489     if (!err && ui.kYear->isEnabled()) {
0490         err = iBudget.setYear(ui.kYear->value());
0491     }
0492     if (!err && ui.kMonth->isEnabled()) {
0493         err = iBudget.setMonth(iMonth != -1 ? iMonth : ui.kMonth->value());
0494     }
0495 
0496     SKGCategoryObject cat;
0497     QString catName = ui.kCategoryEdit->text().trimmed();
0498     IFOKDO(err, SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), catName, cat, true))
0499     IFOKDO(err, iBudget.setCategory(cat))
0500     IFOKDO(err, iBudget.enableSubCategoriesInclusion(ui.kIncludingSubCategories->isChecked()))
0501 
0502     double val = ui.kAmountEdit->value();
0503     // Is the sign forced ?
0504     if (ui.kAmountEdit->sign() == 0) {
0505         // No
0506         SKGObjectBase cat2(cat.getDocument(), QStringLiteral("v_category_display"), cat.getID());
0507 
0508         // Are we able to find to sign with the category ?
0509         if (cat2.getAttribute(QStringLiteral("t_TYPEEXPENSE")) == QStringLiteral("-")) {
0510             val = -val;
0511         }
0512     }
0513     IFOKDO(err, iBudget.setBudgetedAmount(val))
0514 
0515     IFOKDO(err, iBudget.save())
0516     return err;
0517 }
0518 
0519 SKGError SKGBudgetPluginWidget::updateBudgetRule(SKGBudgetRuleObject& iRule)
0520 {
0521     SKGError err;
0522     SKGCategoryObject cat;
0523     QString catName = ui.kCategoryRule->text().trimmed();
0524     IFOKDO(err, SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), catName, cat, true))
0525     SKGCategoryObject catchange;
0526     QString catchangeName = ui.kCategoryTransfer->text().trimmed();
0527     IFOKDO(err, SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), catchangeName, catchange, true))
0528     IFOKDO(err, iRule.enableCategoryCondition(ui.kCategoryCheck->isChecked()))
0529     IFOKDO(err, iRule.setBudgetCategory(cat))
0530     IFOKDO(err, iRule.enableYearCondition(ui.kYearCheck->isChecked()))
0531     IFOKDO(err, iRule.setBudgetYear(ui.kYearRule->value()))
0532     IFOKDO(err, iRule.enableMonthCondition(ui.kMonthCheck->isChecked()))
0533     IFOKDO(err, iRule.setBudgetMonth(ui.kMonthRule->value()))
0534     IFOK(err) {
0535         bool absolute = (ui.kUnitCmb->currentIndex() == 1);
0536         double val = ui.kAmountEdit2->value();
0537         if (!absolute) {
0538             val = qMin(qMax(static_cast<double>(0), val), static_cast<double>(100));
0539         }
0540         err = iRule.setQuantity(val, absolute);
0541     }
0542     IFOKDO(err, iRule.setCondition(static_cast<SKGBudgetRuleObject::Condition>(ui.kConditionCmb->itemData(ui.kConditionCmb->currentIndex()).toInt())))
0543     IFOKDO(err, iRule.enableCategoryChange(ui.kCategoryTransferCheck->isChecked()))
0544     IFOKDO(err, iRule.setTransfer(static_cast<SKGBudgetRuleObject::Mode>(ui.kModeCmb->itemData(ui.kModeCmb->currentIndex()).toInt()), catchange))
0545     IFOKDO(err, iRule.save())
0546     return err;
0547 }
0548 
0549 void SKGBudgetPluginWidget::onCreatorModified()
0550 {
0551     bool test = !ui.kAmountEdit->text().isEmpty() && !ui.kYear->text().isEmpty();
0552     ui.kAddBtn->setEnabled(test || ui.kWidgetSelector->getSelectedMode() != 0);
0553     ui.kModifyBtn->setEnabled((test && ui.kPeriod->currentIndex() != 0 && ui.kWidgetSelector->getSelectedMode() == 0 && (getNbSelectedObjects() != 0))
0554                               || (ui.kWidgetSelector->getSelectedMode() == 2 && getNbSelectedObjects() == 1));
0555 
0556     bool monthCondition = (ui.kPeriod->currentIndex() == 2 || ui.kWidgetSelector->getSelectedMode() == 2);
0557     ui.kMonthLbl->setVisible(monthCondition);
0558     ui.kMonth->setVisible(monthCondition);
0559 }
0560 
0561 void SKGBudgetPluginWidget::onSelectionChanged()
0562 {
0563     SKGTRACEINFUNC(10)
0564     if (m_objectModel == nullptr) {
0565         return;
0566     }
0567 
0568     SKGObjectBase::SKGListSKGObjectBase objs = getSelectedObjects();
0569     int nb = objs.count();
0570     int mode = ui.kWidgetSelector->getSelectedMode();
0571     if (nb != 0) {
0572         if (m_objectModel->getRealTable() == QStringLiteral("budget")) {
0573             SKGBudgetObject budget(objs.at(0));
0574             ui.kYear->setValue(budget.getYear());
0575             ui.kMonth->setValue(budget.getMonth());
0576             ui.kAmountEdit->setValue(budget.getBudgetedAmount());
0577             ui.kCategoryEdit->setText(budget.getAttribute(QStringLiteral("t_CATEGORY")));
0578             ui.kPeriod->setCurrentIndex(budget.getMonth() == 0 ? 1 : 2);  // Set yearly or individual
0579             ui.kIncludingSubCategories->setChecked(budget.isSubCategoriesInclusionEnabled());
0580 
0581             if (mode > 0) {
0582                 ui.kWidgetSelector->setSelectedMode(0);
0583             }
0584         } else {
0585             SKGBudgetRuleObject rule(objs.at(0));
0586             ui.kYearCheck->setChecked(rule.isYearConditionEnabled());
0587             ui.kYearRule->setValue(rule.getBudgetYear());
0588             ui.kMonthCheck->setChecked(rule.isMonthConditionEnabled());
0589             ui.kMonthRule->setValue(rule.getBudgetMonth());
0590             ui.kCategoryCheck->setChecked(rule.isCategoryConditionEnabled());
0591             ui.kCategoryRule->setText(rule.getAttribute(QStringLiteral("t_CATEGORYCONDITION")));
0592             ui.kCategoryTransferCheck->setChecked(rule.isCategoryChangeEnabled());
0593             ui.kCategoryTransfer->setText(rule.getAttribute(QStringLiteral("t_CATEGORY")));
0594             ui.kUnitCmb->setCurrentIndex(rule.isAbolute() ? 1 : 0);
0595             ui.kAmountEdit2->setValue(rule.getQuantity());
0596             ui.kModeCmb->setCurrentIndex(ui.kModeCmb->findData(static_cast<int>(rule.getTransferMode())));
0597             ui.kConditionCmb->setCurrentIndex(ui.kConditionCmb->findData(static_cast<int>(rule.getCondition())));
0598         }
0599     }
0600 
0601     ui.kPeriod->setEnabled(nb <= 1);
0602     ui.kYear->setEnabled(nb <= 1);
0603     ui.kMonth->setEnabled(nb <= 1);
0604 
0605     onCreatorModified();
0606     refreshInfoZone();
0607     Q_EMIT selectionChanged();
0608 }
0609 
0610 void SKGBudgetPluginWidget::activateEditor()
0611 {
0612     if (ui.kWidgetSelector->getSelectedMode() == -1) {
0613         ui.kWidgetSelector->setSelectedMode(0);
0614     }
0615     ui.kAmountEdit->setFocus();
0616 }
0617 
0618 bool SKGBudgetPluginWidget::isEditor()
0619 {
0620     return true;
0621 }
0622 
0623 void SKGBudgetPluginWidget::refreshInfoZone()
0624 {
0625     SKGTRACEINFUNC(10)
0626     auto* doc = qobject_cast<SKGDocumentBank*>(getDocument());
0627     if ((doc != nullptr) && ui.kWidgetSelector->getSelectedMode() != 2) {
0628         SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit();
0629         SKGServices::SKGUnitInfo secondary = doc->getSecondaryUnit();
0630         // Refresh info area with selection
0631         SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects();
0632 
0633         double budgeted = 0;
0634         double modified = 0;
0635         int nb = selection.count();
0636         for (int i = 0; i < nb; ++i) {
0637             SKGBudgetObject budget(selection.at(i));
0638             budgeted += budget.getBudgetedAmount();
0639             modified += budget.getBudgetedModifiedAmount();
0640         }
0641 
0642         QString budgetedS = doc->formatMoney(budgeted, primary);
0643         QString modifiedS = doc->formatMoney(modified, primary);
0644         QString v = (budgetedS == modifiedS ? budgetedS : modifiedS % " <s><small>" % budgetedS % "</small></s>");
0645         if (nb != 0) {
0646             ui.kInfo->setText(i18np("Selection: %1 budget for %2", "Selection: %1 budgets for %2", nb, v));
0647             if (!secondary.Symbol.isEmpty() && (secondary.Value != 0.0)) {
0648                 budgetedS = doc->formatMoney(budgeted, secondary);
0649                 modifiedS = doc->formatMoney(modified, secondary);
0650                 v = (budgetedS == modifiedS ? budgetedS : modifiedS % " <s><small>" % budgetedS % "</small></s>");
0651             }
0652             ui.kInfo->setToolTip(i18np("Selection: %1 budget for %2", "Selection: %1 budgets for %2", nb, v));
0653         } else {
0654             ui.kInfo->setText(i18nc("Noun", "Selection: none"));
0655             ui.kInfo->setToolTip(i18nc("Noun", "Selection: none"));
0656         }
0657     }
0658 }
0659 
0660 void SKGBudgetPluginWidget::onTop()
0661 {
0662     SKGError err;
0663     SKGTRACEINFUNCRC(1, err)
0664 
0665     // Get rules
0666     SKGObjectBase::SKGListSKGObjectBase  rules = getSelectedObjects();
0667     int nb = rules.count();
0668     {
0669         SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget rule update"), err, nb)
0670         for (int i = nb - 1; !err && i >= 0; --i) {
0671             SKGBudgetRuleObject rule(rules.at(i));
0672 
0673             double order = 1;
0674             SKGStringListList result;
0675             err = getDocument()->executeSelectSqliteOrder(QStringLiteral("SELECT min(f_sortorder) from budgetrule"), result);
0676             if (!err && result.count() == 2) {
0677                 order = SKGServices::stringToDouble(result.at(1).at(0)) - 1;
0678             }
0679 
0680             IFOKDO(err, rule.setOrder(order))
0681             IFOKDO(err, rule.save())
0682 
0683             // Send message
0684             IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget rule '%1' has been updated", rule.getDisplayName()), SKGDocument::Hidden))
0685 
0686             IFOKDO(err, getDocument()->stepForward(i + 1))
0687         }
0688     }
0689 
0690     // status bar
0691     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Budget rule updated")))
0692     else {
0693         err.addError(ERR_FAIL, i18nc("Error message",  "Budget rule update failed"));
0694     }
0695 
0696     // Display error
0697     SKGMainPanel::displayErrorMessage(err);
0698 }
0699 
0700 void SKGBudgetPluginWidget::onUp()
0701 {
0702     SKGError err;
0703     SKGTRACEINFUNCRC(1, err)
0704 
0705     // Get rules
0706     SKGObjectBase::SKGListSKGObjectBase  rules = getSelectedObjects();
0707     int nb = rules.count();
0708     {
0709         SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget rule update"), err, nb)
0710         for (int i = 0; !err && i < nb; ++i) {
0711             SKGBudgetRuleObject rule(rules.at(i));
0712 
0713             double order = rule.getOrder();
0714             SKGStringListList result;
0715             err = getDocument()->executeSelectSqliteOrder("SELECT f_sortorder from budgetrule where f_sortorder<" % SKGServices::doubleToString(order) % " ORDER BY f_sortorder DESC", result);
0716             IFOK(err) {
0717                 if (result.count() == 2) {
0718                     order = SKGServices::stringToDouble(result.at(1).at(0)) - 1;
0719                 } else if (result.count() >= 2) {
0720                     order = (SKGServices::stringToDouble(result.at(1).at(0)) + SKGServices::stringToDouble(result.at(2).at(0))) / 2;
0721                 }
0722             }
0723 
0724             IFOKDO(err, rule.setOrder(order))
0725             IFOKDO(err, rule.save())
0726 
0727             // Send message
0728             IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget rule '%1' has been updated", rule.getDisplayName()), SKGDocument::Hidden))
0729 
0730             IFOKDO(err, getDocument()->stepForward(i + 1))
0731         }
0732     }
0733 
0734     // status bar
0735     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Budget rule updated")))
0736     else {
0737         err.addError(ERR_FAIL, i18nc("Error message",  "Budget rule update failed"));
0738     }
0739 
0740     // Display error
0741     SKGMainPanel::displayErrorMessage(err);
0742 }
0743 
0744 void SKGBudgetPluginWidget::onDown()
0745 {
0746     SKGError err;
0747     SKGTRACEINFUNCRC(1, err)
0748 
0749     // Get rules
0750     SKGObjectBase::SKGListSKGObjectBase  rules = getSelectedObjects();
0751     int nb = rules.count();
0752     {
0753         SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget rule update"), err, nb)
0754         for (int i = nb - 1; !err && i >= 0; --i) {
0755             SKGBudgetRuleObject rule(rules.at(i));
0756 
0757             double order = rule.getOrder();
0758             SKGStringListList result;
0759             err = getDocument()->executeSelectSqliteOrder("SELECT f_sortorder from budgetrule where f_sortorder>" % SKGServices::doubleToString(order) % " ORDER BY f_sortorder ASC", result);
0760             IFOK(err) {
0761                 if (result.count() == 2) {
0762                     order = SKGServices::stringToDouble(result.at(1).at(0)) + 1;
0763                 } else if (result.count() >= 2) {
0764                     order = (SKGServices::stringToDouble(result.at(1).at(0)) + SKGServices::stringToDouble(result.at(2).at(0))) / 2;
0765                 }
0766             }
0767 
0768             IFOKDO(err, rule.setOrder(order))
0769             IFOKDO(err, rule.save())
0770 
0771             // Send message
0772             IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget rule '%1' has been updated", rule.getDisplayName()), SKGDocument::Hidden))
0773 
0774             IFOKDO(err, getDocument()->stepForward(i + 1))
0775         }
0776     }
0777 
0778     // status bar
0779     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Budget rule updated")))
0780     else {
0781         err.addError(ERR_FAIL, i18nc("Error message",  "Budget rule update failed"));
0782     }
0783 
0784     // Display error
0785     SKGMainPanel::displayErrorMessage(err);
0786 }
0787 
0788 void SKGBudgetPluginWidget::onBottom()
0789 {
0790     SKGError err;
0791     SKGTRACEINFUNCRC(1, err)
0792 
0793     // Get rules
0794     SKGObjectBase::SKGListSKGObjectBase  rules = getSelectedObjects();
0795     int nb = rules.count();
0796     {
0797         SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget rule update"), err, nb)
0798         for (int i = 0; !err && i < nb; ++i) {
0799             SKGBudgetRuleObject rule(rules.at(i));
0800 
0801             double order = 1;
0802             SKGStringListList result;
0803             err = getDocument()->executeSelectSqliteOrder(QStringLiteral("SELECT max(f_sortorder) from budgetrule"), result);
0804             if (!err && result.count() == 2) {
0805                 order = SKGServices::stringToDouble(result.at(1).at(0)) + 1;
0806             }
0807 
0808             IFOKDO(err, rule.setOrder(order))
0809             IFOKDO(err, rule.save())
0810 
0811             // Send message
0812             IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The budget rule '%1' has been updated", rule.getDisplayName()), SKGDocument::Hidden))
0813 
0814             IFOKDO(err, getDocument()->stepForward(i + 1))
0815         }
0816     }
0817 
0818     // status bar
0819     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Budget rule updated")))
0820     else {
0821         err.addError(ERR_FAIL, i18nc("Error message",  "Budget rule update failed"));
0822     }
0823 
0824     // Display error
0825     SKGMainPanel::displayErrorMessage(err);
0826 }
0827 
0828 
0829