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