File indexing completed on 2024-05-26 05:10:42

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007  * This file is Skrooge plugin for transaction management.
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgoperationpluginwidget.h"
0012 
0013 #include <kcolorscheme.h>
0014 #include <kstandardaction.h>
0015 
0016 #include <qcompleter.h>
0017 #include <qdir.h>
0018 #include <qdom.h>
0019 #include <qfile.h>
0020 #include <qheaderview.h>
0021 #include <qinputdialog.h>
0022 #include <qmap.h>
0023 #include <qscriptengine.h>
0024 #include <qstandardpaths.h>
0025 #include <qstringlistmodel.h>
0026 #include <qtablewidget.h>
0027 
0028 #include "skgbankincludes.h"
0029 #include "skgcalculatoredit.h"
0030 #include "skgmainpanel.h"
0031 #include "skgobjectbase.h"
0032 #include "skgobjectmodel.h"
0033 #include "skgoperation_settings.h"
0034 #include "skgpayeeobject.h"
0035 #include "skgservices.h"
0036 #include "skgshow.h"
0037 #include "skgsplittabledelegate.h"
0038 #include "skgtraces.h"
0039 #include "skgtreeview.h"
0040 
0041 SKGOperationPluginWidget::SKGOperationPluginWidget(QWidget* iParent, SKGDocumentBank* iDocument)
0042     : SKGTabPage(iParent, iDocument), m_objectModel(nullptr), m_fastEditionAction(nullptr), m_lastFastEditionOperationFound(0), m_showClosedAccounts(false),
0043       m_numberFieldIsNotUptodate(true), m_modeInfoZone(0), m_tableDelegate(nullptr)
0044 {
0045     SKGTRACEINFUNC(1)
0046     if (iDocument == nullptr) {
0047         return;
0048     }
0049 
0050     m_timer.setSingleShot(true);
0051     connect(&m_timer, &QTimer::timeout, this, &SKGOperationPluginWidget::onRefreshInformationZone, Qt::QueuedConnection);
0052 
0053     ui.setupUi(this);
0054 
0055     ui.kAccountLabel2->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_ACCOUNT"))));
0056     ui.kDateLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("d_date"))));
0057     ui.kAmountLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("f_CURRENTAMOUNT"))));
0058     ui.kPayeeLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_payee"))));
0059     ui.kTypeLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_mode"))));
0060     ui.kNumberEdit->setPlaceholderText(iDocument->getDisplay(QStringLiteral("t_number")));
0061     ui.kCategoryLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_CATEGORY"))));
0062     ui.kCommentLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_comment"))));
0063     ui.kTrackerLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_REFUND"))));
0064 
0065     ui.kUnitEdit->setDocument(iDocument);
0066     ui.kUnitShare->setDocument(iDocument);
0067 
0068 
0069     ui.kTitle->hide();
0070     ui.kReconciliatorFrame2->hide();
0071     ui.kReconciliateAccount->hide();
0072 
0073     m_attributesForSplit  << QStringLiteral("d_date") << QStringLiteral("t_category") << QStringLiteral("f_value") << QStringLiteral("t_comment")  << QStringLiteral("t_refund");
0074     int nb = m_attributesForSplit.count();
0075     ui.kSubOperationsTable->setColumnCount(nb);
0076     for (int i = 0; i < nb; ++i) {
0077         QString att = m_attributesForSplit.at(i);
0078         auto item = new QTableWidgetItem(iDocument->getIcon(att), iDocument->getDisplay(att));
0079         ui.kSubOperationsTable->setHorizontalHeaderItem(i, item);
0080     }
0081 
0082     {
0083         // Bind transaction view
0084         m_objectModel = new SKGObjectModel(qobject_cast<SKGDocumentBank*>(getDocument()), QStringLiteral("v_operation_display_all"), QStringLiteral("1=0"), this, QLatin1String(""), false);
0085         ui.kOperationView->setModel(m_objectModel);
0086 
0087         // Add registered global action in contextual menu
0088         if (SKGMainPanel::getMainPanel() != nullptr) {
0089             m_fastEditionAction = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("fast_edition"));
0090         }
0091 
0092         connect(ui.kOperationView->getView(), &SKGTreeView::clickEmptyArea, this, &SKGOperationPluginWidget::cleanEditor);
0093         connect(ui.kOperationView->getView(), &SKGTreeView::doubleClicked, this, &SKGOperationPluginWidget::onDoubleClick);
0094         connect(ui.kOperationView->getView(), &SKGTreeView::selectionChangedDelayed, this, [ = ] {this->onSelectionChanged();});
0095     }
0096 
0097     // Add Standard KDE Icons to buttons to Transactions
0098     ui.kModifyOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok")));
0099     ui.kAddOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("list-add")));
0100     ui.kCleanBtn->setIcon(SKGServices::fromTheme(QStringLiteral("edit-clear")));
0101     ui.kReconciliatorButton->setIcon(SKGServices::fromTheme(QStringLiteral("view-refresh")));
0102     ui.kValidate->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok")));
0103     ui.kValidate->setIconSize(QSize(48, 48));
0104     ui.kAutoPoint->setIcon(SKGServices::fromTheme(QStringLiteral("games-solve")));
0105     ui.kCreateFakeOperation->setIcon(SKGServices::fromTheme(QStringLiteral("list-add")));
0106     ui.kFastEditBtn->setIcon(SKGServices::fromTheme(QStringLiteral("games-solve")));
0107 
0108     {
0109         SKGWidgetSelector::SKGListQWidget list;
0110         list.push_back(ui.SKGBasicSection);
0111         list.push_back(ui.SKGPayeeModeSection);
0112         list.push_back(ui.SKGSmallButtons);
0113         list.push_back(ui.SKGEditionButtonsWidget);
0114         list.push_back(ui.SKGSingleOpSection);
0115         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("dialog-ok")), i18n("Standard"), i18n("Display the edit panel for standard transactions"), list);
0116     }
0117 
0118     {
0119         SKGWidgetSelector::SKGListQWidget list;
0120         list.push_back(ui.SKGBasicSection);
0121         list.push_back(ui.SKGPayeeModeSection);
0122         list.push_back(ui.SKGSmallButtons);
0123         list.push_back(ui.SKGEditionButtonsWidget);
0124         list.push_back(ui.SKGSplitOpSection);
0125         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("split")), i18n("Split"), i18n("Display the edit panel for split transactions"), list);
0126     }
0127     {
0128         SKGWidgetSelector::SKGListQWidget list;
0129         list.push_back(ui.SKGBasicSection);
0130         list.push_back(ui.SKGPayeeModeSection);
0131         list.push_back(ui.SKGSmallButtons);
0132         list.push_back(ui.SKGEditionButtonsWidget);
0133         list.push_back(ui.SKGSingleOpSection);
0134         list.push_back(ui.kTargetAccountEdit);
0135         list.push_back(ui.kTargetAccountLabel);
0136         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("exchange-positions")), i18n("Transfer"), i18n("Display the edit panel for transfers between accounts"), list);
0137     }
0138     {
0139         SKGWidgetSelector::SKGListQWidget list;
0140         list.push_back(ui.SKGBasicSection);
0141         list.push_back(ui.SKGPayeeModeSection);
0142         list.push_back(ui.SKGSmallButtons);
0143         list.push_back(ui.SKGEditionButtonsWidget);
0144         list.push_back(ui.SKGSharesSection);
0145         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("view-bank-account-savings")), i18n("Shares"), i18n("Display the edit panel for purchasing or selling shares"), list);
0146     }
0147     connect(ui.kWidgetSelector, &SKGWidgetSelector::selectedModeChanged, this, &SKGOperationPluginWidget::onBtnModeClicked);
0148 
0149     ui.kFreezeBtn->setIcon(SKGServices::fromTheme(QStringLiteral("emblem-locked")));
0150 
0151     // Fast edition
0152     connect(qApp, &QApplication::focusChanged, this, &SKGOperationPluginWidget::onFocusChanged);
0153     connect(m_fastEditionAction, &QAction::triggered, this, &SKGOperationPluginWidget::onFastEdition);
0154 
0155     // SubOperations
0156     connect(ui.kAmountEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onQuantityChanged);
0157     connect(ui.kDateEdit, &SKGDateEdit::dateChanged, this, &SKGOperationPluginWidget::onDateChanged);
0158     connect(ui.kSubOperationsTable, &SKGTableWidget::cellChanged, this, &SKGOperationPluginWidget::onSubopCellChanged);
0159     connect(ui.kSubOperationsTable->verticalHeader(), &QHeaderView::sectionClicked, this, &SKGOperationPluginWidget::onRemoveSubOperation);
0160 
0161     ui.kSubOperationsTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
0162     ui.kSubOperationsTable->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
0163     ui.kSubOperationsTable->setWordWrap(false);
0164     m_tableDelegate = new SKGSplitTableDelegate(ui.kSubOperationsTable, getDocument(), m_attributesForSplit);
0165     m_tableDelegate->addParameterValue(QStringLiteral("total"), '0');
0166     ui.kSubOperationsTable->setItemDelegate(m_tableDelegate);
0167     ui.kSubOperationsTable->setTextElideMode(Qt::ElideMiddle);
0168     connect(ui.kSubOperationsTable, &SKGTableWidget::removeLine, this, &SKGOperationPluginWidget::onRemoveSubOperation);
0169 
0170     ui.kTargetAccountEdit->hide();
0171     ui.kTargetAccountLabel->hide();
0172     ui.SKGSplitOpSection->hide();
0173     ui.SKGSharesSection->hide();
0174 
0175     ui.kWidgetSelector->setSelectedMode(0);
0176 
0177     // Set Event filters to catch CTRL+ENTER or SHIFT+ENTER
0178     mainWidget()->installEventFilter(this);
0179     this->installEventFilter(this);
0180 
0181     // Set Event filters for locking widgets
0182     ui.kTypeEdit->lineEdit()->installEventFilter(this);
0183     ui.kTypeEdit->installEventFilter(this);
0184     ui.kUnitEdit->lineEdit()->installEventFilter(this);
0185     ui.kUnitEdit->installEventFilter(this);
0186     ui.kCategoryEdit->lineEdit()->installEventFilter(this);
0187     ui.kCategoryEdit->installEventFilter(this);
0188     ui.kCommentEdit->lineEdit()->installEventFilter(this);
0189     ui.kCommentEdit->installEventFilter(this);
0190     ui.kPayeeEdit->lineEdit()->installEventFilter(this);
0191     ui.kPayeeEdit->installEventFilter(this);
0192     ui.kTrackerEdit->lineEdit()->installEventFilter(this);
0193     ui.kTrackerEdit->installEventFilter(this);
0194     ui.kAccountEdit->installEventFilter(this);
0195     ui.kTargetAccountLabel->installEventFilter(this);
0196     ui.kAmountEdit->installEventFilter(this);
0197     ui.kNumberEdit->installEventFilter(this);
0198 
0199     connect(getDocument(), &SKGDocument::tableModified, this, &SKGOperationPluginWidget::dataModified, Qt::QueuedConnection);
0200 
0201     connect(ui.kUnitEdit, &SKGUnitComboBox::editTextChanged, this, &SKGOperationPluginWidget::refreshSubOperationAmount, Qt::QueuedConnection);
0202     connect(ui.kUnitEdit, &SKGUnitComboBox::editTextChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
0203     connect(ui.kUnitShare, static_cast<void (SKGUnitComboBox::*)(int)>(&SKGUnitComboBox::currentIndexChanged), this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
0204     connect(ui.kAmountEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
0205     connect(ui.kAmountSharesEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
0206     connect(ui.kCommissionEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
0207     connect(ui.kTaxEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
0208     connect(ui.kAccountEdit, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
0209     connect(ui.kOperationView->getShowWidget(), &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onFilterChanged, Qt::QueuedConnection);
0210     connect(ui.kPayeeEdit->lineEdit(), &QLineEdit::returnPressed, this, &SKGOperationPluginWidget::onPayeeChanged, Qt::QueuedConnection);
0211 
0212     connect(ui.kReconcilitorAmountEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onRefreshInformationZone);
0213     connect(ui.kAddOperationBtn, &QPushButton::clicked, this, &SKGOperationPluginWidget::onAddOperationClicked);
0214     connect(ui.kModifyOperationBtn, &QPushButton::clicked, this, &SKGOperationPluginWidget::onUpdateOperationClicked);
0215     connect(ui.kReconciliatorButton, &QToolButton::clicked, this, &SKGOperationPluginWidget::onRotateAccountTools);
0216     connect(ui.kValidate, &QToolButton::clicked, this, &SKGOperationPluginWidget::onValidateMarkedOperations);
0217     connect(ui.kCleanBtn, &QPushButton::clicked, this, &SKGOperationPluginWidget::cleanEditor);
0218     connect(ui.kSubOperationsTable, &SKGTableWidget::itemSelectionChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified);
0219     connect(ui.kAutoPoint, &QToolButton::clicked, this, &SKGOperationPluginWidget::onAutoPoint);
0220     connect(ui.kFreezeBtn, &QToolButton::clicked, this, &SKGOperationPluginWidget::onFreeze);
0221     connect(ui.kCreateFakeOperation, &QToolButton::clicked, this, &SKGOperationPluginWidget::onAddFakeOperation);
0222     connect(ui.kFastEditBtn, &QPushButton::clicked, this, &SKGOperationPluginWidget::onFastEdition);
0223 
0224     dataModified(QLatin1String(""), 0);
0225     onOperationCreatorModified();
0226 
0227     setAllWidgetsEnabled();
0228 }
0229 
0230 SKGOperationPluginWidget::~SKGOperationPluginWidget()
0231 {
0232     SKGTRACEINFUNC(1)
0233     m_objectModel = nullptr;
0234     m_fastEditionAction = nullptr;
0235 }
0236 
0237 QString SKGOperationPluginWidget::currentAccount()
0238 {
0239     QStringList accounts = SKGServices::splitCSVLine(ui.kOperationView->getShowWidget()->getState());
0240     for (const auto& item : qAsConst(accounts)) {
0241         if (item.startsWith(QLatin1String("##_"))) {
0242             return item.right(item.length() - 3);
0243         }
0244     }
0245     return QLatin1String("");
0246 }
0247 
0248 bool SKGOperationPluginWidget::isWidgetEditionEnabled(QWidget* iWidget)
0249 {
0250     return ((iWidget != nullptr) && (!iWidget->property("frozen").isValid() || !iWidget->property("frozen").toBool()));
0251 }
0252 
0253 void SKGOperationPluginWidget::setWidgetEditionEnabled(QWidget* iWidget, bool iEnabled)
0254 {
0255     if ((iWidget != nullptr) && isWidgetEditionEnabled(iWidget) != iEnabled) {
0256         if (iEnabled) {
0257             iWidget->setStyleSheet(QStringLiteral("background-image:none;"));
0258             iWidget->setProperty("frozen", false);
0259         } else {
0260             auto color = KColorScheme(QPalette::Normal).background(KColorScheme::ActiveBackground).color().name().right(6);
0261             iWidget->setStyleSheet("background-color:#" % color);
0262             iWidget->setProperty("frozen", true);
0263         }
0264 
0265         QString addOn = i18nc("A tool tip", "This field is frozen (it will not be affected by Fast Edition). Double click to unfreeze it");
0266         QString t = iWidget->toolTip().remove('\n' % addOn).remove(addOn);
0267         if (!iEnabled) {
0268             t = iWidget->toolTip();
0269             if (!t.isEmpty()) {
0270                 t += '\n';
0271             }
0272             t += addOn;
0273         }
0274         iWidget->setToolTip(t);
0275 
0276         // 348619: Freeze the unit when amount is frozen
0277         if (iWidget == ui.kAmountEdit) {
0278             setWidgetEditionEnabled(ui.kUnitEdit->lineEdit(), iEnabled);
0279         }
0280     }
0281 }
0282 
0283 bool SKGOperationPluginWidget::eventFilter(QObject* iObject, QEvent* iEvent)
0284 {
0285     if ((iEvent != nullptr) && iEvent->type() == QEvent::MouseButtonDblClick) {
0286         auto* line = qobject_cast<QLineEdit*>(iObject);
0287         if (line != nullptr) {
0288             setWidgetEditionEnabled(line, !isWidgetEditionEnabled(line));
0289         }
0290     } else if ((iEvent != nullptr) && iEvent->type() == QEvent::FocusIn) {
0291         auto* line = qobject_cast<QLineEdit*>(iObject);
0292         if (line != nullptr) {
0293             m_previousValue = line->text();
0294         } else {
0295             auto* cmb = qobject_cast<SKGComboBox*>(iObject);
0296             if (cmb != nullptr) {
0297                 m_previousValue = cmb->text();
0298             }
0299         }
0300     } else if ((iEvent != nullptr) && iEvent->type() == QEvent::FocusOut) {
0301         auto* line = qobject_cast<QLineEdit*>(iObject);
0302         if (line != nullptr) {
0303             if (m_previousValue != line->text() && !line->text().isEmpty()) {
0304                 setWidgetEditionEnabled(line, false);
0305             }
0306         } else {
0307             auto* cmb = qobject_cast<SKGComboBox*>(iObject);
0308             if (cmb != nullptr) {
0309                 if (m_previousValue != cmb->text() && !cmb->text().isEmpty()) {
0310                     setWidgetEditionEnabled(cmb->lineEdit(), false);
0311                 }
0312             }
0313         }
0314     } else if ((iEvent != nullptr) && iEvent->type() == QEvent::KeyPress) {
0315         auto* keyEvent = dynamic_cast<QKeyEvent*>(iEvent);
0316         if (keyEvent && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && iObject == this) {
0317             if ((QApplication::keyboardModifiers() & Qt::ControlModifier) != 0u && ui.kAddOperationBtn->isEnabled()) {
0318                 ui.kAddOperationBtn->click();
0319             } else if ((QApplication::keyboardModifiers() &Qt::ShiftModifier) != 0u && ui.kModifyOperationBtn->isEnabled()) {
0320                 ui.kModifyOperationBtn->click();
0321             }
0322         } else if (keyEvent && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) && iObject == ui.kNumberEdit) {
0323             int c = SKGServices::stringToInt(ui.kNumberEdit->text());
0324             if (c != 0) {
0325                 ui.kNumberEdit->setText(SKGServices::intToString(keyEvent->key() == Qt::Key_Up ? c + 1 : c - 1));
0326             }
0327         }
0328     }
0329 
0330     return SKGTabPage::eventFilter(iObject, iEvent);
0331 }
0332 
0333 void SKGOperationPluginWidget::onFreeze()
0334 {
0335     if (!ui.kFreezeBtn->isChecked()) {
0336         ui.kFreezeBtn->setIcon(SKGServices::fromTheme(QStringLiteral("emblem-locked")));
0337         // At least one fiels is already frozen ==> unfreeze
0338         setAllWidgetsEnabled();
0339     } else {
0340         QStringList overlay;
0341         overlay.push_back(QStringLiteral("edit-delete"));
0342         ui.kFreezeBtn->setIcon(SKGServices::fromTheme(QStringLiteral("emblem-locked"), overlay));
0343         // No wildget frozen ==> freeze widget containing test
0344         if (!ui.kTypeEdit->text().isEmpty()) {
0345             setWidgetEditionEnabled(ui.kTypeEdit->lineEdit(), false);
0346         }
0347         if (!ui.kUnitEdit->text().isEmpty()) {
0348             setWidgetEditionEnabled(ui.kUnitEdit->lineEdit(), false);
0349         }
0350         if (!ui.kCategoryEdit->text().isEmpty()) {
0351             setWidgetEditionEnabled(ui.kCategoryEdit->lineEdit(), false);
0352         }
0353         if (!ui.kCommentEdit->text().isEmpty()) {
0354             setWidgetEditionEnabled(ui.kCommentEdit->lineEdit(), false);
0355         }
0356         if (!ui.kPayeeEdit->text().isEmpty()) {
0357             setWidgetEditionEnabled(ui.kPayeeEdit->lineEdit(), false);
0358         }
0359         if (!ui.kTrackerEdit->text().isEmpty()) {
0360             setWidgetEditionEnabled(ui.kTrackerEdit->lineEdit(), false);
0361         }
0362         // if(!ui.kAccountEdit->text().isEmpty()) setWidgetEditionEnabled(ui.kAccountEdit, false);
0363         if (!ui.kAmountEdit->text().isEmpty()) {
0364             setWidgetEditionEnabled(ui.kAmountEdit, false);
0365         }
0366         if (!ui.kNumberEdit->text().isEmpty()) {
0367             setWidgetEditionEnabled(ui.kNumberEdit, false);
0368         }
0369         if (!ui.kTargetAccountEdit->text().isEmpty()) {
0370             setWidgetEditionEnabled(ui.kTargetAccountEdit, false);
0371         }
0372     }
0373 }
0374 
0375 void SKGOperationPluginWidget::setAllWidgetsEnabled()
0376 {
0377     SKGTRACEINFUNC(10)
0378     // Enable widgets
0379     setWidgetEditionEnabled(ui.kTypeEdit->lineEdit(), true);
0380     setWidgetEditionEnabled(ui.kUnitEdit->lineEdit(), true);
0381     setWidgetEditionEnabled(ui.kCategoryEdit->lineEdit(), true);
0382     setWidgetEditionEnabled(ui.kCommentEdit->lineEdit(), true);
0383     setWidgetEditionEnabled(ui.kPayeeEdit->lineEdit(), true);
0384     setWidgetEditionEnabled(ui.kTrackerEdit->lineEdit(), true);
0385     setWidgetEditionEnabled(ui.kAccountEdit, true);
0386     setWidgetEditionEnabled(ui.kTargetAccountEdit, true);
0387     setWidgetEditionEnabled(ui.kAmountEdit, true);
0388     setWidgetEditionEnabled(ui.kNumberEdit, true);
0389 }
0390 
0391 QString SKGOperationPluginWidget::getAttributeOfSelection(const QString& iAttribute)
0392 {
0393     QString output;
0394     SKGObjectBase::SKGListSKGObjectBase selectedObjects = ui.kOperationView->getView()->getSelectedObjects();
0395     int nb = selectedObjects.count();
0396     for (int i = 0; i < nb ; ++i) {
0397         const SKGObjectBase& obj = selectedObjects.at(i);
0398         QString val = obj.getAttribute(iAttribute);
0399         if (i > 0 && val != output) {
0400             output = NOUPDATE;
0401             break;
0402         }
0403         output = val;
0404     }
0405 
0406     return output;
0407 }
0408 
0409 void SKGOperationPluginWidget::onSelectionChanged()
0410 {
0411     SKGTRACEINFUNC(10)
0412 
0413     int mode = ui.kWidgetSelector->getSelectedMode();
0414 
0415     // Enable widgets
0416     setAllWidgetsEnabled();
0417     ui.kFreezeBtn->setChecked(false);
0418     ui.kFreezeBtn->setIcon(SKGServices::fromTheme(QStringLiteral("emblem-locked")));
0419 
0420     // Mapping
0421     int nbSelect = ui.kOperationView->getView()->getNbSelectedObjects();
0422     bool onConsolidatedTable = false;
0423     if ((nbSelect != 0) && (m_objectModel != nullptr)) {
0424         SKGObjectBase objbase = ui.kOperationView->getView()->getFirstSelectedObject();
0425         SKGOperationObject obj;
0426         onConsolidatedTable = (objbase.getTable() == QStringLiteral("v_suboperation_consolidated"));
0427         if (onConsolidatedTable) {
0428             obj = SKGOperationObject(obj.getDocument(), SKGServices::stringToInt(obj.getAttribute(QStringLiteral("i_OPID"))));
0429         } else {
0430             obj = objbase;
0431         }
0432 
0433         ui.kDateEdit->setDate(SKGServices::stringToTime(objbase.getAttribute(QStringLiteral("d_date"))).date());
0434         m_previousDate = ui.kDateEdit->date();
0435         ui.kCommentEdit->setText(objbase.getAttribute(onConsolidatedTable ? QStringLiteral("t_REALCOMMENT") : QStringLiteral("t_comment")));
0436         QString number = objbase.getAttribute(QStringLiteral("t_number"));
0437         ui.kNumberEdit->setText(number);
0438         QString accountName = objbase.getAttribute(QStringLiteral("t_ACCOUNT"));
0439         if (!m_showClosedAccounts && !accountName.isEmpty() && !ui.kAccountEdit->contains(accountName)) {
0440             // Refresh list of accounts if a closed account is selected
0441             m_showClosedAccounts = true;
0442             dataModified(QLatin1String(""), 0);
0443         }
0444         ui.kAccountEdit->setText(accountName);
0445         ui.kPayeeEdit->setText(objbase.getAttribute(QStringLiteral("t_PAYEE")));
0446         ui.kTypeEdit->setText(objbase.getAttribute(QStringLiteral("t_mode")));
0447         QString unit = objbase.getAttribute(QStringLiteral("t_UNIT"));
0448         ui.kUnitEdit->setText(unit);
0449         QString cat = objbase.getAttribute(QStringLiteral("t_REALCATEGORY"));
0450         if (cat.isEmpty()) {
0451             cat = objbase.getAttribute(QStringLiteral("t_CATEGORY"));
0452         }
0453         ui.kCategoryEdit->setText(cat);
0454         ui.kTrackerEdit->setText(objbase.getAttribute(onConsolidatedTable ? QStringLiteral("t_REALREFUND") : QStringLiteral("t_REFUND")));
0455         QString quantity = objbase.getAttribute(QStringLiteral("f_REALQUANTITY"));
0456         if (quantity.isEmpty()) {
0457             quantity = objbase.getAttribute(QStringLiteral("f_QUANTITY"));
0458         }
0459         double quantityVal = SKGServices::stringToDouble(quantity);
0460         SKGUnitObject unitObject = ui.kUnitEdit->getUnit();
0461         int nbDec = unitObject.getNumberDecimal();
0462         if (nbDec == 0) {
0463             nbDec = 2;
0464         }
0465         quantity = SKGServices::toCurrencyString(qAbs(quantityVal), QLatin1String(""), nbDec);
0466         if (quantity.startsWith(QLocale().positiveSign())) {
0467             quantity = quantity.right(quantity.length() - 1);
0468         }
0469         if (quantityVal > 0) {
0470             quantity = '+' % quantity;
0471         } else {
0472             quantity = '-' % quantity;
0473         }
0474         ui.kAmountEdit->setText(quantity);
0475 
0476         if (nbSelect > 1) {
0477             // In case of multi selection
0478             if (mode >= 0) {
0479                 ui.kWidgetSelector->setSelectedMode(0);
0480             }
0481             ui.kAccountEdit->setText(getAttributeOfSelection(QStringLiteral("t_ACCOUNT")));
0482             ui.kTypeEdit->setText(getAttributeOfSelection(QStringLiteral("t_mode")));
0483             ui.kUnitEdit->setText(getAttributeOfSelection(QStringLiteral("t_UNIT")));
0484             ui.kCategoryEdit->setText(getAttributeOfSelection(onConsolidatedTable ? QStringLiteral("t_REALCATEGORY") : QStringLiteral("t_CATEGORY")));
0485             ui.kTrackerEdit->setText(getAttributeOfSelection(onConsolidatedTable ? QStringLiteral("t_REALREFUND") : QStringLiteral("t_REFUND")));
0486             ui.kCommentEdit->setText(getAttributeOfSelection(onConsolidatedTable ? QStringLiteral("t_REALCOMMENT") : QStringLiteral("t_comment")));
0487             ui.kPayeeEdit->setText(getAttributeOfSelection(QStringLiteral("t_PAYEE")));
0488 
0489             QString d = getAttributeOfSelection(QStringLiteral("d_date"));
0490             if (d == NOUPDATE) {
0491                 ui.kDateEdit->setCurrentText(NOUPDATE);
0492             }
0493 
0494             QString q = getAttributeOfSelection(onConsolidatedTable ? QStringLiteral("f_REALQUANTITY") : QStringLiteral("f_QUANTITY"));
0495             ui.kAmountEdit->setText(q != NOUPDATE ? quantity : NOUPDATE);
0496             ui.kNumberEdit->setText(QLatin1String(""));
0497         } else {
0498             if (obj.getStatus() == SKGOperationObject::MARKED) {
0499                 displayReconciliationInfo();
0500             } else if (m_modeInfoZone != 1) {
0501                 displayBalance();
0502             }
0503 
0504             // It is a single selection
0505             // Is it a split ?
0506             int nbSubOperations = obj.getNbSubOperations();
0507             if (nbSubOperations > 1 && !onConsolidatedTable) {
0508                 // yes, it is a split
0509                 if (mode >= 0) {
0510                     ui.kWidgetSelector->setSelectedMode(1);
0511                 }
0512 
0513                 displaySubOperations();
0514             } else {
0515                 // Is it a transfer ?
0516                 SKGOperationObject op2;
0517                 if (obj.isTransfer(op2) && op2.exist()) {
0518                     // yes it is a transfer
0519                     SKGAccountObject account2;
0520                     op2.getParentAccount(account2);
0521                     QString accountName2 = account2.getName();
0522                     if (!m_showClosedAccounts && !ui.kTargetAccountEdit->contains(accountName2)) {
0523                         // Refresh list of accounts if a closed account is selected
0524                         m_showClosedAccounts = true;
0525                         dataModified(QLatin1String(""), 0);
0526                     }
0527                     ui.kTargetAccountEdit->setText(accountName2);
0528                     if (mode >= 0) {
0529                         ui.kWidgetSelector->setSelectedMode(2);
0530                     }
0531                 } else {
0532                     if (mode >= 0) {
0533                         ui.kWidgetSelector->setSelectedMode(0);
0534                     }
0535                 }
0536             }
0537         }
0538     }
0539 
0540     ui.kNumberEdit->setEnabled(nbSelect <= 1);
0541 
0542     bool splitTest = nbSelect <= 1 && !onConsolidatedTable;
0543     ui.kWidgetSelector->setEnabledMode(1, splitTest);
0544     if (!splitTest && mode == 1) {
0545         ui.kWidgetSelector->setSelectedMode(0);
0546     }
0547 
0548     onOperationCreatorModified();
0549 
0550     Q_EMIT selectionChanged();
0551 }
0552 
0553 void SKGOperationPluginWidget::onOperationCreatorModified()
0554 {
0555     SKGTRACEINFUNC(10)
0556 
0557     int mode = ui.kWidgetSelector->getSelectedMode();
0558 
0559     // Set icons
0560     if (!isTemplateMode()) {
0561         ui.kModifyOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok")));
0562         ui.kAddOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("list-add")));
0563     } else {
0564         QStringList overlay;
0565         overlay.push_back(QStringLiteral("edit-guides"));
0566         ui.kModifyOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok"), overlay));
0567         ui.kAddOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("list-add"), overlay));
0568     }
0569 
0570     // Is it an existing unit ?
0571     QString unitName = ui.kUnitEdit->currentText();
0572     SKGUnitObject unit(getDocument());
0573     unit.setName(unitName);
0574     unit.setSymbol(unitName);
0575     if (unit.load().isSucceeded()) {
0576         ui.kWidgetSelector->setEnabledMode(3, true);
0577         if (mode == 3 && unit.getType() == SKGUnitObject::SHARE) {
0578             // Update units
0579             auto unit = ui.kUnitShare->getUnit();
0580             ui.kUnitCommission->setText(unit.getSymbol());
0581             ui.kUnitTax->setText(unit.getSymbol());
0582 
0583             // Update total in "purchase / sale share" page
0584             double total = ui.kAmountSharesEdit->value() + (ui.kCommissionEdit->value() + ui.kTaxEdit->value()) * (ui.kAmountEdit->value() > 0 ? 1 : -1);
0585             ui.KTotal->setText(SKGServices::toCurrencyString(total, unit.getSymbol(), unit.getNumberDecimal()));
0586         } else {
0587             // BUG 2692665
0588             auto unitShareName = ui.kUnitShare->currentText();
0589             if (unitShareName.isEmpty()) {
0590                 ui.kUnitShare->setText(unitName);
0591                 ui.kUnitCommission->setText(unitName);
0592                 ui.kUnitTax->setText(unitName);
0593                 ui.KTotal->setText(unitName);
0594 
0595             } else {
0596                 ui.kUnitCommission->setText(unitShareName);
0597                 ui.kUnitTax->setText(unitShareName);
0598                 ui.KTotal->setText(unitShareName);
0599             }
0600         }
0601 
0602     } else {
0603         ui.kWidgetSelector->setEnabledMode(3, false);
0604         if (mode == 3) {
0605             ui.kWidgetSelector->setSelectedMode(0);
0606         }
0607     }
0608 
0609     bool activated = mode != -1 &&
0610                      !ui.kAccountEdit->currentText().isEmpty() &&
0611                      ((!ui.kAmountEdit->text().isEmpty() && (ui.kAmountEdit->valid() || ui.kAmountEdit->text() == NOUPDATE)) || !ui.kAmountEdit->isEnabled()) &&
0612                      !unitName.isEmpty() &&
0613                      (mode != 3 || !ui.kAmountSharesEdit->text().isEmpty());
0614 
0615     int nbSelect = getNbSelectedObjects();
0616 
0617     ui.kAddOperationBtn->setEnabled(activated);
0618     ui.kModifyOperationBtn->setEnabled(activated && nbSelect > 0 && (ui.kWidgetSelector->getSelectedMode() == 0 ||  ui.kWidgetSelector->getSelectedMode() == 1 ||  ui.kWidgetSelector->getSelectedMode() == 2));
0619 
0620     m_numberFieldIsNotUptodate = true;
0621     if (ui.kNumberEdit->hasFocus()) {
0622         fillNumber();
0623     }
0624 }
0625 
0626 void SKGOperationPluginWidget::onPayeeChanged()
0627 {
0628     if (skgoperation_settings::setCategoryForPayee() && ui.kCategoryEdit->text().isEmpty()) {
0629         ui.kCategoryEdit->setText(qobject_cast<SKGDocumentBank*>(getDocument())->getCategoryForPayee(ui.kPayeeEdit->text(), false));
0630     }
0631 }
0632 
0633 void SKGOperationPluginWidget::onUpdateOperationClicked()
0634 {
0635     SKGError err;
0636     SKGTRACEINFUNCRC(10, err)
0637     // Get Selection
0638     SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects();
0639 
0640     int nb = selection.count();
0641     {
0642         SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Transaction update"), err, nb)
0643         err = updateSelection(selection);
0644     }
0645 
0646     // status bar
0647     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Transaction updated")))
0648     else {
0649         err.addError(ERR_FAIL, i18nc("Error message",  "Transaction update failed"));
0650     }
0651 
0652     // Display error
0653     SKGMainPanel::displayErrorMessage(err, true);
0654 
0655     // Set focus on table
0656     ui.kOperationView->getView()->setFocus();
0657 }
0658 
0659 SKGError SKGOperationPluginWidget::updateSelection(const SKGObjectBase::SKGListSKGObjectBase& iSelection, bool iForceCreation)
0660 {
0661     SKGError err;
0662     SKGTRACEINFUNCRC(10, err)
0663 
0664     // Initialisation
0665     double ratio = -1;
0666     bool refreshSubOperation = true;
0667 
0668     // Get Selection
0669     int nb = iSelection.count();
0670     double rate = -1;
0671 
0672     for (int i = 0; !err && i < nb; ++i) {
0673         const SKGObjectBase& obj = iSelection.at(i);
0674         SKGOperationObject operationObj;
0675         if (obj.getTable() == QStringLiteral("v_suboperation_consolidated")) {
0676             operationObj = SKGOperationObject(obj.getDocument(), SKGServices::stringToInt(obj.getAttribute(QStringLiteral("i_OPID"))));
0677         } else {
0678             operationObj = SKGOperationObject(obj.getDocument(), obj.getID());
0679         }
0680 
0681         SKGObjectBase::SKGListSKGObjectBase gops;
0682         IFOKDO(err, operationObj.getGroupedOperations(gops))
0683         if (gops.count() == 2 &&  ui.kWidgetSelector->getSelectedMode() < 2) {
0684             getDocument()->sendMessage(i18nc("An information message", "You modified one part of a transfer"), SKGDocument::Warning);
0685         }
0686 
0687         // Update transaction if single selection
0688         if (ui.kWidgetSelector->getSelectedMode() == 0) {
0689             // Get subop
0690             SKGSubOperationObject subOp;
0691             int nbSubop = 0;
0692             if (obj.getTable() == QStringLiteral("v_suboperation_consolidated")) {
0693                 // It is a sub operation
0694                 subOp = SKGSubOperationObject(obj.getDocument(), SKGServices::stringToInt(obj.getAttribute(QStringLiteral("i_SUBOPID"))));
0695                 nbSubop = 1;
0696             } else {
0697                 // It is a real operation, we take the first one
0698                 SKGObjectBase::SKGListSKGObjectBase subOps;
0699                 IFOKDO(err, operationObj.getSubOperations(subOps))
0700                 nbSubop = subOps.count();
0701                 if (nbSubop != 0) {
0702                     subOp = subOps[0];
0703                 }
0704             }
0705 
0706             QString trackerName = ui.kTrackerEdit->text().trimmed();
0707             if (!err && trackerName != NOUPDATE) {
0708                 SKGTrackerObject tracker;
0709                 err = SKGTrackerObject::createTracker(qobject_cast<SKGDocumentBank*>(getDocument()), trackerName, tracker, true);
0710                 IFOKDO(err, subOp.setTracker(tracker))
0711             }
0712 
0713             SKGCategoryObject cat;
0714             QString catName = ui.kCategoryEdit->text().trimmed();
0715             if (!err &&  catName != NOUPDATE) {
0716                 err = SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), catName, cat, true);
0717                 IFOKDO(err, subOp.setCategory(cat))
0718             } else {
0719                 // Get current category to be able to find the appropriate sign
0720                 subOp.getCategory(cat);
0721             }
0722 
0723             if (!err && ui.kAmountEdit->text() != NOUPDATE) {
0724                 if (nbSubop > 1) {
0725                     err = SKGError(25, i18nc("Error message", "Cannot update a split transaction"));
0726                 } else {
0727                     double val = ui.kAmountEdit->value();
0728 
0729                     // Is the sign forced ?
0730                     if (ui.kAmountEdit->sign() == 0) {
0731                         // No
0732                         SKGObjectBase cat2(cat.getDocument(), QStringLiteral("v_category_display"), cat.getID());
0733 
0734                         // Are we able to find to sign with the category ?
0735                         if (cat2.getAttribute(QStringLiteral("t_TYPEEXPENSE")) == QStringLiteral("-")) {
0736                             val = -val;
0737                         }
0738                     }
0739                     err = subOp.setQuantity(val);
0740                 }
0741             }
0742 
0743             if (!err && ui.kCommentEdit->text() != NOUPDATE) {
0744                 err = subOp.setComment(ui.kCommentEdit->text());
0745             }
0746             IFOKDO(err, subOp.save())
0747         } else if (ui.kWidgetSelector->getSelectedMode() == 1) {
0748             // Case split
0749             refreshSubOperation = false;
0750             int nbsubop = ui.kSubOperationsTable->rowCount();
0751             QList<int> listIdSubOp;  // clazy:exclude=container-inside-loop
0752             listIdSubOp.reserve(nbsubop);
0753             for (int j = 0; !err && j < nbsubop; ++j) {
0754                 // Get values
0755                 QTableWidgetItem* item = ui.kSubOperationsTable->item(j, m_attributesForSplit.indexOf(QStringLiteral("t_category")));
0756                 int id = (iForceCreation ? 0 : item->data(Qt::UserRole).toInt());
0757                 QString catName = item->text();
0758 
0759                 item = ui.kSubOperationsTable->item(j, m_attributesForSplit.indexOf(QStringLiteral("t_comment")));
0760                 QString comment = item->text();
0761 
0762                 item = ui.kSubOperationsTable->item(j, m_attributesForSplit.indexOf(QStringLiteral("f_value")));
0763                 double val = item->data(101).toDouble();
0764                 QString formula = item->toolTip();
0765 
0766                 item = ui.kSubOperationsTable->item(j, m_attributesForSplit.indexOf(QStringLiteral("t_refund")));
0767                 QString trackerName = item->text();
0768 
0769                 item = ui.kSubOperationsTable->item(j, m_attributesForSplit.indexOf(QStringLiteral("d_date")));
0770                 QDate date = SKGServices::stringToTime(item->toolTip()).date();
0771                 if (!date.isValid()) {
0772                     date = ui.kDateEdit->date();
0773                 }
0774 
0775                 SKGSubOperationObject subOperation;
0776                 if (id != 0) {
0777                     // Update existing sub op
0778                     subOperation = SKGSubOperationObject(qobject_cast<SKGDocumentBank*>(getDocument()), id);
0779                 } else {
0780                     // Create new sub op
0781                     err = operationObj.addSubOperation(subOperation);
0782                 }
0783 
0784                 // Create sub transaction object
0785                 IFOK(err) {
0786                     SKGCategoryObject cat;
0787                     err = SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), catName, cat, true);
0788                     IFOKDO(err, subOperation.setCategory(cat))
0789                 }
0790                 IFOK(err) {
0791                     SKGTrackerObject tracker;
0792                     err = SKGTrackerObject::createTracker(qobject_cast<SKGDocumentBank*>(getDocument()), trackerName, tracker, true);
0793                     IFOKDO(err, subOperation.setTracker(tracker))
0794                 }
0795                 IFOKDO(err, subOperation.setOrder(ui.kSubOperationsTable->visualRow(j)))
0796                 IFOKDO(err, subOperation.setDate(date))
0797                 IFOKDO(err, subOperation.setComment(comment))
0798                 IFOKDO(err, subOperation.setQuantity(val))
0799                 if (formula.startsWith(QLatin1String("=")) && !err) {
0800                     err = subOperation.setFormula(formula);
0801                 }
0802                 IFOKDO(err, subOperation.save())
0803 
0804                 // The sub transaction created or updated mustn't be removed
0805                 listIdSubOp.push_back(subOperation.getID());
0806             }
0807 
0808             // Remove useless subop
0809             IFOK(err) {
0810                 SKGObjectBase::SKGListSKGObjectBase subOps;
0811                 err = operationObj.getSubOperations(subOps);
0812                 int nbsubop2 = subOps.count();
0813                 for (int j = 0; !err && j < nbsubop2; ++j) {
0814                     const SKGObjectBase& sop = subOps.at(j);
0815                     if (!listIdSubOp.contains(sop.getID())) {
0816                         err = sop.remove(false);
0817                     }
0818                 }
0819             }
0820         } else if (ui.kWidgetSelector->getSelectedMode() == 2) {
0821             // Case transfer
0822             // Create sub transaction object
0823             double operationQuantity = ui.kAmountEdit->value();
0824 
0825 
0826             SKGSubOperationObject subOperation;
0827             SKGObjectBase::SKGListSKGObjectBase subOps;
0828             IFOKDO(err, operationObj.getSubOperations(subOps))
0829             IFOK(err) {
0830                 subOperation = subOps.at(0);
0831 
0832                 double oldQuantity = subOperation.getQuantity();
0833 
0834                 if (ui.kAmountEdit->sign() == 0) {
0835                     operationQuantity = qAbs(operationQuantity);
0836                 } else if (ui.kAmountEdit->sign() > 0) {
0837                     operationQuantity = -qAbs(operationQuantity);
0838                 } else {
0839                     operationQuantity = qAbs(operationQuantity);
0840                     if (oldQuantity == 0) {
0841                         err = getDocument()->sendMessage(i18nc("An information message",  "Absolute value has been used for transfer creation."));
0842                     }
0843                 }
0844 
0845                 if (ui.kAmountEdit->text().trimmed() != NOUPDATE) {
0846                     err = subOperation.setQuantity(-operationQuantity);
0847                 } else {
0848                     operationQuantity = -subOperation.getQuantity();
0849                 }
0850             }
0851 
0852             SKGTrackerObject tracker;
0853             QString trackerName = ui.kTrackerEdit->text().trimmed();
0854             if (!err) {
0855                 if (trackerName != NOUPDATE) {
0856                     err = SKGTrackerObject::createTracker(qobject_cast<SKGDocumentBank*>(getDocument()), trackerName, tracker, true);
0857                     IFOKDO(err, subOperation.setTracker(tracker))
0858                 } else {
0859                     err = subOperation.getTracker(tracker);
0860                 }
0861             }
0862 
0863             SKGCategoryObject cat;
0864             QString catName = ui.kCategoryEdit->text().trimmed();
0865             if (!err) {
0866                 if (catName != NOUPDATE) {
0867                     err = SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), ui.kCategoryEdit->text(), cat, true);
0868                     IFOKDO(err, subOperation.setCategory(cat))
0869                 } else {
0870                     err = subOperation.getCategory(cat);
0871                 }
0872             }
0873             IFOKDO(err, subOperation.setComment(operationObj.getComment()))
0874             IFOKDO(err, subOperation.save())
0875 
0876             // Get account
0877             SKGAccountObject accountObj2(getDocument());
0878             IFOKDO(err, accountObj2.setName(ui.kTargetAccountEdit->currentText()))
0879             IFOKDO(err, accountObj2.load())
0880 
0881             // Check unit of target account
0882             SKGUnitObject unit;
0883             IFOKDO(err, operationObj.getUnit(unit))
0884 
0885             // Get date
0886             QDate operationDate;
0887             if (ui.kDateEdit->currentText().trimmed() != NOUPDATE) {
0888                 operationDate = ui.kDateEdit->date();
0889             } else {
0890                 operationDate = operationObj.getDate();
0891             }
0892 
0893 
0894             // Correction bug 2299303 vvv
0895             SKGUnitObject unitTargetAccount;
0896             IFOKDO(err, accountObj2.getUnit(unitTargetAccount))
0897             if (!err && unitTargetAccount.exist() && unit != unitTargetAccount) {
0898                 // The unit of the transaction is not compliant with the unit of the target account
0899                 // We ask to the user if he wants to continue or convert into the target account
0900                 bool ok = false;
0901                 QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
0902                 int decimal = qMax(unit.getNumberDecimal(), unitTargetAccount.getNumberDecimal());
0903                 double newval;
0904                 double defaultnewval = SKGUnitObject::convert(operationQuantity, unit, unitTargetAccount, operationDate);
0905                 if (nb == 1) {
0906                     newval = QInputDialog::getDouble(SKGMainPanel::getMainPanel(),
0907                                                      i18nc("Question", "Confirmation"),
0908                                                      i18nc("Question", "The operation's unit is not compatible with the target account.\n"
0909                                                            "Click Cancel if you want to continue anyway; "
0910                                                            "otherwise, enter the value in the target account's unit (%1):", unitTargetAccount.getSymbol()),
0911                                                      defaultnewval,
0912                                                      -std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), decimal, &ok);
0913                 } else {
0914                     if (rate == -1) {
0915                         newval = QInputDialog::getDouble(SKGMainPanel::getMainPanel(),
0916                                                          i18nc("Question", "Confirmation"),
0917                                                          i18nc("Question", "Some operation's units are not compatible with the target account.\n"
0918                                                                "The first selected transaction is:%1\n"
0919                                                                "Click Cancel if you want to continue anyway; "
0920                                                                "otherwise, enter the value in the target account's unit (%2):\nThe same rate will be applied to all transactions.", operationObj.getDisplayName(), unitTargetAccount.getSymbol()),
0921                                                          defaultnewval,
0922                                                          -std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), decimal, &ok);
0923                         if (ok) {
0924                             rate = defaultnewval / newval;
0925                         } else {
0926                             // Mode cancel for all
0927                             rate = -2;
0928                         }
0929                     } else {
0930                         if (rate != -2) {
0931                             ok = true;
0932                             newval = defaultnewval / rate;
0933                         }
0934                     }
0935                 }
0936                 QApplication::restoreOverrideCursor();
0937                 if (ok) {
0938                     operationQuantity = newval;
0939                     unit = unitTargetAccount;
0940                 } else {
0941                     // Mode cancel for all
0942                     rate = -2;
0943                 }
0944             }
0945             // Correction bug 2299303 ^^^
0946 
0947             // create transferred operation
0948             SKGOperationObject operation2;
0949 
0950             IFOK(err) {
0951                 if (gops.count() == 2) {
0952                     operation2 = (obj == SKGOperationObject(gops.at(0)) ? gops.at(1) : gops.at(0));
0953                     if (ui.kTargetAccountEdit->text() != NOUPDATE) {
0954                         err = operation2.setParentAccount(accountObj2);
0955                     }
0956                 } else {
0957                     err = accountObj2.addOperation(operation2);
0958                 }
0959             }
0960             if (nb == 1) {
0961                 IFOKDO(err, operation2.setMode(ui.kTypeEdit->currentText()))
0962                 QString payeeName = ui.kPayeeEdit->currentText();
0963                 if (!err &&  payeeName != NOUPDATE) {
0964                     SKGPayeeObject payeeObject;
0965                     err = SKGPayeeObject::createPayee(qobject_cast<SKGDocumentBank*>(getDocument()), payeeName, payeeObject, true);
0966                     IFOKDO(err, operation2.setPayee(payeeObject))
0967                 }
0968                 IFOKDO(err, operation2.setNumber(ui.kNumberEdit->text()))
0969                 IFOKDO(err, operation2.setComment(ui.kCommentEdit->text()))
0970                 IFOKDO(err, operation2.setDate(operationDate, refreshSubOperation))
0971                 IFOKDO(err, operation2.setUnit(unit))
0972 
0973             } else {
0974                 IFOKDO(err, operation2.setMode(operationObj.getMode()))
0975                 IFOKDO(err, operation2.setAttribute(QStringLiteral("r_payee_id"), operationObj.getAttribute(QStringLiteral("r_payee_id"))))
0976                 IFOKDO(err, operation2.setNumber(operationObj.getNumber()))
0977                 IFOKDO(err, operation2.setComment(operationObj.getComment()))
0978                 IFOKDO(err, operation2.setDate(operationDate, refreshSubOperation))
0979                 IFOKDO(err, operation2.setUnit(unit))
0980             }
0981             IFOKDO(err, operation2.setGroupOperation(operationObj))
0982             IFOKDO(err, operationObj.load())
0983             IFOKDO(err, operation2.setTemplate(isTemplateMode()))
0984             IFOKDO(err, operation2.save())
0985 
0986             // Create sub transaction object
0987             SKGSubOperationObject subOperation2;
0988 
0989             SKGObjectBase::SKGListSKGObjectBase subops;
0990             IFOKDO(err, operation2.getSubOperations(subops))
0991             IFOK(err) {
0992                 if (!subops.isEmpty()) {
0993                     subOperation2 = subops.at(0);
0994                 } else {
0995                     err = operation2.addSubOperation(subOperation2);
0996                 }
0997             }
0998             IFOKDO(err, subOperation2.setQuantity(operationQuantity))
0999             IFOKDO(err, subOperation2.setTracker(tracker))
1000             IFOKDO(err, subOperation2.setCategory(cat))
1001             IFOKDO(err, subOperation2.setComment(operationObj.getComment()))
1002             IFOKDO(err, subOperation2.save())
1003         }
1004 
1005         IFOKDO(err, operationObj.setTemplate(isTemplateMode()))
1006         if (ui.kDateEdit->currentText() != NOUPDATE) {
1007             IFOKDO(err, operationObj.setDate(ui.kDateEdit->date(), refreshSubOperation))
1008         }
1009 
1010         if (nb == 1) {
1011             IFOKDO(err, operationObj.setNumber(ui.kNumberEdit->text()))
1012         }
1013         if (!err && ui.kCommentEdit->text() != NOUPDATE) {
1014             if (obj.getTable() != QStringLiteral("v_suboperation_consolidated") || obj.getAttribute(QStringLiteral("i_NBSUBOPERATIONS")) == QStringLiteral("1")) {
1015                 err = operationObj.setComment(ui.kCommentEdit->text());
1016             }
1017         }
1018 
1019         if (!err && ui.kAccountEdit->text() != NOUPDATE) {
1020             SKGAccountObject account(getDocument());
1021             err = account.setName(ui.kAccountEdit->text());
1022             IFOKDO(err, account.load())
1023             IFOKDO(err, operationObj.setParentAccount(account))
1024         }
1025         if (!err && ui.kTypeEdit->text() != NOUPDATE) {
1026             err = operationObj.setMode(ui.kTypeEdit->text());
1027         }
1028         QString payeeName = ui.kPayeeEdit->currentText();
1029         if (!err &&  payeeName != NOUPDATE) {
1030             SKGPayeeObject payeeObject;
1031             err = SKGPayeeObject::createPayee(qobject_cast<SKGDocumentBank*>(getDocument()), payeeName, payeeObject, true);
1032             IFOKDO(err, operationObj.setPayee(payeeObject))
1033         }
1034         if (!err && ui.kUnitEdit->text() != NOUPDATE) {
1035             // Correction bug 282983 vvv
1036             // Check unit of target account
1037             SKGAccountObject account;
1038             IFOKDO(err, operationObj.getParentAccount(account))
1039             SKGUnitObject unitTargetAccount;
1040             IFOKDO(err, account.getUnit(unitTargetAccount))
1041 
1042             SKGUnitObject unit = ui.kUnitEdit->getUnit();
1043 
1044             if (!err && unitTargetAccount.exist() && unit != unitTargetAccount) {
1045                 // Correction bug 283842 vvvv
1046                 bool ok = false;
1047                 if (ratio == -1) {
1048                     // The unit of the transaction is not compliant with the unit of the target account
1049                     // We ask to the user if he wants to continue or convert into the target account
1050                     QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
1051                     double currentAmount = ui.kAmountEdit->value();
1052                     int decimal = qMax(unit.getNumberDecimal(), unitTargetAccount.getNumberDecimal());
1053                     double newval = QInputDialog::getDouble(SKGMainPanel::getMainPanel(),
1054                                                             i18nc("Question", "Confirmation"),
1055                                                             i18nc("Question", "The operation's unit is not compatible with the target account.\n"
1056                                                                     "Click Cancel if you want to continue anyway; "
1057                                                                     "otherwise, enter the value in the target account's unit (%1):", unitTargetAccount.getSymbol()),
1058                                                             SKGUnitObject::convert(currentAmount, unit, unitTargetAccount, ui.kDateEdit->date()),
1059                                                             -std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), decimal, &ok);
1060                     ratio = newval / currentAmount;
1061                     QApplication::restoreOverrideCursor();
1062                 }
1063                 if (ok) {
1064                     // Apply ratio to all operation
1065                     SKGObjectBase::SKGListSKGObjectBase subops;
1066                     err = operationObj.getSubOperations(subops);
1067                     int nbsubops = subops.count();
1068                     for (int j = 0; !err && j < nbsubops; ++j) {
1069                         SKGSubOperationObject subop(subops.at(j));
1070                         err = subop.setQuantity(subop.getQuantity() * ratio);
1071                         IFOKDO(err, subop.save(true, false))
1072                     }
1073 
1074                     // Change unit
1075                     unit = unitTargetAccount;
1076                 } else {
1077                     err = getDocument()->sendMessage(i18nc("Warning message", "You created a transaction in %1 in an account in %2.", unit.getSymbol(), unitTargetAccount.getSymbol()), SKGDocument::Warning);
1078                 }
1079                 // Correction bug 283842 ^^^^
1080             }
1081             // Correction bug 282983 ^^^
1082             IFOKDO(err, operationObj.setUnit(unit))
1083         }
1084 
1085         // Save
1086         IFOKDO(err, operationObj.save())
1087 
1088         // Send message
1089         if (!iForceCreation) {
1090             IFOKDO(err, operationObj.getDocument()->sendMessage(i18nc("An information message", "The transaction '%1' has been updated", operationObj.getDisplayName()), SKGDocument::Hidden))
1091         }
1092 
1093         IFOKDO(err, getDocument()->stepForward(i + 1))
1094     }
1095 
1096     return err;
1097 }
1098 
1099 void SKGOperationPluginWidget::onAddOperationClicked()
1100 {
1101     SKGError err;
1102     SKGTRACEINFUNC(10)
1103 
1104     // Get parameters
1105     QString accountName = ui.kAccountEdit->currentText();
1106     SKGOperationObject operation;
1107     {
1108         SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Transaction creation"), err)
1109 
1110         // Check entries
1111         if (ui.kAccountEdit->text() == NOUPDATE ||
1112             ui.kTypeEdit->text() == NOUPDATE ||
1113             ui.kUnitEdit->text() == NOUPDATE ||
1114             ui.kCategoryEdit->text() == NOUPDATE ||
1115             ui.kTrackerEdit->text() == NOUPDATE ||
1116             ui.kCommentEdit->text() == NOUPDATE ||
1117             ui.kAmountEdit->text() == NOUPDATE ||
1118             ui.kDateEdit->currentText() == NOUPDATE ||
1119             ui.kPayeeEdit->text() == NOUPDATE) {
1120             err = SKGError(ERR_FAIL, i18nc("Error message", "Impossible to create a transaction with one attribute valuated with %1", NOUPDATE));
1121         }
1122 
1123         // Get account
1124         SKGAccountObject accountObj(getDocument());
1125         IFOKDO(err, accountObj.setName(accountName))
1126         IFOKDO(err, accountObj.load())
1127 
1128         // Create transaction object
1129         IFOKDO(err, accountObj.addOperation(operation))
1130         IFOKDO(err, operation.setMode(ui.kTypeEdit->currentText()))
1131         SKGPayeeObject payeeObject;
1132         IFOK(err) {
1133             QString payeeName = ui.kPayeeEdit->currentText();
1134             err = SKGPayeeObject::createPayee(qobject_cast<SKGDocumentBank*>(getDocument()), payeeName, payeeObject, true);
1135             IFOKDO(err, operation.setPayee(payeeObject))
1136         }
1137         IFOKDO(err, operation.setNumber(ui.kNumberEdit->text()))
1138         IFOKDO(err, operation.setComment(ui.kCommentEdit->text()))
1139         IFOKDO(err, operation.setDate(ui.kDateEdit->date()))
1140         IFOKDO(err, operation.setTemplate(isTemplateMode()))
1141         SKGUnitObject unit = ui.kUnitEdit->getUnit();
1142         IFOKDO(err, operation.setUnit(unit))
1143         if (skgoperation_settings::automaticPointInReconciliation() && m_modeInfoZone == 1) {
1144             IFOKDO(err, operation.setStatus(SKGOperationObject::MARKED))
1145         }
1146         IFOKDO(err, operation.save())
1147 
1148         if (ui.kWidgetSelector->getSelectedMode() <= 2) {
1149             // STD OPERATION (SPLIT , TRANSFER OR NOT)
1150             // We must create a suboperation, just be sure to be able to update it
1151             SKGSubOperationObject subOperation;
1152             IFOKDO(err, operation.addSubOperation(subOperation))
1153             IFOKDO(err, subOperation.setQuantity(0))
1154             IFOKDO(err, subOperation.save())
1155 
1156             SKGObjectBase::SKGListSKGObjectBase list;
1157             list << operation;
1158             IFOKDO(err, updateSelection(list, true))
1159 
1160         } else if (ui.kWidgetSelector->getSelectedMode() == 3) {
1161             // PURCHASE OR SALE SHARE
1162             // Create sub transaction object
1163             SKGSubOperationObject subOperation;
1164             double val = ui.kAmountEdit->value();
1165             IFOKDO(err, operation.addSubOperation(subOperation))
1166             IFOKDO(err, subOperation.setQuantity(val))
1167             IFOKDO(err, subOperation.save())
1168 
1169             if (!err && val > 0) {
1170                 err = operation.setProperty(QStringLiteral("SKG_OP_ORIGINAL_AMOUNT"), SKGServices::doubleToString(ui.kAmountSharesEdit->value()));
1171             }
1172             IFOKDO(err, operation.save())
1173 
1174             // Get account
1175             SKGAccountObject accountObj2(getDocument());
1176             IFOKDO(err, accountObj2.setName(ui.kPaymentAccountEdit->currentText()))
1177             IFOKDO(err, accountObj2.load())
1178 
1179             SKGUnitObject unit = ui.kUnitShare->getUnit();
1180 
1181             SKGUnitObject unitTargetAccount;
1182             IFOKDO(err, accountObj2.getUnit(unitTargetAccount))
1183             double ratio = 1.0;
1184             if (!err && unitTargetAccount.exist() && unit != unitTargetAccount) {
1185                 // The unit of the transaction is not compliant with the unit of the target account
1186                 // We ask to the user if he wants to continue or convert into the target account
1187                 bool ok = false;
1188                 QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
1189                 int decimal = qMax(unit.getNumberDecimal(), unitTargetAccount.getNumberDecimal());
1190                 double defaultnewval = SKGUnitObject::convert(ui.kAmountSharesEdit->value(), unit, unitTargetAccount, ui.kDateEdit->date());
1191                 double newval = QInputDialog::getDouble(SKGMainPanel::getMainPanel(),
1192                                                         i18nc("Question", "Confirmation"),
1193                                                         i18nc("Question", "The payment's unit (%1) is not compatible with the target account (%2).\n"
1194                                                                 "Click Cancel if you want to continue anyway; "
1195                                                                 "otherwise, enter the value in the target account's unit (%3):", unit.getSymbol(), accountObj2.getName(), unitTargetAccount.getSymbol()),
1196                                                         defaultnewval,
1197                                                         -std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), decimal, &ok);
1198                 if (ok) {
1199                     ratio = newval / ui.kAmountSharesEdit->value();
1200                     unit = unitTargetAccount;
1201                 }
1202                 QApplication::restoreOverrideCursor();
1203             }
1204 
1205             // create payment transaction for shares
1206             SKGOperationObject operation2;
1207             IFOKDO(err, accountObj2.addOperation(operation2))
1208             IFOKDO(err, operation2.setMode(ui.kTypeEdit->currentText()))
1209             IFOKDO(err, operation2.setPayee(payeeObject))
1210             IFOKDO(err, operation2.setNumber(ui.kNumberEdit->text()))
1211             IFOKDO(err, operation2.setComment(ui.kCommentEdit->text()))
1212             IFOKDO(err, operation2.setDate(ui.kDateEdit->date()))
1213             IFOKDO(err, operation2.setUnit(unit))
1214             IFOKDO(err, operation2.setGroupOperation(operation))
1215             IFOKDO(err, operation2.setTemplate(isTemplateMode()))
1216             IFOKDO(err, operation2.save())
1217 
1218             // Create main sub operation
1219             SKGSubOperationObject subOperation2;
1220             IFOKDO(err, operation2.addSubOperation(subOperation2))
1221             IFOKDO(err, subOperation2.setComment(i18nc("Noun", "Shares")))
1222             IFOKDO(err, subOperation2.setQuantity(ui.kAmountSharesEdit->value() * (val > 0 ? -1 : 1) * ratio))
1223             IFOKDO(err, subOperation2.save())
1224 
1225             // Create commission sub operation
1226             if (ui.kCommissionEdit->value() != 0.0) {
1227                 SKGSubOperationObject subOperation3;
1228                 IFOKDO(err, operation2.addSubOperation(subOperation3))
1229                 IFOKDO(err, subOperation3.setComment(skgoperation_settings::commentCommissionOperation()))
1230 
1231                 QString category = skgoperation_settings::categoryCommissionOperation();
1232                 if (!category.isEmpty()) {
1233                     SKGCategoryObject c;
1234                     IFOKDO(err, SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), category, c, true))
1235                     IFOKDO(err, subOperation3.setCategory(c))
1236                 }
1237 
1238                 IFOKDO(err, subOperation3.setQuantity(-ui.kCommissionEdit->value() * ratio))
1239                 IFOKDO(err, subOperation3.save())
1240             }
1241 
1242             // Create tax sub operation
1243             if (ui.kTaxEdit->value() != 0.0) {
1244                 SKGSubOperationObject subOperation4;
1245                 IFOKDO(err, operation2.addSubOperation(subOperation4))
1246                 IFOKDO(err, subOperation4.setComment(skgoperation_settings::commentTaxOperation()))
1247 
1248                 QString category = skgoperation_settings::categoryTaxOperation();
1249                 if (!category.isEmpty()) {
1250                     SKGCategoryObject c;
1251                     IFOKDO(err, SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), category, c, true))
1252                     IFOKDO(err, subOperation4.setCategory(c))
1253                 }
1254 
1255                 IFOKDO(err, subOperation4.setQuantity(-ui.kTaxEdit->value() * ratio))
1256                 IFOKDO(err, subOperation4.save())
1257             }
1258         }
1259 
1260         // Send message
1261         IFOKDO(err, operation.getDocument()->sendMessage(i18nc("An information to the user that something was added", "The transaction '%1' has been added", operation.getDisplayName()), SKGDocument::Hidden))
1262     }
1263 
1264     // status bar
1265     IFOK(err) {
1266         err = SKGError(0, i18nc("Successful message after an user action", "Transaction created"));
1267         ui.kOperationView->getView()->selectObject(operation.getUniqueID());
1268     } else {
1269         err.addError(ERR_FAIL, i18nc("Error message",  "Transaction creation failed"));
1270     }
1271 
1272     // Display error
1273     SKGMainPanel::displayErrorMessage(err, true);
1274 
1275     // Set focus on date
1276     ui.kDateEdit->setFocus();
1277 }
1278 
1279 SKGTreeView* SKGOperationPluginWidget::getTableView()
1280 {
1281     return ui.kOperationView->getView();
1282 }
1283 
1284 QString SKGOperationPluginWidget::getState()
1285 {
1286     SKGTRACEINFUNC(10)
1287     QDomDocument doc(QStringLiteral("SKGML"));
1288     QDomElement root;
1289     if (m_lastState.hasChildNodes()) {
1290         doc = m_lastState;
1291         root = doc.documentElement();
1292     } else {
1293         root = doc.createElement(QStringLiteral("parameters"));
1294         doc.appendChild(root);
1295     }
1296 
1297     root.setAttribute(QStringLiteral("currentPage"), SKGServices::intToString(ui.kWidgetSelector->getSelectedMode()));
1298     root.setAttribute(QStringLiteral("modeInfoZone"), SKGServices::intToString(m_modeInfoZone));
1299     root.setAttribute(QStringLiteral("reconcilitorAmount"), ui.kReconcilitorAmountEdit->text());
1300     root.removeAttribute(QStringLiteral("account"));
1301 
1302     // Memorize table settings
1303     root.setAttribute(QStringLiteral("view"), ui.kOperationView->getState());
1304 
1305     return doc.toString();
1306 }
1307 
1308 void SKGOperationPluginWidget::setState(const QString& iState)
1309 {
1310     SKGTRACEINFUNC(10)
1311     QDomDocument doc(QStringLiteral("SKGML"));
1312     doc.setContent(iState);
1313     QDomElement root = doc.documentElement();
1314 
1315     QString account = root.attribute(QStringLiteral("account"));
1316     QString currentPage = root.attribute(QStringLiteral("currentPage"));
1317     QString title = root.attribute(QStringLiteral("title"));
1318     QString title_icon = root.attribute(QStringLiteral("title_icon"));
1319     QString modeInfoZoneS = root.attribute(QStringLiteral("modeInfoZone"));
1320     QString reconcilitorAmountS = root.attribute(QStringLiteral("reconcilitorAmount"));
1321     QString operationTable = root.attribute(QStringLiteral("operationTable"));
1322     QString selection = root.attribute(QStringLiteral("selection"));
1323     QString templates = root.attribute(QStringLiteral("template"));
1324     m_operationWhereClause = root.attribute(QStringLiteral("operationWhereClause"));
1325 
1326     // Default values in case of reset
1327     if (currentPage.isEmpty()) {
1328         currentPage = '0';
1329     }
1330     if (operationTable.isEmpty()) {
1331         if (m_operationWhereClause.isEmpty()) {
1332             operationTable = QStringLiteral("v_operation_display_all");
1333         } else {
1334             operationTable = QStringLiteral("v_operation_display");
1335         }
1336     }
1337 
1338     // Set
1339     SKGAccountObject acc;
1340     SKGNamedObject::getObjectByName(getDocument(), QStringLiteral("v_account"), account, acc);
1341     if (acc.isClosed() && !m_showClosedAccounts) {
1342         m_showClosedAccounts = true;
1343         dataModified(QLatin1String(""), 0);
1344     }
1345     bool previous = ui.kReconcilitorAmountEdit->blockSignals(true);
1346     ui.kReconcilitorAmountEdit->setText(reconcilitorAmountS);
1347     ui.kReconcilitorAmountEdit->blockSignals(previous);
1348     ui.kWidgetSelector->setSelectedMode(SKGServices::stringToInt(currentPage));
1349     if (!title.isEmpty()) {
1350         QFontMetrics fm(fontMetrics());
1351         ui.kTitle->setComment("<html><body><b>" % SKGServices::stringToHtml(fm.elidedText(title, Qt::ElideMiddle, 2000)) % "</b></body></html>");
1352         ui.kTitle->setToolTip(title);
1353         ui.kTitle->show();
1354     }
1355     if (!title_icon.isEmpty()) {
1356         ui.kTitle->setIcon(SKGServices::fromTheme(title_icon), KTitleWidget::ImageLeft);
1357     }
1358 
1359     if (m_objectModel != nullptr) {
1360         m_objectModel->setTable(operationTable);
1361     }
1362     if ((m_objectModel != nullptr) && !m_operationWhereClause.isEmpty()) {
1363         ui.kOperationView->getShowWidget()->setEnabled(false);
1364         m_objectModel->setFilter(m_operationWhereClause);
1365     }
1366     if (!operationTable.isEmpty() || !m_operationWhereClause.isEmpty()) {
1367         // We keep a copy of given state in case of bookmark
1368         m_lastState = doc;
1369     } else {
1370         m_lastState = QDomDocument();
1371     }
1372 
1373     // Update model
1374     if (m_objectModel != nullptr) {
1375         previous = m_objectModel->blockRefresh(true);
1376         onAccountChanged();
1377         m_objectModel->blockRefresh(previous);
1378     }
1379 
1380     // !!! Must be done here after onFilterChanged
1381     QString v = root.attribute(QStringLiteral("view"));
1382     if (!v.isEmpty()) {
1383         ui.kOperationView->setState(v);
1384     }
1385     if (!account.isEmpty()) {
1386         QStringList parameters = SKGServices::splitCSVLine(ui.kOperationView->getShowWidget()->getState());
1387         if (parameters.isEmpty()) {
1388             parameters.push_back(QLatin1String(""));
1389             parameters.push_back(QStringLiteral("transactions"));
1390             parameters.push_back(QStringLiteral("hide"));
1391         }
1392         parameters[0] = "##_" % account;
1393         ui.kOperationView->getShowWidget()->setState(SKGServices::stringsToCsv(parameters));
1394     }
1395 
1396     if (templates == QStringLiteral("Y")) {
1397         QStringList parameters = SKGServices::splitCSVLine(ui.kOperationView->getShowWidget()->getState());
1398         parameters.removeAll(QStringLiteral("transactions"));
1399         parameters.push_back(QStringLiteral("templates"));
1400         ui.kOperationView->getShowWidget()->setState(SKGServices::stringsToCsv(parameters));
1401     }
1402 
1403     QStringList parameters = SKGServices::splitCSVLine(ui.kOperationView->getShowWidget()->getState());
1404     if (!parameters.contains(QStringLiteral("transactions")) && !parameters.contains(QStringLiteral("templates"))) {
1405         parameters.push_back(QStringLiteral("transactions"));
1406         ui.kOperationView->getShowWidget()->setState(SKGServices::stringsToCsv(parameters));
1407     }
1408 
1409     if (!selection.isEmpty()) {
1410         QStringList uuids = SKGServices::splitCSVLine(selection);
1411         ui.kOperationView->getView()->selectObjects(uuids, true);  // FIXME // TODO(Stephane MANKOWSKI)
1412         onSelectionChanged();
1413     }
1414 
1415     // Refresh of the information zone
1416     if (!modeInfoZoneS.isEmpty()) {
1417         m_modeInfoZone = SKGServices::stringToInt(modeInfoZoneS) - 1;
1418     } else {
1419         m_modeInfoZone = -1;
1420     }
1421     onRotateAccountTools();
1422 }
1423 
1424 QString SKGOperationPluginWidget::getDefaultStateAttribute()
1425 {
1426     if ((m_objectModel != nullptr) && m_objectModel->getTable() == QStringLiteral("v_suboperation_consolidated")) {
1427         return QStringLiteral("SKGOPERATION_CONSOLIDATED_DEFAULT_PARAMETERS");
1428     }
1429 
1430     if (!m_operationWhereClause.isEmpty()) {
1431         return QLatin1String("");
1432     }
1433 
1434     return QStringLiteral("SKGOPERATION_DEFAULT_PARAMETERS");
1435 }
1436 
1437 void SKGOperationPluginWidget::fillTargetAccount()
1438 {
1439     int nbAccounts = ui.kAccountEdit->count();
1440     QString current = ui.kAccountEdit->text();
1441     QString currentTarget = ui.kTargetAccountEdit->text();
1442     QString currentRecon = ui.kReconciliateAccount->text();
1443     ui.kTargetAccountEdit->clear();
1444     ui.kReconciliateAccount->clear();
1445     ui.kReconciliateAccount->addItem(QLatin1String(""));
1446     for (int i = 0; i < nbAccounts; ++i) {
1447         if (ui.kAccountEdit->itemText(i) != current) {
1448             ui.kTargetAccountEdit->addItem(ui.kAccountEdit->itemIcon(i), ui.kAccountEdit->itemText(i));
1449             ui.kReconciliateAccount->addItem(ui.kAccountEdit->itemIcon(i), ui.kAccountEdit->itemText(i));
1450         }
1451     }
1452     if (ui.kTargetAccountEdit->contains(currentTarget)) {
1453         ui.kTargetAccountEdit->setText(currentTarget);
1454     }
1455 
1456     SKGError err;
1457     SKGAccountObject currentActObj(getDocument());
1458     IFOKDO(err, currentActObj.setName(current))
1459     IFOKDO(err, currentActObj.load())
1460 
1461     SKGAccountObject linkedActObj;
1462     IFOKDO(err, currentActObj.getLinkedAccount(linkedActObj))
1463     if (linkedActObj.getID() != 0) {
1464         currentRecon = linkedActObj.getName();
1465     }
1466 
1467     if (ui.kReconciliateAccount->contains(currentRecon)) {
1468         ui.kReconciliateAccount->setText(currentRecon);
1469     }
1470 }
1471 
1472 void SKGOperationPluginWidget::dataModified(const QString& iTableName, int iIdTransaction, bool iLightTransaction)
1473 {
1474     SKGTRACEINFUNC(10)
1475     Q_UNUSED(iIdTransaction)
1476 
1477     // Refresh widgets
1478     QSqlDatabase* db = getDocument()->getMainDatabase();
1479     setEnabled(db != nullptr);
1480     if (db != nullptr) {
1481         if (iTableName == QStringLiteral("account") || iTableName.isEmpty()) {
1482             SKGShow* showWidget = ui.kOperationView->getShowWidget();
1483             QString current = currentAccount();
1484             QString currentState = showWidget->getState();
1485 
1486             // Disconnect combo filter account
1487             disconnect(showWidget, &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onAccountChanged);
1488             disconnect(showWidget, &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onRefreshInformationZoneDelayed);
1489             disconnect(showWidget, &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified);
1490             disconnect(ui.kAccountEdit,  static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGOperationPluginWidget::fillTargetAccount);
1491 
1492             // Clear
1493             ui.kAccountEdit->clear();
1494             ui.kPaymentAccountEdit->clear();
1495 
1496             SKGStringListList listAccount;
1497             getDocument()->executeSelectSqliteOrder(QStringLiteral("SELECT t_ICON, t_name, t_bookmarked, id, t_type from v_account_display ") % (m_showClosedAccounts ? "" : "where t_close='N' ")
1498                                                     % "order by t_bookmarked DESC, t_name ASC", listAccount);
1499 
1500             int nbAccounts = listAccount.count() - 1;
1501             if (!m_lastState.hasChildNodes()) {
1502                 ui.kTitle->hide();
1503             }
1504 
1505             // Set show widget
1506             showWidget->clear();
1507 
1508             if (nbAccounts > 1) {
1509                 showWidget->addGroupedItem(QStringLiteral("all"), i18nc("Option to for display of transactions", "All"), QLatin1String(""), QStringLiteral("1=1"), QStringLiteral("account"), Qt::META + Qt::Key_A);
1510                 showWidget->addSeparator();
1511             }
1512 
1513             QString uncheck;
1514             bool accountSeparatorAdded = false;
1515             for (int i = 1; i <= nbAccounts; ++i) {  // Ignore header
1516                 const bool wallet = (listAccount.at(i).at(4) == QStringLiteral("W"));
1517                 QString iconName = wallet ? QStringLiteral("wallet-closed") : QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/images/logo/" % listAccount.at(i).at(0));
1518                 if (iconName.isEmpty()) {
1519                     iconName = listAccount.at(i).at(0);
1520                 }
1521                 QIcon icon(iconName);
1522                 QString text = listAccount.at(i).at(1);
1523                 QString id = "##_" % text;
1524                 if (nbAccounts == 1) {
1525                     QStringList items = SKGServices::splitCSVLine(currentState);
1526                     if (items.isEmpty()) {
1527                         items.push_back(QStringLiteral("all"));
1528                     }
1529                     if (items[0] == QStringLiteral("all") || !items[0].startsWith(QLatin1String("##_"))) {
1530                         items[0] = id;
1531                     }
1532                     if (items.count() == 1) {
1533                         items.push_back(QStringLiteral("transactions"));
1534                         items.push_back(QStringLiteral("hide"));
1535                     }
1536                     currentState = SKGServices::stringsToCsv(items);
1537                 }
1538                 ui.kAccountEdit->addItem(icon, text);
1539                 ui.kPaymentAccountEdit->addItem(icon, text);
1540 
1541                 if (!accountSeparatorAdded && listAccount.at(i).at(2) == QStringLiteral("N")) {
1542                     if (i > 1) {
1543                         showWidget->addSeparator();
1544                     }
1545                     accountSeparatorAdded = true;
1546                 }
1547 
1548                 QString account_id = listAccount.at(i).at(3);
1549                 showWidget->addGroupedItem(id, text, iconName, "rd_account_id=" + account_id, QStringLiteral("account"),
1550                                            (i < 10 ? QKeySequence::fromString("Meta+" % SKGServices::intToString(i)) : QKeySequence()));
1551 
1552                 SKGStringListList listLinkedAccounts;
1553                 getDocument()->executeSelectSqliteOrder(QStringLiteral("SELECT id FROM v_account_display where t_close='N' AND r_account_id=") + account_id, listLinkedAccounts);
1554                 int nbLinkedAccount = listLinkedAccounts.count();
1555                 if (nbLinkedAccount > 1) {
1556                     QString f = account_id;
1557                     for (int j = 1; j < nbLinkedAccount; ++j) {  // Ignore header
1558                         f += ",'" + listLinkedAccounts.at(j).at(0) + '\'';
1559                     }
1560 
1561                     showWidget->addGroupedItem(id + "_cc", i18nc("Information", "%1 + its credit cards", text), iconName, "rd_account_id IN(" + f + ')', QStringLiteral("account"),
1562                                                QKeySequence());
1563                 }
1564 
1565                 if (!uncheck.isEmpty()) {
1566                     uncheck = uncheck % ";";
1567                 }
1568                 uncheck = uncheck % id;
1569             }
1570 
1571             int nb = showWidget->count();
1572             for (int i = 1; i < nb; ++i) {
1573                 showWidget->setListIdToUncheckWhenChecked(i, uncheck % ";all");
1574             }
1575             if (nbAccounts > 1) {
1576                 showWidget->setListIdToUncheckWhenChecked(0, uncheck);
1577             }
1578 
1579             showWidget->addSeparator();
1580             showWidget->addGroupedItem(QStringLiteral("transactions"), i18nc("Option to for display of transactions", "Transactions"), QStringLiteral("view-bank-account"), QStringLiteral("d_date!='0000-00-00' AND t_template='N'"),
1581                                        QStringLiteral("type"), Qt::META + Qt::Key_O);
1582             showWidget->addGroupedItem(QStringLiteral("templates"), i18nc("Option to for display of transactions", "Templates"), QStringLiteral("edit-guides"), QStringLiteral("d_date!='0000-00-00' AND t_template='Y'"),
1583                                        QStringLiteral("type"), Qt::META + Qt::Key_T);
1584             showWidget->addSeparator();
1585             showWidget->addItem(QStringLiteral("hidepointed"), i18nc("Option to for display of transactions", "Hide marked transactions"), QStringLiteral("dialog-ok"), QStringLiteral("t_status<>'P'"), QLatin1String(""), QLatin1String(""), QLatin1String(""), QLatin1String(""), Qt::META + Qt::Key_P);
1586             showWidget->addItem(QStringLiteral("hide"), i18nc("Option to for display of transactions", "Hide checked transactions"), QStringLiteral("dialog-ok"), QStringLiteral("t_status<>'Y'"), QLatin1String(""), QLatin1String(""), QLatin1String(""), QLatin1String(""), Qt::META + Qt::Key_C);
1587             showWidget->addSeparator();
1588             showWidget->addPeriodItem(QStringLiteral("period"));
1589             showWidget->setMode(SKGShow::AND);
1590             if (currentState.isEmpty()) {
1591                 showWidget->setDefaultState(QStringLiteral("all;transactions;hide"));
1592             } else {
1593                 showWidget->setState(currentState);
1594             }
1595 
1596             if (!current.isEmpty()) {
1597                 ui.kAccountEdit->setText(current);
1598             }
1599 
1600             // Connect combo filter account
1601             connect(ui.kAccountEdit, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGOperationPluginWidget::fillTargetAccount, Qt::QueuedConnection);
1602             connect(showWidget, &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onAccountChanged, Qt::QueuedConnection);
1603             connect(showWidget, &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onRefreshInformationZoneDelayed, Qt::QueuedConnection);
1604             connect(showWidget, &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
1605 
1606             fillTargetAccount();
1607 
1608             if (nbAccounts == 0) {
1609                 ui.kTitle->setText(i18nc("Message", "First, you have to create an account."));
1610                 ui.kTitle->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-information")), KTitleWidget::ImageLeft);
1611                 ui.kTitle->show();
1612                 showWidget->hide();
1613             } else {
1614                 showWidget->show();
1615             }
1616         }
1617 
1618         if (iTableName == QStringLiteral("refund") || iTableName.isEmpty()) {
1619             SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kTrackerEdit, getDocument(), QStringLiteral("refund"), QStringLiteral("t_name"), QStringLiteral("t_close='N'"));
1620         }
1621         if (!iLightTransaction) {
1622             if (iTableName == QStringLiteral("category") || iTableName.isEmpty()) {
1623                 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kCategoryEdit, getDocument(), QStringLiteral("category"), QStringLiteral("t_fullname"), QStringLiteral("t_close='N'"));
1624             }
1625             if (iTableName == QStringLiteral("payee") || iTableName.isEmpty()) {
1626                 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kPayeeEdit, getDocument(), QStringLiteral("payee"), QStringLiteral("t_name"), QStringLiteral("t_close='N'"));
1627             }
1628             if (iTableName == QStringLiteral("operation") || iTableName.isEmpty()) {
1629                 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kTypeEdit, getDocument(), QStringLiteral("operation"), QStringLiteral("t_mode"), QLatin1String(""), true);
1630                 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kCommentEdit, getDocument(), QStringLiteral("v_operation_all_comment"), QStringLiteral("t_comment"), QLatin1String(""), true);
1631 
1632                 // Set type number
1633                 m_numberFieldIsNotUptodate = true;
1634 
1635                 // Correction 215658: vvv because the table is modified, the selection is modified
1636                 onSelectionChanged();
1637                 // Correction 215658: ^^^
1638 
1639                 onRefreshInformationZoneDelayed();
1640             }
1641         } else if (iTableName == QStringLiteral("operation") || iTableName.isEmpty()) {
1642             onRefreshInformationZoneDelayed();
1643         }
1644     }
1645 }
1646 
1647 void SKGOperationPluginWidget::onDoubleClick()
1648 {
1649     _SKGTRACEINFUNC(10)
1650 
1651     // Get selection
1652     SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects();
1653     if (selection.count() == 1) {
1654         SKGOperationObject op(selection.at(0));
1655 
1656         if (op.isTemplate() && selection.at(0).getRealTable() == QStringLiteral("operation")) {
1657             // this is a template, we have to create an operation
1658             SKGError err;
1659             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Transaction creation"), err)
1660             SKGOperationObject operation;
1661             err = op.duplicate(operation);
1662             if (skgoperation_settings::automaticPointInReconciliation() && m_modeInfoZone == 1) {
1663                 IFOKDO(err, operation.setStatus(SKGOperationObject::MARKED))
1664                 IFOKDO(err, operation.save())
1665             }
1666 
1667             // Send message
1668             IFOKDO(err, operation.getDocument()->sendMessage(i18nc("An information to the user that something was added", "The transaction '%1' has been added", operation.getDisplayName()), SKGDocument::Hidden))
1669 
1670             // status bar
1671             IFOK(err) {
1672                 setTemplateMode(false);
1673                 err = SKGError(0, i18nc("Successful message after an user action", "Transaction created"));
1674                 ui.kOperationView->getView()->selectObject(operation.getUniqueID());
1675             } else {
1676                 err.addError(ERR_FAIL, i18nc("Error message",  "Transaction creation failed"));
1677             }
1678 
1679             // Display error
1680             SKGMainPanel::displayErrorMessage(err);
1681 
1682         } else {
1683             // This is not a template, we have to open it
1684             SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("open"))->trigger();
1685         }
1686     }
1687 }
1688 
1689 void SKGOperationPluginWidget::onRefreshInformationZoneDelayed()
1690 {
1691     m_timer.start(300);
1692 }
1693 
1694 void SKGOperationPluginWidget::onRefreshInformationZone()
1695 {
1696     SKGTRACEINFUNC(1)
1697     ui.kReconciliateAccount->hide();
1698 
1699     auto* doc = qobject_cast<SKGDocumentBank*>(getDocument());
1700     QString current = currentAccount();
1701     if (doc != nullptr && SKGMainPanel::getMainPanel()) {
1702         if (m_modeInfoZone == 0) {
1703             // Refresh info area
1704             // Compute where clause
1705             QString filter = QStringLiteral("1=1");
1706             if (!current.isEmpty()) {
1707                 filter = "t_name='" % SKGServices::stringToSqlString(current) % '\'';
1708             }
1709             ui.kInfo->setText(i18nc("Message", "Computing…"));
1710             doc->concurrentExecuteSelectSqliteOrder("SELECT TOTAL(f_CURRENTAMOUNT), TOTAL(f_CHECKED), TOTAL(f_COMING_SOON), TOTAL(f_COMING_SOON_FROM_LINKED_ACCOUNT) from v_account_display WHERE " % filter, [ = ](const SKGStringListList & iResult) {
1711                 if (iResult.count() == 2 && SKGMainPanel::getMainPanel()->pageIndex(this) != -1) {
1712                     SKGServices::SKGUnitInfo unit1 = doc->getPrimaryUnit();
1713                     SKGServices::SKGUnitInfo unit2 = doc->getSecondaryUnit();
1714                     if (!current.isEmpty()) {
1715                         SKGAccountObject account(getDocument());
1716                         if (account.setName(current).isSucceeded()) {
1717                             if (account.load().isSucceeded()) {
1718                                 SKGUnitObject unitAccount;
1719                                 if (account.getUnit(unitAccount).isSucceeded()) {
1720                                     if (!unitAccount.getSymbol().isEmpty()) {
1721                                         unit1.Symbol = unitAccount.getSymbol();
1722                                         unit1.Value = SKGServices::stringToDouble(unitAccount.getAttribute(QStringLiteral("f_CURRENTAMOUNT")));
1723 
1724                                         if (unit1.Symbol != qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit().Symbol) {
1725                                             unit2 = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
1726                                         }
1727                                     }
1728                                 }
1729                             }
1730                         }
1731                     }
1732 
1733                     double v1 = SKGServices::stringToDouble(iResult.at(1).at(0));
1734                     double v2 = SKGServices::stringToDouble(iResult.at(1).at(1));
1735                     double v3 = SKGServices::stringToDouble(iResult.at(1).at(2));
1736                     double v4 = SKGServices::stringToDouble(iResult.at(1).at(3));
1737                     QString s1 = doc->formatMoney(v1, unit1);
1738                     QString s2 = doc->formatMoney(v2, unit1);
1739                     QString s3 = doc->formatMoney(v3, unit1);
1740                     QString s4 = doc->formatMoney(v4, unit1);
1741                     QString zero = doc->formatMoney(0, unit1);
1742                     ui.kInfo->setText(i18nc("Message", "Balance: %1     Checked: %2     To be Checked: %3", s1, s2, !current.isEmpty() && s4 != zero ? s3 % " + " % s4 : s3));
1743                     if (!unit2.Symbol.isEmpty() && (unit2.Value != 0.0)) {
1744                         s1 = doc->formatMoney(v1, unit2);
1745                         s2 = doc->formatMoney(v2, unit2);
1746                         s3 = doc->formatMoney(v3, unit2);
1747                         s4 = doc->formatMoney(v4, unit2);
1748                     }
1749                     ui.kInfo->setToolTip(i18nc("Message", "<p>Balance: %1</p><p>Checked: %2</p><p>To be Checked: %3</p>", s1, s2, !current.isEmpty() && s4 != zero ? s3 % " + " % s4 : s3));
1750                 }
1751             });
1752         } else if (m_modeInfoZone == 1) {
1753             // Refresh reconciliation area
1754             SKGServices::SKGUnitInfo unit1 = doc->getPrimaryUnit();
1755             SKGServices::SKGUnitInfo unit2 = doc->getSecondaryUnit();
1756             // Compute where clause
1757             QString filter = '\'' % SKGServices::stringToSqlString(currentAccount()) % '\'';
1758             SKGStringListList listTmp;
1759             getDocument()->executeSelectSqliteOrder(
1760                 "SELECT ABS(TOTAL(f_CURRENTAMOUNT_EXPENSE)),TOTAL(f_CURRENTAMOUNT_INCOME) FROM v_operation_display WHERE t_status='P' AND t_ACCOUNT=" % filter,
1761                 listTmp);
1762             if (listTmp.count() == 2) {
1763                 SKGAccountObject::AccountType accountType = SKGAccountObject::OTHER;
1764                 if (!current.isEmpty()) {
1765                     SKGAccountObject account(getDocument());
1766                     if (account.setName(current).isSucceeded()) {
1767                         if (account.load().isSucceeded()) {
1768                             accountType = account.getType();
1769 
1770                             SKGUnitObject unitAccount;
1771                             if (account.getUnit(unitAccount).isSucceeded()) {
1772                                 if (!unitAccount.getSymbol().isEmpty()) {
1773                                     unit1.Symbol = unitAccount.getSymbol();
1774                                     unit1.Value = SKGServices::stringToDouble(unitAccount.getAttribute(QStringLiteral("f_CURRENTAMOUNT")));
1775 
1776                                     if (unit1.Symbol != qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit().Symbol) {
1777                                         unit2 = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
1778                                     }
1779                                 }
1780                             }
1781                         }
1782                     }
1783                 }
1784                 if (accountType == SKGAccountObject::SKGAccountObject::CREDITCARD) {
1785                     ui.kReconciliationTitle->setText(i18nc("A title", "Total amount:"));
1786                     ui.kReconciliateAccount->show();
1787                 } else {
1788                     ui.kReconciliationTitle->setText(i18nc("A title", "Final balance:"));
1789                 }
1790 
1791                 ui.kAutoPoint->setVisible(accountType != SKGAccountObject::WALLET);
1792 
1793                 SKGStringListList listTmp2;
1794                 double diff = 0;
1795                 getDocument()->executeSelectSqliteOrder(
1796                     "SELECT TOTAL(f_CHECKEDANDPOINTED) from v_account_display WHERE t_name=" % filter,
1797                     listTmp2);
1798                 if (listTmp2.count() == 2) {
1799                     diff = SKGServices::stringToDouble(listTmp2.at(1).at(0)) - ui.kReconcilitorAmountEdit->value() * unit1.Value;
1800                 }
1801 
1802                 // Set tooltip
1803                 if (current.isEmpty()) {
1804                     ui.kReconciliatorInfo->setText(i18nc("Description", "You must select only one account to use reconciliation."));
1805                     ui.kReconciliatorInfo->setToolTip(ui.kReconciliatorInfo->text());
1806                 } else {
1807                     double v1 = SKGServices::stringToDouble(listTmp.at(1).at(0));
1808                     double v2 = SKGServices::stringToDouble(listTmp.at(1).at(1));
1809                     QString sdiff = doc->formatMoney(diff, unit1);
1810                     QString s1 = doc->formatMoney(v1, unit1);
1811                     QString s2 = doc->formatMoney(v2, unit1);
1812                     ui.kReconciliatorInfo->setText(i18nc("Message", "%1 - Delta: %2     Expenditure: %3     Income: %4", unit1.Symbol, sdiff, s1, s2));
1813 
1814                     // Comparison
1815                     QString zero = doc->formatMoney(0, unit1);
1816                     QString negativezero = doc->formatMoney(-EPSILON, unit1);
1817                     ui.kValidate->setVisible(sdiff == zero || sdiff == negativezero);
1818                     ui.kCreateFakeOperation->setVisible(!ui.kValidate->isVisible());
1819                     ui.kAutoPoint->setVisible(!ui.kValidate->isVisible());
1820 
1821                     if (!unit2.Symbol.isEmpty() && (unit2.Value != 0.0)) {
1822                         sdiff = doc->formatMoney(diff, unit2);
1823                         s1 = doc->formatMoney(v1, unit2);
1824                         s2 = doc->formatMoney(v2, unit2);
1825                     }
1826                     ui.kReconciliatorInfo->setToolTip(i18nc("Message", "<p>Delta: %1</p><p>Expenditure: %2</p><p>Income: %3</p>", sdiff, s1, s2));
1827                 }
1828             }
1829         }
1830     }
1831 }
1832 
1833 void SKGOperationPluginWidget::onAccountChanged()
1834 {
1835     SKGTRACEINFUNC(1)
1836     if (!currentAccount().isEmpty() && ui.kOperationView->getView()->getNbSelectedObjects() == 0) {
1837         // Get account object
1838         SKGAccountObject act(getDocument());
1839         SKGError err = act.setName(currentAccount());
1840         IFOKDO(err, act.load())
1841 
1842         // Get unit
1843         SKGUnitObject unit;
1844         IFOKDO(err, act.getUnit(unit))
1845         if (!err && !unit.getSymbol().isEmpty()) {
1846             ui.kUnitEdit->setText(unit.getSymbol());
1847         }
1848     }
1849     onFilterChanged();
1850 }
1851 
1852 void SKGOperationPluginWidget::onFilterChanged()
1853 {
1854     SKGTRACEINFUNC(1)
1855     if (!isEnabled()) {
1856         return;
1857     }
1858     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1859 
1860     // Enable/ disable widgets
1861     bool onOneAccount = (!currentAccount().isEmpty());
1862     ui.kReconciliatorFrame2->setEnabled(onOneAccount);
1863     if (!onOneAccount && m_modeInfoZone == 1) {
1864         ui.kReconciliatorFrame2->hide();
1865         ui.kInfo->show();
1866         m_modeInfoZone = 0;
1867     }
1868 
1869     QString current = currentAccount();
1870     if (!current.isEmpty() && ui.kOperationView->getView()->getNbSelectedObjects() == 0) {
1871         ui.kAccountEdit->setText(current);
1872     }
1873 
1874     QApplication::restoreOverrideCursor();
1875 }
1876 
1877 void SKGOperationPluginWidget::fillNumber()
1878 {
1879     SKGTRACEINFUNC(10)
1880     QStringList list;
1881     QString account = ui.kAccountEdit->text();
1882     QString wc;
1883     if (!account.isEmpty()) {
1884         wc = "rd_account_id IN (SELECT id FROM account WHERE t_name='" + SKGServices::stringToSqlString(account) + "')";
1885     }
1886     getDocument()->getDistinctValues(QStringLiteral("v_operation_next_numbers"), QStringLiteral("t_number"), wc, list);
1887 
1888     // Fill completion
1889     auto comp = new QCompleter(list);
1890     comp->setFilterMode(Qt::MatchContains);
1891     ui.kNumberEdit->setCompleter(comp);
1892 
1893     m_numberFieldIsNotUptodate = false;
1894 }
1895 
1896 void SKGOperationPluginWidget::onFocusChanged()
1897 {
1898     _SKGTRACEINFUNC(10)
1899     if (!qApp->closingDown()) {
1900         if ((SKGMainPanel::getMainPanel() != nullptr) && SKGMainPanel::getMainPanel()->currentPage() == this) {
1901             if (m_numberFieldIsNotUptodate && ui.kNumberEdit->hasFocus()) {
1902                 fillNumber();
1903             }
1904 
1905             bool test = ui.kTypeEdit->hasFocus() ||
1906                         //                  ui.kAmountEdit->hasFocus() ||
1907                         //              ui.kNumberEdit->hasFocus() ||
1908                         ui.kUnitEdit->hasFocus() ||
1909                         ui.kCategoryEdit->hasFocus() ||
1910                         ui.kTrackerEdit->hasFocus() ||
1911                         ui.kCommentEdit->hasFocus() ||
1912                         ui.kPayeeEdit->hasFocus();
1913             if (m_fastEditionAction != nullptr) {
1914                 m_fastEditionAction->setEnabled(test);
1915             }
1916         }
1917     }
1918 }
1919 
1920 void SKGOperationPluginWidget::onFastEdition()
1921 {
1922     if (SKGMainPanel::getMainPanel()->currentPage() != this) {
1923         return;
1924     }
1925     SKGTRACEINFUNC(10)
1926     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1927     SKGError err;
1928 
1929     // Get widget item
1930     QWidget* w = QApplication::focusWidget();
1931     auto* cmb = qobject_cast<SKGComboBox*>(w);
1932     if (cmb != nullptr) {
1933         setWidgetEditionEnabled(cmb->lineEdit(), false);
1934     } else {
1935         setWidgetEditionEnabled(w, false);
1936     }
1937 
1938     // Build the where clause
1939     QString wc;
1940     if (ui.kTypeEdit->hasFocus()) {
1941         wc = "t_mode LIKE '" % SKGServices::stringToSqlString(ui.kTypeEdit->text()) % "%'";
1942     } else if (ui.kUnitEdit->hasFocus()) {
1943         wc = "t_UNIT LIKE '" % SKGServices::stringToSqlString(ui.kUnitEdit->text()) % "%'";
1944     } else if (ui.kCategoryEdit->hasFocus()) {
1945         wc = "t_CATEGORY LIKE '" % SKGServices::stringToSqlString(ui.kCategoryEdit->text()) % "%'";
1946     } else if (ui.kTrackerEdit->hasFocus()) {
1947         wc = "t_REFUND LIKE '" % SKGServices::stringToSqlString(ui.kTrackerEdit->text()) % "%'";
1948     } else if (ui.kCommentEdit->hasFocus()) {
1949         wc = "t_comment LIKE '" % SKGServices::stringToSqlString(ui.kCommentEdit->text()) % "%'";
1950     } else if (ui.kPayeeEdit->hasFocus()) {
1951         wc = "t_PAYEE LIKE '" % SKGServices::stringToSqlString(ui.kPayeeEdit->text()) % "%'";
1952     }
1953 
1954     if (!wc.isEmpty()) {
1955         QString accountName = ui.kAccountEdit->currentText();
1956         if (!accountName.isEmpty() && skgoperation_settings::oncurrentaccountonly()) {
1957             wc += " AND t_ACCOUNT LIKE '" % SKGServices::stringToSqlString(accountName) % "%'";
1958         }
1959 
1960         // Read Setting
1961         QString fasteditmode = skgoperation_settings::fasteditmode();
1962 
1963         /*
1964         0-Search in templates only
1965         1-Search first in templates and after in transactions
1966         2-Search in transactions only
1967         3-Search first in transactions and after in templates
1968         */
1969         if (fasteditmode == QStringLiteral("0")) {
1970             wc += QStringLiteral(" AND t_template='Y'");
1971         } else if (fasteditmode == QStringLiteral("2")) {
1972             wc += QStringLiteral(" AND t_template='N'");
1973         }
1974 
1975         if (wc != m_lastFastEditionWhereClause) {
1976             m_lastFastEditionWhereClause = wc;
1977             m_lastFastEditionOperationFound = 0;
1978         }
1979 
1980         // Look for last operation
1981         if (m_lastFastEditionOperationFound != 0) {
1982             wc += " AND id<" % SKGServices::intToString(m_lastFastEditionOperationFound);
1983         }
1984 
1985         // Add order by
1986         wc += QStringLiteral(" ORDER BY ");
1987         if (fasteditmode == QStringLiteral("1")) {
1988             wc += QStringLiteral(" t_template DESC, ");
1989         } else if (fasteditmode == QStringLiteral("3")) {
1990             wc += QStringLiteral(" t_template ASC, ");
1991         }
1992         wc += QStringLiteral("d_date DESC, id DESC LIMIT 1");
1993 
1994         SKGObjectBase::SKGListSKGObjectBase transactions;
1995         err = getDocument()->getObjects(QStringLiteral("v_operation_display_all"), wc, transactions);
1996         if (!err && !transactions.isEmpty()) {
1997             SKGOperationObject op(transactions.at(0));
1998 
1999             m_lastFastEditionOperationFound = op.getID();
2000             if (isWidgetEditionEnabled(ui.kTypeEdit->lineEdit())) {
2001                 ui.kTypeEdit->setText(op.getMode());
2002             }
2003             if (isWidgetEditionEnabled(ui.kUnitEdit->lineEdit())) {
2004                 ui.kUnitEdit->setText(op.getAttribute(QStringLiteral("t_UNIT")));
2005             }
2006             if (isWidgetEditionEnabled(ui.kCategoryEdit->lineEdit())) {
2007                 ui.kCategoryEdit->setText(op.getAttribute(QStringLiteral("t_CATEGORY")));
2008             }
2009             if (isWidgetEditionEnabled(ui.kCommentEdit->lineEdit())) {
2010                 ui.kCommentEdit->setText(op.getComment());
2011             }
2012             if (isWidgetEditionEnabled(ui.kPayeeEdit->lineEdit())) {
2013                 ui.kPayeeEdit->setText(op.getAttribute(QStringLiteral("t_PAYEE")));
2014             }
2015             if (isWidgetEditionEnabled(ui.kTrackerEdit->lineEdit())) {
2016                 ui.kTrackerEdit->setText(op.getAttribute(QStringLiteral("t_REFUND")));
2017             }
2018             if (currentAccount().isEmpty()) {
2019                 ui.kAccountEdit->setText(op.getAttribute(QStringLiteral("t_ACCOUNT")));
2020             }
2021             if (isWidgetEditionEnabled(ui.kAmountEdit)) {
2022                 QString quantity = op.getAttribute(QStringLiteral("f_QUANTITY"));
2023 
2024                 double quantityVal = SKGServices::stringToDouble(quantity);
2025                 SKGUnitObject unitObject = ui.kUnitEdit->getUnit();
2026                 int nbDec = unitObject.getNumberDecimal();
2027                 if (nbDec == 0) {
2028                     nbDec = 2;
2029                 }
2030                 quantity = SKGServices::toCurrencyString(qAbs(quantityVal), QLatin1String(""), nbDec);
2031                 if (quantity.startsWith(QLocale().positiveSign())) {
2032                     quantity = quantity.right(quantity.length() - 1);
2033                 }
2034                 if (quantityVal > 0) {
2035                     quantity = '+' % quantity;
2036                 } else {
2037                     quantity = '-' % quantity;
2038                 }
2039                 ui.kAmountEdit->setText(quantity);
2040             }
2041 
2042             // set next number
2043             if (isWidgetEditionEnabled(ui.kNumberEdit)) {
2044                 int number = SKGServices::stringToInt(op.getNumber());
2045                 if (number == 0) {
2046                     ui.kNumberEdit->setText(QLatin1String(""));
2047                 } else {
2048                     if (m_numberFieldIsNotUptodate) {
2049                         fillNumber();
2050                     }
2051 
2052                     QCompleter* comp = ui.kNumberEdit->completer();
2053                     if (comp != nullptr) {
2054                         QStringList list = qobject_cast<QStringListModel*>(comp->model())->stringList();
2055                         int nb = list.count();
2056                         int cpt = 0;
2057                         while (nb >= 0 && cpt >= 0 && cpt < 1000) {
2058                             ++number;
2059 
2060                             if (list.contains(SKGServices::intToString(number))) {
2061                                 cpt = -2;
2062                             }
2063                             ++cpt;
2064                         }
2065 
2066                         if (cpt < 0) {
2067                             ui.kNumberEdit->setText(SKGServices::intToString(number));
2068                         }
2069                     }
2070                 }
2071             }
2072 
2073             // Get nb transaction linked
2074             SKGObjectBase::SKGListSKGObjectBase groupedOperations;
2075             op.getGroupedOperations(groupedOperations);
2076             int nbGroupedOp = groupedOperations.count();
2077 
2078             // Get nb sub op
2079             SKGObjectBase::SKGListSKGObjectBase subOperations;
2080             op.getSubOperations(subOperations);
2081             int nbSupOp = subOperations.count();
2082 
2083             if (nbSupOp > 1) {
2084                 // It is a SPLIT operation
2085                 ui.kWidgetSelector->setSelectedMode(1);
2086                 QDate d = ui.kDateEdit->date();
2087                 if (!d.isValid()) {
2088                     d = QDate::currentDate();
2089                 }
2090                 displaySubOperations(op, false, d);
2091 
2092             } else {
2093                 if (nbGroupedOp > 1) {
2094                     // It is a TRANSFER
2095                     SKGOperationObject op2(groupedOperations.at(0));
2096                     if (op2 == op) {
2097                         op2 = groupedOperations.at(1);
2098                     }
2099 
2100                     SKGAccountObject targetAccount;
2101                     op2.getParentAccount(targetAccount);
2102 
2103                     if (isWidgetEditionEnabled(ui.kTargetAccountEdit)) {
2104                         ui.kTargetAccountEdit->setText(targetAccount.getName());
2105                     }
2106                 } else {
2107                     ui.kWidgetSelector->setSelectedMode(0);
2108                 }
2109             }
2110 
2111         } else {
2112             m_lastFastEditionWhereClause = QLatin1String("");
2113             m_lastFastEditionOperationFound = 0;
2114         }
2115     }
2116 
2117     if (w != nullptr) {
2118         w->setFocus(Qt::OtherFocusReason);
2119     }
2120     QApplication::restoreOverrideCursor();
2121 
2122     // Display error
2123     SKGMainPanel::displayErrorMessage(err);
2124 }
2125 
2126 bool SKGOperationPluginWidget::isTemplateMode()
2127 {
2128     QAction* act = ui.kOperationView->getShowWidget()->getAction(QStringLiteral("templates"));
2129     return ((act != nullptr) && act->isChecked());
2130 }
2131 
2132 void SKGOperationPluginWidget::setTemplateMode(bool iTemplate)
2133 {
2134     SKGTRACEINFUNC(10)
2135 
2136     if (iTemplate != isTemplateMode()) {
2137         QAction* act = ui.kOperationView->getShowWidget()->getAction(QStringLiteral("templates"));
2138         if (act != nullptr) {
2139             act->setChecked(iTemplate);
2140         }
2141 
2142         act = ui.kOperationView->getShowWidget()->getAction(QStringLiteral("transactions"));
2143         if (act != nullptr) {
2144             act->setChecked(!iTemplate);
2145         }
2146     }
2147 }
2148 
2149 void SKGOperationPluginWidget::onBtnModeClicked(int mode)
2150 {
2151     SKGTRACEINFUNC(10)
2152     if (mode != 1 && mode != -1) {
2153         ui.kSubOperationsTable->setRowCount(0);
2154         ui.kSubOperationsTable->clearContents();
2155     }
2156 
2157     if (mode == 1) {
2158         if (ui.kSubOperationsTable->rowCount() == 0) {
2159             addSubOperationLine(0, ui.kDateEdit->date(), ui.kCategoryEdit->text(), ui.kTrackerEdit->text(), ui.kCommentEdit->text(), ui.kAmountEdit->value(), nullptr);
2160         }
2161     }
2162     onOperationCreatorModified();
2163 }
2164 
2165 void SKGOperationPluginWidget::displaySubOperations(const SKGOperationObject& iOperation, bool iKeepId, QDate iSubOperationsDate)
2166 {
2167     SKGTRACEINFUNC(10)
2168     ui.kSubOperationsTable->setRowCount(0);
2169     ui.kSubOperationsTable->clearContents();
2170 
2171     int nbSubOperations = 0;
2172 
2173     SKGObjectBase::SKGListSKGObjectBase subOperations;
2174     SKGError err =  iOperation.getSubOperations(subOperations);
2175     nbSubOperations = subOperations.count();
2176     for (int i = 0; i < nbSubOperations; ++i) {
2177         SKGSubOperationObject subOperation(subOperations.at(i));
2178 
2179         SKGCategoryObject category;
2180         subOperation.getCategory(category);
2181 
2182         SKGTrackerObject tracker;
2183         subOperation.getTracker(tracker);
2184 
2185         addSubOperationLine(i, iSubOperationsDate.isValid() ? iSubOperationsDate : subOperation.getDate(), category.getFullName(), tracker.getName(),
2186                             subOperation.getComment(), subOperation.getQuantity(), subOperation.getFormula(),
2187                             (iKeepId ? subOperation.getID() : 0));
2188     }
2189 
2190     onQuantityChanged();
2191 }
2192 
2193 void SKGOperationPluginWidget::displaySubOperations()
2194 {
2195     SKGTRACEINFUNC(10)
2196     SKGOperationObject operation;
2197     if (getSelectedOperation(operation).isSucceeded()) {
2198         displaySubOperations(operation);
2199     }
2200 }
2201 
2202 double SKGOperationPluginWidget::getRemainingQuantity()
2203 {
2204     SKGTRACEINFUNC(10)
2205     double sumQuantities = 0;
2206     int nbSubOperations = ui.kSubOperationsTable->rowCount();
2207 
2208     for (int i = 0; i < nbSubOperations ; ++i) {
2209         QTableWidgetItem* quantityItem = ui.kSubOperationsTable->item(i, m_attributesForSplit.indexOf(QStringLiteral("f_value")));
2210         if (quantityItem != nullptr) {
2211             sumQuantities = sumQuantities + quantityItem->data(101).toDouble();
2212         }
2213     }
2214 
2215     return ui.kAmountEdit->value() - sumQuantities;
2216 }
2217 
2218 void SKGOperationPluginWidget::onDateChanged(QDate iDate)
2219 {
2220     SKGTRACEINFUNC(10)
2221     bool previous = ui.kSubOperationsTable->blockSignals(true);  // Disable signals so that filling cell doesn't create new lines
2222     if (sender() == ui.kDateEdit && iDate.isValid() && m_previousDate.isValid()) {
2223         // Refresh dates
2224         int nbSubOperations = ui.kSubOperationsTable->rowCount();
2225         for (int i = 0; i < nbSubOperations ; ++i) {
2226             QTableWidgetItem* dateItem = ui.kSubOperationsTable->item(i, m_attributesForSplit.indexOf(QStringLiteral("d_date")));
2227             if (dateItem != nullptr) {
2228                 auto datestring = dateItem->toolTip();
2229                 QDate previousSubDate = SKGServices::stringToTime(datestring).date();
2230                 if (previousSubDate.isValid()) {
2231                     int delta = m_previousDate.daysTo(iDate);
2232 
2233                     auto newDate = previousSubDate.addDays(delta);
2234                     dateItem->setText(SKGMainPanel::dateToString(newDate));
2235                     dateItem->setToolTip(SKGServices::dateToSqlString(newDate));
2236                 }
2237             }
2238         }
2239     }
2240     m_previousDate = iDate;
2241     ui.kSubOperationsTable->blockSignals(previous);    // Reenable signals
2242 }
2243 
2244 void SKGOperationPluginWidget::refreshSubOperationAmount()
2245 {
2246     SKGTRACEINFUNC(10)
2247     bool previous = ui.kSubOperationsTable->blockSignals(true);  // Disable signals so that filling cell doesn't create new lines
2248 
2249     int nbSubOperations = ui.kSubOperationsTable->rowCount();
2250 
2251     // Refresh computed amounts
2252     auto unit = ui.kUnitEdit->getUnit().getUnitInfo();
2253     unit.Value = 1.0;
2254     for (int i = 0; i < nbSubOperations ; ++i) {
2255         QTableWidgetItem* quantityItem = ui.kSubOperationsTable->item(i, m_attributesForSplit.indexOf(QStringLiteral("f_value")));
2256         if (quantityItem != nullptr) {
2257             QString formula = quantityItem->toolTip();
2258             if (formula.startsWith(QLatin1String("="))) {
2259                 formula = formula.right(formula.length() - 1);
2260                 formula.replace(',', '.');  // Replace comma by a point in case of typo
2261                 formula.remove(' ');
2262                 formula.replace(QStringLiteral("total"), SKGServices::doubleToString(ui.kAmountEdit->value()));
2263 
2264                 QScriptEngine myEngine;
2265                 QScriptValue result = myEngine.evaluate(formula);
2266                 if (result.isNumber()) {
2267                     auto value = result.toNumber();
2268                     quantityItem->setText(getDocument()->formatMoney(value, unit, false));
2269                     quantityItem->setData(101, value);
2270                 }
2271             } else {
2272                 auto value = quantityItem->data(101).toDouble();
2273                 quantityItem->setText(getDocument()->formatMoney(value, unit, false));
2274             }
2275         }
2276     }
2277     ui.kSubOperationsTable->blockSignals(previous);    // Reenable signals
2278 }
2279 
2280 void SKGOperationPluginWidget::onQuantityChanged()
2281 {
2282     SKGTRACEINFUNC(10)
2283     int nbSubOperations = ui.kSubOperationsTable->rowCount();
2284 
2285     bool previous = ui.kSubOperationsTable->blockSignals(true);  // Disable signals so that filling cell doesn't create new lines
2286     if (sender() == ui.kAmountEdit) {
2287         // Update the total amount
2288         m_tableDelegate->addParameterValue(QStringLiteral("total"), ui.kAmountEdit->value());
2289 
2290         // Refresh computed amounts
2291         refreshSubOperationAmount();
2292     }
2293 
2294     // This code put the remaining quantity on the all sub transactions with the same ratios ^^^
2295     // Specific code for the last one to avoid "round" error
2296     QTableWidgetItem* remainingQuantityItem = ui.kSubOperationsTable->item(nbSubOperations - 1, m_attributesForSplit.indexOf(QStringLiteral("f_value")));
2297     if (remainingQuantityItem != nullptr) {
2298         // 348490 vvv
2299         double remain = remainingQuantityItem->data(101).toDouble() + getRemainingQuantity();
2300         if (qAbs(remain) < 1e-10) {
2301             onRemoveSubOperation(nbSubOperations - 1);
2302         } else {
2303             auto unit = ui.kUnitEdit->getUnit().getUnitInfo();
2304             unit.Value = 1.0;
2305             remainingQuantityItem->setText(getDocument()->formatMoney(remain, unit, false));
2306             remainingQuantityItem->setData(101, remain);
2307             remainingQuantityItem->setToolTip(SKGServices::doubleToString(remain));
2308         }
2309         // 348490 ^^^
2310     }
2311     ui.kSubOperationsTable->blockSignals(previous);    // Reenable signals
2312 }
2313 
2314 void SKGOperationPluginWidget::onSubopCellChanged(int row, int column)
2315 {
2316     SKGTRACEINFUNC(10)
2317     QTableWidgetItem* subop_cell = ui.kSubOperationsTable->item(row, column);
2318     QBrush base_brush = ui.kSubOperationsTable->item(row, 0)->foreground();
2319 
2320     if (column == m_attributesForSplit.indexOf(QStringLiteral("f_value"))) {
2321         // If the quantity in the last line is edited, we add a new
2322         // line with the new remaining quantity
2323         addSubOperationLine(ui.kSubOperationsTable->rowCount(), ui.kDateEdit->date(), QLatin1String(""),
2324                             QLatin1String(""), QLatin1String(""), 0, QLatin1String(""));
2325 
2326         if (subop_cell->data(101).toDouble() != 0) {
2327             onQuantityChanged();
2328         } else {
2329             base_brush = KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText);
2330         }
2331         subop_cell->setForeground(base_brush);
2332 
2333         refreshSubOperationAmount();
2334     }
2335 }
2336 
2337 void SKGOperationPluginWidget::onRemoveSubOperation(int iRow)
2338 {
2339     SKGTRACEINFUNC(10)
2340     bool previous = ui.kSubOperationsTable->blockSignals(true);
2341     ui.kSubOperationsTable->removeRow(iRow);
2342 
2343     // If all rows removed, add an empty line
2344     if (ui.kSubOperationsTable->rowCount() == 0) {
2345         addSubOperationLine(0, ui.kDateEdit->date(), QLatin1String(""), QLatin1String(""), QLatin1String(""), 0, QLatin1String(""));
2346     }
2347 
2348     if (!previous) {
2349         onQuantityChanged();
2350     }
2351     ui.kSubOperationsTable->blockSignals(previous);
2352 }
2353 
2354 void SKGOperationPluginWidget::onRotateAccountTools()
2355 {
2356     SKGTRACEINFUNC(10)
2357     if (m_modeInfoZone == 0) {
2358         displayReconciliationInfo();
2359     } else {
2360         displayBalance();
2361     }
2362 }
2363 
2364 
2365 void SKGOperationPluginWidget::displayBalance()
2366 {
2367     if (m_modeInfoZone != 0) {
2368         ui.kReconciliatorFrame2->hide();
2369         ui.kInfo->show();
2370         m_modeInfoZone = 0;
2371         onRefreshInformationZoneDelayed();
2372     }
2373 }
2374 
2375 void SKGOperationPluginWidget::displayReconciliationInfo()
2376 {
2377     if (!currentAccount().isEmpty()) {
2378         // Only show reconciliation info if only one account is displayed
2379         ui.kReconciliatorFrame2->show();
2380         ui.kInfo->hide();
2381         m_modeInfoZone = 1;
2382         onRefreshInformationZoneDelayed();
2383     } else {
2384         // If more than one account is displayed, skip reconciliation mode
2385         // (it doesn't make sense to reconcile several accounts at once)
2386         // and move to the next modeInfoZone
2387         m_modeInfoZone = 1;
2388         onRotateAccountTools();
2389     }
2390 }
2391 
2392 void SKGOperationPluginWidget::onAutoPoint()
2393 {
2394     SKGError err;
2395     SKGTRACEINFUNCRC(10, err)
2396 
2397     {
2398         SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Auto mark account"), err)
2399         SKGAccountObject act(getDocument());
2400         err = act.setName(currentAccount());
2401         IFOKDO(err, act.load())
2402         IFOKDO(err, act.autoReconcile(ui.kReconcilitorAmountEdit->value()))
2403 
2404         // Send message
2405         IFOKDO(err, act.getDocument()->sendMessage(i18nc("An information message", "The account '%1' has been auto marked", act.getDisplayName()), SKGDocument::Hidden))
2406     }
2407     // status bar
2408     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Account auto marked.")))
2409 
2410     // Display error
2411     SKGMainPanel::displayErrorMessage(err);
2412 }
2413 
2414 void SKGOperationPluginWidget::onAddFakeOperation()
2415 {
2416     SKGError err;
2417     SKGTRACEINFUNCRC(10, err) {
2418         SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Create fake transaction"), err)
2419 
2420         SKGAccountObject accountObj(getDocument());
2421         IFOKDO(err, accountObj.setName(currentAccount()))
2422         IFOKDO(err, accountObj.load())
2423 
2424         SKGOperationObject op;
2425         IFOKDO(err, accountObj.addOperation(op))
2426         IFOKDO(err, op.setDate(QDate::currentDate()))
2427         IFOKDO(err, op.setComment(skgoperation_settings::commentFakeOperation()))
2428         QString payee = skgoperation_settings::payeeFakeOperation();
2429         if (!payee.isEmpty()) {
2430             SKGPayeeObject p;
2431             IFOKDO(err, SKGPayeeObject::createPayee(qobject_cast<SKGDocumentBank*>(getDocument()), payee, p, true))
2432             IFOKDO(err, op.setPayee(p))
2433         }
2434 
2435         SKGUnitObject unit;
2436         IFOKDO(err, accountObj.getUnit(unit))
2437         IFOKDO(err, op.setUnit(unit))
2438         if (skgoperation_settings::automaticPointInReconciliation() && m_modeInfoZone == 1) {
2439             IFOKDO(err, op.setStatus(SKGOperationObject::MARKED))
2440         }
2441         IFOKDO(err, op.save())
2442 
2443         SKGSubOperationObject sop;
2444         IFOKDO(err, op.addSubOperation(sop))
2445 
2446         SKGStringListList listTmp2;
2447         double diff = 0;
2448         getDocument()->executeSelectSqliteOrder(
2449             "SELECT f_CHECKEDANDPOINTED from v_account_display WHERE t_name='" % SKGServices::stringToSqlString(currentAccount()) % '\'',
2450             listTmp2);
2451         if (listTmp2.count() == 2) {
2452             diff = SKGServices::stringToDouble(listTmp2.at(1).at(0)) / unit.getAmount() - ui.kReconcilitorAmountEdit->value();
2453         }
2454 
2455         IFOKDO(err, sop.setQuantity(-diff))
2456         IFOKDO(err, sop.setComment(skgoperation_settings::commentFakeOperation()))
2457         QString category = skgoperation_settings::categoryFakeOperation();
2458         if (!category.isEmpty()) {
2459             SKGCategoryObject c;
2460             IFOKDO(err, SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), category, c, true))
2461             IFOKDO(err, sop.setCategory(c))
2462         }
2463         IFOKDO(err, sop.save())
2464 
2465         // Send message
2466         IFOKDO(err, op.getDocument()->sendMessage(i18nc("An information to the user that something was added", "The transaction '%1' has been added", op.getDisplayName()), SKGDocument::Hidden))
2467     }
2468 
2469     // status bar
2470     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Fake transaction created.")))
2471     else {
2472         err.addError(ERR_FAIL, i18nc("Error message",  "Creation failed"));
2473     }
2474 
2475     // Display error
2476     SKGMainPanel::displayErrorMessage(err);
2477 }
2478 
2479 
2480 void SKGOperationPluginWidget::onValidateMarkedOperations()
2481 {
2482     SKGError err;
2483     SKGTRACEINFUNCRC(10, err)
2484 
2485     QString account = currentAccount();
2486     if (!account.isEmpty()) {
2487         // Get reconciled account
2488         SKGAccountObject act(getDocument());
2489         IFOKDO(err, act.setName(account))
2490         IFOKDO(err, act.load())
2491 
2492         QString bindAccount = ui.kReconciliateAccount->currentText();
2493 
2494         if (act.getType() == SKGAccountObject::CREDITCARD && !bindAccount.isEmpty()) {
2495             //
2496             IFOK(err) {
2497                 SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Switch to checked"), err, 3)
2498                 SKGAccountObject accountObj2(getDocument());
2499                 IFOKDO(err, accountObj2.setName(bindAccount))
2500                 IFOKDO(err, accountObj2.load())
2501                 IFOKDO(err, getDocument()->stepForward(1))
2502 
2503                 IFOKDO(err, act.transferDeferredOperations(accountObj2))
2504                 IFOKDO(err, getDocument()->stepForward(2))
2505 
2506                 // Change reconciliation date of the account
2507                 IFOKDO(err, act.setReconciliationDate(QDate::currentDate()))
2508                 IFOKDO(err, act.setReconciliationBalance(ui.kReconcilitorAmountEdit->value()))
2509                 IFOKDO(err, act.setLinkedAccount(accountObj2))
2510                 IFOKDO(err, act.save())
2511 
2512                 // Send message
2513                 IFOKDO(err, act.getDocument()->sendMessage(i18nc("An information message", "The account '%1' is reconciled", act.getDisplayName()), SKGDocument::Positive))
2514 
2515                 IFOKDO(err, getDocument()->stepForward(3))
2516             }
2517         } else {
2518             // Change state of all transactions
2519             SKGObjectBase::SKGListSKGObjectBase list;
2520             IFOKDO(err, getDocument()->getObjects(QStringLiteral("v_operation_display"), "t_status='P' AND t_ACCOUNT='" % SKGServices::stringToSqlString(account) % '\'', list))
2521             int nb = list.count();
2522             IFOK(err) {
2523                 SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Switch to checked"), err, nb + 1)
2524 
2525                 if (act.getType() == SKGAccountObject::CREDITCARD && bindAccount.isEmpty()) {
2526                     // Reset the linked account
2527                     IFOKDO(err, act.setLinkedAccount(SKGAccountObject()))
2528                     IFOKDO(err, act.save())
2529                 }
2530 
2531                 for (int i = 0; !err && i < nb; ++i) {
2532                     // Set transaction checked
2533                     SKGOperationObject op(list.at(i));
2534                     err = op.setStatus(SKGOperationObject::CHECKED);
2535                     IFOKDO(err, op.save())
2536 
2537                     // Send message
2538                     IFOKDO(err, op.getDocument()->sendMessage(i18nc("An information message", "The transaction '%1' has been checked", op.getDisplayName()), SKGDocument::Hidden))
2539 
2540                     IFOKDO(err, getDocument()->stepForward(i + 1))
2541                 }
2542 
2543                 // Change reconciliation date of the account
2544                 IFOKDO(err, act.setReconciliationDate(QDate::currentDate()))
2545                 IFOKDO(err, act.setReconciliationBalance(ui.kReconcilitorAmountEdit->value()))
2546                 IFOKDO(err, act.save())
2547 
2548                 // Send message
2549                 IFOKDO(err, act.getDocument()->sendMessage(i18nc("An information message", "The account '%1' is reconciled", act.getDisplayName()), SKGDocument::Positive))
2550 
2551                 IFOKDO(err, getDocument()->stepForward(nb + 1))
2552             }
2553         }
2554     }
2555 
2556     // status bar
2557     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Transaction checked.")))
2558     else {
2559         err.addError(ERR_FAIL, i18nc("Error message",  "Switch failed"));
2560     }
2561 
2562     // Display error
2563     SKGMainPanel::displayErrorMessage(err);
2564 }
2565 
2566 void SKGOperationPluginWidget::addSubOperationLine(int row, QDate date, const QString& category, const QString& tracker, const QString& comment, double quantity, const QString& formula, int id)
2567 {
2568     SKGTRACEINFUNC(10)
2569     bool previous = ui.kSubOperationsTable->blockSignals(true);
2570 
2571     ui.kSubOperationsTable->insertRow(row);
2572 
2573     // Add a delete icon on the line:
2574     auto hitem = new QTableWidgetItem(SKGServices::fromTheme(QStringLiteral("edit-delete")), QLatin1String(""));
2575     ui.kSubOperationsTable->setVerticalHeaderItem(row, hitem);
2576     QHeaderView* headerView = ui.kSubOperationsTable->verticalHeader();
2577     headerView->setSectionsMovable(true);
2578 
2579     // Category
2580     auto categoryItem = new QTableWidgetItem(category);
2581     categoryItem->setToolTip(category);
2582     categoryItem->setData(Qt::UserRole, id);
2583     ui.kSubOperationsTable->setItem(row, m_attributesForSplit.indexOf(QStringLiteral("t_category")), categoryItem);
2584 
2585     // Comment
2586     auto commentItem = new QTableWidgetItem(comment);
2587     commentItem->setToolTip(comment);
2588     ui.kSubOperationsTable->setItem(row, m_attributesForSplit.indexOf(QStringLiteral("t_comment")), commentItem);
2589 
2590     // Quantity
2591     auto unit = ui.kUnitEdit->getUnit().getUnitInfo();
2592     unit.Value = 1.0;
2593     auto quantityItem = new QTableWidgetItem(getDocument()->formatMoney(quantity, unit, false));
2594     quantityItem->setTextAlignment(Qt::AlignVCenter | Qt::AlignRight);
2595     quantityItem->setData(101, quantity);
2596     quantityItem->setToolTip(formula.isEmpty() ? SKGServices::doubleToString(quantity) : formula);
2597     ui.kSubOperationsTable->setItem(row, m_attributesForSplit.indexOf(QStringLiteral("f_value")), quantityItem);
2598 
2599     // Refund
2600     auto trackerItem = new QTableWidgetItem(tracker);
2601     trackerItem->setToolTip(tracker);
2602     categoryItem->setData(Qt::UserRole, id);
2603     ui.kSubOperationsTable->setItem(row, m_attributesForSplit.indexOf(QStringLiteral("t_refund")), trackerItem);
2604 
2605     // Date
2606     auto dateItem = new QTableWidgetItem(SKGMainPanel::dateToString(date));
2607     dateItem->setToolTip(SKGServices::dateToSqlString(date));
2608     ui.kSubOperationsTable->setItem(row, m_attributesForSplit.indexOf(QStringLiteral("d_date")), dateItem);
2609 
2610     ui.kSubOperationsTable->blockSignals(previous);
2611 
2612     ui.kSubOperationsTable->resizeColumnsToContents();
2613     ui.kSubOperationsTable->horizontalHeader()->setStretchLastSection(true);
2614     if (row == 0 && category.isEmpty()) {
2615         ui.kSubOperationsTable->horizontalHeader()->resizeSection(0, 300);
2616     }
2617 }
2618 
2619 QWidget* SKGOperationPluginWidget::mainWidget()
2620 {
2621     return ui.kOperationView->getView();
2622 }
2623 
2624 SKGError SKGOperationPluginWidget::getSelectedOperation(SKGOperationObject& operation)
2625 {
2626     SKGError err;
2627     SKGObjectBase::SKGListSKGObjectBase selectedOperations = getSelectedObjects();
2628     if (!selectedOperations.isEmpty()) {
2629         operation = selectedOperations.at(0);
2630         err.setReturnCode(0);
2631     } else {
2632         err.setReturnCode(1).setMessage(i18nc("Error message",  "No Transaction Selected"));
2633     }
2634     return err;
2635 }
2636 
2637 void SKGOperationPluginWidget::cleanEditor()
2638 {
2639     if (getNbSelectedObjects() == 0 || sender() == ui.kCleanBtn) {
2640         ui.kOperationView->getView()->clearSelection();
2641         ui.kDateEdit->setDate(QDate::currentDate());
2642         ui.kPayeeEdit->setText(QLatin1String(""));
2643         ui.kCategoryEdit->setText(QLatin1String(""));
2644         ui.kTrackerEdit->setText(QLatin1String(""));
2645         ui.kAmountEdit->setText(QLatin1String(""));
2646         ui.kTypeEdit->setText(QLatin1String(""));
2647         ui.kCommentEdit->setText(QLatin1String(""));
2648         ui.kNumberEdit->setText(QLatin1String(""));
2649 
2650         if (!currentAccount().isEmpty()) {
2651             ui.kAccountEdit->setText(currentAccount());
2652         }
2653 
2654         // BUG 376025 vvvv
2655         ui.kUnitEdit->setDocument(qobject_cast<SKGDocumentBank*>(getDocument()));
2656         // BUG 376025 ^^^^
2657         ui.kUnitShare->setDocument(qobject_cast<SKGDocumentBank*>(getDocument()));
2658 
2659         setAllWidgetsEnabled();
2660         m_previousDate = QDate::currentDate();
2661     }
2662     if (sender() == ui.kCleanBtn) {
2663         ui.kWidgetSelector->setSelectedMode(0);
2664     }
2665 }
2666 
2667 bool SKGOperationPluginWidget::isEditor()
2668 {
2669     return true;
2670 }
2671 
2672 void SKGOperationPluginWidget::activateEditor()
2673 {
2674     if (ui.kWidgetSelector->getSelectedMode() == -1) {
2675         ui.kWidgetSelector->setSelectedMode(0);
2676     }
2677     ui.kPayeeEdit->setFocus();
2678 }