1 /***************************************************************************
2  * SPDX-FileCopyrightText: 2021 S. MANKOWSKI stephane@mankowski.fr
3  * SPDX-FileCopyrightText: 2021 G. DE BURE support@mankowski.fr
4  * SPDX-License-Identifier: GPL-3.0-or-later
5  ***************************************************************************/
6 /** @file
7  * This file is Skrooge plugin for operation management.
8  *
9  * @author Stephane MANKOWSKI / Guillaume DE BURE
10  */
11 #include "skgoperationpluginwidget.h"
12 
13 #include <kcolorscheme.h>
14 #include <kstandardaction.h>
15 
16 #include <qcompleter.h>
17 #include <qdir.h>
18 #include <qdom.h>
19 #include <qfile.h>
20 #include <qheaderview.h>
21 #include <qinputdialog.h>
22 #include <qmap.h>
23 #include <qscriptengine.h>
24 #include <qstandardpaths.h>
25 #include <qstringlistmodel.h>
26 #include <qtablewidget.h>
27 
28 #include "skgbankincludes.h"
29 #include "skgcalculatoredit.h"
30 #include "skgmainpanel.h"
31 #include "skgobjectbase.h"
32 #include "skgobjectmodel.h"
33 #include "skgoperation_settings.h"
34 #include "skgpayeeobject.h"
35 #include "skgservices.h"
36 #include "skgshow.h"
37 #include "skgsplittabledelegate.h"
38 #include "skgtraces.h"
39 #include "skgtreeview.h"
40 
SKGOperationPluginWidget(QWidget * iParent,SKGDocumentBank * iDocument)41 SKGOperationPluginWidget::SKGOperationPluginWidget(QWidget* iParent, SKGDocumentBank* iDocument)
42     : SKGTabPage(iParent, iDocument), m_objectModel(nullptr), m_fastEditionAction(nullptr), m_lastFastEditionOperationFound(0), m_showClosedAccounts(false),
43       m_numberFieldIsNotUptodate(true), m_modeInfoZone(0), m_tableDelegate(nullptr)
44 {
45     SKGTRACEINFUNC(1)
46     if (iDocument == nullptr) {
47         return;
48     }
49 
50     m_timer.setSingleShot(true);
51     connect(&m_timer, &QTimer::timeout, this, &SKGOperationPluginWidget::onRefreshInformationZone, Qt::QueuedConnection);
52 
53     ui.setupUi(this);
54 
55     ui.kAccountLabel2->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_ACCOUNT"))));
56     ui.kDateLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("d_date"))));
57     ui.kAmountLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("f_CURRENTAMOUNT"))));
58     ui.kPayeeLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_payee"))));
59     ui.kTypeLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_mode"))));
60     ui.kNumberEdit->setPlaceholderText(iDocument->getDisplay(QStringLiteral("t_number")));
61     ui.kCategoryLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_CATEGORY"))));
62     ui.kCommentLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_comment"))));
63     ui.kTrackerLabel->setText(i18n("%1:", iDocument->getDisplay(QStringLiteral("t_REFUND"))));
64 
65     ui.kUnitEdit->setDocument(iDocument);
66     ui.kUnitShare->setDocument(iDocument);
67 
68 
69     ui.kTitle->hide();
70     ui.kReconciliatorFrame2->hide();
71     ui.kReconciliateAccount->hide();
72 
73     m_attributesForSplit  << QStringLiteral("d_date") << QStringLiteral("t_category") << QStringLiteral("f_value") << QStringLiteral("t_comment")  << QStringLiteral("t_refund");
74     int nb = m_attributesForSplit.count();
75     ui.kSubOperationsTable->setColumnCount(nb);
76     for (int i = 0; i < nb; ++i) {
77         QString att = m_attributesForSplit.at(i);
78         auto item = new QTableWidgetItem(iDocument->getIcon(att), iDocument->getDisplay(att));
79         ui.kSubOperationsTable->setHorizontalHeaderItem(i, item);
80     }
81 
82     {
83         // Bind operation view
84         m_objectModel = new SKGObjectModel(qobject_cast<SKGDocumentBank*>(getDocument()), QStringLiteral("v_operation_display_all"), QStringLiteral("1=0"), this, QLatin1String(""), false);
85         ui.kOperationView->setModel(m_objectModel);
86 
87         // Add registered global action in contextual menu
88         if (SKGMainPanel::getMainPanel() != nullptr) {
89             m_fastEditionAction = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("fast_edition"));
90         }
91 
92         connect(ui.kOperationView->getView(), &SKGTreeView::clickEmptyArea, this, &SKGOperationPluginWidget::cleanEditor);
93         connect(ui.kOperationView->getView(), &SKGTreeView::doubleClicked, this, &SKGOperationPluginWidget::onDoubleClick);
94         connect(ui.kOperationView->getView(), &SKGTreeView::selectionChangedDelayed, this, [ = ] {this->onSelectionChanged();});
95     }
96 
97     // Add Standard KDE Icons to buttons to Operations
98     ui.kModifyOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok")));
99     ui.kAddOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("list-add")));
100     ui.kCleanBtn->setIcon(SKGServices::fromTheme(QStringLiteral("edit-clear")));
101     ui.kReconciliatorButton->setIcon(SKGServices::fromTheme(QStringLiteral("view-refresh")));
102     ui.kValidate->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok")));
103     ui.kValidate->setIconSize(QSize(48, 48));
104     ui.kAutoPoint->setIcon(SKGServices::fromTheme(QStringLiteral("games-solve")));
105     ui.kCreateFakeOperation->setIcon(SKGServices::fromTheme(QStringLiteral("list-add")));
106     ui.kFastEditBtn->setIcon(SKGServices::fromTheme(QStringLiteral("games-solve")));
107 
108     {
109         SKGWidgetSelector::SKGListQWidget list;
110         list.push_back(ui.SKGBasicSection);
111         list.push_back(ui.SKGPayeeModeSection);
112         list.push_back(ui.SKGSmallButtons);
113         list.push_back(ui.SKGEditionButtonsWidget);
114         list.push_back(ui.SKGSingleOpSection);
115         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("dialog-ok")), i18n("Standard"), i18n("Display the edit panel for standard operations"), list);
116     }
117 
118     {
119         SKGWidgetSelector::SKGListQWidget list;
120         list.push_back(ui.SKGBasicSection);
121         list.push_back(ui.SKGPayeeModeSection);
122         list.push_back(ui.SKGSmallButtons);
123         list.push_back(ui.SKGEditionButtonsWidget);
124         list.push_back(ui.SKGSplitOpSection);
125         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("split")), i18n("Split"), i18n("Display the edit panel for split operations"), list);
126     }
127     {
128         SKGWidgetSelector::SKGListQWidget list;
129         list.push_back(ui.SKGBasicSection);
130         list.push_back(ui.SKGPayeeModeSection);
131         list.push_back(ui.SKGSmallButtons);
132         list.push_back(ui.SKGEditionButtonsWidget);
133         list.push_back(ui.SKGSingleOpSection);
134         list.push_back(ui.kTargetAccountEdit);
135         list.push_back(ui.kTargetAccountLabel);
136         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("exchange-positions")), i18n("Transfer"), i18n("Display the edit panel for transfers between accounts"), list);
137     }
138     {
139         SKGWidgetSelector::SKGListQWidget list;
140         list.push_back(ui.SKGBasicSection);
141         list.push_back(ui.SKGPayeeModeSection);
142         list.push_back(ui.SKGSmallButtons);
143         list.push_back(ui.SKGEditionButtonsWidget);
144         list.push_back(ui.SKGSharesSection);
145         ui.kWidgetSelector->addButton(SKGServices::fromTheme(QStringLiteral("view-bank-account-savings")), i18n("Shares"), i18n("Display the edit panel for purchasing or selling shares"), list);
146     }
147     connect(ui.kWidgetSelector, &SKGWidgetSelector::selectedModeChanged, this, &SKGOperationPluginWidget::onBtnModeClicked);
148 
149     ui.kFreezeBtn->setIcon(SKGServices::fromTheme(QStringLiteral("emblem-locked")));
150 
151     // Fast edition
152     connect(qApp, &QApplication::focusChanged, this, &SKGOperationPluginWidget::onFocusChanged);
153     connect(m_fastEditionAction, &QAction::triggered, this, &SKGOperationPluginWidget::onFastEdition);
154 
155     // SubOperations
156     connect(ui.kAmountEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onQuantityChanged);
157     connect(ui.kDateEdit, &SKGDateEdit::dateChanged, this, &SKGOperationPluginWidget::onDateChanged);
158     connect(ui.kSubOperationsTable, &SKGTableWidget::cellChanged, this, &SKGOperationPluginWidget::onSubopCellChanged);
159     connect(ui.kSubOperationsTable->verticalHeader(), &QHeaderView::sectionClicked, this, &SKGOperationPluginWidget::onRemoveSubOperation);
160 
161     ui.kSubOperationsTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
162     ui.kSubOperationsTable->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
163     ui.kSubOperationsTable->setWordWrap(false);
164     m_tableDelegate = new SKGSplitTableDelegate(ui.kSubOperationsTable, getDocument(), m_attributesForSplit);
165     m_tableDelegate->addParameterValue(QStringLiteral("total"), '0');
166     ui.kSubOperationsTable->setItemDelegate(m_tableDelegate);
167     ui.kSubOperationsTable->setTextElideMode(Qt::ElideMiddle);
168     connect(ui.kSubOperationsTable, &SKGTableWidget::removeLine, this, &SKGOperationPluginWidget::onRemoveSubOperation);
169 
170     ui.kTargetAccountEdit->hide();
171     ui.kTargetAccountLabel->hide();
172     ui.SKGSplitOpSection->hide();
173     ui.SKGSharesSection->hide();
174 
175     ui.kWidgetSelector->setSelectedMode(0);
176 
177     // Set Event filters to catch CTRL+ENTER or SHIFT+ENTER
178     mainWidget()->installEventFilter(this);
179     this->installEventFilter(this);
180 
181     // Set Event filters for locking widgets
182     ui.kTypeEdit->lineEdit()->installEventFilter(this);
183     ui.kTypeEdit->installEventFilter(this);
184     ui.kUnitEdit->lineEdit()->installEventFilter(this);
185     ui.kUnitEdit->installEventFilter(this);
186     ui.kCategoryEdit->lineEdit()->installEventFilter(this);
187     ui.kCategoryEdit->installEventFilter(this);
188     ui.kCommentEdit->lineEdit()->installEventFilter(this);
189     ui.kCommentEdit->installEventFilter(this);
190     ui.kPayeeEdit->lineEdit()->installEventFilter(this);
191     ui.kPayeeEdit->installEventFilter(this);
192     ui.kTrackerEdit->lineEdit()->installEventFilter(this);
193     ui.kTrackerEdit->installEventFilter(this);
194     ui.kAccountEdit->installEventFilter(this);
195     ui.kTargetAccountLabel->installEventFilter(this);
196     ui.kAmountEdit->installEventFilter(this);
197     ui.kNumberEdit->installEventFilter(this);
198 
199     connect(getDocument(), &SKGDocument::tableModified, this, &SKGOperationPluginWidget::dataModified, Qt::QueuedConnection);
200 
201     connect(ui.kUnitEdit, &SKGUnitComboBox::editTextChanged, this, &SKGOperationPluginWidget::refreshSubOperationAmount, Qt::QueuedConnection);
202     connect(ui.kUnitEdit, &SKGUnitComboBox::editTextChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
203     connect(ui.kUnitShare, static_cast<void (SKGUnitComboBox::*)(int)>(&SKGUnitComboBox::currentIndexChanged), this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
204     connect(ui.kAmountEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
205     connect(ui.kAmountSharesEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
206     connect(ui.kCommissionEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
207     connect(ui.kTaxEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
208     connect(ui.kAccountEdit, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
209     connect(ui.kOperationView->getShowWidget(), &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onFilterChanged, Qt::QueuedConnection);
210     connect(ui.kPayeeEdit->lineEdit(), &QLineEdit::returnPressed, this, &SKGOperationPluginWidget::onPayeeChanged, Qt::QueuedConnection);
211 
212     connect(ui.kReconcilitorAmountEdit, &SKGCalculatorEdit::textChanged, this, &SKGOperationPluginWidget::onRefreshInformationZone);
213     connect(ui.kAddOperationBtn, &QPushButton::clicked, this, &SKGOperationPluginWidget::onAddOperationClicked);
214     connect(ui.kModifyOperationBtn, &QPushButton::clicked, this, &SKGOperationPluginWidget::onUpdateOperationClicked);
215     connect(ui.kReconciliatorButton, &QToolButton::clicked, this, &SKGOperationPluginWidget::onRotateAccountTools);
216     connect(ui.kValidate, &QToolButton::clicked, this, &SKGOperationPluginWidget::onValidatePointedOperations);
217     connect(ui.kCleanBtn, &QPushButton::clicked, this, &SKGOperationPluginWidget::cleanEditor);
218     connect(ui.kSubOperationsTable, &SKGTableWidget::itemSelectionChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified);
219     connect(ui.kAutoPoint, &QToolButton::clicked, this, &SKGOperationPluginWidget::onAutoPoint);
220     connect(ui.kFreezeBtn, &QToolButton::clicked, this, &SKGOperationPluginWidget::onFreeze);
221     connect(ui.kCreateFakeOperation, &QToolButton::clicked, this, &SKGOperationPluginWidget::onAddFakeOperation);
222     connect(ui.kFastEditBtn, &QPushButton::clicked, this, &SKGOperationPluginWidget::onFastEdition);
223 
224     dataModified(QLatin1String(""), 0);
225     onOperationCreatorModified();
226 
227     setAllWidgetsEnabled();
228 }
229 
~SKGOperationPluginWidget()230 SKGOperationPluginWidget::~SKGOperationPluginWidget()
231 {
232     SKGTRACEINFUNC(1)
233     m_objectModel = nullptr;
234     m_fastEditionAction = nullptr;
235 }
236 
currentAccount()237 QString SKGOperationPluginWidget::currentAccount()
238 {
239     QStringList accounts = SKGServices::splitCSVLine(ui.kOperationView->getShowWidget()->getState());
240     for (const auto& item : qAsConst(accounts)) {
241         if (item.startsWith(QLatin1String("##_"))) {
242             return item.right(item.length() - 3);
243         }
244     }
245     return QLatin1String("");
246 }
247 
isWidgetEditionEnabled(QWidget * iWidget)248 bool SKGOperationPluginWidget::isWidgetEditionEnabled(QWidget* iWidget)
249 {
250     return ((iWidget != nullptr) && (!iWidget->property("frozen").isValid() || !iWidget->property("frozen").toBool()));
251 }
252 
setWidgetEditionEnabled(QWidget * iWidget,bool iEnabled)253 void SKGOperationPluginWidget::setWidgetEditionEnabled(QWidget* iWidget, bool iEnabled)
254 {
255     if ((iWidget != nullptr) && isWidgetEditionEnabled(iWidget) != iEnabled) {
256         if (iEnabled) {
257             iWidget->setStyleSheet(QStringLiteral("background-image:none;"));
258             iWidget->setProperty("frozen", false);
259         } else {
260             auto color = KColorScheme(QPalette::Normal).background(KColorScheme::ActiveBackground).color().name().right(6);
261             iWidget->setStyleSheet("background-color:#" % color);
262             iWidget->setProperty("frozen", true);
263         }
264 
265         QString addOn = i18nc("A tool tip", "This field is frozen (it will not be affected by Fast Edition). Double click to unfreeze it");
266         QString t = iWidget->toolTip().remove('\n' % addOn).remove(addOn);
267         if (!iEnabled) {
268             t = iWidget->toolTip();
269             if (!t.isEmpty()) {
270                 t += '\n';
271             }
272             t += addOn;
273         }
274         iWidget->setToolTip(t);
275 
276         // 348619: Freeze the unit when amount is frozen
277         if (iWidget == ui.kAmountEdit) {
278             setWidgetEditionEnabled(ui.kUnitEdit->lineEdit(), iEnabled);
279         }
280     }
281 }
282 
eventFilter(QObject * iObject,QEvent * iEvent)283 bool SKGOperationPluginWidget::eventFilter(QObject* iObject, QEvent* iEvent)
284 {
285     if ((iEvent != nullptr) && iEvent->type() == QEvent::MouseButtonDblClick) {
286         auto* line = qobject_cast<QLineEdit*>(iObject);
287         if (line != nullptr) {
288             setWidgetEditionEnabled(line, !isWidgetEditionEnabled(line));
289         }
290     } else if ((iEvent != nullptr) && iEvent->type() == QEvent::FocusIn) {
291         auto* line = qobject_cast<QLineEdit*>(iObject);
292         if (line != nullptr) {
293             m_previousValue = line->text();
294         } else {
295             auto* cmb = qobject_cast<SKGComboBox*>(iObject);
296             if (cmb != nullptr) {
297                 m_previousValue = cmb->text();
298             }
299         }
300     } else if ((iEvent != nullptr) && iEvent->type() == QEvent::FocusOut) {
301         auto* line = qobject_cast<QLineEdit*>(iObject);
302         if (line != nullptr) {
303             if (m_previousValue != line->text() && !line->text().isEmpty()) {
304                 setWidgetEditionEnabled(line, false);
305             }
306         } else {
307             auto* cmb = qobject_cast<SKGComboBox*>(iObject);
308             if (cmb != nullptr) {
309                 if (m_previousValue != cmb->text() && !cmb->text().isEmpty()) {
310                     setWidgetEditionEnabled(cmb->lineEdit(), false);
311                 }
312             }
313         }
314     } else if ((iEvent != nullptr) && iEvent->type() == QEvent::KeyPress) {
315         auto* keyEvent = dynamic_cast<QKeyEvent*>(iEvent);
316         if (keyEvent && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && iObject == this) {
317             if ((QApplication::keyboardModifiers() & Qt::ControlModifier) != 0u && ui.kAddOperationBtn->isEnabled()) {
318                 ui.kAddOperationBtn->click();
319             } else if ((QApplication::keyboardModifiers() &Qt::ShiftModifier) != 0u && ui.kModifyOperationBtn->isEnabled()) {
320                 ui.kModifyOperationBtn->click();
321             }
322         } else if (keyEvent && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) && iObject == ui.kNumberEdit) {
323             int c = SKGServices::stringToInt(ui.kNumberEdit->text());
324             if (c != 0) {
325                 ui.kNumberEdit->setText(SKGServices::intToString(keyEvent->key() == Qt::Key_Up ? c + 1 : c - 1));
326             }
327         }
328     }
329 
330     return SKGTabPage::eventFilter(iObject, iEvent);
331 }
332 
onFreeze()333 void SKGOperationPluginWidget::onFreeze()
334 {
335     if (!ui.kFreezeBtn->isChecked()) {
336         ui.kFreezeBtn->setIcon(SKGServices::fromTheme(QStringLiteral("emblem-locked")));
337         // At least one fiels is already frozen ==> unfreeze
338         setAllWidgetsEnabled();
339     } else {
340         QStringList overlay;
341         overlay.push_back(QStringLiteral("edit-delete"));
342         ui.kFreezeBtn->setIcon(SKGServices::fromTheme(QStringLiteral("emblem-locked"), overlay));
343         // No wildget frozen ==> freeze widget containing test
344         if (!ui.kTypeEdit->text().isEmpty()) {
345             setWidgetEditionEnabled(ui.kTypeEdit->lineEdit(), false);
346         }
347         if (!ui.kUnitEdit->text().isEmpty()) {
348             setWidgetEditionEnabled(ui.kUnitEdit->lineEdit(), false);
349         }
350         if (!ui.kCategoryEdit->text().isEmpty()) {
351             setWidgetEditionEnabled(ui.kCategoryEdit->lineEdit(), false);
352         }
353         if (!ui.kCommentEdit->text().isEmpty()) {
354             setWidgetEditionEnabled(ui.kCommentEdit->lineEdit(), false);
355         }
356         if (!ui.kPayeeEdit->text().isEmpty()) {
357             setWidgetEditionEnabled(ui.kPayeeEdit->lineEdit(), false);
358         }
359         if (!ui.kTrackerEdit->text().isEmpty()) {
360             setWidgetEditionEnabled(ui.kTrackerEdit->lineEdit(), false);
361         }
362         // if(!ui.kAccountEdit->text().isEmpty()) setWidgetEditionEnabled(ui.kAccountEdit, false);
363         if (!ui.kAmountEdit->text().isEmpty()) {
364             setWidgetEditionEnabled(ui.kAmountEdit, false);
365         }
366         if (!ui.kNumberEdit->text().isEmpty()) {
367             setWidgetEditionEnabled(ui.kNumberEdit, false);
368         }
369         if (!ui.kTargetAccountEdit->text().isEmpty()) {
370             setWidgetEditionEnabled(ui.kTargetAccountEdit, false);
371         }
372     }
373 }
374 
setAllWidgetsEnabled()375 void SKGOperationPluginWidget::setAllWidgetsEnabled()
376 {
377     SKGTRACEINFUNC(10)
378     // Enable widgets
379     setWidgetEditionEnabled(ui.kTypeEdit->lineEdit(), true);
380     setWidgetEditionEnabled(ui.kUnitEdit->lineEdit(), true);
381     setWidgetEditionEnabled(ui.kCategoryEdit->lineEdit(), true);
382     setWidgetEditionEnabled(ui.kCommentEdit->lineEdit(), true);
383     setWidgetEditionEnabled(ui.kPayeeEdit->lineEdit(), true);
384     setWidgetEditionEnabled(ui.kTrackerEdit->lineEdit(), true);
385     setWidgetEditionEnabled(ui.kAccountEdit, true);
386     setWidgetEditionEnabled(ui.kTargetAccountEdit, true);
387     setWidgetEditionEnabled(ui.kAmountEdit, true);
388     setWidgetEditionEnabled(ui.kNumberEdit, true);
389 }
390 
getAttributeOfSelection(const QString & iAttribute)391 QString SKGOperationPluginWidget::getAttributeOfSelection(const QString& iAttribute)
392 {
393     QString output;
394     SKGObjectBase::SKGListSKGObjectBase selectedObjects = ui.kOperationView->getView()->getSelectedObjects();
395     int nb = selectedObjects.count();
396     for (int i = 0; i < nb ; ++i) {
397         const SKGObjectBase& obj = selectedObjects.at(i);
398         QString val = obj.getAttribute(iAttribute);
399         if (i > 0 && val != output) {
400             output = NOUPDATE;
401             break;
402         }
403         output = val;
404     }
405 
406     return output;
407 }
408 
onSelectionChanged()409 void SKGOperationPluginWidget::onSelectionChanged()
410 {
411     SKGTRACEINFUNC(10)
412 
413     int mode = ui.kWidgetSelector->getSelectedMode();
414 
415     // Enable widgets
416     setAllWidgetsEnabled();
417     ui.kFreezeBtn->setChecked(false);
418     ui.kFreezeBtn->setIcon(SKGServices::fromTheme(QStringLiteral("emblem-locked")));
419 
420     // Mapping
421     int nbSelect = ui.kOperationView->getView()->getNbSelectedObjects();
422     bool onConsolidatedTable = false;
423     if ((nbSelect != 0) && (m_objectModel != nullptr)) {
424         SKGObjectBase objbase = ui.kOperationView->getView()->getFirstSelectedObject();
425         SKGOperationObject obj;
426         onConsolidatedTable = (objbase.getTable() == QStringLiteral("v_suboperation_consolidated"));
427         if (onConsolidatedTable) {
428             obj = SKGOperationObject(obj.getDocument(), SKGServices::stringToInt(obj.getAttribute(QStringLiteral("i_OPID"))));
429         } else {
430             obj = objbase;
431         }
432 
433         ui.kDateEdit->setDate(SKGServices::stringToTime(objbase.getAttribute(QStringLiteral("d_date"))).date());
434         m_previousDate = ui.kDateEdit->date();
435         ui.kCommentEdit->setText(objbase.getAttribute(onConsolidatedTable ? QStringLiteral("t_REALCOMMENT") : QStringLiteral("t_comment")));
436         QString number = objbase.getAttribute(QStringLiteral("t_number"));
437         ui.kNumberEdit->setText(number);
438         QString accountName = objbase.getAttribute(QStringLiteral("t_ACCOUNT"));
439         if (!m_showClosedAccounts && !accountName.isEmpty() && !ui.kAccountEdit->contains(accountName)) {
440             // Refresh list of accounts if a closed account is selected
441             m_showClosedAccounts = true;
442             dataModified(QLatin1String(""), 0);
443         }
444         ui.kAccountEdit->setText(accountName);
445         ui.kPayeeEdit->setText(objbase.getAttribute(QStringLiteral("t_PAYEE")));
446         ui.kTypeEdit->setText(objbase.getAttribute(QStringLiteral("t_mode")));
447         QString unit = objbase.getAttribute(QStringLiteral("t_UNIT"));
448         ui.kUnitEdit->setText(unit);
449         QString cat = objbase.getAttribute(QStringLiteral("t_REALCATEGORY"));
450         if (cat.isEmpty()) {
451             cat = objbase.getAttribute(QStringLiteral("t_CATEGORY"));
452         }
453         ui.kCategoryEdit->setText(cat);
454         ui.kTrackerEdit->setText(objbase.getAttribute(onConsolidatedTable ? QStringLiteral("t_REALREFUND") : QStringLiteral("t_REFUND")));
455         QString quantity = objbase.getAttribute(QStringLiteral("f_REALQUANTITY"));
456         if (quantity.isEmpty()) {
457             quantity = objbase.getAttribute(QStringLiteral("f_QUANTITY"));
458         }
459         double quantityVal = SKGServices::stringToDouble(quantity);
460         SKGUnitObject unitObject = ui.kUnitEdit->getUnit();
461         int nbDec = unitObject.getNumberDecimal();
462         if (nbDec == 0) {
463             nbDec = 2;
464         }
465         quantity = SKGServices::toCurrencyString(qAbs(quantityVal), QLatin1String(""), nbDec);
466         if (quantity.startsWith(QLocale().positiveSign())) {
467             quantity = quantity.right(quantity.length() - 1);
468         }
469         if (quantityVal > 0) {
470             quantity = '+' % quantity;
471         } else {
472             quantity = '-' % quantity;
473         }
474         ui.kAmountEdit->setText(quantity);
475 
476         if (nbSelect > 1) {
477             // In case of multi selection
478             if (mode >= 0) {
479                 ui.kWidgetSelector->setSelectedMode(0);
480             }
481             ui.kAccountEdit->setText(getAttributeOfSelection(QStringLiteral("t_ACCOUNT")));
482             ui.kTypeEdit->setText(getAttributeOfSelection(QStringLiteral("t_mode")));
483             ui.kUnitEdit->setText(getAttributeOfSelection(QStringLiteral("t_UNIT")));
484             ui.kCategoryEdit->setText(getAttributeOfSelection(onConsolidatedTable ? QStringLiteral("t_REALCATEGORY") : QStringLiteral("t_CATEGORY")));
485             ui.kTrackerEdit->setText(getAttributeOfSelection(onConsolidatedTable ? QStringLiteral("t_REALREFUND") : QStringLiteral("t_REFUND")));
486             ui.kCommentEdit->setText(getAttributeOfSelection(onConsolidatedTable ? QStringLiteral("t_REALCOMMENT") : QStringLiteral("t_comment")));
487             ui.kPayeeEdit->setText(getAttributeOfSelection(QStringLiteral("t_PAYEE")));
488 
489             QString d = getAttributeOfSelection(QStringLiteral("d_date"));
490             if (d == NOUPDATE) {
491                 ui.kDateEdit->setCurrentText(NOUPDATE);
492             }
493 
494             QString q = getAttributeOfSelection(onConsolidatedTable ? QStringLiteral("f_REALQUANTITY") : QStringLiteral("f_QUANTITY"));
495             ui.kAmountEdit->setText(q != NOUPDATE ? quantity : NOUPDATE);
496             ui.kNumberEdit->setText(QLatin1String(""));
497         } else {
498             if (obj.getStatus() == SKGOperationObject::POINTED) {
499                 displayReconciliationInfo();
500             } else if (m_modeInfoZone != 1) {
501                 displayBalance();
502             }
503 
504             // It is a single selection
505             // Is it a split ?
506             int nbSubOperations = obj.getNbSubOperations();
507             if (nbSubOperations > 1 && !onConsolidatedTable) {
508                 // yes, it is a split
509                 if (mode >= 0) {
510                     ui.kWidgetSelector->setSelectedMode(1);
511                 }
512 
513                 displaySubOperations();
514             } else {
515                 // Is it a transfer ?
516                 SKGOperationObject op2;
517                 if (obj.isTransfer(op2) && op2.exist()) {
518                     // yes it is a transfer
519                     SKGAccountObject account2;
520                     op2.getParentAccount(account2);
521                     QString accountName2 = account2.getName();
522                     if (!m_showClosedAccounts && !ui.kTargetAccountEdit->contains(accountName2)) {
523                         // Refresh list of accounts if a closed account is selected
524                         m_showClosedAccounts = true;
525                         dataModified(QLatin1String(""), 0);
526                     }
527                     ui.kTargetAccountEdit->setText(accountName2);
528                     if (mode >= 0) {
529                         ui.kWidgetSelector->setSelectedMode(2);
530                     }
531                 } else {
532                     if (mode >= 0) {
533                         ui.kWidgetSelector->setSelectedMode(0);
534                     }
535                 }
536             }
537         }
538     }
539 
540     ui.kNumberEdit->setEnabled(nbSelect <= 1);
541 
542     bool splitTest = nbSelect <= 1 && !onConsolidatedTable;
543     ui.kWidgetSelector->setEnabledMode(1, splitTest);
544     if (!splitTest && mode == 1) {
545         ui.kWidgetSelector->setSelectedMode(0);
546     }
547 
548     onOperationCreatorModified();
549 
550     Q_EMIT selectionChanged();
551 }
552 
onOperationCreatorModified()553 void SKGOperationPluginWidget::onOperationCreatorModified()
554 {
555     SKGTRACEINFUNC(10)
556 
557     int mode = ui.kWidgetSelector->getSelectedMode();
558 
559     // Set icons
560     if (!isTemplateMode()) {
561         ui.kModifyOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok")));
562         ui.kAddOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("list-add")));
563     } else {
564         QStringList overlay;
565         overlay.push_back(QStringLiteral("edit-guides"));
566         ui.kModifyOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok"), overlay));
567         ui.kAddOperationBtn->setIcon(SKGServices::fromTheme(QStringLiteral("list-add"), overlay));
568     }
569 
570     // Is it an existing unit ?
571     QString unitName = ui.kUnitEdit->currentText();
572     SKGUnitObject unit(getDocument());
573     unit.setName(unitName);
574     unit.setSymbol(unitName);
575     if (unit.load().isSucceeded()) {
576         ui.kWidgetSelector->setEnabledMode(3, true);
577         if (mode == 3 && unit.getType() == SKGUnitObject::SHARE) {
578             // Update units
579             auto unit = ui.kUnitShare->getUnit();
580             ui.kUnitCommission->setText(unit.getSymbol());
581             ui.kUnitTax->setText(unit.getSymbol());
582 
583             // Update total in "purchase / sale share" page
584             double total = ui.kAmountSharesEdit->value() + (ui.kCommissionEdit->value() + ui.kTaxEdit->value()) * (ui.kAmountEdit->value() > 0 ? 1 : -1);
585             ui.KTotal->setText(SKGServices::toCurrencyString(total, unit.getSymbol(), unit.getNumberDecimal()));
586         } else {
587             // BUG 2692665
588             auto unitShareName = ui.kUnitShare->currentText();
589             if (unitShareName.isEmpty()) {
590                 ui.kUnitShare->setText(unitName);
591                 ui.kUnitCommission->setText(unitName);
592                 ui.kUnitTax->setText(unitName);
593                 ui.KTotal->setText(unitName);
594 
595             } else {
596                 ui.kUnitCommission->setText(unitShareName);
597                 ui.kUnitTax->setText(unitShareName);
598                 ui.KTotal->setText(unitShareName);
599             }
600         }
601 
602     } else {
603         ui.kWidgetSelector->setEnabledMode(3, false);
604         if (mode == 3) {
605             ui.kWidgetSelector->setSelectedMode(0);
606         }
607     }
608 
609     bool activated = mode != -1 &&
610                      !ui.kAccountEdit->currentText().isEmpty() &&
611                      ((!ui.kAmountEdit->text().isEmpty() && (ui.kAmountEdit->valid() || ui.kAmountEdit->text() == NOUPDATE)) || !ui.kAmountEdit->isEnabled()) &&
612                      !unitName.isEmpty() &&
613                      (mode != 3 || !ui.kAmountSharesEdit->text().isEmpty());
614 
615     int nbSelect = getNbSelectedObjects();
616 
617     ui.kAddOperationBtn->setEnabled(activated);
618     ui.kModifyOperationBtn->setEnabled(activated && nbSelect > 0 && (ui.kWidgetSelector->getSelectedMode() == 0 ||  ui.kWidgetSelector->getSelectedMode() == 1 ||  ui.kWidgetSelector->getSelectedMode() == 2));
619 
620     m_numberFieldIsNotUptodate = true;
621     if (ui.kNumberEdit->hasFocus()) {
622         fillNumber();
623     }
624 }
625 
onPayeeChanged()626 void SKGOperationPluginWidget::onPayeeChanged()
627 {
628     if (skgoperation_settings::setCategoryForPayee() && ui.kCategoryEdit->text().isEmpty()) {
629         ui.kCategoryEdit->setText(qobject_cast<SKGDocumentBank*>(getDocument())->getCategoryForPayee(ui.kPayeeEdit->text(), false));
630     }
631 }
632 
onUpdateOperationClicked()633 void SKGOperationPluginWidget::onUpdateOperationClicked()
634 {
635     SKGError err;
636     SKGTRACEINFUNCRC(10, err)
637     // Get Selection
638     SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects();
639 
640     int nb = selection.count();
641     {
642         SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Operation update"), err, nb)
643         err = updateSelection(selection);
644     }
645 
646     // status bar
647     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Operation updated")))
648     else {
649         err.addError(ERR_FAIL, i18nc("Error message",  "Operation update failed"));
650     }
651 
652     // Display error
653     SKGMainPanel::displayErrorMessage(err, true);
654 
655     // Set focus on table
656     ui.kOperationView->getView()->setFocus();
657 }
658 
updateSelection(const SKGObjectBase::SKGListSKGObjectBase & iSelection,bool iForceCreation)659 SKGError SKGOperationPluginWidget::updateSelection(const SKGObjectBase::SKGListSKGObjectBase& iSelection, bool iForceCreation)
660 {
661     SKGError err;
662     SKGTRACEINFUNCRC(10, err)
663 
664     // Initialisation
665     double ratio = -1;
666     bool refreshSubOperation = true;
667 
668     // Get Selection
669     int nb = iSelection.count();
670     double rate = -1;
671 
672     for (int i = 0; !err && i < nb; ++i) {
673         const SKGObjectBase& obj = iSelection.at(i);
674         SKGOperationObject operationObj;
675         if (obj.getTable() == QStringLiteral("v_suboperation_consolidated")) {
676             operationObj = SKGOperationObject(obj.getDocument(), SKGServices::stringToInt(obj.getAttribute(QStringLiteral("i_OPID"))));
677         } else {
678             operationObj = SKGOperationObject(obj.getDocument(), obj.getID());
679         }
680 
681         SKGObjectBase::SKGListSKGObjectBase gops;
682         IFOKDO(err, operationObj.getGroupedOperations(gops))
683         if (gops.count() == 2 &&  ui.kWidgetSelector->getSelectedMode() < 2) {
684             getDocument()->sendMessage(i18nc("An information message", "You modified one part of a transfer"), SKGDocument::Warning);
685         }
686 
687         // Update operation if single selection
688         if (ui.kWidgetSelector->getSelectedMode() == 0) {
689             // Get subop
690             SKGSubOperationObject subOp;
691             int nbSubop = 0;
692             if (obj.getTable() == QStringLiteral("v_suboperation_consolidated")) {
693                 // It is a sub operation
694                 subOp = SKGSubOperationObject(obj.getDocument(), SKGServices::stringToInt(obj.getAttribute(QStringLiteral("i_SUBOPID"))));
695                 nbSubop = 1;
696             } else {
697                 // It is a real operation, we take the first one
698                 SKGObjectBase::SKGListSKGObjectBase subOps;
699                 IFOKDO(err, operationObj.getSubOperations(subOps))
700                 nbSubop = subOps.count();
701                 if (nbSubop != 0) {
702                     subOp = subOps[0];
703                 }
704             }
705 
706             QString trackerName = ui.kTrackerEdit->text().trimmed();
707             if (!err && trackerName != NOUPDATE) {
708                 SKGTrackerObject tracker;
709                 err = SKGTrackerObject::createTracker(qobject_cast<SKGDocumentBank*>(getDocument()), trackerName, tracker, true);
710                 IFOKDO(err, subOp.setTracker(tracker))
711             }
712 
713             SKGCategoryObject cat;
714             QString catName = ui.kCategoryEdit->text().trimmed();
715             if (!err &&  catName != NOUPDATE) {
716                 err = SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), catName, cat, true);
717                 IFOKDO(err, subOp.setCategory(cat))
718             } else {
719                 // Get current category to be able to find the appropriate sign
720                 subOp.getCategory(cat);
721             }
722 
723             if (!err && ui.kAmountEdit->text() != NOUPDATE) {
724                 if (nbSubop > 1) {
725                     err = SKGError(25, i18nc("Error message", "Cannot update a split operation"));
726                 } else {
727                     double val = ui.kAmountEdit->value();
728 
729                     // Is the sign forced ?
730                     if (ui.kAmountEdit->sign() == 0) {
731                         // No
732                         SKGObjectBase cat2(cat.getDocument(), QStringLiteral("v_category_display"), cat.getID());
733 
734                         // Are we able to find to sign with the category ?
735                         if (cat2.getAttribute(QStringLiteral("t_TYPEEXPENSE")) == QStringLiteral("-")) {
736                             val = -val;
737                         }
738                     }
739                     err = subOp.setQuantity(val);
740                 }
741             }
742 
743             if (!err && ui.kCommentEdit->text() != NOUPDATE) {
744                 err = subOp.setComment(ui.kCommentEdit->text());
745             }
746             IFOKDO(err, subOp.save())
747         } else if (ui.kWidgetSelector->getSelectedMode() == 1) {
748             // Case split
749             refreshSubOperation = false;
750             int nbsubop = ui.kSubOperationsTable->rowCount();
751             QList<int> listIdSubOp;  // clazy:exclude=container-inside-loop
752             listIdSubOp.reserve(nbsubop);
753             for (int j = 0; !err && j < nbsubop; ++j) {
754                 // Get values
755                 QTableWidgetItem* item = ui.kSubOperationsTable->item(j, m_attributesForSplit.indexOf(QStringLiteral("t_category")));
756                 int id = (iForceCreation ? 0 : item->data(Qt::UserRole).toInt());
757                 QString catName = item->text();
758 
759                 item = ui.kSubOperationsTable->item(j, m_attributesForSplit.indexOf(QStringLiteral("t_comment")));
760                 QString comment = item->text();
761 
762                 item = ui.kSubOperationsTable->item(j, m_attributesForSplit.indexOf(QStringLiteral("f_value")));
763                 double val = item->data(101).toDouble();
764                 QString formula = item->toolTip();
765 
766                 item = ui.kSubOperationsTable->item(j, m_attributesForSplit.indexOf(QStringLiteral("t_refund")));
767                 QString trackerName = item->text();
768 
769                 item = ui.kSubOperationsTable->item(j, m_attributesForSplit.indexOf(QStringLiteral("d_date")));
770                 QDate date = SKGServices::stringToTime(item->toolTip()).date();
771                 if (!date.isValid()) {
772                     date = ui.kDateEdit->date();
773                 }
774 
775                 SKGSubOperationObject subOperation;
776                 if (id != 0) {
777                     // Update existing sub op
778                     subOperation = SKGSubOperationObject(qobject_cast<SKGDocumentBank*>(getDocument()), id);
779                 } else {
780                     // Create new sub op
781                     err = operationObj.addSubOperation(subOperation);
782                 }
783 
784                 // Create sub operation object
785                 IFOK(err) {
786                     SKGCategoryObject cat;
787                     err = SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), catName, cat, true);
788                     IFOKDO(err, subOperation.setCategory(cat))
789                 }
790                 IFOK(err) {
791                     SKGTrackerObject tracker;
792                     err = SKGTrackerObject::createTracker(qobject_cast<SKGDocumentBank*>(getDocument()), trackerName, tracker, true);
793                     IFOKDO(err, subOperation.setTracker(tracker))
794                 }
795                 IFOKDO(err, subOperation.setOrder(ui.kSubOperationsTable->visualRow(j)))
796                 IFOKDO(err, subOperation.setDate(date))
797                 IFOKDO(err, subOperation.setComment(comment))
798                 IFOKDO(err, subOperation.setQuantity(val))
799                 if (formula.startsWith(QLatin1String("=")) && !err) {
800                     err = subOperation.setFormula(formula);
801                 }
802                 IFOKDO(err, subOperation.save())
803 
804                 // The sub operation created or updated mustn't be removed
805                 listIdSubOp.push_back(subOperation.getID());
806             }
807 
808             // Remove useless subop
809             IFOK(err) {
810                 SKGObjectBase::SKGListSKGObjectBase subOps;
811                 err = operationObj.getSubOperations(subOps);
812                 int nbsubop2 = subOps.count();
813                 for (int j = 0; !err && j < nbsubop2; ++j) {
814                     const SKGObjectBase& sop = subOps.at(j);
815                     if (!listIdSubOp.contains(sop.getID())) {
816                         err = sop.remove(false);
817                     }
818                 }
819             }
820         } else if (ui.kWidgetSelector->getSelectedMode() == 2) {
821             // Case transfer
822             // Create sub operation object
823             double operationQuantity = ui.kAmountEdit->value();
824 
825 
826             SKGSubOperationObject subOperation;
827             SKGObjectBase::SKGListSKGObjectBase subOps;
828             IFOKDO(err, operationObj.getSubOperations(subOps))
829             IFOK(err) {
830                 subOperation = subOps.at(0);
831 
832                 double oldQuantity = subOperation.getQuantity();
833 
834                 if (ui.kAmountEdit->sign() == 0) {
835                     operationQuantity = qAbs(operationQuantity);
836                 } else if (ui.kAmountEdit->sign() > 0) {
837                     operationQuantity = -qAbs(operationQuantity);
838                 } else {
839                     operationQuantity = qAbs(operationQuantity);
840                     if (oldQuantity == 0) {
841                         err = getDocument()->sendMessage(i18nc("An information message",  "Absolute value has been used for transfer creation."));
842                     }
843                 }
844 
845                 if (ui.kAmountEdit->text().trimmed() != NOUPDATE) {
846                     err = subOperation.setQuantity(-operationQuantity);
847                 } else {
848                     operationQuantity = -subOperation.getQuantity();
849                 }
850             }
851 
852             SKGTrackerObject tracker;
853             QString trackerName = ui.kTrackerEdit->text().trimmed();
854             if (!err) {
855                 if (trackerName != NOUPDATE) {
856                     err = SKGTrackerObject::createTracker(qobject_cast<SKGDocumentBank*>(getDocument()), trackerName, tracker, true);
857                     IFOKDO(err, subOperation.setTracker(tracker))
858                 } else {
859                     err = subOperation.getTracker(tracker);
860                 }
861             }
862 
863             SKGCategoryObject cat;
864             QString catName = ui.kCategoryEdit->text().trimmed();
865             if (!err) {
866                 if (catName != NOUPDATE) {
867                     err = SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), ui.kCategoryEdit->text(), cat, true);
868                     IFOKDO(err, subOperation.setCategory(cat))
869                 } else {
870                     err = subOperation.getCategory(cat);
871                 }
872             }
873             IFOKDO(err, subOperation.setComment(operationObj.getComment()))
874             IFOKDO(err, subOperation.save())
875 
876             // Get account
877             SKGAccountObject accountObj2(getDocument());
878             IFOKDO(err, accountObj2.setName(ui.kTargetAccountEdit->currentText()))
879             IFOKDO(err, accountObj2.load())
880 
881             // Check unit of target account
882             SKGUnitObject unit;
883             IFOKDO(err, operationObj.getUnit(unit))
884 
885             // Get date
886             QDate operationDate;
887             if (ui.kDateEdit->currentText().trimmed() != NOUPDATE) {
888                 operationDate = ui.kDateEdit->date();
889             } else {
890                 operationDate = operationObj.getDate();
891             }
892 
893 
894             // Correction bug 2299303 vvv
895             SKGUnitObject unitTargetAccount;
896             IFOKDO(err, accountObj2.getUnit(unitTargetAccount))
897             if (!err && unitTargetAccount.exist() && unit != unitTargetAccount) {
898                 // The unit of the operation is not compliant with the unit of the target account
899                 // We ask to the user if he wants to continue or convert into the target account
900                 bool ok = false;
901                 QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
902                 int decimal = qMax(unit.getNumberDecimal(), unitTargetAccount.getNumberDecimal());
903                 double newval;
904                 double defaultnewval = SKGUnitObject::convert(operationQuantity, unit, unitTargetAccount, operationDate);
905                 if (nb == 1) {
906                     newval = QInputDialog::getDouble(SKGMainPanel::getMainPanel(),
907                                                      i18nc("Question", "Confirmation"),
908                                                      i18nc("Question", "The operation's unit is not compatible with the target account.\n"
909                                                            "Click Cancel if you want to continue anyway; "
910                                                            "otherwise, enter the value in the target account's unit (%1):", unitTargetAccount.getSymbol()),
911                                                      defaultnewval,
912                                                      -std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), decimal, &ok);
913                 } else {
914                     if (rate == -1) {
915                         newval = QInputDialog::getDouble(SKGMainPanel::getMainPanel(),
916                                                          i18nc("Question", "Confirmation"),
917                                                          i18nc("Question", "Some operation's units are not compatible with the target account.\n"
918                                                                "The first selected operation is:%1\n"
919                                                                "Click Cancel if you want to continue anyway; "
920                                                                "otherwise, enter the value in the target account's unit (%2):\nThe same rate will be applied to all operations.", operationObj.getDisplayName(), unitTargetAccount.getSymbol()),
921                                                          defaultnewval,
922                                                          -std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), decimal, &ok);
923                         if (ok) {
924                             rate = defaultnewval / newval;
925                         } else {
926                             // Mode cancel for all
927                             rate = -2;
928                         }
929                     } else {
930                         if (rate != -2) {
931                             ok = true;
932                             newval = defaultnewval / rate;
933                         }
934                     }
935                 }
936                 QApplication::restoreOverrideCursor();
937                 if (ok) {
938                     operationQuantity = newval;
939                     unit = unitTargetAccount;
940                 } else {
941                     // Mode cancel for all
942                     rate = -2;
943                 }
944             }
945             // Correction bug 2299303 ^^^
946 
947             // create transferred operation
948             SKGOperationObject operation2;
949 
950             IFOK(err) {
951                 if (gops.count() == 2) {
952                     operation2 = (obj == SKGOperationObject(gops.at(0)) ? gops.at(1) : gops.at(0));
953                     if (ui.kTargetAccountEdit->text() != NOUPDATE) {
954                         err = operation2.setParentAccount(accountObj2);
955                     }
956                 } else {
957                     err = accountObj2.addOperation(operation2);
958                 }
959             }
960             if (nb == 1) {
961                 IFOKDO(err, operation2.setMode(ui.kTypeEdit->currentText()))
962                 QString payeeName = ui.kPayeeEdit->currentText();
963                 if (!err &&  payeeName != NOUPDATE) {
964                     SKGPayeeObject payeeObject;
965                     err = SKGPayeeObject::createPayee(qobject_cast<SKGDocumentBank*>(getDocument()), payeeName, payeeObject, true);
966                     IFOKDO(err, operation2.setPayee(payeeObject))
967                 }
968                 IFOKDO(err, operation2.setNumber(ui.kNumberEdit->text()))
969                 IFOKDO(err, operation2.setComment(ui.kCommentEdit->text()))
970                 IFOKDO(err, operation2.setDate(operationDate, refreshSubOperation))
971                 IFOKDO(err, operation2.setUnit(unit))
972 
973             } else {
974                 IFOKDO(err, operation2.setMode(operationObj.getMode()))
975                 IFOKDO(err, operation2.setAttribute(QStringLiteral("r_payee_id"), operationObj.getAttribute(QStringLiteral("r_payee_id"))))
976                 IFOKDO(err, operation2.setNumber(operationObj.getNumber()))
977                 IFOKDO(err, operation2.setComment(operationObj.getComment()))
978                 IFOKDO(err, operation2.setDate(operationDate, refreshSubOperation))
979                 IFOKDO(err, operation2.setUnit(unit))
980             }
981             IFOKDO(err, operation2.setGroupOperation(operationObj))
982             IFOKDO(err, operationObj.load())
983             IFOKDO(err, operation2.setTemplate(isTemplateMode()))
984             IFOKDO(err, operation2.save())
985 
986             // Create sub operation object
987             SKGSubOperationObject subOperation2;
988 
989             SKGObjectBase::SKGListSKGObjectBase subops;
990             IFOKDO(err, operation2.getSubOperations(subops))
991             IFOK(err) {
992                 if (!subops.isEmpty()) {
993                     subOperation2 = subops.at(0);
994                 } else {
995                     err = operation2.addSubOperation(subOperation2);
996                 }
997             }
998             IFOKDO(err, subOperation2.setQuantity(operationQuantity))
999             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 operation 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 an operation 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 operation '%1' has been updated", operationObj.getDisplayName()), SKGDocument::Hidden))
1091         }
1092 
1093         IFOKDO(err, getDocument()->stepForward(i + 1))
1094     }
1095 
1096     return err;
1097 }
1098 
onAddOperationClicked()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", "Operation 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 an operation 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 operation 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::POINTED))
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 operation 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 operation 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 operation 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 operation '%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", "Operation created"));
1267         ui.kOperationView->getView()->selectObject(operation.getUniqueID());
1268     } else {
1269         err.addError(ERR_FAIL, i18nc("Error message",  "Operation creation failed"));
1270     }
1271 
1272     // Display error
1273     SKGMainPanel::displayErrorMessage(err, true);
1274 
1275     // Set focus on date
1276     ui.kDateEdit->setFocus();
1277 }
1278 
getTableView()1279 SKGTreeView* SKGOperationPluginWidget::getTableView()
1280 {
1281     return ui.kOperationView->getView();
1282 }
1283 
getState()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 
setState(const QString & iState)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("operations"));
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("operations"));
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("operations")) && !parameters.contains(QStringLiteral("templates"))) {
1405         parameters.push_back(QStringLiteral("operations"));
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 
getDefaultStateAttribute()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 
fillTargetAccount()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 
dataModified(const QString & iTableName,int iIdTransaction,bool iLightTransaction)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 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 operations", "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                 QString iconName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/images/logo/" % listAccount.at(i).at(0));
1517                 if (iconName.isEmpty()) {
1518                     iconName = listAccount.at(i).at(0);
1519                 }
1520                 QIcon icon(iconName);
1521                 QString text = listAccount.at(i).at(1);
1522                 QString id = "##_" % text;
1523                 if (nbAccounts == 1) {
1524                     QStringList items = SKGServices::splitCSVLine(currentState);
1525                     if (items.isEmpty()) {
1526                         items.push_back(QStringLiteral("all"));
1527                     }
1528                     if (items[0] == QStringLiteral("all") || !items[0].startsWith(QLatin1String("##_"))) {
1529                         items[0] = id;
1530                     }
1531                     if (items.count() == 1) {
1532                         items.push_back(QStringLiteral("operations"));
1533                         items.push_back(QStringLiteral("hide"));
1534                     }
1535                     currentState = SKGServices::stringsToCsv(items);
1536                 }
1537                 ui.kAccountEdit->addItem(icon, text);
1538                 ui.kPaymentAccountEdit->addItem(icon, text);
1539 
1540                 if (!accountSeparatorAdded && listAccount.at(i).at(2) == QStringLiteral("N")) {
1541                     if (i > 1) {
1542                         showWidget->addSeparator();
1543                     }
1544                     accountSeparatorAdded = true;
1545                 }
1546 
1547                 QString account_id = listAccount.at(i).at(3);
1548                 showWidget->addGroupedItem(id, text, iconName, "rd_account_id=" + account_id, QStringLiteral("account"),
1549                                            (i < 10 ? QKeySequence::fromString("Meta+" % SKGServices::intToString(i)) : QKeySequence()));
1550 
1551                 SKGStringListList listLinkedAccounts;
1552                 getDocument()->executeSelectSqliteOrder(QStringLiteral("SELECT id FROM v_account_display where t_close='N' AND r_account_id=") + account_id, listLinkedAccounts);
1553                 int nbLinkedAccount = listLinkedAccounts.count();
1554                 if (nbLinkedAccount > 1) {
1555                     QString f = account_id;
1556                     for (int j = 1; j < nbLinkedAccount; ++j) {  // Ignore header
1557                         f += ",'" + listLinkedAccounts.at(j).at(0) + '\'';
1558                     }
1559 
1560                     showWidget->addGroupedItem(id + "_cc", i18nc("Information", "%1 + its credit cards", text), iconName, "rd_account_id IN(" + f + ')', QStringLiteral("account"),
1561                                                QKeySequence());
1562                 }
1563 
1564                 if (!uncheck.isEmpty()) {
1565                     uncheck = uncheck % ";";
1566                 }
1567                 uncheck = uncheck % id;
1568             }
1569 
1570             int nb = showWidget->count();
1571             for (int i = 1; i < nb; ++i) {
1572                 showWidget->setListIdToUncheckWhenChecked(i, uncheck % ";all");
1573             }
1574             if (nbAccounts > 1) {
1575                 showWidget->setListIdToUncheckWhenChecked(0, uncheck);
1576             }
1577 
1578             showWidget->addSeparator();
1579             showWidget->addGroupedItem(QStringLiteral("operations"), i18nc("Option to for display of operations", "Operations"), QStringLiteral("view-bank-account"), QStringLiteral("d_date!='0000-00-00' AND t_template='N'"),
1580                                        QStringLiteral("type"), Qt::META + Qt::Key_O);
1581             showWidget->addGroupedItem(QStringLiteral("templates"), i18nc("Option to for display of operations", "Templates"), QStringLiteral("edit-guides"), QStringLiteral("d_date!='0000-00-00' AND t_template='Y'"),
1582                                        QStringLiteral("type"), Qt::META + Qt::Key_T);
1583             showWidget->addSeparator();
1584             showWidget->addItem(QStringLiteral("hidepointed"), i18nc("Option to for display of operations", "Hide pointed operations"), QStringLiteral("dialog-ok"), QStringLiteral("t_status<>'P'"), QLatin1String(""), QLatin1String(""), QLatin1String(""), QLatin1String(""), Qt::META + Qt::Key_P);
1585             showWidget->addItem(QStringLiteral("hide"), i18nc("Option to for display of operations", "Hide checked operations"), QStringLiteral("dialog-ok"), QStringLiteral("t_status<>'Y'"), QLatin1String(""), QLatin1String(""), QLatin1String(""), QLatin1String(""), Qt::META + Qt::Key_C);
1586             showWidget->addSeparator();
1587             showWidget->addPeriodItem(QStringLiteral("period"));
1588             showWidget->setMode(SKGShow::AND);
1589             if (currentState.isEmpty()) {
1590                 showWidget->setDefaultState(QStringLiteral("all;operations;hide"));
1591             } else {
1592                 showWidget->setState(currentState);
1593             }
1594 
1595             if (!current.isEmpty()) {
1596                 ui.kAccountEdit->setText(current);
1597             }
1598 
1599             // Connect combo filter account
1600             connect(ui.kAccountEdit, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGOperationPluginWidget::fillTargetAccount, Qt::QueuedConnection);
1601             connect(showWidget, &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onAccountChanged, Qt::QueuedConnection);
1602             connect(showWidget, &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onRefreshInformationZoneDelayed, Qt::QueuedConnection);
1603             connect(showWidget, &SKGShow::stateChanged, this, &SKGOperationPluginWidget::onOperationCreatorModified, Qt::QueuedConnection);
1604 
1605             fillTargetAccount();
1606 
1607             if (nbAccounts == 0) {
1608                 ui.kTitle->setText(i18nc("Message", "First, you have to create an account."));
1609                 ui.kTitle->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-information")), KTitleWidget::ImageLeft);
1610                 ui.kTitle->show();
1611                 showWidget->hide();
1612             } else {
1613                 showWidget->show();
1614             }
1615         }
1616 
1617         if (iTableName == QStringLiteral("refund") || iTableName.isEmpty()) {
1618             SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kTrackerEdit, getDocument(), QStringLiteral("refund"), QStringLiteral("t_name"), QStringLiteral("t_close='N'"));
1619         }
1620         if (!iLightTransaction) {
1621             if (iTableName == QStringLiteral("category") || iTableName.isEmpty()) {
1622                 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kCategoryEdit, getDocument(), QStringLiteral("category"), QStringLiteral("t_fullname"), QStringLiteral("t_close='N'"));
1623             }
1624             if (iTableName == QStringLiteral("payee") || iTableName.isEmpty()) {
1625                 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kPayeeEdit, getDocument(), QStringLiteral("payee"), QStringLiteral("t_name"), QStringLiteral("t_close='N'"));
1626             }
1627             if (iTableName == QStringLiteral("operation") || iTableName.isEmpty()) {
1628                 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kTypeEdit, getDocument(), QStringLiteral("operation"), QStringLiteral("t_mode"), QLatin1String(""), true);
1629                 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kCommentEdit, getDocument(), QStringLiteral("v_operation_all_comment"), QStringLiteral("t_comment"), QLatin1String(""), true);
1630 
1631                 // Set type number
1632                 m_numberFieldIsNotUptodate = true;
1633 
1634                 // Correction 215658: vvv because the table is modified, the selection is modified
1635                 onSelectionChanged();
1636                 // Correction 215658: ^^^
1637 
1638                 onRefreshInformationZoneDelayed();
1639             }
1640         } else if (iTableName == QStringLiteral("operation") || iTableName.isEmpty()) {
1641             onRefreshInformationZoneDelayed();
1642         }
1643     }
1644 }
1645 
onDoubleClick()1646 void SKGOperationPluginWidget::onDoubleClick()
1647 {
1648     _SKGTRACEINFUNC(10)
1649 
1650     // Get selection
1651     SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects();
1652     if (selection.count() == 1) {
1653         SKGOperationObject op(selection.at(0));
1654 
1655         if (op.isTemplate() && selection.at(0).getRealTable() == QStringLiteral("operation")) {
1656             // this is a template, we have to create an operation
1657             SKGError err;
1658             SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Operation creation"), err)
1659             SKGOperationObject operation;
1660             err = op.duplicate(operation);
1661             if (skgoperation_settings::automaticPointInReconciliation() && m_modeInfoZone == 1) {
1662                 IFOKDO(err, operation.setStatus(SKGOperationObject::POINTED))
1663                 IFOKDO(err, operation.save())
1664             }
1665 
1666             // Send message
1667             IFOKDO(err, operation.getDocument()->sendMessage(i18nc("An information to the user that something was added", "The operation '%1' has been added", operation.getDisplayName()), SKGDocument::Hidden))
1668 
1669             // status bar
1670             IFOK(err) {
1671                 setTemplateMode(false);
1672                 err = SKGError(0, i18nc("Successful message after an user action", "Operation created"));
1673                 ui.kOperationView->getView()->selectObject(operation.getUniqueID());
1674             } else {
1675                 err.addError(ERR_FAIL, i18nc("Error message",  "Operation creation failed"));
1676             }
1677 
1678             // Display error
1679             SKGMainPanel::displayErrorMessage(err);
1680 
1681         } else {
1682             // This is not a template, we have to open it
1683             SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("open"))->trigger();
1684         }
1685     }
1686 }
1687 
onRefreshInformationZoneDelayed()1688 void SKGOperationPluginWidget::onRefreshInformationZoneDelayed()
1689 {
1690     m_timer.start(300);
1691 }
1692 
onRefreshInformationZone()1693 void SKGOperationPluginWidget::onRefreshInformationZone()
1694 {
1695     SKGTRACEINFUNC(1)
1696     ui.kReconciliateAccount->hide();
1697 
1698     auto* doc = qobject_cast<SKGDocumentBank*>(getDocument());
1699     QString current = currentAccount();
1700     if (doc != nullptr && SKGMainPanel::getMainPanel()) {
1701         if (m_modeInfoZone == 0) {
1702             // Refresh info area
1703             // Compute where clause
1704             QString filter = QStringLiteral("1=1");
1705             if (!current.isEmpty()) {
1706                 filter = "t_name='" % SKGServices::stringToSqlString(current) % '\'';
1707             }
1708             ui.kInfo->setText(i18nc("Message", "Computing..."));
1709             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) {
1710                 if (iResult.count() == 2 && SKGMainPanel::getMainPanel()->pageIndex(this) != -1) {
1711                     SKGServices::SKGUnitInfo unit1 = doc->getPrimaryUnit();
1712                     SKGServices::SKGUnitInfo unit2 = doc->getSecondaryUnit();
1713                     if (!current.isEmpty()) {
1714                         SKGAccountObject account(getDocument());
1715                         if (account.setName(current).isSucceeded()) {
1716                             if (account.load().isSucceeded()) {
1717                                 SKGUnitObject unitAccount;
1718                                 if (account.getUnit(unitAccount).isSucceeded()) {
1719                                     if (!unitAccount.getSymbol().isEmpty()) {
1720                                         unit1.Symbol = unitAccount.getSymbol();
1721                                         unit1.Value = SKGServices::stringToDouble(unitAccount.getAttribute(QStringLiteral("f_CURRENTAMOUNT")));
1722 
1723                                         if (unit1.Symbol != qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit().Symbol) {
1724                                             unit2 = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
1725                                         }
1726                                     }
1727                                 }
1728                             }
1729                         }
1730                     }
1731 
1732                     double v1 = SKGServices::stringToDouble(iResult.at(1).at(0));
1733                     double v2 = SKGServices::stringToDouble(iResult.at(1).at(1));
1734                     double v3 = SKGServices::stringToDouble(iResult.at(1).at(2));
1735                     double v4 = SKGServices::stringToDouble(iResult.at(1).at(3));
1736                     QString s1 = doc->formatMoney(v1, unit1);
1737                     QString s2 = doc->formatMoney(v2, unit1);
1738                     QString s3 = doc->formatMoney(v3, unit1);
1739                     QString s4 = doc->formatMoney(v4, unit1);
1740                     QString zero = doc->formatMoney(0, unit1);
1741                     ui.kInfo->setText(i18nc("Message", "Balance: %1     Checked: %2     To be Checked: %3", s1, s2, !current.isEmpty() && s4 != zero ? s3 % " + " % s4 : s3));
1742                     if (!unit2.Symbol.isEmpty() && (unit2.Value != 0.0)) {
1743                         s1 = doc->formatMoney(v1, unit2);
1744                         s2 = doc->formatMoney(v2, unit2);
1745                         s3 = doc->formatMoney(v3, unit2);
1746                         s4 = doc->formatMoney(v4, unit2);
1747                     }
1748                     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));
1749                 }
1750             });
1751         } else if (m_modeInfoZone == 1) {
1752             // Refresh reconciliation area
1753             SKGServices::SKGUnitInfo unit1 = doc->getPrimaryUnit();
1754             SKGServices::SKGUnitInfo unit2 = doc->getSecondaryUnit();
1755             // Compute where clause
1756             QString filter = '\'' % SKGServices::stringToSqlString(currentAccount()) % '\'';
1757             SKGStringListList listTmp;
1758             getDocument()->executeSelectSqliteOrder(
1759                 "SELECT ABS(TOTAL(f_CURRENTAMOUNT_EXPENSE)),TOTAL(f_CURRENTAMOUNT_INCOME) FROM v_operation_display WHERE t_status='P' AND t_ACCOUNT=" % filter,
1760                 listTmp);
1761             if (listTmp.count() == 2) {
1762                 SKGAccountObject::AccountType accountType = SKGAccountObject::OTHER;
1763                 if (!current.isEmpty()) {
1764                     SKGAccountObject account(getDocument());
1765                     if (account.setName(current).isSucceeded()) {
1766                         if (account.load().isSucceeded()) {
1767                             accountType = account.getType();
1768 
1769                             SKGUnitObject unitAccount;
1770                             if (account.getUnit(unitAccount).isSucceeded()) {
1771                                 if (!unitAccount.getSymbol().isEmpty()) {
1772                                     unit1.Symbol = unitAccount.getSymbol();
1773                                     unit1.Value = SKGServices::stringToDouble(unitAccount.getAttribute(QStringLiteral("f_CURRENTAMOUNT")));
1774 
1775                                     if (unit1.Symbol != qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit().Symbol) {
1776                                         unit2 = qobject_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
1777                                     }
1778                                 }
1779                             }
1780                         }
1781                     }
1782                 }
1783                 if (accountType == SKGAccountObject::SKGAccountObject::CREDITCARD) {
1784                     ui.kReconciliationTitle->setText(i18nc("A title", "Total amount:"));
1785                     ui.kReconciliateAccount->show();
1786                 } else {
1787                     ui.kReconciliationTitle->setText(i18nc("A title", "Final balance:"));
1788                 }
1789 
1790                 ui.kAutoPoint->setVisible(accountType != SKGAccountObject::WALLET);
1791 
1792                 SKGStringListList listTmp2;
1793                 double diff = 0;
1794                 getDocument()->executeSelectSqliteOrder(
1795                     "SELECT TOTAL(f_CHECKEDANDPOINTED) from v_account_display WHERE t_name=" % filter,
1796                     listTmp2);
1797                 if (listTmp2.count() == 2) {
1798                     diff = SKGServices::stringToDouble(listTmp2.at(1).at(0)) - ui.kReconcilitorAmountEdit->value() * unit1.Value;
1799                 }
1800 
1801                 // Set tooltip
1802                 if (current.isEmpty()) {
1803                     ui.kReconciliatorInfo->setText(i18nc("Description", "You must select only one account to use reconciliation."));
1804                     ui.kReconciliatorInfo->setToolTip(ui.kReconciliatorInfo->text());
1805                 } else {
1806                     double v1 = SKGServices::stringToDouble(listTmp.at(1).at(0));
1807                     double v2 = SKGServices::stringToDouble(listTmp.at(1).at(1));
1808                     QString sdiff = doc->formatMoney(diff, unit1);
1809                     QString s1 = doc->formatMoney(v1, unit1);
1810                     QString s2 = doc->formatMoney(v2, unit1);
1811                     ui.kReconciliatorInfo->setText(i18nc("Message", "%1 - Delta: %2     Expenditure: %3     Income: %4", unit1.Symbol, sdiff, s1, s2));
1812 
1813                     // Comparison
1814                     QString zero = doc->formatMoney(0, unit1);
1815                     QString negativezero = doc->formatMoney(-EPSILON, unit1);
1816                     ui.kValidate->setVisible(sdiff == zero || sdiff == negativezero);
1817                     ui.kCreateFakeOperation->setVisible(!ui.kValidate->isVisible());
1818                     ui.kAutoPoint->setVisible(!ui.kValidate->isVisible());
1819 
1820                     if (!unit2.Symbol.isEmpty() && (unit2.Value != 0.0)) {
1821                         sdiff = doc->formatMoney(diff, unit2);
1822                         s1 = doc->formatMoney(v1, unit2);
1823                         s2 = doc->formatMoney(v2, unit2);
1824                     }
1825                     ui.kReconciliatorInfo->setToolTip(i18nc("Message", "<p>Delta: %1</p><p>Expenditure: %2</p><p>Income: %3</p>", sdiff, s1, s2));
1826                 }
1827             }
1828         }
1829     }
1830 }
1831 
onAccountChanged()1832 void SKGOperationPluginWidget::onAccountChanged()
1833 {
1834     SKGTRACEINFUNC(1)
1835     if (!currentAccount().isEmpty() && ui.kOperationView->getView()->getNbSelectedObjects() == 0) {
1836         // Get account object
1837         SKGAccountObject act(getDocument());
1838         SKGError err = act.setName(currentAccount());
1839         IFOKDO(err, act.load())
1840 
1841         // Get unit
1842         SKGUnitObject unit;
1843         IFOKDO(err, act.getUnit(unit))
1844         if (!err && !unit.getSymbol().isEmpty()) {
1845             ui.kUnitEdit->setText(unit.getSymbol());
1846         }
1847     }
1848     onFilterChanged();
1849 }
1850 
onFilterChanged()1851 void SKGOperationPluginWidget::onFilterChanged()
1852 {
1853     SKGTRACEINFUNC(1)
1854     if (!isEnabled()) {
1855         return;
1856     }
1857     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1858 
1859     // Enable/ disable widgets
1860     bool onOneAccount = (!currentAccount().isEmpty());
1861     ui.kReconciliatorFrame2->setEnabled(onOneAccount);
1862     if (!onOneAccount && m_modeInfoZone == 1) {
1863         ui.kReconciliatorFrame2->hide();
1864         ui.kInfo->show();
1865         m_modeInfoZone = 0;
1866     }
1867 
1868     QString current = currentAccount();
1869     if (!current.isEmpty() && ui.kOperationView->getView()->getNbSelectedObjects() == 0) {
1870         ui.kAccountEdit->setText(current);
1871     }
1872 
1873     QApplication::restoreOverrideCursor();
1874 }
1875 
fillNumber()1876 void SKGOperationPluginWidget::fillNumber()
1877 {
1878     SKGTRACEINFUNC(10)
1879     QStringList list;
1880     QString account = ui.kAccountEdit->text();
1881     QString wc;
1882     if (!account.isEmpty()) {
1883         wc = "rd_account_id IN (SELECT id FROM account WHERE t_name='" + SKGServices::stringToSqlString(account) + "')";
1884     }
1885     getDocument()->getDistinctValues(QStringLiteral("v_operation_next_numbers"), QStringLiteral("t_number"), wc, list);
1886 
1887     // Fill completion
1888     auto comp = new QCompleter(list);
1889     comp->setFilterMode(Qt::MatchContains);
1890     ui.kNumberEdit->setCompleter(comp);
1891 
1892     m_numberFieldIsNotUptodate = false;
1893 }
1894 
onFocusChanged()1895 void SKGOperationPluginWidget::onFocusChanged()
1896 {
1897     _SKGTRACEINFUNC(10)
1898     if (!qApp->closingDown()) {
1899         if ((SKGMainPanel::getMainPanel() != nullptr) && SKGMainPanel::getMainPanel()->currentPage() == this) {
1900             if (m_numberFieldIsNotUptodate && ui.kNumberEdit->hasFocus()) {
1901                 fillNumber();
1902             }
1903 
1904             bool test = ui.kTypeEdit->hasFocus() ||
1905                         //                  ui.kAmountEdit->hasFocus() ||
1906                         //              ui.kNumberEdit->hasFocus() ||
1907                         ui.kUnitEdit->hasFocus() ||
1908                         ui.kCategoryEdit->hasFocus() ||
1909                         ui.kTrackerEdit->hasFocus() ||
1910                         ui.kCommentEdit->hasFocus() ||
1911                         ui.kPayeeEdit->hasFocus();
1912             if (m_fastEditionAction != nullptr) {
1913                 m_fastEditionAction->setEnabled(test);
1914             }
1915         }
1916     }
1917 }
1918 
onFastEdition()1919 void SKGOperationPluginWidget::onFastEdition()
1920 {
1921     if (SKGMainPanel::getMainPanel()->currentPage() != this) {
1922         return;
1923     }
1924     SKGTRACEINFUNC(10)
1925     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1926     SKGError err;
1927 
1928     // Get widget item
1929     QWidget* w = QApplication::focusWidget();
1930     auto* cmb = qobject_cast<SKGComboBox*>(w);
1931     if (cmb != nullptr) {
1932         setWidgetEditionEnabled(cmb->lineEdit(), false);
1933     } else {
1934         setWidgetEditionEnabled(w, false);
1935     }
1936 
1937     // Build the where clause
1938     QString wc;
1939     if (ui.kTypeEdit->hasFocus()) {
1940         wc = "t_mode LIKE '" % SKGServices::stringToSqlString(ui.kTypeEdit->text()) % "%'";
1941     } else if (ui.kUnitEdit->hasFocus()) {
1942         wc = "t_UNIT LIKE '" % SKGServices::stringToSqlString(ui.kUnitEdit->text()) % "%'";
1943     } else if (ui.kCategoryEdit->hasFocus()) {
1944         wc = "t_CATEGORY LIKE '" % SKGServices::stringToSqlString(ui.kCategoryEdit->text()) % "%'";
1945     } else if (ui.kTrackerEdit->hasFocus()) {
1946         wc = "t_REFUND LIKE '" % SKGServices::stringToSqlString(ui.kTrackerEdit->text()) % "%'";
1947     } else if (ui.kCommentEdit->hasFocus()) {
1948         wc = "t_comment LIKE '" % SKGServices::stringToSqlString(ui.kCommentEdit->text()) % "%'";
1949     } else if (ui.kPayeeEdit->hasFocus()) {
1950         wc = "t_PAYEE LIKE '" % SKGServices::stringToSqlString(ui.kPayeeEdit->text()) % "%'";
1951     }
1952 
1953     if (!wc.isEmpty()) {
1954         QString accountName = ui.kAccountEdit->currentText();
1955         if (!accountName.isEmpty() && skgoperation_settings::oncurrentaccountonly()) {
1956             wc += " AND t_ACCOUNT LIKE '" % SKGServices::stringToSqlString(accountName) % "%'";
1957         }
1958 
1959         // Read Setting
1960         QString fasteditmode = skgoperation_settings::fasteditmode();
1961 
1962         /*
1963         0-Search in templates only
1964         1-Search first in templates and after in operations
1965         2-Search in operations only
1966         3-Search first in operations and after in templates
1967         */
1968         if (fasteditmode == QStringLiteral("0")) {
1969             wc += QStringLiteral(" AND t_template='Y'");
1970         } else if (fasteditmode == QStringLiteral("2")) {
1971             wc += QStringLiteral(" AND t_template='N'");
1972         }
1973 
1974         if (wc != m_lastFastEditionWhereClause) {
1975             m_lastFastEditionWhereClause = wc;
1976             m_lastFastEditionOperationFound = 0;
1977         }
1978 
1979         // Look for last operation
1980         if (m_lastFastEditionOperationFound != 0) {
1981             wc += " AND id<" % SKGServices::intToString(m_lastFastEditionOperationFound);
1982         }
1983 
1984         // Add order by
1985         wc += QStringLiteral(" ORDER BY ");
1986         if (fasteditmode == QStringLiteral("1")) {
1987             wc += QStringLiteral(" t_template DESC, ");
1988         } else if (fasteditmode == QStringLiteral("3")) {
1989             wc += QStringLiteral(" t_template ASC, ");
1990         }
1991         wc += QStringLiteral("d_date DESC, id DESC LIMIT 1");
1992 
1993         SKGObjectBase::SKGListSKGObjectBase operations;
1994         err = getDocument()->getObjects(QStringLiteral("v_operation_display_all"), wc, operations);
1995         if (!err && !operations.isEmpty()) {
1996             SKGOperationObject op(operations.at(0));
1997 
1998             m_lastFastEditionOperationFound = op.getID();
1999             if (isWidgetEditionEnabled(ui.kTypeEdit->lineEdit())) {
2000                 ui.kTypeEdit->setText(op.getMode());
2001             }
2002             if (isWidgetEditionEnabled(ui.kUnitEdit->lineEdit())) {
2003                 ui.kUnitEdit->setText(op.getAttribute(QStringLiteral("t_UNIT")));
2004             }
2005             if (isWidgetEditionEnabled(ui.kCategoryEdit->lineEdit())) {
2006                 ui.kCategoryEdit->setText(op.getAttribute(QStringLiteral("t_CATEGORY")));
2007             }
2008             if (isWidgetEditionEnabled(ui.kCommentEdit->lineEdit())) {
2009                 ui.kCommentEdit->setText(op.getComment());
2010             }
2011             if (isWidgetEditionEnabled(ui.kPayeeEdit->lineEdit())) {
2012                 ui.kPayeeEdit->setText(op.getAttribute(QStringLiteral("t_PAYEE")));
2013             }
2014             if (isWidgetEditionEnabled(ui.kTrackerEdit->lineEdit())) {
2015                 ui.kTrackerEdit->setText(op.getAttribute(QStringLiteral("t_REFUND")));
2016             }
2017             if (currentAccount().isEmpty()) {
2018                 ui.kAccountEdit->setText(op.getAttribute(QStringLiteral("t_ACCOUNT")));
2019             }
2020             if (isWidgetEditionEnabled(ui.kAmountEdit)) {
2021                 QString quantity = op.getAttribute(QStringLiteral("f_QUANTITY"));
2022 
2023                 double quantityVal = SKGServices::stringToDouble(quantity);
2024                 SKGUnitObject unitObject = ui.kUnitEdit->getUnit();
2025                 int nbDec = unitObject.getNumberDecimal();
2026                 if (nbDec == 0) {
2027                     nbDec = 2;
2028                 }
2029                 quantity = SKGServices::toCurrencyString(qAbs(quantityVal), QLatin1String(""), nbDec);
2030                 if (quantity.startsWith(QLocale().positiveSign())) {
2031                     quantity = quantity.right(quantity.length() - 1);
2032                 }
2033                 if (quantityVal > 0) {
2034                     quantity = '+' % quantity;
2035                 } else {
2036                     quantity = '-' % quantity;
2037                 }
2038                 ui.kAmountEdit->setText(quantity);
2039             }
2040 
2041             // set next number
2042             if (isWidgetEditionEnabled(ui.kNumberEdit)) {
2043                 int number = SKGServices::stringToInt(op.getNumber());
2044                 if (number == 0) {
2045                     ui.kNumberEdit->setText(QLatin1String(""));
2046                 } else {
2047                     if (m_numberFieldIsNotUptodate) {
2048                         fillNumber();
2049                     }
2050 
2051                     QCompleter* comp = ui.kNumberEdit->completer();
2052                     if (comp != nullptr) {
2053                         QStringList list = qobject_cast<QStringListModel*>(comp->model())->stringList();
2054                         int nb = list.count();
2055                         int cpt = 0;
2056                         while (nb >= 0 && cpt >= 0 && cpt < 1000) {
2057                             ++number;
2058 
2059                             if (list.contains(SKGServices::intToString(number))) {
2060                                 cpt = -2;
2061                             }
2062                             ++cpt;
2063                         }
2064 
2065                         if (cpt < 0) {
2066                             ui.kNumberEdit->setText(SKGServices::intToString(number));
2067                         }
2068                     }
2069                 }
2070             }
2071 
2072             // Get nb operation linked
2073             SKGObjectBase::SKGListSKGObjectBase groupedOperations;
2074             op.getGroupedOperations(groupedOperations);
2075             int nbGroupedOp = groupedOperations.count();
2076 
2077             // Get nb sub op
2078             SKGObjectBase::SKGListSKGObjectBase subOperations;
2079             op.getSubOperations(subOperations);
2080             int nbSupOp = subOperations.count();
2081 
2082             if (nbSupOp > 1) {
2083                 // It is a SPLIT operation
2084                 ui.kWidgetSelector->setSelectedMode(1);
2085                 QDate d = ui.kDateEdit->date();
2086                 if (!d.isValid()) {
2087                     d = QDate::currentDate();
2088                 }
2089                 displaySubOperations(op, false, d);
2090 
2091             } else {
2092                 if (nbGroupedOp > 1) {
2093                     // It is a TRANSFER
2094                     SKGOperationObject op2(groupedOperations.at(0));
2095                     if (op2 == op) {
2096                         op2 = groupedOperations.at(1);
2097                     }
2098 
2099                     SKGAccountObject targetAccount;
2100                     op2.getParentAccount(targetAccount);
2101 
2102                     if (isWidgetEditionEnabled(ui.kTargetAccountEdit)) {
2103                         ui.kTargetAccountEdit->setText(targetAccount.getName());
2104                     }
2105                 } else {
2106                     ui.kWidgetSelector->setSelectedMode(0);
2107                 }
2108             }
2109 
2110         } else {
2111             m_lastFastEditionWhereClause = QLatin1String("");
2112             m_lastFastEditionOperationFound = 0;
2113         }
2114     }
2115 
2116     if (w != nullptr) {
2117         w->setFocus(Qt::OtherFocusReason);
2118     }
2119     QApplication::restoreOverrideCursor();
2120 
2121     // Display error
2122     SKGMainPanel::displayErrorMessage(err);
2123 }
2124 
isTemplateMode()2125 bool SKGOperationPluginWidget::isTemplateMode()
2126 {
2127     QAction* act = ui.kOperationView->getShowWidget()->getAction(QStringLiteral("templates"));
2128     return ((act != nullptr) && act->isChecked());
2129 }
2130 
setTemplateMode(bool iTemplate)2131 void SKGOperationPluginWidget::setTemplateMode(bool iTemplate)
2132 {
2133     SKGTRACEINFUNC(10)
2134 
2135     if (iTemplate != isTemplateMode()) {
2136         QAction* act = ui.kOperationView->getShowWidget()->getAction(QStringLiteral("templates"));
2137         if (act != nullptr) {
2138             act->setChecked(iTemplate);
2139         }
2140 
2141         act = ui.kOperationView->getShowWidget()->getAction(QStringLiteral("operations"));
2142         if (act != nullptr) {
2143             act->setChecked(!iTemplate);
2144         }
2145     }
2146 }
2147 
onBtnModeClicked(int mode)2148 void SKGOperationPluginWidget::onBtnModeClicked(int mode)
2149 {
2150     SKGTRACEINFUNC(10)
2151     if (mode != 1 && mode != -1) {
2152         ui.kSubOperationsTable->setRowCount(0);
2153         ui.kSubOperationsTable->clearContents();
2154     }
2155 
2156     if (mode == 1) {
2157         if (ui.kSubOperationsTable->rowCount() == 0) {
2158             addSubOperationLine(0, ui.kDateEdit->date(), ui.kCategoryEdit->text(), ui.kTrackerEdit->text(), ui.kCommentEdit->text(), ui.kAmountEdit->value(), nullptr);
2159         }
2160     }
2161     onOperationCreatorModified();
2162 }
2163 
displaySubOperations(const SKGOperationObject & iOperation,bool iKeepId,QDate iSubOperationsDate)2164 void SKGOperationPluginWidget::displaySubOperations(const SKGOperationObject& iOperation, bool iKeepId, QDate iSubOperationsDate)
2165 {
2166     SKGTRACEINFUNC(10)
2167     ui.kSubOperationsTable->setRowCount(0);
2168     ui.kSubOperationsTable->clearContents();
2169 
2170     int nbSubOperations = 0;
2171 
2172     SKGObjectBase::SKGListSKGObjectBase subOperations;
2173     SKGError err =  iOperation.getSubOperations(subOperations);
2174     nbSubOperations = subOperations.count();
2175     for (int i = 0; i < nbSubOperations; ++i) {
2176         SKGSubOperationObject subOperation(subOperations.at(i));
2177 
2178         SKGCategoryObject category;
2179         subOperation.getCategory(category);
2180 
2181         SKGTrackerObject tracker;
2182         subOperation.getTracker(tracker);
2183 
2184         addSubOperationLine(i, iSubOperationsDate.isValid() ? iSubOperationsDate : subOperation.getDate(), category.getFullName(), tracker.getName(),
2185                             subOperation.getComment(), subOperation.getQuantity(), subOperation.getFormula(),
2186                             (iKeepId ? subOperation.getID() : 0));
2187     }
2188 
2189     onQuantityChanged();
2190 }
2191 
displaySubOperations()2192 void SKGOperationPluginWidget::displaySubOperations()
2193 {
2194     SKGTRACEINFUNC(10)
2195     SKGOperationObject operation;
2196     if (getSelectedOperation(operation).isSucceeded()) {
2197         displaySubOperations(operation);
2198     }
2199 }
2200 
getRemainingQuantity()2201 double SKGOperationPluginWidget::getRemainingQuantity()
2202 {
2203     SKGTRACEINFUNC(10)
2204     double sumQuantities = 0;
2205     int nbSubOperations = ui.kSubOperationsTable->rowCount();
2206 
2207     for (int i = 0; i < nbSubOperations ; ++i) {
2208         QTableWidgetItem* quantityItem = ui.kSubOperationsTable->item(i, m_attributesForSplit.indexOf(QStringLiteral("f_value")));
2209         if (quantityItem != nullptr) {
2210             sumQuantities = sumQuantities + quantityItem->data(101).toDouble();
2211         }
2212     }
2213 
2214     return ui.kAmountEdit->value() - sumQuantities;
2215 }
2216 
onDateChanged(QDate iDate)2217 void SKGOperationPluginWidget::onDateChanged(QDate iDate)
2218 {
2219     SKGTRACEINFUNC(10)
2220     bool previous = ui.kSubOperationsTable->blockSignals(true);  // Disable signals so that filling cell doesn't create new lines
2221     if (sender() == ui.kDateEdit && iDate.isValid() && m_previousDate.isValid()) {
2222         // Refresh dates
2223         int nbSubOperations = ui.kSubOperationsTable->rowCount();
2224         for (int i = 0; i < nbSubOperations ; ++i) {
2225             QTableWidgetItem* dateItem = ui.kSubOperationsTable->item(i, m_attributesForSplit.indexOf(QStringLiteral("d_date")));
2226             if (dateItem != nullptr) {
2227                 auto datestring = dateItem->toolTip();
2228                 QDate previousSubDate = SKGServices::stringToTime(datestring).date();
2229                 if (previousSubDate.isValid()) {
2230                     int delta = m_previousDate.daysTo(iDate);
2231 
2232                     auto newDate = previousSubDate.addDays(delta);
2233                     dateItem->setText(SKGMainPanel::dateToString(newDate));
2234                     dateItem->setToolTip(SKGServices::dateToSqlString(newDate));
2235                 }
2236             }
2237         }
2238     }
2239     m_previousDate = iDate;
2240     ui.kSubOperationsTable->blockSignals(previous);    // Reenable signals
2241 }
2242 
refreshSubOperationAmount()2243 void SKGOperationPluginWidget::refreshSubOperationAmount()
2244 {
2245     SKGTRACEINFUNC(10)
2246     bool previous = ui.kSubOperationsTable->blockSignals(true);  // Disable signals so that filling cell doesn't create new lines
2247 
2248     int nbSubOperations = ui.kSubOperationsTable->rowCount();
2249 
2250     // Refresh computed amounts
2251     auto unit = ui.kUnitEdit->getUnit().getUnitInfo();
2252     unit.Value = 1.0;
2253     for (int i = 0; i < nbSubOperations ; ++i) {
2254         QTableWidgetItem* quantityItem = ui.kSubOperationsTable->item(i, m_attributesForSplit.indexOf(QStringLiteral("f_value")));
2255         if (quantityItem != nullptr) {
2256             QString formula = quantityItem->toolTip();
2257             if (formula.startsWith(QLatin1String("="))) {
2258                 formula = formula.right(formula.length() - 1);
2259                 formula.replace(',', '.');  // Replace comma by a point in case of typo
2260                 formula.remove(' ');
2261                 formula.replace(QStringLiteral("total"), SKGServices::doubleToString(ui.kAmountEdit->value()));
2262 
2263                 QScriptEngine myEngine;
2264                 QScriptValue result = myEngine.evaluate(formula);
2265                 if (result.isNumber()) {
2266                     auto value = result.toNumber();
2267                     quantityItem->setText(getDocument()->formatMoney(value, unit, false));
2268                     quantityItem->setData(101, value);
2269                 }
2270             } else {
2271                 auto value = quantityItem->data(101).toDouble();
2272                 quantityItem->setText(getDocument()->formatMoney(value, unit, false));
2273             }
2274         }
2275     }
2276     ui.kSubOperationsTable->blockSignals(previous);    // Reenable signals
2277 }
2278 
onQuantityChanged()2279 void SKGOperationPluginWidget::onQuantityChanged()
2280 {
2281     SKGTRACEINFUNC(10)
2282     int nbSubOperations = ui.kSubOperationsTable->rowCount();
2283 
2284     bool previous = ui.kSubOperationsTable->blockSignals(true);  // Disable signals so that filling cell doesn't create new lines
2285     if (sender() == ui.kAmountEdit) {
2286         // Update the total amount
2287         m_tableDelegate->addParameterValue(QStringLiteral("total"), ui.kAmountEdit->value());
2288 
2289         // Refresh computed amounts
2290         refreshSubOperationAmount();
2291     }
2292 
2293     // This code put the remaining quantity on the all sub operations with the same ratios ^^^
2294     // Specific code for the last one to avoid "round" error
2295     QTableWidgetItem* remainingQuantityItem = ui.kSubOperationsTable->item(nbSubOperations - 1, m_attributesForSplit.indexOf(QStringLiteral("f_value")));
2296     if (remainingQuantityItem != nullptr) {
2297         // 348490 vvv
2298         double remain = remainingQuantityItem->data(101).toDouble() + getRemainingQuantity();
2299         if (qAbs(remain) < 1e-10) {
2300             onRemoveSubOperation(nbSubOperations - 1);
2301         } else {
2302             auto unit = ui.kUnitEdit->getUnit().getUnitInfo();
2303             unit.Value = 1.0;
2304             remainingQuantityItem->setText(getDocument()->formatMoney(remain, unit, false));
2305             remainingQuantityItem->setData(101, remain);
2306             remainingQuantityItem->setToolTip(SKGServices::doubleToString(remain));
2307         }
2308         // 348490 ^^^
2309     }
2310     ui.kSubOperationsTable->blockSignals(previous);    // Reenable signals
2311 }
2312 
onSubopCellChanged(int row,int column)2313 void SKGOperationPluginWidget::onSubopCellChanged(int row, int column)
2314 {
2315     SKGTRACEINFUNC(10)
2316     QTableWidgetItem* subop_cell = ui.kSubOperationsTable->item(row, column);
2317     QBrush base_brush = ui.kSubOperationsTable->item(row, 0)->foreground();
2318 
2319     if (column == m_attributesForSplit.indexOf(QStringLiteral("f_value"))) {
2320         // If the quantity in the last line is edited, we add a new
2321         // line with the new remaining quantity
2322         addSubOperationLine(ui.kSubOperationsTable->rowCount(), ui.kDateEdit->date(), QLatin1String(""),
2323                             QLatin1String(""), QLatin1String(""), 0, QLatin1String(""));
2324 
2325         if (subop_cell->data(101).toDouble() != 0) {
2326             onQuantityChanged();
2327         } else {
2328             base_brush = KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText);
2329         }
2330         subop_cell->setForeground(base_brush);
2331 
2332         refreshSubOperationAmount();
2333     }
2334 }
2335 
onRemoveSubOperation(int iRow)2336 void SKGOperationPluginWidget::onRemoveSubOperation(int iRow)
2337 {
2338     SKGTRACEINFUNC(10)
2339     bool previous = ui.kSubOperationsTable->blockSignals(true);
2340     ui.kSubOperationsTable->removeRow(iRow);
2341 
2342     // If all rows removed, add an empty line
2343     if (ui.kSubOperationsTable->rowCount() == 0) {
2344         addSubOperationLine(0, ui.kDateEdit->date(), QLatin1String(""), QLatin1String(""), QLatin1String(""), 0, QLatin1String(""));
2345     }
2346 
2347     if (!previous) {
2348         onQuantityChanged();
2349     }
2350     ui.kSubOperationsTable->blockSignals(previous);
2351 }
2352 
onRotateAccountTools()2353 void SKGOperationPluginWidget::onRotateAccountTools()
2354 {
2355     SKGTRACEINFUNC(10)
2356     if (m_modeInfoZone == 0) {
2357         displayReconciliationInfo();
2358     } else {
2359         displayBalance();
2360     }
2361 }
2362 
2363 
displayBalance()2364 void SKGOperationPluginWidget::displayBalance()
2365 {
2366     if (m_modeInfoZone != 0) {
2367         ui.kReconciliatorFrame2->hide();
2368         ui.kInfo->show();
2369         m_modeInfoZone = 0;
2370         onRefreshInformationZoneDelayed();
2371     }
2372 }
2373 
displayReconciliationInfo()2374 void SKGOperationPluginWidget::displayReconciliationInfo()
2375 {
2376     if (!currentAccount().isEmpty()) {
2377         // Only show reconciliation info if only one account is displayed
2378         ui.kReconciliatorFrame2->show();
2379         ui.kInfo->hide();
2380         m_modeInfoZone = 1;
2381         onRefreshInformationZoneDelayed();
2382     } else {
2383         // If more than one account is displayed, skip reconciliation mode
2384         // (it doesn't make sense to reconcile several accounts at once)
2385         // and move to the next modeInfoZone
2386         m_modeInfoZone = 1;
2387         onRotateAccountTools();
2388     }
2389 }
2390 
onAutoPoint()2391 void SKGOperationPluginWidget::onAutoPoint()
2392 {
2393     SKGError err;
2394     SKGTRACEINFUNCRC(10, err)
2395 
2396     {
2397         SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Auto point account"), err)
2398         SKGAccountObject act(getDocument());
2399         err = act.setName(currentAccount());
2400         IFOKDO(err, act.load())
2401         IFOKDO(err, act.autoReconcile(ui.kReconcilitorAmountEdit->value()))
2402 
2403         // Send message
2404         IFOKDO(err, act.getDocument()->sendMessage(i18nc("An information message", "The account '%1' has been auto pointed", act.getDisplayName()), SKGDocument::Hidden))
2405     }
2406     // status bar
2407     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Account auto pointed.")))
2408 
2409     // Display error
2410     SKGMainPanel::displayErrorMessage(err);
2411 }
2412 
onAddFakeOperation()2413 void SKGOperationPluginWidget::onAddFakeOperation()
2414 {
2415     SKGError err;
2416     SKGTRACEINFUNCRC(10, err) {
2417         SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Create fake operation"), err)
2418 
2419         SKGAccountObject accountObj(getDocument());
2420         IFOKDO(err, accountObj.setName(currentAccount()))
2421         IFOKDO(err, accountObj.load())
2422 
2423         SKGOperationObject op;
2424         IFOKDO(err, accountObj.addOperation(op))
2425         IFOKDO(err, op.setDate(QDate::currentDate()))
2426         IFOKDO(err, op.setComment(skgoperation_settings::commentFakeOperation()))
2427         QString payee = skgoperation_settings::payeeFakeOperation();
2428         if (!payee.isEmpty()) {
2429             SKGPayeeObject p;
2430             IFOKDO(err, SKGPayeeObject::createPayee(qobject_cast<SKGDocumentBank*>(getDocument()), payee, p, true))
2431             IFOKDO(err, op.setPayee(p))
2432         }
2433 
2434         SKGUnitObject unit;
2435         IFOKDO(err, accountObj.getUnit(unit))
2436         IFOKDO(err, op.setUnit(unit))
2437         if (skgoperation_settings::automaticPointInReconciliation() && m_modeInfoZone == 1) {
2438             IFOKDO(err, op.setStatus(SKGOperationObject::POINTED))
2439         }
2440         IFOKDO(err, op.save())
2441 
2442         SKGSubOperationObject sop;
2443         IFOKDO(err, op.addSubOperation(sop))
2444 
2445         SKGStringListList listTmp2;
2446         double diff = 0;
2447         getDocument()->executeSelectSqliteOrder(
2448             "SELECT f_CHECKEDANDPOINTED from v_account_display WHERE t_name='" % SKGServices::stringToSqlString(currentAccount()) % '\'',
2449             listTmp2);
2450         if (listTmp2.count() == 2) {
2451             diff = SKGServices::stringToDouble(listTmp2.at(1).at(0)) / unit.getAmount() - ui.kReconcilitorAmountEdit->value();
2452         }
2453 
2454         IFOKDO(err, sop.setQuantity(-diff))
2455         IFOKDO(err, sop.setComment(skgoperation_settings::commentFakeOperation()))
2456         QString category = skgoperation_settings::categoryFakeOperation();
2457         if (!category.isEmpty()) {
2458             SKGCategoryObject c;
2459             IFOKDO(err, SKGCategoryObject::createPathCategory(qobject_cast<SKGDocumentBank*>(getDocument()), category, c, true))
2460             IFOKDO(err, sop.setCategory(c))
2461         }
2462         IFOKDO(err, sop.save())
2463 
2464         // Send message
2465         IFOKDO(err, op.getDocument()->sendMessage(i18nc("An information to the user that something was added", "The operation '%1' has been added", op.getDisplayName()), SKGDocument::Hidden))
2466     }
2467 
2468     // status bar
2469     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Fake operation created.")))
2470     else {
2471         err.addError(ERR_FAIL, i18nc("Error message",  "Creation failed"));
2472     }
2473 
2474     // Display error
2475     SKGMainPanel::displayErrorMessage(err);
2476 }
2477 
2478 
onValidatePointedOperations()2479 void SKGOperationPluginWidget::onValidatePointedOperations()
2480 {
2481     SKGError err;
2482     SKGTRACEINFUNCRC(10, err)
2483 
2484     QString account = currentAccount();
2485     if (!account.isEmpty()) {
2486         // Get reconciled account
2487         SKGAccountObject act(getDocument());
2488         IFOKDO(err, act.setName(account))
2489         IFOKDO(err, act.load())
2490 
2491         QString bindAccount = ui.kReconciliateAccount->currentText();
2492 
2493         if (act.getType() == SKGAccountObject::CREDITCARD && !bindAccount.isEmpty()) {
2494             //
2495             IFOK(err) {
2496                 SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Switch to checked"), err, 3)
2497                 SKGAccountObject accountObj2(getDocument());
2498                 IFOKDO(err, accountObj2.setName(bindAccount))
2499                 IFOKDO(err, accountObj2.load())
2500                 IFOKDO(err, getDocument()->stepForward(1))
2501 
2502                 IFOKDO(err, act.transferDeferredOperations(accountObj2))
2503                 IFOKDO(err, getDocument()->stepForward(2))
2504 
2505                 // Change reconciliation date of the account
2506                 IFOKDO(err, act.setReconciliationDate(QDate::currentDate()))
2507                 IFOKDO(err, act.setReconciliationBalance(ui.kReconcilitorAmountEdit->value()))
2508                 IFOKDO(err, act.setLinkedAccount(accountObj2))
2509                 IFOKDO(err, act.save())
2510 
2511                 // Send message
2512                 IFOKDO(err, act.getDocument()->sendMessage(i18nc("An information message", "The account '%1' has been reconciled", act.getDisplayName()), SKGDocument::Positive))
2513 
2514                 IFOKDO(err, getDocument()->stepForward(3))
2515             }
2516         } else {
2517             // Change state of all operations
2518             SKGObjectBase::SKGListSKGObjectBase list;
2519             IFOKDO(err, getDocument()->getObjects(QStringLiteral("v_operation_display"), "t_status='P' AND t_ACCOUNT='" % SKGServices::stringToSqlString(account) % '\'', list))
2520             int nb = list.count();
2521             IFOK(err) {
2522                 SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Switch to checked"), err, nb + 1)
2523                 for (int i = 0; !err && i < nb; ++i) {
2524                     // Set operation checked
2525                     SKGOperationObject op(list.at(i));
2526                     err = op.setStatus(SKGOperationObject::CHECKED);
2527                     IFOKDO(err, op.save())
2528 
2529                     // Send message
2530                     IFOKDO(err, op.getDocument()->sendMessage(i18nc("An information message", "The operation '%1' has been checked", op.getDisplayName()), SKGDocument::Hidden))
2531 
2532                     IFOKDO(err, getDocument()->stepForward(i + 1))
2533                 }
2534 
2535                 // Change reconciliation date of the account
2536                 IFOKDO(err, act.setReconciliationDate(QDate::currentDate()))
2537                 IFOKDO(err, act.setReconciliationBalance(ui.kReconcilitorAmountEdit->value()))
2538                 IFOKDO(err, act.save())
2539 
2540                 // Send message
2541                 IFOKDO(err, act.getDocument()->sendMessage(i18nc("An information message", "The account '%1' has been reconciled", act.getDisplayName()), SKGDocument::Positive))
2542 
2543                 IFOKDO(err, getDocument()->stepForward(nb + 1))
2544             }
2545         }
2546     }
2547 
2548     // status bar
2549     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Operation checked.")))
2550     else {
2551         err.addError(ERR_FAIL, i18nc("Error message",  "Switch failed"));
2552     }
2553 
2554     // Display error
2555     SKGMainPanel::displayErrorMessage(err);
2556 }
2557 
addSubOperationLine(int row,QDate date,const QString & category,const QString & tracker,const QString & comment,double quantity,const QString & formula,int id)2558 void SKGOperationPluginWidget::addSubOperationLine(int row, QDate date, const QString& category, const QString& tracker, const QString& comment, double quantity, const QString& formula, int id)
2559 {
2560     SKGTRACEINFUNC(10)
2561     bool previous = ui.kSubOperationsTable->blockSignals(true);
2562 
2563     ui.kSubOperationsTable->insertRow(row);
2564 
2565     // Add a delete icon on the line:
2566     auto hitem = new QTableWidgetItem(SKGServices::fromTheme(QStringLiteral("edit-delete")), QLatin1String(""));
2567     ui.kSubOperationsTable->setVerticalHeaderItem(row, hitem);
2568     QHeaderView* headerView = ui.kSubOperationsTable->verticalHeader();
2569     headerView->setSectionsMovable(true);
2570 
2571     // Category
2572     auto categoryItem = new QTableWidgetItem(category);
2573     categoryItem->setToolTip(category);
2574     categoryItem->setData(Qt::UserRole, id);
2575     ui.kSubOperationsTable->setItem(row, m_attributesForSplit.indexOf(QStringLiteral("t_category")), categoryItem);
2576 
2577     // Comment
2578     auto commentItem = new QTableWidgetItem(comment);
2579     commentItem->setToolTip(comment);
2580     ui.kSubOperationsTable->setItem(row, m_attributesForSplit.indexOf(QStringLiteral("t_comment")), commentItem);
2581 
2582     // Quantity
2583     auto unit = ui.kUnitEdit->getUnit().getUnitInfo();
2584     unit.Value = 1.0;
2585     auto quantityItem = new QTableWidgetItem(getDocument()->formatMoney(quantity, unit, false));
2586     quantityItem->setTextAlignment(Qt::AlignVCenter | Qt::AlignRight);
2587     quantityItem->setData(101, quantity);
2588     quantityItem->setToolTip(formula.isEmpty() ? SKGServices::doubleToString(quantity) : formula);
2589     ui.kSubOperationsTable->setItem(row, m_attributesForSplit.indexOf(QStringLiteral("f_value")), quantityItem);
2590 
2591     // Refund
2592     auto trackerItem = new QTableWidgetItem(tracker);
2593     trackerItem->setToolTip(tracker);
2594     categoryItem->setData(Qt::UserRole, id);
2595     ui.kSubOperationsTable->setItem(row, m_attributesForSplit.indexOf(QStringLiteral("t_refund")), trackerItem);
2596 
2597     // Date
2598     auto dateItem = new QTableWidgetItem(SKGMainPanel::dateToString(date));
2599     dateItem->setToolTip(SKGServices::dateToSqlString(date));
2600     ui.kSubOperationsTable->setItem(row, m_attributesForSplit.indexOf(QStringLiteral("d_date")), dateItem);
2601 
2602     ui.kSubOperationsTable->blockSignals(previous);
2603 
2604     ui.kSubOperationsTable->resizeColumnsToContents();
2605     ui.kSubOperationsTable->horizontalHeader()->setStretchLastSection(true);
2606     if (row == 0 && category.isEmpty()) {
2607         ui.kSubOperationsTable->horizontalHeader()->resizeSection(0, 300);
2608     }
2609 }
2610 
mainWidget()2611 QWidget* SKGOperationPluginWidget::mainWidget()
2612 {
2613     return ui.kOperationView->getView();
2614 }
2615 
getSelectedOperation(SKGOperationObject & operation)2616 SKGError SKGOperationPluginWidget::getSelectedOperation(SKGOperationObject& operation)
2617 {
2618     SKGError err;
2619     SKGObjectBase::SKGListSKGObjectBase selectedOperations = getSelectedObjects();
2620     if (!selectedOperations.isEmpty()) {
2621         operation = selectedOperations.at(0);
2622         err.setReturnCode(0);
2623     } else {
2624         err.setReturnCode(1).setMessage(i18nc("Error message",  "No Operation Selected"));
2625     }
2626     return err;
2627 }
2628 
cleanEditor()2629 void SKGOperationPluginWidget::cleanEditor()
2630 {
2631     if (getNbSelectedObjects() == 0 || sender() == ui.kCleanBtn) {
2632         ui.kOperationView->getView()->clearSelection();
2633         ui.kDateEdit->setDate(QDate::currentDate());
2634         ui.kPayeeEdit->setText(QLatin1String(""));
2635         ui.kCategoryEdit->setText(QLatin1String(""));
2636         ui.kTrackerEdit->setText(QLatin1String(""));
2637         ui.kAmountEdit->setText(QLatin1String(""));
2638         ui.kTypeEdit->setText(QLatin1String(""));
2639         ui.kCommentEdit->setText(QLatin1String(""));
2640         ui.kNumberEdit->setText(QLatin1String(""));
2641 
2642         if (!currentAccount().isEmpty()) {
2643             ui.kAccountEdit->setText(currentAccount());
2644         }
2645 
2646         // BUG 376025 vvvv
2647         ui.kUnitEdit->setDocument(qobject_cast<SKGDocumentBank*>(getDocument()));
2648         // BUG 376025 ^^^^
2649         ui.kUnitShare->setDocument(qobject_cast<SKGDocumentBank*>(getDocument()));
2650 
2651         setAllWidgetsEnabled();
2652         m_previousDate = QDate::currentDate();
2653     }
2654     if (sender() == ui.kCleanBtn) {
2655         ui.kWidgetSelector->setSelectedMode(0);
2656     }
2657 }
2658 
isEditor()2659 bool SKGOperationPluginWidget::isEditor()
2660 {
2661     return true;
2662 }
2663 
activateEditor()2664 void SKGOperationPluginWidget::activateEditor()
2665 {
2666     if (ui.kWidgetSelector->getSelectedMode() == -1) {
2667         ui.kWidgetSelector->setSelectedMode(0);
2668     }
2669     ui.kPayeeEdit->setFocus();
2670 }
2671